PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
tablecmds.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/commands/tablecmds.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include "access/attmap.h"
18#include "access/genam.h"
19#include "access/gist.h"
20#include "access/heapam.h"
21#include "access/heapam_xlog.h"
22#include "access/multixact.h"
23#include "access/reloptions.h"
24#include "access/relscan.h"
25#include "access/sysattr.h"
26#include "access/tableam.h"
28#include "access/xact.h"
29#include "access/xlog.h"
30#include "access/xloginsert.h"
31#include "catalog/catalog.h"
32#include "catalog/heap.h"
33#include "catalog/index.h"
34#include "catalog/namespace.h"
36#include "catalog/partition.h"
37#include "catalog/pg_am.h"
38#include "catalog/pg_attrdef.h"
41#include "catalog/pg_depend.h"
43#include "catalog/pg_inherits.h"
46#include "catalog/pg_opclass.h"
47#include "catalog/pg_policy.h"
48#include "catalog/pg_proc.h"
50#include "catalog/pg_rewrite.h"
53#include "catalog/pg_trigger.h"
54#include "catalog/pg_type.h"
55#include "catalog/storage.h"
57#include "catalog/toasting.h"
58#include "commands/cluster.h"
59#include "commands/comment.h"
60#include "commands/defrem.h"
62#include "commands/sequence.h"
63#include "commands/tablecmds.h"
64#include "commands/tablespace.h"
65#include "commands/trigger.h"
66#include "commands/typecmds.h"
67#include "commands/user.h"
68#include "commands/vacuum.h"
69#include "common/int.h"
70#include "executor/executor.h"
71#include "foreign/fdwapi.h"
72#include "foreign/foreign.h"
73#include "miscadmin.h"
74#include "nodes/makefuncs.h"
75#include "nodes/nodeFuncs.h"
76#include "nodes/parsenodes.h"
77#include "optimizer/optimizer.h"
78#include "parser/parse_coerce.h"
80#include "parser/parse_expr.h"
82#include "parser/parse_type.h"
84#include "parser/parser.h"
87#include "pgstat.h"
91#include "storage/bufmgr.h"
92#include "storage/lmgr.h"
93#include "storage/lock.h"
94#include "storage/predicate.h"
95#include "storage/smgr.h"
96#include "tcop/utility.h"
97#include "utils/acl.h"
98#include "utils/builtins.h"
99#include "utils/fmgroids.h"
100#include "utils/inval.h"
101#include "utils/lsyscache.h"
102#include "utils/memutils.h"
103#include "utils/partcache.h"
104#include "utils/relcache.h"
105#include "utils/ruleutils.h"
106#include "utils/snapmgr.h"
107#include "utils/syscache.h"
108#include "utils/timestamp.h"
109#include "utils/typcache.h"
110#include "utils/usercontext.h"
111
112/*
113 * ON COMMIT action list
114 */
115typedef struct OnCommitItem
116{
117 Oid relid; /* relid of relation */
118 OnCommitAction oncommit; /* what to do at end of xact */
119
120 /*
121 * If this entry was created during the current transaction,
122 * creating_subid is the ID of the creating subxact; if created in a prior
123 * transaction, creating_subid is zero. If deleted during the current
124 * transaction, deleting_subid is the ID of the deleting subxact; if no
125 * deletion request is pending, deleting_subid is zero.
126 */
130
132
133
134/*
135 * State information for ALTER TABLE
136 *
137 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
138 * structs, one for each table modified by the operation (the named table
139 * plus any child tables that are affected). We save lists of subcommands
140 * to apply to this table (possibly modified by parse transformation steps);
141 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
142 * necessary information is stored in the constraints and newvals lists.
143 *
144 * Phase 2 is divided into multiple passes; subcommands are executed in
145 * a pass determined by subcommand type.
146 */
147
148typedef enum AlterTablePass
149{
150 AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
151 AT_PASS_DROP, /* DROP (all flavors) */
152 AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
153 AT_PASS_ADD_COL, /* ADD COLUMN */
154 AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
155 AT_PASS_OLD_INDEX, /* re-add existing indexes */
156 AT_PASS_OLD_CONSTR, /* re-add existing constraints */
157 /* We could support a RENAME COLUMN pass here, but not currently used */
158 AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
159 AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
160 AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
161 AT_PASS_ADD_INDEX, /* ADD indexes */
162 AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
163 AT_PASS_MISC, /* other stuff */
165
166#define AT_NUM_PASSES (AT_PASS_MISC + 1)
167
168typedef struct AlteredTableInfo
169{
170 /* Information saved before any work commences: */
171 Oid relid; /* Relation to work on */
172 char relkind; /* Its relkind */
173 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
174
175 /*
176 * Transiently set during Phase 2, normally set to NULL.
177 *
178 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
179 * returns control. This can be exploited by ATExecCmd subroutines to
180 * close/reopen across transaction boundaries.
181 */
183
184 /* Information saved by Phase 1 for Phase 2: */
185 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
186 /* Information saved by Phases 1/2 for Phase 3: */
187 List *constraints; /* List of NewConstraint */
188 List *newvals; /* List of NewColumnValue */
189 List *afterStmts; /* List of utility command parsetrees */
190 bool verify_new_notnull; /* T if we should recheck NOT NULL */
191 int rewrite; /* Reason for forced rewrite, if any */
192 bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
193 Oid newAccessMethod; /* new access method; 0 means no change,
194 * if above is true */
195 Oid newTableSpace; /* new tablespace; 0 means no change */
196 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
197 char newrelpersistence; /* if above is true */
198 Expr *partition_constraint; /* for attach partition validation */
199 /* true, if validating default due to some other attach/detach */
201 /* Objects to rebuild after completing ALTER TYPE operations */
202 List *changedConstraintOids; /* OIDs of constraints to rebuild */
203 List *changedConstraintDefs; /* string definitions of same */
204 List *changedIndexOids; /* OIDs of indexes to rebuild */
205 List *changedIndexDefs; /* string definitions of same */
206 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
207 char *clusterOnIndex; /* index to use for CLUSTER */
208 List *changedStatisticsOids; /* OIDs of statistics to rebuild */
209 List *changedStatisticsDefs; /* string definitions of same */
211
212/* Struct describing one new constraint to check in Phase 3 scan */
213/* Note: new not-null constraints are handled elsewhere */
214typedef struct NewConstraint
215{
216 char *name; /* Constraint name, or NULL if none */
217 ConstrType contype; /* CHECK or FOREIGN */
218 Oid refrelid; /* PK rel, if FOREIGN */
219 Oid refindid; /* OID of PK's index, if FOREIGN */
220 bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
221 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
222 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
223 ExprState *qualstate; /* Execution state for CHECK expr */
225
226/*
227 * Struct describing one new column value that needs to be computed during
228 * Phase 3 copy (this could be either a new column with a non-null default, or
229 * a column that we're changing the type of). Columns without such an entry
230 * are just copied from the old table during ATRewriteTable. Note that the
231 * expr is an expression over *old* table values, except when is_generated
232 * is true; then it is an expression over columns of the *new* tuple.
233 */
234typedef struct NewColumnValue
235{
236 AttrNumber attnum; /* which column */
237 Expr *expr; /* expression to compute */
238 ExprState *exprstate; /* execution state */
239 bool is_generated; /* is it a GENERATED expression? */
241
242/*
243 * Error-reporting support for RemoveRelations
244 */
246{
247 char kind;
249 const char *nonexistent_msg;
250 const char *skipping_msg;
251 const char *nota_msg;
252 const char *drophint_msg;
253};
254
255static const struct dropmsgstrings dropmsgstringarray[] = {
256 {RELKIND_RELATION,
258 gettext_noop("table \"%s\" does not exist"),
259 gettext_noop("table \"%s\" does not exist, skipping"),
260 gettext_noop("\"%s\" is not a table"),
261 gettext_noop("Use DROP TABLE to remove a table.")},
262 {RELKIND_SEQUENCE,
264 gettext_noop("sequence \"%s\" does not exist"),
265 gettext_noop("sequence \"%s\" does not exist, skipping"),
266 gettext_noop("\"%s\" is not a sequence"),
267 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
268 {RELKIND_VIEW,
270 gettext_noop("view \"%s\" does not exist"),
271 gettext_noop("view \"%s\" does not exist, skipping"),
272 gettext_noop("\"%s\" is not a view"),
273 gettext_noop("Use DROP VIEW to remove a view.")},
274 {RELKIND_MATVIEW,
276 gettext_noop("materialized view \"%s\" does not exist"),
277 gettext_noop("materialized view \"%s\" does not exist, skipping"),
278 gettext_noop("\"%s\" is not a materialized view"),
279 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
280 {RELKIND_INDEX,
281 ERRCODE_UNDEFINED_OBJECT,
282 gettext_noop("index \"%s\" does not exist"),
283 gettext_noop("index \"%s\" does not exist, skipping"),
284 gettext_noop("\"%s\" is not an index"),
285 gettext_noop("Use DROP INDEX to remove an index.")},
286 {RELKIND_COMPOSITE_TYPE,
287 ERRCODE_UNDEFINED_OBJECT,
288 gettext_noop("type \"%s\" does not exist"),
289 gettext_noop("type \"%s\" does not exist, skipping"),
290 gettext_noop("\"%s\" is not a type"),
291 gettext_noop("Use DROP TYPE to remove a type.")},
292 {RELKIND_FOREIGN_TABLE,
293 ERRCODE_UNDEFINED_OBJECT,
294 gettext_noop("foreign table \"%s\" does not exist"),
295 gettext_noop("foreign table \"%s\" does not exist, skipping"),
296 gettext_noop("\"%s\" is not a foreign table"),
297 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
298 {RELKIND_PARTITIONED_TABLE,
300 gettext_noop("table \"%s\" does not exist"),
301 gettext_noop("table \"%s\" does not exist, skipping"),
302 gettext_noop("\"%s\" is not a table"),
303 gettext_noop("Use DROP TABLE to remove a table.")},
304 {RELKIND_PARTITIONED_INDEX,
305 ERRCODE_UNDEFINED_OBJECT,
306 gettext_noop("index \"%s\" does not exist"),
307 gettext_noop("index \"%s\" does not exist, skipping"),
308 gettext_noop("\"%s\" is not an index"),
309 gettext_noop("Use DROP INDEX to remove an index.")},
310 {'\0', 0, NULL, NULL, NULL, NULL}
311};
312
313/* communication between RemoveRelations and RangeVarCallbackForDropRelation */
315{
316 /* These fields are set by RemoveRelations: */
319 /* These fields are state to track which subsidiary locks are held: */
322 /* These fields are passed back by RangeVarCallbackForDropRelation: */
325};
326
327/* Alter table target-type flags for ATSimplePermissions */
328#define ATT_TABLE 0x0001
329#define ATT_VIEW 0x0002
330#define ATT_MATVIEW 0x0004
331#define ATT_INDEX 0x0008
332#define ATT_COMPOSITE_TYPE 0x0010
333#define ATT_FOREIGN_TABLE 0x0020
334#define ATT_PARTITIONED_INDEX 0x0040
335#define ATT_SEQUENCE 0x0080
336#define ATT_PARTITIONED_TABLE 0x0100
337
338/*
339 * ForeignTruncateInfo
340 *
341 * Information related to truncation of foreign tables. This is used for
342 * the elements in a hash table. It uses the server OID as lookup key,
343 * and includes a per-server list of all foreign tables involved in the
344 * truncation.
345 */
347{
351
352/* Partial or complete FK creation in addFkConstraint() */
354{
359
360/*
361 * Partition tables are expected to be dropped when the parent partitioned
362 * table gets dropped. Hence for partitioning we use AUTO dependency.
363 * Otherwise, for regular inheritance use NORMAL dependency.
364 */
365#define child_dependency_type(child_is_partition) \
366 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
367
368static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
369static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
370static void truncate_check_activity(Relation rel);
371static void RangeVarCallbackForTruncate(const RangeVar *relation,
372 Oid relId, Oid oldRelId, void *arg);
373static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
374 bool is_partition, List **supconstr,
375 List **supnotnulls);
376static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
377static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
378static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
379static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
380static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
381static void StoreCatalogInheritance(Oid relationId, List *supers,
382 bool child_is_partition);
383static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
384 int32 seqNumber, Relation inhRelation,
385 bool child_is_partition);
386static int findAttrByName(const char *attributeName, const List *columns);
387static void AlterIndexNamespaces(Relation classRel, Relation rel,
388 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
389static void AlterSeqNamespaces(Relation classRel, Relation rel,
390 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
391 LOCKMODE lockmode);
393 ATAlterConstraint *cmdcon,
394 bool recurse, LOCKMODE lockmode);
395static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
396 Relation tgrel, Relation rel, HeapTuple contuple,
397 bool recurse, LOCKMODE lockmode);
398static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
399 Relation conrel, Relation tgrel,
400 const Oid fkrelid, const Oid pkrelid,
401 HeapTuple contuple, LOCKMODE lockmode,
402 Oid ReferencedParentDelTrigger,
403 Oid ReferencedParentUpdTrigger,
404 Oid ReferencingParentInsTrigger,
405 Oid ReferencingParentUpdTrigger);
406static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
407 Relation conrel, Relation tgrel, Relation rel,
408 HeapTuple contuple, bool recurse,
409 List **otherrelids, LOCKMODE lockmode);
410static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
411 Relation conrel, Relation rel,
412 HeapTuple contuple, LOCKMODE lockmode);
413static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
414 bool deferrable, bool initdeferred,
415 List **otherrelids);
416static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
417 Relation conrel, Relation tgrel,
418 const Oid fkrelid, const Oid pkrelid,
419 HeapTuple contuple, LOCKMODE lockmode,
420 Oid ReferencedParentDelTrigger,
421 Oid ReferencedParentUpdTrigger,
422 Oid ReferencingParentInsTrigger,
423 Oid ReferencingParentUpdTrigger);
424static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
425 Relation conrel, Relation tgrel, Relation rel,
426 HeapTuple contuple, bool recurse,
427 List **otherrelids, LOCKMODE lockmode);
429 HeapTuple contuple);
431 Relation rel, char *constrName,
432 bool recurse, bool recursing, LOCKMODE lockmode);
433static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
434 HeapTuple contuple, LOCKMODE lockmode);
435static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
436 char *constrName, HeapTuple contuple,
437 bool recurse, bool recursing, LOCKMODE lockmode);
438static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
439 HeapTuple contuple, bool recurse, bool recursing,
440 LOCKMODE lockmode);
441static int transformColumnNameList(Oid relId, List *colList,
442 int16 *attnums, Oid *atttypids, Oid *attcollids);
443static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
444 List **attnamelist,
445 int16 *attnums, Oid *atttypids, Oid *attcollids,
446 Oid *opclasses, bool *pk_has_without_overlaps);
448 int numattrs, int16 *attnums,
449 bool with_period, Oid *opclasses,
450 bool *pk_has_without_overlaps);
451static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
452static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
453 Oid *funcid);
454static void validateForeignKeyConstraint(char *conname,
455 Relation rel, Relation pkrel,
456 Oid pkindOid, Oid constraintOid, bool hasperiod);
457static void CheckAlterTableIsSafe(Relation rel);
458static void ATController(AlterTableStmt *parsetree,
459 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
460 AlterTableUtilityContext *context);
461static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
462 bool recurse, bool recursing, LOCKMODE lockmode,
463 AlterTableUtilityContext *context);
464static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
465 AlterTableUtilityContext *context);
466static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
467 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
468 AlterTableUtilityContext *context);
470 Relation rel, AlterTableCmd *cmd,
471 bool recurse, LOCKMODE lockmode,
472 AlterTablePass cur_pass,
473 AlterTableUtilityContext *context);
474static void ATRewriteTables(AlterTableStmt *parsetree,
475 List **wqueue, LOCKMODE lockmode,
476 AlterTableUtilityContext *context);
477static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
478static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
479static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
480static void ATSimpleRecursion(List **wqueue, Relation rel,
481 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
482 AlterTableUtilityContext *context);
483static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
484static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
485 LOCKMODE lockmode,
486 AlterTableUtilityContext *context);
487static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
488 DropBehavior behavior);
489static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
490 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
491 AlterTableUtilityContext *context);
493 Relation rel, AlterTableCmd **cmd,
494 bool recurse, bool recursing,
495 LOCKMODE lockmode, AlterTablePass cur_pass,
496 AlterTableUtilityContext *context);
497static bool check_for_column_name_collision(Relation rel, const char *colname,
498 bool if_not_exists);
499static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
501static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
502 LOCKMODE lockmode);
503static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
504 bool is_valid, bool queue_validation);
505static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
506 char *constrname, char *colName,
507 bool recurse, bool recursing,
508 LOCKMODE lockmode);
511 List *testConstraint, List *provenConstraint);
512static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
513 Node *newDefault, LOCKMODE lockmode);
515 Node *newDefault);
516static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
517 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
518static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
519 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
520static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
521 bool recurse, bool recursing);
522static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
523 Node *newExpr, LOCKMODE lockmode);
524static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
525static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
526static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
527 Node *newValue, LOCKMODE lockmode);
528static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
529 Node *options, bool isReset, LOCKMODE lockmode);
530static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
531 Node *newValue, LOCKMODE lockmode);
532static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
533 AlterTableCmd *cmd, LOCKMODE lockmode,
534 AlterTableUtilityContext *context);
535static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
536 DropBehavior behavior,
537 bool recurse, bool recursing,
538 bool missing_ok, LOCKMODE lockmode,
539 ObjectAddresses *addrs);
540static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
541 bool recurse, LOCKMODE lockmode,
542 AlterTableUtilityContext *context);
544 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
546 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
548 AlteredTableInfo *tab, Relation rel,
549 Constraint *newConstraint, bool recurse, bool is_readd,
550 LOCKMODE lockmode);
551static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
553 IndexStmt *stmt, LOCKMODE lockmode);
555 AlteredTableInfo *tab, Relation rel,
556 Constraint *constr,
557 bool recurse, bool recursing, bool is_readd,
558 LOCKMODE lockmode);
560 Relation rel, Constraint *fkconstraint,
561 bool recurse, bool recursing,
562 LOCKMODE lockmode);
563static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
564 int numfksetcols, int16 *fksetcolsattnums,
565 List *fksetcols);
567 char *constraintname,
568 Constraint *fkconstraint, Relation rel,
569 Relation pkrel, Oid indexOid,
570 Oid parentConstr,
571 int numfks, int16 *pkattnum, int16 *fkattnum,
572 Oid *pfeqoperators, Oid *ppeqoperators,
573 Oid *ffeqoperators, int numfkdelsetcols,
574 int16 *fkdelsetcols, bool is_internal,
575 bool with_period);
576static void addFkRecurseReferenced(Constraint *fkconstraint,
577 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
578 int numfks, int16 *pkattnum, int16 *fkattnum,
579 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
580 int numfkdelsetcols, int16 *fkdelsetcols,
581 bool old_check_ok,
582 Oid parentDelTrigger, Oid parentUpdTrigger,
583 bool with_period);
584static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
585 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
586 int numfks, int16 *pkattnum, int16 *fkattnum,
587 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
588 int numfkdelsetcols, int16 *fkdelsetcols,
589 bool old_check_ok, LOCKMODE lockmode,
590 Oid parentInsTrigger, Oid parentUpdTrigger,
591 bool with_period);
592static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
593 Relation partitionRel);
594static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
595static void CloneFkReferencing(List **wqueue, Relation parentRel,
596 Relation partRel);
597static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
598 Constraint *fkconstraint, Oid constraintOid,
599 Oid indexOid,
600 Oid parentInsTrigger, Oid parentUpdTrigger,
601 Oid *insertTrigOid, Oid *updateTrigOid);
602static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
603 Constraint *fkconstraint, Oid constraintOid,
604 Oid indexOid,
605 Oid parentDelTrigger, Oid parentUpdTrigger,
606 Oid *deleteTrigOid, Oid *updateTrigOid);
607static bool tryAttachPartitionForeignKey(List **wqueue,
609 Relation partition,
610 Oid parentConstrOid, int numfks,
611 AttrNumber *mapped_conkey, AttrNumber *confkey,
612 Oid *conpfeqop,
613 Oid parentInsTrigger,
614 Oid parentUpdTrigger,
615 Relation trigrel);
616static void AttachPartitionForeignKey(List **wqueue, Relation partition,
617 Oid partConstrOid, Oid parentConstrOid,
618 Oid parentInsTrigger, Oid parentUpdTrigger,
619 Relation trigrel);
620static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
621 Oid conoid, Oid conrelid);
622static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
623 Oid confrelid, Oid conrelid);
624static void GetForeignKeyActionTriggers(Relation trigrel,
625 Oid conoid, Oid confrelid, Oid conrelid,
626 Oid *deleteTriggerOid,
627 Oid *updateTriggerOid);
628static void GetForeignKeyCheckTriggers(Relation trigrel,
629 Oid conoid, Oid confrelid, Oid conrelid,
630 Oid *insertTriggerOid,
631 Oid *updateTriggerOid);
632static void ATExecDropConstraint(Relation rel, const char *constrName,
633 DropBehavior behavior, bool recurse,
634 bool missing_ok, LOCKMODE lockmode);
636 HeapTuple constraintTup, DropBehavior behavior,
637 bool recurse, bool recursing,
638 bool missing_ok, LOCKMODE lockmode);
639static void ATPrepAlterColumnType(List **wqueue,
640 AlteredTableInfo *tab, Relation rel,
641 bool recurse, bool recursing,
642 AlterTableCmd *cmd, LOCKMODE lockmode,
643 AlterTableUtilityContext *context);
644static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
646 AlterTableCmd *cmd, LOCKMODE lockmode);
648 Relation rel, AttrNumber attnum, const char *colName);
650static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
652static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
653 LOCKMODE lockmode);
654static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
655 char *cmd, List **wqueue, LOCKMODE lockmode,
656 bool rewrite);
658 Oid objid, Relation rel, List *domname,
659 const char *conname);
660static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
661static void TryReuseForeignKey(Oid oldId, Constraint *con);
662static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
663 List *options, LOCKMODE lockmode);
664static void change_owner_fix_column_acls(Oid relationOid,
665 Oid oldOwnerId, Oid newOwnerId);
666static void change_owner_recurse_to_sequences(Oid relationOid,
667 Oid newOwnerId, LOCKMODE lockmode);
668static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
669 LOCKMODE lockmode);
670static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
671static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
672static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
674 bool toLogged);
675static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
676 const char *tablespacename, LOCKMODE lockmode);
677static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
678static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
679static void ATExecSetRelOptions(Relation rel, List *defList,
680 AlterTableType operation,
681 LOCKMODE lockmode);
682static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
683 char fires_when, bool skip_system, bool recurse,
684 LOCKMODE lockmode);
685static void ATExecEnableDisableRule(Relation rel, const char *rulename,
686 char fires_when, LOCKMODE lockmode);
687static void ATPrepAddInherit(Relation child_rel);
688static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
689static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
690static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
691 DependencyType deptype);
692static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
693static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
695static void ATExecGenericOptions(Relation rel, List *options);
696static void ATExecSetRowSecurity(Relation rel, bool rls);
697static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
699 const char *column, Node *newValue, LOCKMODE lockmode);
700
701static void index_copy_data(Relation rel, RelFileLocator newrlocator);
702static const char *storage_name(char c);
703
704static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
705 Oid oldRelOid, void *arg);
706static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
707 Oid oldrelid, void *arg);
709static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
710 List **partexprs, Oid *partopclass, Oid *partcollation,
711 PartitionStrategy strategy);
712static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
713static void RemoveInheritance(Relation child_rel, Relation parent_rel,
714 bool expect_detached);
716 PartitionCmd *cmd,
717 AlterTableUtilityContext *context);
718static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
719static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
720 List *partConstraint,
721 bool validate_default);
722static void CloneRowTriggersToPartition(Relation parent, Relation partition);
723static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
724static void DropClonedTriggersFromPartition(Oid partitionId);
726 Relation rel, RangeVar *name,
727 bool concurrent);
728static void DetachPartitionFinalize(Relation rel, Relation partRel,
729 bool concurrent, Oid defaultPartOid);
731static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
732 RangeVar *name);
733static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
734static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
735 Relation partitionTbl);
736static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
737static List *GetParentedForeignKeyRefs(Relation partition);
738static void ATDetachCheckNoForeignKeyRefs(Relation partition);
739static char GetAttributeCompression(Oid atttypid, const char *compression);
740static char GetAttributeStorage(Oid atttypid, const char *storagemode);
741
742
743/* ----------------------------------------------------------------
744 * DefineRelation
745 * Creates a new relation.
746 *
747 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
748 * The other arguments are used to extend the behavior for other cases:
749 * relkind: relkind to assign to the new relation
750 * ownerId: if not InvalidOid, use this as the new relation's owner.
751 * typaddress: if not null, it's set to the pg_type entry's address.
752 * queryString: for error reporting
753 *
754 * Note that permissions checks are done against current user regardless of
755 * ownerId. A nonzero ownerId is used when someone is creating a relation
756 * "on behalf of" someone else, so we still want to see that the current user
757 * has permissions to do it.
758 *
759 * If successful, returns the address of the new relation.
760 * ----------------------------------------------------------------
761 */
763DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
764 ObjectAddress *typaddress, const char *queryString)
765{
766 char relname[NAMEDATALEN];
767 Oid namespaceId;
768 Oid relationId;
769 Oid tablespaceId;
770 Relation rel;
772 List *inheritOids;
773 List *old_constraints;
774 List *old_notnulls;
775 List *rawDefaults;
776 List *cookedDefaults;
777 List *nncols;
778 Datum reloptions;
779 ListCell *listptr;
781 bool partitioned;
782 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
783 Oid ofTypeId;
784 ObjectAddress address;
785 LOCKMODE parentLockmode;
786 Oid accessMethodId = InvalidOid;
787
788 /*
789 * Truncate relname to appropriate length (probably a waste of time, as
790 * parser should have done this already).
791 */
792 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
793
794 /*
795 * Check consistency of arguments
796 */
797 if (stmt->oncommit != ONCOMMIT_NOOP
798 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
800 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
801 errmsg("ON COMMIT can only be used on temporary tables")));
802
803 if (stmt->partspec != NULL)
804 {
805 if (relkind != RELKIND_RELATION)
806 elog(ERROR, "unexpected relkind: %d", (int) relkind);
807
808 relkind = RELKIND_PARTITIONED_TABLE;
809 partitioned = true;
810 }
811 else
812 partitioned = false;
813
814 if (relkind == RELKIND_PARTITIONED_TABLE &&
815 stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
817 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
818 errmsg("partitioned tables cannot be unlogged")));
819
820 /*
821 * Look up the namespace in which we are supposed to create the relation,
822 * check we have permission to create there, lock it against concurrent
823 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
824 * namespace is selected.
825 */
826 namespaceId =
828
829 /*
830 * Security check: disallow creating temp tables from security-restricted
831 * code. This is needed because calling code might not expect untrusted
832 * tables to appear in pg_temp at the front of its search path.
833 */
834 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
837 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
838 errmsg("cannot create temporary table within security-restricted operation")));
839
840 /*
841 * Determine the lockmode to use when scanning parents. A self-exclusive
842 * lock is needed here.
843 *
844 * For regular inheritance, if two backends attempt to add children to the
845 * same parent simultaneously, and that parent has no pre-existing
846 * children, then both will attempt to update the parent's relhassubclass
847 * field, leading to a "tuple concurrently updated" error. Also, this
848 * interlocks against a concurrent ANALYZE on the parent table, which
849 * might otherwise be attempting to clear the parent's relhassubclass
850 * field, if its previous children were recently dropped.
851 *
852 * If the child table is a partition, then we instead grab an exclusive
853 * lock on the parent because its partition descriptor will be changed by
854 * addition of the new partition.
855 */
856 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
858
859 /* Determine the list of OIDs of the parents. */
860 inheritOids = NIL;
861 foreach(listptr, stmt->inhRelations)
862 {
863 RangeVar *rv = (RangeVar *) lfirst(listptr);
864 Oid parentOid;
865
866 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
867
868 /*
869 * Reject duplications in the list of parents.
870 */
871 if (list_member_oid(inheritOids, parentOid))
873 (errcode(ERRCODE_DUPLICATE_TABLE),
874 errmsg("relation \"%s\" would be inherited from more than once",
875 get_rel_name(parentOid))));
876
877 inheritOids = lappend_oid(inheritOids, parentOid);
878 }
879
880 /*
881 * Select tablespace to use: an explicitly indicated one, or (in the case
882 * of a partitioned table) the parent's, if it has one.
883 */
884 if (stmt->tablespacename)
885 {
886 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
887
888 if (partitioned && tablespaceId == MyDatabaseTableSpace)
890 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
891 errmsg("cannot specify default tablespace for partitioned relations")));
892 }
893 else if (stmt->partbound)
894 {
895 Assert(list_length(inheritOids) == 1);
896 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
897 }
898 else
899 tablespaceId = InvalidOid;
900
901 /* still nothing? use the default */
902 if (!OidIsValid(tablespaceId))
903 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
904 partitioned);
905
906 /* Check permissions except when using database's default */
907 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
908 {
909 AclResult aclresult;
910
911 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
912 ACL_CREATE);
913 if (aclresult != ACLCHECK_OK)
915 get_tablespace_name(tablespaceId));
916 }
917
918 /* In all cases disallow placing user relations in pg_global */
919 if (tablespaceId == GLOBALTABLESPACE_OID)
921 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
922 errmsg("only shared relations can be placed in pg_global tablespace")));
923
924 /* Identify user ID that will own the table */
925 if (!OidIsValid(ownerId))
926 ownerId = GetUserId();
927
928 /*
929 * Parse and validate reloptions, if any.
930 */
931 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
932 true, false);
933
934 switch (relkind)
935 {
936 case RELKIND_VIEW:
937 (void) view_reloptions(reloptions, true);
938 break;
939 case RELKIND_PARTITIONED_TABLE:
940 (void) partitioned_table_reloptions(reloptions, true);
941 break;
942 default:
943 (void) heap_reloptions(relkind, reloptions, true);
944 }
945
946 if (stmt->ofTypename)
947 {
948 AclResult aclresult;
949
950 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
951
952 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
953 if (aclresult != ACLCHECK_OK)
954 aclcheck_error_type(aclresult, ofTypeId);
955 }
956 else
957 ofTypeId = InvalidOid;
958
959 /*
960 * Look up inheritance ancestors and generate relation schema, including
961 * inherited attributes. (Note that stmt->tableElts is destructively
962 * modified by MergeAttributes.)
963 */
964 stmt->tableElts =
965 MergeAttributes(stmt->tableElts, inheritOids,
966 stmt->relation->relpersistence,
967 stmt->partbound != NULL,
968 &old_constraints, &old_notnulls);
969
970 /*
971 * Create a tuple descriptor from the relation schema. Note that this
972 * deals with column names, types, and in-descriptor NOT NULL flags, but
973 * not default values, NOT NULL or CHECK constraints; we handle those
974 * below.
975 */
977
978 /*
979 * Find columns with default values and prepare for insertion of the
980 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
981 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
982 * while raw defaults go into a list of RawColumnDefault structs that will
983 * be processed by AddRelationNewConstraints. (We can't deal with raw
984 * expressions until we can do transformExpr.)
985 */
986 rawDefaults = NIL;
987 cookedDefaults = NIL;
988 attnum = 0;
989
990 foreach(listptr, stmt->tableElts)
991 {
992 ColumnDef *colDef = lfirst(listptr);
993
994 attnum++;
995 if (colDef->raw_default != NULL)
996 {
997 RawColumnDefault *rawEnt;
998
999 Assert(colDef->cooked_default == NULL);
1000
1001 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
1002 rawEnt->attnum = attnum;
1003 rawEnt->raw_default = colDef->raw_default;
1004 rawEnt->generated = colDef->generated;
1005 rawDefaults = lappend(rawDefaults, rawEnt);
1006 }
1007 else if (colDef->cooked_default != NULL)
1008 {
1009 CookedConstraint *cooked;
1010
1011 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
1012 cooked->contype = CONSTR_DEFAULT;
1013 cooked->conoid = InvalidOid; /* until created */
1014 cooked->name = NULL;
1015 cooked->attnum = attnum;
1016 cooked->expr = colDef->cooked_default;
1017 cooked->is_enforced = true;
1018 cooked->skip_validation = false;
1019 cooked->is_local = true; /* not used for defaults */
1020 cooked->inhcount = 0; /* ditto */
1021 cooked->is_no_inherit = false;
1022 cookedDefaults = lappend(cookedDefaults, cooked);
1023 }
1024 }
1025
1026 /*
1027 * For relations with table AM and partitioned tables, select access
1028 * method to use: an explicitly indicated one, or (in the case of a
1029 * partitioned table) the parent's, if it has one.
1030 */
1031 if (stmt->accessMethod != NULL)
1032 {
1033 Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1034 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1035 }
1036 else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1037 {
1038 if (stmt->partbound)
1039 {
1040 Assert(list_length(inheritOids) == 1);
1041 accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1042 }
1043
1044 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1045 accessMethodId = get_table_am_oid(default_table_access_method, false);
1046 }
1047
1048 /*
1049 * Create the relation. Inherited defaults and CHECK constraints are
1050 * passed in for immediate handling --- since they don't need parsing,
1051 * they can be stored immediately.
1052 */
1053 relationId = heap_create_with_catalog(relname,
1054 namespaceId,
1055 tablespaceId,
1056 InvalidOid,
1057 InvalidOid,
1058 ofTypeId,
1059 ownerId,
1060 accessMethodId,
1061 descriptor,
1062 list_concat(cookedDefaults,
1063 old_constraints),
1064 relkind,
1065 stmt->relation->relpersistence,
1066 false,
1067 false,
1068 stmt->oncommit,
1069 reloptions,
1070 true,
1072 false,
1073 InvalidOid,
1074 typaddress);
1075
1076 /*
1077 * We must bump the command counter to make the newly-created relation
1078 * tuple visible for opening.
1079 */
1081
1082 /*
1083 * Open the new relation and acquire exclusive lock on it. This isn't
1084 * really necessary for locking out other backends (since they can't see
1085 * the new rel anyway until we commit), but it keeps the lock manager from
1086 * complaining about deadlock risks.
1087 */
1088 rel = relation_open(relationId, AccessExclusiveLock);
1089
1090 /*
1091 * Now add any newly specified column default and generation expressions
1092 * to the new relation. These are passed to us in the form of raw
1093 * parsetrees; we need to transform them to executable expression trees
1094 * before they can be added. The most convenient way to do that is to
1095 * apply the parser's transformExpr routine, but transformExpr doesn't
1096 * work unless we have a pre-existing relation. So, the transformation has
1097 * to be postponed to this final step of CREATE TABLE.
1098 *
1099 * This needs to be before processing the partitioning clauses because
1100 * those could refer to generated columns.
1101 */
1102 if (rawDefaults)
1103 AddRelationNewConstraints(rel, rawDefaults, NIL,
1104 true, true, false, queryString);
1105
1106 /*
1107 * Make column generation expressions visible for use by partitioning.
1108 */
1110
1111 /* Process and store partition bound, if any. */
1112 if (stmt->partbound)
1113 {
1114 PartitionBoundSpec *bound;
1115 ParseState *pstate;
1116 Oid parentId = linitial_oid(inheritOids),
1117 defaultPartOid;
1118 Relation parent,
1119 defaultRel = NULL;
1120 ParseNamespaceItem *nsitem;
1121
1122 /* Already have strong enough lock on the parent */
1123 parent = table_open(parentId, NoLock);
1124
1125 /*
1126 * We are going to try to validate the partition bound specification
1127 * against the partition key of parentRel, so it better have one.
1128 */
1129 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1130 ereport(ERROR,
1131 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1132 errmsg("\"%s\" is not partitioned",
1133 RelationGetRelationName(parent))));
1134
1135 /*
1136 * The partition constraint of the default partition depends on the
1137 * partition bounds of every other partition. It is possible that
1138 * another backend might be about to execute a query on the default
1139 * partition table, and that the query relies on previously cached
1140 * default partition constraints. We must therefore take a table lock
1141 * strong enough to prevent all queries on the default partition from
1142 * proceeding until we commit and send out a shared-cache-inval notice
1143 * that will make them update their index lists.
1144 *
1145 * Order of locking: The relation being added won't be visible to
1146 * other backends until it is committed, hence here in
1147 * DefineRelation() the order of locking the default partition and the
1148 * relation being added does not matter. But at all other places we
1149 * need to lock the default relation before we lock the relation being
1150 * added or removed i.e. we should take the lock in same order at all
1151 * the places such that lock parent, lock default partition and then
1152 * lock the partition so as to avoid a deadlock.
1153 */
1154 defaultPartOid =
1156 true));
1157 if (OidIsValid(defaultPartOid))
1158 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1159
1160 /* Transform the bound values */
1161 pstate = make_parsestate(NULL);
1162 pstate->p_sourcetext = queryString;
1163
1164 /*
1165 * Add an nsitem containing this relation, so that transformExpr
1166 * called on partition bound expressions is able to report errors
1167 * using a proper context.
1168 */
1169 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1170 NULL, false, false);
1171 addNSItemToQuery(pstate, nsitem, false, true, true);
1172
1173 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1174
1175 /*
1176 * Check first that the new partition's bound is valid and does not
1177 * overlap with any of existing partitions of the parent.
1178 */
1179 check_new_partition_bound(relname, parent, bound, pstate);
1180
1181 /*
1182 * If the default partition exists, its partition constraints will
1183 * change after the addition of this new partition such that it won't
1184 * allow any row that qualifies for this new partition. So, check that
1185 * the existing data in the default partition satisfies the constraint
1186 * as it will exist after adding this partition.
1187 */
1188 if (OidIsValid(defaultPartOid))
1189 {
1190 check_default_partition_contents(parent, defaultRel, bound);
1191 /* Keep the lock until commit. */
1192 table_close(defaultRel, NoLock);
1193 }
1194
1195 /* Update the pg_class entry. */
1196 StorePartitionBound(rel, parent, bound);
1197
1198 table_close(parent, NoLock);
1199 }
1200
1201 /* Store inheritance information for new rel. */
1202 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1203
1204 /*
1205 * Process the partitioning specification (if any) and store the partition
1206 * key information into the catalog.
1207 */
1208 if (partitioned)
1209 {
1210 ParseState *pstate;
1211 int partnatts;
1212 AttrNumber partattrs[PARTITION_MAX_KEYS];
1213 Oid partopclass[PARTITION_MAX_KEYS];
1214 Oid partcollation[PARTITION_MAX_KEYS];
1215 List *partexprs = NIL;
1216
1217 pstate = make_parsestate(NULL);
1218 pstate->p_sourcetext = queryString;
1219
1220 partnatts = list_length(stmt->partspec->partParams);
1221
1222 /* Protect fixed-size arrays here and in executor */
1223 if (partnatts > PARTITION_MAX_KEYS)
1224 ereport(ERROR,
1225 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1226 errmsg("cannot partition using more than %d columns",
1228
1229 /*
1230 * We need to transform the raw parsetrees corresponding to partition
1231 * expressions into executable expression trees. Like column defaults
1232 * and CHECK constraints, we could not have done the transformation
1233 * earlier.
1234 */
1235 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1236
1237 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1238 partattrs, &partexprs, partopclass,
1239 partcollation, stmt->partspec->strategy);
1240
1241 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1242 partexprs,
1243 partopclass, partcollation);
1244
1245 /* make it all visible */
1247 }
1248
1249 /*
1250 * If we're creating a partition, create now all the indexes, triggers,
1251 * FKs defined in the parent.
1252 *
1253 * We can't do it earlier, because DefineIndex wants to know the partition
1254 * key which we just stored.
1255 */
1256 if (stmt->partbound)
1257 {
1258 Oid parentId = linitial_oid(inheritOids);
1259 Relation parent;
1260 List *idxlist;
1261 ListCell *cell;
1262
1263 /* Already have strong enough lock on the parent */
1264 parent = table_open(parentId, NoLock);
1265 idxlist = RelationGetIndexList(parent);
1266
1267 /*
1268 * For each index in the parent table, create one in the partition
1269 */
1270 foreach(cell, idxlist)
1271 {
1273 AttrMap *attmap;
1274 IndexStmt *idxstmt;
1275 Oid constraintOid;
1276
1277 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1278 {
1279 if (idxRel->rd_index->indisunique)
1280 ereport(ERROR,
1281 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1282 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1283 RelationGetRelationName(parent)),
1284 errdetail("Table \"%s\" contains indexes that are unique.",
1285 RelationGetRelationName(parent))));
1286 else
1287 {
1289 continue;
1290 }
1291 }
1292
1294 RelationGetDescr(parent),
1295 false);
1296 idxstmt =
1297 generateClonedIndexStmt(NULL, idxRel,
1298 attmap, &constraintOid);
1300 idxstmt,
1301 InvalidOid,
1302 RelationGetRelid(idxRel),
1303 constraintOid,
1304 -1,
1305 false, false, false, false, false);
1306
1308 }
1309
1310 list_free(idxlist);
1311
1312 /*
1313 * If there are any row-level triggers, clone them to the new
1314 * partition.
1315 */
1316 if (parent->trigdesc != NULL)
1317 CloneRowTriggersToPartition(parent, rel);
1318
1319 /*
1320 * And foreign keys too. Note that because we're freshly creating the
1321 * table, there is no need to verify these new constraints.
1322 */
1323 CloneForeignKeyConstraints(NULL, parent, rel);
1324
1325 table_close(parent, NoLock);
1326 }
1327
1328 /*
1329 * Now add any newly specified CHECK constraints to the new relation. Same
1330 * as for defaults above, but these need to come after partitioning is set
1331 * up.
1332 */
1333 if (stmt->constraints)
1334 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1335 true, true, false, queryString);
1336
1337 /*
1338 * Finally, merge the not-null constraints that are declared directly with
1339 * those that come from parent relations (making sure to count inheritance
1340 * appropriately for each), create them, and set the attnotnull flag on
1341 * columns that don't yet have it.
1342 */
1343 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1344 old_notnulls);
1345 foreach_int(attrnum, nncols)
1346 set_attnotnull(NULL, rel, attrnum, true, false);
1347
1348 ObjectAddressSet(address, RelationRelationId, relationId);
1349
1350 /*
1351 * Clean up. We keep lock on new relation (although it shouldn't be
1352 * visible to anyone else anyway, until commit).
1353 */
1354 relation_close(rel, NoLock);
1355
1356 return address;
1357}
1358
1359/*
1360 * BuildDescForRelation
1361 *
1362 * Given a list of ColumnDef nodes, build a TupleDesc.
1363 *
1364 * Note: This is only for the limited purpose of table and view creation. Not
1365 * everything is filled in. A real tuple descriptor should be obtained from
1366 * the relcache.
1367 */
1370{
1371 int natts;
1373 ListCell *l;
1374 TupleDesc desc;
1375 char *attname;
1376 Oid atttypid;
1377 int32 atttypmod;
1378 Oid attcollation;
1379 int attdim;
1380
1381 /*
1382 * allocate a new tuple descriptor
1383 */
1384 natts = list_length(columns);
1385 desc = CreateTemplateTupleDesc(natts);
1386
1387 attnum = 0;
1388
1389 foreach(l, columns)
1390 {
1391 ColumnDef *entry = lfirst(l);
1392 AclResult aclresult;
1394
1395 /*
1396 * for each entry in the list, get the name and type information from
1397 * the list and have TupleDescInitEntry fill in the attribute
1398 * information we need.
1399 */
1400 attnum++;
1401
1402 attname = entry->colname;
1403 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1404
1405 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1406 if (aclresult != ACLCHECK_OK)
1407 aclcheck_error_type(aclresult, atttypid);
1408
1409 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1410 attdim = list_length(entry->typeName->arrayBounds);
1411 if (attdim > PG_INT16_MAX)
1412 ereport(ERROR,
1413 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1414 errmsg("too many array dimensions"));
1415
1416 if (entry->typeName->setof)
1417 ereport(ERROR,
1418 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1419 errmsg("column \"%s\" cannot be declared SETOF",
1420 attname)));
1421
1423 atttypid, atttypmod, attdim);
1424 att = TupleDescAttr(desc, attnum - 1);
1425
1426 /* Override TupleDescInitEntry's settings as requested */
1427 TupleDescInitEntryCollation(desc, attnum, attcollation);
1428
1429 /* Fill in additional stuff not handled by TupleDescInitEntry */
1430 att->attnotnull = entry->is_not_null;
1431 att->attislocal = entry->is_local;
1432 att->attinhcount = entry->inhcount;
1433 att->attidentity = entry->identity;
1434 att->attgenerated = entry->generated;
1435 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1436 if (entry->storage)
1437 att->attstorage = entry->storage;
1438 else if (entry->storage_name)
1439 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1440
1442 }
1443
1444 return desc;
1445}
1446
1447/*
1448 * Emit the right error or warning message for a "DROP" command issued on a
1449 * non-existent relation
1450 */
1451static void
1452DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1453{
1454 const struct dropmsgstrings *rentry;
1455
1456 if (rel->schemaname != NULL &&
1458 {
1459 if (!missing_ok)
1460 {
1461 ereport(ERROR,
1462 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1463 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1464 }
1465 else
1466 {
1468 (errmsg("schema \"%s\" does not exist, skipping",
1469 rel->schemaname)));
1470 }
1471 return;
1472 }
1473
1474 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1475 {
1476 if (rentry->kind == rightkind)
1477 {
1478 if (!missing_ok)
1479 {
1480 ereport(ERROR,
1481 (errcode(rentry->nonexistent_code),
1482 errmsg(rentry->nonexistent_msg, rel->relname)));
1483 }
1484 else
1485 {
1486 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1487 break;
1488 }
1489 }
1490 }
1491
1492 Assert(rentry->kind != '\0'); /* Should be impossible */
1493}
1494
1495/*
1496 * Emit the right error message for a "DROP" command issued on a
1497 * relation of the wrong type
1498 */
1499static void
1500DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1501{
1502 const struct dropmsgstrings *rentry;
1503 const struct dropmsgstrings *wentry;
1504
1505 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1506 if (rentry->kind == rightkind)
1507 break;
1508 Assert(rentry->kind != '\0');
1509
1510 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1511 if (wentry->kind == wrongkind)
1512 break;
1513 /* wrongkind could be something we don't have in our table... */
1514
1515 ereport(ERROR,
1516 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1517 errmsg(rentry->nota_msg, relname),
1518 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1519}
1520
1521/*
1522 * RemoveRelations
1523 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1524 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1525 */
1526void
1528{
1529 ObjectAddresses *objects;
1530 char relkind;
1531 ListCell *cell;
1532 int flags = 0;
1533 LOCKMODE lockmode = AccessExclusiveLock;
1534
1535 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1536 if (drop->concurrent)
1537 {
1538 /*
1539 * Note that for temporary relations this lock may get upgraded later
1540 * on, but as no other session can access a temporary relation, this
1541 * is actually fine.
1542 */
1543 lockmode = ShareUpdateExclusiveLock;
1544 Assert(drop->removeType == OBJECT_INDEX);
1545 if (list_length(drop->objects) != 1)
1546 ereport(ERROR,
1547 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1548 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1549 if (drop->behavior == DROP_CASCADE)
1550 ereport(ERROR,
1551 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1552 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1553 }
1554
1555 /*
1556 * First we identify all the relations, then we delete them in a single
1557 * performMultipleDeletions() call. This is to avoid unwanted DROP
1558 * RESTRICT errors if one of the relations depends on another.
1559 */
1560
1561 /* Determine required relkind */
1562 switch (drop->removeType)
1563 {
1564 case OBJECT_TABLE:
1565 relkind = RELKIND_RELATION;
1566 break;
1567
1568 case OBJECT_INDEX:
1569 relkind = RELKIND_INDEX;
1570 break;
1571
1572 case OBJECT_SEQUENCE:
1573 relkind = RELKIND_SEQUENCE;
1574 break;
1575
1576 case OBJECT_VIEW:
1577 relkind = RELKIND_VIEW;
1578 break;
1579
1580 case OBJECT_MATVIEW:
1581 relkind = RELKIND_MATVIEW;
1582 break;
1583
1585 relkind = RELKIND_FOREIGN_TABLE;
1586 break;
1587
1588 default:
1589 elog(ERROR, "unrecognized drop object type: %d",
1590 (int) drop->removeType);
1591 relkind = 0; /* keep compiler quiet */
1592 break;
1593 }
1594
1595 /* Lock and validate each relation; build a list of object addresses */
1596 objects = new_object_addresses();
1597
1598 foreach(cell, drop->objects)
1599 {
1600 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1601 Oid relOid;
1602 ObjectAddress obj;
1604
1605 /*
1606 * These next few steps are a great deal like relation_openrv, but we
1607 * don't bother building a relcache entry since we don't need it.
1608 *
1609 * Check for shared-cache-inval messages before trying to access the
1610 * relation. This is needed to cover the case where the name
1611 * identifies a rel that has been dropped and recreated since the
1612 * start of our transaction: if we don't flush the old syscache entry,
1613 * then we'll latch onto that entry and suffer an error later.
1614 */
1616
1617 /* Look up the appropriate relation using namespace search. */
1618 state.expected_relkind = relkind;
1619 state.heap_lockmode = drop->concurrent ?
1621 /* We must initialize these fields to show that no locks are held: */
1622 state.heapOid = InvalidOid;
1623 state.partParentOid = InvalidOid;
1624
1625 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1627 &state);
1628
1629 /* Not there? */
1630 if (!OidIsValid(relOid))
1631 {
1632 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1633 continue;
1634 }
1635
1636 /*
1637 * Decide if concurrent mode needs to be used here or not. The
1638 * callback retrieved the rel's persistence for us.
1639 */
1640 if (drop->concurrent &&
1641 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1642 {
1643 Assert(list_length(drop->objects) == 1 &&
1644 drop->removeType == OBJECT_INDEX);
1646 }
1647
1648 /*
1649 * Concurrent index drop cannot be used with partitioned indexes,
1650 * either.
1651 */
1652 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1653 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1654 ereport(ERROR,
1655 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1656 errmsg("cannot drop partitioned index \"%s\" concurrently",
1657 rel->relname)));
1658
1659 /*
1660 * If we're told to drop a partitioned index, we must acquire lock on
1661 * all the children of its parent partitioned table before proceeding.
1662 * Otherwise we'd try to lock the child index partitions before their
1663 * tables, leading to potential deadlock against other sessions that
1664 * will lock those objects in the other order.
1665 */
1666 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1667 (void) find_all_inheritors(state.heapOid,
1668 state.heap_lockmode,
1669 NULL);
1670
1671 /* OK, we're ready to delete this one */
1672 obj.classId = RelationRelationId;
1673 obj.objectId = relOid;
1674 obj.objectSubId = 0;
1675
1676 add_exact_object_address(&obj, objects);
1677 }
1678
1679 performMultipleDeletions(objects, drop->behavior, flags);
1680
1681 free_object_addresses(objects);
1682}
1683
1684/*
1685 * Before acquiring a table lock, check whether we have sufficient rights.
1686 * In the case of DROP INDEX, also try to lock the table before the index.
1687 * Also, if the table to be dropped is a partition, we try to lock the parent
1688 * first.
1689 */
1690static void
1691RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1692 void *arg)
1693{
1694 HeapTuple tuple;
1696 char expected_relkind;
1697 bool is_partition;
1698 Form_pg_class classform;
1700 bool invalid_system_index = false;
1701
1702 state = (struct DropRelationCallbackState *) arg;
1703 heap_lockmode = state->heap_lockmode;
1704
1705 /*
1706 * If we previously locked some other index's heap, and the name we're
1707 * looking up no longer refers to that relation, release the now-useless
1708 * lock.
1709 */
1710 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1711 {
1713 state->heapOid = InvalidOid;
1714 }
1715
1716 /*
1717 * Similarly, if we previously locked some other partition's heap, and the
1718 * name we're looking up no longer refers to that relation, release the
1719 * now-useless lock.
1720 */
1721 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1722 {
1724 state->partParentOid = InvalidOid;
1725 }
1726
1727 /* Didn't find a relation, so no need for locking or permission checks. */
1728 if (!OidIsValid(relOid))
1729 return;
1730
1731 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1732 if (!HeapTupleIsValid(tuple))
1733 return; /* concurrently dropped, so nothing to do */
1734 classform = (Form_pg_class) GETSTRUCT(tuple);
1735 is_partition = classform->relispartition;
1736
1737 /* Pass back some data to save lookups in RemoveRelations */
1738 state->actual_relkind = classform->relkind;
1739 state->actual_relpersistence = classform->relpersistence;
1740
1741 /*
1742 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1743 * but RemoveRelations() can only pass one relkind for a given relation.
1744 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1745 * That means we must be careful before giving the wrong type error when
1746 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1747 * exists with indexes.
1748 */
1749 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1750 expected_relkind = RELKIND_RELATION;
1751 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1752 expected_relkind = RELKIND_INDEX;
1753 else
1754 expected_relkind = classform->relkind;
1755
1756 if (state->expected_relkind != expected_relkind)
1757 DropErrorMsgWrongType(rel->relname, classform->relkind,
1758 state->expected_relkind);
1759
1760 /* Allow DROP to either table owner or schema owner */
1761 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1762 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1764 get_relkind_objtype(classform->relkind),
1765 rel->relname);
1766
1767 /*
1768 * Check the case of a system index that might have been invalidated by a
1769 * failed concurrent process and allow its drop. For the time being, this
1770 * only concerns indexes of toast relations that became invalid during a
1771 * REINDEX CONCURRENTLY process.
1772 */
1773 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1774 {
1775 HeapTuple locTuple;
1776 Form_pg_index indexform;
1777 bool indisvalid;
1778
1779 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1780 if (!HeapTupleIsValid(locTuple))
1781 {
1782 ReleaseSysCache(tuple);
1783 return;
1784 }
1785
1786 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1787 indisvalid = indexform->indisvalid;
1788 ReleaseSysCache(locTuple);
1789
1790 /* Mark object as being an invalid index of system catalogs */
1791 if (!indisvalid)
1792 invalid_system_index = true;
1793 }
1794
1795 /* In the case of an invalid index, it is fine to bypass this check */
1796 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1797 ereport(ERROR,
1798 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1799 errmsg("permission denied: \"%s\" is a system catalog",
1800 rel->relname)));
1801
1802 ReleaseSysCache(tuple);
1803
1804 /*
1805 * In DROP INDEX, attempt to acquire lock on the parent table before
1806 * locking the index. index_drop() will need this anyway, and since
1807 * regular queries lock tables before their indexes, we risk deadlock if
1808 * we do it the other way around. No error if we don't find a pg_index
1809 * entry, though --- the relation may have been dropped. Note that this
1810 * code will execute for either plain or partitioned indexes.
1811 */
1812 if (expected_relkind == RELKIND_INDEX &&
1813 relOid != oldRelOid)
1814 {
1815 state->heapOid = IndexGetRelation(relOid, true);
1816 if (OidIsValid(state->heapOid))
1818 }
1819
1820 /*
1821 * Similarly, if the relation is a partition, we must acquire lock on its
1822 * parent before locking the partition. That's because queries lock the
1823 * parent before its partitions, so we risk deadlock if we do it the other
1824 * way around.
1825 */
1826 if (is_partition && relOid != oldRelOid)
1827 {
1828 state->partParentOid = get_partition_parent(relOid, true);
1829 if (OidIsValid(state->partParentOid))
1830 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1831 }
1832}
1833
1834/*
1835 * ExecuteTruncate
1836 * Executes a TRUNCATE command.
1837 *
1838 * This is a multi-relation truncate. We first open and grab exclusive
1839 * lock on all relations involved, checking permissions and otherwise
1840 * verifying that the relation is OK for truncation. Note that if relations
1841 * are foreign tables, at this stage, we have not yet checked that their
1842 * foreign data in external data sources are OK for truncation. These are
1843 * checked when foreign data are actually truncated later. In CASCADE mode,
1844 * relations having FK references to the targeted relations are automatically
1845 * added to the group; in RESTRICT mode, we check that all FK references are
1846 * internal to the group that's being truncated. Finally all the relations
1847 * are truncated and reindexed.
1848 */
1849void
1851{
1852 List *rels = NIL;
1853 List *relids = NIL;
1854 List *relids_logged = NIL;
1855 ListCell *cell;
1856
1857 /*
1858 * Open, exclusive-lock, and check all the explicitly-specified relations
1859 */
1860 foreach(cell, stmt->relations)
1861 {
1862 RangeVar *rv = lfirst(cell);
1863 Relation rel;
1864 bool recurse = rv->inh;
1865 Oid myrelid;
1866 LOCKMODE lockmode = AccessExclusiveLock;
1867
1868 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1870 NULL);
1871
1872 /* don't throw error for "TRUNCATE foo, foo" */
1873 if (list_member_oid(relids, myrelid))
1874 continue;
1875
1876 /* open the relation, we already hold a lock on it */
1877 rel = table_open(myrelid, NoLock);
1878
1879 /*
1880 * RangeVarGetRelidExtended() has done most checks with its callback,
1881 * but other checks with the now-opened Relation remain.
1882 */
1884
1885 rels = lappend(rels, rel);
1886 relids = lappend_oid(relids, myrelid);
1887
1888 /* Log this relation only if needed for logical decoding */
1890 relids_logged = lappend_oid(relids_logged, myrelid);
1891
1892 if (recurse)
1893 {
1894 ListCell *child;
1895 List *children;
1896
1897 children = find_all_inheritors(myrelid, lockmode, NULL);
1898
1899 foreach(child, children)
1900 {
1901 Oid childrelid = lfirst_oid(child);
1902
1903 if (list_member_oid(relids, childrelid))
1904 continue;
1905
1906 /* find_all_inheritors already got lock */
1907 rel = table_open(childrelid, NoLock);
1908
1909 /*
1910 * It is possible that the parent table has children that are
1911 * temp tables of other backends. We cannot safely access
1912 * such tables (because of buffering issues), and the best
1913 * thing to do is to silently ignore them. Note that this
1914 * check is the same as one of the checks done in
1915 * truncate_check_activity() called below, still it is kept
1916 * here for simplicity.
1917 */
1918 if (RELATION_IS_OTHER_TEMP(rel))
1919 {
1920 table_close(rel, lockmode);
1921 continue;
1922 }
1923
1924 /*
1925 * Inherited TRUNCATE commands perform access permission
1926 * checks on the parent table only. So we skip checking the
1927 * children's permissions and don't call
1928 * truncate_check_perms() here.
1929 */
1932
1933 rels = lappend(rels, rel);
1934 relids = lappend_oid(relids, childrelid);
1935
1936 /* Log this relation only if needed for logical decoding */
1938 relids_logged = lappend_oid(relids_logged, childrelid);
1939 }
1940 }
1941 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1942 ereport(ERROR,
1943 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1944 errmsg("cannot truncate only a partitioned table"),
1945 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1946 }
1947
1948 ExecuteTruncateGuts(rels, relids, relids_logged,
1949 stmt->behavior, stmt->restart_seqs, false);
1950
1951 /* And close the rels */
1952 foreach(cell, rels)
1953 {
1954 Relation rel = (Relation) lfirst(cell);
1955
1956 table_close(rel, NoLock);
1957 }
1958}
1959
1960/*
1961 * ExecuteTruncateGuts
1962 *
1963 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1964 * command (see above) as well as replication subscribers that execute a
1965 * replicated TRUNCATE action.
1966 *
1967 * explicit_rels is the list of Relations to truncate that the command
1968 * specified. relids is the list of Oids corresponding to explicit_rels.
1969 * relids_logged is the list of Oids (a subset of relids) that require
1970 * WAL-logging. This is all a bit redundant, but the existing callers have
1971 * this information handy in this form.
1972 */
1973void
1975 List *relids,
1976 List *relids_logged,
1977 DropBehavior behavior, bool restart_seqs,
1978 bool run_as_table_owner)
1979{
1980 List *rels;
1981 List *seq_relids = NIL;
1982 HTAB *ft_htab = NULL;
1983 EState *estate;
1984 ResultRelInfo *resultRelInfos;
1985 ResultRelInfo *resultRelInfo;
1986 SubTransactionId mySubid;
1987 ListCell *cell;
1988 Oid *logrelids;
1989
1990 /*
1991 * Check the explicitly-specified relations.
1992 *
1993 * In CASCADE mode, suck in all referencing relations as well. This
1994 * requires multiple iterations to find indirectly-dependent relations. At
1995 * each phase, we need to exclusive-lock new rels before looking for their
1996 * dependencies, else we might miss something. Also, we check each rel as
1997 * soon as we open it, to avoid a faux pas such as holding lock for a long
1998 * time on a rel we have no permissions for.
1999 */
2000 rels = list_copy(explicit_rels);
2001 if (behavior == DROP_CASCADE)
2002 {
2003 for (;;)
2004 {
2005 List *newrelids;
2006
2007 newrelids = heap_truncate_find_FKs(relids);
2008 if (newrelids == NIL)
2009 break; /* nothing else to add */
2010
2011 foreach(cell, newrelids)
2012 {
2013 Oid relid = lfirst_oid(cell);
2014 Relation rel;
2015
2016 rel = table_open(relid, AccessExclusiveLock);
2018 (errmsg("truncate cascades to table \"%s\"",
2020 truncate_check_rel(relid, rel->rd_rel);
2021 truncate_check_perms(relid, rel->rd_rel);
2023 rels = lappend(rels, rel);
2024 relids = lappend_oid(relids, relid);
2025
2026 /* Log this relation only if needed for logical decoding */
2028 relids_logged = lappend_oid(relids_logged, relid);
2029 }
2030 }
2031 }
2032
2033 /*
2034 * Check foreign key references. In CASCADE mode, this should be
2035 * unnecessary since we just pulled in all the references; but as a
2036 * cross-check, do it anyway if in an Assert-enabled build.
2037 */
2038#ifdef USE_ASSERT_CHECKING
2039 heap_truncate_check_FKs(rels, false);
2040#else
2041 if (behavior == DROP_RESTRICT)
2042 heap_truncate_check_FKs(rels, false);
2043#endif
2044
2045 /*
2046 * If we are asked to restart sequences, find all the sequences, lock them
2047 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2048 * We want to do this early since it's pointless to do all the truncation
2049 * work only to fail on sequence permissions.
2050 */
2051 if (restart_seqs)
2052 {
2053 foreach(cell, rels)
2054 {
2055 Relation rel = (Relation) lfirst(cell);
2056 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2057 ListCell *seqcell;
2058
2059 foreach(seqcell, seqlist)
2060 {
2061 Oid seq_relid = lfirst_oid(seqcell);
2062 Relation seq_rel;
2063
2064 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2065
2066 /* This check must match AlterSequence! */
2067 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2069 RelationGetRelationName(seq_rel));
2070
2071 seq_relids = lappend_oid(seq_relids, seq_relid);
2072
2073 relation_close(seq_rel, NoLock);
2074 }
2075 }
2076 }
2077
2078 /* Prepare to catch AFTER triggers. */
2080
2081 /*
2082 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2083 * each relation. We don't need to call ExecOpenIndices, though.
2084 *
2085 * We put the ResultRelInfos in the es_opened_result_relations list, even
2086 * though we don't have a range table and don't populate the
2087 * es_result_relations array. That's a bit bogus, but it's enough to make
2088 * ExecGetTriggerResultRel() find them.
2089 */
2090 estate = CreateExecutorState();
2091 resultRelInfos = (ResultRelInfo *)
2092 palloc(list_length(rels) * sizeof(ResultRelInfo));
2093 resultRelInfo = resultRelInfos;
2094 foreach(cell, rels)
2095 {
2096 Relation rel = (Relation) lfirst(cell);
2097
2098 InitResultRelInfo(resultRelInfo,
2099 rel,
2100 0, /* dummy rangetable index */
2101 NULL,
2102 0);
2104 lappend(estate->es_opened_result_relations, resultRelInfo);
2105 resultRelInfo++;
2106 }
2107
2108 /*
2109 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2110 * truncating (this is because one of them might throw an error). Also, if
2111 * we were to allow them to prevent statement execution, that would need
2112 * to be handled here.
2113 */
2114 resultRelInfo = resultRelInfos;
2115 foreach(cell, rels)
2116 {
2117 UserContext ucxt;
2118
2119 if (run_as_table_owner)
2120 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2121 &ucxt);
2122 ExecBSTruncateTriggers(estate, resultRelInfo);
2123 if (run_as_table_owner)
2124 RestoreUserContext(&ucxt);
2125 resultRelInfo++;
2126 }
2127
2128 /*
2129 * OK, truncate each table.
2130 */
2131 mySubid = GetCurrentSubTransactionId();
2132
2133 foreach(cell, rels)
2134 {
2135 Relation rel = (Relation) lfirst(cell);
2136
2137 /* Skip partitioned tables as there is nothing to do */
2138 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2139 continue;
2140
2141 /*
2142 * Build the lists of foreign tables belonging to each foreign server
2143 * and pass each list to the foreign data wrapper's callback function,
2144 * so that each server can truncate its all foreign tables in bulk.
2145 * Each list is saved as a single entry in a hash table that uses the
2146 * server OID as lookup key.
2147 */
2148 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2149 {
2151 bool found;
2152 ForeignTruncateInfo *ft_info;
2153
2154 /* First time through, initialize hashtable for foreign tables */
2155 if (!ft_htab)
2156 {
2157 HASHCTL hctl;
2158
2159 memset(&hctl, 0, sizeof(HASHCTL));
2160 hctl.keysize = sizeof(Oid);
2161 hctl.entrysize = sizeof(ForeignTruncateInfo);
2163
2164 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2165 32, /* start small and extend */
2166 &hctl,
2168 }
2169
2170 /* Find or create cached entry for the foreign table */
2171 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2172 if (!found)
2173 ft_info->rels = NIL;
2174
2175 /*
2176 * Save the foreign table in the entry of the server that the
2177 * foreign table belongs to.
2178 */
2179 ft_info->rels = lappend(ft_info->rels, rel);
2180 continue;
2181 }
2182
2183 /*
2184 * Normally, we need a transaction-safe truncation here. However, if
2185 * the table was either created in the current (sub)transaction or has
2186 * a new relfilenumber in the current (sub)transaction, then we can
2187 * just truncate it in-place, because a rollback would cause the whole
2188 * table or the current physical file to be thrown away anyway.
2189 */
2190 if (rel->rd_createSubid == mySubid ||
2191 rel->rd_newRelfilelocatorSubid == mySubid)
2192 {
2193 /* Immediate, non-rollbackable truncation is OK */
2195 }
2196 else
2197 {
2198 Oid heap_relid;
2199 Oid toast_relid;
2200 ReindexParams reindex_params = {0};
2201
2202 /*
2203 * This effectively deletes all rows in the table, and may be done
2204 * in a serializable transaction. In that case we must record a
2205 * rw-conflict in to this transaction from each transaction
2206 * holding a predicate lock on the table.
2207 */
2209
2210 /*
2211 * Need the full transaction-safe pushups.
2212 *
2213 * Create a new empty storage file for the relation, and assign it
2214 * as the relfilenumber value. The old storage file is scheduled
2215 * for deletion at commit.
2216 */
2217 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2218
2219 heap_relid = RelationGetRelid(rel);
2220
2221 /*
2222 * The same for the toast table, if any.
2223 */
2224 toast_relid = rel->rd_rel->reltoastrelid;
2225 if (OidIsValid(toast_relid))
2226 {
2227 Relation toastrel = relation_open(toast_relid,
2229
2231 toastrel->rd_rel->relpersistence);
2232 table_close(toastrel, NoLock);
2233 }
2234
2235 /*
2236 * Reconstruct the indexes to match, and we're done.
2237 */
2239 &reindex_params);
2240 }
2241
2243 }
2244
2245 /* Now go through the hash table, and truncate foreign tables */
2246 if (ft_htab)
2247 {
2248 ForeignTruncateInfo *ft_info;
2249 HASH_SEQ_STATUS seq;
2250
2251 hash_seq_init(&seq, ft_htab);
2252
2253 PG_TRY();
2254 {
2255 while ((ft_info = hash_seq_search(&seq)) != NULL)
2256 {
2257 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2258
2259 /* truncate_check_rel() has checked that already */
2260 Assert(routine->ExecForeignTruncate != NULL);
2261
2262 routine->ExecForeignTruncate(ft_info->rels,
2263 behavior,
2264 restart_seqs);
2265 }
2266 }
2267 PG_FINALLY();
2268 {
2269 hash_destroy(ft_htab);
2270 }
2271 PG_END_TRY();
2272 }
2273
2274 /*
2275 * Restart owned sequences if we were asked to.
2276 */
2277 foreach(cell, seq_relids)
2278 {
2279 Oid seq_relid = lfirst_oid(cell);
2280
2281 ResetSequence(seq_relid);
2282 }
2283
2284 /*
2285 * Write a WAL record to allow this set of actions to be logically
2286 * decoded.
2287 *
2288 * Assemble an array of relids so we can write a single WAL record for the
2289 * whole action.
2290 */
2291 if (relids_logged != NIL)
2292 {
2293 xl_heap_truncate xlrec;
2294 int i = 0;
2295
2296 /* should only get here if wal_level >= logical */
2298
2299 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2300 foreach(cell, relids_logged)
2301 logrelids[i++] = lfirst_oid(cell);
2302
2303 xlrec.dbId = MyDatabaseId;
2304 xlrec.nrelids = list_length(relids_logged);
2305 xlrec.flags = 0;
2306 if (behavior == DROP_CASCADE)
2307 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2308 if (restart_seqs)
2310
2313 XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2314
2316
2317 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2318 }
2319
2320 /*
2321 * Process all AFTER STATEMENT TRUNCATE triggers.
2322 */
2323 resultRelInfo = resultRelInfos;
2324 foreach(cell, rels)
2325 {
2326 UserContext ucxt;
2327
2328 if (run_as_table_owner)
2329 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2330 &ucxt);
2331 ExecASTruncateTriggers(estate, resultRelInfo);
2332 if (run_as_table_owner)
2333 RestoreUserContext(&ucxt);
2334 resultRelInfo++;
2335 }
2336
2337 /* Handle queued AFTER triggers */
2338 AfterTriggerEndQuery(estate);
2339
2340 /* We can clean up the EState now */
2341 FreeExecutorState(estate);
2342
2343 /*
2344 * Close any rels opened by CASCADE (can't do this while EState still
2345 * holds refs)
2346 */
2347 rels = list_difference_ptr(rels, explicit_rels);
2348 foreach(cell, rels)
2349 {
2350 Relation rel = (Relation) lfirst(cell);
2351
2352 table_close(rel, NoLock);
2353 }
2354}
2355
2356/*
2357 * Check that a given relation is safe to truncate. Subroutine for
2358 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2359 */
2360static void
2362{
2363 char *relname = NameStr(reltuple->relname);
2364
2365 /*
2366 * Only allow truncate on regular tables, foreign tables using foreign
2367 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2368 * latter are only being included here for the following checks; no
2369 * physical truncation will occur in their case.).
2370 */
2371 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2372 {
2373 Oid serverid = GetForeignServerIdByRelId(relid);
2374 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2375
2376 if (!fdwroutine->ExecForeignTruncate)
2377 ereport(ERROR,
2378 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2379 errmsg("cannot truncate foreign table \"%s\"",
2380 relname)));
2381 }
2382 else if (reltuple->relkind != RELKIND_RELATION &&
2383 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2384 ereport(ERROR,
2385 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2386 errmsg("\"%s\" is not a table", relname)));
2387
2388 /*
2389 * Most system catalogs can't be truncated at all, or at least not unless
2390 * allow_system_table_mods=on. As an exception, however, we allow
2391 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2392 * to change its relfilenode to match the old cluster, and allowing a
2393 * TRUNCATE command to be executed is the easiest way of doing that.
2394 */
2395 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2396 && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2397 ereport(ERROR,
2398 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2399 errmsg("permission denied: \"%s\" is a system catalog",
2400 relname)));
2401
2403}
2404
2405/*
2406 * Check that current user has the permission to truncate given relation.
2407 */
2408static void
2410{
2411 char *relname = NameStr(reltuple->relname);
2412 AclResult aclresult;
2413
2414 /* Permissions checks */
2415 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2416 if (aclresult != ACLCHECK_OK)
2417 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2418 relname);
2419}
2420
2421/*
2422 * Set of extra sanity checks to check if a given relation is safe to
2423 * truncate. This is split with truncate_check_rel() as
2424 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2425 */
2426static void
2428{
2429 /*
2430 * Don't allow truncate on temp tables of other backends ... their local
2431 * buffer manager is not going to cope.
2432 */
2433 if (RELATION_IS_OTHER_TEMP(rel))
2434 ereport(ERROR,
2435 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2436 errmsg("cannot truncate temporary tables of other sessions")));
2437
2438 /*
2439 * Also check for active uses of the relation in the current transaction,
2440 * including open scans and pending AFTER trigger events.
2441 */
2442 CheckTableNotInUse(rel, "TRUNCATE");
2443}
2444
2445/*
2446 * storage_name
2447 * returns the name corresponding to a typstorage/attstorage enum value
2448 */
2449static const char *
2451{
2452 switch (c)
2453 {
2454 case TYPSTORAGE_PLAIN:
2455 return "PLAIN";
2456 case TYPSTORAGE_EXTERNAL:
2457 return "EXTERNAL";
2458 case TYPSTORAGE_EXTENDED:
2459 return "EXTENDED";
2460 case TYPSTORAGE_MAIN:
2461 return "MAIN";
2462 default:
2463 return "???";
2464 }
2465}
2466
2467/*----------
2468 * MergeAttributes
2469 * Returns new schema given initial schema and superclasses.
2470 *
2471 * Input arguments:
2472 * 'columns' is the column/attribute definition for the table. (It's a list
2473 * of ColumnDef's.) It is destructively changed.
2474 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2475 * 'relpersistence' is the persistence type of the table.
2476 * 'is_partition' tells if the table is a partition.
2477 *
2478 * Output arguments:
2479 * 'supconstr' receives a list of CookedConstraint representing
2480 * CHECK constraints belonging to parent relations, updated as
2481 * necessary to be valid for the child.
2482 * 'supnotnulls' receives a list of CookedConstraint representing
2483 * not-null constraints based on those from parent relations.
2484 *
2485 * Return value:
2486 * Completed schema list.
2487 *
2488 * Notes:
2489 * The order in which the attributes are inherited is very important.
2490 * Intuitively, the inherited attributes should come first. If a table
2491 * inherits from multiple parents, the order of those attributes are
2492 * according to the order of the parents specified in CREATE TABLE.
2493 *
2494 * Here's an example:
2495 *
2496 * create table person (name text, age int4, location point);
2497 * create table emp (salary int4, manager text) inherits(person);
2498 * create table student (gpa float8) inherits (person);
2499 * create table stud_emp (percent int4) inherits (emp, student);
2500 *
2501 * The order of the attributes of stud_emp is:
2502 *
2503 * person {1:name, 2:age, 3:location}
2504 * / \
2505 * {6:gpa} student emp {4:salary, 5:manager}
2506 * \ /
2507 * stud_emp {7:percent}
2508 *
2509 * If the same attribute name appears multiple times, then it appears
2510 * in the result table in the proper location for its first appearance.
2511 *
2512 * Constraints (including not-null constraints) for the child table
2513 * are the union of all relevant constraints, from both the child schema
2514 * and parent tables. In addition, in legacy inheritance, each column that
2515 * appears in a primary key in any of the parents also gets a NOT NULL
2516 * constraint (partitioning doesn't need this, because the PK itself gets
2517 * inherited.)
2518 *
2519 * The default value for a child column is defined as:
2520 * (1) If the child schema specifies a default, that value is used.
2521 * (2) If neither the child nor any parent specifies a default, then
2522 * the column will not have a default.
2523 * (3) If conflicting defaults are inherited from different parents
2524 * (and not overridden by the child), an error is raised.
2525 * (4) Otherwise the inherited default is used.
2526 *
2527 * Note that the default-value infrastructure is used for generated
2528 * columns' expressions too, so most of the preceding paragraph applies
2529 * to generation expressions too. We insist that a child column be
2530 * generated if and only if its parent(s) are, but it need not have
2531 * the same generation expression.
2532 *----------
2533 */
2534static List *
2535MergeAttributes(List *columns, const List *supers, char relpersistence,
2536 bool is_partition, List **supconstr, List **supnotnulls)
2537{
2538 List *inh_columns = NIL;
2539 List *constraints = NIL;
2540 List *nnconstraints = NIL;
2541 bool have_bogus_defaults = false;
2542 int child_attno;
2543 static Node bogus_marker = {0}; /* marks conflicting defaults */
2544 List *saved_columns = NIL;
2545 ListCell *lc;
2546
2547 /*
2548 * Check for and reject tables with too many columns. We perform this
2549 * check relatively early for two reasons: (a) we don't run the risk of
2550 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2551 * okay if we're processing <= 1600 columns, but could take minutes to
2552 * execute if the user attempts to create a table with hundreds of
2553 * thousands of columns.
2554 *
2555 * Note that we also need to check that we do not exceed this figure after
2556 * including columns from inherited relations.
2557 */
2558 if (list_length(columns) > MaxHeapAttributeNumber)
2559 ereport(ERROR,
2560 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2561 errmsg("tables can have at most %d columns",
2563
2564 /*
2565 * Check for duplicate names in the explicit list of attributes.
2566 *
2567 * Although we might consider merging such entries in the same way that we
2568 * handle name conflicts for inherited attributes, it seems to make more
2569 * sense to assume such conflicts are errors.
2570 *
2571 * We don't use foreach() here because we have two nested loops over the
2572 * columns list, with possible element deletions in the inner one. If we
2573 * used foreach_delete_current() it could only fix up the state of one of
2574 * the loops, so it seems cleaner to use looping over list indexes for
2575 * both loops. Note that any deletion will happen beyond where the outer
2576 * loop is, so its index never needs adjustment.
2577 */
2578 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2579 {
2580 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2581
2582 if (!is_partition && coldef->typeName == NULL)
2583 {
2584 /*
2585 * Typed table column option that does not belong to a column from
2586 * the type. This works because the columns from the type come
2587 * first in the list. (We omit this check for partition column
2588 * lists; those are processed separately below.)
2589 */
2590 ereport(ERROR,
2591 (errcode(ERRCODE_UNDEFINED_COLUMN),
2592 errmsg("column \"%s\" does not exist",
2593 coldef->colname)));
2594 }
2595
2596 /* restpos scans all entries beyond coldef; incr is in loop body */
2597 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2598 {
2599 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2600
2601 if (strcmp(coldef->colname, restdef->colname) == 0)
2602 {
2603 if (coldef->is_from_type)
2604 {
2605 /*
2606 * merge the column options into the column from the type
2607 */
2608 coldef->is_not_null = restdef->is_not_null;
2609 coldef->raw_default = restdef->raw_default;
2610 coldef->cooked_default = restdef->cooked_default;
2611 coldef->constraints = restdef->constraints;
2612 coldef->is_from_type = false;
2613 columns = list_delete_nth_cell(columns, restpos);
2614 }
2615 else
2616 ereport(ERROR,
2617 (errcode(ERRCODE_DUPLICATE_COLUMN),
2618 errmsg("column \"%s\" specified more than once",
2619 coldef->colname)));
2620 }
2621 else
2622 restpos++;
2623 }
2624 }
2625
2626 /*
2627 * In case of a partition, there are no new column definitions, only dummy
2628 * ColumnDefs created for column constraints. Set them aside for now and
2629 * process them at the end.
2630 */
2631 if (is_partition)
2632 {
2633 saved_columns = columns;
2634 columns = NIL;
2635 }
2636
2637 /*
2638 * Scan the parents left-to-right, and merge their attributes to form a
2639 * list of inherited columns (inh_columns).
2640 */
2641 child_attno = 0;
2642 foreach(lc, supers)
2643 {
2644 Oid parent = lfirst_oid(lc);
2645 Relation relation;
2646 TupleDesc tupleDesc;
2647 TupleConstr *constr;
2648 AttrMap *newattmap;
2649 List *inherited_defaults;
2650 List *cols_with_defaults;
2651 List *nnconstrs;
2652 ListCell *lc1;
2653 ListCell *lc2;
2654 Bitmapset *nncols = NULL;
2655
2656 /* caller already got lock */
2657 relation = table_open(parent, NoLock);
2658
2659 /*
2660 * Check for active uses of the parent partitioned table in the
2661 * current transaction, such as being used in some manner by an
2662 * enclosing command.
2663 */
2664 if (is_partition)
2665 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2666
2667 /*
2668 * We do not allow partitioned tables and partitions to participate in
2669 * regular inheritance.
2670 */
2671 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2672 ereport(ERROR,
2673 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2674 errmsg("cannot inherit from partitioned table \"%s\"",
2675 RelationGetRelationName(relation))));
2676 if (relation->rd_rel->relispartition && !is_partition)
2677 ereport(ERROR,
2678 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2679 errmsg("cannot inherit from partition \"%s\"",
2680 RelationGetRelationName(relation))));
2681
2682 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2683 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2684 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2685 ereport(ERROR,
2686 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2687 errmsg("inherited relation \"%s\" is not a table or foreign table",
2688 RelationGetRelationName(relation))));
2689
2690 /*
2691 * If the parent is permanent, so must be all of its partitions. Note
2692 * that inheritance allows that case.
2693 */
2694 if (is_partition &&
2695 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2696 relpersistence == RELPERSISTENCE_TEMP)
2697 ereport(ERROR,
2698 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2699 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2700 RelationGetRelationName(relation))));
2701
2702 /* Permanent rels cannot inherit from temporary ones */
2703 if (relpersistence != RELPERSISTENCE_TEMP &&
2704 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2705 ereport(ERROR,
2706 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2707 errmsg(!is_partition
2708 ? "cannot inherit from temporary relation \"%s\""
2709 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2710 RelationGetRelationName(relation))));
2711
2712 /* If existing rel is temp, it must belong to this session */
2713 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2714 !relation->rd_islocaltemp)
2715 ereport(ERROR,
2716 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2717 errmsg(!is_partition
2718 ? "cannot inherit from temporary relation of another session"
2719 : "cannot create as partition of temporary relation of another session")));
2720
2721 /*
2722 * We should have an UNDER permission flag for this, but for now,
2723 * demand that creator of a child table own the parent.
2724 */
2725 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2727 RelationGetRelationName(relation));
2728
2729 tupleDesc = RelationGetDescr(relation);
2730 constr = tupleDesc->constr;
2731
2732 /*
2733 * newattmap->attnums[] will contain the child-table attribute numbers
2734 * for the attributes of this parent table. (They are not the same
2735 * for parents after the first one, nor if we have dropped columns.)
2736 */
2737 newattmap = make_attrmap(tupleDesc->natts);
2738
2739 /* We can't process inherited defaults until newattmap is complete. */
2740 inherited_defaults = cols_with_defaults = NIL;
2741
2742 /*
2743 * Request attnotnull on columns that have a not-null constraint
2744 * that's not marked NO INHERIT (even if not valid).
2745 */
2746 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2747 true, false);
2748 foreach_ptr(CookedConstraint, cc, nnconstrs)
2749 nncols = bms_add_member(nncols, cc->attnum);
2750
2751 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2752 parent_attno++)
2753 {
2754 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2755 parent_attno - 1);
2756 char *attributeName = NameStr(attribute->attname);
2757 int exist_attno;
2758 ColumnDef *newdef;
2759 ColumnDef *mergeddef;
2760
2761 /*
2762 * Ignore dropped columns in the parent.
2763 */
2764 if (attribute->attisdropped)
2765 continue; /* leave newattmap->attnums entry as zero */
2766
2767 /*
2768 * Create new column definition
2769 */
2770 newdef = makeColumnDef(attributeName, attribute->atttypid,
2771 attribute->atttypmod, attribute->attcollation);
2772 newdef->storage = attribute->attstorage;
2773 newdef->generated = attribute->attgenerated;
2774 if (CompressionMethodIsValid(attribute->attcompression))
2775 newdef->compression =
2776 pstrdup(GetCompressionMethodName(attribute->attcompression));
2777
2778 /*
2779 * Regular inheritance children are independent enough not to
2780 * inherit identity columns. But partitions are integral part of
2781 * a partitioned table and inherit identity column.
2782 */
2783 if (is_partition)
2784 newdef->identity = attribute->attidentity;
2785
2786 /*
2787 * Does it match some previously considered column from another
2788 * parent?
2789 */
2790 exist_attno = findAttrByName(attributeName, inh_columns);
2791 if (exist_attno > 0)
2792 {
2793 /*
2794 * Yes, try to merge the two column definitions.
2795 */
2796 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2797
2798 newattmap->attnums[parent_attno - 1] = exist_attno;
2799
2800 /*
2801 * Partitions have only one parent, so conflict should never
2802 * occur.
2803 */
2804 Assert(!is_partition);
2805 }
2806 else
2807 {
2808 /*
2809 * No, create a new inherited column
2810 */
2811 newdef->inhcount = 1;
2812 newdef->is_local = false;
2813 inh_columns = lappend(inh_columns, newdef);
2814
2815 newattmap->attnums[parent_attno - 1] = ++child_attno;
2816 mergeddef = newdef;
2817 }
2818
2819 /*
2820 * mark attnotnull if parent has it
2821 */
2822 if (bms_is_member(parent_attno, nncols))
2823 mergeddef->is_not_null = true;
2824
2825 /*
2826 * Locate default/generation expression if any
2827 */
2828 if (attribute->atthasdef)
2829 {
2830 Node *this_default;
2831
2832 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2833 if (this_default == NULL)
2834 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2835 parent_attno, RelationGetRelationName(relation));
2836
2837 /*
2838 * If it's a GENERATED default, it might contain Vars that
2839 * need to be mapped to the inherited column(s)' new numbers.
2840 * We can't do that till newattmap is ready, so just remember
2841 * all the inherited default expressions for the moment.
2842 */
2843 inherited_defaults = lappend(inherited_defaults, this_default);
2844 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2845 }
2846 }
2847
2848 /*
2849 * Now process any inherited default expressions, adjusting attnos
2850 * using the completed newattmap map.
2851 */
2852 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2853 {
2854 Node *this_default = (Node *) lfirst(lc1);
2855 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2856 bool found_whole_row;
2857
2858 /* Adjust Vars to match new table's column numbering */
2859 this_default = map_variable_attnos(this_default,
2860 1, 0,
2861 newattmap,
2862 InvalidOid, &found_whole_row);
2863
2864 /*
2865 * For the moment we have to reject whole-row variables. We could
2866 * convert them, if we knew the new table's rowtype OID, but that
2867 * hasn't been assigned yet. (A variable could only appear in a
2868 * generation expression, so the error message is correct.)
2869 */
2870 if (found_whole_row)
2871 ereport(ERROR,
2872 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2873 errmsg("cannot convert whole-row table reference"),
2874 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2875 def->colname,
2876 RelationGetRelationName(relation))));
2877
2878 /*
2879 * If we already had a default from some prior parent, check to
2880 * see if they are the same. If so, no problem; if not, mark the
2881 * column as having a bogus default. Below, we will complain if
2882 * the bogus default isn't overridden by the child columns.
2883 */
2884 Assert(def->raw_default == NULL);
2885 if (def->cooked_default == NULL)
2886 def->cooked_default = this_default;
2887 else if (!equal(def->cooked_default, this_default))
2888 {
2889 def->cooked_default = &bogus_marker;
2890 have_bogus_defaults = true;
2891 }
2892 }
2893
2894 /*
2895 * Now copy the CHECK constraints of this parent, adjusting attnos
2896 * using the completed newattmap map. Identically named constraints
2897 * are merged if possible, else we throw error.
2898 */
2899 if (constr && constr->num_check > 0)
2900 {
2901 ConstrCheck *check = constr->check;
2902
2903 for (int i = 0; i < constr->num_check; i++)
2904 {
2905 char *name = check[i].ccname;
2906 Node *expr;
2907 bool found_whole_row;
2908
2909 /* ignore if the constraint is non-inheritable */
2910 if (check[i].ccnoinherit)
2911 continue;
2912
2913 /* Adjust Vars to match new table's column numbering */
2914 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2915 1, 0,
2916 newattmap,
2917 InvalidOid, &found_whole_row);
2918
2919 /*
2920 * For the moment we have to reject whole-row variables. We
2921 * could convert them, if we knew the new table's rowtype OID,
2922 * but that hasn't been assigned yet.
2923 */
2924 if (found_whole_row)
2925 ereport(ERROR,
2926 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2927 errmsg("cannot convert whole-row table reference"),
2928 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2929 name,
2930 RelationGetRelationName(relation))));
2931
2932 constraints = MergeCheckConstraint(constraints, name, expr,
2933 check[i].ccenforced);
2934 }
2935 }
2936
2937 /*
2938 * Also copy the not-null constraints from this parent. The
2939 * attnotnull markings were already installed above.
2940 */
2941 foreach_ptr(CookedConstraint, nn, nnconstrs)
2942 {
2943 Assert(nn->contype == CONSTR_NOTNULL);
2944
2945 nn->attnum = newattmap->attnums[nn->attnum - 1];
2946
2947 nnconstraints = lappend(nnconstraints, nn);
2948 }
2949
2950 free_attrmap(newattmap);
2951
2952 /*
2953 * Close the parent rel, but keep our lock on it until xact commit.
2954 * That will prevent someone else from deleting or ALTERing the parent
2955 * before the child is committed.
2956 */
2957 table_close(relation, NoLock);
2958 }
2959
2960 /*
2961 * If we had no inherited attributes, the result columns are just the
2962 * explicitly declared columns. Otherwise, we need to merge the declared
2963 * columns into the inherited column list. Although, we never have any
2964 * explicitly declared columns if the table is a partition.
2965 */
2966 if (inh_columns != NIL)
2967 {
2968 int newcol_attno = 0;
2969
2970 foreach(lc, columns)
2971 {
2972 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2973 char *attributeName = newdef->colname;
2974 int exist_attno;
2975
2976 /*
2977 * Partitions have only one parent and have no column definitions
2978 * of their own, so conflict should never occur.
2979 */
2980 Assert(!is_partition);
2981
2982 newcol_attno++;
2983
2984 /*
2985 * Does it match some inherited column?
2986 */
2987 exist_attno = findAttrByName(attributeName, inh_columns);
2988 if (exist_attno > 0)
2989 {
2990 /*
2991 * Yes, try to merge the two column definitions.
2992 */
2993 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2994 }
2995 else
2996 {
2997 /*
2998 * No, attach new column unchanged to result columns.
2999 */
3000 inh_columns = lappend(inh_columns, newdef);
3001 }
3002 }
3003
3004 columns = inh_columns;
3005
3006 /*
3007 * Check that we haven't exceeded the legal # of columns after merging
3008 * in inherited columns.
3009 */
3010 if (list_length(columns) > MaxHeapAttributeNumber)
3011 ereport(ERROR,
3012 (errcode(ERRCODE_TOO_MANY_COLUMNS),
3013 errmsg("tables can have at most %d columns",
3015 }
3016
3017 /*
3018 * Now that we have the column definition list for a partition, we can
3019 * check whether the columns referenced in the column constraint specs
3020 * actually exist. Also, merge column defaults.
3021 */
3022 if (is_partition)
3023 {
3024 foreach(lc, saved_columns)
3025 {
3026 ColumnDef *restdef = lfirst(lc);
3027 bool found = false;
3028 ListCell *l;
3029
3030 foreach(l, columns)
3031 {
3032 ColumnDef *coldef = lfirst(l);
3033
3034 if (strcmp(coldef->colname, restdef->colname) == 0)
3035 {
3036 found = true;
3037
3038 /*
3039 * Check for conflicts related to generated columns.
3040 *
3041 * Same rules as above: generated-ness has to match the
3042 * parent, but the contents of the generation expression
3043 * can be different.
3044 */
3045 if (coldef->generated)
3046 {
3047 if (restdef->raw_default && !restdef->generated)
3048 ereport(ERROR,
3049 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3050 errmsg("column \"%s\" inherits from generated column but specifies default",
3051 restdef->colname)));
3052 if (restdef->identity)
3053 ereport(ERROR,
3054 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3055 errmsg("column \"%s\" inherits from generated column but specifies identity",
3056 restdef->colname)));
3057 }
3058 else
3059 {
3060 if (restdef->generated)
3061 ereport(ERROR,
3062 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3063 errmsg("child column \"%s\" specifies generation expression",
3064 restdef->colname),
3065 errhint("A child table column cannot be generated unless its parent column is.")));
3066 }
3067
3068 if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3069 ereport(ERROR,
3070 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3071 errmsg("column \"%s\" inherits from generated column of different kind",
3072 restdef->colname),
3073 errdetail("Parent column is %s, child column is %s.",
3074 coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3075 restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3076
3077 /*
3078 * Override the parent's default value for this column
3079 * (coldef->cooked_default) with the partition's local
3080 * definition (restdef->raw_default), if there's one. It
3081 * should be physically impossible to get a cooked default
3082 * in the local definition or a raw default in the
3083 * inherited definition, but make sure they're nulls, for
3084 * future-proofing.
3085 */
3086 Assert(restdef->cooked_default == NULL);
3087 Assert(coldef->raw_default == NULL);
3088 if (restdef->raw_default)
3089 {
3090 coldef->raw_default = restdef->raw_default;
3091 coldef->cooked_default = NULL;
3092 }
3093 }
3094 }
3095
3096 /* complain for constraints on columns not in parent */
3097 if (!found)
3098 ereport(ERROR,
3099 (errcode(ERRCODE_UNDEFINED_COLUMN),
3100 errmsg("column \"%s\" does not exist",
3101 restdef->colname)));
3102 }
3103 }
3104
3105 /*
3106 * If we found any conflicting parent default values, check to make sure
3107 * they were overridden by the child.
3108 */
3109 if (have_bogus_defaults)
3110 {
3111 foreach(lc, columns)
3112 {
3113 ColumnDef *def = lfirst(lc);
3114
3115 if (def->cooked_default == &bogus_marker)
3116 {
3117 if (def->generated)
3118 ereport(ERROR,
3119 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3120 errmsg("column \"%s\" inherits conflicting generation expressions",
3121 def->colname),
3122 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3123 else
3124 ereport(ERROR,
3125 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3126 errmsg("column \"%s\" inherits conflicting default values",
3127 def->colname),
3128 errhint("To resolve the conflict, specify a default explicitly.")));
3129 }
3130 }
3131 }
3132
3133 *supconstr = constraints;
3134 *supnotnulls = nnconstraints;
3135
3136 return columns;
3137}
3138
3139
3140/*
3141 * MergeCheckConstraint
3142 * Try to merge an inherited CHECK constraint with previous ones
3143 *
3144 * If we inherit identically-named constraints from multiple parents, we must
3145 * merge them, or throw an error if they don't have identical definitions.
3146 *
3147 * constraints is a list of CookedConstraint structs for previous constraints.
3148 *
3149 * If the new constraint matches an existing one, then the existing
3150 * constraint's inheritance count is updated. If there is a conflict (same
3151 * name but different expression), throw an error. If the constraint neither
3152 * matches nor conflicts with an existing one, a new constraint is appended to
3153 * the list.
3154 */
3155static List *
3156MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3157{
3158 ListCell *lc;
3159 CookedConstraint *newcon;
3160
3161 foreach(lc, constraints)
3162 {
3164
3165 Assert(ccon->contype == CONSTR_CHECK);
3166
3167 /* Non-matching names never conflict */
3168 if (strcmp(ccon->name, name) != 0)
3169 continue;
3170
3171 if (equal(expr, ccon->expr))
3172 {
3173 /* OK to merge constraint with existing */
3174 if (pg_add_s16_overflow(ccon->inhcount, 1,
3175 &ccon->inhcount))
3176 ereport(ERROR,
3177 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3178 errmsg("too many inheritance parents"));
3179
3180 /*
3181 * When enforceability differs, the merged constraint should be
3182 * marked as ENFORCED because one of the parents is ENFORCED.
3183 */
3184 if (!ccon->is_enforced && is_enforced)
3185 {
3186 ccon->is_enforced = true;
3187 ccon->skip_validation = false;
3188 }
3189
3190 return constraints;
3191 }
3192
3193 ereport(ERROR,
3195 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3196 name)));
3197 }
3198
3199 /*
3200 * Constraint couldn't be merged with an existing one and also didn't
3201 * conflict with an existing one, so add it as a new one to the list.
3202 */
3204 newcon->contype = CONSTR_CHECK;
3205 newcon->name = pstrdup(name);
3206 newcon->expr = expr;
3207 newcon->inhcount = 1;
3208 newcon->is_enforced = is_enforced;
3209 newcon->skip_validation = !is_enforced;
3210 return lappend(constraints, newcon);
3211}
3212
3213/*
3214 * MergeChildAttribute
3215 * Merge given child attribute definition into given inherited attribute.
3216 *
3217 * Input arguments:
3218 * 'inh_columns' is the list of inherited ColumnDefs.
3219 * 'exist_attno' is the number of the inherited attribute in inh_columns
3220 * 'newcol_attno' is the attribute number in child table's schema definition
3221 * 'newdef' is the column/attribute definition from the child table.
3222 *
3223 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3224 * ColumnDef remains unchanged.
3225 *
3226 * Notes:
3227 * - The attribute is merged according to the rules laid out in the prologue
3228 * of MergeAttributes().
3229 * - If matching inherited attribute exists but the child attribute can not be
3230 * merged into it, the function throws respective errors.
3231 * - A partition can not have its own column definitions. Hence this function
3232 * is applicable only to a regular inheritance child.
3233 */
3234static void
3235MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3236{
3237 char *attributeName = newdef->colname;
3238 ColumnDef *inhdef;
3239 Oid inhtypeid,
3240 newtypeid;
3241 int32 inhtypmod,
3242 newtypmod;
3243 Oid inhcollid,
3244 newcollid;
3245
3246 if (exist_attno == newcol_attno)
3248 (errmsg("merging column \"%s\" with inherited definition",
3249 attributeName)));
3250 else
3252 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3253 errdetail("User-specified column moved to the position of the inherited column.")));
3254
3255 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3256
3257 /*
3258 * Must have the same type and typmod
3259 */
3260 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3261 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3262 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3263 ereport(ERROR,
3264 (errcode(ERRCODE_DATATYPE_MISMATCH),
3265 errmsg("column \"%s\" has a type conflict",
3266 attributeName),
3267 errdetail("%s versus %s",
3268 format_type_with_typemod(inhtypeid, inhtypmod),
3269 format_type_with_typemod(newtypeid, newtypmod))));
3270
3271 /*
3272 * Must have the same collation
3273 */
3274 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3275 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3276 if (inhcollid != newcollid)
3277 ereport(ERROR,
3278 (errcode(ERRCODE_COLLATION_MISMATCH),
3279 errmsg("column \"%s\" has a collation conflict",
3280 attributeName),
3281 errdetail("\"%s\" versus \"%s\"",
3282 get_collation_name(inhcollid),
3283 get_collation_name(newcollid))));
3284
3285 /*
3286 * Identity is never inherited by a regular inheritance child. Pick
3287 * child's identity definition if there's one.
3288 */
3289 inhdef->identity = newdef->identity;
3290
3291 /*
3292 * Copy storage parameter
3293 */
3294 if (inhdef->storage == 0)
3295 inhdef->storage = newdef->storage;
3296 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3297 ereport(ERROR,
3298 (errcode(ERRCODE_DATATYPE_MISMATCH),
3299 errmsg("column \"%s\" has a storage parameter conflict",
3300 attributeName),
3301 errdetail("%s versus %s",
3302 storage_name(inhdef->storage),
3303 storage_name(newdef->storage))));
3304
3305 /*
3306 * Copy compression parameter
3307 */
3308 if (inhdef->compression == NULL)
3309 inhdef->compression = newdef->compression;
3310 else if (newdef->compression != NULL)
3311 {
3312 if (strcmp(inhdef->compression, newdef->compression) != 0)
3313 ereport(ERROR,
3314 (errcode(ERRCODE_DATATYPE_MISMATCH),
3315 errmsg("column \"%s\" has a compression method conflict",
3316 attributeName),
3317 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3318 }
3319
3320 /*
3321 * Merge of not-null constraints = OR 'em together
3322 */
3323 inhdef->is_not_null |= newdef->is_not_null;
3324
3325 /*
3326 * Check for conflicts related to generated columns.
3327 *
3328 * If the parent column is generated, the child column will be made a
3329 * generated column if it isn't already. If it is a generated column,
3330 * we'll take its generation expression in preference to the parent's. We
3331 * must check that the child column doesn't specify a default value or
3332 * identity, which matches the rules for a single column in
3333 * parse_utilcmd.c.
3334 *
3335 * Conversely, if the parent column is not generated, the child column
3336 * can't be either. (We used to allow that, but it results in being able
3337 * to override the generation expression via UPDATEs through the parent.)
3338 */
3339 if (inhdef->generated)
3340 {
3341 if (newdef->raw_default && !newdef->generated)
3342 ereport(ERROR,
3343 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3344 errmsg("column \"%s\" inherits from generated column but specifies default",
3345 inhdef->colname)));
3346 if (newdef->identity)
3347 ereport(ERROR,
3348 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3349 errmsg("column \"%s\" inherits from generated column but specifies identity",
3350 inhdef->colname)));
3351 }
3352 else
3353 {
3354 if (newdef->generated)
3355 ereport(ERROR,
3356 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3357 errmsg("child column \"%s\" specifies generation expression",
3358 inhdef->colname),
3359 errhint("A child table column cannot be generated unless its parent column is.")));
3360 }
3361
3362 if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3363 ereport(ERROR,
3364 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3365 errmsg("column \"%s\" inherits from generated column of different kind",
3366 inhdef->colname),
3367 errdetail("Parent column is %s, child column is %s.",
3368 inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3369 newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3370
3371 /*
3372 * If new def has a default, override previous default
3373 */
3374 if (newdef->raw_default != NULL)
3375 {
3376 inhdef->raw_default = newdef->raw_default;
3377 inhdef->cooked_default = newdef->cooked_default;
3378 }
3379
3380 /* Mark the column as locally defined */
3381 inhdef->is_local = true;
3382}
3383
3384/*
3385 * MergeInheritedAttribute
3386 * Merge given parent attribute definition into specified attribute
3387 * inherited from the previous parents.
3388 *
3389 * Input arguments:
3390 * 'inh_columns' is the list of previously inherited ColumnDefs.
3391 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3392 * 'newdef' is the new parent column/attribute definition to be merged.
3393 *
3394 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3395 *
3396 * Notes:
3397 * - The attribute is merged according to the rules laid out in the prologue
3398 * of MergeAttributes().
3399 * - If matching inherited attribute exists but the new attribute can not be
3400 * merged into it, the function throws respective errors.
3401 * - A partition inherits from only a single parent. Hence this function is
3402 * applicable only to a regular inheritance.
3403 */
3404static ColumnDef *
3406 int exist_attno,
3407 const ColumnDef *newdef)
3408{
3409 char *attributeName = newdef->colname;
3410 ColumnDef *prevdef;
3411 Oid prevtypeid,
3412 newtypeid;
3413 int32 prevtypmod,
3414 newtypmod;
3415 Oid prevcollid,
3416 newcollid;
3417
3419 (errmsg("merging multiple inherited definitions of column \"%s\"",
3420 attributeName)));
3421 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3422
3423 /*
3424 * Must have the same type and typmod
3425 */
3426 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3427 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3428 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3429 ereport(ERROR,
3430 (errcode(ERRCODE_DATATYPE_MISMATCH),
3431 errmsg("inherited column \"%s\" has a type conflict",
3432 attributeName),
3433 errdetail("%s versus %s",
3434 format_type_with_typemod(prevtypeid, prevtypmod),
3435 format_type_with_typemod(newtypeid, newtypmod))));
3436
3437 /*
3438 * Must have the same collation
3439 */
3440 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3441 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3442 if (prevcollid != newcollid)
3443 ereport(ERROR,
3444 (errcode(ERRCODE_COLLATION_MISMATCH),
3445 errmsg("inherited column \"%s\" has a collation conflict",
3446 attributeName),
3447 errdetail("\"%s\" versus \"%s\"",
3448 get_collation_name(prevcollid),
3449 get_collation_name(newcollid))));
3450
3451 /*
3452 * Copy/check storage parameter
3453 */
3454 if (prevdef->storage == 0)
3455 prevdef->storage = newdef->storage;
3456 else if (prevdef->storage != newdef->storage)
3457 ereport(ERROR,
3458 (errcode(ERRCODE_DATATYPE_MISMATCH),
3459 errmsg("inherited column \"%s\" has a storage parameter conflict",
3460 attributeName),
3461 errdetail("%s versus %s",
3462 storage_name(prevdef->storage),
3463 storage_name(newdef->storage))));
3464
3465 /*
3466 * Copy/check compression parameter
3467 */
3468 if (prevdef->compression == NULL)
3469 prevdef->compression = newdef->compression;
3470 else if (newdef->compression != NULL)
3471 {
3472 if (strcmp(prevdef->compression, newdef->compression) != 0)
3473 ereport(ERROR,
3474 (errcode(ERRCODE_DATATYPE_MISMATCH),
3475 errmsg("column \"%s\" has a compression method conflict",
3476 attributeName),
3477 errdetail("%s versus %s",
3478 prevdef->compression, newdef->compression)));
3479 }
3480
3481 /*
3482 * Check for GENERATED conflicts
3483 */
3484 if (prevdef->generated != newdef->generated)
3485 ereport(ERROR,
3486 (errcode(ERRCODE_DATATYPE_MISMATCH),
3487 errmsg("inherited column \"%s\" has a generation conflict",
3488 attributeName)));
3489
3490 /*
3491 * Default and other constraints are handled by the caller.
3492 */
3493
3494 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3495 &prevdef->inhcount))
3496 ereport(ERROR,
3497 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3498 errmsg("too many inheritance parents"));
3499
3500 return prevdef;
3501}
3502
3503/*
3504 * StoreCatalogInheritance
3505 * Updates the system catalogs with proper inheritance information.
3506 *
3507 * supers is a list of the OIDs of the new relation's direct ancestors.
3508 */
3509static void
3511 bool child_is_partition)
3512{
3513 Relation relation;
3514 int32 seqNumber;
3515 ListCell *entry;
3516
3517 /*
3518 * sanity checks
3519 */
3520 Assert(OidIsValid(relationId));
3521
3522 if (supers == NIL)
3523 return;
3524
3525 /*
3526 * Store INHERITS information in pg_inherits using direct ancestors only.
3527 * Also enter dependencies on the direct ancestors, and make sure they are
3528 * marked with relhassubclass = true.
3529 *
3530 * (Once upon a time, both direct and indirect ancestors were found here
3531 * and then entered into pg_ipl. Since that catalog doesn't exist
3532 * anymore, there's no need to look for indirect ancestors.)
3533 */
3534 relation = table_open(InheritsRelationId, RowExclusiveLock);
3535
3536 seqNumber = 1;
3537 foreach(entry, supers)
3538 {
3539 Oid parentOid = lfirst_oid(entry);
3540
3541 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3542 child_is_partition);
3543 seqNumber++;
3544 }
3545
3546 table_close(relation, RowExclusiveLock);
3547}
3548
3549/*
3550 * Make catalog entries showing relationId as being an inheritance child
3551 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3552 */
3553static void
3554StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3555 int32 seqNumber, Relation inhRelation,
3556 bool child_is_partition)
3557{
3558 ObjectAddress childobject,
3559 parentobject;
3560
3561 /* store the pg_inherits row */
3562 StoreSingleInheritance(relationId, parentOid, seqNumber);
3563
3564 /*
3565 * Store a dependency too
3566 */
3567 parentobject.classId = RelationRelationId;
3568 parentobject.objectId = parentOid;
3569 parentobject.objectSubId = 0;
3570 childobject.classId = RelationRelationId;
3571 childobject.objectId = relationId;
3572 childobject.objectSubId = 0;
3573
3574 recordDependencyOn(&childobject, &parentobject,
3575 child_dependency_type(child_is_partition));
3576
3577 /*
3578 * Post creation hook of this inheritance. Since object_access_hook
3579 * doesn't take multiple object identifiers, we relay oid of parent
3580 * relation using auxiliary_id argument.
3581 */
3582 InvokeObjectPostAlterHookArg(InheritsRelationId,
3583 relationId, 0,
3584 parentOid, false);
3585
3586 /*
3587 * Mark the parent as having subclasses.
3588 */
3589 SetRelationHasSubclass(parentOid, true);
3590}
3591
3592/*
3593 * Look for an existing column entry with the given name.
3594 *
3595 * Returns the index (starting with 1) if attribute already exists in columns,
3596 * 0 if it doesn't.
3597 */
3598static int
3599findAttrByName(const char *attributeName, const List *columns)
3600{
3601 ListCell *lc;
3602 int i = 1;
3603
3604 foreach(lc, columns)
3605 {
3606 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3607 return i;
3608
3609 i++;
3610 }
3611 return 0;
3612}
3613
3614
3615/*
3616 * SetRelationHasSubclass
3617 * Set the value of the relation's relhassubclass field in pg_class.
3618 *
3619 * It's always safe to set this field to true, because all SQL commands are
3620 * ready to see true and then find no children. On the other hand, commands
3621 * generally assume zero children if this is false.
3622 *
3623 * Caller must hold any self-exclusive lock until end of transaction. If the
3624 * new value is false, caller must have acquired that lock before reading the
3625 * evidence that justified the false value. That way, it properly waits if
3626 * another backend is simultaneously concluding no need to change the tuple
3627 * (new and old values are true).
3628 *
3629 * NOTE: an important side-effect of this operation is that an SI invalidation
3630 * message is sent out to all backends --- including me --- causing plans
3631 * referencing the relation to be rebuilt with the new list of children.
3632 * This must happen even if we find that no change is needed in the pg_class
3633 * row.
3634 */
3635void
3636SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3637{
3638 Relation relationRelation;
3639 HeapTuple tuple;
3640 Form_pg_class classtuple;
3641
3643 ShareUpdateExclusiveLock, false) ||
3644 CheckRelationOidLockedByMe(relationId,
3645 ShareRowExclusiveLock, true));
3646
3647 /*
3648 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3649 */
3650 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3651 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3652 if (!HeapTupleIsValid(tuple))
3653 elog(ERROR, "cache lookup failed for relation %u", relationId);
3654 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3655
3656 if (classtuple->relhassubclass != relhassubclass)
3657 {
3658 classtuple->relhassubclass = relhassubclass;
3659 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3660 }
3661 else
3662 {
3663 /* no need to change tuple, but force relcache rebuild anyway */
3665 }
3666
3667 heap_freetuple(tuple);
3668 table_close(relationRelation, RowExclusiveLock);
3669}
3670
3671/*
3672 * CheckRelationTableSpaceMove
3673 * Check if relation can be moved to new tablespace.
3674 *
3675 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3676 *
3677 * Returns true if the relation can be moved to the new tablespace; raises
3678 * an error if it is not possible to do the move; returns false if the move
3679 * would have no effect.
3680 */
3681bool
3683{
3684 Oid oldTableSpaceId;
3685
3686 /*
3687 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3688 * stored as 0.
3689 */
3690 oldTableSpaceId = rel->rd_rel->reltablespace;
3691 if (newTableSpaceId == oldTableSpaceId ||
3692 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3693 return false;
3694
3695 /*
3696 * We cannot support moving mapped relations into different tablespaces.
3697 * (In particular this eliminates all shared catalogs.)
3698 */
3699 if (RelationIsMapped(rel))
3700 ereport(ERROR,
3701 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3702 errmsg("cannot move system relation \"%s\"",
3704
3705 /* Cannot move a non-shared relation into pg_global */
3706 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3707 ereport(ERROR,
3708 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3709 errmsg("only shared relations can be placed in pg_global tablespace")));
3710
3711 /*
3712 * Do not allow moving temp tables of other backends ... their local
3713 * buffer manager is not going to cope.
3714 */
3715 if (RELATION_IS_OTHER_TEMP(rel))
3716 ereport(ERROR,
3717 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3718 errmsg("cannot move temporary tables of other sessions")));
3719
3720 return true;
3721}
3722
3723/*
3724 * SetRelationTableSpace
3725 * Set new reltablespace and relfilenumber in pg_class entry.
3726 *
3727 * newTableSpaceId is the new tablespace for the relation, and
3728 * newRelFilenumber its new filenumber. If newRelFilenumber is
3729 * InvalidRelFileNumber, this field is not updated.
3730 *
3731 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3732 *
3733 * The caller of this routine had better check if a relation can be
3734 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3735 * first, and is responsible for making the change visible with
3736 * CommandCounterIncrement().
3737 */
3738void
3740 Oid newTableSpaceId,
3741 RelFileNumber newRelFilenumber)
3742{
3743 Relation pg_class;
3744 HeapTuple tuple;
3745 ItemPointerData otid;
3746 Form_pg_class rd_rel;
3747 Oid reloid = RelationGetRelid(rel);
3748
3749 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3750
3751 /* Get a modifiable copy of the relation's pg_class row. */
3752 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3753
3754 tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3755 if (!HeapTupleIsValid(tuple))
3756 elog(ERROR, "cache lookup failed for relation %u", reloid);
3757 otid = tuple->t_self;
3758 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3759
3760 /* Update the pg_class row. */
3761 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3762 InvalidOid : newTableSpaceId;
3763 if (RelFileNumberIsValid(newRelFilenumber))
3764 rd_rel->relfilenode = newRelFilenumber;
3765 CatalogTupleUpdate(pg_class, &otid, tuple);
3766 UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3767
3768 /*
3769 * Record dependency on tablespace. This is only required for relations
3770 * that have no physical storage.
3771 */
3772 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3773 changeDependencyOnTablespace(RelationRelationId, reloid,
3774 rd_rel->reltablespace);
3775
3776 heap_freetuple(tuple);
3777 table_close(pg_class, RowExclusiveLock);
3778}
3779
3780/*
3781 * renameatt_check - basic sanity checks before attribute rename
3782 */
3783static void
3784renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3785{
3786 char relkind = classform->relkind;
3787
3788 if (classform->reloftype && !recursing)
3789 ereport(ERROR,
3790 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3791 errmsg("cannot rename column of typed table")));
3792
3793 /*
3794 * Renaming the columns of sequences or toast tables doesn't actually
3795 * break anything from the system's point of view, since internal
3796 * references are by attnum. But it doesn't seem right to allow users to
3797 * change names that are hardcoded into the system, hence the following
3798 * restriction.
3799 */
3800 if (relkind != RELKIND_RELATION &&
3801 relkind != RELKIND_VIEW &&
3802 relkind != RELKIND_MATVIEW &&
3803 relkind != RELKIND_COMPOSITE_TYPE &&
3804 relkind != RELKIND_INDEX &&
3805 relkind != RELKIND_PARTITIONED_INDEX &&
3806 relkind != RELKIND_FOREIGN_TABLE &&
3807 relkind != RELKIND_PARTITIONED_TABLE)
3808 ereport(ERROR,
3809 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3810 errmsg("cannot rename columns of relation \"%s\"",
3811 NameStr(classform->relname)),
3813
3814 /*
3815 * permissions checking. only the owner of a class can change its schema.
3816 */
3817 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3819 NameStr(classform->relname));
3820 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3821 ereport(ERROR,
3822 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3823 errmsg("permission denied: \"%s\" is a system catalog",
3824 NameStr(classform->relname))));
3825}
3826
3827/*
3828 * renameatt_internal - workhorse for renameatt
3829 *
3830 * Return value is the attribute number in the 'myrelid' relation.
3831 */
3832static AttrNumber
3834 const char *oldattname,
3835 const char *newattname,
3836 bool recurse,
3837 bool recursing,
3838 int expected_parents,
3839 DropBehavior behavior)
3840{
3841 Relation targetrelation;
3842 Relation attrelation;
3843 HeapTuple atttup;
3844 Form_pg_attribute attform;
3846
3847 /*
3848 * Grab an exclusive lock on the target table, which we will NOT release
3849 * until end of transaction.
3850 */
3851 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3852 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3853
3854 /*
3855 * if the 'recurse' flag is set then we are supposed to rename this
3856 * attribute in all classes that inherit from 'relname' (as well as in
3857 * 'relname').
3858 *
3859 * any permissions or problems with duplicate attributes will cause the
3860 * whole transaction to abort, which is what we want -- all or nothing.
3861 */
3862 if (recurse)
3863 {
3864 List *child_oids,
3865 *child_numparents;
3866 ListCell *lo,
3867 *li;
3868
3869 /*
3870 * we need the number of parents for each child so that the recursive
3871 * calls to renameatt() can determine whether there are any parents
3872 * outside the inheritance hierarchy being processed.
3873 */
3874 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3875 &child_numparents);
3876
3877 /*
3878 * find_all_inheritors does the recursive search of the inheritance
3879 * hierarchy, so all we have to do is process all of the relids in the
3880 * list that it returns.
3881 */
3882 forboth(lo, child_oids, li, child_numparents)
3883 {
3884 Oid childrelid = lfirst_oid(lo);
3885 int numparents = lfirst_int(li);
3886
3887 if (childrelid == myrelid)
3888 continue;
3889 /* note we need not recurse again */
3890 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3891 }
3892 }
3893 else
3894 {
3895 /*
3896 * If we are told not to recurse, there had better not be any child
3897 * tables; else the rename would put them out of step.
3898 *
3899 * expected_parents will only be 0 if we are not already recursing.
3900 */
3901 if (expected_parents == 0 &&
3903 ereport(ERROR,
3904 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3905 errmsg("inherited column \"%s\" must be renamed in child tables too",
3906 oldattname)));
3907 }
3908
3909 /* rename attributes in typed tables of composite type */
3910 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3911 {
3912 List *child_oids;
3913 ListCell *lo;
3914
3915 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3916 RelationGetRelationName(targetrelation),
3917 behavior);
3918
3919 foreach(lo, child_oids)
3920 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3921 }
3922
3923 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3924
3925 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3926 if (!HeapTupleIsValid(atttup))
3927 ereport(ERROR,
3928 (errcode(ERRCODE_UNDEFINED_COLUMN),
3929 errmsg("column \"%s\" does not exist",
3930 oldattname)));
3931 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3932
3933 attnum = attform->attnum;
3934 if (attnum <= 0)
3935 ereport(ERROR,
3936 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3937 errmsg("cannot rename system column \"%s\"",
3938 oldattname)));
3939
3940 /*
3941 * if the attribute is inherited, forbid the renaming. if this is a
3942 * top-level call to renameatt(), then expected_parents will be 0, so the
3943 * effect of this code will be to prohibit the renaming if the attribute
3944 * is inherited at all. if this is a recursive call to renameatt(),
3945 * expected_parents will be the number of parents the current relation has
3946 * within the inheritance hierarchy being processed, so we'll prohibit the
3947 * renaming only if there are additional parents from elsewhere.
3948 */
3949 if (attform->attinhcount > expected_parents)
3950 ereport(ERROR,
3951 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3952 errmsg("cannot rename inherited column \"%s\"",
3953 oldattname)));
3954
3955 /* new name should not already exist */
3956 (void) check_for_column_name_collision(targetrelation, newattname, false);
3957
3958 /* apply the update */
3959 namestrcpy(&(attform->attname), newattname);
3960
3961 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3962
3963 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3964
3965 heap_freetuple(atttup);
3966
3967 table_close(attrelation, RowExclusiveLock);
3968
3969 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3970
3971 return attnum;
3972}
3973
3974/*
3975 * Perform permissions and integrity checks before acquiring a relation lock.
3976 */
3977static void
3979 void *arg)
3980{
3981 HeapTuple tuple;
3982 Form_pg_class form;
3983
3984 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3985 if (!HeapTupleIsValid(tuple))
3986 return; /* concurrently dropped */
3987 form = (Form_pg_class) GETSTRUCT(tuple);
3988 renameatt_check(relid, form, false);
3989 ReleaseSysCache(tuple);
3990}
3991
3992/*
3993 * renameatt - changes the name of an attribute in a relation
3994 *
3995 * The returned ObjectAddress is that of the renamed column.
3996 */
3999{
4000 Oid relid;
4002 ObjectAddress address;
4003
4004 /* lock level taken here should match renameatt_internal */
4006 stmt->missing_ok ? RVR_MISSING_OK : 0,
4008 NULL);
4009
4010 if (!OidIsValid(relid))
4011 {
4013 (errmsg("relation \"%s\" does not exist, skipping",
4014 stmt->relation->relname)));
4015 return InvalidObjectAddress;
4016 }
4017
4018 attnum =
4019 renameatt_internal(relid,
4020 stmt->subname, /* old att name */
4021 stmt->newname, /* new att name */
4022 stmt->relation->inh, /* recursive? */
4023 false, /* recursing? */
4024 0, /* expected inhcount */
4025 stmt->behavior);
4026
4027 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4028
4029 return address;
4030}
4031
4032/*
4033 * same logic as renameatt_internal
4034 */
4035static ObjectAddress
4037 Oid mytypid,
4038 const char *oldconname,
4039 const char *newconname,
4040 bool recurse,
4041 bool recursing,
4042 int expected_parents)
4043{
4044 Relation targetrelation = NULL;
4045 Oid constraintOid;
4046 HeapTuple tuple;
4048 ObjectAddress address;
4049
4050 Assert(!myrelid || !mytypid);
4051
4052 if (mytypid)
4053 {
4054 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4055 }
4056 else
4057 {
4058 targetrelation = relation_open(myrelid, AccessExclusiveLock);
4059
4060 /*
4061 * don't tell it whether we're recursing; we allow changing typed
4062 * tables here
4063 */
4064 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4065
4066 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4067 }
4068
4069 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4070 if (!HeapTupleIsValid(tuple))
4071 elog(ERROR, "cache lookup failed for constraint %u",
4072 constraintOid);
4073 con = (Form_pg_constraint) GETSTRUCT(tuple);
4074
4075 if (myrelid &&
4076 (con->contype == CONSTRAINT_CHECK ||
4077 con->contype == CONSTRAINT_NOTNULL) &&
4078 !con->connoinherit)
4079 {
4080 if (recurse)
4081 {
4082 List *child_oids,
4083 *child_numparents;
4084 ListCell *lo,
4085 *li;
4086
4087 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4088 &child_numparents);
4089
4090 forboth(lo, child_oids, li, child_numparents)
4091 {
4092 Oid childrelid = lfirst_oid(lo);
4093 int numparents = lfirst_int(li);
4094
4095 if (childrelid == myrelid)
4096 continue;
4097
4098 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4099 }
4100 }
4101 else
4102 {
4103 if (expected_parents == 0 &&
4105 ereport(ERROR,
4106 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4107 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4108 oldconname)));
4109 }
4110
4111 if (con->coninhcount > expected_parents)
4112 ereport(ERROR,
4113 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4114 errmsg("cannot rename inherited constraint \"%s\"",
4115 oldconname)));
4116 }
4117
4118 if (con->conindid
4119 && (con->contype == CONSTRAINT_PRIMARY
4120 || con->contype == CONSTRAINT_UNIQUE
4121 || con->contype == CONSTRAINT_EXCLUSION))
4122 /* rename the index; this renames the constraint as well */
4123 RenameRelationInternal(con->conindid, newconname, false, true);
4124 else
4125 RenameConstraintById(constraintOid, newconname);
4126
4127 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4128
4129 ReleaseSysCache(tuple);
4130
4131 if (targetrelation)
4132 {
4133 /*
4134 * Invalidate relcache so as others can see the new constraint name.
4135 */
4136 CacheInvalidateRelcache(targetrelation);
4137
4138 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4139 }
4140
4141 return address;
4142}
4143
4146{
4147 Oid relid = InvalidOid;
4148 Oid typid = InvalidOid;
4149
4150 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4151 {
4152 Relation rel;
4153 HeapTuple tup;
4154
4155 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4156 rel = table_open(TypeRelationId, RowExclusiveLock);
4157 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4158 if (!HeapTupleIsValid(tup))
4159 elog(ERROR, "cache lookup failed for type %u", typid);
4160 checkDomainOwner(tup);
4161 ReleaseSysCache(tup);
4162 table_close(rel, NoLock);
4163 }
4164 else
4165 {
4166 /* lock level taken here should match rename_constraint_internal */
4168 stmt->missing_ok ? RVR_MISSING_OK : 0,
4170 NULL);
4171 if (!OidIsValid(relid))
4172 {
4174 (errmsg("relation \"%s\" does not exist, skipping",
4175 stmt->relation->relname)));
4176 return InvalidObjectAddress;
4177 }
4178 }
4179
4180 return
4181 rename_constraint_internal(relid, typid,
4182 stmt->subname,
4183 stmt->newname,
4184 (stmt->relation &&
4185 stmt->relation->inh), /* recursive? */
4186 false, /* recursing? */
4187 0 /* expected inhcount */ );
4188}
4189
4190/*
4191 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4192 * RENAME
4193 */
4196{
4197 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4198 Oid relid;
4199 ObjectAddress address;
4200
4201 /*
4202 * Grab an exclusive lock on the target table, index, sequence, view,
4203 * materialized view, or foreign table, which we will NOT release until
4204 * end of transaction.
4205 *
4206 * Lock level used here should match RenameRelationInternal, to avoid lock
4207 * escalation. However, because ALTER INDEX can be used with any relation
4208 * type, we mustn't believe without verification.
4209 */
4210 for (;;)
4211 {
4212 LOCKMODE lockmode;
4213 char relkind;
4214 bool obj_is_index;
4215
4216 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4217
4218 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4219 stmt->missing_ok ? RVR_MISSING_OK : 0,
4221 stmt);
4222
4223 if (!OidIsValid(relid))
4224 {
4226 (errmsg("relation \"%s\" does not exist, skipping",
4227 stmt->relation->relname)));
4228 return InvalidObjectAddress;
4229 }
4230
4231 /*
4232 * We allow mismatched statement and object types (e.g., ALTER INDEX
4233 * to rename a table), but we might've used the wrong lock level. If
4234 * that happens, retry with the correct lock level. We don't bother
4235 * if we already acquired AccessExclusiveLock with an index, however.
4236 */
4237 relkind = get_rel_relkind(relid);
4238 obj_is_index = (relkind == RELKIND_INDEX ||
4239 relkind == RELKIND_PARTITIONED_INDEX);
4240 if (obj_is_index || is_index_stmt == obj_is_index)
4241 break;
4242
4243 UnlockRelationOid(relid, lockmode);
4244 is_index_stmt = obj_is_index;
4245 }
4246
4247 /* Do the work */
4248 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4249
4250 ObjectAddressSet(address, RelationRelationId, relid);
4251
4252 return address;
4253}
4254
4255/*
4256 * RenameRelationInternal - change the name of a relation
4257 */
4258void
4259RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4260{
4261 Relation targetrelation;
4262 Relation relrelation; /* for RELATION relation */
4263 ItemPointerData otid;
4264 HeapTuple reltup;
4265 Form_pg_class relform;
4266 Oid namespaceId;
4267
4268 /*
4269 * Grab a lock on the target relation, which we will NOT release until end
4270 * of transaction. We need at least a self-exclusive lock so that
4271 * concurrent DDL doesn't overwrite the rename if they start updating
4272 * while still seeing the old version. The lock also guards against
4273 * triggering relcache reloads in concurrent sessions, which might not
4274 * handle this information changing under them. For indexes, we can use a
4275 * reduced lock level because RelationReloadIndexInfo() handles indexes
4276 * specially.
4277 */
4278 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4279 namespaceId = RelationGetNamespace(targetrelation);
4280
4281 /*
4282 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4283 */
4284 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4285
4286 reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4287 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4288 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4289 otid = reltup->t_self;
4290 relform = (Form_pg_class) GETSTRUCT(reltup);
4291
4292 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4293 ereport(ERROR,
4294 (errcode(ERRCODE_DUPLICATE_TABLE),
4295 errmsg("relation \"%s\" already exists",
4296 newrelname)));
4297
4298 /*
4299 * RenameRelation is careful not to believe the caller's idea of the
4300 * relation kind being handled. We don't have to worry about this, but
4301 * let's not be totally oblivious to it. We can process an index as
4302 * not-an-index, but not the other way around.
4303 */
4304 Assert(!is_index ||
4305 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4306 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4307
4308 /*
4309 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4310 * because it's a copy...)
4311 */
4312 namestrcpy(&(relform->relname), newrelname);
4313
4314 CatalogTupleUpdate(relrelation, &otid, reltup);
4315 UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4316
4317 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4318 InvalidOid, is_internal);
4319
4320 heap_freetuple(reltup);
4321 table_close(relrelation, RowExclusiveLock);
4322
4323 /*
4324 * Also rename the associated type, if any.
4325 */
4326 if (OidIsValid(targetrelation->rd_rel->reltype))
4327 RenameTypeInternal(targetrelation->rd_rel->reltype,
4328 newrelname, namespaceId);
4329
4330 /*
4331 * Also rename the associated constraint, if any.
4332 */
4333 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4334 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4335 {
4336 Oid constraintId = get_index_constraint(myrelid);
4337
4338 if (OidIsValid(constraintId))
4339 RenameConstraintById(constraintId, newrelname);
4340 }
4341
4342 /*
4343 * Close rel, but keep lock!
4344 */
4345 relation_close(targetrelation, NoLock);
4346}
4347
4348/*
4349 * ResetRelRewrite - reset relrewrite
4350 */
4351void
4353{
4354 Relation relrelation; /* for RELATION relation */
4355 HeapTuple reltup;
4356 Form_pg_class relform;
4357
4358 /*
4359 * Find relation's pg_class tuple.
4360 */
4361 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4362
4363 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4364 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4365 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4366 relform = (Form_pg_class) GETSTRUCT(reltup);
4367
4368 /*
4369 * Update pg_class tuple.
4370 */
4371 relform->relrewrite = InvalidOid;
4372
4373 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4374
4375 heap_freetuple(reltup);
4376 table_close(relrelation, RowExclusiveLock);
4377}
4378
4379/*
4380 * Disallow ALTER TABLE (and similar commands) when the current backend has
4381 * any open reference to the target table besides the one just acquired by
4382 * the calling command; this implies there's an open cursor or active plan.
4383 * We need this check because our lock doesn't protect us against stomping
4384 * on our own foot, only other people's feet!
4385 *
4386 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4387 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4388 * possibly be relaxed to only error out for certain types of alterations.
4389 * But the use-case for allowing any of these things is not obvious, so we
4390 * won't work hard at it for now.
4391 *
4392 * We also reject these commands if there are any pending AFTER trigger events
4393 * for the rel. This is certainly necessary for the rewriting variants of
4394 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4395 * events would try to fetch the wrong tuples. It might be overly cautious
4396 * in other cases, but again it seems better to err on the side of paranoia.
4397 *
4398 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4399 * we are worried about active indexscans on the index. The trigger-event
4400 * check can be skipped, since we are doing no damage to the parent table.
4401 *
4402 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4403 */
4404void
4406{
4407 int expected_refcnt;
4408
4409 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4410 if (rel->rd_refcnt != expected_refcnt)
4411 ereport(ERROR,
4412 (errcode(ERRCODE_OBJECT_IN_USE),
4413 /* translator: first %s is a SQL command, eg ALTER TABLE */
4414 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4416
4417 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4418 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4420 ereport(ERROR,
4421 (errcode(ERRCODE_OBJECT_IN_USE),
4422 /* translator: first %s is a SQL command, eg ALTER TABLE */
4423 errmsg("cannot %s \"%s\" because it has pending trigger events",
4425}
4426
4427/*
4428 * CheckAlterTableIsSafe
4429 * Verify that it's safe to allow ALTER TABLE on this relation.
4430 *
4431 * This consists of CheckTableNotInUse() plus a check that the relation
4432 * isn't another session's temp table. We must split out the temp-table
4433 * check because there are callers of CheckTableNotInUse() that don't want
4434 * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4435 * an orphaned temp schema.) Compare truncate_check_activity().
4436 */
4437static void
4439{
4440 /*
4441 * Don't allow ALTER on temp tables of other backends. Their local buffer
4442 * manager is not going to cope if we need to change the table's contents.
4443 * Even if we don't, there may be optimizations that assume temp tables
4444 * aren't subject to such interference.
4445 */
4446 if (RELATION_IS_OTHER_TEMP(rel))
4447 ereport(ERROR,
4448 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4449 errmsg("cannot alter temporary tables of other sessions")));
4450
4451 /*
4452 * Also check for active uses of the relation in the current transaction,
4453 * including open scans and pending AFTER trigger events.
4454 */
4455 CheckTableNotInUse(rel, "ALTER TABLE");
4456}
4457
4458/*
4459 * AlterTableLookupRelation
4460 * Look up, and lock, the OID for the relation named by an alter table
4461 * statement.
4462 */
4463Oid
4465{
4466 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4467 stmt->missing_ok ? RVR_MISSING_OK : 0,
4469 stmt);
4470}
4471
4472/*
4473 * AlterTable
4474 * Execute ALTER TABLE, which can be a list of subcommands
4475 *
4476 * ALTER TABLE is performed in three phases:
4477 * 1. Examine subcommands and perform pre-transformation checking.
4478 * 2. Validate and transform subcommands, and update system catalogs.
4479 * 3. Scan table(s) to check new constraints, and optionally recopy
4480 * the data into new table(s).
4481 * Phase 3 is not performed unless one or more of the subcommands requires
4482 * it. The intention of this design is to allow multiple independent
4483 * updates of the table schema to be performed with only one pass over the
4484 * data.
4485 *
4486 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4487 * each table to be affected (there may be multiple affected tables if the
4488 * commands traverse a table inheritance hierarchy). Also we do preliminary
4489 * validation of the subcommands. Because earlier subcommands may change
4490 * the catalog state seen by later commands, there are limits to what can
4491 * be done in this phase. Generally, this phase acquires table locks,
4492 * checks permissions and relkind, and recurses to find child tables.
4493 *
4494 * ATRewriteCatalogs performs phase 2 for each affected table.
4495 * Certain subcommands need to be performed before others to avoid
4496 * unnecessary conflicts; for example, DROP COLUMN should come before
4497 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4498 * lists, one for each logical "pass" of phase 2.
4499 *
4500 * ATRewriteTables performs phase 3 for those tables that need it.
4501 *
4502 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4503 * since phase 1 already does it. However, for certain subcommand types
4504 * it is only possible to determine how to recurse at phase 2 time; for
4505 * those cases, phase 1 sets the cmd->recurse flag.
4506 *
4507 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4508 * the whole operation; we don't have to do anything special to clean up.
4509 *
4510 * The caller must lock the relation, with an appropriate lock level
4511 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4512 * or higher. We pass the lock level down
4513 * so that we can apply it recursively to inherited tables. Note that the
4514 * lock level we want as we recurse might well be higher than required for
4515 * that specific subcommand. So we pass down the overall lock requirement,
4516 * rather than reassess it at lower levels.
4517 *
4518 * The caller also provides a "context" which is to be passed back to
4519 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4520 * Some of the fields therein, such as the relid, are used here as well.
4521 */
4522void
4524 AlterTableUtilityContext *context)
4525{
4526 Relation rel;
4527
4528 /* Caller is required to provide an adequate lock. */
4529 rel = relation_open(context->relid, NoLock);
4530
4532
4533 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4534}
4535
4536/*
4537 * AlterTableInternal
4538 *
4539 * ALTER TABLE with target specified by OID
4540 *
4541 * We do not reject if the relation is already open, because it's quite
4542 * likely that one or more layers of caller have it open. That means it
4543 * is unsafe to use this entry point for alterations that could break
4544 * existing query plans. On the assumption it's not used for such, we
4545 * don't have to reject pending AFTER triggers, either.
4546 *
4547 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4548 * used for any subcommand types that require parse transformation or
4549 * could generate subcommands that have to be passed to ProcessUtility.
4550 */
4551void
4552AlterTableInternal(Oid relid, List *cmds, bool recurse)
4553{
4554 Relation rel;
4555 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4556
4557 rel = relation_open(relid, lockmode);
4558
4560
4561 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4562}
4563
4564/*
4565 * AlterTableGetLockLevel
4566 *
4567 * Sets the overall lock level required for the supplied list of subcommands.
4568 * Policy for doing this set according to needs of AlterTable(), see
4569 * comments there for overall explanation.
4570 *
4571 * Function is called before and after parsing, so it must give same
4572 * answer each time it is called. Some subcommands are transformed
4573 * into other subcommand types, so the transform must never be made to a
4574 * lower lock level than previously assigned. All transforms are noted below.
4575 *
4576 * Since this is called before we lock the table we cannot use table metadata
4577 * to influence the type of lock we acquire.
4578 *
4579 * There should be no lockmodes hardcoded into the subcommand functions. All
4580 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4581 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4582 * and does not travel through this section of code and cannot be combined with
4583 * any of the subcommands given here.
4584 *
4585 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4586 * so any changes that might affect SELECTs running on standbys need to use
4587 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4588 * have a solution for that also.
4589 *
4590 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4591 * that takes a lock less than AccessExclusiveLock can change object definitions
4592 * while pg_dump is running. Be careful to check that the appropriate data is
4593 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4594 * otherwise we might end up with an inconsistent dump that can't restore.
4595 */
4598{
4599 /*
4600 * This only works if we read catalog tables using MVCC snapshots.
4601 */
4602 ListCell *lcmd;
4604
4605 foreach(lcmd, cmds)
4606 {
4607 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4608 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4609
4610 switch (cmd->subtype)
4611 {
4612 /*
4613 * These subcommands rewrite the heap, so require full locks.
4614 */
4615 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4616 * to SELECT */
4617 case AT_SetAccessMethod: /* must rewrite heap */
4618 case AT_SetTableSpace: /* must rewrite heap */
4619 case AT_AlterColumnType: /* must rewrite heap */
4620 cmd_lockmode = AccessExclusiveLock;
4621 break;
4622
4623 /*
4624 * These subcommands may require addition of toast tables. If
4625 * we add a toast table to a table currently being scanned, we
4626 * might miss data added to the new toast table by concurrent
4627 * insert transactions.
4628 */
4629 case AT_SetStorage: /* may add toast tables, see
4630 * ATRewriteCatalogs() */
4631 cmd_lockmode = AccessExclusiveLock;
4632 break;
4633
4634 /*
4635 * Removing constraints can affect SELECTs that have been
4636 * optimized assuming the constraint holds true. See also
4637 * CloneFkReferenced.
4638 */
4639 case AT_DropConstraint: /* as DROP INDEX */
4640 case AT_DropNotNull: /* may change some SQL plans */
4641 cmd_lockmode = AccessExclusiveLock;
4642 break;
4643
4644 /*
4645 * Subcommands that may be visible to concurrent SELECTs
4646 */
4647 case AT_DropColumn: /* change visible to SELECT */
4648 case AT_AddColumnToView: /* CREATE VIEW */
4649 case AT_DropOids: /* used to equiv to DropColumn */
4650 case AT_EnableAlwaysRule: /* may change SELECT rules */
4651 case AT_EnableReplicaRule: /* may change SELECT rules */
4652 case AT_EnableRule: /* may change SELECT rules */
4653 case AT_DisableRule: /* may change SELECT rules */
4654 cmd_lockmode = AccessExclusiveLock;
4655 break;
4656
4657 /*
4658 * Changing owner may remove implicit SELECT privileges
4659 */
4660 case AT_ChangeOwner: /* change visible to SELECT */
4661 cmd_lockmode = AccessExclusiveLock;
4662 break;
4663
4664 /*
4665 * Changing foreign table options may affect optimization.
4666 */
4667 case AT_GenericOptions:
4669 cmd_lockmode = AccessExclusiveLock;
4670 break;
4671
4672 /*
4673 * These subcommands affect write operations only.
4674 */
4675 case AT_EnableTrig:
4678 case AT_EnableTrigAll:
4679 case AT_EnableTrigUser:
4680 case AT_DisableTrig:
4681 case AT_DisableTrigAll:
4682 case AT_DisableTrigUser:
4683 cmd_lockmode = ShareRowExclusiveLock;
4684 break;
4685
4686 /*
4687 * These subcommands affect write operations only. XXX
4688 * Theoretically, these could be ShareRowExclusiveLock.
4689 */
4690 case AT_ColumnDefault:
4692 case AT_AlterConstraint:
4693 case AT_AddIndex: /* from ADD CONSTRAINT */
4695 case AT_ReplicaIdentity:
4696 case AT_SetNotNull:
4701 case AT_AddIdentity:
4702 case AT_DropIdentity:
4703 case AT_SetIdentity:
4704 case AT_SetExpression:
4705 case AT_DropExpression:
4706 case AT_SetCompression:
4707 cmd_lockmode = AccessExclusiveLock;
4708 break;
4709
4710 case AT_AddConstraint:
4711 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4712 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4713 if (IsA(cmd->def, Constraint))
4714 {
4715 Constraint *con = (Constraint *) cmd->def;
4716
4717 switch (con->contype)
4718 {
4719 case CONSTR_EXCLUSION:
4720 case CONSTR_PRIMARY:
4721 case CONSTR_UNIQUE:
4722
4723 /*
4724 * Cases essentially the same as CREATE INDEX. We
4725 * could reduce the lock strength to ShareLock if
4726 * we can work out how to allow concurrent catalog
4727 * updates. XXX Might be set down to
4728 * ShareRowExclusiveLock but requires further
4729 * analysis.
4730 */
4731 cmd_lockmode = AccessExclusiveLock;
4732 break;
4733 case CONSTR_FOREIGN:
4734
4735 /*
4736 * We add triggers to both tables when we add a
4737 * Foreign Key, so the lock level must be at least
4738 * as strong as CREATE TRIGGER.
4739 */
4740 cmd_lockmode = ShareRowExclusiveLock;
4741 break;
4742
4743 default:
4744 cmd_lockmode = AccessExclusiveLock;
4745 }
4746 }
4747 break;
4748
4749 /*
4750 * These subcommands affect inheritance behaviour. Queries
4751 * started before us will continue to see the old inheritance
4752 * behaviour, while queries started after we commit will see
4753 * new behaviour. No need to prevent reads or writes to the
4754 * subtable while we hook it up though. Changing the TupDesc
4755 * may be a problem, so keep highest lock.
4756 */
4757 case AT_AddInherit:
4758 case AT_DropInherit:
4759 cmd_lockmode = AccessExclusiveLock;
4760 break;
4761
4762 /*
4763 * These subcommands affect implicit row type conversion. They
4764 * have affects similar to CREATE/DROP CAST on queries. don't
4765 * provide for invalidating parse trees as a result of such
4766 * changes, so we keep these at AccessExclusiveLock.
4767 */
4768 case AT_AddOf:
4769 case AT_DropOf:
4770 cmd_lockmode = AccessExclusiveLock;
4771 break;
4772
4773 /*
4774 * Only used by CREATE OR REPLACE VIEW which must conflict
4775 * with an SELECTs currently using the view.
4776 */
4778 cmd_lockmode = AccessExclusiveLock;
4779 break;
4780
4781 /*
4782 * These subcommands affect general strategies for performance
4783 * and maintenance, though don't change the semantic results
4784 * from normal data reads and writes. Delaying an ALTER TABLE
4785 * behind currently active writes only delays the point where
4786 * the new strategy begins to take effect, so there is no
4787 * benefit in waiting. In this case the minimum restriction
4788 * applies: we don't currently allow concurrent catalog
4789 * updates.
4790 */
4791 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4792 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4793 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4794 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4795 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4796 cmd_lockmode = ShareUpdateExclusiveLock;
4797 break;
4798
4799 case AT_SetLogged:
4800 case AT_SetUnLogged:
4801 cmd_lockmode = AccessExclusiveLock;
4802 break;
4803
4804 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4805 cmd_lockmode = ShareUpdateExclusiveLock;
4806 break;
4807
4808 /*
4809 * Rel options are more complex than first appears. Options
4810 * are set here for tables, views and indexes; for historical
4811 * reasons these can all be used with ALTER TABLE, so we can't
4812 * decide between them using the basic grammar.
4813 */
4814 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4815 * getTables() */
4816 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4817 * getTables() */
4818 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4819 break;
4820
4821 case AT_AttachPartition:
4822 cmd_lockmode = ShareUpdateExclusiveLock;
4823 break;
4824
4825 case AT_DetachPartition:
4826 if (((PartitionCmd *) cmd->def)->concurrent)
4827 cmd_lockmode = ShareUpdateExclusiveLock;
4828 else
4829 cmd_lockmode = AccessExclusiveLock;
4830 break;
4831
4833 cmd_lockmode = ShareUpdateExclusiveLock;
4834 break;
4835
4836 default: /* oops */
4837 elog(ERROR, "unrecognized alter table type: %d",
4838 (int) cmd->subtype);
4839 break;
4840 }
4841
4842 /*
4843 * Take the greatest lockmode from any subcommand
4844 */
4845 if (cmd_lockmode > lockmode)
4846 lockmode = cmd_lockmode;
4847 }
4848
4849 return lockmode;
4850}
4851
4852/*
4853 * ATController provides top level control over the phases.
4854 *
4855 * parsetree is passed in to allow it to be passed to event triggers
4856 * when requested.
4857 */
4858static void
4860 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4861 AlterTableUtilityContext *context)
4862{
4863 List *wqueue = NIL;
4864 ListCell *lcmd;
4865
4866 /* Phase 1: preliminary examination of commands, create work queue */
4867 foreach(lcmd, cmds)
4868 {
4869 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4870
4871 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4872 }
4873
4874 /* Close the relation, but keep lock until commit */
4875 relation_close(rel, NoLock);
4876
4877 /* Phase 2: update system catalogs */
4878 ATRewriteCatalogs(&wqueue, lockmode, context);
4879
4880 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4881 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4882}
4883
4884/*
4885 * ATPrepCmd
4886 *
4887 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4888 * recursion and permission checks.
4889 *
4890 * Caller must have acquired appropriate lock type on relation already.
4891 * This lock should be held until commit.
4892 */
4893static void
4895 bool recurse, bool recursing, LOCKMODE lockmode,
4896 AlterTableUtilityContext *context)
4897{
4898 AlteredTableInfo *tab;
4900
4901 /* Find or create work queue entry for this table */
4902 tab = ATGetQueueEntry(wqueue, rel);
4903
4904 /*
4905 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4906 * partitions that are pending detach.
4907 */
4908 if (rel->rd_rel->relispartition &&
4911 ereport(ERROR,
4912 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4913 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4915 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4916
4917 /*
4918 * Copy the original subcommand for each table, so we can scribble on it.
4919 * This avoids conflicts when different child tables need to make
4920 * different parse transformations (for example, the same column may have
4921 * different column numbers in different children).
4922 */
4923 cmd = copyObject(cmd);
4924
4925 /*
4926 * Do permissions and relkind checking, recursion to child tables if
4927 * needed, and any additional phase-1 processing needed. (But beware of
4928 * adding any processing that looks at table details that another
4929 * subcommand could change. In some cases we reject multiple subcommands
4930 * that could try to change the same state in contrary ways.)
4931 */
4932 switch (cmd->subtype)
4933 {
4934 case AT_AddColumn: /* ADD COLUMN */
4935 ATSimplePermissions(cmd->subtype, rel,
4938 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4939 lockmode, context);
4940 /* Recursion occurs during execution phase */
4941 pass = AT_PASS_ADD_COL;
4942 break;
4943 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4945 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4946 lockmode, context);
4947 /* Recursion occurs during execution phase */
4948 pass = AT_PASS_ADD_COL;
4949 break;
4950 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4951
4952 /*
4953 * We allow defaults on views so that INSERT into a view can have
4954 * default-ish behavior. This works because the rewriter
4955 * substitutes default values into INSERTs before it expands
4956 * rules.
4957 */
4958 ATSimplePermissions(cmd->subtype, rel,
4961 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4962 /* No command-specific prep needed */
4964 break;
4965 case AT_CookedColumnDefault: /* add a pre-cooked default */
4966 /* This is currently used only in CREATE TABLE */
4967 /* (so the permission check really isn't necessary) */
4968 ATSimplePermissions(cmd->subtype, rel,
4970 /* This command never recurses */
4972 break;
4973 case AT_AddIdentity:
4974 ATSimplePermissions(cmd->subtype, rel,
4977 /* Set up recursion for phase 2; no other prep needed */
4978 if (recurse)
4979 cmd->recurse = true;
4981 break;
4982 case AT_SetIdentity:
4983 ATSimplePermissions(cmd->subtype, rel,
4986 /* Set up recursion for phase 2; no other prep needed */
4987 if (recurse)
4988 cmd->recurse = true;
4989 /* This should run after AddIdentity, so do it in MISC pass */
4990 pass = AT_PASS_MISC;
4991 break;
4992 case AT_DropIdentity:
4993 ATSimplePermissions(cmd->subtype, rel,
4996 /* Set up recursion for phase 2; no other prep needed */
4997 if (recurse)
4998 cmd->recurse = true;
4999 pass = AT_PASS_DROP;
5000 break;
5001 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5002 ATSimplePermissions(cmd->subtype, rel,
5004 /* Set up recursion for phase 2; no other prep needed */
5005 if (recurse)
5006 cmd->recurse = true;
5007 pass = AT_PASS_DROP;
5008 break;
5009 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5010 ATSimplePermissions(cmd->subtype, rel,
5012 /* Set up recursion for phase 2; no other prep needed */
5013 if (recurse)
5014 cmd->recurse = true;
5015 pass = AT_PASS_COL_ATTRS;
5016 break;
5017 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5018 ATSimplePermissions(cmd->subtype, rel,
5020 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5022 break;
5023 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5024 ATSimplePermissions(cmd->subtype, rel,
5026 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5027 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5028 pass = AT_PASS_DROP;
5029 break;
5030 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5031 ATSimplePermissions(cmd->subtype, rel,
5034 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5035 /* No command-specific prep needed */
5036 pass = AT_PASS_MISC;
5037 break;
5038 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5039 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5040 ATSimplePermissions(cmd->subtype, rel,
5043 /* This command never recurses */
5044 pass = AT_PASS_MISC;
5045 break;
5046 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5047 ATSimplePermissions(cmd->subtype, rel,
5050 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5051 /* No command-specific prep needed */
5052 pass = AT_PASS_MISC;
5053 break;
5054 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5055 ATSimplePermissions(cmd->subtype, rel,
5057 /* This command never recurses */
5058 /* No command-specific prep needed */
5059 pass = AT_PASS_MISC;
5060 break;
5061 case AT_DropColumn: /* DROP COLUMN */
5062 ATSimplePermissions(cmd->subtype, rel,
5065 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5066 lockmode, context);
5067 /* Recursion occurs during execution phase */
5068 pass = AT_PASS_DROP;
5069 break;
5070 case AT_AddIndex: /* ADD INDEX */
5072 /* This command never recurses */
5073 /* No command-specific prep needed */
5074 pass = AT_PASS_ADD_INDEX;
5075 break;
5076 case AT_AddConstraint: /* ADD CONSTRAINT */
5077 ATSimplePermissions(cmd->subtype, rel,
5079 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5080 if (recurse)
5081 {
5082 /* recurses at exec time; lock descendants and set flag */
5083 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5084 cmd->recurse = true;
5085 }
5086 pass = AT_PASS_ADD_CONSTR;
5087 break;
5088 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5090 /* This command never recurses */
5091 /* No command-specific prep needed */
5093 break;
5094 case AT_DropConstraint: /* DROP CONSTRAINT */
5095 ATSimplePermissions(cmd->subtype, rel,
5097 ATCheckPartitionsNotInUse(rel, lockmode);
5098 /* Other recursion occurs during execution phase */
5099 /* No command-specific prep needed except saving recurse flag */
5100 if (recurse)
5101 cmd->recurse = true;
5102 pass = AT_PASS_DROP;
5103 break;
5104 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5105 ATSimplePermissions(cmd->subtype, rel,
5108 /* See comments for ATPrepAlterColumnType */
5109 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5110 AT_PASS_UNSET, context);
5111 Assert(cmd != NULL);
5112 /* Performs own recursion */
5113 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5114 lockmode, context);
5115 pass = AT_PASS_ALTER_TYPE;
5116 break;
5119 /* This command never recurses */
5120 /* No command-specific prep needed */
5121 pass = AT_PASS_MISC;
5122 break;
5123 case AT_ChangeOwner: /* ALTER OWNER */
5124 /* This command never recurses */
5125 /* No command-specific prep needed */
5126 pass = AT_PASS_MISC;
5127 break;
5128 case AT_ClusterOn: /* CLUSTER ON */
5129 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5130 ATSimplePermissions(cmd->subtype, rel,
5132 /* These commands never recurse */
5133 /* No command-specific prep needed */
5134 pass = AT_PASS_MISC;
5135 break;
5136 case AT_SetLogged: /* SET LOGGED */
5137 case AT_SetUnLogged: /* SET UNLOGGED */
5139 if (tab->chgPersistence)
5140 ereport(ERROR,
5141 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5142 errmsg("cannot change persistence setting twice")));
5143 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5144 pass = AT_PASS_MISC;
5145 break;
5146 case AT_DropOids: /* SET WITHOUT OIDS */
5147 ATSimplePermissions(cmd->subtype, rel,
5149 pass = AT_PASS_DROP;
5150 break;
5151 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5152 ATSimplePermissions(cmd->subtype, rel,
5154
5155 /* check if another access method change was already requested */
5156 if (tab->chgAccessMethod)
5157 ereport(ERROR,
5158 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5159 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5160
5161 ATPrepSetAccessMethod(tab, rel, cmd->name);
5162 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5163 break;
5164 case AT_SetTableSpace: /* SET TABLESPACE */
5167 /* This command never recurses */
5168 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5169 pass = AT_PASS_MISC; /* doesn't actually matter */
5170 break;
5171 case AT_SetRelOptions: /* SET (...) */
5172 case AT_ResetRelOptions: /* RESET (...) */
5173 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5174 ATSimplePermissions(cmd->subtype, rel,
5177 /* This command never recurses */
5178 /* No command-specific prep needed */
5179 pass = AT_PASS_MISC;
5180 break;
5181 case AT_AddInherit: /* INHERIT */
5182 ATSimplePermissions(cmd->subtype, rel,
5184 /* This command never recurses */
5185 ATPrepAddInherit(rel);
5186 pass = AT_PASS_MISC;
5187 break;
5188 case AT_DropInherit: /* NO INHERIT */
5189 ATSimplePermissions(cmd->subtype, rel,
5191 /* This command never recurses */
5192 /* No command-specific prep needed */
5193 pass = AT_PASS_MISC;
5194 break;
5195 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5196 ATSimplePermissions(cmd->subtype, rel,
5198 /* Recursion occurs during execution phase */
5199 if (recurse)
5200 cmd->recurse = true;
5201 pass = AT_PASS_MISC;
5202 break;
5203 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5204 ATSimplePermissions(cmd->subtype, rel,
5206 /* Recursion occurs during execution phase */
5207 /* No command-specific prep needed except saving recurse flag */
5208 if (recurse)
5209 cmd->recurse = true;
5210 pass = AT_PASS_MISC;
5211 break;
5212 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5213 ATSimplePermissions(cmd->subtype, rel,
5215 pass = AT_PASS_MISC;
5216 /* This command never recurses */
5217 /* No command-specific prep needed */
5218 break;
5219 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5222 case AT_EnableTrigAll:
5223 case AT_EnableTrigUser:
5224 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5225 case AT_DisableTrigAll:
5226 case AT_DisableTrigUser:
5227 ATSimplePermissions(cmd->subtype, rel,
5229 /* Set up recursion for phase 2; no other prep needed */
5230 if (recurse)
5231 cmd->recurse = true;
5232 pass = AT_PASS_MISC;
5233 break;
5234 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5237 case AT_DisableRule:
5238 case AT_AddOf: /* OF */
5239 case AT_DropOf: /* NOT OF */
5244 ATSimplePermissions(cmd->subtype, rel,
5246 /* These commands never recurse */
5247 /* No command-specific prep needed */
5248 pass = AT_PASS_MISC;
5249 break;
5250 case AT_GenericOptions:
5252 /* No command-specific prep needed */
5253 pass = AT_PASS_MISC;
5254 break;
5255 case AT_AttachPartition:
5256 ATSimplePermissions(cmd->subtype, rel,
5258 /* No command-specific prep needed */
5259 pass = AT_PASS_MISC;
5260 break;
5261 case AT_DetachPartition:
5263 /* No command-specific prep needed */
5264 pass = AT_PASS_MISC;
5265 break;
5268 /* No command-specific prep needed */
5269 pass = AT_PASS_MISC;
5270 break;
5271 default: /* oops */
5272 elog(ERROR, "unrecognized alter table type: %d",
5273 (int) cmd->subtype);
5274 pass = AT_PASS_UNSET; /* keep compiler quiet */
5275 break;
5276 }
5277 Assert(pass > AT_PASS_UNSET);
5278
5279 /* Add the subcommand to the appropriate list for phase 2 */
5280 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5281}
5282
5283/*
5284 * ATRewriteCatalogs
5285 *
5286 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5287 * dispatched in a "safe" execution order (designed to avoid unnecessary
5288 * conflicts).
5289 */
5290static void
5292 AlterTableUtilityContext *context)
5293{
5294 ListCell *ltab;
5295
5296 /*
5297 * We process all the tables "in parallel", one pass at a time. This is
5298 * needed because we may have to propagate work from one table to another
5299 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5300 * re-adding of the foreign key constraint to the other table). Work can
5301 * only be propagated into later passes, however.
5302 */
5303 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5304 {
5305 /* Go through each table that needs to be processed */
5306 foreach(ltab, *wqueue)
5307 {
5308 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5309 List *subcmds = tab->subcmds[pass];
5310 ListCell *lcmd;
5311
5312 if (subcmds == NIL)
5313 continue;
5314
5315 /*
5316 * Open the relation and store it in tab. This allows subroutines
5317 * close and reopen, if necessary. Appropriate lock was obtained
5318 * by phase 1, needn't get it again.
5319 */
5320 tab->rel = relation_open(tab->relid, NoLock);
5321
5322 foreach(lcmd, subcmds)
5323 ATExecCmd(wqueue, tab,
5325 lockmode, pass, context);
5326
5327 /*
5328 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5329 * (this is not done in ATExecAlterColumnType since it should be
5330 * done only once if multiple columns of a table are altered).
5331 */
5332 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5333 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5334
5335 if (tab->rel)
5336 {
5337 relation_close(tab->rel, NoLock);
5338 tab->rel = NULL;
5339 }
5340 }
5341 }
5342
5343 /* Check to see if a toast table must be added. */
5344 foreach(ltab, *wqueue)
5345 {
5346 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5347
5348 /*
5349 * If the table is source table of ATTACH PARTITION command, we did
5350 * not modify anything about it that will change its toasting
5351 * requirement, so no need to check.
5352 */
5353 if (((tab->relkind == RELKIND_RELATION ||
5354 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5355 tab->partition_constraint == NULL) ||
5356 tab->relkind == RELKIND_MATVIEW)
5357 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5358 }
5359}
5360
5361/*
5362 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5363 */
5364static void
5366 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5367 AlterTableUtilityContext *context)
5368{
5370 Relation rel = tab->rel;
5371
5372 switch (cmd->subtype)
5373 {
5374 case AT_AddColumn: /* ADD COLUMN */
5375 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5376 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5377 cmd->recurse, false,
5378 lockmode, cur_pass, context);
5379 break;
5380 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5381 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5382 break;
5383 case AT_CookedColumnDefault: /* add a pre-cooked default */
5384 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5385 break;
5386 case AT_AddIdentity:
5387 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5388 cur_pass, context);
5389 Assert(cmd != NULL);
5390 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5391 break;
5392 case AT_SetIdentity:
5393 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5394 cur_pass, context);
5395 Assert(cmd != NULL);
5396 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5397 break;
5398 case AT_DropIdentity:
5399 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5400 break;
5401 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5402 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5403 break;
5404 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5405 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5406 cmd->recurse, false, lockmode);
5407 break;
5408 case AT_SetExpression:
5409 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5410 break;
5411 case AT_DropExpression:
5412 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5413 break;
5414 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5415 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5416 break;
5417 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5418 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5419 break;
5420 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5421 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5422 break;
5423 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5424 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5425 break;
5426 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5427 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5428 lockmode);
5429 break;
5430 case AT_DropColumn: /* DROP COLUMN */
5431 address = ATExecDropColumn(wqueue, rel, cmd->name,
5432 cmd->behavior, cmd->recurse, false,
5433 cmd->missing_ok, lockmode,
5434 NULL);
5435 break;
5436 case AT_AddIndex: /* ADD INDEX */
5437 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5438 lockmode);
5439 break;
5440 case AT_ReAddIndex: /* ADD INDEX */
5441 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5442 lockmode);
5443 break;
5444 case AT_ReAddStatistics: /* ADD STATISTICS */
5445 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5446 true, lockmode);
5447 break;
5448 case AT_AddConstraint: /* ADD CONSTRAINT */
5449 /* Transform the command only during initial examination */
5450 if (cur_pass == AT_PASS_ADD_CONSTR)
5451 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5452 cmd->recurse, lockmode,
5453 cur_pass, context);
5454 /* Depending on constraint type, might be no more work to do now */
5455 if (cmd != NULL)
5456 address =
5457 ATExecAddConstraint(wqueue, tab, rel,
5458 (Constraint *) cmd->def,
5459 cmd->recurse, false, lockmode);
5460 break;
5461 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5462 address =
5463 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5464 true, true, lockmode);
5465 break;
5466 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5467 * constraint */
5468 address =
5469 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5470 ((AlterDomainStmt *) cmd->def)->def,
5471 NULL);
5472 break;
5473 case AT_ReAddComment: /* Re-add existing comment */
5474 address = CommentObject((CommentStmt *) cmd->def);
5475 break;
5476 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5477 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5478 lockmode);
5479 break;
5480 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5481 address = ATExecAlterConstraint(wqueue, rel,
5483 cmd->recurse, lockmode);
5484 break;
5485 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5486 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5487 false, lockmode);
5488 break;
5489 case AT_DropConstraint: /* DROP CONSTRAINT */
5490 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5491 cmd->recurse,
5492 cmd->missing_ok, lockmode);
5493 break;
5494 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5495 /* parse transformation was done earlier */
5496 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5497 break;
5498 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5499 address =
5501 (List *) cmd->def, lockmode);
5502 break;
5503 case AT_ChangeOwner: /* ALTER OWNER */
5505 get_rolespec_oid(cmd->newowner, false),
5506 false, lockmode);
5507 break;
5508 case AT_ClusterOn: /* CLUSTER ON */
5509 address = ATExecClusterOn(rel, cmd->name, lockmode);
5510 break;
5511 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5512 ATExecDropCluster(rel, lockmode);
5513 break;
5514 case AT_SetLogged: /* SET LOGGED */
5515 case AT_SetUnLogged: /* SET UNLOGGED */
5516 break;
5517 case AT_DropOids: /* SET WITHOUT OIDS */
5518 /* nothing to do here, oid columns don't exist anymore */
5519 break;
5520 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5521
5522 /*
5523 * Only do this for partitioned tables, for which this is just a
5524 * catalog change. Tables with storage are handled by Phase 3.
5525 */
5526 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5527 tab->chgAccessMethod)
5529 break;
5530 case AT_SetTableSpace: /* SET TABLESPACE */
5531
5532 /*
5533 * Only do this for partitioned tables and indexes, for which this
5534 * is just a catalog change. Other relation types which have
5535 * storage are handled by Phase 3.
5536 */
5537 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5538 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5540
5541 break;
5542 case AT_SetRelOptions: /* SET (...) */
5543 case AT_ResetRelOptions: /* RESET (...) */
5544 case AT_ReplaceRelOptions: /* replace entire option list */
5545 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5546 break;
5547 case AT_EnableTrig: /* ENABLE TRIGGER name */
5550 cmd->recurse,
5551 lockmode);
5552 break;
5553 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5555 TRIGGER_FIRES_ALWAYS, false,
5556 cmd->recurse,
5557 lockmode);
5558 break;
5559 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5562 cmd->recurse,
5563 lockmode);
5564 break;
5565 case AT_DisableTrig: /* DISABLE TRIGGER name */
5567 TRIGGER_DISABLED, false,
5568 cmd->recurse,
5569 lockmode);
5570 break;
5571 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5574 cmd->recurse,
5575 lockmode);
5576 break;
5577 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5579 TRIGGER_DISABLED, false,
5580 cmd->recurse,
5581 lockmode);
5582 break;
5583 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5586 cmd->recurse,
5587 lockmode);
5588 break;
5589 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5591 TRIGGER_DISABLED, true,
5592 cmd->recurse,
5593 lockmode);
5594 break;
5595
5596 case AT_EnableRule: /* ENABLE RULE name */
5597 ATExecEnableDisableRule(rel, cmd->name,
5598 RULE_FIRES_ON_ORIGIN, lockmode);
5599 break;
5600 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5601 ATExecEnableDisableRule(rel, cmd->name,
5602 RULE_FIRES_ALWAYS, lockmode);
5603 break;
5604 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5605 ATExecEnableDisableRule(rel, cmd->name,
5606 RULE_FIRES_ON_REPLICA, lockmode);
5607 break;
5608 case AT_DisableRule: /* DISABLE RULE name */
5609 ATExecEnableDisableRule(rel, cmd->name,
5610 RULE_DISABLED, lockmode);
5611 break;
5612
5613 case AT_AddInherit:
5614 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5615 break;
5616 case AT_DropInherit:
5617 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5618 break;
5619 case AT_AddOf:
5620 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5621 break;
5622 case AT_DropOf:
5623 ATExecDropOf(rel, lockmode);
5624 break;
5625 case AT_ReplicaIdentity:
5626 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5627 break;
5629 ATExecSetRowSecurity(rel, true);
5630 break;
5632 ATExecSetRowSecurity(rel, false);
5633 break;
5636 break;
5639 break;
5640 case AT_GenericOptions:
5641 ATExecGenericOptions(rel, (List *) cmd->def);
5642 break;
5643 case AT_AttachPartition:
5644 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5645 cur_pass, context);
5646 Assert(cmd != NULL);
5647 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5648 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5649 context);
5650 else
5651 address = ATExecAttachPartitionIdx(wqueue, rel,
5652 ((PartitionCmd *) cmd->def)->name);
5653 break;
5654 case AT_DetachPartition:
5655 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5656 cur_pass, context);
5657 Assert(cmd != NULL);
5658 /* ATPrepCmd ensures it must be a table */
5659 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5660 address = ATExecDetachPartition(wqueue, tab, rel,
5661 ((PartitionCmd *) cmd->def)->name,
5662 ((PartitionCmd *) cmd->def)->concurrent);
5663 break;
5665 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5666 break;
5667 default: /* oops */
5668 elog(ERROR, "unrecognized alter table type: %d",
5669 (int) cmd->subtype);
5670 break;
5671 }
5672
5673 /*
5674 * Report the subcommand to interested event triggers.
5675 */
5676 if (cmd)
5677 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5678
5679 /*
5680 * Bump the command counter to ensure the next subcommand in the sequence
5681 * can see the changes so far
5682 */
5684}
5685
5686/*
5687 * ATParseTransformCmd: perform parse transformation for one subcommand
5688 *
5689 * Returns the transformed subcommand tree, if there is one, else NULL.
5690 *
5691 * The parser may hand back additional AlterTableCmd(s) and/or other
5692 * utility statements, either before or after the original subcommand.
5693 * Other AlterTableCmds are scheduled into the appropriate slot of the
5694 * AlteredTableInfo (they had better be for later passes than the current one).
5695 * Utility statements that are supposed to happen before the AlterTableCmd
5696 * are executed immediately. Those that are supposed to happen afterwards
5697 * are added to the tab->afterStmts list to be done at the very end.
5698 */
5699static AlterTableCmd *
5701 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5702 AlterTablePass cur_pass, AlterTableUtilityContext *context)
5703{
5704 AlterTableCmd *newcmd = NULL;
5706 List *beforeStmts;
5707 List *afterStmts;
5708 ListCell *lc;
5709
5710 /* Gin up an AlterTableStmt with just this subcommand and this table */
5711 atstmt->relation =
5714 -1);
5715 atstmt->relation->inh = recurse;
5716 atstmt->cmds = list_make1(cmd);
5717 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5718 atstmt->missing_ok = false;
5719
5720 /* Transform the AlterTableStmt */
5722 atstmt,
5723 context->queryString,
5724 &beforeStmts,
5725 &afterStmts);
5726
5727 /* Execute any statements that should happen before these subcommand(s) */
5728 foreach(lc, beforeStmts)
5729 {
5730 Node *stmt = (Node *) lfirst(lc);
5731
5734 }
5735
5736 /* Examine the transformed subcommands and schedule them appropriately */
5737 foreach(lc, atstmt->cmds)
5738 {
5740 AlterTablePass pass;
5741
5742 /*
5743 * This switch need only cover the subcommand types that can be added
5744 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5745 * executing the subcommand immediately, as a substitute for the
5746 * original subcommand. (Note, however, that this does cause
5747 * AT_AddConstraint subcommands to be rescheduled into later passes,
5748 * which is important for index and foreign key constraints.)
5749 *
5750 * We assume we needn't do any phase-1 checks for added subcommands.
5751 */
5752 switch (cmd2->subtype)
5753 {
5754 case AT_AddIndex:
5755 pass = AT_PASS_ADD_INDEX;
5756 break;
5759 break;
5760 case AT_AddConstraint:
5761 /* Recursion occurs during execution phase */
5762 if (recurse)
5763 cmd2->recurse = true;
5764 switch (castNode(Constraint, cmd2->def)->contype)
5765 {
5766 case CONSTR_NOTNULL:
5767 pass = AT_PASS_COL_ATTRS;
5768 break;
5769 case CONSTR_PRIMARY:
5770 case CONSTR_UNIQUE:
5771 case CONSTR_EXCLUSION:
5773 break;
5774 default:
5776 break;
5777 }
5778 break;
5780 /* This command never recurses */
5781 /* No command-specific prep needed */
5782 pass = AT_PASS_MISC;
5783 break;
5784 default:
5785 pass = cur_pass;
5786 break;
5787 }
5788
5789 if (pass < cur_pass)
5790 {
5791 /* Cannot schedule into a pass we already finished */
5792 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5793 pass);
5794 }
5795 else if (pass > cur_pass)
5796 {
5797 /* OK, queue it up for later */
5798 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5799 }
5800 else
5801 {
5802 /*
5803 * We should see at most one subcommand for the current pass,
5804 * which is the transformed version of the original subcommand.
5805 */
5806 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5807 {
5808 /* Found the transformed version of our subcommand */
5809 newcmd = cmd2;
5810 }
5811 else
5812 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5813 pass);
5814 }
5815 }
5816
5817 /* Queue up any after-statements to happen at the end */
5818 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5819
5820 return newcmd;
5821}
5822
5823/*
5824 * ATRewriteTables: ALTER TABLE phase 3
5825 */
5826static void
5827ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5828 AlterTableUtilityContext *context)
5829{
5830 ListCell *ltab;
5831
5832 /* Go through each table that needs to be checked or rewritten */
5833 foreach(ltab, *wqueue)
5834 {
5835 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5836
5837 /* Relations without storage may be ignored here */
5838 if (!RELKIND_HAS_STORAGE(tab->relkind))
5839 continue;
5840
5841 /*
5842 * If we change column data types, the operation has to be propagated
5843 * to tables that use this table's rowtype as a column type.
5844 * tab->newvals will also be non-NULL in the case where we're adding a
5845 * column with a default. We choose to forbid that case as well,
5846 * since composite types might eventually support defaults.
5847 *
5848 * (Eventually we'll probably need to check for composite type
5849 * dependencies even when we're just scanning the table without a
5850 * rewrite, but at the moment a composite type does not enforce any
5851 * constraints, so it's not necessary/appropriate to enforce them just
5852 * during ALTER.)
5853 */
5854 if (tab->newvals != NIL || tab->rewrite > 0)
5855 {
5856 Relation rel;
5857
5858 rel = table_open(tab->relid, NoLock);
5859 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5860 table_close(rel, NoLock);
5861 }
5862
5863 /*
5864 * We only need to rewrite the table if at least one column needs to
5865 * be recomputed, or we are changing its persistence or access method.
5866 *
5867 * There are two reasons for requiring a rewrite when changing
5868 * persistence: on one hand, we need to ensure that the buffers
5869 * belonging to each of the two relations are marked with or without
5870 * BM_PERMANENT properly. On the other hand, since rewriting creates
5871 * and assigns a new relfilenumber, we automatically create or drop an
5872 * init fork for the relation as appropriate.
5873 */
5874 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5875 {
5876 /* Build a temporary relation and copy data */
5877 Relation OldHeap;
5878 Oid OIDNewHeap;
5879 Oid NewAccessMethod;
5880 Oid NewTableSpace;
5881 char persistence;
5882
5883 OldHeap = table_open(tab->relid, NoLock);
5884
5885 /*
5886 * We don't support rewriting of system catalogs; there are too
5887 * many corner cases and too little benefit. In particular this
5888 * is certainly not going to work for mapped catalogs.
5889 */
5890 if (IsSystemRelation(OldHeap))
5891 ereport(ERROR,
5892 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5893 errmsg("cannot rewrite system relation \"%s\"",
5894 RelationGetRelationName(OldHeap))));
5895
5896 if (RelationIsUsedAsCatalogTable(OldHeap))
5897 ereport(ERROR,
5898 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5899 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5900 RelationGetRelationName(OldHeap))));
5901
5902 /*
5903 * Don't allow rewrite on temp tables of other backends ... their
5904 * local buffer manager is not going to cope. (This is redundant
5905 * with the check in CheckAlterTableIsSafe, but for safety we'll
5906 * check here too.)
5907 */
5908 if (RELATION_IS_OTHER_TEMP(OldHeap))
5909 ereport(ERROR,
5910 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5911 errmsg("cannot rewrite temporary tables of other sessions")));
5912
5913 /*
5914 * Select destination tablespace (same as original unless user
5915 * requested a change)
5916 */
5917 if (tab->newTableSpace)
5918 NewTableSpace = tab->newTableSpace;
5919 else
5920 NewTableSpace = OldHeap->rd_rel->reltablespace;
5921
5922 /*
5923 * Select destination access method (same as original unless user
5924 * requested a change)
5925 */
5926 if (tab->chgAccessMethod)
5927 NewAccessMethod = tab->newAccessMethod;
5928 else
5929 NewAccessMethod = OldHeap->rd_rel->relam;
5930
5931 /*
5932 * Select persistence of transient table (same as original unless
5933 * user requested a change)
5934 */
5935 persistence = tab->chgPersistence ?
5936 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5937
5938 table_close(OldHeap, NoLock);
5939
5940 /*
5941 * Fire off an Event Trigger now, before actually rewriting the
5942 * table.
5943 *
5944 * We don't support Event Trigger for nested commands anywhere,
5945 * here included, and parsetree is given NULL when coming from
5946 * AlterTableInternal.
5947 *
5948 * And fire it only once.
5949 */
5950 if (parsetree)
5951 EventTriggerTableRewrite((Node *) parsetree,
5952 tab->relid,
5953 tab->rewrite);
5954
5955 /*
5956 * Create transient table that will receive the modified data.
5957 *
5958 * Ensure it is marked correctly as logged or unlogged. We have
5959 * to do this here so that buffers for the new relfilenumber will
5960 * have the right persistence set, and at the same time ensure
5961 * that the original filenumbers's buffers will get read in with
5962 * the correct setting (i.e. the original one). Otherwise a
5963 * rollback after the rewrite would possibly result with buffers
5964 * for the original filenumbers having the wrong persistence
5965 * setting.
5966 *
5967 * NB: This relies on swap_relation_files() also swapping the
5968 * persistence. That wouldn't work for pg_class, but that can't be
5969 * unlogged anyway.
5970 */
5971 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5972 persistence, lockmode);
5973
5974 /*
5975 * Copy the heap data into the new table with the desired
5976 * modifications, and test the current data within the table
5977 * against new constraints generated by ALTER TABLE commands.
5978 */
5979 ATRewriteTable(tab, OIDNewHeap);
5980
5981 /*
5982 * Swap the physical files of the old and new heaps, then rebuild
5983 * indexes and discard the old heap. We can use RecentXmin for
5984 * the table's new relfrozenxid because we rewrote all the tuples
5985 * in ATRewriteTable, so no older Xid remains in the table. Also,
5986 * we never try to swap toast tables by content, since we have no
5987 * interest in letting this code work on system catalogs.
5988 */
5989 finish_heap_swap(tab->relid, OIDNewHeap,
5990 false, false, true,
5992 RecentXmin,
5994 persistence);
5995
5996 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5997 }
5998 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5999 {
6000 if (tab->chgPersistence)
6002 }
6003 else
6004 {
6005 /*
6006 * If required, test the current data within the table against new
6007 * constraints generated by ALTER TABLE commands, but don't
6008 * rebuild data.
6009 */
6010 if (tab->constraints != NIL || tab->verify_new_notnull ||
6011 tab->partition_constraint != NULL)
6013
6014 /*
6015 * If we had SET TABLESPACE but no reason to reconstruct tuples,
6016 * just do a block-by-block copy.
6017 */
6018 if (tab->newTableSpace)
6019 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6020 }
6021
6022 /*
6023 * Also change persistence of owned sequences, so that it matches the
6024 * table persistence.
6025 */
6026 if (tab->chgPersistence)
6027 {
6028 List *seqlist = getOwnedSequences(tab->relid);
6029 ListCell *lc;
6030
6031 foreach(lc, seqlist)
6032 {
6033 Oid seq_relid = lfirst_oid(lc);
6034
6036 }
6037 }
6038 }
6039
6040 /*
6041 * Foreign key constraints are checked in a final pass, since (a) it's
6042 * generally best to examine each one separately, and (b) it's at least
6043 * theoretically possible that we have changed both relations of the
6044 * foreign key, and we'd better have finished both rewrites before we try
6045 * to read the tables.
6046 */
6047 foreach(ltab, *wqueue)
6048 {
6049 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6050 Relation rel = NULL;
6051 ListCell *lcon;
6052
6053 /* Relations without storage may be ignored here too */
6054 if (!RELKIND_HAS_STORAGE(tab->relkind))
6055 continue;
6056
6057 foreach(lcon, tab->constraints)
6058 {
6059 NewConstraint *con = lfirst(lcon);
6060
6061 if (con->contype == CONSTR_FOREIGN)
6062 {
6063 Constraint *fkconstraint = (Constraint *) con->qual;
6064 Relation refrel;
6065
6066 if (rel == NULL)
6067 {
6068 /* Long since locked, no need for another */
6069 rel = table_open(tab->relid, NoLock);
6070 }
6071
6072 refrel = table_open(con->refrelid, RowShareLock);
6073
6074 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6075 con->refindid,
6076 con->conid,
6077 con->conwithperiod);
6078
6079 /*
6080 * No need to mark the constraint row as validated, we did
6081 * that when we inserted the row earlier.
6082 */
6083
6084 table_close(refrel, NoLock);
6085 }
6086 }
6087
6088 if (rel)
6089 table_close(rel, NoLock);
6090 }
6091
6092 /* Finally, run any afterStmts that were queued up */
6093 foreach(ltab, *wqueue)
6094 {
6095 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6096 ListCell *lc;
6097
6098 foreach(lc, tab->afterStmts)
6099 {
6100 Node *stmt = (Node *) lfirst(lc);
6101
6104 }
6105 }
6106}
6107
6108/*
6109 * ATRewriteTable: scan or rewrite one table
6110 *
6111 * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6112 * must already hold AccessExclusiveLock on it.
6113 */
6114static void
6116{
6117 Relation oldrel;
6118 Relation newrel;
6119 TupleDesc oldTupDesc;
6120 TupleDesc newTupDesc;
6121 bool needscan = false;
6122 List *notnull_attrs;
6123 List *notnull_virtual_attrs;
6124 int i;
6125 ListCell *l;
6126 EState *estate;
6127 CommandId mycid;
6128 BulkInsertState bistate;
6129 int ti_options;
6130 ExprState *partqualstate = NULL;
6131
6132 /*
6133 * Open the relation(s). We have surely already locked the existing
6134 * table.
6135 */
6136 oldrel = table_open(tab->relid, NoLock);
6137 oldTupDesc = tab->oldDesc;
6138 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6139
6140 if (OidIsValid(OIDNewHeap))
6141 {
6143 false));
6144 newrel = table_open(OIDNewHeap, NoLock);
6145 }
6146 else
6147 newrel = NULL;
6148
6149 /*
6150 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6151 * is empty, so don't bother using it.
6152 */
6153 if (newrel)
6154 {
6155 mycid = GetCurrentCommandId(true);
6156 bistate = GetBulkInsertState();
6157 ti_options = TABLE_INSERT_SKIP_FSM;
6158 }
6159 else
6160 {
6161 /* keep compiler quiet about using these uninitialized */
6162 mycid = 0;
6163 bistate = NULL;
6164 ti_options = 0;
6165 }
6166
6167 /*
6168 * Generate the constraint and default execution states
6169 */
6170
6171 estate = CreateExecutorState();
6172
6173 /* Build the needed expression execution states */
6174 foreach(l, tab->constraints)
6175 {
6176 NewConstraint *con = lfirst(l);
6177
6178 switch (con->contype)
6179 {
6180 case CONSTR_CHECK:
6181 needscan = true;
6182 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6183 break;
6184 case CONSTR_FOREIGN:
6185 /* Nothing to do here */
6186 break;
6187 default:
6188 elog(ERROR, "unrecognized constraint type: %d",
6189 (int) con->contype);
6190 }
6191 }
6192
6193 /* Build expression execution states for partition check quals */
6194 if (tab->partition_constraint)
6195 {
6196 needscan = true;
6197 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6198 }
6199
6200 foreach(l, tab->newvals)
6201 {
6202 NewColumnValue *ex = lfirst(l);
6203
6204 /* expr already planned */
6205 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6206 }
6207
6208 notnull_attrs = notnull_virtual_attrs = NIL;
6209 if (newrel || tab->verify_new_notnull)
6210 {
6211 /*
6212 * If we are rebuilding the tuples OR if we added any new but not
6213 * verified not-null constraints, check all *valid* not-null
6214 * constraints. This is a bit of overkill but it minimizes risk of
6215 * bugs.
6216 *
6217 * notnull_attrs does *not* collect attribute numbers for valid
6218 * not-null constraints over virtual generated columns; instead, they
6219 * are collected in notnull_virtual_attrs for verification elsewhere.
6220 */
6221 for (i = 0; i < newTupDesc->natts; i++)
6222 {
6223 CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6224
6225 if (attr->attnullability == ATTNULLABLE_VALID &&
6226 !attr->attisdropped)
6227 {
6228 Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6229
6230 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6231 notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6232 else
6233 notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6234 wholeatt->attnum);
6235 }
6236 }
6237 if (notnull_attrs || notnull_virtual_attrs)
6238 needscan = true;
6239 }
6240
6241 if (newrel || needscan)
6242 {
6243 ExprContext *econtext;
6244 TupleTableSlot *oldslot;
6245 TupleTableSlot *newslot;
6246 TableScanDesc scan;
6247 MemoryContext oldCxt;
6248 List *dropped_attrs = NIL;
6249 ListCell *lc;
6250 Snapshot snapshot;
6251 ResultRelInfo *rInfo = NULL;
6252
6253 /*
6254 * When adding or changing a virtual generated column with a not-null
6255 * constraint, we need to evaluate whether the generation expression
6256 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6257 * prepare a dummy ResultRelInfo.
6258 */
6259 if (notnull_virtual_attrs != NIL)
6260 {
6261 MemoryContext oldcontext;
6262
6263 Assert(newTupDesc->constr->has_generated_virtual);
6264 Assert(newTupDesc->constr->has_not_null);
6265 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6266 rInfo = makeNode(ResultRelInfo);
6267 InitResultRelInfo(rInfo,
6268 oldrel,
6269 0, /* dummy rangetable index */
6270 NULL,
6271 estate->es_instrument);
6272 MemoryContextSwitchTo(oldcontext);
6273 }
6274
6275 if (newrel)
6277 (errmsg_internal("rewriting table \"%s\"",
6278 RelationGetRelationName(oldrel))));
6279 else
6281 (errmsg_internal("verifying table \"%s\"",
6282 RelationGetRelationName(oldrel))));
6283
6284 if (newrel)
6285 {
6286 /*
6287 * All predicate locks on the tuples or pages are about to be made
6288 * invalid, because we move tuples around. Promote them to
6289 * relation locks.
6290 */
6292 }
6293
6294 econtext = GetPerTupleExprContext(estate);
6295
6296 /*
6297 * Create necessary tuple slots. When rewriting, two slots are needed,
6298 * otherwise one suffices. In the case where one slot suffices, we
6299 * need to use the new tuple descriptor, otherwise some constraints
6300 * can't be evaluated. Note that even when the tuple layout is the
6301 * same and no rewrite is required, the tupDescs might not be
6302 * (consider ADD COLUMN without a default).
6303 */
6304 if (tab->rewrite)
6305 {
6306 Assert(newrel != NULL);
6307 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6308 table_slot_callbacks(oldrel));
6309 newslot = MakeSingleTupleTableSlot(newTupDesc,
6310 table_slot_callbacks(newrel));
6311
6312 /*
6313 * Set all columns in the new slot to NULL initially, to ensure
6314 * columns added as part of the rewrite are initialized to NULL.
6315 * That is necessary as tab->newvals will not contain an
6316 * expression for columns with a NULL default, e.g. when adding a
6317 * column without a default together with a column with a default
6318 * requiring an actual rewrite.
6319 */
6320 ExecStoreAllNullTuple(newslot);
6321 }
6322 else
6323 {
6324 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6325 table_slot_callbacks(oldrel));
6326 newslot = NULL;
6327 }
6328
6329 /*
6330 * Any attributes that are dropped according to the new tuple
6331 * descriptor can be set to NULL. We precompute the list of dropped
6332 * attributes to avoid needing to do so in the per-tuple loop.
6333 */
6334 for (i = 0; i < newTupDesc->natts; i++)
6335 {
6336 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6337 dropped_attrs = lappend_int(dropped_attrs, i);
6338 }
6339
6340 /*
6341 * Scan through the rows, generating a new row if needed and then
6342 * checking all the constraints.
6343 */
6344 snapshot = RegisterSnapshot(GetLatestSnapshot());
6345 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6346
6347 /*
6348 * Switch to per-tuple memory context and reset it for each tuple
6349 * produced, so we don't leak memory.
6350 */
6352
6353 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6354 {
6355 TupleTableSlot *insertslot;
6356
6357 if (tab->rewrite > 0)
6358 {
6359 /* Extract data from old tuple */
6360 slot_getallattrs(oldslot);
6361 ExecClearTuple(newslot);
6362
6363 /* copy attributes */
6364 memcpy(newslot->tts_values, oldslot->tts_values,
6365 sizeof(Datum) * oldslot->tts_nvalid);
6366 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6367 sizeof(bool) * oldslot->tts_nvalid);
6368
6369 /* Set dropped attributes to null in new tuple */
6370 foreach(lc, dropped_attrs)
6371 newslot->tts_isnull[lfirst_int(lc)] = true;
6372
6373 /*
6374 * Constraints and GENERATED expressions might reference the
6375 * tableoid column, so fill tts_tableOid with the desired
6376 * value. (We must do this each time, because it gets
6377 * overwritten with newrel's OID during storing.)
6378 */
6379 newslot->tts_tableOid = RelationGetRelid(oldrel);
6380
6381 /*
6382 * Process supplied expressions to replace selected columns.
6383 *
6384 * First, evaluate expressions whose inputs come from the old
6385 * tuple.
6386 */
6387 econtext->ecxt_scantuple = oldslot;
6388
6389 foreach(l, tab->newvals)
6390 {
6391 NewColumnValue *ex = lfirst(l);
6392
6393 if (ex->is_generated)
6394 continue;
6395
6396 newslot->tts_values[ex->attnum - 1]
6397 = ExecEvalExpr(ex->exprstate,
6398 econtext,
6399 &newslot->tts_isnull[ex->attnum - 1]);
6400 }
6401
6402 ExecStoreVirtualTuple(newslot);
6403
6404 /*
6405 * Now, evaluate any expressions whose inputs come from the
6406 * new tuple. We assume these columns won't reference each
6407 * other, so that there's no ordering dependency.
6408 */
6409 econtext->ecxt_scantuple = newslot;
6410
6411 foreach(l, tab->newvals)
6412 {
6413 NewColumnValue *ex = lfirst(l);
6414
6415 if (!ex->is_generated)
6416 continue;
6417
6418 newslot->tts_values[ex->attnum - 1]
6419 = ExecEvalExpr(ex->exprstate,
6420 econtext,
6421 &newslot->tts_isnull[ex->attnum - 1]);
6422 }
6423
6424 insertslot = newslot;
6425 }
6426 else
6427 {
6428 /*
6429 * If there's no rewrite, old and new table are guaranteed to
6430 * have the same AM, so we can just use the old slot to verify
6431 * new constraints etc.
6432 */
6433 insertslot = oldslot;
6434 }
6435
6436 /* Now check any constraints on the possibly-changed tuple */
6437 econtext->ecxt_scantuple = insertslot;
6438
6439 foreach_int(attn, notnull_attrs)
6440 {
6441 if (slot_attisnull(insertslot, attn))
6442 {
6443 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6444
6445 ereport(ERROR,
6446 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6447 errmsg("column \"%s\" of relation \"%s\" contains null values",
6448 NameStr(attr->attname),
6449 RelationGetRelationName(oldrel)),
6450 errtablecol(oldrel, attn)));
6451 }
6452 }
6453
6454 if (notnull_virtual_attrs != NIL)
6455 {
6457
6458 attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6459 estate,
6460 notnull_virtual_attrs);
6462 {
6463 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6464
6465 ereport(ERROR,
6466 errcode(ERRCODE_NOT_NULL_VIOLATION),
6467 errmsg("column \"%s\" of relation \"%s\" contains null values",
6468 NameStr(attr->attname),
6469 RelationGetRelationName(oldrel)),
6470 errtablecol(oldrel, attnum));
6471 }
6472 }
6473
6474 foreach(l, tab->constraints)
6475 {
6476 NewConstraint *con = lfirst(l);
6477
6478 switch (con->contype)
6479 {
6480 case CONSTR_CHECK:
6481 if (!ExecCheck(con->qualstate, econtext))
6482 ereport(ERROR,
6483 (errcode(ERRCODE_CHECK_VIOLATION),
6484 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6485 con->name,
6486 RelationGetRelationName(oldrel)),
6487 errtableconstraint(oldrel, con->name)));
6488 break;
6489 case CONSTR_NOTNULL:
6490 case CONSTR_FOREIGN:
6491 /* Nothing to do here */
6492 break;
6493 default:
6494 elog(ERROR, "unrecognized constraint type: %d",
6495 (int) con->contype);
6496 }
6497 }
6498
6499 if (partqualstate && !ExecCheck(partqualstate, econtext))
6500 {
6501 if (tab->validate_default)
6502 ereport(ERROR,
6503 (errcode(ERRCODE_CHECK_VIOLATION),
6504 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6505 RelationGetRelationName(oldrel)),
6506 errtable(oldrel)));
6507 else
6508 ereport(ERROR,
6509 (errcode(ERRCODE_CHECK_VIOLATION),
6510 errmsg("partition constraint of relation \"%s\" is violated by some row",
6511 RelationGetRelationName(oldrel)),
6512 errtable(oldrel)));
6513 }
6514
6515 /* Write the tuple out to the new relation */
6516 if (newrel)
6517 table_tuple_insert(newrel, insertslot, mycid,
6518 ti_options, bistate);
6519
6520 ResetExprContext(econtext);
6521
6523 }
6524
6525 MemoryContextSwitchTo(oldCxt);
6526 table_endscan(scan);
6527 UnregisterSnapshot(snapshot);
6528
6530 if (newslot)
6532 }
6533
6534 FreeExecutorState(estate);
6535
6536 table_close(oldrel, NoLock);
6537 if (newrel)
6538 {
6539 FreeBulkInsertState(bistate);
6540
6541 table_finish_bulk_insert(newrel, ti_options);
6542
6543 table_close(newrel, NoLock);
6544 }
6545}
6546
6547/*
6548 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6549 */
6550static AlteredTableInfo *
6552{
6553 Oid relid = RelationGetRelid(rel);
6554 AlteredTableInfo *tab;
6555 ListCell *ltab;
6556
6557 foreach(ltab, *wqueue)
6558 {
6559 tab = (AlteredTableInfo *) lfirst(ltab);
6560 if (tab->relid == relid)
6561 return tab;
6562 }
6563
6564 /*
6565 * Not there, so add it. Note that we make a copy of the relation's
6566 * existing descriptor before anything interesting can happen to it.
6567 */
6568 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6569 tab->relid = relid;
6570 tab->rel = NULL; /* set later */
6571 tab->relkind = rel->rd_rel->relkind;
6574 tab->chgAccessMethod = false;
6576 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6577 tab->chgPersistence = false;
6578
6579 *wqueue = lappend(*wqueue, tab);
6580
6581 return tab;
6582}
6583
6584static const char *
6586{
6587 switch (cmdtype)
6588 {
6589 case AT_AddColumn:
6590 case AT_AddColumnToView:
6591 return "ADD COLUMN";
6592 case AT_ColumnDefault:
6594 return "ALTER COLUMN ... SET DEFAULT";
6595 case AT_DropNotNull:
6596 return "ALTER COLUMN ... DROP NOT NULL";
6597 case AT_SetNotNull:
6598 return "ALTER COLUMN ... SET NOT NULL";
6599 case AT_SetExpression:
6600 return "ALTER COLUMN ... SET EXPRESSION";
6601 case AT_DropExpression:
6602 return "ALTER COLUMN ... DROP EXPRESSION";
6603 case AT_SetStatistics:
6604 return "ALTER COLUMN ... SET STATISTICS";
6605 case AT_SetOptions:
6606 return "ALTER COLUMN ... SET";
6607 case AT_ResetOptions:
6608 return "ALTER COLUMN ... RESET";
6609 case AT_SetStorage:
6610 return "ALTER COLUMN ... SET STORAGE";
6611 case AT_SetCompression:
6612 return "ALTER COLUMN ... SET COMPRESSION";
6613 case AT_DropColumn:
6614 return "DROP COLUMN";
6615 case AT_AddIndex:
6616 case AT_ReAddIndex:
6617 return NULL; /* not real grammar */
6618 case AT_AddConstraint:
6619 case AT_ReAddConstraint:
6622 return "ADD CONSTRAINT";
6623 case AT_AlterConstraint:
6624 return "ALTER CONSTRAINT";
6626 return "VALIDATE CONSTRAINT";
6627 case AT_DropConstraint:
6628 return "DROP CONSTRAINT";
6629 case AT_ReAddComment:
6630 return NULL; /* not real grammar */
6631 case AT_AlterColumnType:
6632 return "ALTER COLUMN ... SET DATA TYPE";
6634 return "ALTER COLUMN ... OPTIONS";
6635 case AT_ChangeOwner:
6636 return "OWNER TO";
6637 case AT_ClusterOn:
6638 return "CLUSTER ON";
6639 case AT_DropCluster:
6640 return "SET WITHOUT CLUSTER";
6641 case AT_SetAccessMethod:
6642 return "SET ACCESS METHOD";
6643 case AT_SetLogged:
6644 return "SET LOGGED";
6645 case AT_SetUnLogged:
6646 return "SET UNLOGGED";
6647 case AT_DropOids:
6648 return "SET WITHOUT OIDS";
6649 case AT_SetTableSpace:
6650 return "SET TABLESPACE";
6651 case AT_SetRelOptions:
6652 return "SET";
6653 case AT_ResetRelOptions:
6654 return "RESET";
6656 return NULL; /* not real grammar */
6657 case AT_EnableTrig:
6658 return "ENABLE TRIGGER";
6660 return "ENABLE ALWAYS TRIGGER";
6662 return "ENABLE REPLICA TRIGGER";
6663 case AT_DisableTrig:
6664 return "DISABLE TRIGGER";
6665 case AT_EnableTrigAll:
6666 return "ENABLE TRIGGER ALL";
6667 case AT_DisableTrigAll:
6668 return "DISABLE TRIGGER ALL";
6669 case AT_EnableTrigUser:
6670 return "ENABLE TRIGGER USER";
6671 case AT_DisableTrigUser:
6672 return "DISABLE TRIGGER USER";
6673 case AT_EnableRule:
6674 return "ENABLE RULE";
6676 return "ENABLE ALWAYS RULE";
6678 return "ENABLE REPLICA RULE";
6679 case AT_DisableRule:
6680 return "DISABLE RULE";
6681 case AT_AddInherit:
6682 return "INHERIT";
6683 case AT_DropInherit:
6684 return "NO INHERIT";
6685 case AT_AddOf:
6686 return "OF";
6687 case AT_DropOf:
6688 return "NOT OF";
6689 case AT_ReplicaIdentity:
6690 return "REPLICA IDENTITY";
6692 return "ENABLE ROW SECURITY";
6694 return "DISABLE ROW SECURITY";
6696 return "FORCE ROW SECURITY";
6698 return "NO FORCE ROW SECURITY";
6699 case AT_GenericOptions:
6700 return "OPTIONS";
6701 case AT_AttachPartition:
6702 return "ATTACH PARTITION";
6703 case AT_DetachPartition:
6704 return "DETACH PARTITION";
6706 return "DETACH PARTITION ... FINALIZE";
6707 case AT_AddIdentity:
6708 return "ALTER COLUMN ... ADD IDENTITY";
6709 case AT_SetIdentity:
6710 return "ALTER COLUMN ... SET";
6711 case AT_DropIdentity:
6712 return "ALTER COLUMN ... DROP IDENTITY";
6713 case AT_ReAddStatistics:
6714 return NULL; /* not real grammar */
6715 }
6716
6717 return NULL;
6718}
6719
6720/*
6721 * ATSimplePermissions
6722 *
6723 * - Ensure that it is a relation (or possibly a view)
6724 * - Ensure this user is the owner
6725 * - Ensure that it is not a system table
6726 */
6727static void
6728ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6729{
6730 int actual_target;
6731
6732 switch (rel->rd_rel->relkind)
6733 {
6734 case RELKIND_RELATION:
6735 actual_target = ATT_TABLE;
6736 break;
6737 case RELKIND_PARTITIONED_TABLE:
6738 actual_target = ATT_PARTITIONED_TABLE;
6739 break;
6740 case RELKIND_VIEW:
6741 actual_target = ATT_VIEW;
6742 break;
6743 case RELKIND_MATVIEW:
6744 actual_target = ATT_MATVIEW;
6745 break;
6746 case RELKIND_INDEX:
6747 actual_target = ATT_INDEX;
6748 break;
6749 case RELKIND_PARTITIONED_INDEX:
6750 actual_target = ATT_PARTITIONED_INDEX;
6751 break;
6752 case RELKIND_COMPOSITE_TYPE:
6753 actual_target = ATT_COMPOSITE_TYPE;
6754 break;
6755 case RELKIND_FOREIGN_TABLE:
6756 actual_target = ATT_FOREIGN_TABLE;
6757 break;
6758 case RELKIND_SEQUENCE:
6759 actual_target = ATT_SEQUENCE;
6760 break;
6761 default:
6762 actual_target = 0;
6763 break;
6764 }
6765
6766 /* Wrong target type? */
6767 if ((actual_target & allowed_targets) == 0)
6768 {
6769 const char *action_str = alter_table_type_to_string(cmdtype);
6770
6771 if (action_str)
6772 ereport(ERROR,
6773 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6774 /* translator: %s is a group of some SQL keywords */
6775 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6776 action_str, RelationGetRelationName(rel)),
6777 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6778 else
6779 /* internal error? */
6780 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6782 }
6783
6784 /* Permissions checks */
6785 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6788
6790 ereport(ERROR,
6791 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6792 errmsg("permission denied: \"%s\" is a system catalog",
6794}
6795
6796/*
6797 * ATSimpleRecursion
6798 *
6799 * Simple table recursion sufficient for most ALTER TABLE operations.
6800 * All direct and indirect children are processed in an unspecified order.
6801 * Note that if a child inherits from the original table via multiple
6802 * inheritance paths, it will be visited just once.
6803 */
6804static void
6806 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6807 AlterTableUtilityContext *context)
6808{
6809 /*
6810 * Propagate to children, if desired and if there are (or might be) any
6811 * children.
6812 */
6813 if (recurse && rel->rd_rel->relhassubclass)
6814 {
6815 Oid relid = RelationGetRelid(rel);
6816 ListCell *child;
6817 List *children;
6818
6819 children = find_all_inheritors(relid, lockmode, NULL);
6820
6821 /*
6822 * find_all_inheritors does the recursive search of the inheritance
6823 * hierarchy, so all we have to do is process all of the relids in the
6824 * list that it returns.
6825 */
6826 foreach(child, children)
6827 {
6828 Oid childrelid = lfirst_oid(child);
6829 Relation childrel;
6830
6831 if (childrelid == relid)
6832 continue;
6833 /* find_all_inheritors already got lock */
6834 childrel = relation_open(childrelid, NoLock);
6835 CheckAlterTableIsSafe(childrel);
6836 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6837 relation_close(childrel, NoLock);
6838 }
6839 }
6840}
6841
6842/*
6843 * Obtain list of partitions of the given table, locking them all at the given
6844 * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6845 *
6846 * This function is a no-op if the given relation is not a partitioned table;
6847 * in particular, nothing is done if it's a legacy inheritance parent.
6848 */
6849static void
6851{
6852 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6853 {
6854 List *inh;
6855 ListCell *cell;
6856
6857 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6858 /* first element is the parent rel; must ignore it */
6859 for_each_from(cell, inh, 1)
6860 {
6861 Relation childrel;
6862
6863 /* find_all_inheritors already got lock */
6864 childrel = table_open(lfirst_oid(cell), NoLock);
6865 CheckAlterTableIsSafe(childrel);
6866 table_close(childrel, NoLock);
6867 }
6868 list_free(inh);
6869 }
6870}
6871
6872/*
6873 * ATTypedTableRecursion
6874 *
6875 * Propagate ALTER TYPE operations to the typed tables of that type.
6876 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6877 * recursion to inheritance children of the typed tables.
6878 */
6879static void
6881 LOCKMODE lockmode, AlterTableUtilityContext *context)
6882{
6883 ListCell *child;
6884 List *children;
6885
6886 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6887
6888 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6890 cmd->behavior);
6891
6892 foreach(child, children)
6893 {
6894 Oid childrelid = lfirst_oid(child);
6895 Relation childrel;
6896
6897 childrel = relation_open(childrelid, lockmode);
6898 CheckAlterTableIsSafe(childrel);
6899 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6900 relation_close(childrel, NoLock);
6901 }
6902}
6903
6904
6905/*
6906 * find_composite_type_dependencies
6907 *
6908 * Check to see if the type "typeOid" is being used as a column in some table
6909 * (possibly nested several levels deep in composite types, arrays, etc!).
6910 * Eventually, we'd like to propagate the check or rewrite operation
6911 * into such tables, but for now, just error out if we find any.
6912 *
6913 * Caller should provide either the associated relation of a rowtype,
6914 * or a type name (not both) for use in the error message, if any.
6915 *
6916 * Note that "typeOid" is not necessarily a composite type; it could also be
6917 * another container type such as an array or range, or a domain over one of
6918 * these things. The name of this function is therefore somewhat historical,
6919 * but it's not worth changing.
6920 *
6921 * We assume that functions and views depending on the type are not reasons
6922 * to reject the ALTER. (How safe is this really?)
6923 */
6924void
6926 const char *origTypeName)
6927{
6928 Relation depRel;
6929 ScanKeyData key[2];
6930 SysScanDesc depScan;
6931 HeapTuple depTup;
6932
6933 /* since this function recurses, it could be driven to stack overflow */
6935
6936 /*
6937 * We scan pg_depend to find those things that depend on the given type.
6938 * (We assume we can ignore refobjsubid for a type.)
6939 */
6940 depRel = table_open(DependRelationId, AccessShareLock);
6941
6942 ScanKeyInit(&key[0],
6943 Anum_pg_depend_refclassid,
6944 BTEqualStrategyNumber, F_OIDEQ,
6945 ObjectIdGetDatum(TypeRelationId));
6946 ScanKeyInit(&key[1],
6947 Anum_pg_depend_refobjid,
6948 BTEqualStrategyNumber, F_OIDEQ,
6949 ObjectIdGetDatum(typeOid));
6950
6951 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6952 NULL, 2, key);
6953
6954 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6955 {
6956 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6957 Relation rel;
6958 TupleDesc tupleDesc;
6960
6961 /* Check for directly dependent types */
6962 if (pg_depend->classid == TypeRelationId)
6963 {
6964 /*
6965 * This must be an array, domain, or range containing the given
6966 * type, so recursively check for uses of this type. Note that
6967 * any error message will mention the original type not the
6968 * container; this is intentional.
6969 */
6970 find_composite_type_dependencies(pg_depend->objid,
6971 origRelation, origTypeName);
6972 continue;
6973 }
6974
6975 /* Else, ignore dependees that aren't relations */
6976 if (pg_depend->classid != RelationRelationId)
6977 continue;
6978
6979 rel = relation_open(pg_depend->objid, AccessShareLock);
6980 tupleDesc = RelationGetDescr(rel);
6981
6982 /*
6983 * If objsubid identifies a specific column, refer to that in error
6984 * messages. Otherwise, search to see if there's a user column of the
6985 * type. (We assume system columns are never of interesting types.)
6986 * The search is needed because an index containing an expression
6987 * column of the target type will just be recorded as a whole-relation
6988 * dependency. If we do not find a column of the type, the dependency
6989 * must indicate that the type is transiently referenced in an index
6990 * expression but not stored on disk, which we assume is OK, just as
6991 * we do for references in views. (It could also be that the target
6992 * type is embedded in some container type that is stored in an index
6993 * column, but the previous recursion should catch such cases.)
6994 */
6995 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6996 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6997 else
6998 {
6999 att = NULL;
7000 for (int attno = 1; attno <= tupleDesc->natts; attno++)
7001 {
7002 att = TupleDescAttr(tupleDesc, attno - 1);
7003 if (att->atttypid == typeOid && !att->attisdropped)
7004 break;
7005 att = NULL;
7006 }
7007 if (att == NULL)
7008 {
7009 /* No such column, so assume OK */
7011 continue;
7012 }
7013 }
7014
7015 /*
7016 * We definitely should reject if the relation has storage. If it's
7017 * partitioned, then perhaps we don't have to reject: if there are
7018 * partitions then we'll fail when we find one, else there is no
7019 * stored data to worry about. However, it's possible that the type
7020 * change would affect conclusions about whether the type is sortable
7021 * or hashable and thus (if it's a partitioning column) break the
7022 * partitioning rule. For now, reject for partitioned rels too.
7023 */
7024 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7025 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7026 {
7027 if (origTypeName)
7028 ereport(ERROR,
7029 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7030 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7031 origTypeName,
7033 NameStr(att->attname))));
7034 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7035 ereport(ERROR,
7036 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7037 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7038 RelationGetRelationName(origRelation),
7040 NameStr(att->attname))));
7041 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7042 ereport(ERROR,
7043 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7044 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7045 RelationGetRelationName(origRelation),
7047 NameStr(att->attname))));
7048 else
7049 ereport(ERROR,
7050 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7051 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7052 RelationGetRelationName(origRelation),
7054 NameStr(att->attname))));
7055 }
7056 else if (OidIsValid(rel->rd_rel->reltype))
7057 {
7058 /*
7059 * A view or composite type itself isn't a problem, but we must
7060 * recursively check for indirect dependencies via its rowtype.
7061 */
7063 origRelation, origTypeName);
7064 }
7065
7067 }
7068
7069 systable_endscan(depScan);
7070
7072}
7073
7074
7075/*
7076 * find_typed_table_dependencies
7077 *
7078 * Check to see if a composite type is being used as the type of a
7079 * typed table. Abort if any are found and behavior is RESTRICT.
7080 * Else return the list of tables.
7081 */
7082static List *
7083find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7084{
7085 Relation classRel;
7086 ScanKeyData key[1];
7087 TableScanDesc scan;
7088 HeapTuple tuple;
7089 List *result = NIL;
7090
7091 classRel = table_open(RelationRelationId, AccessShareLock);
7092
7093 ScanKeyInit(&key[0],
7094 Anum_pg_class_reloftype,
7095 BTEqualStrategyNumber, F_OIDEQ,
7096 ObjectIdGetDatum(typeOid));
7097
7098 scan = table_beginscan_catalog(classRel, 1, key);
7099
7100 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7101 {
7102 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7103
7104 if (behavior == DROP_RESTRICT)
7105 ereport(ERROR,
7106 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7107 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7108 typeName),
7109 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7110 else
7111 result = lappend_oid(result, classform->oid);
7112 }
7113
7114 table_endscan(scan);
7115 table_close(classRel, AccessShareLock);
7116
7117 return result;
7118}
7119
7120
7121/*
7122 * check_of_type
7123 *
7124 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7125 * isn't suitable, throw an error. Currently, we require that the type
7126 * originated with CREATE TYPE AS. We could support any row type, but doing so
7127 * would require handling a number of extra corner cases in the DDL commands.
7128 * (Also, allowing domain-over-composite would open up a can of worms about
7129 * whether and how the domain's constraints should apply to derived tables.)
7130 */
7131void
7133{
7134 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7135 bool typeOk = false;
7136
7137 if (typ->typtype == TYPTYPE_COMPOSITE)
7138 {
7139 Relation typeRelation;
7140
7141 Assert(OidIsValid(typ->typrelid));
7142 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7143 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7144
7145 /*
7146 * Close the parent rel, but keep our AccessShareLock on it until xact
7147 * commit. That will prevent someone else from deleting or ALTERing
7148 * the type before the typed table creation/conversion commits.
7149 */
7150 relation_close(typeRelation, NoLock);
7151
7152 if (!typeOk)
7153 ereport(ERROR,
7154 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7155 errmsg("type %s is the row type of another table",
7156 format_type_be(typ->oid)),
7157 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7158 }
7159 else
7160 ereport(ERROR,
7161 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7162 errmsg("type %s is not a composite type",
7163 format_type_be(typ->oid))));
7164}
7165
7166
7167/*
7168 * ALTER TABLE ADD COLUMN
7169 *
7170 * Adds an additional attribute to a relation making the assumption that
7171 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7172 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7173 * AlterTableCmd's.
7174 *
7175 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7176 * have to decide at runtime whether to recurse or not depending on whether we
7177 * actually add a column or merely merge with an existing column. (We can't
7178 * check this in a static pre-pass because it won't handle multiple inheritance
7179 * situations correctly.)
7180 */
7181static void
7182ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7183 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7184 AlterTableUtilityContext *context)
7185{
7186 if (rel->rd_rel->reloftype && !recursing)
7187 ereport(ERROR,
7188 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7189 errmsg("cannot add column to typed table")));
7190
7191 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7192 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7193
7194 if (recurse && !is_view)
7195 cmd->recurse = true;
7196}
7197
7198/*
7199 * Add a column to a table. The return value is the address of the
7200 * new column in the parent relation.
7201 *
7202 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7203 * copy (but that happens only after we check for IF NOT EXISTS).
7204 */
7205static ObjectAddress
7207 AlterTableCmd **cmd, bool recurse, bool recursing,
7208 LOCKMODE lockmode, AlterTablePass cur_pass,
7209 AlterTableUtilityContext *context)
7210{
7211 Oid myrelid = RelationGetRelid(rel);
7212 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7213 bool if_not_exists = (*cmd)->missing_ok;
7214 Relation pgclass,
7215 attrdesc;
7216 HeapTuple reltup;
7217 Form_pg_class relform;
7218 Form_pg_attribute attribute;
7219 int newattnum;
7220 char relkind;
7221 Expr *defval;
7222 List *children;
7223 ListCell *child;
7224 AlterTableCmd *childcmd;
7225 ObjectAddress address;
7226 TupleDesc tupdesc;
7227
7228 /* since this function recurses, it could be driven to stack overflow */
7230
7231 /* At top level, permission check was done in ATPrepCmd, else do it */
7232 if (recursing)
7233 ATSimplePermissions((*cmd)->subtype, rel,
7235
7236 if (rel->rd_rel->relispartition && !recursing)
7237 ereport(ERROR,
7238 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7239 errmsg("cannot add column to a partition")));
7240
7241 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7242
7243 /*
7244 * Are we adding the column to a recursion child? If so, check whether to
7245 * merge with an existing definition for the column. If we do merge, we
7246 * must not recurse. Children will already have the column, and recursing
7247 * into them would mess up attinhcount.
7248 */
7249 if (colDef->inhcount > 0)
7250 {
7251 HeapTuple tuple;
7252
7253 /* Does child already have a column by this name? */
7254 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7255 if (HeapTupleIsValid(tuple))
7256 {
7257 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7258 Oid ctypeId;
7259 int32 ctypmod;
7260 Oid ccollid;
7261
7262 /* Child column must match on type, typmod, and collation */
7263 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7264 if (ctypeId != childatt->atttypid ||
7265 ctypmod != childatt->atttypmod)
7266 ereport(ERROR,
7267 (errcode(ERRCODE_DATATYPE_MISMATCH),
7268 errmsg("child table \"%s\" has different type for column \"%s\"",
7269 RelationGetRelationName(rel), colDef->colname)));
7270 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7271 if (ccollid != childatt->attcollation)
7272 ereport(ERROR,
7273 (errcode(ERRCODE_COLLATION_MISMATCH),
7274 errmsg("child table \"%s\" has different collation for column \"%s\"",
7275 RelationGetRelationName(rel), colDef->colname),
7276 errdetail("\"%s\" versus \"%s\"",
7277 get_collation_name(ccollid),
7278 get_collation_name(childatt->attcollation))));
7279
7280 /* Bump the existing child att's inhcount */
7281 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7282 &childatt->attinhcount))
7283 ereport(ERROR,
7284 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7285 errmsg("too many inheritance parents"));
7286 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7287
7288 heap_freetuple(tuple);
7289
7290 /* Inform the user about the merge */
7292 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7293 colDef->colname, RelationGetRelationName(rel))));
7294
7295 table_close(attrdesc, RowExclusiveLock);
7296
7297 /* Make the child column change visible */
7299
7300 return InvalidObjectAddress;
7301 }
7302 }
7303
7304 /* skip if the name already exists and if_not_exists is true */
7305 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7306 {
7307 table_close(attrdesc, RowExclusiveLock);
7308 return InvalidObjectAddress;
7309 }
7310
7311 /*
7312 * Okay, we need to add the column, so go ahead and do parse
7313 * transformation. This can result in queueing up, or even immediately
7314 * executing, subsidiary operations (such as creation of unique indexes);
7315 * so we mustn't do it until we have made the if_not_exists check.
7316 *
7317 * When recursing, the command was already transformed and we needn't do
7318 * so again. Also, if context isn't given we can't transform. (That
7319 * currently happens only for AT_AddColumnToView; we expect that view.c
7320 * passed us a ColumnDef that doesn't need work.)
7321 */
7322 if (context != NULL && !recursing)
7323 {
7324 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7325 cur_pass, context);
7326 Assert(*cmd != NULL);
7327 colDef = castNode(ColumnDef, (*cmd)->def);
7328 }
7329
7330 /*
7331 * Regular inheritance children are independent enough not to inherit the
7332 * identity column from parent hence cannot recursively add identity
7333 * column if the table has inheritance children.
7334 *
7335 * Partitions, on the other hand, are integral part of a partitioned table
7336 * and inherit identity column. Hence propagate identity column down the
7337 * partition hierarchy.
7338 */
7339 if (colDef->identity &&
7340 recurse &&
7341 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7343 ereport(ERROR,
7344 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7345 errmsg("cannot recursively add identity column to table that has child tables")));
7346
7347 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7348
7349 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7350 if (!HeapTupleIsValid(reltup))
7351 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7352 relform = (Form_pg_class) GETSTRUCT(reltup);
7353 relkind = relform->relkind;
7354
7355 /* Determine the new attribute's number */
7356 newattnum = relform->relnatts + 1;
7357 if (newattnum > MaxHeapAttributeNumber)
7358 ereport(ERROR,
7359 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7360 errmsg("tables can have at most %d columns",
7362
7363 /*
7364 * Construct new attribute's pg_attribute entry.
7365 */
7366 tupdesc = BuildDescForRelation(list_make1(colDef));
7367
7368 attribute = TupleDescAttr(tupdesc, 0);
7369
7370 /* Fix up attribute number */
7371 attribute->attnum = newattnum;
7372
7373 /* make sure datatype is legal for a column */
7374 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7375 list_make1_oid(rel->rd_rel->reltype),
7376 0);
7377
7378 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7379
7380 table_close(attrdesc, RowExclusiveLock);
7381
7382 /*
7383 * Update pg_class tuple as appropriate
7384 */
7385 relform->relnatts = newattnum;
7386
7387 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7388
7389 heap_freetuple(reltup);
7390
7391 /* Post creation hook for new attribute */
7392 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7393
7394 table_close(pgclass, RowExclusiveLock);
7395
7396 /* Make the attribute's catalog entry visible */
7398
7399 /*
7400 * Store the DEFAULT, if any, in the catalogs
7401 */
7402 if (colDef->raw_default)
7403 {
7404 RawColumnDefault *rawEnt;
7405
7406 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7407 rawEnt->attnum = attribute->attnum;
7408 rawEnt->raw_default = copyObject(colDef->raw_default);
7409 rawEnt->generated = colDef->generated;
7410
7411 /*
7412 * This function is intended for CREATE TABLE, so it processes a
7413 * _list_ of defaults, but we just do one.
7414 */
7416 false, true, false, NULL);
7417
7418 /* Make the additional catalog changes visible */
7420 }
7421
7422 /*
7423 * Tell Phase 3 to fill in the default expression, if there is one.
7424 *
7425 * If there is no default, Phase 3 doesn't have to do anything, because
7426 * that effectively means that the default is NULL. The heap tuple access
7427 * routines always check for attnum > # of attributes in tuple, and return
7428 * NULL if so, so without any modification of the tuple data we will get
7429 * the effect of NULL values in the new column.
7430 *
7431 * An exception occurs when the new column is of a domain type: the domain
7432 * might have a not-null constraint, or a check constraint that indirectly
7433 * rejects nulls. If there are any domain constraints then we construct
7434 * an explicit NULL default value that will be passed through
7435 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7436 * rewriting the table which we really wouldn't have to do; but we do it
7437 * to preserve the historical behavior that such a failure will be raised
7438 * only if the table currently contains some rows.)
7439 *
7440 * Note: we use build_column_default, and not just the cooked default
7441 * returned by AddRelationNewConstraints, so that the right thing happens
7442 * when a datatype's default applies.
7443 *
7444 * Note: it might seem that this should happen at the end of Phase 2, so
7445 * that the effects of subsequent subcommands can be taken into account.
7446 * It's intentional that we do it now, though. The new column should be
7447 * filled according to what is said in the ADD COLUMN subcommand, so that
7448 * the effects are the same as if this subcommand had been run by itself
7449 * and the later subcommands had been issued in new ALTER TABLE commands.
7450 *
7451 * We can skip this entirely for relations without storage, since Phase 3
7452 * is certainly not going to touch them.
7453 */
7454 if (RELKIND_HAS_STORAGE(relkind))
7455 {
7456 bool has_domain_constraints;
7457 bool has_missing = false;
7458
7459 /*
7460 * For an identity column, we can't use build_column_default(),
7461 * because the sequence ownership isn't set yet. So do it manually.
7462 */
7463 if (colDef->identity)
7464 {
7466
7467 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7468 nve->typeId = attribute->atttypid;
7469
7470 defval = (Expr *) nve;
7471 }
7472 else
7473 defval = (Expr *) build_column_default(rel, attribute->attnum);
7474
7475 /* Build CoerceToDomain(NULL) expression if needed */
7476 has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7477 if (!defval && has_domain_constraints)
7478 {
7479 Oid baseTypeId;
7480 int32 baseTypeMod;
7481 Oid baseTypeColl;
7482
7483 baseTypeMod = attribute->atttypmod;
7484 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7485 baseTypeColl = get_typcollation(baseTypeId);
7486 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7487 defval = (Expr *) coerce_to_target_type(NULL,
7488 (Node *) defval,
7489 baseTypeId,
7490 attribute->atttypid,
7491 attribute->atttypmod,
7494 -1);
7495 if (defval == NULL) /* should not happen */
7496 elog(ERROR, "failed to coerce base type to domain");
7497 }
7498
7499 if (defval)
7500 {
7502
7503 /* Prepare defval for execution, either here or in Phase 3 */
7504 defval = expression_planner(defval);
7505
7506 /* Add the new default to the newvals list */
7508 newval->attnum = attribute->attnum;
7509 newval->expr = defval;
7510 newval->is_generated = (colDef->generated != '\0');
7511
7512 tab->newvals = lappend(tab->newvals, newval);
7513
7514 /*
7515 * Attempt to skip a complete table rewrite by storing the
7516 * specified DEFAULT value outside of the heap. This is only
7517 * allowed for plain relations and non-generated columns, and the
7518 * default expression can't be volatile (stable is OK). Note that
7519 * contain_volatile_functions deems CoerceToDomain immutable, but
7520 * here we consider that coercion to a domain with constraints is
7521 * volatile; else it might fail even when the table is empty.
7522 */
7523 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7524 !colDef->generated &&
7525 !has_domain_constraints &&
7526 !contain_volatile_functions((Node *) defval))
7527 {
7528 EState *estate;
7529 ExprState *exprState;
7530 Datum missingval;
7531 bool missingIsNull;
7532
7533 /* Evaluate the default expression */
7534 estate = CreateExecutorState();
7535 exprState = ExecPrepareExpr(defval, estate);
7536 missingval = ExecEvalExpr(exprState,
7537 GetPerTupleExprContext(estate),
7538 &missingIsNull);
7539 /* If it turns out NULL, nothing to do; else store it */
7540 if (!missingIsNull)
7541 {
7542 StoreAttrMissingVal(rel, attribute->attnum, missingval);
7543 /* Make the additional catalog change visible */
7545 has_missing = true;
7546 }
7547 FreeExecutorState(estate);
7548 }
7549 else
7550 {
7551 /*
7552 * Failed to use missing mode. We have to do a table rewrite
7553 * to install the value --- unless it's a virtual generated
7554 * column.
7555 */
7556 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7558 }
7559 }
7560
7561 if (!has_missing)
7562 {
7563 /*
7564 * If the new column is NOT NULL, and there is no missing value,
7565 * tell Phase 3 it needs to check for NULLs.
7566 */
7567 tab->verify_new_notnull |= colDef->is_not_null;
7568 }
7569 }
7570
7571 /*
7572 * Add needed dependency entries for the new column.
7573 */
7574 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7575 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7576
7577 /*
7578 * Propagate to children as appropriate. Unlike most other ALTER
7579 * routines, we have to do this one level of recursion at a time; we can't
7580 * use find_all_inheritors to do it in one pass.
7581 */
7582 children =
7584
7585 /*
7586 * If we are told not to recurse, there had better not be any child
7587 * tables; else the addition would put them out of step.
7588 */
7589 if (children && !recurse)
7590 ereport(ERROR,
7591 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7592 errmsg("column must be added to child tables too")));
7593
7594 /* Children should see column as singly inherited */
7595 if (!recursing)
7596 {
7597 childcmd = copyObject(*cmd);
7598 colDef = castNode(ColumnDef, childcmd->def);
7599 colDef->inhcount = 1;
7600 colDef->is_local = false;
7601 }
7602 else
7603 childcmd = *cmd; /* no need to copy again */
7604
7605 foreach(child, children)
7606 {
7607 Oid childrelid = lfirst_oid(child);
7608 Relation childrel;
7609 AlteredTableInfo *childtab;
7610
7611 /* find_inheritance_children already got lock */
7612 childrel = table_open(childrelid, NoLock);
7613 CheckAlterTableIsSafe(childrel);
7614
7615 /* Find or create work queue entry for this table */
7616 childtab = ATGetQueueEntry(wqueue, childrel);
7617
7618 /* Recurse to child; return value is ignored */
7619 ATExecAddColumn(wqueue, childtab, childrel,
7620 &childcmd, recurse, true,
7621 lockmode, cur_pass, context);
7622
7623 table_close(childrel, NoLock);
7624 }
7625
7626 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7627 return address;
7628}
7629
7630/*
7631 * If a new or renamed column will collide with the name of an existing
7632 * column and if_not_exists is false then error out, else do nothing.
7633 */
7634static bool
7636 bool if_not_exists)
7637{
7638 HeapTuple attTuple;
7639 int attnum;
7640
7641 /*
7642 * this test is deliberately not attisdropped-aware, since if one tries to
7643 * add a column matching a dropped column name, it's gonna fail anyway.
7644 */
7645 attTuple = SearchSysCache2(ATTNAME,
7647 PointerGetDatum(colname));
7648 if (!HeapTupleIsValid(attTuple))
7649 return true;
7650
7651 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7652 ReleaseSysCache(attTuple);
7653
7654 /*
7655 * We throw a different error message for conflicts with system column
7656 * names, since they are normally not shown and the user might otherwise
7657 * be confused about the reason for the conflict.
7658 */
7659 if (attnum <= 0)
7660 ereport(ERROR,
7661 (errcode(ERRCODE_DUPLICATE_COLUMN),
7662 errmsg("column name \"%s\" conflicts with a system column name",
7663 colname)));
7664 else
7665 {
7666 if (if_not_exists)
7667 {
7669 (errcode(ERRCODE_DUPLICATE_COLUMN),
7670 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7671 colname, RelationGetRelationName(rel))));
7672 return false;
7673 }
7674
7675 ereport(ERROR,
7676 (errcode(ERRCODE_DUPLICATE_COLUMN),
7677 errmsg("column \"%s\" of relation \"%s\" already exists",
7678 colname, RelationGetRelationName(rel))));
7679 }
7680
7681 return true;
7682}
7683
7684/*
7685 * Install a column's dependency on its datatype.
7686 */
7687static void
7689{
7690 ObjectAddress myself,
7691 referenced;
7692
7693 myself.classId = RelationRelationId;
7694 myself.objectId = relid;
7695 myself.objectSubId = attnum;
7696 referenced.classId = TypeRelationId;
7697 referenced.objectId = typid;
7698 referenced.objectSubId = 0;
7699 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7700}
7701
7702/*
7703 * Install a column's dependency on its collation.
7704 */
7705static void
7707{
7708 ObjectAddress myself,
7709 referenced;
7710
7711 /* We know the default collation is pinned, so don't bother recording it */
7712 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7713 {
7714 myself.classId = RelationRelationId;
7715 myself.objectId = relid;
7716 myself.objectSubId = attnum;
7717 referenced.classId = CollationRelationId;
7718 referenced.objectId = collid;
7719 referenced.objectSubId = 0;
7720 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7721 }
7722}
7723
7724/*
7725 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7726 *
7727 * Return the address of the modified column. If the column was already
7728 * nullable, InvalidObjectAddress is returned.
7729 */
7730static ObjectAddress
7731ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7732 LOCKMODE lockmode)
7733{
7734 HeapTuple tuple;
7735 HeapTuple conTup;
7736 Form_pg_attribute attTup;
7738 Relation attr_rel;
7739 ObjectAddress address;
7740
7741 /*
7742 * lookup the attribute
7743 */
7744 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7745
7746 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7747 if (!HeapTupleIsValid(tuple))
7748 ereport(ERROR,
7749 (errcode(ERRCODE_UNDEFINED_COLUMN),
7750 errmsg("column \"%s\" of relation \"%s\" does not exist",
7751 colName, RelationGetRelationName(rel))));
7752 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7753 attnum = attTup->attnum;
7754 ObjectAddressSubSet(address, RelationRelationId,
7755 RelationGetRelid(rel), attnum);
7756
7757 /* If the column is already nullable there's nothing to do. */
7758 if (!attTup->attnotnull)
7759 {
7760 table_close(attr_rel, RowExclusiveLock);
7761 return InvalidObjectAddress;
7762 }
7763
7764 /* Prevent them from altering a system attribute */
7765 if (attnum <= 0)
7766 ereport(ERROR,
7767 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7768 errmsg("cannot alter system column \"%s\"",
7769 colName)));
7770
7771 if (attTup->attidentity)
7772 ereport(ERROR,
7773 (errcode(ERRCODE_SYNTAX_ERROR),
7774 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7775 colName, RelationGetRelationName(rel))));
7776
7777 /*
7778 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7779 */
7780 if (rel->rd_rel->relispartition)
7781 {
7782 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7783 Relation parent = table_open(parentId, AccessShareLock);
7784 TupleDesc tupDesc = RelationGetDescr(parent);
7785 AttrNumber parent_attnum;
7786
7787 parent_attnum = get_attnum(parentId, colName);
7788 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7789 ereport(ERROR,
7790 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7791 errmsg("column \"%s\" is marked NOT NULL in parent table",
7792 colName)));
7794 }
7795
7796 /*
7797 * Find the constraint that makes this column NOT NULL, and drop it.
7798 * dropconstraint_internal() resets attnotnull.
7799 */
7801 if (conTup == NULL)
7802 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7803 colName, RelationGetRelationName(rel));
7804
7805 /* The normal case: we have a pg_constraint row, remove it */
7806 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7807 false, lockmode);
7808 heap_freetuple(conTup);
7809
7810 InvokeObjectPostAlterHook(RelationRelationId,
7811 RelationGetRelid(rel), attnum);
7812
7813 table_close(attr_rel, RowExclusiveLock);
7814
7815 return address;
7816}
7817
7818/*
7819 * set_attnotnull
7820 * Helper to update/validate the pg_attribute status of a not-null
7821 * constraint
7822 *
7823 * pg_attribute.attnotnull is set true, if it isn't already.
7824 * If queue_validation is true, also set up wqueue to validate the constraint.
7825 * wqueue may be given as NULL when validation is not needed (e.g., on table
7826 * creation).
7827 */
7828static void
7830 bool is_valid, bool queue_validation)
7831{
7832 Form_pg_attribute attr;
7833 CompactAttribute *thisatt;
7834
7835 Assert(!queue_validation || wqueue);
7836
7838
7839 /*
7840 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7841 * attribute.
7842 */
7843 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7844 if (attr->attisdropped)
7845 return;
7846
7847 if (!attr->attnotnull)
7848 {
7849 Relation attr_rel;
7850 HeapTuple tuple;
7851
7852 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7853
7855 if (!HeapTupleIsValid(tuple))
7856 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7857 attnum, RelationGetRelid(rel));
7858
7859 thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7861
7862 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7863
7864 attr->attnotnull = true;
7865 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7866
7867 /*
7868 * If the nullness isn't already proven by validated constraints, have
7869 * ALTER TABLE phase 3 test for it.
7870 */
7871 if (queue_validation && wqueue &&
7873 {
7874 AlteredTableInfo *tab;
7875
7876 tab = ATGetQueueEntry(wqueue, rel);
7877 tab->verify_new_notnull = true;
7878 }
7879
7881
7882 table_close(attr_rel, RowExclusiveLock);
7883 heap_freetuple(tuple);
7884 }
7885 else
7886 {
7888 }
7889}
7890
7891/*
7892 * ALTER TABLE ALTER COLUMN SET NOT NULL
7893 *
7894 * Add a not-null constraint to a single table and its children. Returns
7895 * the address of the constraint added to the parent relation, if one gets
7896 * added, or InvalidObjectAddress otherwise.
7897 *
7898 * We must recurse to child tables during execution, rather than using
7899 * ALTER TABLE's normal prep-time recursion.
7900 */
7901static ObjectAddress
7902ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7903 bool recurse, bool recursing, LOCKMODE lockmode)
7904{
7905 HeapTuple tuple;
7907 ObjectAddress address;
7908 Constraint *constraint;
7909 CookedConstraint *ccon;
7910 List *cooked;
7911 bool is_no_inherit = false;
7912
7913 /* Guard against stack overflow due to overly deep inheritance tree. */
7915
7916 /* At top level, permission check was done in ATPrepCmd, else do it */
7917 if (recursing)
7918 {
7921 Assert(conName != NULL);
7922 }
7923
7924 attnum = get_attnum(RelationGetRelid(rel), colName);
7926 ereport(ERROR,
7927 (errcode(ERRCODE_UNDEFINED_COLUMN),
7928 errmsg("column \"%s\" of relation \"%s\" does not exist",
7929 colName, RelationGetRelationName(rel))));
7930
7931 /* Prevent them from altering a system attribute */
7932 if (attnum <= 0)
7933 ereport(ERROR,
7934 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7935 errmsg("cannot alter system column \"%s\"",
7936 colName)));
7937
7938 /* See if there's already a constraint */
7940 if (HeapTupleIsValid(tuple))
7941 {
7943 bool changed = false;
7944
7945 /*
7946 * Don't let a NO INHERIT constraint be changed into inherit.
7947 */
7948 if (conForm->connoinherit && recurse)
7949 ereport(ERROR,
7950 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7951 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7952 NameStr(conForm->conname),
7954
7955 /*
7956 * If we find an appropriate constraint, we're almost done, but just
7957 * need to change some properties on it: if we're recursing, increment
7958 * coninhcount; if not, set conislocal if not already set.
7959 */
7960 if (recursing)
7961 {
7962 if (pg_add_s16_overflow(conForm->coninhcount, 1,
7963 &conForm->coninhcount))
7964 ereport(ERROR,
7965 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7966 errmsg("too many inheritance parents"));
7967 changed = true;
7968 }
7969 else if (!conForm->conislocal)
7970 {
7971 conForm->conislocal = true;
7972 changed = true;
7973 }
7974 else if (!conForm->convalidated)
7975 {
7976 /*
7977 * Flip attnotnull and convalidated, and also validate the
7978 * constraint.
7979 */
7980 return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
7981 recurse, recursing, lockmode);
7982 }
7983
7984 if (changed)
7985 {
7986 Relation constr_rel;
7987
7988 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7989
7990 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7991 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7992 table_close(constr_rel, RowExclusiveLock);
7993 }
7994
7995 if (changed)
7996 return address;
7997 else
7998 return InvalidObjectAddress;
7999 }
8000
8001 /*
8002 * If we're asked not to recurse, and children exist, raise an error for
8003 * partitioned tables. For inheritance, we act as if NO INHERIT had been
8004 * specified.
8005 */
8006 if (!recurse &&
8008 NoLock) != NIL)
8009 {
8010 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8011 ereport(ERROR,
8012 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8013 errmsg("constraint must be added to child tables too"),
8014 errhint("Do not specify the ONLY keyword."));
8015 else
8016 is_no_inherit = true;
8017 }
8018
8019 /*
8020 * No constraint exists; we must add one. First determine a name to use,
8021 * if we haven't already.
8022 */
8023 if (!recursing)
8024 {
8025 Assert(conName == NULL);
8027 colName, "not_null",
8029 NIL);
8030 }
8031
8032 constraint = makeNotNullConstraint(makeString(colName));
8033 constraint->is_no_inherit = is_no_inherit;
8034 constraint->conname = conName;
8035
8036 /* and do it */
8037 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8038 false, !recursing, false, NULL);
8039 ccon = linitial(cooked);
8040 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8041
8042 InvokeObjectPostAlterHook(RelationRelationId,
8043 RelationGetRelid(rel), attnum);
8044
8045 /* Mark pg_attribute.attnotnull for the column and queue validation */
8046 set_attnotnull(wqueue, rel, attnum, true, true);
8047
8048 /*
8049 * Recurse to propagate the constraint to children that don't have one.
8050 */
8051 if (recurse)
8052 {
8053 List *children;
8054
8056 lockmode);
8057
8058 foreach_oid(childoid, children)
8059 {
8060 Relation childrel = table_open(childoid, NoLock);
8061
8063
8064 ATExecSetNotNull(wqueue, childrel, conName, colName,
8065 recurse, true, lockmode);
8066 table_close(childrel, NoLock);
8067 }
8068 }
8069
8070 return address;
8071}
8072
8073/*
8074 * NotNullImpliedByRelConstraints
8075 * Does rel's existing constraints imply NOT NULL for the given attribute?
8076 */
8077static bool
8079{
8080 NullTest *nnulltest = makeNode(NullTest);
8081
8082 nnulltest->arg = (Expr *) makeVar(1,
8083 attr->attnum,
8084 attr->atttypid,
8085 attr->atttypmod,
8086 attr->attcollation,
8087 0);
8088 nnulltest->nulltesttype = IS_NOT_NULL;
8089
8090 /*
8091 * argisrow = false is correct even for a composite column, because
8092 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8093 * case, just IS DISTINCT FROM NULL.
8094 */
8095 nnulltest->argisrow = false;
8096 nnulltest->location = -1;
8097
8098 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8099 {
8101 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8102 RelationGetRelationName(rel), NameStr(attr->attname))));
8103 return true;
8104 }
8105
8106 return false;
8107}
8108
8109/*
8110 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8111 *
8112 * Return the address of the affected column.
8113 */
8114static ObjectAddress
8115ATExecColumnDefault(Relation rel, const char *colName,
8116 Node *newDefault, LOCKMODE lockmode)
8117{
8118 TupleDesc tupdesc = RelationGetDescr(rel);
8120 ObjectAddress address;
8121
8122 /*
8123 * get the number of the attribute
8124 */
8125 attnum = get_attnum(RelationGetRelid(rel), colName);
8127 ereport(ERROR,
8128 (errcode(ERRCODE_UNDEFINED_COLUMN),
8129 errmsg("column \"%s\" of relation \"%s\" does not exist",
8130 colName, RelationGetRelationName(rel))));
8131
8132 /* Prevent them from altering a system attribute */
8133 if (attnum <= 0)
8134 ereport(ERROR,
8135 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8136 errmsg("cannot alter system column \"%s\"",
8137 colName)));
8138
8139 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8140 ereport(ERROR,
8141 (errcode(ERRCODE_SYNTAX_ERROR),
8142 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8143 colName, RelationGetRelationName(rel)),
8144 /* translator: %s is an SQL ALTER command */
8145 newDefault ? 0 : errhint("Use %s instead.",
8146 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8147
8148 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8149 ereport(ERROR,
8150 (errcode(ERRCODE_SYNTAX_ERROR),
8151 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8152 colName, RelationGetRelationName(rel)),
8153 newDefault ?
8154 /* translator: %s is an SQL ALTER command */
8155 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8156 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8157 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8158
8159 /*
8160 * Remove any old default for the column. We use RESTRICT here for
8161 * safety, but at present we do not expect anything to depend on the
8162 * default.
8163 *
8164 * We treat removing the existing default as an internal operation when it
8165 * is preparatory to adding a new default, but as a user-initiated
8166 * operation when the user asked for a drop.
8167 */
8169 newDefault != NULL);
8170
8171 if (newDefault)
8172 {
8173 /* SET DEFAULT */
8174 RawColumnDefault *rawEnt;
8175
8176 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8177 rawEnt->attnum = attnum;
8178 rawEnt->raw_default = newDefault;
8179 rawEnt->generated = '\0';
8180
8181 /*
8182 * This function is intended for CREATE TABLE, so it processes a
8183 * _list_ of defaults, but we just do one.
8184 */
8186 false, true, false, NULL);
8187 }
8188
8189 ObjectAddressSubSet(address, RelationRelationId,
8190 RelationGetRelid(rel), attnum);
8191 return address;
8192}
8193
8194/*
8195 * Add a pre-cooked default expression.
8196 *
8197 * Return the address of the affected column.
8198 */
8199static ObjectAddress
8201 Node *newDefault)
8202{
8203 ObjectAddress address;
8204
8205 /* We assume no checking is required */
8206
8207 /*
8208 * Remove any old default for the column. We use RESTRICT here for
8209 * safety, but at present we do not expect anything to depend on the
8210 * default. (In ordinary cases, there could not be a default in place
8211 * anyway, but it's possible when combining LIKE with inheritance.)
8212 */
8214 true);
8215
8216 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8217
8218 ObjectAddressSubSet(address, RelationRelationId,
8219 RelationGetRelid(rel), attnum);
8220 return address;
8221}
8222
8223/*
8224 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8225 *
8226 * Return the address of the affected column.
8227 */
8228static ObjectAddress
8229ATExecAddIdentity(Relation rel, const char *colName,
8230 Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8231{
8232 Relation attrelation;
8233 HeapTuple tuple;
8234 Form_pg_attribute attTup;
8236 ObjectAddress address;
8237 ColumnDef *cdef = castNode(ColumnDef, def);
8238 bool ispartitioned;
8239
8240 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8241 if (ispartitioned && !recurse)
8242 ereport(ERROR,
8243 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8244 errmsg("cannot add identity to a column of only the partitioned table"),
8245 errhint("Do not specify the ONLY keyword.")));
8246
8247 if (rel->rd_rel->relispartition && !recursing)
8248 ereport(ERROR,
8249 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8250 errmsg("cannot add identity to a column of a partition"));
8251
8252 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8253
8254 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8255 if (!HeapTupleIsValid(tuple))
8256 ereport(ERROR,
8257 (errcode(ERRCODE_UNDEFINED_COLUMN),
8258 errmsg("column \"%s\" of relation \"%s\" does not exist",
8259 colName, RelationGetRelationName(rel))));
8260 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8261 attnum = attTup->attnum;
8262
8263 /* Can't alter a system attribute */
8264 if (attnum <= 0)
8265 ereport(ERROR,
8266 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8267 errmsg("cannot alter system column \"%s\"",
8268 colName)));
8269
8270 /*
8271 * Creating a column as identity implies NOT NULL, so adding the identity
8272 * to an existing column that is not NOT NULL would create a state that
8273 * cannot be reproduced without contortions.
8274 */
8275 if (!attTup->attnotnull)
8276 ereport(ERROR,
8277 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8278 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8279 colName, RelationGetRelationName(rel))));
8280
8281 if (attTup->attidentity)
8282 ereport(ERROR,
8283 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8284 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8285 colName, RelationGetRelationName(rel))));
8286
8287 if (attTup->atthasdef)
8288 ereport(ERROR,
8289 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8290 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8291 colName, RelationGetRelationName(rel))));
8292
8293 attTup->attidentity = cdef->identity;
8294 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8295
8296 InvokeObjectPostAlterHook(RelationRelationId,
8297 RelationGetRelid(rel),
8298 attTup->attnum);
8299 ObjectAddressSubSet(address, RelationRelationId,
8300 RelationGetRelid(rel), attnum);
8301 heap_freetuple(tuple);
8302
8303 table_close(attrelation, RowExclusiveLock);
8304
8305 /*
8306 * Recurse to propagate the identity column to partitions. Identity is
8307 * not inherited in regular inheritance children.
8308 */
8309 if (recurse && ispartitioned)
8310 {
8311 List *children;
8312 ListCell *lc;
8313
8314 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8315
8316 foreach(lc, children)
8317 {
8318 Relation childrel;
8319
8320 childrel = table_open(lfirst_oid(lc), NoLock);
8321 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8322 table_close(childrel, NoLock);
8323 }
8324 }
8325
8326 return address;
8327}
8328
8329/*
8330 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8331 *
8332 * Return the address of the affected column.
8333 */
8334static ObjectAddress
8335ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8336 LOCKMODE lockmode, bool recurse, bool recursing)
8337{
8339 DefElem *generatedEl = NULL;
8340 HeapTuple tuple;
8341 Form_pg_attribute attTup;
8343 Relation attrelation;
8344 ObjectAddress address;
8345 bool ispartitioned;
8346
8347 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8348 if (ispartitioned && !recurse)
8349 ereport(ERROR,
8350 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8351 errmsg("cannot change identity column of only the partitioned table"),
8352 errhint("Do not specify the ONLY keyword.")));
8353
8354 if (rel->rd_rel->relispartition && !recursing)
8355 ereport(ERROR,
8356 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8357 errmsg("cannot change identity column of a partition"));
8358
8359 foreach(option, castNode(List, def))
8360 {
8361 DefElem *defel = lfirst_node(DefElem, option);
8362
8363 if (strcmp(defel->defname, "generated") == 0)
8364 {
8365 if (generatedEl)
8366 ereport(ERROR,
8367 (errcode(ERRCODE_SYNTAX_ERROR),
8368 errmsg("conflicting or redundant options")));
8369 generatedEl = defel;
8370 }
8371 else
8372 elog(ERROR, "option \"%s\" not recognized",
8373 defel->defname);
8374 }
8375
8376 /*
8377 * Even if there is nothing to change here, we run all the checks. There
8378 * will be a subsequent ALTER SEQUENCE that relies on everything being
8379 * there.
8380 */
8381
8382 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8383 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8384 if (!HeapTupleIsValid(tuple))
8385 ereport(ERROR,
8386 (errcode(ERRCODE_UNDEFINED_COLUMN),
8387 errmsg("column \"%s\" of relation \"%s\" does not exist",
8388 colName, RelationGetRelationName(rel))));
8389
8390 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8391 attnum = attTup->attnum;
8392
8393 if (attnum <= 0)
8394 ereport(ERROR,
8395 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8396 errmsg("cannot alter system column \"%s\"",
8397 colName)));
8398
8399 if (!attTup->attidentity)
8400 ereport(ERROR,
8401 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8402 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8403 colName, RelationGetRelationName(rel))));
8404
8405 if (generatedEl)
8406 {
8407 attTup->attidentity = defGetInt32(generatedEl);
8408 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8409
8410 InvokeObjectPostAlterHook(RelationRelationId,
8411 RelationGetRelid(rel),
8412 attTup->attnum);
8413 ObjectAddressSubSet(address, RelationRelationId,
8414 RelationGetRelid(rel), attnum);
8415 }
8416 else
8417 address = InvalidObjectAddress;
8418
8419 heap_freetuple(tuple);
8420 table_close(attrelation, RowExclusiveLock);
8421
8422 /*
8423 * Recurse to propagate the identity change to partitions. Identity is not
8424 * inherited in regular inheritance children.
8425 */
8426 if (generatedEl && recurse && ispartitioned)
8427 {
8428 List *children;
8429 ListCell *lc;
8430
8431 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8432
8433 foreach(lc, children)
8434 {
8435 Relation childrel;
8436
8437 childrel = table_open(lfirst_oid(lc), NoLock);
8438 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8439 table_close(childrel, NoLock);
8440 }
8441 }
8442
8443 return address;
8444}
8445
8446/*
8447 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8448 *
8449 * Return the address of the affected column.
8450 */
8451static ObjectAddress
8452ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8453 bool recurse, bool recursing)
8454{
8455 HeapTuple tuple;
8456 Form_pg_attribute attTup;
8458 Relation attrelation;
8459 ObjectAddress address;
8460 Oid seqid;
8461 ObjectAddress seqaddress;
8462 bool ispartitioned;
8463
8464 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8465 if (ispartitioned && !recurse)
8466 ereport(ERROR,
8467 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8468 errmsg("cannot drop identity from a column of only the partitioned table"),
8469 errhint("Do not specify the ONLY keyword.")));
8470
8471 if (rel->rd_rel->relispartition && !recursing)
8472 ereport(ERROR,
8473 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8474 errmsg("cannot drop identity from a column of a partition"));
8475
8476 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8477 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8478 if (!HeapTupleIsValid(tuple))
8479 ereport(ERROR,
8480 (errcode(ERRCODE_UNDEFINED_COLUMN),
8481 errmsg("column \"%s\" of relation \"%s\" does not exist",
8482 colName, RelationGetRelationName(rel))));
8483
8484 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8485 attnum = attTup->attnum;
8486
8487 if (attnum <= 0)
8488 ereport(ERROR,
8489 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8490 errmsg("cannot alter system column \"%s\"",
8491 colName)));
8492
8493 if (!attTup->attidentity)
8494 {
8495 if (!missing_ok)
8496 ereport(ERROR,
8497 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8498 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8499 colName, RelationGetRelationName(rel))));
8500 else
8501 {
8503 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8504 colName, RelationGetRelationName(rel))));
8505 heap_freetuple(tuple);
8506 table_close(attrelation, RowExclusiveLock);
8507 return InvalidObjectAddress;
8508 }
8509 }
8510
8511 attTup->attidentity = '\0';
8512 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8513
8514 InvokeObjectPostAlterHook(RelationRelationId,
8515 RelationGetRelid(rel),
8516 attTup->attnum);
8517 ObjectAddressSubSet(address, RelationRelationId,
8518 RelationGetRelid(rel), attnum);
8519 heap_freetuple(tuple);
8520
8521 table_close(attrelation, RowExclusiveLock);
8522
8523 /*
8524 * Recurse to drop the identity from column in partitions. Identity is
8525 * not inherited in regular inheritance children so ignore them.
8526 */
8527 if (recurse && ispartitioned)
8528 {
8529 List *children;
8530 ListCell *lc;
8531
8532 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8533
8534 foreach(lc, children)
8535 {
8536 Relation childrel;
8537
8538 childrel = table_open(lfirst_oid(lc), NoLock);
8539 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8540 table_close(childrel, NoLock);
8541 }
8542 }
8543
8544 if (!recursing)
8545 {
8546 /* drop the internal sequence */
8547 seqid = getIdentitySequence(rel, attnum, false);
8548 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8549 RelationRelationId, DEPENDENCY_INTERNAL);
8551 seqaddress.classId = RelationRelationId;
8552 seqaddress.objectId = seqid;
8553 seqaddress.objectSubId = 0;
8555 }
8556
8557 return address;
8558}
8559
8560/*
8561 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8562 *
8563 * Return the address of the affected column.
8564 */
8565static ObjectAddress
8566ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8567 Node *newExpr, LOCKMODE lockmode)
8568{
8569 HeapTuple tuple;
8570 Form_pg_attribute attTup;
8572 char attgenerated;
8573 bool rewrite;
8574 Oid attrdefoid;
8575 ObjectAddress address;
8576 Expr *defval;
8578 RawColumnDefault *rawEnt;
8579
8580 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8581 if (!HeapTupleIsValid(tuple))
8582 ereport(ERROR,
8583 (errcode(ERRCODE_UNDEFINED_COLUMN),
8584 errmsg("column \"%s\" of relation \"%s\" does not exist",
8585 colName, RelationGetRelationName(rel))));
8586
8587 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8588
8589 attnum = attTup->attnum;
8590 if (attnum <= 0)
8591 ereport(ERROR,
8592 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8593 errmsg("cannot alter system column \"%s\"",
8594 colName)));
8595
8596 attgenerated = attTup->attgenerated;
8597 if (!attgenerated)
8598 ereport(ERROR,
8599 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8600 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8601 colName, RelationGetRelationName(rel))));
8602
8603 /*
8604 * TODO: This could be done, just need to recheck any constraints
8605 * afterwards.
8606 */
8607 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8608 rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8609 ereport(ERROR,
8610 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8611 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables with check constraints"),
8612 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8613 colName, RelationGetRelationName(rel))));
8614
8615 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8616 tab->verify_new_notnull = true;
8617
8618 /*
8619 * We need to prevent this because a change of expression could affect a
8620 * row filter and inject expressions that are not permitted in a row
8621 * filter. XXX We could try to have a more precise check to catch only
8622 * publications with row filters, or even re-verify the row filter
8623 * expressions.
8624 */
8625 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8627 ereport(ERROR,
8628 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8629 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables that are part of a publication"),
8630 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8631 colName, RelationGetRelationName(rel))));
8632
8633 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8634
8635 ReleaseSysCache(tuple);
8636
8637 if (rewrite)
8638 {
8639 /*
8640 * Clear all the missing values if we're rewriting the table, since
8641 * this renders them pointless.
8642 */
8644
8645 /* make sure we don't conflict with later attribute modifications */
8647
8648 /*
8649 * Find everything that depends on the column (constraints, indexes,
8650 * etc), and record enough information to let us recreate the objects
8651 * after rewrite.
8652 */
8654 }
8655
8656 /*
8657 * Drop the dependency records of the GENERATED expression, in particular
8658 * its INTERNAL dependency on the column, which would otherwise cause
8659 * dependency.c to refuse to perform the deletion.
8660 */
8661 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8662 if (!OidIsValid(attrdefoid))
8663 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8664 RelationGetRelid(rel), attnum);
8665 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8666
8667 /* Make above changes visible */
8669
8670 /*
8671 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8672 * safety, but at present we do not expect anything to depend on the
8673 * expression.
8674 */
8676 false, false);
8677
8678 /* Prepare to store the new expression, in the catalogs */
8679 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8680 rawEnt->attnum = attnum;
8681 rawEnt->raw_default = newExpr;
8682 rawEnt->generated = attgenerated;
8683
8684 /* Store the generated expression */
8686 false, true, false, NULL);
8687
8688 /* Make above new expression visible */
8690
8691 if (rewrite)
8692 {
8693 /* Prepare for table rewrite */
8694 defval = (Expr *) build_column_default(rel, attnum);
8695
8697 newval->attnum = attnum;
8698 newval->expr = expression_planner(defval);
8699 newval->is_generated = true;
8700
8701 tab->newvals = lappend(tab->newvals, newval);
8703 }
8704
8705 /* Drop any pg_statistic entry for the column */
8707
8708 InvokeObjectPostAlterHook(RelationRelationId,
8709 RelationGetRelid(rel), attnum);
8710
8711 ObjectAddressSubSet(address, RelationRelationId,
8712 RelationGetRelid(rel), attnum);
8713 return address;
8714}
8715
8716/*
8717 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8718 */
8719static void
8720ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8721{
8722 /*
8723 * Reject ONLY if there are child tables. We could implement this, but it
8724 * is a bit complicated. GENERATED clauses must be attached to the column
8725 * definition and cannot be added later like DEFAULT, so if a child table
8726 * has a generation expression that the parent does not have, the child
8727 * column will necessarily be an attislocal column. So to implement ONLY
8728 * here, we'd need extra code to update attislocal of the direct child
8729 * tables, somewhat similar to how DROP COLUMN does it, so that the
8730 * resulting state can be properly dumped and restored.
8731 */
8732 if (!recurse &&
8734 ereport(ERROR,
8735 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8736 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8737
8738 /*
8739 * Cannot drop generation expression from inherited columns.
8740 */
8741 if (!recursing)
8742 {
8743 HeapTuple tuple;
8744 Form_pg_attribute attTup;
8745
8747 if (!HeapTupleIsValid(tuple))
8748 ereport(ERROR,
8749 (errcode(ERRCODE_UNDEFINED_COLUMN),
8750 errmsg("column \"%s\" of relation \"%s\" does not exist",
8751 cmd->name, RelationGetRelationName(rel))));
8752
8753 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8754
8755 if (attTup->attinhcount > 0)
8756 ereport(ERROR,
8757 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8758 errmsg("cannot drop generation expression from inherited column")));
8759 }
8760}
8761
8762/*
8763 * Return the address of the affected column.
8764 */
8765static ObjectAddress
8766ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8767{
8768 HeapTuple tuple;
8769 Form_pg_attribute attTup;
8771 Relation attrelation;
8772 Oid attrdefoid;
8773 ObjectAddress address;
8774
8775 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8776 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8777 if (!HeapTupleIsValid(tuple))
8778 ereport(ERROR,
8779 (errcode(ERRCODE_UNDEFINED_COLUMN),
8780 errmsg("column \"%s\" of relation \"%s\" does not exist",
8781 colName, RelationGetRelationName(rel))));
8782
8783 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8784 attnum = attTup->attnum;
8785
8786 if (attnum <= 0)
8787 ereport(ERROR,
8788 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8789 errmsg("cannot alter system column \"%s\"",
8790 colName)));
8791
8792 /*
8793 * TODO: This could be done, but it would need a table rewrite to
8794 * materialize the generated values. Note that for the time being, we
8795 * still error with missing_ok, so that we don't silently leave the column
8796 * as generated.
8797 */
8798 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8799 ereport(ERROR,
8800 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8801 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8802 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8803 colName, RelationGetRelationName(rel))));
8804
8805 if (!attTup->attgenerated)
8806 {
8807 if (!missing_ok)
8808 ereport(ERROR,
8809 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8810 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8811 colName, RelationGetRelationName(rel))));
8812 else
8813 {
8815 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8816 colName, RelationGetRelationName(rel))));
8817 heap_freetuple(tuple);
8818 table_close(attrelation, RowExclusiveLock);
8819 return InvalidObjectAddress;
8820 }
8821 }
8822
8823 /*
8824 * Mark the column as no longer generated. (The atthasdef flag needs to
8825 * get cleared too, but RemoveAttrDefault will handle that.)
8826 */
8827 attTup->attgenerated = '\0';
8828 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8829
8830 InvokeObjectPostAlterHook(RelationRelationId,
8831 RelationGetRelid(rel),
8832 attnum);
8833 heap_freetuple(tuple);
8834
8835 table_close(attrelation, RowExclusiveLock);
8836
8837 /*
8838 * Drop the dependency records of the GENERATED expression, in particular
8839 * its INTERNAL dependency on the column, which would otherwise cause
8840 * dependency.c to refuse to perform the deletion.
8841 */
8842 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8843 if (!OidIsValid(attrdefoid))
8844 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8845 RelationGetRelid(rel), attnum);
8846 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8847
8848 /* Make above changes visible */
8850
8851 /*
8852 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8853 * safety, but at present we do not expect anything to depend on the
8854 * default.
8855 */
8857 false, false);
8858
8859 ObjectAddressSubSet(address, RelationRelationId,
8860 RelationGetRelid(rel), attnum);
8861 return address;
8862}
8863
8864/*
8865 * ALTER TABLE ALTER COLUMN SET STATISTICS
8866 *
8867 * Return value is the address of the modified column
8868 */
8869static ObjectAddress
8870ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8871{
8872 int newtarget = 0;
8873 bool newtarget_default;
8874 Relation attrelation;
8875 HeapTuple tuple,
8876 newtuple;
8877 Form_pg_attribute attrtuple;
8879 ObjectAddress address;
8880 Datum repl_val[Natts_pg_attribute];
8881 bool repl_null[Natts_pg_attribute];
8882 bool repl_repl[Natts_pg_attribute];
8883
8884 /*
8885 * We allow referencing columns by numbers only for indexes, since table
8886 * column numbers could contain gaps if columns are later dropped.
8887 */
8888 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8889 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8890 !colName)
8891 ereport(ERROR,
8892 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8893 errmsg("cannot refer to non-index column by number")));
8894
8895 /* -1 was used in previous versions for the default setting */
8896 if (newValue && intVal(newValue) != -1)
8897 {
8898 newtarget = intVal(newValue);
8899 newtarget_default = false;
8900 }
8901 else
8902 newtarget_default = true;
8903
8904 if (!newtarget_default)
8905 {
8906 /*
8907 * Limit target to a sane range
8908 */
8909 if (newtarget < 0)
8910 {
8911 ereport(ERROR,
8912 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8913 errmsg("statistics target %d is too low",
8914 newtarget)));
8915 }
8916 else if (newtarget > MAX_STATISTICS_TARGET)
8917 {
8918 newtarget = MAX_STATISTICS_TARGET;
8920 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8921 errmsg("lowering statistics target to %d",
8922 newtarget)));
8923 }
8924 }
8925
8926 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8927
8928 if (colName)
8929 {
8930 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8931
8932 if (!HeapTupleIsValid(tuple))
8933 ereport(ERROR,
8934 (errcode(ERRCODE_UNDEFINED_COLUMN),
8935 errmsg("column \"%s\" of relation \"%s\" does not exist",
8936 colName, RelationGetRelationName(rel))));
8937 }
8938 else
8939 {
8940 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8941
8942 if (!HeapTupleIsValid(tuple))
8943 ereport(ERROR,
8944 (errcode(ERRCODE_UNDEFINED_COLUMN),
8945 errmsg("column number %d of relation \"%s\" does not exist",
8946 colNum, RelationGetRelationName(rel))));
8947 }
8948
8949 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8950
8951 attnum = attrtuple->attnum;
8952 if (attnum <= 0)
8953 ereport(ERROR,
8954 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8955 errmsg("cannot alter system column \"%s\"",
8956 colName)));
8957
8958 /*
8959 * Prevent this as long as the ANALYZE code skips virtual generated
8960 * columns.
8961 */
8962 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8963 ereport(ERROR,
8964 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8965 errmsg("cannot alter statistics on virtual generated column \"%s\"",
8966 colName)));
8967
8968 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8969 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8970 {
8971 if (attnum > rel->rd_index->indnkeyatts)
8972 ereport(ERROR,
8973 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8974 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8975 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8976 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8977 ereport(ERROR,
8978 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8979 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8980 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8981 errhint("Alter statistics on table column instead.")));
8982 }
8983
8984 /* Build new tuple. */
8985 memset(repl_null, false, sizeof(repl_null));
8986 memset(repl_repl, false, sizeof(repl_repl));
8987 if (!newtarget_default)
8988 repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8989 else
8990 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8991 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8992 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8993 repl_val, repl_null, repl_repl);
8994 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8995
8996 InvokeObjectPostAlterHook(RelationRelationId,
8997 RelationGetRelid(rel),
8998 attrtuple->attnum);
8999 ObjectAddressSubSet(address, RelationRelationId,
9000 RelationGetRelid(rel), attnum);
9001
9002 heap_freetuple(newtuple);
9003
9004 ReleaseSysCache(tuple);
9005
9006 table_close(attrelation, RowExclusiveLock);
9007
9008 return address;
9009}
9010
9011/*
9012 * Return value is the address of the modified column
9013 */
9014static ObjectAddress
9015ATExecSetOptions(Relation rel, const char *colName, Node *options,
9016 bool isReset, LOCKMODE lockmode)
9017{
9018 Relation attrelation;
9019 HeapTuple tuple,
9020 newtuple;
9021 Form_pg_attribute attrtuple;
9023 Datum datum,
9024 newOptions;
9025 bool isnull;
9026 ObjectAddress address;
9027 Datum repl_val[Natts_pg_attribute];
9028 bool repl_null[Natts_pg_attribute];
9029 bool repl_repl[Natts_pg_attribute];
9030
9031 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9032
9033 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9034
9035 if (!HeapTupleIsValid(tuple))
9036 ereport(ERROR,
9037 (errcode(ERRCODE_UNDEFINED_COLUMN),
9038 errmsg("column \"%s\" of relation \"%s\" does not exist",
9039 colName, RelationGetRelationName(rel))));
9040 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9041
9042 attnum = attrtuple->attnum;
9043 if (attnum <= 0)
9044 ereport(ERROR,
9045 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9046 errmsg("cannot alter system column \"%s\"",
9047 colName)));
9048
9049 /* Generate new proposed attoptions (text array) */
9050 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9051 &isnull);
9052 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9053 castNode(List, options), NULL, NULL,
9054 false, isReset);
9055 /* Validate new options */
9056 (void) attribute_reloptions(newOptions, true);
9057
9058 /* Build new tuple. */
9059 memset(repl_null, false, sizeof(repl_null));
9060 memset(repl_repl, false, sizeof(repl_repl));
9061 if (newOptions != (Datum) 0)
9062 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9063 else
9064 repl_null[Anum_pg_attribute_attoptions - 1] = true;
9065 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9066 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9067 repl_val, repl_null, repl_repl);
9068
9069 /* Update system catalog. */
9070 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9071
9072 InvokeObjectPostAlterHook(RelationRelationId,
9073 RelationGetRelid(rel),
9074 attrtuple->attnum);
9075 ObjectAddressSubSet(address, RelationRelationId,
9076 RelationGetRelid(rel), attnum);
9077
9078 heap_freetuple(newtuple);
9079
9080 ReleaseSysCache(tuple);
9081
9082 table_close(attrelation, RowExclusiveLock);
9083
9084 return address;
9085}
9086
9087/*
9088 * Helper function for ATExecSetStorage and ATExecSetCompression
9089 *
9090 * Set the attstorage and/or attcompression fields for index columns
9091 * associated with the specified table column.
9092 */
9093static void
9096 bool setstorage, char newstorage,
9097 bool setcompression, char newcompression,
9098 LOCKMODE lockmode)
9099{
9100 ListCell *lc;
9101
9102 foreach(lc, RelationGetIndexList(rel))
9103 {
9104 Oid indexoid = lfirst_oid(lc);
9105 Relation indrel;
9106 AttrNumber indattnum = 0;
9107 HeapTuple tuple;
9108
9109 indrel = index_open(indexoid, lockmode);
9110
9111 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9112 {
9113 if (indrel->rd_index->indkey.values[i] == attnum)
9114 {
9115 indattnum = i + 1;
9116 break;
9117 }
9118 }
9119
9120 if (indattnum == 0)
9121 {
9122 index_close(indrel, lockmode);
9123 continue;
9124 }
9125
9126 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9127
9128 if (HeapTupleIsValid(tuple))
9129 {
9130 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9131
9132 if (setstorage)
9133 attrtuple->attstorage = newstorage;
9134
9135 if (setcompression)
9136 attrtuple->attcompression = newcompression;
9137
9138 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9139
9140 InvokeObjectPostAlterHook(RelationRelationId,
9141 RelationGetRelid(rel),
9142 attrtuple->attnum);
9143
9144 heap_freetuple(tuple);
9145 }
9146
9147 index_close(indrel, lockmode);
9148 }
9149}
9150
9151/*
9152 * ALTER TABLE ALTER COLUMN SET STORAGE
9153 *
9154 * Return value is the address of the modified column
9155 */
9156static ObjectAddress
9157ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9158{
9159 Relation attrelation;
9160 HeapTuple tuple;
9161 Form_pg_attribute attrtuple;
9163 ObjectAddress address;
9164
9165 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9166
9167 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9168
9169 if (!HeapTupleIsValid(tuple))
9170 ereport(ERROR,
9171 (errcode(ERRCODE_UNDEFINED_COLUMN),
9172 errmsg("column \"%s\" of relation \"%s\" does not exist",
9173 colName, RelationGetRelationName(rel))));
9174 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9175
9176 attnum = attrtuple->attnum;
9177 if (attnum <= 0)
9178 ereport(ERROR,
9179 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9180 errmsg("cannot alter system column \"%s\"",
9181 colName)));
9182
9183 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9184
9185 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9186
9187 InvokeObjectPostAlterHook(RelationRelationId,
9188 RelationGetRelid(rel),
9189 attrtuple->attnum);
9190
9191 /*
9192 * Apply the change to indexes as well (only for simple index columns,
9193 * matching behavior of index.c ConstructTupleDescriptor()).
9194 */
9195 SetIndexStorageProperties(rel, attrelation, attnum,
9196 true, attrtuple->attstorage,
9197 false, 0,
9198 lockmode);
9199
9200 heap_freetuple(tuple);
9201
9202 table_close(attrelation, RowExclusiveLock);
9203
9204 ObjectAddressSubSet(address, RelationRelationId,
9205 RelationGetRelid(rel), attnum);
9206 return address;
9207}
9208
9209
9210/*
9211 * ALTER TABLE DROP COLUMN
9212 *
9213 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9214 * because we have to decide at runtime whether to recurse or not depending
9215 * on whether attinhcount goes to zero or not. (We can't check this in a
9216 * static pre-pass because it won't handle multiple inheritance situations
9217 * correctly.)
9218 */
9219static void
9220ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9221 AlterTableCmd *cmd, LOCKMODE lockmode,
9222 AlterTableUtilityContext *context)
9223{
9224 if (rel->rd_rel->reloftype && !recursing)
9225 ereport(ERROR,
9226 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9227 errmsg("cannot drop column from typed table")));
9228
9229 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9230 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9231
9232 if (recurse)
9233 cmd->recurse = true;
9234}
9235
9236/*
9237 * Drops column 'colName' from relation 'rel' and returns the address of the
9238 * dropped column. The column is also dropped (or marked as no longer
9239 * inherited from relation) from the relation's inheritance children, if any.
9240 *
9241 * In the recursive invocations for inheritance child relations, instead of
9242 * dropping the column directly (if to be dropped at all), its object address
9243 * is added to 'addrs', which must be non-NULL in such invocations. All
9244 * columns are dropped at the same time after all the children have been
9245 * checked recursively.
9246 */
9247static ObjectAddress
9248ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9249 DropBehavior behavior,
9250 bool recurse, bool recursing,
9251 bool missing_ok, LOCKMODE lockmode,
9252 ObjectAddresses *addrs)
9253{
9254 HeapTuple tuple;
9255 Form_pg_attribute targetatt;
9257 List *children;
9258 ObjectAddress object;
9259 bool is_expr;
9260
9261 /* At top level, permission check was done in ATPrepCmd, else do it */
9262 if (recursing)
9265
9266 /* Initialize addrs on the first invocation */
9267 Assert(!recursing || addrs != NULL);
9268
9269 /* since this function recurses, it could be driven to stack overflow */
9271
9272 if (!recursing)
9273 addrs = new_object_addresses();
9274
9275 /*
9276 * get the number of the attribute
9277 */
9278 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9279 if (!HeapTupleIsValid(tuple))
9280 {
9281 if (!missing_ok)
9282 {
9283 ereport(ERROR,
9284 (errcode(ERRCODE_UNDEFINED_COLUMN),
9285 errmsg("column \"%s\" of relation \"%s\" does not exist",
9286 colName, RelationGetRelationName(rel))));
9287 }
9288 else
9289 {
9291 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9292 colName, RelationGetRelationName(rel))));
9293 return InvalidObjectAddress;
9294 }
9295 }
9296 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9297
9298 attnum = targetatt->attnum;
9299
9300 /* Can't drop a system attribute */
9301 if (attnum <= 0)
9302 ereport(ERROR,
9303 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9304 errmsg("cannot drop system column \"%s\"",
9305 colName)));
9306
9307 /*
9308 * Don't drop inherited columns, unless recursing (presumably from a drop
9309 * of the parent column)
9310 */
9311 if (targetatt->attinhcount > 0 && !recursing)
9312 ereport(ERROR,
9313 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9314 errmsg("cannot drop inherited column \"%s\"",
9315 colName)));
9316
9317 /*
9318 * Don't drop columns used in the partition key, either. (If we let this
9319 * go through, the key column's dependencies would cause a cascaded drop
9320 * of the whole table, which is surely not what the user expected.)
9321 */
9322 if (has_partition_attrs(rel,
9324 &is_expr))
9325 ereport(ERROR,
9326 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9327 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9328 colName, RelationGetRelationName(rel))));
9329
9330 ReleaseSysCache(tuple);
9331
9332 /*
9333 * Propagate to children as appropriate. Unlike most other ALTER
9334 * routines, we have to do this one level of recursion at a time; we can't
9335 * use find_all_inheritors to do it in one pass.
9336 */
9337 children =
9339
9340 if (children)
9341 {
9342 Relation attr_rel;
9343 ListCell *child;
9344
9345 /*
9346 * In case of a partitioned table, the column must be dropped from the
9347 * partitions as well.
9348 */
9349 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9350 ereport(ERROR,
9351 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9352 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9353 errhint("Do not specify the ONLY keyword.")));
9354
9355 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9356 foreach(child, children)
9357 {
9358 Oid childrelid = lfirst_oid(child);
9359 Relation childrel;
9360 Form_pg_attribute childatt;
9361
9362 /* find_inheritance_children already got lock */
9363 childrel = table_open(childrelid, NoLock);
9364 CheckAlterTableIsSafe(childrel);
9365
9366 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9367 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9368 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9369 colName, childrelid);
9370 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9371
9372 if (childatt->attinhcount <= 0) /* shouldn't happen */
9373 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9374 childrelid, colName);
9375
9376 if (recurse)
9377 {
9378 /*
9379 * If the child column has other definition sources, just
9380 * decrement its inheritance count; if not, recurse to delete
9381 * it.
9382 */
9383 if (childatt->attinhcount == 1 && !childatt->attislocal)
9384 {
9385 /* Time to delete this child column, too */
9386 ATExecDropColumn(wqueue, childrel, colName,
9387 behavior, true, true,
9388 false, lockmode, addrs);
9389 }
9390 else
9391 {
9392 /* Child column must survive my deletion */
9393 childatt->attinhcount--;
9394
9395 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9396
9397 /* Make update visible */
9399 }
9400 }
9401 else
9402 {
9403 /*
9404 * If we were told to drop ONLY in this table (no recursion),
9405 * we need to mark the inheritors' attributes as locally
9406 * defined rather than inherited.
9407 */
9408 childatt->attinhcount--;
9409 childatt->attislocal = true;
9410
9411 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9412
9413 /* Make update visible */
9415 }
9416
9417 heap_freetuple(tuple);
9418
9419 table_close(childrel, NoLock);
9420 }
9421 table_close(attr_rel, RowExclusiveLock);
9422 }
9423
9424 /* Add object to delete */
9425 object.classId = RelationRelationId;
9426 object.objectId = RelationGetRelid(rel);
9427 object.objectSubId = attnum;
9428 add_exact_object_address(&object, addrs);
9429
9430 if (!recursing)
9431 {
9432 /* Recursion has ended, drop everything that was collected */
9433 performMultipleDeletions(addrs, behavior, 0);
9434 free_object_addresses(addrs);
9435 }
9436
9437 return object;
9438}
9439
9440/*
9441 * Prepare to add a primary key on table, by adding not-null constraints
9442 * on all columns.
9443 */
9444static void
9446 bool recurse, LOCKMODE lockmode,
9447 AlterTableUtilityContext *context)
9448{
9449 Constraint *pkconstr;
9450
9451 pkconstr = castNode(Constraint, cmd->def);
9452 if (pkconstr->contype != CONSTR_PRIMARY)
9453 return;
9454
9455 /*
9456 * If not recursing, we must ensure that all children have a NOT NULL
9457 * constraint on the columns, and error out if not.
9458 */
9459 if (!recurse)
9460 {
9461 List *children;
9462
9464 lockmode);
9465 foreach_oid(childrelid, children)
9466 {
9467 foreach_node(String, attname, pkconstr->keys)
9468 {
9469 HeapTuple tup;
9470 Form_pg_attribute attrForm;
9471
9472 tup = SearchSysCacheAttName(childrelid, strVal(attname));
9473 if (!tup)
9474 elog(ERROR, "cache lookup failed for attribute %s of relation %u",
9475 strVal(attname), childrelid);
9476 attrForm = (Form_pg_attribute) GETSTRUCT(tup);
9477 if (!attrForm->attnotnull)
9478 ereport(ERROR,
9479 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9480 strVal(attname), get_rel_name(childrelid)));
9481 ReleaseSysCache(tup);
9482 }
9483 }
9484 }
9485
9486 /* Verify that columns are not-null, or request that they be made so */
9487 foreach_node(String, column, pkconstr->keys)
9488 {
9489 AlterTableCmd *newcmd;
9490 Constraint *nnconstr;
9491 HeapTuple tuple;
9492
9493 /*
9494 * First check if a suitable constraint exists. If it does, we don't
9495 * need to request another one. We do need to bail out if it's not
9496 * valid, though.
9497 */
9498 tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9499 if (tuple != NULL)
9500 {
9502
9503 /* a NO INHERIT constraint is no good */
9504 if (conForm->connoinherit)
9505 ereport(ERROR,
9506 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9507 errmsg("cannot create primary key on column \"%s\"",
9508 strVal(column)),
9509 /*- translator: third %s is a constraint characteristic such as NOT VALID */
9510 errdetail("The constraint \"%s\" on column \"%s\", marked %s, is incompatible with a primary key.",
9511 NameStr(conForm->conname), strVal(column), "NO INHERIT"),
9512 errhint("You will need to make it inheritable using %s.",
9513 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9514
9515 /* an unvalidated constraint is no good */
9516 if (!conForm->convalidated)
9517 ereport(ERROR,
9518 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9519 errmsg("cannot create primary key on column \"%s\"",
9520 strVal(column)),
9521 /*- translator: third %s is a constraint characteristic such as NOT VALID */
9522 errdetail("The constraint \"%s\" on column \"%s\", marked %s, is incompatible with a primary key.",
9523 NameStr(conForm->conname), strVal(column), "NOT VALID"),
9524 errhint("You will need to validate it using %s.",
9525 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9526
9527 /* All good with this one; don't request another */
9528 heap_freetuple(tuple);
9529 continue;
9530 }
9531
9532 /* This column is not already not-null, so add it to the queue */
9533 nnconstr = makeNotNullConstraint(column);
9534
9535 newcmd = makeNode(AlterTableCmd);
9536 newcmd->subtype = AT_AddConstraint;
9537 newcmd->recurse = true;
9538 newcmd->def = (Node *) nnconstr;
9539
9540 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9541 }
9542}
9543
9544/*
9545 * ALTER TABLE ADD INDEX
9546 *
9547 * There is no such command in the grammar, but parse_utilcmd.c converts
9548 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9549 * us schedule creation of the index at the appropriate time during ALTER.
9550 *
9551 * Return value is the address of the new index.
9552 */
9553static ObjectAddress
9555 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9556{
9557 bool check_rights;
9558 bool skip_build;
9559 bool quiet;
9560 ObjectAddress address;
9561
9563 Assert(!stmt->concurrent);
9564
9565 /* The IndexStmt has already been through transformIndexStmt */
9566 Assert(stmt->transformed);
9567
9568 /* suppress schema rights check when rebuilding existing index */
9569 check_rights = !is_rebuild;
9570 /* skip index build if phase 3 will do it or we're reusing an old one */
9571 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9572 /* suppress notices when rebuilding existing index */
9573 quiet = is_rebuild;
9574
9575 address = DefineIndex(RelationGetRelid(rel),
9576 stmt,
9577 InvalidOid, /* no predefined OID */
9578 InvalidOid, /* no parent index */
9579 InvalidOid, /* no parent constraint */
9580 -1, /* total_parts unknown */
9581 true, /* is_alter_table */
9582 check_rights,
9583 false, /* check_not_in_use - we did it already */
9584 skip_build,
9585 quiet);
9586
9587 /*
9588 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9589 * new index instead of building from scratch. Restore associated fields.
9590 * This may store InvalidSubTransactionId in both fields, in which case
9591 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9592 * this after the CCI that made catalog rows visible to any rebuild. The
9593 * DROP of the old edition of this index will have scheduled the storage
9594 * for deletion at commit, so cancel that pending deletion.
9595 */
9596 if (RelFileNumberIsValid(stmt->oldNumber))
9597 {
9598 Relation irel = index_open(address.objectId, NoLock);
9599
9600 irel->rd_createSubid = stmt->oldCreateSubid;
9601 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9603 index_close(irel, NoLock);
9604 }
9605
9606 return address;
9607}
9608
9609/*
9610 * ALTER TABLE ADD STATISTICS
9611 *
9612 * This is no such command in the grammar, but we use this internally to add
9613 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9614 * column type change.
9615 */
9616static ObjectAddress
9618 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9619{
9620 ObjectAddress address;
9621
9623
9624 /* The CreateStatsStmt has already been through transformStatsStmt */
9625 Assert(stmt->transformed);
9626
9627 address = CreateStatistics(stmt);
9628
9629 return address;
9630}
9631
9632/*
9633 * ALTER TABLE ADD CONSTRAINT USING INDEX
9634 *
9635 * Returns the address of the new constraint.
9636 */
9637static ObjectAddress
9639 IndexStmt *stmt, LOCKMODE lockmode)
9640{
9641 Oid index_oid = stmt->indexOid;
9642 Relation indexRel;
9643 char *indexName;
9644 IndexInfo *indexInfo;
9645 char *constraintName;
9646 char constraintType;
9647 ObjectAddress address;
9648 bits16 flags;
9649
9651 Assert(OidIsValid(index_oid));
9652 Assert(stmt->isconstraint);
9653
9654 /*
9655 * Doing this on partitioned tables is not a simple feature to implement,
9656 * so let's punt for now.
9657 */
9658 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9659 ereport(ERROR,
9660 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9661 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9662
9663 indexRel = index_open(index_oid, AccessShareLock);
9664
9665 indexName = pstrdup(RelationGetRelationName(indexRel));
9666
9667 indexInfo = BuildIndexInfo(indexRel);
9668
9669 /* this should have been checked at parse time */
9670 if (!indexInfo->ii_Unique)
9671 elog(ERROR, "index \"%s\" is not unique", indexName);
9672
9673 /*
9674 * Determine name to assign to constraint. We require a constraint to
9675 * have the same name as the underlying index; therefore, use the index's
9676 * existing name as the default constraint name, and if the user
9677 * explicitly gives some other name for the constraint, rename the index
9678 * to match.
9679 */
9680 constraintName = stmt->idxname;
9681 if (constraintName == NULL)
9682 constraintName = indexName;
9683 else if (strcmp(constraintName, indexName) != 0)
9684 {
9686 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9687 indexName, constraintName)));
9688 RenameRelationInternal(index_oid, constraintName, false, true);
9689 }
9690
9691 /* Extra checks needed if making primary key */
9692 if (stmt->primary)
9693 index_check_primary_key(rel, indexInfo, true, stmt);
9694
9695 /* Note we currently don't support EXCLUSION constraints here */
9696 if (stmt->primary)
9697 constraintType = CONSTRAINT_PRIMARY;
9698 else
9699 constraintType = CONSTRAINT_UNIQUE;
9700
9701 /* Create the catalog entries for the constraint */
9704 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9705 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9707
9708 address = index_constraint_create(rel,
9709 index_oid,
9710 InvalidOid,
9711 indexInfo,
9712 constraintName,
9713 constraintType,
9714 flags,
9716 false); /* is_internal */
9717
9718 index_close(indexRel, NoLock);
9719
9720 return address;
9721}
9722
9723/*
9724 * ALTER TABLE ADD CONSTRAINT
9725 *
9726 * Return value is the address of the new constraint; if no constraint was
9727 * added, InvalidObjectAddress is returned.
9728 */
9729static ObjectAddress
9731 Constraint *newConstraint, bool recurse, bool is_readd,
9732 LOCKMODE lockmode)
9733{
9735
9736 Assert(IsA(newConstraint, Constraint));
9737
9738 /*
9739 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9740 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9741 * parse_utilcmd.c).
9742 */
9743 switch (newConstraint->contype)
9744 {
9745 case CONSTR_CHECK:
9746 case CONSTR_NOTNULL:
9747 address =
9748 ATAddCheckNNConstraint(wqueue, tab, rel,
9749 newConstraint, recurse, false, is_readd,
9750 lockmode);
9751 break;
9752
9753 case CONSTR_FOREIGN:
9754
9755 /*
9756 * Assign or validate constraint name
9757 */
9758 if (newConstraint->conname)
9759 {
9761 RelationGetRelid(rel),
9762 newConstraint->conname))
9763 ereport(ERROR,
9765 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9766 newConstraint->conname,
9768 }
9769 else
9770 newConstraint->conname =
9773 "fkey",
9775 NIL);
9776
9777 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9778 newConstraint,
9779 recurse, false,
9780 lockmode);
9781 break;
9782
9783 default:
9784 elog(ERROR, "unrecognized constraint type: %d",
9785 (int) newConstraint->contype);
9786 }
9787
9788 return address;
9789}
9790
9791/*
9792 * Generate the column-name portion of the constraint name for a new foreign
9793 * key given the list of column names that reference the referenced
9794 * table. This will be passed to ChooseConstraintName along with the parent
9795 * table name and the "fkey" suffix.
9796 *
9797 * We know that less than NAMEDATALEN characters will actually be used, so we
9798 * can truncate the result once we've generated that many.
9799 *
9800 * XXX see also ChooseExtendedStatisticNameAddition and
9801 * ChooseIndexNameAddition.
9802 */
9803static char *
9805{
9806 char buf[NAMEDATALEN * 2];
9807 int buflen = 0;
9808 ListCell *lc;
9809
9810 buf[0] = '\0';
9811 foreach(lc, colnames)
9812 {
9813 const char *name = strVal(lfirst(lc));
9814
9815 if (buflen > 0)
9816 buf[buflen++] = '_'; /* insert _ between names */
9817
9818 /*
9819 * At this point we have buflen <= NAMEDATALEN. name should be less
9820 * than NAMEDATALEN already, but use strlcpy for paranoia.
9821 */
9822 strlcpy(buf + buflen, name, NAMEDATALEN);
9823 buflen += strlen(buf + buflen);
9824 if (buflen >= NAMEDATALEN)
9825 break;
9826 }
9827 return pstrdup(buf);
9828}
9829
9830/*
9831 * Add a check or not-null constraint to a single table and its children.
9832 * Returns the address of the constraint added to the parent relation,
9833 * if one gets added, or InvalidObjectAddress otherwise.
9834 *
9835 * Subroutine for ATExecAddConstraint.
9836 *
9837 * We must recurse to child tables during execution, rather than using
9838 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9839 * constraints *must* be given the same name, else they won't be seen as
9840 * related later. If the user didn't explicitly specify a name, then
9841 * AddRelationNewConstraints would normally assign different names to the
9842 * child constraints. To fix that, we must capture the name assigned at
9843 * the parent table and pass that down.
9844 */
9845static ObjectAddress
9847 Constraint *constr, bool recurse, bool recursing,
9848 bool is_readd, LOCKMODE lockmode)
9849{
9850 List *newcons;
9851 ListCell *lcon;
9852 List *children;
9853 ListCell *child;
9855
9856 /* Guard against stack overflow due to overly deep inheritance tree. */
9858
9859 /* At top level, permission check was done in ATPrepCmd, else do it */
9860 if (recursing)
9863
9864 /*
9865 * Call AddRelationNewConstraints to do the work, making sure it works on
9866 * a copy of the Constraint so transformExpr can't modify the original. It
9867 * returns a list of cooked constraints.
9868 *
9869 * If the constraint ends up getting merged with a pre-existing one, it's
9870 * omitted from the returned list, which is what we want: we do not need
9871 * to do any validation work. That can only happen at child tables,
9872 * though, since we disallow merging at the top level.
9873 */
9874 newcons = AddRelationNewConstraints(rel, NIL,
9875 list_make1(copyObject(constr)),
9876 recursing || is_readd, /* allow_merge */
9877 !recursing, /* is_local */
9878 is_readd, /* is_internal */
9879 NULL); /* queryString not available
9880 * here */
9881
9882 /* we don't expect more than one constraint here */
9883 Assert(list_length(newcons) <= 1);
9884
9885 /* Add each to-be-validated constraint to Phase 3's queue */
9886 foreach(lcon, newcons)
9887 {
9888 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9889
9890 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9891 {
9892 NewConstraint *newcon;
9893
9894 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9895 newcon->name = ccon->name;
9896 newcon->contype = ccon->contype;
9897 newcon->qual = ccon->expr;
9898
9899 tab->constraints = lappend(tab->constraints, newcon);
9900 }
9901
9902 /* Save the actually assigned name if it was defaulted */
9903 if (constr->conname == NULL)
9904 constr->conname = ccon->name;
9905
9906 /*
9907 * If adding a valid not-null constraint, set the pg_attribute flag
9908 * and tell phase 3 to verify existing rows, if needed. For an
9909 * invalid constraint, just set attnotnull, without queueing
9910 * verification.
9911 */
9912 if (constr->contype == CONSTR_NOTNULL)
9913 set_attnotnull(wqueue, rel, ccon->attnum,
9914 !constr->skip_validation,
9915 !constr->skip_validation);
9916
9917 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9918 }
9919
9920 /* At this point we must have a locked-down name to use */
9921 Assert(newcons == NIL || constr->conname != NULL);
9922
9923 /* Advance command counter in case same table is visited multiple times */
9925
9926 /*
9927 * If the constraint got merged with an existing constraint, we're done.
9928 * We mustn't recurse to child tables in this case, because they've
9929 * already got the constraint, and visiting them again would lead to an
9930 * incorrect value for coninhcount.
9931 */
9932 if (newcons == NIL)
9933 return address;
9934
9935 /*
9936 * If adding a NO INHERIT constraint, no need to find our children.
9937 */
9938 if (constr->is_no_inherit)
9939 return address;
9940
9941 /*
9942 * Propagate to children as appropriate. Unlike most other ALTER
9943 * routines, we have to do this one level of recursion at a time; we can't
9944 * use find_all_inheritors to do it in one pass.
9945 */
9946 children =
9948
9949 /*
9950 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9951 * constraint creation only if there are no children currently. Error out
9952 * otherwise.
9953 */
9954 if (!recurse && children != NIL)
9955 ereport(ERROR,
9956 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9957 errmsg("constraint must be added to child tables too")));
9958
9959 /*
9960 * Recurse to create the constraint on each child.
9961 */
9962 foreach(child, children)
9963 {
9964 Oid childrelid = lfirst_oid(child);
9965 Relation childrel;
9966 AlteredTableInfo *childtab;
9967
9968 /* find_inheritance_children already got lock */
9969 childrel = table_open(childrelid, NoLock);
9970 CheckAlterTableIsSafe(childrel);
9971
9972 /* Find or create work queue entry for this table */
9973 childtab = ATGetQueueEntry(wqueue, childrel);
9974
9975 /* Recurse to this child */
9976 ATAddCheckNNConstraint(wqueue, childtab, childrel,
9977 constr, recurse, true, is_readd, lockmode);
9978
9979 table_close(childrel, NoLock);
9980 }
9981
9982 return address;
9983}
9984
9985/*
9986 * Add a foreign-key constraint to a single table; return the new constraint's
9987 * address.
9988 *
9989 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9990 * lock on the rel, and have done appropriate validity checks for it.
9991 * We do permissions checks here, however.
9992 *
9993 * When the referenced or referencing tables (or both) are partitioned,
9994 * multiple pg_constraint rows are required -- one for each partitioned table
9995 * and each partition on each side (fortunately, not one for every combination
9996 * thereof). We also need action triggers on each leaf partition on the
9997 * referenced side, and check triggers on each leaf partition on the
9998 * referencing side.
9999 */
10000static ObjectAddress
10002 Constraint *fkconstraint,
10003 bool recurse, bool recursing, LOCKMODE lockmode)
10004{
10005 Relation pkrel;
10006 int16 pkattnum[INDEX_MAX_KEYS] = {0};
10007 int16 fkattnum[INDEX_MAX_KEYS] = {0};
10008 Oid pktypoid[INDEX_MAX_KEYS] = {0};
10009 Oid fktypoid[INDEX_MAX_KEYS] = {0};
10010 Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10011 Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10012 Oid opclasses[INDEX_MAX_KEYS] = {0};
10013 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10014 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10015 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10016 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10017 bool with_period;
10018 bool pk_has_without_overlaps;
10019 int i;
10020 int numfks,
10021 numpks,
10022 numfkdelsetcols;
10023 Oid indexOid;
10024 bool old_check_ok;
10025 ObjectAddress address;
10026 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10027
10028 /*
10029 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10030 * delete rows out from under us.
10031 */
10032 if (OidIsValid(fkconstraint->old_pktable_oid))
10033 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10034 else
10035 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10036
10037 /*
10038 * Validity checks (permission checks wait till we have the column
10039 * numbers)
10040 */
10041 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10042 ereport(ERROR,
10043 errcode(ERRCODE_WRONG_OBJECT_TYPE),
10044 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10046 RelationGetRelationName(pkrel)));
10047
10048 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10049 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10050 ereport(ERROR,
10051 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10052 errmsg("referenced relation \"%s\" is not a table",
10053 RelationGetRelationName(pkrel))));
10054
10056 ereport(ERROR,
10057 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10058 errmsg("permission denied: \"%s\" is a system catalog",
10059 RelationGetRelationName(pkrel))));
10060
10061 /*
10062 * References from permanent or unlogged tables to temp tables, and from
10063 * permanent tables to unlogged tables, are disallowed because the
10064 * referenced data can vanish out from under us. References from temp
10065 * tables to any other table type are also disallowed, because other
10066 * backends might need to run the RI triggers on the perm table, but they
10067 * can't reliably see tuples in the local buffers of other backends.
10068 */
10069 switch (rel->rd_rel->relpersistence)
10070 {
10071 case RELPERSISTENCE_PERMANENT:
10072 if (!RelationIsPermanent(pkrel))
10073 ereport(ERROR,
10074 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10075 errmsg("constraints on permanent tables may reference only permanent tables")));
10076 break;
10077 case RELPERSISTENCE_UNLOGGED:
10078 if (!RelationIsPermanent(pkrel)
10079 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10080 ereport(ERROR,
10081 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10082 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10083 break;
10084 case RELPERSISTENCE_TEMP:
10085 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10086 ereport(ERROR,
10087 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10088 errmsg("constraints on temporary tables may reference only temporary tables")));
10089 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10090 ereport(ERROR,
10091 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10092 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10093 break;
10094 }
10095
10096 /*
10097 * Look up the referencing attributes to make sure they exist, and record
10098 * their attnums and type and collation OIDs.
10099 */
10101 fkconstraint->fk_attrs,
10102 fkattnum, fktypoid, fkcolloid);
10103 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10104 if (with_period && !fkconstraint->fk_with_period)
10105 ereport(ERROR,
10106 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10107 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10108
10109 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10110 fkconstraint->fk_del_set_cols,
10111 fkdelsetcols, NULL, NULL);
10112 numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10113 numfkdelsetcols,
10114 fkdelsetcols,
10115 fkconstraint->fk_del_set_cols);
10116
10117 /*
10118 * If the attribute list for the referenced table was omitted, lookup the
10119 * definition of the primary key and use it. Otherwise, validate the
10120 * supplied attribute list. In either case, discover the index OID and
10121 * index opclasses, and the attnums and type and collation OIDs of the
10122 * attributes.
10123 */
10124 if (fkconstraint->pk_attrs == NIL)
10125 {
10126 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10127 &fkconstraint->pk_attrs,
10128 pkattnum, pktypoid, pkcolloid,
10129 opclasses, &pk_has_without_overlaps);
10130
10131 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10132 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10133 ereport(ERROR,
10134 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10135 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10136 }
10137 else
10138 {
10140 fkconstraint->pk_attrs,
10141 pkattnum, pktypoid, pkcolloid);
10142
10143 /* Since we got pk_attrs, one should be a period. */
10144 if (with_period && !fkconstraint->pk_with_period)
10145 ereport(ERROR,
10146 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10147 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10148
10149 /* Look for an index matching the column list */
10150 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10151 with_period, opclasses, &pk_has_without_overlaps);
10152 }
10153
10154 /*
10155 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10156 * must use PERIOD.
10157 */
10158 if (pk_has_without_overlaps && !with_period)
10159 ereport(ERROR,
10160 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10161 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10162
10163 /*
10164 * Now we can check permissions.
10165 */
10166 checkFkeyPermissions(pkrel, pkattnum, numpks);
10167
10168 /*
10169 * Check some things for generated columns.
10170 */
10171 for (i = 0; i < numfks; i++)
10172 {
10173 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10174
10175 if (attgenerated)
10176 {
10177 /*
10178 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10179 */
10180 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10181 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10182 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10183 ereport(ERROR,
10184 (errcode(ERRCODE_SYNTAX_ERROR),
10185 errmsg("invalid %s action for foreign key constraint containing generated column",
10186 "ON UPDATE")));
10187 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10189 ereport(ERROR,
10190 (errcode(ERRCODE_SYNTAX_ERROR),
10191 errmsg("invalid %s action for foreign key constraint containing generated column",
10192 "ON DELETE")));
10193 }
10194
10195 /*
10196 * FKs on virtual columns are not supported. This would require
10197 * various additional support in ri_triggers.c, including special
10198 * handling in ri_NullCheck(), ri_KeysEqual(),
10199 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10200 * as NULL there). Also not really practical as long as you can't
10201 * index virtual columns.
10202 */
10203 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10204 ereport(ERROR,
10205 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10206 errmsg("foreign key constraints on virtual generated columns are not supported")));
10207 }
10208
10209 /*
10210 * Some actions are currently unsupported for foreign keys using PERIOD.
10211 */
10212 if (fkconstraint->fk_with_period)
10213 {
10214 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10215 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10216 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10218 ereport(ERROR,
10219 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10220 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10221 "ON UPDATE"));
10222
10223 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10224 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10225 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10227 ereport(ERROR,
10228 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10229 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10230 "ON DELETE"));
10231 }
10232
10233 /*
10234 * Look up the equality operators to use in the constraint.
10235 *
10236 * Note that we have to be careful about the difference between the actual
10237 * PK column type and the opclass' declared input type, which might be
10238 * only binary-compatible with it. The declared opcintype is the right
10239 * thing to probe pg_amop with.
10240 */
10241 if (numfks != numpks)
10242 ereport(ERROR,
10243 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10244 errmsg("number of referencing and referenced columns for foreign key disagree")));
10245
10246 /*
10247 * On the strength of a previous constraint, we might avoid scanning
10248 * tables to validate this one. See below.
10249 */
10250 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10251 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10252
10253 for (i = 0; i < numpks; i++)
10254 {
10255 Oid pktype = pktypoid[i];
10256 Oid fktype = fktypoid[i];
10257 Oid fktyped;
10258 Oid pkcoll = pkcolloid[i];
10259 Oid fkcoll = fkcolloid[i];
10260 HeapTuple cla_ht;
10261 Form_pg_opclass cla_tup;
10262 Oid amid;
10263 Oid opfamily;
10264 Oid opcintype;
10265 bool for_overlaps;
10266 CompareType cmptype;
10267 Oid pfeqop;
10268 Oid ppeqop;
10269 Oid ffeqop;
10270 int16 eqstrategy;
10271 Oid pfeqop_right;
10272
10273 /* We need several fields out of the pg_opclass entry */
10274 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10275 if (!HeapTupleIsValid(cla_ht))
10276 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10277 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10278 amid = cla_tup->opcmethod;
10279 opfamily = cla_tup->opcfamily;
10280 opcintype = cla_tup->opcintype;
10281 ReleaseSysCache(cla_ht);
10282
10283 /*
10284 * Get strategy number from index AM.
10285 *
10286 * For a normal foreign-key constraint, this should not fail, since we
10287 * already checked that the index is unique and should therefore have
10288 * appropriate equal operators. For a period foreign key, this could
10289 * fail if we selected a non-matching exclusion constraint earlier.
10290 * (XXX Maybe we should do these lookups earlier so we don't end up
10291 * doing that.)
10292 */
10293 for_overlaps = with_period && i == numpks - 1;
10294 cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10295 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10296 if (eqstrategy == InvalidStrategy)
10297 ereport(ERROR,
10298 errcode(ERRCODE_UNDEFINED_OBJECT),
10299 for_overlaps
10300 ? errmsg("could not identify an overlaps operator for foreign key")
10301 : errmsg("could not identify an equality operator for foreign key"),
10302 errdetail("Could not translate compare type %d for operator family \"%s\", input type %s, access method \"%s\".",
10303 cmptype, get_opfamily_name(opfamily, false), format_type_be(opcintype), get_am_name(amid)));
10304
10305 /*
10306 * There had better be a primary equality operator for the index.
10307 * We'll use it for PK = PK comparisons.
10308 */
10309 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10310 eqstrategy);
10311
10312 if (!OidIsValid(ppeqop))
10313 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10314 eqstrategy, opcintype, opcintype, opfamily);
10315
10316 /*
10317 * Are there equality operators that take exactly the FK type? Assume
10318 * we should look through any domain here.
10319 */
10320 fktyped = getBaseType(fktype);
10321
10322 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10323 eqstrategy);
10324 if (OidIsValid(pfeqop))
10325 {
10326 pfeqop_right = fktyped;
10327 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10328 eqstrategy);
10329 }
10330 else
10331 {
10332 /* keep compiler quiet */
10333 pfeqop_right = InvalidOid;
10334 ffeqop = InvalidOid;
10335 }
10336
10337 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10338 {
10339 /*
10340 * Otherwise, look for an implicit cast from the FK type to the
10341 * opcintype, and if found, use the primary equality operator.
10342 * This is a bit tricky because opcintype might be a polymorphic
10343 * type such as ANYARRAY or ANYENUM; so what we have to test is
10344 * whether the two actual column types can be concurrently cast to
10345 * that type. (Otherwise, we'd fail to reject combinations such
10346 * as int[] and point[].)
10347 */
10348 Oid input_typeids[2];
10349 Oid target_typeids[2];
10350
10351 input_typeids[0] = pktype;
10352 input_typeids[1] = fktype;
10353 target_typeids[0] = opcintype;
10354 target_typeids[1] = opcintype;
10355 if (can_coerce_type(2, input_typeids, target_typeids,
10357 {
10358 pfeqop = ffeqop = ppeqop;
10359 pfeqop_right = opcintype;
10360 }
10361 }
10362
10363 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10364 ereport(ERROR,
10365 (errcode(ERRCODE_DATATYPE_MISMATCH),
10366 errmsg("foreign key constraint \"%s\" cannot be implemented",
10367 fkconstraint->conname),
10368 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10369 "are of incompatible types: %s and %s.",
10370 strVal(list_nth(fkconstraint->fk_attrs, i)),
10371 strVal(list_nth(fkconstraint->pk_attrs, i)),
10372 format_type_be(fktype),
10373 format_type_be(pktype))));
10374
10375 /*
10376 * This shouldn't be possible, but better check to make sure we have a
10377 * consistent state for the check below.
10378 */
10379 if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10380 elog(ERROR, "key columns are not both collatable");
10381
10382 if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10383 {
10384 bool pkcolldet;
10385 bool fkcolldet;
10386
10387 pkcolldet = get_collation_isdeterministic(pkcoll);
10388 fkcolldet = get_collation_isdeterministic(fkcoll);
10389
10390 /*
10391 * SQL requires that both collations are the same. This is
10392 * because we need a consistent notion of equality on both
10393 * columns. We relax this by allowing different collations if
10394 * they are both deterministic. (This is also for backward
10395 * compatibility, because PostgreSQL has always allowed this.)
10396 */
10397 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10398 ereport(ERROR,
10399 (errcode(ERRCODE_COLLATION_MISMATCH),
10400 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10401 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10402 "have incompatible collations: \"%s\" and \"%s\". "
10403 "If either collation is nondeterministic, then both collations have to be the same.",
10404 strVal(list_nth(fkconstraint->fk_attrs, i)),
10405 strVal(list_nth(fkconstraint->pk_attrs, i)),
10406 get_collation_name(fkcoll),
10407 get_collation_name(pkcoll))));
10408 }
10409
10410 if (old_check_ok)
10411 {
10412 /*
10413 * When a pfeqop changes, revalidate the constraint. We could
10414 * permit intra-opfamily changes, but that adds subtle complexity
10415 * without any concrete benefit for core types. We need not
10416 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10417 */
10418 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10419 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10420 old_pfeqop_item);
10421 }
10422 if (old_check_ok)
10423 {
10424 Oid old_fktype;
10425 Oid new_fktype;
10426 CoercionPathType old_pathtype;
10427 CoercionPathType new_pathtype;
10428 Oid old_castfunc;
10429 Oid new_castfunc;
10430 Oid old_fkcoll;
10431 Oid new_fkcoll;
10433 fkattnum[i] - 1);
10434
10435 /*
10436 * Identify coercion pathways from each of the old and new FK-side
10437 * column types to the right (foreign) operand type of the pfeqop.
10438 * We may assume that pg_constraint.conkey is not changing.
10439 */
10440 old_fktype = attr->atttypid;
10441 new_fktype = fktype;
10442 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10443 &old_castfunc);
10444 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10445 &new_castfunc);
10446
10447 old_fkcoll = attr->attcollation;
10448 new_fkcoll = fkcoll;
10449
10450 /*
10451 * Upon a change to the cast from the FK column to its pfeqop
10452 * operand, revalidate the constraint. For this evaluation, a
10453 * binary coercion cast is equivalent to no cast at all. While
10454 * type implementors should design implicit casts with an eye
10455 * toward consistency of operations like equality, we cannot
10456 * assume here that they have done so.
10457 *
10458 * A function with a polymorphic argument could change behavior
10459 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10460 * when the cast destination is polymorphic, we only avoid
10461 * revalidation if the input type has not changed at all. Given
10462 * just the core data types and operator classes, this requirement
10463 * prevents no would-be optimizations.
10464 *
10465 * If the cast converts from a base type to a domain thereon, then
10466 * that domain type must be the opcintype of the unique index.
10467 * Necessarily, the primary key column must then be of the domain
10468 * type. Since the constraint was previously valid, all values on
10469 * the foreign side necessarily exist on the primary side and in
10470 * turn conform to the domain. Consequently, we need not treat
10471 * domains specially here.
10472 *
10473 * If the collation changes, revalidation is required, unless both
10474 * collations are deterministic, because those share the same
10475 * notion of equality (because texteq reduces to bitwise
10476 * equality).
10477 *
10478 * We need not directly consider the PK type. It's necessarily
10479 * binary coercible to the opcintype of the unique index column,
10480 * and ri_triggers.c will only deal with PK datums in terms of
10481 * that opcintype. Changing the opcintype also changes pfeqop.
10482 */
10483 old_check_ok = (new_pathtype == old_pathtype &&
10484 new_castfunc == old_castfunc &&
10485 (!IsPolymorphicType(pfeqop_right) ||
10486 new_fktype == old_fktype) &&
10487 (new_fkcoll == old_fkcoll ||
10488 (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10489 }
10490
10491 pfeqoperators[i] = pfeqop;
10492 ppeqoperators[i] = ppeqop;
10493 ffeqoperators[i] = ffeqop;
10494 }
10495
10496 /*
10497 * For FKs with PERIOD we need additional operators to check whether the
10498 * referencing row's range is contained by the aggregated ranges of the
10499 * referenced row(s). For rangetypes and multirangetypes this is
10500 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10501 * support for now. FKs will look these up at "runtime", but we should
10502 * make sure the lookup works here, even if we don't use the values.
10503 */
10504 if (with_period)
10505 {
10506 Oid periodoperoid;
10507 Oid aggedperiodoperoid;
10508 Oid intersectoperoid;
10509
10510 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10511 &intersectoperoid);
10512 }
10513
10514 /* First, create the constraint catalog entry itself. */
10516 fkconstraint->conname, fkconstraint, rel, pkrel,
10517 indexOid,
10518 InvalidOid, /* no parent constraint */
10519 numfks,
10520 pkattnum,
10521 fkattnum,
10522 pfeqoperators,
10523 ppeqoperators,
10524 ffeqoperators,
10525 numfkdelsetcols,
10526 fkdelsetcols,
10527 false,
10528 with_period);
10529
10530 /* Next process the action triggers at the referenced side and recurse */
10531 addFkRecurseReferenced(fkconstraint, rel, pkrel,
10532 indexOid,
10533 address.objectId,
10534 numfks,
10535 pkattnum,
10536 fkattnum,
10537 pfeqoperators,
10538 ppeqoperators,
10539 ffeqoperators,
10540 numfkdelsetcols,
10541 fkdelsetcols,
10542 old_check_ok,
10544 with_period);
10545
10546 /* Lastly create the check triggers at the referencing side and recurse */
10547 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10548 indexOid,
10549 address.objectId,
10550 numfks,
10551 pkattnum,
10552 fkattnum,
10553 pfeqoperators,
10554 ppeqoperators,
10555 ffeqoperators,
10556 numfkdelsetcols,
10557 fkdelsetcols,
10558 old_check_ok,
10559 lockmode,
10561 with_period);
10562
10563 /*
10564 * Done. Close pk table, but keep lock until we've committed.
10565 */
10566 table_close(pkrel, NoLock);
10567
10568 return address;
10569}
10570
10571/*
10572 * validateFkOnDeleteSetColumns
10573 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10574 * column lists are valid.
10575 *
10576 * If there are duplicates in the fksetcolsattnums[] array, this silently
10577 * removes the dups. The new count of numfksetcols is returned.
10578 */
10579static int
10580validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10581 int numfksetcols, int16 *fksetcolsattnums,
10582 List *fksetcols)
10583{
10584 int numcolsout = 0;
10585
10586 for (int i = 0; i < numfksetcols; i++)
10587 {
10588 int16 setcol_attnum = fksetcolsattnums[i];
10589 bool seen = false;
10590
10591 /* Make sure it's in fkattnums[] */
10592 for (int j = 0; j < numfks; j++)
10593 {
10594 if (fkattnums[j] == setcol_attnum)
10595 {
10596 seen = true;
10597 break;
10598 }
10599 }
10600
10601 if (!seen)
10602 {
10603 char *col = strVal(list_nth(fksetcols, i));
10604
10605 ereport(ERROR,
10606 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10607 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10608 }
10609
10610 /* Now check for dups */
10611 seen = false;
10612 for (int j = 0; j < numcolsout; j++)
10613 {
10614 if (fksetcolsattnums[j] == setcol_attnum)
10615 {
10616 seen = true;
10617 break;
10618 }
10619 }
10620 if (!seen)
10621 fksetcolsattnums[numcolsout++] = setcol_attnum;
10622 }
10623 return numcolsout;
10624}
10625
10626/*
10627 * addFkConstraint
10628 * Install pg_constraint entries to implement a foreign key constraint.
10629 * Caller must separately invoke addFkRecurseReferenced and
10630 * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10631 * and (for partitioned tables) recurse to partitions.
10632 *
10633 * fkside: the side of the FK (or both) to create. Caller should
10634 * call addFkRecurseReferenced if this is addFkReferencedSide,
10635 * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10636 * addFkBothSides.
10637 * constraintname: the base name for the constraint being added,
10638 * copied to fkconstraint->conname if the latter is not set
10639 * fkconstraint: the constraint being added
10640 * rel: the root referencing relation
10641 * pkrel: the referenced relation; might be a partition, if recursing
10642 * indexOid: the OID of the index (on pkrel) implementing this constraint
10643 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10644 * top-level constraint
10645 * numfks: the number of columns in the foreign key
10646 * pkattnum: the attnum array of referenced attributes
10647 * fkattnum: the attnum array of referencing attributes
10648 * pf/pp/ffeqoperators: OID array of operators between columns
10649 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10650 * (...) clause
10651 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10652 * NULL/DEFAULT clause
10653 * with_period: true if this is a temporal FK
10654 */
10655static ObjectAddress
10657 char *constraintname, Constraint *fkconstraint,
10658 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10659 int numfks, int16 *pkattnum,
10660 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10661 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10662 bool is_internal, bool with_period)
10663{
10664 ObjectAddress address;
10665 Oid constrOid;
10666 char *conname;
10667 bool conislocal;
10668 int16 coninhcount;
10669 bool connoinherit;
10670
10671 /*
10672 * Verify relkind for each referenced partition. At the top level, this
10673 * is redundant with a previous check, but we need it when recursing.
10674 */
10675 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10676 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10677 ereport(ERROR,
10678 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10679 errmsg("referenced relation \"%s\" is not a table",
10680 RelationGetRelationName(pkrel))));
10681
10682 /*
10683 * Caller supplies us with a constraint name; however, it may be used in
10684 * this partition, so come up with a different one in that case.
10685 */
10687 RelationGetRelid(rel),
10688 constraintname))
10691 "fkey",
10693 else
10694 conname = constraintname;
10695
10696 if (fkconstraint->conname == NULL)
10697 fkconstraint->conname = pstrdup(conname);
10698
10699 if (OidIsValid(parentConstr))
10700 {
10701 conislocal = false;
10702 coninhcount = 1;
10703 connoinherit = false;
10704 }
10705 else
10706 {
10707 conislocal = true;
10708 coninhcount = 0;
10709
10710 /*
10711 * always inherit for partitioned tables, never for legacy inheritance
10712 */
10713 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10714 }
10715
10716 /*
10717 * Record the FK constraint in pg_constraint.
10718 */
10719 constrOid = CreateConstraintEntry(conname,
10721 CONSTRAINT_FOREIGN,
10722 fkconstraint->deferrable,
10723 fkconstraint->initdeferred,
10724 fkconstraint->is_enforced,
10725 fkconstraint->initially_valid,
10726 parentConstr,
10727 RelationGetRelid(rel),
10728 fkattnum,
10729 numfks,
10730 numfks,
10731 InvalidOid, /* not a domain constraint */
10732 indexOid,
10733 RelationGetRelid(pkrel),
10734 pkattnum,
10735 pfeqoperators,
10736 ppeqoperators,
10737 ffeqoperators,
10738 numfks,
10739 fkconstraint->fk_upd_action,
10740 fkconstraint->fk_del_action,
10741 fkdelsetcols,
10742 numfkdelsetcols,
10743 fkconstraint->fk_matchtype,
10744 NULL, /* no exclusion constraint */
10745 NULL, /* no check constraint */
10746 NULL,
10747 conislocal, /* islocal */
10748 coninhcount, /* inhcount */
10749 connoinherit, /* conNoInherit */
10750 with_period, /* conPeriod */
10751 is_internal); /* is_internal */
10752
10753 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10754
10755 /*
10756 * In partitioning cases, create the dependency entries for this
10757 * constraint. (For non-partitioned cases, relevant entries were created
10758 * by CreateConstraintEntry.)
10759 *
10760 * On the referenced side, we need the constraint to have an internal
10761 * dependency on its parent constraint; this means that this constraint
10762 * cannot be dropped on its own -- only through the parent constraint. It
10763 * also means the containing partition cannot be dropped on its own, but
10764 * it can be detached, at which point this dependency is removed (after
10765 * verifying that no rows are referenced via this FK.)
10766 *
10767 * When processing the referencing side, we link the constraint via the
10768 * special partitioning dependencies: the parent constraint is the primary
10769 * dependent, and the partition on which the foreign key exists is the
10770 * secondary dependency. That way, this constraint is dropped if either
10771 * of these objects is.
10772 *
10773 * Note that this is only necessary for the subsidiary pg_constraint rows
10774 * in partitions; the topmost row doesn't need any of this.
10775 */
10776 if (OidIsValid(parentConstr))
10777 {
10778 ObjectAddress referenced;
10779
10780 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10781
10782 Assert(fkside != addFkBothSides);
10783 if (fkside == addFkReferencedSide)
10784 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10785 else
10786 {
10787 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10788 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10789 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10790 }
10791 }
10792
10793 /* make new constraint visible, in case we add more */
10795
10796 return address;
10797}
10798
10799/*
10800 * addFkRecurseReferenced
10801 * Recursive helper for the referenced side of foreign key creation,
10802 * which creates the action triggers and recurses
10803 *
10804 * If the referenced relation is a plain relation, create the necessary action
10805 * triggers that implement the constraint. If the referenced relation is a
10806 * partitioned table, then we create a pg_constraint row referencing the parent
10807 * of the referencing side for it and recurse on this routine for each
10808 * partition.
10809 *
10810 * fkconstraint: the constraint being added
10811 * rel: the root referencing relation
10812 * pkrel: the referenced relation; might be a partition, if recursing
10813 * indexOid: the OID of the index (on pkrel) implementing this constraint
10814 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10815 * top-level constraint
10816 * numfks: the number of columns in the foreign key
10817 * pkattnum: the attnum array of referenced attributes
10818 * fkattnum: the attnum array of referencing attributes
10819 * numfkdelsetcols: the number of columns in the ON DELETE SET
10820 * NULL/DEFAULT (...) clause
10821 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10822 * NULL/DEFAULT clause
10823 * pf/pp/ffeqoperators: OID array of operators between columns
10824 * old_check_ok: true if this constraint replaces an existing one that
10825 * was already validated (thus this one doesn't need validation)
10826 * parentDelTrigger and parentUpdTrigger: when recursively called on a
10827 * partition, the OIDs of the parent action triggers for DELETE and
10828 * UPDATE respectively.
10829 * with_period: true if this is a temporal FK
10830 */
10831static void
10833 Relation pkrel, Oid indexOid, Oid parentConstr,
10834 int numfks,
10835 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10836 Oid *ppeqoperators, Oid *ffeqoperators,
10837 int numfkdelsetcols, int16 *fkdelsetcols,
10838 bool old_check_ok,
10839 Oid parentDelTrigger, Oid parentUpdTrigger,
10840 bool with_period)
10841{
10842 Oid deleteTriggerOid = InvalidOid,
10843 updateTriggerOid = InvalidOid;
10844
10847
10848 /*
10849 * Create action triggers to enforce the constraint, or skip them if the
10850 * constraint is NOT ENFORCED.
10851 */
10852 if (fkconstraint->is_enforced)
10854 RelationGetRelid(pkrel),
10855 fkconstraint,
10856 parentConstr, indexOid,
10857 parentDelTrigger, parentUpdTrigger,
10858 &deleteTriggerOid, &updateTriggerOid);
10859
10860 /*
10861 * If the referenced table is partitioned, recurse on ourselves to handle
10862 * each partition. We need one pg_constraint row created for each
10863 * partition in addition to the pg_constraint row for the parent table.
10864 */
10865 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10866 {
10867 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10868
10869 for (int i = 0; i < pd->nparts; i++)
10870 {
10871 Relation partRel;
10872 AttrMap *map;
10873 AttrNumber *mapped_pkattnum;
10874 Oid partIndexId;
10875 ObjectAddress address;
10876
10877 /* XXX would it be better to acquire these locks beforehand? */
10878 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10879
10880 /*
10881 * Map the attribute numbers in the referenced side of the FK
10882 * definition to match the partition's column layout.
10883 */
10885 RelationGetDescr(pkrel),
10886 false);
10887 if (map)
10888 {
10889 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10890 for (int j = 0; j < numfks; j++)
10891 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10892 }
10893 else
10894 mapped_pkattnum = pkattnum;
10895
10896 /* Determine the index to use at this level */
10897 partIndexId = index_get_partition(partRel, indexOid);
10898 if (!OidIsValid(partIndexId))
10899 elog(ERROR, "index for %u not found in partition %s",
10900 indexOid, RelationGetRelationName(partRel));
10901
10902 /* Create entry at this level ... */
10904 fkconstraint->conname, fkconstraint, rel,
10905 partRel, partIndexId, parentConstr,
10906 numfks, mapped_pkattnum,
10907 fkattnum, pfeqoperators, ppeqoperators,
10908 ffeqoperators, numfkdelsetcols,
10909 fkdelsetcols, true, with_period);
10910 /* ... and recurse to our children */
10911 addFkRecurseReferenced(fkconstraint, rel, partRel,
10912 partIndexId, address.objectId, numfks,
10913 mapped_pkattnum, fkattnum,
10914 pfeqoperators, ppeqoperators, ffeqoperators,
10915 numfkdelsetcols, fkdelsetcols,
10916 old_check_ok,
10917 deleteTriggerOid, updateTriggerOid,
10918 with_period);
10919
10920 /* Done -- clean up (but keep the lock) */
10921 table_close(partRel, NoLock);
10922 if (map)
10923 {
10924 pfree(mapped_pkattnum);
10925 free_attrmap(map);
10926 }
10927 }
10928 }
10929}
10930
10931/*
10932 * addFkRecurseReferencing
10933 * Recursive helper for the referencing side of foreign key creation,
10934 * which creates the check triggers and recurses
10935 *
10936 * If the referencing relation is a plain relation, create the necessary check
10937 * triggers that implement the constraint, and set up for Phase 3 constraint
10938 * verification. If the referencing relation is a partitioned table, then
10939 * we create a pg_constraint row for it and recurse on this routine for each
10940 * partition.
10941 *
10942 * We assume that the referenced relation is locked against concurrent
10943 * deletions. If it's a partitioned relation, every partition must be so
10944 * locked.
10945 *
10946 * wqueue: the ALTER TABLE work queue; NULL when not running as part
10947 * of an ALTER TABLE sequence.
10948 * fkconstraint: the constraint being added
10949 * rel: the referencing relation; might be a partition, if recursing
10950 * pkrel: the root referenced relation
10951 * indexOid: the OID of the index (on pkrel) implementing this constraint
10952 * parentConstr: the OID of the parent constraint (there is always one)
10953 * numfks: the number of columns in the foreign key
10954 * pkattnum: the attnum array of referenced attributes
10955 * fkattnum: the attnum array of referencing attributes
10956 * pf/pp/ffeqoperators: OID array of operators between columns
10957 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10958 * (...) clause
10959 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10960 * NULL/DEFAULT clause
10961 * old_check_ok: true if this constraint replaces an existing one that
10962 * was already validated (thus this one doesn't need validation)
10963 * lockmode: the lockmode to acquire on partitions when recursing
10964 * parentInsTrigger and parentUpdTrigger: when being recursively called on
10965 * a partition, the OIDs of the parent check triggers for INSERT and
10966 * UPDATE respectively.
10967 * with_period: true if this is a temporal FK
10968 */
10969static void
10971 Relation pkrel, Oid indexOid, Oid parentConstr,
10972 int numfks, int16 *pkattnum, int16 *fkattnum,
10973 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10974 int numfkdelsetcols, int16 *fkdelsetcols,
10975 bool old_check_ok, LOCKMODE lockmode,
10976 Oid parentInsTrigger, Oid parentUpdTrigger,
10977 bool with_period)
10978{
10979 Oid insertTriggerOid = InvalidOid,
10980 updateTriggerOid = InvalidOid;
10981
10982 Assert(OidIsValid(parentConstr));
10985
10986 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10987 ereport(ERROR,
10988 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10989 errmsg("foreign key constraints are not supported on foreign tables")));
10990
10991 /*
10992 * Add check triggers if the constraint is ENFORCED, and if needed,
10993 * schedule them to be checked in Phase 3.
10994 *
10995 * If the relation is partitioned, drill down to do it to its partitions.
10996 */
10997 if (fkconstraint->is_enforced)
10999 RelationGetRelid(pkrel),
11000 fkconstraint,
11001 parentConstr,
11002 indexOid,
11003 parentInsTrigger, parentUpdTrigger,
11004 &insertTriggerOid, &updateTriggerOid);
11005
11006 if (rel->rd_rel->relkind == RELKIND_RELATION)
11007 {
11008 /*
11009 * Tell Phase 3 to check that the constraint is satisfied by existing
11010 * rows. We can skip this during table creation, when constraint is
11011 * specified as NOT ENFORCED, or when requested explicitly by
11012 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11013 * recreating a constraint following a SET DATA TYPE operation that
11014 * did not impugn its validity.
11015 */
11016 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11017 fkconstraint->is_enforced)
11018 {
11019 NewConstraint *newcon;
11020 AlteredTableInfo *tab;
11021
11022 tab = ATGetQueueEntry(wqueue, rel);
11023
11024 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11025 newcon->name = get_constraint_name(parentConstr);
11026 newcon->contype = CONSTR_FOREIGN;
11027 newcon->refrelid = RelationGetRelid(pkrel);
11028 newcon->refindid = indexOid;
11029 newcon->conid = parentConstr;
11030 newcon->conwithperiod = fkconstraint->fk_with_period;
11031 newcon->qual = (Node *) fkconstraint;
11032
11033 tab->constraints = lappend(tab->constraints, newcon);
11034 }
11035 }
11036 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11037 {
11039 Relation trigrel;
11040
11041 /*
11042 * Triggers of the foreign keys will be manipulated a bunch of times
11043 * in the loop below. To avoid repeatedly opening/closing the trigger
11044 * catalog relation, we open it here and pass it to the subroutines
11045 * called below.
11046 */
11047 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11048
11049 /*
11050 * Recurse to take appropriate action on each partition; either we
11051 * find an existing constraint to reparent to ours, or we create a new
11052 * one.
11053 */
11054 for (int i = 0; i < pd->nparts; i++)
11055 {
11056 Relation partition = table_open(pd->oids[i], lockmode);
11057 List *partFKs;
11058 AttrMap *attmap;
11059 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11060 bool attached;
11061 ObjectAddress address;
11062
11063 CheckAlterTableIsSafe(partition);
11064
11065 attmap = build_attrmap_by_name(RelationGetDescr(partition),
11066 RelationGetDescr(rel),
11067 false);
11068 for (int j = 0; j < numfks; j++)
11069 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11070
11071 /* Check whether an existing constraint can be repurposed */
11072 partFKs = copyObject(RelationGetFKeyList(partition));
11073 attached = false;
11074 foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11075 {
11077 fk,
11078 partition,
11079 parentConstr,
11080 numfks,
11081 mapped_fkattnum,
11082 pkattnum,
11083 pfeqoperators,
11084 insertTriggerOid,
11085 updateTriggerOid,
11086 trigrel))
11087 {
11088 attached = true;
11089 break;
11090 }
11091 }
11092 if (attached)
11093 {
11094 table_close(partition, NoLock);
11095 continue;
11096 }
11097
11098 /*
11099 * No luck finding a good constraint to reuse; create our own.
11100 */
11102 fkconstraint->conname, fkconstraint,
11103 partition, pkrel, indexOid, parentConstr,
11104 numfks, pkattnum,
11105 mapped_fkattnum, pfeqoperators,
11106 ppeqoperators, ffeqoperators,
11107 numfkdelsetcols, fkdelsetcols, true,
11108 with_period);
11109
11110 /* call ourselves to finalize the creation and we're done */
11111 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11112 indexOid,
11113 address.objectId,
11114 numfks,
11115 pkattnum,
11116 mapped_fkattnum,
11117 pfeqoperators,
11118 ppeqoperators,
11119 ffeqoperators,
11120 numfkdelsetcols,
11121 fkdelsetcols,
11122 old_check_ok,
11123 lockmode,
11124 insertTriggerOid,
11125 updateTriggerOid,
11126 with_period);
11127
11128 table_close(partition, NoLock);
11129 }
11130
11131 table_close(trigrel, RowExclusiveLock);
11132 }
11133}
11134
11135/*
11136 * CloneForeignKeyConstraints
11137 * Clone foreign keys from a partitioned table to a newly acquired
11138 * partition.
11139 *
11140 * partitionRel is a partition of parentRel, so we can be certain that it has
11141 * the same columns with the same datatypes. The columns may be in different
11142 * order, though.
11143 *
11144 * wqueue must be passed to set up phase 3 constraint checking, unless the
11145 * referencing-side partition is known to be empty (such as in CREATE TABLE /
11146 * PARTITION OF).
11147 */
11148static void
11150 Relation partitionRel)
11151{
11152 /* This only works for declarative partitioning */
11153 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11154
11155 /*
11156 * Clone constraints for which the parent is on the referenced side.
11157 */
11158 CloneFkReferenced(parentRel, partitionRel);
11159
11160 /*
11161 * Now clone constraints where the parent is on the referencing side.
11162 */
11163 CloneFkReferencing(wqueue, parentRel, partitionRel);
11164}
11165
11166/*
11167 * CloneFkReferenced
11168 * Subroutine for CloneForeignKeyConstraints
11169 *
11170 * Find all the FKs that have the parent relation on the referenced side;
11171 * clone those constraints to the given partition. This is to be called
11172 * when the partition is being created or attached.
11173 *
11174 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
11175 *
11176 * This recurses to partitions, if the relation being attached is partitioned.
11177 * Recursion is done by calling addFkRecurseReferenced.
11178 */
11179static void
11180CloneFkReferenced(Relation parentRel, Relation partitionRel)
11181{
11182 Relation pg_constraint;
11183 AttrMap *attmap;
11184 ListCell *cell;
11185 SysScanDesc scan;
11186 ScanKeyData key[2];
11187 HeapTuple tuple;
11188 List *clone = NIL;
11189 Relation trigrel;
11190
11191 /*
11192 * Search for any constraints where this partition's parent is in the
11193 * referenced side. However, we must not clone any constraint whose
11194 * parent constraint is also going to be cloned, to avoid duplicates. So
11195 * do it in two steps: first construct the list of constraints to clone,
11196 * then go over that list cloning those whose parents are not in the list.
11197 * (We must not rely on the parent being seen first, since the catalog
11198 * scan could return children first.)
11199 */
11200 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11201 ScanKeyInit(&key[0],
11202 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11203 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11204 ScanKeyInit(&key[1],
11205 Anum_pg_constraint_contype, BTEqualStrategyNumber,
11206 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11207 /* This is a seqscan, as we don't have a usable index ... */
11208 scan = systable_beginscan(pg_constraint, InvalidOid, true,
11209 NULL, 2, key);
11210 while ((tuple = systable_getnext(scan)) != NULL)
11211 {
11212 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11213
11214 clone = lappend_oid(clone, constrForm->oid);
11215 }
11216 systable_endscan(scan);
11217 table_close(pg_constraint, RowShareLock);
11218
11219 /*
11220 * Triggers of the foreign keys will be manipulated a bunch of times in
11221 * the loop below. To avoid repeatedly opening/closing the trigger
11222 * catalog relation, we open it here and pass it to the subroutines called
11223 * below.
11224 */
11225 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11226
11227 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11228 RelationGetDescr(parentRel),
11229 false);
11230 foreach(cell, clone)
11231 {
11232 Oid constrOid = lfirst_oid(cell);
11233 Form_pg_constraint constrForm;
11234 Relation fkRel;
11235 Oid indexOid;
11236 Oid partIndexId;
11237 int numfks;
11238 AttrNumber conkey[INDEX_MAX_KEYS];
11239 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11240 AttrNumber confkey[INDEX_MAX_KEYS];
11241 Oid conpfeqop[INDEX_MAX_KEYS];
11242 Oid conppeqop[INDEX_MAX_KEYS];
11243 Oid conffeqop[INDEX_MAX_KEYS];
11244 int numfkdelsetcols;
11245 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11246 Constraint *fkconstraint;
11247 ObjectAddress address;
11248 Oid deleteTriggerOid = InvalidOid,
11249 updateTriggerOid = InvalidOid;
11250
11251 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11252 if (!HeapTupleIsValid(tuple))
11253 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11254 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11255
11256 /*
11257 * As explained above: don't try to clone a constraint for which we're
11258 * going to clone the parent.
11259 */
11260 if (list_member_oid(clone, constrForm->conparentid))
11261 {
11262 ReleaseSysCache(tuple);
11263 continue;
11264 }
11265
11266 /*
11267 * Don't clone self-referencing foreign keys, which can be in the
11268 * partitioned table or in the partition-to-be.
11269 */
11270 if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11271 constrForm->conrelid == RelationGetRelid(partitionRel))
11272 {
11273 ReleaseSysCache(tuple);
11274 continue;
11275 }
11276
11277 /* We need the same lock level that CreateTrigger will acquire */
11278 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11279
11280 indexOid = constrForm->conindid;
11282 &numfks,
11283 conkey,
11284 confkey,
11285 conpfeqop,
11286 conppeqop,
11287 conffeqop,
11288 &numfkdelsetcols,
11289 confdelsetcols);
11290
11291 for (int i = 0; i < numfks; i++)
11292 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11293
11294 fkconstraint = makeNode(Constraint);
11295 fkconstraint->contype = CONSTRAINT_FOREIGN;
11296 fkconstraint->conname = NameStr(constrForm->conname);
11297 fkconstraint->deferrable = constrForm->condeferrable;
11298 fkconstraint->initdeferred = constrForm->condeferred;
11299 fkconstraint->location = -1;
11300 fkconstraint->pktable = NULL;
11301 /* ->fk_attrs determined below */
11302 fkconstraint->pk_attrs = NIL;
11303 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11304 fkconstraint->fk_upd_action = constrForm->confupdtype;
11305 fkconstraint->fk_del_action = constrForm->confdeltype;
11306 fkconstraint->fk_del_set_cols = NIL;
11307 fkconstraint->old_conpfeqop = NIL;
11308 fkconstraint->old_pktable_oid = InvalidOid;
11309 fkconstraint->is_enforced = constrForm->conenforced;
11310 fkconstraint->skip_validation = false;
11311 fkconstraint->initially_valid = constrForm->convalidated;
11312
11313 /* set up colnames that are used to generate the constraint name */
11314 for (int i = 0; i < numfks; i++)
11315 {
11317
11318 att = TupleDescAttr(RelationGetDescr(fkRel),
11319 conkey[i] - 1);
11320 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11321 makeString(NameStr(att->attname)));
11322 }
11323
11324 /*
11325 * Add the new foreign key constraint pointing to the new partition.
11326 * Because this new partition appears in the referenced side of the
11327 * constraint, we don't need to set up for Phase 3 check.
11328 */
11329 partIndexId = index_get_partition(partitionRel, indexOid);
11330 if (!OidIsValid(partIndexId))
11331 elog(ERROR, "index for %u not found in partition %s",
11332 indexOid, RelationGetRelationName(partitionRel));
11333
11334 /*
11335 * Get the "action" triggers belonging to the constraint to pass as
11336 * parent OIDs for similar triggers that will be created on the
11337 * partition in addFkRecurseReferenced().
11338 */
11339 if (constrForm->conenforced)
11340 GetForeignKeyActionTriggers(trigrel, constrOid,
11341 constrForm->confrelid, constrForm->conrelid,
11342 &deleteTriggerOid, &updateTriggerOid);
11343
11344 /* Add this constraint ... */
11346 fkconstraint->conname, fkconstraint, fkRel,
11347 partitionRel, partIndexId, constrOid,
11348 numfks, mapped_confkey,
11349 conkey, conpfeqop, conppeqop, conffeqop,
11350 numfkdelsetcols, confdelsetcols, false,
11351 constrForm->conperiod);
11352 /* ... and recurse */
11353 addFkRecurseReferenced(fkconstraint,
11354 fkRel,
11355 partitionRel,
11356 partIndexId,
11357 address.objectId,
11358 numfks,
11359 mapped_confkey,
11360 conkey,
11361 conpfeqop,
11362 conppeqop,
11363 conffeqop,
11364 numfkdelsetcols,
11365 confdelsetcols,
11366 true,
11367 deleteTriggerOid,
11368 updateTriggerOid,
11369 constrForm->conperiod);
11370
11371 table_close(fkRel, NoLock);
11372 ReleaseSysCache(tuple);
11373 }
11374
11375 table_close(trigrel, RowExclusiveLock);
11376}
11377
11378/*
11379 * CloneFkReferencing
11380 * Subroutine for CloneForeignKeyConstraints
11381 *
11382 * For each FK constraint of the parent relation in the given list, find an
11383 * equivalent constraint in its partition relation that can be reparented;
11384 * if one cannot be found, create a new constraint in the partition as its
11385 * child.
11386 *
11387 * If wqueue is given, it is used to set up phase-3 verification for each
11388 * cloned constraint; omit it if such verification is not needed
11389 * (example: the partition is being created anew).
11390 */
11391static void
11392CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11393{
11394 AttrMap *attmap;
11395 List *partFKs;
11396 List *clone = NIL;
11397 ListCell *cell;
11398 Relation trigrel;
11399
11400 /* obtain a list of constraints that we need to clone */
11401 foreach(cell, RelationGetFKeyList(parentRel))
11402 {
11403 ForeignKeyCacheInfo *fk = lfirst(cell);
11404
11405 /*
11406 * Refuse to attach a table as partition that this partitioned table
11407 * already has a foreign key to. This isn't useful schema, which is
11408 * proven by the fact that there have been no user complaints that
11409 * it's already impossible to achieve this in the opposite direction,
11410 * i.e., creating a foreign key that references a partition. This
11411 * restriction allows us to dodge some complexities around
11412 * pg_constraint and pg_trigger row creations that would be needed
11413 * during ATTACH/DETACH for this kind of relationship.
11414 */
11415 if (fk->confrelid == RelationGetRelid(partRel))
11416 ereport(ERROR,
11417 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11418 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11419 RelationGetRelationName(partRel),
11421
11422 clone = lappend_oid(clone, fk->conoid);
11423 }
11424
11425 /*
11426 * Silently do nothing if there's nothing to do. In particular, this
11427 * avoids throwing a spurious error for foreign tables.
11428 */
11429 if (clone == NIL)
11430 return;
11431
11432 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11433 ereport(ERROR,
11434 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11435 errmsg("foreign key constraints are not supported on foreign tables")));
11436
11437 /*
11438 * Triggers of the foreign keys will be manipulated a bunch of times in
11439 * the loop below. To avoid repeatedly opening/closing the trigger
11440 * catalog relation, we open it here and pass it to the subroutines called
11441 * below.
11442 */
11443 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11444
11445 /*
11446 * The constraint key may differ, if the columns in the partition are
11447 * different. This map is used to convert them.
11448 */
11449 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11450 RelationGetDescr(parentRel),
11451 false);
11452
11453 partFKs = copyObject(RelationGetFKeyList(partRel));
11454
11455 foreach(cell, clone)
11456 {
11457 Oid parentConstrOid = lfirst_oid(cell);
11458 Form_pg_constraint constrForm;
11459 Relation pkrel;
11460 HeapTuple tuple;
11461 int numfks;
11462 AttrNumber conkey[INDEX_MAX_KEYS];
11463 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11464 AttrNumber confkey[INDEX_MAX_KEYS];
11465 Oid conpfeqop[INDEX_MAX_KEYS];
11466 Oid conppeqop[INDEX_MAX_KEYS];
11467 Oid conffeqop[INDEX_MAX_KEYS];
11468 int numfkdelsetcols;
11469 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11470 Constraint *fkconstraint;
11471 bool attached;
11472 Oid indexOid;
11473 ObjectAddress address;
11474 ListCell *lc;
11475 Oid insertTriggerOid = InvalidOid,
11476 updateTriggerOid = InvalidOid;
11477 bool with_period;
11478
11479 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11480 if (!HeapTupleIsValid(tuple))
11481 elog(ERROR, "cache lookup failed for constraint %u",
11482 parentConstrOid);
11483 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11484
11485 /* Don't clone constraints whose parents are being cloned */
11486 if (list_member_oid(clone, constrForm->conparentid))
11487 {
11488 ReleaseSysCache(tuple);
11489 continue;
11490 }
11491
11492 /*
11493 * Need to prevent concurrent deletions. If pkrel is a partitioned
11494 * relation, that means to lock all partitions.
11495 */
11496 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11497 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11499 ShareRowExclusiveLock, NULL);
11500
11501 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11502 conpfeqop, conppeqop, conffeqop,
11503 &numfkdelsetcols, confdelsetcols);
11504 for (int i = 0; i < numfks; i++)
11505 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11506
11507 /*
11508 * Get the "check" triggers belonging to the constraint, if it is
11509 * ENFORCED, to pass as parent OIDs for similar triggers that will be
11510 * created on the partition in addFkRecurseReferencing(). They are
11511 * also passed to tryAttachPartitionForeignKey() below to simply
11512 * assign as parents to the partition's existing "check" triggers,
11513 * that is, if the corresponding constraints is deemed attachable to
11514 * the parent constraint.
11515 */
11516 if (constrForm->conenforced)
11517 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11518 constrForm->confrelid, constrForm->conrelid,
11519 &insertTriggerOid, &updateTriggerOid);
11520
11521 /*
11522 * Before creating a new constraint, see whether any existing FKs are
11523 * fit for the purpose. If one is, attach the parent constraint to
11524 * it, and don't clone anything. This way we avoid the expensive
11525 * verification step and don't end up with a duplicate FK, and we
11526 * don't need to recurse to partitions for this constraint.
11527 */
11528 attached = false;
11529 foreach(lc, partFKs)
11530 {
11532
11534 fk,
11535 partRel,
11536 parentConstrOid,
11537 numfks,
11538 mapped_conkey,
11539 confkey,
11540 conpfeqop,
11541 insertTriggerOid,
11542 updateTriggerOid,
11543 trigrel))
11544 {
11545 attached = true;
11546 table_close(pkrel, NoLock);
11547 break;
11548 }
11549 }
11550 if (attached)
11551 {
11552 ReleaseSysCache(tuple);
11553 continue;
11554 }
11555
11556 /* No dice. Set up to create our own constraint */
11557 fkconstraint = makeNode(Constraint);
11558 fkconstraint->contype = CONSTRAINT_FOREIGN;
11559 /* ->conname determined below */
11560 fkconstraint->deferrable = constrForm->condeferrable;
11561 fkconstraint->initdeferred = constrForm->condeferred;
11562 fkconstraint->location = -1;
11563 fkconstraint->pktable = NULL;
11564 /* ->fk_attrs determined below */
11565 fkconstraint->pk_attrs = NIL;
11566 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11567 fkconstraint->fk_upd_action = constrForm->confupdtype;
11568 fkconstraint->fk_del_action = constrForm->confdeltype;
11569 fkconstraint->fk_del_set_cols = NIL;
11570 fkconstraint->old_conpfeqop = NIL;
11571 fkconstraint->old_pktable_oid = InvalidOid;
11572 fkconstraint->is_enforced = constrForm->conenforced;
11573 fkconstraint->skip_validation = false;
11574 fkconstraint->initially_valid = constrForm->convalidated;
11575 for (int i = 0; i < numfks; i++)
11576 {
11578
11579 att = TupleDescAttr(RelationGetDescr(partRel),
11580 mapped_conkey[i] - 1);
11581 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11582 makeString(NameStr(att->attname)));
11583 }
11584
11585 indexOid = constrForm->conindid;
11586 with_period = constrForm->conperiod;
11587
11588 /* Create the pg_constraint entry at this level */
11590 NameStr(constrForm->conname), fkconstraint,
11591 partRel, pkrel, indexOid, parentConstrOid,
11592 numfks, confkey,
11593 mapped_conkey, conpfeqop,
11594 conppeqop, conffeqop,
11595 numfkdelsetcols, confdelsetcols,
11596 false, with_period);
11597
11598 /* Done with the cloned constraint's tuple */
11599 ReleaseSysCache(tuple);
11600
11601 /* Create the check triggers, and recurse to partitions, if any */
11603 fkconstraint,
11604 partRel,
11605 pkrel,
11606 indexOid,
11607 address.objectId,
11608 numfks,
11609 confkey,
11610 mapped_conkey,
11611 conpfeqop,
11612 conppeqop,
11613 conffeqop,
11614 numfkdelsetcols,
11615 confdelsetcols,
11616 false, /* no old check exists */
11618 insertTriggerOid,
11619 updateTriggerOid,
11620 with_period);
11621 table_close(pkrel, NoLock);
11622 }
11623
11624 table_close(trigrel, RowExclusiveLock);
11625}
11626
11627/*
11628 * When the parent of a partition receives [the referencing side of] a foreign
11629 * key, we must propagate that foreign key to the partition. However, the
11630 * partition might already have an equivalent foreign key; this routine
11631 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11632 * by the other parameters. If they are equivalent, create the link between
11633 * the two constraints and return true.
11634 *
11635 * If the given FK does not match the one defined by rest of the params,
11636 * return false.
11637 */
11638static bool
11641 Relation partition,
11642 Oid parentConstrOid,
11643 int numfks,
11644 AttrNumber *mapped_conkey,
11645 AttrNumber *confkey,
11646 Oid *conpfeqop,
11647 Oid parentInsTrigger,
11648 Oid parentUpdTrigger,
11649 Relation trigrel)
11650{
11651 HeapTuple parentConstrTup;
11652 Form_pg_constraint parentConstr;
11653 HeapTuple partcontup;
11654 Form_pg_constraint partConstr;
11655
11656 parentConstrTup = SearchSysCache1(CONSTROID,
11657 ObjectIdGetDatum(parentConstrOid));
11658 if (!HeapTupleIsValid(parentConstrTup))
11659 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11660 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11661
11662 /*
11663 * Do some quick & easy initial checks. If any of these fail, we cannot
11664 * use this constraint.
11665 */
11666 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11667 {
11668 ReleaseSysCache(parentConstrTup);
11669 return false;
11670 }
11671 for (int i = 0; i < numfks; i++)
11672 {
11673 if (fk->conkey[i] != mapped_conkey[i] ||
11674 fk->confkey[i] != confkey[i] ||
11675 fk->conpfeqop[i] != conpfeqop[i])
11676 {
11677 ReleaseSysCache(parentConstrTup);
11678 return false;
11679 }
11680 }
11681
11682 /* Looks good so far; perform more extensive checks. */
11683 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11684 if (!HeapTupleIsValid(partcontup))
11685 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11686 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11687
11688 /*
11689 * An error should be raised if the constraint enforceability is
11690 * different. Returning false without raising an error, as we do for other
11691 * attributes, could lead to a duplicate constraint with the same
11692 * enforceability as the parent. While this may be acceptable, it may not
11693 * be ideal. Therefore, it's better to raise an error and allow the user
11694 * to correct the enforceability before proceeding.
11695 */
11696 if (partConstr->conenforced != parentConstr->conenforced)
11697 ereport(ERROR,
11698 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11699 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11700 NameStr(parentConstr->conname),
11701 NameStr(partConstr->conname),
11702 RelationGetRelationName(partition))));
11703
11704 if (OidIsValid(partConstr->conparentid) ||
11705 partConstr->condeferrable != parentConstr->condeferrable ||
11706 partConstr->condeferred != parentConstr->condeferred ||
11707 partConstr->confupdtype != parentConstr->confupdtype ||
11708 partConstr->confdeltype != parentConstr->confdeltype ||
11709 partConstr->confmatchtype != parentConstr->confmatchtype)
11710 {
11711 ReleaseSysCache(parentConstrTup);
11712 ReleaseSysCache(partcontup);
11713 return false;
11714 }
11715
11716 ReleaseSysCache(parentConstrTup);
11717 ReleaseSysCache(partcontup);
11718
11719 /* Looks good! Attach this constraint. */
11720 AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11721 parentConstrOid, parentInsTrigger,
11722 parentUpdTrigger, trigrel);
11723
11724 return true;
11725}
11726
11727/*
11728 * AttachPartitionForeignKey
11729 *
11730 * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11731 * attaching the constraint, removing redundant triggers and entries from
11732 * pg_constraint, and setting the constraint's parent.
11733 */
11734static void
11736 Relation partition,
11737 Oid partConstrOid,
11738 Oid parentConstrOid,
11739 Oid parentInsTrigger,
11740 Oid parentUpdTrigger,
11741 Relation trigrel)
11742{
11743 HeapTuple parentConstrTup;
11744 Form_pg_constraint parentConstr;
11745 HeapTuple partcontup;
11746 Form_pg_constraint partConstr;
11747 bool queueValidation;
11748 Oid partConstrFrelid;
11749 Oid partConstrRelid;
11750 bool parentConstrIsEnforced;
11751
11752 /* Fetch the parent constraint tuple */
11753 parentConstrTup = SearchSysCache1(CONSTROID,
11754 ObjectIdGetDatum(parentConstrOid));
11755 if (!HeapTupleIsValid(parentConstrTup))
11756 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11757 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11758 parentConstrIsEnforced = parentConstr->conenforced;
11759
11760 /* Fetch the child constraint tuple */
11761 partcontup = SearchSysCache1(CONSTROID,
11762 ObjectIdGetDatum(partConstrOid));
11763 if (!HeapTupleIsValid(partcontup))
11764 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11765 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11766 partConstrFrelid = partConstr->confrelid;
11767 partConstrRelid = partConstr->conrelid;
11768
11769 /*
11770 * If the referenced table is partitioned, then the partition we're
11771 * attaching now has extra pg_constraint rows and action triggers that are
11772 * no longer needed. Remove those.
11773 */
11774 if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11775 {
11776 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11777
11778 RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11779 partConstrRelid);
11780
11781 table_close(pg_constraint, RowShareLock);
11782 }
11783
11784 /*
11785 * Will we need to validate this constraint? A valid parent constraint
11786 * implies that all child constraints have been validated, so if this one
11787 * isn't, we must trigger phase 3 validation.
11788 */
11789 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11790
11791 ReleaseSysCache(partcontup);
11792 ReleaseSysCache(parentConstrTup);
11793
11794 /*
11795 * The action triggers in the new partition become redundant -- the parent
11796 * table already has equivalent ones, and those will be able to reach the
11797 * partition. Remove the ones in the partition. We identify them because
11798 * they have our constraint OID, as well as being on the referenced rel.
11799 */
11800 DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11801 partConstrRelid);
11802
11803 ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11804 RelationGetRelid(partition));
11805
11806 /*
11807 * Like the constraint, attach partition's "check" triggers to the
11808 * corresponding parent triggers if the constraint is ENFORCED. NOT
11809 * ENFORCED constraints do not have these triggers.
11810 */
11811 if (parentConstrIsEnforced)
11812 {
11813 Oid insertTriggerOid,
11814 updateTriggerOid;
11815
11817 partConstrOid, partConstrFrelid, partConstrRelid,
11818 &insertTriggerOid, &updateTriggerOid);
11819 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11820 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11821 RelationGetRelid(partition));
11822 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11823 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11824 RelationGetRelid(partition));
11825 }
11826
11827 /*
11828 * We updated this pg_constraint row above to set its parent; validating
11829 * it will cause its convalidated flag to change, so we need CCI here. In
11830 * addition, we need it unconditionally for the rare case where the parent
11831 * table has *two* identical constraints; when reaching this function for
11832 * the second one, we must have made our changes visible, otherwise we
11833 * would try to attach both to this one.
11834 */
11836
11837 /* If validation is needed, put it in the queue now. */
11838 if (queueValidation)
11839 {
11840 Relation conrel;
11841
11842 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11843
11844 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11845 if (!HeapTupleIsValid(partcontup))
11846 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11847
11848 /* Use the same lock as for AT_ValidateConstraint */
11849 QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
11851 ReleaseSysCache(partcontup);
11853 }
11854}
11855
11856/*
11857 * RemoveInheritedConstraint
11858 *
11859 * Removes the constraint and its associated trigger from the specified
11860 * relation, which inherited the given constraint.
11861 */
11862static void
11864 Oid conrelid)
11865{
11866 ObjectAddresses *objs;
11867 HeapTuple consttup;
11869 SysScanDesc scan;
11870 HeapTuple trigtup;
11871
11873 Anum_pg_constraint_conrelid,
11874 BTEqualStrategyNumber, F_OIDEQ,
11875 ObjectIdGetDatum(conrelid));
11876
11877 scan = systable_beginscan(conrel,
11878 ConstraintRelidTypidNameIndexId,
11879 true, NULL, 1, &key);
11880 objs = new_object_addresses();
11881 while ((consttup = systable_getnext(scan)) != NULL)
11882 {
11883 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11884
11885 if (conform->conparentid != conoid)
11886 continue;
11887 else
11888 {
11889 ObjectAddress addr;
11890 SysScanDesc scan2;
11891 ScanKeyData key2;
11893
11894 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11895 add_exact_object_address(&addr, objs);
11896
11897 /*
11898 * First we must delete the dependency record that binds the
11899 * constraint records together.
11900 */
11901 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11902 conform->oid,
11904 ConstraintRelationId,
11905 conoid);
11906 Assert(n == 1); /* actually only one is expected */
11907
11908 /*
11909 * Now search for the triggers for this constraint and set them up
11910 * for deletion too
11911 */
11912 ScanKeyInit(&key2,
11913 Anum_pg_trigger_tgconstraint,
11914 BTEqualStrategyNumber, F_OIDEQ,
11915 ObjectIdGetDatum(conform->oid));
11916 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11917 true, NULL, 1, &key2);
11918 while ((trigtup = systable_getnext(scan2)) != NULL)
11919 {
11920 ObjectAddressSet(addr, TriggerRelationId,
11921 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11922 add_exact_object_address(&addr, objs);
11923 }
11924 systable_endscan(scan2);
11925 }
11926 }
11927 /* make the dependency deletions visible */
11931 systable_endscan(scan);
11932}
11933
11934/*
11935 * DropForeignKeyConstraintTriggers
11936 *
11937 * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11938 * action triggers for the foreign key constraint.
11939 *
11940 * If valid confrelid and conrelid values are not provided, the respective
11941 * trigger check will be skipped, and the trigger will be considered for
11942 * removal.
11943 */
11944static void
11946 Oid conrelid)
11947{
11949 SysScanDesc scan;
11950 HeapTuple trigtup;
11951
11953 Anum_pg_trigger_tgconstraint,
11954 BTEqualStrategyNumber, F_OIDEQ,
11955 ObjectIdGetDatum(conoid));
11956 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11957 NULL, 1, &key);
11958 while ((trigtup = systable_getnext(scan)) != NULL)
11959 {
11960 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11961 ObjectAddress trigger;
11962
11963 /* Invalid if trigger is not for a referential integrity constraint */
11964 if (!OidIsValid(trgform->tgconstrrelid))
11965 continue;
11966 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
11967 continue;
11968 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
11969 continue;
11970
11971 /* We should be droping trigger related to foreign key constraint */
11972 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
11973 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
11974 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
11975 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
11976 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
11977 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
11978 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
11979 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
11980 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
11981 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
11982 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
11983 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
11984
11985 /*
11986 * The constraint is originally set up to contain this trigger as an
11987 * implementation object, so there's a dependency record that links
11988 * the two; however, since the trigger is no longer needed, we remove
11989 * the dependency link in order to be able to drop the trigger while
11990 * keeping the constraint intact.
11991 */
11992 deleteDependencyRecordsFor(TriggerRelationId,
11993 trgform->oid,
11994 false);
11995 /* make dependency deletion visible to performDeletion */
11997 ObjectAddressSet(trigger, TriggerRelationId,
11998 trgform->oid);
11999 performDeletion(&trigger, DROP_RESTRICT, 0);
12000 /* make trigger drop visible, in case the loop iterates */
12002 }
12003
12004 systable_endscan(scan);
12005}
12006
12007/*
12008 * GetForeignKeyActionTriggers
12009 * Returns delete and update "action" triggers of the given relation
12010 * belonging to the given constraint
12011 */
12012static void
12014 Oid conoid, Oid confrelid, Oid conrelid,
12015 Oid *deleteTriggerOid,
12016 Oid *updateTriggerOid)
12017{
12019 SysScanDesc scan;
12020 HeapTuple trigtup;
12021
12022 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12024 Anum_pg_trigger_tgconstraint,
12025 BTEqualStrategyNumber, F_OIDEQ,
12026 ObjectIdGetDatum(conoid));
12027
12028 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12029 NULL, 1, &key);
12030 while ((trigtup = systable_getnext(scan)) != NULL)
12031 {
12032 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12033
12034 if (trgform->tgconstrrelid != conrelid)
12035 continue;
12036 if (trgform->tgrelid != confrelid)
12037 continue;
12038 /* Only ever look at "action" triggers on the PK side. */
12039 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12040 continue;
12041 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12042 {
12043 Assert(*deleteTriggerOid == InvalidOid);
12044 *deleteTriggerOid = trgform->oid;
12045 }
12046 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12047 {
12048 Assert(*updateTriggerOid == InvalidOid);
12049 *updateTriggerOid = trgform->oid;
12050 }
12051#ifndef USE_ASSERT_CHECKING
12052 /* In an assert-enabled build, continue looking to find duplicates */
12053 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12054 break;
12055#endif
12056 }
12057
12058 if (!OidIsValid(*deleteTriggerOid))
12059 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12060 conoid);
12061 if (!OidIsValid(*updateTriggerOid))
12062 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12063 conoid);
12064
12065 systable_endscan(scan);
12066}
12067
12068/*
12069 * GetForeignKeyCheckTriggers
12070 * Returns insert and update "check" triggers of the given relation
12071 * belonging to the given constraint
12072 */
12073static void
12075 Oid conoid, Oid confrelid, Oid conrelid,
12076 Oid *insertTriggerOid,
12077 Oid *updateTriggerOid)
12078{
12080 SysScanDesc scan;
12081 HeapTuple trigtup;
12082
12083 *insertTriggerOid = *updateTriggerOid = InvalidOid;
12085 Anum_pg_trigger_tgconstraint,
12086 BTEqualStrategyNumber, F_OIDEQ,
12087 ObjectIdGetDatum(conoid));
12088
12089 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12090 NULL, 1, &key);
12091 while ((trigtup = systable_getnext(scan)) != NULL)
12092 {
12093 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12094
12095 if (trgform->tgconstrrelid != confrelid)
12096 continue;
12097 if (trgform->tgrelid != conrelid)
12098 continue;
12099 /* Only ever look at "check" triggers on the FK side. */
12100 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12101 continue;
12102 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12103 {
12104 Assert(*insertTriggerOid == InvalidOid);
12105 *insertTriggerOid = trgform->oid;
12106 }
12107 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12108 {
12109 Assert(*updateTriggerOid == InvalidOid);
12110 *updateTriggerOid = trgform->oid;
12111 }
12112#ifndef USE_ASSERT_CHECKING
12113 /* In an assert-enabled build, continue looking to find duplicates. */
12114 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12115 break;
12116#endif
12117 }
12118
12119 if (!OidIsValid(*insertTriggerOid))
12120 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12121 conoid);
12122 if (!OidIsValid(*updateTriggerOid))
12123 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12124 conoid);
12125
12126 systable_endscan(scan);
12127}
12128
12129/*
12130 * ALTER TABLE ALTER CONSTRAINT
12131 *
12132 * Update the attributes of a constraint.
12133 *
12134 * Currently only works for Foreign Key and not null constraints.
12135 *
12136 * If the constraint is modified, returns its address; otherwise, return
12137 * InvalidObjectAddress.
12138 */
12139static ObjectAddress
12141 bool recurse, LOCKMODE lockmode)
12142{
12143 Relation conrel;
12144 Relation tgrel;
12145 SysScanDesc scan;
12146 ScanKeyData skey[3];
12147 HeapTuple contuple;
12148 Form_pg_constraint currcon;
12149 ObjectAddress address;
12150
12151 /*
12152 * Disallow altering ONLY a partitioned table, as it would make no sense.
12153 * This is okay for legacy inheritance.
12154 */
12155 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12156 ereport(ERROR,
12157 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12158 errmsg("constraint must be altered in child tables too"),
12159 errhint("Do not specify the ONLY keyword."));
12160
12161
12162 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12163 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12164
12165 /*
12166 * Find and check the target constraint
12167 */
12168 ScanKeyInit(&skey[0],
12169 Anum_pg_constraint_conrelid,
12170 BTEqualStrategyNumber, F_OIDEQ,
12172 ScanKeyInit(&skey[1],
12173 Anum_pg_constraint_contypid,
12174 BTEqualStrategyNumber, F_OIDEQ,
12176 ScanKeyInit(&skey[2],
12177 Anum_pg_constraint_conname,
12178 BTEqualStrategyNumber, F_NAMEEQ,
12179 CStringGetDatum(cmdcon->conname));
12180 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12181 true, NULL, 3, skey);
12182
12183 /* There can be at most one matching row */
12184 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12185 ereport(ERROR,
12186 (errcode(ERRCODE_UNDEFINED_OBJECT),
12187 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12188 cmdcon->conname, RelationGetRelationName(rel))));
12189
12190 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12191 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12192 ereport(ERROR,
12193 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12194 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12195 cmdcon->conname, RelationGetRelationName(rel))));
12196 if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12197 ereport(ERROR,
12198 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12199 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12200 cmdcon->conname, RelationGetRelationName(rel))));
12201 if (cmdcon->alterInheritability &&
12202 currcon->contype != CONSTRAINT_NOTNULL)
12203 ereport(ERROR,
12204 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12205 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12206 cmdcon->conname, RelationGetRelationName(rel)));
12207
12208 /* Refuse to modify inheritability of inherited constraints */
12209 if (cmdcon->alterInheritability &&
12210 cmdcon->noinherit && currcon->coninhcount > 0)
12211 ereport(ERROR,
12212 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12213 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12214 NameStr(currcon->conname),
12216
12217 /*
12218 * If it's not the topmost constraint, raise an error.
12219 *
12220 * Altering a non-topmost constraint leaves some triggers untouched, since
12221 * they are not directly connected to this constraint; also, pg_dump would
12222 * ignore the deferrability status of the individual constraint, since it
12223 * only dumps topmost constraints. Avoid these problems by refusing this
12224 * operation and telling the user to alter the parent constraint instead.
12225 */
12226 if (OidIsValid(currcon->conparentid))
12227 {
12228 HeapTuple tp;
12229 Oid parent = currcon->conparentid;
12230 char *ancestorname = NULL;
12231 char *ancestortable = NULL;
12232
12233 /* Loop to find the topmost constraint */
12234 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12235 {
12237
12238 /* If no parent, this is the constraint we want */
12239 if (!OidIsValid(contup->conparentid))
12240 {
12241 ancestorname = pstrdup(NameStr(contup->conname));
12242 ancestortable = get_rel_name(contup->conrelid);
12243 ReleaseSysCache(tp);
12244 break;
12245 }
12246
12247 parent = contup->conparentid;
12248 ReleaseSysCache(tp);
12249 }
12250
12251 ereport(ERROR,
12252 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12253 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12254 cmdcon->conname, RelationGetRelationName(rel)),
12255 ancestorname && ancestortable ?
12256 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12257 cmdcon->conname, ancestorname, ancestortable) : 0,
12258 errhint("You may alter the constraint it derives from instead.")));
12259 }
12260
12261 address = InvalidObjectAddress;
12262
12263 /*
12264 * Do the actual catalog work, and recurse if necessary.
12265 */
12266 if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12267 contuple, recurse, lockmode))
12268 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12269
12270 systable_endscan(scan);
12271
12274
12275 return address;
12276}
12277
12278/*
12279 * A subroutine of ATExecAlterConstraint that calls the respective routines for
12280 * altering constraint's enforceability, deferrability or inheritability.
12281 */
12282static bool
12284 Relation conrel, Relation tgrel, Relation rel,
12285 HeapTuple contuple, bool recurse,
12286 LOCKMODE lockmode)
12287{
12288 Form_pg_constraint currcon;
12289 bool changed = false;
12290 List *otherrelids = NIL;
12291
12292 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12293
12294 /*
12295 * Do the catalog work for the enforceability or deferrability change,
12296 * recurse if necessary.
12297 *
12298 * Note that even if deferrability is requested to be altered along with
12299 * enforceability, we don't need to explicitly update multiple entries in
12300 * pg_trigger related to deferrability.
12301 *
12302 * Modifying enforceability involves either creating or dropping the
12303 * trigger, during which the deferrability setting will be adjusted
12304 * automatically.
12305 */
12306 if (cmdcon->alterEnforceability &&
12307 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12308 currcon->conrelid, currcon->confrelid,
12309 contuple, lockmode, InvalidOid,
12311 changed = true;
12312
12313 else if (cmdcon->alterDeferrability &&
12314 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12315 contuple, recurse, &otherrelids,
12316 lockmode))
12317 {
12318 /*
12319 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12320 * the relations having the constraint itself; here we also invalidate
12321 * for relations that have any triggers that are part of the
12322 * constraint.
12323 */
12324 foreach_oid(relid, otherrelids)
12326
12327 changed = true;
12328 }
12329
12330 /*
12331 * Do the catalog work for the inheritability change.
12332 */
12333 if (cmdcon->alterInheritability &&
12334 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12335 lockmode))
12336 changed = true;
12337
12338 return changed;
12339}
12340
12341/*
12342 * Returns true if the constraint's enforceability is altered.
12343 *
12344 * Depending on whether the constraint is being set to ENFORCED or NOT
12345 * ENFORCED, it creates or drops the trigger accordingly.
12346 *
12347 * Note that we must recurse even when trying to change a constraint to not
12348 * enforced if it is already not enforced, in case descendant constraints
12349 * might be enforced and need to be changed to not enforced. Conversely, we
12350 * should do nothing if a constraint is being set to enforced and is already
12351 * enforced, as descendant constraints cannot be different in that case.
12352 */
12353static bool
12355 Relation conrel, Relation tgrel,
12356 const Oid fkrelid, const Oid pkrelid,
12357 HeapTuple contuple, LOCKMODE lockmode,
12358 Oid ReferencedParentDelTrigger,
12359 Oid ReferencedParentUpdTrigger,
12360 Oid ReferencingParentInsTrigger,
12361 Oid ReferencingParentUpdTrigger)
12362{
12363 Form_pg_constraint currcon;
12364 Oid conoid;
12365 Relation rel;
12366 bool changed = false;
12367
12368 /* Since this function recurses, it could be driven to stack overflow */
12370
12371 Assert(cmdcon->alterEnforceability);
12372
12373 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12374 conoid = currcon->oid;
12375
12376 /* Should be foreign key constraint */
12377 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12378
12379 rel = table_open(currcon->conrelid, lockmode);
12380
12381 if (currcon->conenforced != cmdcon->is_enforced)
12382 {
12383 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12384 changed = true;
12385 }
12386
12387 /* Drop triggers */
12388 if (!cmdcon->is_enforced)
12389 {
12390 /*
12391 * When setting a constraint to NOT ENFORCED, the constraint triggers
12392 * need to be dropped. Therefore, we must process the child relations
12393 * first, followed by the parent, to account for dependencies.
12394 */
12395 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12396 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12397 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12398 fkrelid, pkrelid, contuple,
12399 lockmode, InvalidOid, InvalidOid,
12401
12402 /* Drop all the triggers */
12404 }
12405 else if (changed) /* Create triggers */
12406 {
12407 Oid ReferencedDelTriggerOid = InvalidOid,
12408 ReferencedUpdTriggerOid = InvalidOid,
12409 ReferencingInsTriggerOid = InvalidOid,
12410 ReferencingUpdTriggerOid = InvalidOid;
12411
12412 /* Prepare the minimal information required for trigger creation. */
12413 Constraint *fkconstraint = makeNode(Constraint);
12414
12415 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12416 fkconstraint->fk_matchtype = currcon->confmatchtype;
12417 fkconstraint->fk_upd_action = currcon->confupdtype;
12418 fkconstraint->fk_del_action = currcon->confdeltype;
12419
12420 /* Create referenced triggers */
12421 if (currcon->conrelid == fkrelid)
12422 createForeignKeyActionTriggers(currcon->conrelid,
12423 currcon->confrelid,
12424 fkconstraint,
12425 conoid,
12426 currcon->conindid,
12427 ReferencedParentDelTrigger,
12428 ReferencedParentUpdTrigger,
12429 &ReferencedDelTriggerOid,
12430 &ReferencedUpdTriggerOid);
12431
12432 /* Create referencing triggers */
12433 if (currcon->confrelid == pkrelid)
12434 createForeignKeyCheckTriggers(currcon->conrelid,
12435 pkrelid,
12436 fkconstraint,
12437 conoid,
12438 currcon->conindid,
12439 ReferencingParentInsTrigger,
12440 ReferencingParentUpdTrigger,
12441 &ReferencingInsTriggerOid,
12442 &ReferencingUpdTriggerOid);
12443
12444 /*
12445 * Tell Phase 3 to check that the constraint is satisfied by existing
12446 * rows.
12447 */
12448 if (rel->rd_rel->relkind == RELKIND_RELATION)
12449 {
12450 AlteredTableInfo *tab;
12451 NewConstraint *newcon;
12452
12453 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12454 newcon->name = fkconstraint->conname;
12455 newcon->contype = CONSTR_FOREIGN;
12456 newcon->refrelid = currcon->confrelid;
12457 newcon->refindid = currcon->conindid;
12458 newcon->conid = currcon->oid;
12459 newcon->qual = (Node *) fkconstraint;
12460
12461 /* Find or create work queue entry for this table */
12462 tab = ATGetQueueEntry(wqueue, rel);
12463 tab->constraints = lappend(tab->constraints, newcon);
12464 }
12465
12466 /*
12467 * If the table at either end of the constraint is partitioned, we
12468 * need to recurse and create triggers for each constraint that is a
12469 * child of this one.
12470 */
12471 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12472 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12473 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12474 fkrelid, pkrelid, contuple,
12475 lockmode, ReferencedDelTriggerOid,
12476 ReferencedUpdTriggerOid,
12477 ReferencingInsTriggerOid,
12478 ReferencingUpdTriggerOid);
12479 }
12480
12481 table_close(rel, NoLock);
12482
12483 return changed;
12484}
12485
12486/*
12487 * Returns true if the constraint's deferrability is altered.
12488 *
12489 * *otherrelids is appended OIDs of relations containing affected triggers.
12490 *
12491 * Note that we must recurse even when the values are correct, in case
12492 * indirect descendants have had their constraints altered locally.
12493 * (This could be avoided if we forbade altering constraints in partitions
12494 * but existing releases don't do that.)
12495 */
12496static bool
12498 Relation conrel, Relation tgrel, Relation rel,
12499 HeapTuple contuple, bool recurse,
12500 List **otherrelids, LOCKMODE lockmode)
12501{
12502 Form_pg_constraint currcon;
12503 Oid refrelid;
12504 bool changed = false;
12505
12506 /* since this function recurses, it could be driven to stack overflow */
12508
12509 Assert(cmdcon->alterDeferrability);
12510
12511 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12512 refrelid = currcon->confrelid;
12513
12514 /* Should be foreign key constraint */
12515 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12516
12517 /*
12518 * If called to modify a constraint that's already in the desired state,
12519 * silently do nothing.
12520 */
12521 if (currcon->condeferrable != cmdcon->deferrable ||
12522 currcon->condeferred != cmdcon->initdeferred)
12523 {
12524 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12525 changed = true;
12526
12527 /*
12528 * Now we need to update the multiple entries in pg_trigger that
12529 * implement the constraint.
12530 */
12531 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12532 cmdcon->deferrable,
12533 cmdcon->initdeferred, otherrelids);
12534 }
12535
12536 /*
12537 * If the table at either end of the constraint is partitioned, we need to
12538 * handle every constraint that is a child of this one.
12539 */
12540 if (recurse && changed &&
12541 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12542 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12543 AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12544 contuple, recurse, otherrelids,
12545 lockmode);
12546
12547 return changed;
12548}
12549
12550/*
12551 * Returns true if the constraint's inheritability is altered.
12552 */
12553static bool
12555 Relation conrel, Relation rel,
12556 HeapTuple contuple, LOCKMODE lockmode)
12557{
12558 Form_pg_constraint currcon;
12559 AttrNumber colNum;
12560 char *colName;
12561 List *children;
12562
12563 Assert(cmdcon->alterInheritability);
12564
12565 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12566
12567 /* The current implementation only works for NOT NULL constraints */
12568 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12569
12570 /*
12571 * If called to modify a constraint that's already in the desired state,
12572 * silently do nothing.
12573 */
12574 if (cmdcon->noinherit == currcon->connoinherit)
12575 return false;
12576
12577 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12579
12580 /* Fetch the column number and name */
12581 colNum = extractNotNullColumn(contuple);
12582 colName = get_attname(currcon->conrelid, colNum, false);
12583
12584 /*
12585 * Propagate the change to children. For this subcommand type we don't
12586 * recursively affect children, just the immediate level.
12587 */
12589 lockmode);
12590 foreach_oid(childoid, children)
12591 {
12592 ObjectAddress addr;
12593
12594 if (cmdcon->noinherit)
12595 {
12596 HeapTuple childtup;
12597 Form_pg_constraint childcon;
12598
12599 childtup = findNotNullConstraint(childoid, colName);
12600 if (!childtup)
12601 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12602 colName, childoid);
12603 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12604 Assert(childcon->coninhcount > 0);
12605 childcon->coninhcount--;
12606 childcon->conislocal = true;
12607 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12608 heap_freetuple(childtup);
12609 }
12610 else
12611 {
12612 Relation childrel = table_open(childoid, NoLock);
12613
12614 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12615 colName, true, true, lockmode);
12616 if (OidIsValid(addr.objectId))
12618 table_close(childrel, NoLock);
12619 }
12620 }
12621
12622 return true;
12623}
12624
12625/*
12626 * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12627 * trigger's deferrability.
12628 *
12629 * The arguments to this function have the same meaning as the arguments to
12630 * ATExecAlterConstrDeferrability.
12631 */
12632static void
12634 bool deferrable, bool initdeferred,
12635 List **otherrelids)
12636{
12637 HeapTuple tgtuple;
12638 ScanKeyData tgkey;
12639 SysScanDesc tgscan;
12640
12641 ScanKeyInit(&tgkey,
12642 Anum_pg_trigger_tgconstraint,
12643 BTEqualStrategyNumber, F_OIDEQ,
12644 ObjectIdGetDatum(conoid));
12645 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12646 NULL, 1, &tgkey);
12647 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12648 {
12649 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12650 Form_pg_trigger copy_tg;
12651 HeapTuple tgCopyTuple;
12652
12653 /*
12654 * Remember OIDs of other relation(s) involved in FK constraint.
12655 * (Note: it's likely that we could skip forcing a relcache inval for
12656 * other rels that don't have a trigger whose properties change, but
12657 * let's be conservative.)
12658 */
12659 if (tgform->tgrelid != RelationGetRelid(rel))
12660 *otherrelids = list_append_unique_oid(*otherrelids,
12661 tgform->tgrelid);
12662
12663 /*
12664 * Update enable status and deferrability of RI_FKey_noaction_del,
12665 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12666 * triggers, but not others; see createForeignKeyActionTriggers and
12667 * CreateFKCheckTrigger.
12668 */
12669 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12670 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12671 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12672 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12673 continue;
12674
12675 tgCopyTuple = heap_copytuple(tgtuple);
12676 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12677
12678 copy_tg->tgdeferrable = deferrable;
12679 copy_tg->tginitdeferred = initdeferred;
12680 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12681
12682 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12683
12684 heap_freetuple(tgCopyTuple);
12685 }
12686
12687 systable_endscan(tgscan);
12688}
12689
12690/*
12691 * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12692 * the specified constraint.
12693 *
12694 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12695 * list of child relations and recursing; instead it uses the conparentid
12696 * relationships. This may need to be reconsidered.
12697 *
12698 * The arguments to this function have the same meaning as the arguments to
12699 * ATExecAlterConstrEnforceability.
12700 */
12701static void
12703 Relation conrel, Relation tgrel,
12704 const Oid fkrelid, const Oid pkrelid,
12705 HeapTuple contuple, LOCKMODE lockmode,
12706 Oid ReferencedParentDelTrigger,
12707 Oid ReferencedParentUpdTrigger,
12708 Oid ReferencingParentInsTrigger,
12709 Oid ReferencingParentUpdTrigger)
12710{
12711 Form_pg_constraint currcon;
12712 Oid conoid;
12713 ScanKeyData pkey;
12714 SysScanDesc pscan;
12715 HeapTuple childtup;
12716
12717 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12718 conoid = currcon->oid;
12719
12720 ScanKeyInit(&pkey,
12721 Anum_pg_constraint_conparentid,
12722 BTEqualStrategyNumber, F_OIDEQ,
12723 ObjectIdGetDatum(conoid));
12724
12725 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12726 true, NULL, 1, &pkey);
12727
12728 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12729 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12730 pkrelid, childtup, lockmode,
12731 ReferencedParentDelTrigger,
12732 ReferencedParentUpdTrigger,
12733 ReferencingParentInsTrigger,
12734 ReferencingParentUpdTrigger);
12735
12736 systable_endscan(pscan);
12737}
12738
12739/*
12740 * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12741 * the specified constraint.
12742 *
12743 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12744 * list of child relations and recursing; instead it uses the conparentid
12745 * relationships. This may need to be reconsidered.
12746 *
12747 * The arguments to this function have the same meaning as the arguments to
12748 * ATExecAlterConstrDeferrability.
12749 */
12750static void
12752 Relation conrel, Relation tgrel, Relation rel,
12753 HeapTuple contuple, bool recurse,
12754 List **otherrelids, LOCKMODE lockmode)
12755{
12756 Form_pg_constraint currcon;
12757 Oid conoid;
12758 ScanKeyData pkey;
12759 SysScanDesc pscan;
12760 HeapTuple childtup;
12761
12762 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12763 conoid = currcon->oid;
12764
12765 ScanKeyInit(&pkey,
12766 Anum_pg_constraint_conparentid,
12767 BTEqualStrategyNumber, F_OIDEQ,
12768 ObjectIdGetDatum(conoid));
12769
12770 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12771 true, NULL, 1, &pkey);
12772
12773 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12774 {
12775 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12776 Relation childrel;
12777
12778 childrel = table_open(childcon->conrelid, lockmode);
12779
12780 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12781 childtup, recurse, otherrelids, lockmode);
12782 table_close(childrel, NoLock);
12783 }
12784
12785 systable_endscan(pscan);
12786}
12787
12788/*
12789 * Update the constraint entry for the given ATAlterConstraint command, and
12790 * invoke the appropriate hooks.
12791 */
12792static void
12794 HeapTuple contuple)
12795{
12796 HeapTuple copyTuple;
12797 Form_pg_constraint copy_con;
12798
12799 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12800 cmdcon->alterInheritability);
12801
12802 copyTuple = heap_copytuple(contuple);
12803 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12804
12805 if (cmdcon->alterEnforceability)
12806 {
12807 copy_con->conenforced = cmdcon->is_enforced;
12808
12809 /*
12810 * NB: The convalidated status is irrelevant when the constraint is
12811 * set to NOT ENFORCED, but for consistency, it should still be set
12812 * appropriately. Similarly, if the constraint is later changed to
12813 * ENFORCED, validation will be performed during phase 3, so it makes
12814 * sense to mark it as valid in that case.
12815 */
12816 copy_con->convalidated = cmdcon->is_enforced;
12817 }
12818 if (cmdcon->alterDeferrability)
12819 {
12820 copy_con->condeferrable = cmdcon->deferrable;
12821 copy_con->condeferred = cmdcon->initdeferred;
12822 }
12823 if (cmdcon->alterInheritability)
12824 copy_con->connoinherit = cmdcon->noinherit;
12825
12826 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12827 InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12828
12829 /* Make new constraint flags visible to others */
12830 CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12831
12832 heap_freetuple(copyTuple);
12833}
12834
12835/*
12836 * ALTER TABLE VALIDATE CONSTRAINT
12837 *
12838 * XXX The reason we handle recursion here rather than at Phase 1 is because
12839 * there's no good way to skip recursing when handling foreign keys: there is
12840 * no need to lock children in that case, yet we wouldn't be able to avoid
12841 * doing so at that level.
12842 *
12843 * Return value is the address of the validated constraint. If the constraint
12844 * was already validated, InvalidObjectAddress is returned.
12845 */
12846static ObjectAddress
12847ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12848 bool recurse, bool recursing, LOCKMODE lockmode)
12849{
12850 Relation conrel;
12851 SysScanDesc scan;
12852 ScanKeyData skey[3];
12853 HeapTuple tuple;
12855 ObjectAddress address;
12856
12857 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12858
12859 /*
12860 * Find and check the target constraint
12861 */
12862 ScanKeyInit(&skey[0],
12863 Anum_pg_constraint_conrelid,
12864 BTEqualStrategyNumber, F_OIDEQ,
12866 ScanKeyInit(&skey[1],
12867 Anum_pg_constraint_contypid,
12868 BTEqualStrategyNumber, F_OIDEQ,
12870 ScanKeyInit(&skey[2],
12871 Anum_pg_constraint_conname,
12872 BTEqualStrategyNumber, F_NAMEEQ,
12873 CStringGetDatum(constrName));
12874 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12875 true, NULL, 3, skey);
12876
12877 /* There can be at most one matching row */
12878 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12879 ereport(ERROR,
12880 (errcode(ERRCODE_UNDEFINED_OBJECT),
12881 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12882 constrName, RelationGetRelationName(rel))));
12883
12884 con = (Form_pg_constraint) GETSTRUCT(tuple);
12885 if (con->contype != CONSTRAINT_FOREIGN &&
12886 con->contype != CONSTRAINT_CHECK &&
12887 con->contype != CONSTRAINT_NOTNULL)
12888 ereport(ERROR,
12889 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12890 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key, check, or not-null constraint",
12891 constrName, RelationGetRelationName(rel)));
12892
12893 if (!con->conenforced)
12894 ereport(ERROR,
12895 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12896 errmsg("cannot validate NOT ENFORCED constraint")));
12897
12898 if (!con->convalidated)
12899 {
12900 if (con->contype == CONSTRAINT_FOREIGN)
12901 {
12902 QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
12903 }
12904 else if (con->contype == CONSTRAINT_CHECK)
12905 {
12906 QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12907 tuple, recurse, recursing, lockmode);
12908 }
12909 else if (con->contype == CONSTRAINT_NOTNULL)
12910 {
12911 QueueNNConstraintValidation(wqueue, conrel, rel,
12912 tuple, recurse, recursing, lockmode);
12913 }
12914
12915 ObjectAddressSet(address, ConstraintRelationId, con->oid);
12916 }
12917 else
12918 address = InvalidObjectAddress; /* already validated */
12919
12920 systable_endscan(scan);
12921
12923
12924 return address;
12925}
12926
12927/*
12928 * QueueFKConstraintValidation
12929 *
12930 * Add an entry to the wqueue to validate the given foreign key constraint in
12931 * Phase 3 and update the convalidated field in the pg_constraint catalog
12932 * for the specified relation and all its children.
12933 */
12934static void
12936 HeapTuple contuple, LOCKMODE lockmode)
12937{
12939 AlteredTableInfo *tab;
12940 HeapTuple copyTuple;
12941 Form_pg_constraint copy_con;
12942
12943 con = (Form_pg_constraint) GETSTRUCT(contuple);
12944 Assert(con->contype == CONSTRAINT_FOREIGN);
12945 Assert(!con->convalidated);
12946
12947 if (rel->rd_rel->relkind == RELKIND_RELATION)
12948 {
12949 NewConstraint *newcon;
12950 Constraint *fkconstraint;
12951
12952 /* Queue validation for phase 3 */
12953 fkconstraint = makeNode(Constraint);
12954 /* for now this is all we need */
12955 fkconstraint->conname = pstrdup(NameStr(con->conname));
12956
12957 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12958 newcon->name = fkconstraint->conname;
12959 newcon->contype = CONSTR_FOREIGN;
12960 newcon->refrelid = con->confrelid;
12961 newcon->refindid = con->conindid;
12962 newcon->conid = con->oid;
12963 newcon->qual = (Node *) fkconstraint;
12964
12965 /* Find or create work queue entry for this table */
12966 tab = ATGetQueueEntry(wqueue, rel);
12967 tab->constraints = lappend(tab->constraints, newcon);
12968 }
12969
12970 /*
12971 * If the table at either end of the constraint is partitioned, we need to
12972 * recurse and handle every constraint that is a child of this constraint.
12973 */
12974 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12975 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
12976 {
12977 ScanKeyData pkey;
12978 SysScanDesc pscan;
12979 HeapTuple childtup;
12980
12981 ScanKeyInit(&pkey,
12982 Anum_pg_constraint_conparentid,
12983 BTEqualStrategyNumber, F_OIDEQ,
12984 ObjectIdGetDatum(con->oid));
12985
12986 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12987 true, NULL, 1, &pkey);
12988
12989 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12990 {
12991 Form_pg_constraint childcon;
12992 Relation childrel;
12993
12994 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12995
12996 /*
12997 * If the child constraint has already been validated, no further
12998 * action is required for it or its descendants, as they are all
12999 * valid.
13000 */
13001 if (childcon->convalidated)
13002 continue;
13003
13004 childrel = table_open(childcon->conrelid, lockmode);
13005
13006 QueueFKConstraintValidation(wqueue, conrel, childrel, childtup,
13007 lockmode);
13008 table_close(childrel, NoLock);
13009 }
13010
13011 systable_endscan(pscan);
13012 }
13013
13014 /*
13015 * Now update the catalog, while we have the door open.
13016 */
13017 copyTuple = heap_copytuple(contuple);
13018 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13019 copy_con->convalidated = true;
13020 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13021
13022 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13023
13024 heap_freetuple(copyTuple);
13025}
13026
13027/*
13028 * QueueCheckConstraintValidation
13029 *
13030 * Add an entry to the wqueue to validate the given check constraint in Phase 3
13031 * and update the convalidated field in the pg_constraint catalog for the
13032 * specified relation and all its inheriting children.
13033 */
13034static void
13036 char *constrName, HeapTuple contuple,
13037 bool recurse, bool recursing, LOCKMODE lockmode)
13038{
13040 AlteredTableInfo *tab;
13041 HeapTuple copyTuple;
13042 Form_pg_constraint copy_con;
13043
13044 List *children = NIL;
13045 ListCell *child;
13046 NewConstraint *newcon;
13047 Datum val;
13048 char *conbin;
13049
13050 con = (Form_pg_constraint) GETSTRUCT(contuple);
13051 Assert(con->contype == CONSTRAINT_CHECK);
13052
13053 /*
13054 * If we're recursing, the parent has already done this, so skip it. Also,
13055 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13056 * for it in the children.
13057 */
13058 if (!recursing && !con->connoinherit)
13059 children = find_all_inheritors(RelationGetRelid(rel),
13060 lockmode, NULL);
13061
13062 /*
13063 * For CHECK constraints, we must ensure that we only mark the constraint
13064 * as validated on the parent if it's already validated on the children.
13065 *
13066 * We recurse before validating on the parent, to reduce risk of
13067 * deadlocks.
13068 */
13069 foreach(child, children)
13070 {
13071 Oid childoid = lfirst_oid(child);
13072 Relation childrel;
13073
13074 if (childoid == RelationGetRelid(rel))
13075 continue;
13076
13077 /*
13078 * If we are told not to recurse, there had better not be any child
13079 * tables, because we can't mark the constraint on the parent valid
13080 * unless it is valid for all child tables.
13081 */
13082 if (!recurse)
13083 ereport(ERROR,
13084 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13085 errmsg("constraint must be validated on child tables too")));
13086
13087 /* find_all_inheritors already got lock */
13088 childrel = table_open(childoid, NoLock);
13089
13090 ATExecValidateConstraint(wqueue, childrel, constrName, false,
13091 true, lockmode);
13092 table_close(childrel, NoLock);
13093 }
13094
13095 /* Queue validation for phase 3 */
13096 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13097 newcon->name = constrName;
13098 newcon->contype = CONSTR_CHECK;
13099 newcon->refrelid = InvalidOid;
13100 newcon->refindid = InvalidOid;
13101 newcon->conid = con->oid;
13102
13103 val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13104 Anum_pg_constraint_conbin);
13105 conbin = TextDatumGetCString(val);
13106 newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13107
13108 /* Find or create work queue entry for this table */
13109 tab = ATGetQueueEntry(wqueue, rel);
13110 tab->constraints = lappend(tab->constraints, newcon);
13111
13112 /*
13113 * Invalidate relcache so that others see the new validated constraint.
13114 */
13116
13117 /*
13118 * Now update the catalog, while we have the door open.
13119 */
13120 copyTuple = heap_copytuple(contuple);
13121 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13122 copy_con->convalidated = true;
13123 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13124
13125 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13126
13127 heap_freetuple(copyTuple);
13128}
13129
13130/*
13131 * QueueNNConstraintValidation
13132 *
13133 * Add an entry to the wqueue to validate the given not-null constraint in
13134 * Phase 3 and update the convalidated field in the pg_constraint catalog for
13135 * the specified relation and all its inheriting children.
13136 */
13137static void
13139 HeapTuple contuple, bool recurse, bool recursing,
13140 LOCKMODE lockmode)
13141{
13143 AlteredTableInfo *tab;
13144 HeapTuple copyTuple;
13145 Form_pg_constraint copy_con;
13146 List *children = NIL;
13148 char *colname;
13149
13150 con = (Form_pg_constraint) GETSTRUCT(contuple);
13151 Assert(con->contype == CONSTRAINT_NOTNULL);
13152
13153 attnum = extractNotNullColumn(contuple);
13154
13155 /*
13156 * If we're recursing, we've already done this for parent, so skip it.
13157 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13158 * look for it in the children.
13159 *
13160 * We recurse before validating on the parent, to reduce risk of
13161 * deadlocks.
13162 */
13163 if (!recursing && !con->connoinherit)
13164 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13165
13166 colname = get_attname(RelationGetRelid(rel), attnum, false);
13167 foreach_oid(childoid, children)
13168 {
13169 Relation childrel;
13170 HeapTuple contup;
13171 Form_pg_constraint childcon;
13172 char *conname;
13173
13174 if (childoid == RelationGetRelid(rel))
13175 continue;
13176
13177 /*
13178 * If we are told not to recurse, there had better not be any child
13179 * tables, because we can't mark the constraint on the parent valid
13180 * unless it is valid for all child tables.
13181 */
13182 if (!recurse)
13183 ereport(ERROR,
13184 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13185 errmsg("constraint must be validated on child tables too"));
13186
13187 /*
13188 * The column on child might have a different attnum, so search by
13189 * column name.
13190 */
13191 contup = findNotNullConstraint(childoid, colname);
13192 if (!contup)
13193 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13194 colname, get_rel_name(childoid));
13195 childcon = (Form_pg_constraint) GETSTRUCT(contup);
13196 if (childcon->convalidated)
13197 continue;
13198
13199 /* find_all_inheritors already got lock */
13200 childrel = table_open(childoid, NoLock);
13201 conname = pstrdup(NameStr(childcon->conname));
13202
13203 /* XXX improve ATExecValidateConstraint API to avoid double search */
13204 ATExecValidateConstraint(wqueue, childrel, conname,
13205 false, true, lockmode);
13206 table_close(childrel, NoLock);
13207 }
13208
13209 /* Set attnotnull appropriately without queueing another validation */
13210 set_attnotnull(NULL, rel, attnum, true, false);
13211
13212 tab = ATGetQueueEntry(wqueue, rel);
13213 tab->verify_new_notnull = true;
13214
13215 /*
13216 * Invalidate relcache so that others see the new validated constraint.
13217 */
13219
13220 /*
13221 * Now update the catalogs, while we have the door open.
13222 */
13223 copyTuple = heap_copytuple(contuple);
13224 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13225 copy_con->convalidated = true;
13226 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13227
13228 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13229
13230 heap_freetuple(copyTuple);
13231}
13232
13233/*
13234 * transformColumnNameList - transform list of column names
13235 *
13236 * Lookup each name and return its attnum and, optionally, type and collation
13237 * OIDs
13238 *
13239 * Note: the name of this function suggests that it's general-purpose,
13240 * but actually it's only used to look up names appearing in foreign-key
13241 * clauses. The error messages would need work to use it in other cases,
13242 * and perhaps the validity checks as well.
13243 */
13244static int
13246 int16 *attnums, Oid *atttypids, Oid *attcollids)
13247{
13248 ListCell *l;
13249 int attnum;
13250
13251 attnum = 0;
13252 foreach(l, colList)
13253 {
13254 char *attname = strVal(lfirst(l));
13255 HeapTuple atttuple;
13256 Form_pg_attribute attform;
13257
13258 atttuple = SearchSysCacheAttName(relId, attname);
13259 if (!HeapTupleIsValid(atttuple))
13260 ereport(ERROR,
13261 (errcode(ERRCODE_UNDEFINED_COLUMN),
13262 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13263 attname)));
13264 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13265 if (attform->attnum < 0)
13266 ereport(ERROR,
13267 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13268 errmsg("system columns cannot be used in foreign keys")));
13269 if (attnum >= INDEX_MAX_KEYS)
13270 ereport(ERROR,
13271 (errcode(ERRCODE_TOO_MANY_COLUMNS),
13272 errmsg("cannot have more than %d keys in a foreign key",
13273 INDEX_MAX_KEYS)));
13274 attnums[attnum] = attform->attnum;
13275 if (atttypids != NULL)
13276 atttypids[attnum] = attform->atttypid;
13277 if (attcollids != NULL)
13278 attcollids[attnum] = attform->attcollation;
13279 ReleaseSysCache(atttuple);
13280 attnum++;
13281 }
13282
13283 return attnum;
13284}
13285
13286/*
13287 * transformFkeyGetPrimaryKey -
13288 *
13289 * Look up the names, attnums, types, and collations of the primary key attributes
13290 * for the pkrel. Also return the index OID and index opclasses of the
13291 * index supporting the primary key. Also return whether the index has
13292 * WITHOUT OVERLAPS.
13293 *
13294 * All parameters except pkrel are output parameters. Also, the function
13295 * return value is the number of attributes in the primary key.
13296 *
13297 * Used when the column list in the REFERENCES specification is omitted.
13298 */
13299static int
13301 List **attnamelist,
13302 int16 *attnums, Oid *atttypids, Oid *attcollids,
13303 Oid *opclasses, bool *pk_has_without_overlaps)
13304{
13305 List *indexoidlist;
13306 ListCell *indexoidscan;
13307 HeapTuple indexTuple = NULL;
13308 Form_pg_index indexStruct = NULL;
13309 Datum indclassDatum;
13310 oidvector *indclass;
13311 int i;
13312
13313 /*
13314 * Get the list of index OIDs for the table from the relcache, and look up
13315 * each one in the pg_index syscache until we find one marked primary key
13316 * (hopefully there isn't more than one such). Insist it's valid, too.
13317 */
13318 *indexOid = InvalidOid;
13319
13320 indexoidlist = RelationGetIndexList(pkrel);
13321
13322 foreach(indexoidscan, indexoidlist)
13323 {
13324 Oid indexoid = lfirst_oid(indexoidscan);
13325
13326 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13327 if (!HeapTupleIsValid(indexTuple))
13328 elog(ERROR, "cache lookup failed for index %u", indexoid);
13329 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13330 if (indexStruct->indisprimary && indexStruct->indisvalid)
13331 {
13332 /*
13333 * Refuse to use a deferrable primary key. This is per SQL spec,
13334 * and there would be a lot of interesting semantic problems if we
13335 * tried to allow it.
13336 */
13337 if (!indexStruct->indimmediate)
13338 ereport(ERROR,
13339 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13340 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13341 RelationGetRelationName(pkrel))));
13342
13343 *indexOid = indexoid;
13344 break;
13345 }
13346 ReleaseSysCache(indexTuple);
13347 }
13348
13349 list_free(indexoidlist);
13350
13351 /*
13352 * Check that we found it
13353 */
13354 if (!OidIsValid(*indexOid))
13355 ereport(ERROR,
13356 (errcode(ERRCODE_UNDEFINED_OBJECT),
13357 errmsg("there is no primary key for referenced table \"%s\"",
13358 RelationGetRelationName(pkrel))));
13359
13360 /* Must get indclass the hard way */
13361 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13362 Anum_pg_index_indclass);
13363 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13364
13365 /*
13366 * Now build the list of PK attributes from the indkey definition (we
13367 * assume a primary key cannot have expressional elements)
13368 */
13369 *attnamelist = NIL;
13370 for (i = 0; i < indexStruct->indnkeyatts; i++)
13371 {
13372 int pkattno = indexStruct->indkey.values[i];
13373
13374 attnums[i] = pkattno;
13375 atttypids[i] = attnumTypeId(pkrel, pkattno);
13376 attcollids[i] = attnumCollationId(pkrel, pkattno);
13377 opclasses[i] = indclass->values[i];
13378 *attnamelist = lappend(*attnamelist,
13379 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13380 }
13381
13382 *pk_has_without_overlaps = indexStruct->indisexclusion;
13383
13384 ReleaseSysCache(indexTuple);
13385
13386 return i;
13387}
13388
13389/*
13390 * transformFkeyCheckAttrs -
13391 *
13392 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13393 * reference as part of a foreign key constraint.
13394 *
13395 * Returns the OID of the unique index supporting the constraint and
13396 * populates the caller-provided 'opclasses' array with the opclasses
13397 * associated with the index columns. Also sets whether the index
13398 * uses WITHOUT OVERLAPS.
13399 *
13400 * Raises an ERROR on validation failure.
13401 */
13402static Oid
13404 int numattrs, int16 *attnums,
13405 bool with_period, Oid *opclasses,
13406 bool *pk_has_without_overlaps)
13407{
13408 Oid indexoid = InvalidOid;
13409 bool found = false;
13410 bool found_deferrable = false;
13411 List *indexoidlist;
13412 ListCell *indexoidscan;
13413 int i,
13414 j;
13415
13416 /*
13417 * Reject duplicate appearances of columns in the referenced-columns list.
13418 * Such a case is forbidden by the SQL standard, and even if we thought it
13419 * useful to allow it, there would be ambiguity about how to match the
13420 * list to unique indexes (in particular, it'd be unclear which index
13421 * opclass goes with which FK column).
13422 */
13423 for (i = 0; i < numattrs; i++)
13424 {
13425 for (j = i + 1; j < numattrs; j++)
13426 {
13427 if (attnums[i] == attnums[j])
13428 ereport(ERROR,
13429 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13430 errmsg("foreign key referenced-columns list must not contain duplicates")));
13431 }
13432 }
13433
13434 /*
13435 * Get the list of index OIDs for the table from the relcache, and look up
13436 * each one in the pg_index syscache, and match unique indexes to the list
13437 * of attnums we are given.
13438 */
13439 indexoidlist = RelationGetIndexList(pkrel);
13440
13441 foreach(indexoidscan, indexoidlist)
13442 {
13443 HeapTuple indexTuple;
13444 Form_pg_index indexStruct;
13445
13446 indexoid = lfirst_oid(indexoidscan);
13447 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13448 if (!HeapTupleIsValid(indexTuple))
13449 elog(ERROR, "cache lookup failed for index %u", indexoid);
13450 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13451
13452 /*
13453 * Must have the right number of columns; must be unique (or if
13454 * temporal then exclusion instead) and not a partial index; forget it
13455 * if there are any expressions, too. Invalid indexes are out as well.
13456 */
13457 if (indexStruct->indnkeyatts == numattrs &&
13458 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13459 indexStruct->indisvalid &&
13460 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13461 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13462 {
13463 Datum indclassDatum;
13464 oidvector *indclass;
13465
13466 /* Must get indclass the hard way */
13467 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13468 Anum_pg_index_indclass);
13469 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13470
13471 /*
13472 * The given attnum list may match the index columns in any order.
13473 * Check for a match, and extract the appropriate opclasses while
13474 * we're at it.
13475 *
13476 * We know that attnums[] is duplicate-free per the test at the
13477 * start of this function, and we checked above that the number of
13478 * index columns agrees, so if we find a match for each attnums[]
13479 * entry then we must have a one-to-one match in some order.
13480 */
13481 for (i = 0; i < numattrs; i++)
13482 {
13483 found = false;
13484 for (j = 0; j < numattrs; j++)
13485 {
13486 if (attnums[i] == indexStruct->indkey.values[j])
13487 {
13488 opclasses[i] = indclass->values[j];
13489 found = true;
13490 break;
13491 }
13492 }
13493 if (!found)
13494 break;
13495 }
13496 /* The last attribute in the index must be the PERIOD FK part */
13497 if (found && with_period)
13498 {
13499 int16 periodattnum = attnums[numattrs - 1];
13500
13501 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13502 }
13503
13504 /*
13505 * Refuse to use a deferrable unique/primary key. This is per SQL
13506 * spec, and there would be a lot of interesting semantic problems
13507 * if we tried to allow it.
13508 */
13509 if (found && !indexStruct->indimmediate)
13510 {
13511 /*
13512 * Remember that we found an otherwise matching index, so that
13513 * we can generate a more appropriate error message.
13514 */
13515 found_deferrable = true;
13516 found = false;
13517 }
13518
13519 /* We need to know whether the index has WITHOUT OVERLAPS */
13520 if (found)
13521 *pk_has_without_overlaps = indexStruct->indisexclusion;
13522 }
13523 ReleaseSysCache(indexTuple);
13524 if (found)
13525 break;
13526 }
13527
13528 if (!found)
13529 {
13530 if (found_deferrable)
13531 ereport(ERROR,
13532 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13533 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13534 RelationGetRelationName(pkrel))));
13535 else
13536 ereport(ERROR,
13537 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13538 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13539 RelationGetRelationName(pkrel))));
13540 }
13541
13542 list_free(indexoidlist);
13543
13544 return indexoid;
13545}
13546
13547/*
13548 * findFkeyCast -
13549 *
13550 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13551 * Caller has equal regard for binary coercibility and for an exact match.
13552*/
13553static CoercionPathType
13554findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13555{
13556 CoercionPathType ret;
13557
13558 if (targetTypeId == sourceTypeId)
13559 {
13561 *funcid = InvalidOid;
13562 }
13563 else
13564 {
13565 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13566 COERCION_IMPLICIT, funcid);
13567 if (ret == COERCION_PATH_NONE)
13568 /* A previously-relied-upon cast is now gone. */
13569 elog(ERROR, "could not find cast from %u to %u",
13570 sourceTypeId, targetTypeId);
13571 }
13572
13573 return ret;
13574}
13575
13576/*
13577 * Permissions checks on the referenced table for ADD FOREIGN KEY
13578 *
13579 * Note: we have already checked that the user owns the referencing table,
13580 * else we'd have failed much earlier; no additional checks are needed for it.
13581 */
13582static void
13583checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13584{
13585 Oid roleid = GetUserId();
13586 AclResult aclresult;
13587 int i;
13588
13589 /* Okay if we have relation-level REFERENCES permission */
13590 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13592 if (aclresult == ACLCHECK_OK)
13593 return;
13594 /* Else we must have REFERENCES on each column */
13595 for (i = 0; i < natts; i++)
13596 {
13597 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13598 roleid, ACL_REFERENCES);
13599 if (aclresult != ACLCHECK_OK)
13600 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13602 }
13603}
13604
13605/*
13606 * Scan the existing rows in a table to verify they meet a proposed FK
13607 * constraint.
13608 *
13609 * Caller must have opened and locked both relations appropriately.
13610 */
13611static void
13613 Relation rel,
13614 Relation pkrel,
13615 Oid pkindOid,
13616 Oid constraintOid,
13617 bool hasperiod)
13618{
13619 TupleTableSlot *slot;
13620 TableScanDesc scan;
13621 Trigger trig = {0};
13622 Snapshot snapshot;
13623 MemoryContext oldcxt;
13624 MemoryContext perTupCxt;
13625
13627 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13628
13629 /*
13630 * Build a trigger call structure; we'll need it either way.
13631 */
13632 trig.tgoid = InvalidOid;
13633 trig.tgname = conname;
13635 trig.tgisinternal = true;
13636 trig.tgconstrrelid = RelationGetRelid(pkrel);
13637 trig.tgconstrindid = pkindOid;
13638 trig.tgconstraint = constraintOid;
13639 trig.tgdeferrable = false;
13640 trig.tginitdeferred = false;
13641 /* we needn't fill in remaining fields */
13642
13643 /*
13644 * See if we can do it with a single LEFT JOIN query. A false result
13645 * indicates we must proceed with the fire-the-trigger method. We can't do
13646 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13647 * left joins.
13648 */
13649 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13650 return;
13651
13652 /*
13653 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13654 * if that tuple had just been inserted. If any of those fail, it should
13655 * ereport(ERROR) and that's that.
13656 */
13657 snapshot = RegisterSnapshot(GetLatestSnapshot());
13658 slot = table_slot_create(rel, NULL);
13659 scan = table_beginscan(rel, snapshot, 0, NULL);
13660
13662 "validateForeignKeyConstraint",
13664 oldcxt = MemoryContextSwitchTo(perTupCxt);
13665
13666 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13667 {
13668 LOCAL_FCINFO(fcinfo, 0);
13669 TriggerData trigdata = {0};
13670
13672
13673 /*
13674 * Make a call to the trigger function
13675 *
13676 * No parameters are passed, but we do set a context
13677 */
13678 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13679
13680 /*
13681 * We assume RI_FKey_check_ins won't look at flinfo...
13682 */
13683 trigdata.type = T_TriggerData;
13685 trigdata.tg_relation = rel;
13686 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13687 trigdata.tg_trigslot = slot;
13688 trigdata.tg_trigger = &trig;
13689
13690 fcinfo->context = (Node *) &trigdata;
13691
13692 RI_FKey_check_ins(fcinfo);
13693
13694 MemoryContextReset(perTupCxt);
13695 }
13696
13697 MemoryContextSwitchTo(oldcxt);
13698 MemoryContextDelete(perTupCxt);
13699 table_endscan(scan);
13700 UnregisterSnapshot(snapshot);
13702}
13703
13704/*
13705 * CreateFKCheckTrigger
13706 * Creates the insert (on_insert=true) or update "check" trigger that
13707 * implements a given foreign key
13708 *
13709 * Returns the OID of the so created trigger.
13710 */
13711static Oid
13712CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13713 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13714 bool on_insert)
13715{
13716 ObjectAddress trigAddress;
13717 CreateTrigStmt *fk_trigger;
13718
13719 /*
13720 * Note: for a self-referential FK (referencing and referenced tables are
13721 * the same), it is important that the ON UPDATE action fires before the
13722 * CHECK action, since both triggers will fire on the same row during an
13723 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13724 * state of the row. Triggers fire in name order, so we ensure this by
13725 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13726 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13727 */
13728 fk_trigger = makeNode(CreateTrigStmt);
13729 fk_trigger->replace = false;
13730 fk_trigger->isconstraint = true;
13731 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13732 fk_trigger->relation = NULL;
13733
13734 /* Either ON INSERT or ON UPDATE */
13735 if (on_insert)
13736 {
13737 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13738 fk_trigger->events = TRIGGER_TYPE_INSERT;
13739 }
13740 else
13741 {
13742 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13743 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13744 }
13745
13746 fk_trigger->args = NIL;
13747 fk_trigger->row = true;
13748 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13749 fk_trigger->columns = NIL;
13750 fk_trigger->whenClause = NULL;
13751 fk_trigger->transitionRels = NIL;
13752 fk_trigger->deferrable = fkconstraint->deferrable;
13753 fk_trigger->initdeferred = fkconstraint->initdeferred;
13754 fk_trigger->constrrel = NULL;
13755
13756 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13757 constraintOid, indexOid, InvalidOid,
13758 parentTrigOid, NULL, true, false);
13759
13760 /* Make changes-so-far visible */
13762
13763 return trigAddress.objectId;
13764}
13765
13766/*
13767 * createForeignKeyActionTriggers
13768 * Create the referenced-side "action" triggers that implement a foreign
13769 * key.
13770 *
13771 * Returns the OIDs of the so created triggers in *deleteTrigOid and
13772 * *updateTrigOid.
13773 */
13774static void
13775createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13776 Oid constraintOid, Oid indexOid,
13777 Oid parentDelTrigger, Oid parentUpdTrigger,
13778 Oid *deleteTrigOid, Oid *updateTrigOid)
13779{
13780 CreateTrigStmt *fk_trigger;
13781 ObjectAddress trigAddress;
13782
13783 /*
13784 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13785 * DELETE action on the referenced table.
13786 */
13787 fk_trigger = makeNode(CreateTrigStmt);
13788 fk_trigger->replace = false;
13789 fk_trigger->isconstraint = true;
13790 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13791 fk_trigger->relation = NULL;
13792 fk_trigger->args = NIL;
13793 fk_trigger->row = true;
13794 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13795 fk_trigger->events = TRIGGER_TYPE_DELETE;
13796 fk_trigger->columns = NIL;
13797 fk_trigger->whenClause = NULL;
13798 fk_trigger->transitionRels = NIL;
13799 fk_trigger->constrrel = NULL;
13800
13801 switch (fkconstraint->fk_del_action)
13802 {
13804 fk_trigger->deferrable = fkconstraint->deferrable;
13805 fk_trigger->initdeferred = fkconstraint->initdeferred;
13806 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13807 break;
13809 fk_trigger->deferrable = false;
13810 fk_trigger->initdeferred = false;
13811 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13812 break;
13814 fk_trigger->deferrable = false;
13815 fk_trigger->initdeferred = false;
13816 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13817 break;
13819 fk_trigger->deferrable = false;
13820 fk_trigger->initdeferred = false;
13821 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13822 break;
13824 fk_trigger->deferrable = false;
13825 fk_trigger->initdeferred = false;
13826 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13827 break;
13828 default:
13829 elog(ERROR, "unrecognized FK action type: %d",
13830 (int) fkconstraint->fk_del_action);
13831 break;
13832 }
13833
13834 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13835 constraintOid, indexOid, InvalidOid,
13836 parentDelTrigger, NULL, true, false);
13837 if (deleteTrigOid)
13838 *deleteTrigOid = trigAddress.objectId;
13839
13840 /* Make changes-so-far visible */
13842
13843 /*
13844 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13845 * UPDATE action on the referenced table.
13846 */
13847 fk_trigger = makeNode(CreateTrigStmt);
13848 fk_trigger->replace = false;
13849 fk_trigger->isconstraint = true;
13850 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13851 fk_trigger->relation = NULL;
13852 fk_trigger->args = NIL;
13853 fk_trigger->row = true;
13854 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13855 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13856 fk_trigger->columns = NIL;
13857 fk_trigger->whenClause = NULL;
13858 fk_trigger->transitionRels = NIL;
13859 fk_trigger->constrrel = NULL;
13860
13861 switch (fkconstraint->fk_upd_action)
13862 {
13864 fk_trigger->deferrable = fkconstraint->deferrable;
13865 fk_trigger->initdeferred = fkconstraint->initdeferred;
13866 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13867 break;
13869 fk_trigger->deferrable = false;
13870 fk_trigger->initdeferred = false;
13871 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13872 break;
13874 fk_trigger->deferrable = false;
13875 fk_trigger->initdeferred = false;
13876 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13877 break;
13879 fk_trigger->deferrable = false;
13880 fk_trigger->initdeferred = false;
13881 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13882 break;
13884 fk_trigger->deferrable = false;
13885 fk_trigger->initdeferred = false;
13886 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13887 break;
13888 default:
13889 elog(ERROR, "unrecognized FK action type: %d",
13890 (int) fkconstraint->fk_upd_action);
13891 break;
13892 }
13893
13894 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13895 constraintOid, indexOid, InvalidOid,
13896 parentUpdTrigger, NULL, true, false);
13897 if (updateTrigOid)
13898 *updateTrigOid = trigAddress.objectId;
13899}
13900
13901/*
13902 * createForeignKeyCheckTriggers
13903 * Create the referencing-side "check" triggers that implement a foreign
13904 * key.
13905 *
13906 * Returns the OIDs of the so created triggers in *insertTrigOid and
13907 * *updateTrigOid.
13908 */
13909static void
13911 Constraint *fkconstraint, Oid constraintOid,
13912 Oid indexOid,
13913 Oid parentInsTrigger, Oid parentUpdTrigger,
13914 Oid *insertTrigOid, Oid *updateTrigOid)
13915{
13916 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13917 constraintOid, indexOid,
13918 parentInsTrigger, true);
13919 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13920 constraintOid, indexOid,
13921 parentUpdTrigger, false);
13922}
13923
13924/*
13925 * ALTER TABLE DROP CONSTRAINT
13926 *
13927 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
13928 */
13929static void
13930ATExecDropConstraint(Relation rel, const char *constrName,
13931 DropBehavior behavior, bool recurse,
13932 bool missing_ok, LOCKMODE lockmode)
13933{
13934 Relation conrel;
13935 SysScanDesc scan;
13936 ScanKeyData skey[3];
13937 HeapTuple tuple;
13938 bool found = false;
13939
13940 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13941
13942 /*
13943 * Find and drop the target constraint
13944 */
13945 ScanKeyInit(&skey[0],
13946 Anum_pg_constraint_conrelid,
13947 BTEqualStrategyNumber, F_OIDEQ,
13949 ScanKeyInit(&skey[1],
13950 Anum_pg_constraint_contypid,
13951 BTEqualStrategyNumber, F_OIDEQ,
13953 ScanKeyInit(&skey[2],
13954 Anum_pg_constraint_conname,
13955 BTEqualStrategyNumber, F_NAMEEQ,
13956 CStringGetDatum(constrName));
13957 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13958 true, NULL, 3, skey);
13959
13960 /* There can be at most one matching row */
13961 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
13962 {
13963 dropconstraint_internal(rel, tuple, behavior, recurse, false,
13964 missing_ok, lockmode);
13965 found = true;
13966 }
13967
13968 systable_endscan(scan);
13969
13970 if (!found)
13971 {
13972 if (!missing_ok)
13973 ereport(ERROR,
13974 errcode(ERRCODE_UNDEFINED_OBJECT),
13975 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13976 constrName, RelationGetRelationName(rel)));
13977 else
13979 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
13980 constrName, RelationGetRelationName(rel)));
13981 }
13982
13984}
13985
13986/*
13987 * Remove a constraint, using its pg_constraint tuple
13988 *
13989 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
13990 * DROP NOT NULL.
13991 *
13992 * Returns the address of the constraint being removed.
13993 */
13994static ObjectAddress
13996 bool recurse, bool recursing, bool missing_ok,
13997 LOCKMODE lockmode)
13998{
13999 Relation conrel;
14001 ObjectAddress conobj;
14002 List *children;
14003 bool is_no_inherit_constraint = false;
14004 char *constrName;
14005 char *colname = NULL;
14006
14007 /* Guard against stack overflow due to overly deep inheritance tree. */
14009
14010 /* At top level, permission check was done in ATPrepCmd, else do it */
14011 if (recursing)
14014
14015 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14016
14017 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14018 constrName = NameStr(con->conname);
14019
14020 /* Don't allow drop of inherited constraints */
14021 if (con->coninhcount > 0 && !recursing)
14022 ereport(ERROR,
14023 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14024 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14025 constrName, RelationGetRelationName(rel))));
14026
14027 /*
14028 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14029 *
14030 * While doing that, we're in a good position to disallow dropping a not-
14031 * null constraint underneath a primary key, a replica identity index, or
14032 * a generated identity column.
14033 */
14034 if (con->contype == CONSTRAINT_NOTNULL)
14035 {
14036 Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14037 AttrNumber attnum = extractNotNullColumn(constraintTup);
14038 Bitmapset *pkattrs;
14039 Bitmapset *irattrs;
14040 HeapTuple atttup;
14041 Form_pg_attribute attForm;
14042
14043 /* save column name for recursion step */
14044 colname = get_attname(RelationGetRelid(rel), attnum, false);
14045
14046 /*
14047 * Disallow if it's in the primary key. For partitioned tables we
14048 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14049 * return NULL if the primary key is invalid; but we still need to
14050 * protect not-null constraints under such a constraint, so check the
14051 * slow way.
14052 */
14054
14055 if (pkattrs == NULL &&
14056 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14057 {
14058 Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14059
14060 if (OidIsValid(pkindex))
14061 {
14062 Relation pk = relation_open(pkindex, AccessShareLock);
14063
14064 pkattrs = NULL;
14065 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14066 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14067
14069 }
14070 }
14071
14072 if (pkattrs &&
14074 ereport(ERROR,
14075 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14076 errmsg("column \"%s\" is in a primary key",
14077 get_attname(RelationGetRelid(rel), attnum, false)));
14078
14079 /* Disallow if it's in the replica identity */
14082 ereport(ERROR,
14083 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14084 errmsg("column \"%s\" is in index used as replica identity",
14085 get_attname(RelationGetRelid(rel), attnum, false)));
14086
14087 /* Disallow if it's a GENERATED AS IDENTITY column */
14089 if (!HeapTupleIsValid(atttup))
14090 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14091 attnum, RelationGetRelid(rel));
14092 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14093 if (attForm->attidentity != '\0')
14094 ereport(ERROR,
14095 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14096 errmsg("column \"%s\" of relation \"%s\" is an identity column",
14098 false),
14100
14101 /* All good -- reset attnotnull if needed */
14102 if (attForm->attnotnull)
14103 {
14104 attForm->attnotnull = false;
14105 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14106 }
14107
14109 }
14110
14111 is_no_inherit_constraint = con->connoinherit;
14112
14113 /*
14114 * If it's a foreign-key constraint, we'd better lock the referenced table
14115 * and check that that's not in use, just as we've already done for the
14116 * constrained table (else we might, eg, be dropping a trigger that has
14117 * unfired events). But we can/must skip that in the self-referential
14118 * case.
14119 */
14120 if (con->contype == CONSTRAINT_FOREIGN &&
14121 con->confrelid != RelationGetRelid(rel))
14122 {
14123 Relation frel;
14124
14125 /* Must match lock taken by RemoveTriggerById: */
14126 frel = table_open(con->confrelid, AccessExclusiveLock);
14128 table_close(frel, NoLock);
14129 }
14130
14131 /*
14132 * Perform the actual constraint deletion
14133 */
14134 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14135 performDeletion(&conobj, behavior, 0);
14136
14137 /*
14138 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14139 * are dropped via the dependency mechanism, so we're done here.
14140 */
14141 if (con->contype != CONSTRAINT_CHECK &&
14142 con->contype != CONSTRAINT_NOTNULL &&
14143 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14144 {
14146 return conobj;
14147 }
14148
14149 /*
14150 * Propagate to children as appropriate. Unlike most other ALTER
14151 * routines, we have to do this one level of recursion at a time; we can't
14152 * use find_all_inheritors to do it in one pass.
14153 */
14154 if (!is_no_inherit_constraint)
14155 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14156 else
14157 children = NIL;
14158
14159 foreach_oid(childrelid, children)
14160 {
14161 Relation childrel;
14162 HeapTuple tuple;
14163 Form_pg_constraint childcon;
14164
14165 /* find_inheritance_children already got lock */
14166 childrel = table_open(childrelid, NoLock);
14167 CheckAlterTableIsSafe(childrel);
14168
14169 /*
14170 * We search for not-null constraints by column name, and others by
14171 * constraint name.
14172 */
14173 if (con->contype == CONSTRAINT_NOTNULL)
14174 {
14175 tuple = findNotNullConstraint(childrelid, colname);
14176 if (!HeapTupleIsValid(tuple))
14177 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14178 colname, RelationGetRelid(childrel));
14179 }
14180 else
14181 {
14182 SysScanDesc scan;
14183 ScanKeyData skey[3];
14184
14185 ScanKeyInit(&skey[0],
14186 Anum_pg_constraint_conrelid,
14187 BTEqualStrategyNumber, F_OIDEQ,
14188 ObjectIdGetDatum(childrelid));
14189 ScanKeyInit(&skey[1],
14190 Anum_pg_constraint_contypid,
14191 BTEqualStrategyNumber, F_OIDEQ,
14193 ScanKeyInit(&skey[2],
14194 Anum_pg_constraint_conname,
14195 BTEqualStrategyNumber, F_NAMEEQ,
14196 CStringGetDatum(constrName));
14197 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14198 true, NULL, 3, skey);
14199 /* There can only be one, so no need to loop */
14200 tuple = systable_getnext(scan);
14201 if (!HeapTupleIsValid(tuple))
14202 ereport(ERROR,
14203 (errcode(ERRCODE_UNDEFINED_OBJECT),
14204 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14205 constrName,
14206 RelationGetRelationName(childrel))));
14207 tuple = heap_copytuple(tuple);
14208 systable_endscan(scan);
14209 }
14210
14211 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14212
14213 /* Right now only CHECK and not-null constraints can be inherited */
14214 if (childcon->contype != CONSTRAINT_CHECK &&
14215 childcon->contype != CONSTRAINT_NOTNULL)
14216 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14217
14218 if (childcon->coninhcount <= 0) /* shouldn't happen */
14219 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14220 childrelid, NameStr(childcon->conname));
14221
14222 if (recurse)
14223 {
14224 /*
14225 * If the child constraint has other definition sources, just
14226 * decrement its inheritance count; if not, recurse to delete it.
14227 */
14228 if (childcon->coninhcount == 1 && !childcon->conislocal)
14229 {
14230 /* Time to delete this child constraint, too */
14231 dropconstraint_internal(childrel, tuple, behavior,
14232 recurse, true, missing_ok,
14233 lockmode);
14234 }
14235 else
14236 {
14237 /* Child constraint must survive my deletion */
14238 childcon->coninhcount--;
14239 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14240
14241 /* Make update visible */
14243 }
14244 }
14245 else
14246 {
14247 /*
14248 * If we were told to drop ONLY in this table (no recursion) and
14249 * there are no further parents for this constraint, we need to
14250 * mark the inheritors' constraints as locally defined rather than
14251 * inherited.
14252 */
14253 childcon->coninhcount--;
14254 if (childcon->coninhcount == 0)
14255 childcon->conislocal = true;
14256
14257 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14258
14259 /* Make update visible */
14261 }
14262
14263 heap_freetuple(tuple);
14264
14265 table_close(childrel, NoLock);
14266 }
14267
14269
14270 return conobj;
14271}
14272
14273/*
14274 * ALTER COLUMN TYPE
14275 *
14276 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14277 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14278 * transformed (and must be, because we rely on some transformed fields).
14279 *
14280 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14281 * table will be done "in parallel" during phase 3, so all the USING
14282 * expressions should be parsed assuming the original column types. Also,
14283 * this allows a USING expression to refer to a field that will be dropped.
14284 *
14285 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14286 * the first two execution steps in phase 2; they must not see the effects
14287 * of any other subcommand types, since the USING expressions are parsed
14288 * against the unmodified table's state.
14289 */
14290static void
14292 AlteredTableInfo *tab, Relation rel,
14293 bool recurse, bool recursing,
14294 AlterTableCmd *cmd, LOCKMODE lockmode,
14295 AlterTableUtilityContext *context)
14296{
14297 char *colName = cmd->name;
14298 ColumnDef *def = (ColumnDef *) cmd->def;
14299 TypeName *typeName = def->typeName;
14300 Node *transform = def->cooked_default;
14301 HeapTuple tuple;
14302 Form_pg_attribute attTup;
14304 Oid targettype;
14305 int32 targettypmod;
14306 Oid targetcollid;
14308 ParseState *pstate = make_parsestate(NULL);
14309 AclResult aclresult;
14310 bool is_expr;
14311
14312 pstate->p_sourcetext = context->queryString;
14313
14314 if (rel->rd_rel->reloftype && !recursing)
14315 ereport(ERROR,
14316 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14317 errmsg("cannot alter column type of typed table"),
14318 parser_errposition(pstate, def->location)));
14319
14320 /* lookup the attribute so we can check inheritance status */
14321 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14322 if (!HeapTupleIsValid(tuple))
14323 ereport(ERROR,
14324 (errcode(ERRCODE_UNDEFINED_COLUMN),
14325 errmsg("column \"%s\" of relation \"%s\" does not exist",
14326 colName, RelationGetRelationName(rel)),
14327 parser_errposition(pstate, def->location)));
14328 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14329 attnum = attTup->attnum;
14330
14331 /* Can't alter a system attribute */
14332 if (attnum <= 0)
14333 ereport(ERROR,
14334 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14335 errmsg("cannot alter system column \"%s\"", colName),
14336 parser_errposition(pstate, def->location)));
14337
14338 /*
14339 * Cannot specify USING when altering type of a generated column, because
14340 * that would violate the generation expression.
14341 */
14342 if (attTup->attgenerated && def->cooked_default)
14343 ereport(ERROR,
14344 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14345 errmsg("cannot specify USING when altering type of generated column"),
14346 errdetail("Column \"%s\" is a generated column.", colName),
14347 parser_errposition(pstate, def->location)));
14348
14349 /*
14350 * Don't alter inherited columns. At outer level, there had better not be
14351 * any inherited definition; when recursing, we assume this was checked at
14352 * the parent level (see below).
14353 */
14354 if (attTup->attinhcount > 0 && !recursing)
14355 ereport(ERROR,
14356 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14357 errmsg("cannot alter inherited column \"%s\"", colName),
14358 parser_errposition(pstate, def->location)));
14359
14360 /* Don't alter columns used in the partition key */
14361 if (has_partition_attrs(rel,
14363 &is_expr))
14364 ereport(ERROR,
14365 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14366 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14367 colName, RelationGetRelationName(rel)),
14368 parser_errposition(pstate, def->location)));
14369
14370 /* Look up the target type */
14371 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14372
14373 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14374 if (aclresult != ACLCHECK_OK)
14375 aclcheck_error_type(aclresult, targettype);
14376
14377 /* And the collation */
14378 targetcollid = GetColumnDefCollation(pstate, def, targettype);
14379
14380 /* make sure datatype is legal for a column */
14381 CheckAttributeType(colName, targettype, targetcollid,
14382 list_make1_oid(rel->rd_rel->reltype),
14383 0);
14384
14385 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14386 {
14387 /* do nothing */
14388 }
14389 else if (tab->relkind == RELKIND_RELATION ||
14390 tab->relkind == RELKIND_PARTITIONED_TABLE)
14391 {
14392 /*
14393 * Set up an expression to transform the old data value to the new
14394 * type. If a USING option was given, use the expression as
14395 * transformed by transformAlterTableStmt, else just take the old
14396 * value and try to coerce it. We do this first so that type
14397 * incompatibility can be detected before we waste effort, and because
14398 * we need the expression to be parsed against the original table row
14399 * type.
14400 */
14401 if (!transform)
14402 {
14403 transform = (Node *) makeVar(1, attnum,
14404 attTup->atttypid, attTup->atttypmod,
14405 attTup->attcollation,
14406 0);
14407 }
14408
14409 transform = coerce_to_target_type(pstate,
14410 transform, exprType(transform),
14411 targettype, targettypmod,
14414 -1);
14415 if (transform == NULL)
14416 {
14417 /* error text depends on whether USING was specified or not */
14418 if (def->cooked_default != NULL)
14419 ereport(ERROR,
14420 (errcode(ERRCODE_DATATYPE_MISMATCH),
14421 errmsg("result of USING clause for column \"%s\""
14422 " cannot be cast automatically to type %s",
14423 colName, format_type_be(targettype)),
14424 errhint("You might need to add an explicit cast.")));
14425 else
14426 ereport(ERROR,
14427 (errcode(ERRCODE_DATATYPE_MISMATCH),
14428 errmsg("column \"%s\" cannot be cast automatically to type %s",
14429 colName, format_type_be(targettype)),
14430 !attTup->attgenerated ?
14431 /* translator: USING is SQL, don't translate it */
14432 errhint("You might need to specify \"USING %s::%s\".",
14433 quote_identifier(colName),
14434 format_type_with_typemod(targettype,
14435 targettypmod)) : 0));
14436 }
14437
14438 /* Fix collations after all else */
14439 assign_expr_collations(pstate, transform);
14440
14441 /* Plan the expr now so we can accurately assess the need to rewrite. */
14442 transform = (Node *) expression_planner((Expr *) transform);
14443
14444 /*
14445 * Add a work queue item to make ATRewriteTable update the column
14446 * contents.
14447 */
14449 newval->attnum = attnum;
14450 newval->expr = (Expr *) transform;
14451 newval->is_generated = false;
14452
14453 tab->newvals = lappend(tab->newvals, newval);
14454 if (ATColumnChangeRequiresRewrite(transform, attnum))
14456 }
14457 else if (transform)
14458 ereport(ERROR,
14459 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14460 errmsg("\"%s\" is not a table",
14462
14463 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14464 {
14465 /*
14466 * For relations or columns without storage, do this check now.
14467 * Regular tables will check it later when the table is being
14468 * rewritten.
14469 */
14470 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14471 }
14472
14473 ReleaseSysCache(tuple);
14474
14475 /*
14476 * Recurse manually by queueing a new command for each child, if
14477 * necessary. We cannot apply ATSimpleRecursion here because we need to
14478 * remap attribute numbers in the USING expression, if any.
14479 *
14480 * If we are told not to recurse, there had better not be any child
14481 * tables; else the alter would put them out of step.
14482 */
14483 if (recurse)
14484 {
14485 Oid relid = RelationGetRelid(rel);
14486 List *child_oids,
14487 *child_numparents;
14488 ListCell *lo,
14489 *li;
14490
14491 child_oids = find_all_inheritors(relid, lockmode,
14492 &child_numparents);
14493
14494 /*
14495 * find_all_inheritors does the recursive search of the inheritance
14496 * hierarchy, so all we have to do is process all of the relids in the
14497 * list that it returns.
14498 */
14499 forboth(lo, child_oids, li, child_numparents)
14500 {
14501 Oid childrelid = lfirst_oid(lo);
14502 int numparents = lfirst_int(li);
14503 Relation childrel;
14504 HeapTuple childtuple;
14505 Form_pg_attribute childattTup;
14506
14507 if (childrelid == relid)
14508 continue;
14509
14510 /* find_all_inheritors already got lock */
14511 childrel = relation_open(childrelid, NoLock);
14512 CheckAlterTableIsSafe(childrel);
14513
14514 /*
14515 * Verify that the child doesn't have any inherited definitions of
14516 * this column that came from outside this inheritance hierarchy.
14517 * (renameatt makes a similar test, though in a different way
14518 * because of its different recursion mechanism.)
14519 */
14520 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14521 colName);
14522 if (!HeapTupleIsValid(childtuple))
14523 ereport(ERROR,
14524 (errcode(ERRCODE_UNDEFINED_COLUMN),
14525 errmsg("column \"%s\" of relation \"%s\" does not exist",
14526 colName, RelationGetRelationName(childrel))));
14527 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14528
14529 if (childattTup->attinhcount > numparents)
14530 ereport(ERROR,
14531 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14532 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14533 colName, RelationGetRelationName(childrel))));
14534
14535 ReleaseSysCache(childtuple);
14536
14537 /*
14538 * Remap the attribute numbers. If no USING expression was
14539 * specified, there is no need for this step.
14540 */
14541 if (def->cooked_default)
14542 {
14543 AttrMap *attmap;
14544 bool found_whole_row;
14545
14546 /* create a copy to scribble on */
14547 cmd = copyObject(cmd);
14548
14549 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14550 RelationGetDescr(rel),
14551 false);
14552 ((ColumnDef *) cmd->def)->cooked_default =
14554 1, 0,
14555 attmap,
14556 InvalidOid, &found_whole_row);
14557 if (found_whole_row)
14558 ereport(ERROR,
14559 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14560 errmsg("cannot convert whole-row table reference"),
14561 errdetail("USING expression contains a whole-row table reference.")));
14562 pfree(attmap);
14563 }
14564 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14565 relation_close(childrel, NoLock);
14566 }
14567 }
14568 else if (!recursing &&
14570 ereport(ERROR,
14571 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14572 errmsg("type of inherited column \"%s\" must be changed in child tables too",
14573 colName)));
14574
14575 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14576 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14577}
14578
14579/*
14580 * When the data type of a column is changed, a rewrite might not be required
14581 * if the new type is sufficiently identical to the old one, and the USING
14582 * clause isn't trying to insert some other value. It's safe to skip the
14583 * rewrite in these cases:
14584 *
14585 * - the old type is binary coercible to the new type
14586 * - the new type is an unconstrained domain over the old type
14587 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14588 *
14589 * In the case of a constrained domain, we could get by with scanning the
14590 * table and checking the constraint rather than actually rewriting it, but we
14591 * don't currently try to do that.
14592 */
14593static bool
14595{
14596 Assert(expr != NULL);
14597
14598 for (;;)
14599 {
14600 /* only one varno, so no need to check that */
14601 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14602 return false;
14603 else if (IsA(expr, RelabelType))
14604 expr = (Node *) ((RelabelType *) expr)->arg;
14605 else if (IsA(expr, CoerceToDomain))
14606 {
14607 CoerceToDomain *d = (CoerceToDomain *) expr;
14608
14610 return true;
14611 expr = (Node *) d->arg;
14612 }
14613 else if (IsA(expr, FuncExpr))
14614 {
14615 FuncExpr *f = (FuncExpr *) expr;
14616
14617 switch (f->funcid)
14618 {
14619 case F_TIMESTAMPTZ_TIMESTAMP:
14620 case F_TIMESTAMP_TIMESTAMPTZ:
14622 return true;
14623 else
14624 expr = linitial(f->args);
14625 break;
14626 default:
14627 return true;
14628 }
14629 }
14630 else
14631 return true;
14632 }
14633}
14634
14635/*
14636 * ALTER COLUMN .. SET DATA TYPE
14637 *
14638 * Return the address of the modified column.
14639 */
14640static ObjectAddress
14642 AlterTableCmd *cmd, LOCKMODE lockmode)
14643{
14644 char *colName = cmd->name;
14645 ColumnDef *def = (ColumnDef *) cmd->def;
14646 TypeName *typeName = def->typeName;
14647 HeapTuple heapTup;
14648 Form_pg_attribute attTup,
14649 attOldTup;
14651 HeapTuple typeTuple;
14652 Form_pg_type tform;
14653 Oid targettype;
14654 int32 targettypmod;
14655 Oid targetcollid;
14656 Node *defaultexpr;
14657 Relation attrelation;
14658 Relation depRel;
14659 ScanKeyData key[3];
14660 SysScanDesc scan;
14661 HeapTuple depTup;
14662 ObjectAddress address;
14663
14664 /*
14665 * Clear all the missing values if we're rewriting the table, since this
14666 * renders them pointless.
14667 */
14668 if (tab->rewrite)
14669 {
14670 Relation newrel;
14671
14672 newrel = table_open(RelationGetRelid(rel), NoLock);
14673 RelationClearMissing(newrel);
14674 relation_close(newrel, NoLock);
14675 /* make sure we don't conflict with later attribute modifications */
14677 }
14678
14679 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14680
14681 /* Look up the target column */
14682 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14683 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14684 ereport(ERROR,
14685 (errcode(ERRCODE_UNDEFINED_COLUMN),
14686 errmsg("column \"%s\" of relation \"%s\" does not exist",
14687 colName, RelationGetRelationName(rel))));
14688 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14689 attnum = attTup->attnum;
14690 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14691
14692 /* Check for multiple ALTER TYPE on same column --- can't cope */
14693 if (attTup->atttypid != attOldTup->atttypid ||
14694 attTup->atttypmod != attOldTup->atttypmod)
14695 ereport(ERROR,
14696 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14697 errmsg("cannot alter type of column \"%s\" twice",
14698 colName)));
14699
14700 /* Look up the target type (should not fail, since prep found it) */
14701 typeTuple = typenameType(NULL, typeName, &targettypmod);
14702 tform = (Form_pg_type) GETSTRUCT(typeTuple);
14703 targettype = tform->oid;
14704 /* And the collation */
14705 targetcollid = GetColumnDefCollation(NULL, def, targettype);
14706
14707 /*
14708 * If there is a default expression for the column, get it and ensure we
14709 * can coerce it to the new datatype. (We must do this before changing
14710 * the column type, because build_column_default itself will try to
14711 * coerce, and will not issue the error message we want if it fails.)
14712 *
14713 * We remove any implicit coercion steps at the top level of the old
14714 * default expression; this has been agreed to satisfy the principle of
14715 * least surprise. (The conversion to the new column type should act like
14716 * it started from what the user sees as the stored expression, and the
14717 * implicit coercions aren't going to be shown.)
14718 */
14719 if (attTup->atthasdef)
14720 {
14721 defaultexpr = build_column_default(rel, attnum);
14722 Assert(defaultexpr);
14723 defaultexpr = strip_implicit_coercions(defaultexpr);
14724 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14725 defaultexpr, exprType(defaultexpr),
14726 targettype, targettypmod,
14729 -1);
14730 if (defaultexpr == NULL)
14731 {
14732 if (attTup->attgenerated)
14733 ereport(ERROR,
14734 (errcode(ERRCODE_DATATYPE_MISMATCH),
14735 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14736 colName, format_type_be(targettype))));
14737 else
14738 ereport(ERROR,
14739 (errcode(ERRCODE_DATATYPE_MISMATCH),
14740 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14741 colName, format_type_be(targettype))));
14742 }
14743 }
14744 else
14745 defaultexpr = NULL;
14746
14747 /*
14748 * Find everything that depends on the column (constraints, indexes, etc),
14749 * and record enough information to let us recreate the objects.
14750 *
14751 * The actual recreation does not happen here, but only after we have
14752 * performed all the individual ALTER TYPE operations. We have to save
14753 * the info before executing ALTER TYPE, though, else the deparser will
14754 * get confused.
14755 */
14757
14758 /*
14759 * Now scan for dependencies of this column on other things. The only
14760 * things we should find are the dependency on the column datatype and
14761 * possibly a collation dependency. Those can be removed.
14762 */
14763 depRel = table_open(DependRelationId, RowExclusiveLock);
14764
14765 ScanKeyInit(&key[0],
14766 Anum_pg_depend_classid,
14767 BTEqualStrategyNumber, F_OIDEQ,
14768 ObjectIdGetDatum(RelationRelationId));
14769 ScanKeyInit(&key[1],
14770 Anum_pg_depend_objid,
14771 BTEqualStrategyNumber, F_OIDEQ,
14773 ScanKeyInit(&key[2],
14774 Anum_pg_depend_objsubid,
14775 BTEqualStrategyNumber, F_INT4EQ,
14777
14778 scan = systable_beginscan(depRel, DependDependerIndexId, true,
14779 NULL, 3, key);
14780
14781 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14782 {
14783 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14784 ObjectAddress foundObject;
14785
14786 foundObject.classId = foundDep->refclassid;
14787 foundObject.objectId = foundDep->refobjid;
14788 foundObject.objectSubId = foundDep->refobjsubid;
14789
14790 if (foundDep->deptype != DEPENDENCY_NORMAL)
14791 elog(ERROR, "found unexpected dependency type '%c'",
14792 foundDep->deptype);
14793 if (!(foundDep->refclassid == TypeRelationId &&
14794 foundDep->refobjid == attTup->atttypid) &&
14795 !(foundDep->refclassid == CollationRelationId &&
14796 foundDep->refobjid == attTup->attcollation))
14797 elog(ERROR, "found unexpected dependency for column: %s",
14798 getObjectDescription(&foundObject, false));
14799
14800 CatalogTupleDelete(depRel, &depTup->t_self);
14801 }
14802
14803 systable_endscan(scan);
14804
14806
14807 /*
14808 * Here we go --- change the recorded column type and collation. (Note
14809 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14810 * fix up the missing value if any.
14811 */
14812 if (attTup->atthasmissing)
14813 {
14814 Datum missingval;
14815 bool missingNull;
14816
14817 /* if rewrite is true the missing value should already be cleared */
14818 Assert(tab->rewrite == 0);
14819
14820 /* Get the missing value datum */
14821 missingval = heap_getattr(heapTup,
14822 Anum_pg_attribute_attmissingval,
14823 attrelation->rd_att,
14824 &missingNull);
14825
14826 /* if it's a null array there is nothing to do */
14827
14828 if (!missingNull)
14829 {
14830 /*
14831 * Get the datum out of the array and repack it in a new array
14832 * built with the new type data. We assume that since the table
14833 * doesn't need rewriting, the actual Datum doesn't need to be
14834 * changed, only the array metadata.
14835 */
14836
14837 int one = 1;
14838 bool isNull;
14839 Datum valuesAtt[Natts_pg_attribute] = {0};
14840 bool nullsAtt[Natts_pg_attribute] = {0};
14841 bool replacesAtt[Natts_pg_attribute] = {0};
14842 HeapTuple newTup;
14843
14844 missingval = array_get_element(missingval,
14845 1,
14846 &one,
14847 0,
14848 attTup->attlen,
14849 attTup->attbyval,
14850 attTup->attalign,
14851 &isNull);
14852 missingval = PointerGetDatum(construct_array(&missingval,
14853 1,
14854 targettype,
14855 tform->typlen,
14856 tform->typbyval,
14857 tform->typalign));
14858
14859 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14860 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14861 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14862
14863 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14864 valuesAtt, nullsAtt, replacesAtt);
14865 heap_freetuple(heapTup);
14866 heapTup = newTup;
14867 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14868 }
14869 }
14870
14871 attTup->atttypid = targettype;
14872 attTup->atttypmod = targettypmod;
14873 attTup->attcollation = targetcollid;
14874 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14875 ereport(ERROR,
14876 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14877 errmsg("too many array dimensions"));
14878 attTup->attndims = list_length(typeName->arrayBounds);
14879 attTup->attlen = tform->typlen;
14880 attTup->attbyval = tform->typbyval;
14881 attTup->attalign = tform->typalign;
14882 attTup->attstorage = tform->typstorage;
14883 attTup->attcompression = InvalidCompressionMethod;
14884
14885 ReleaseSysCache(typeTuple);
14886
14887 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14888
14889 table_close(attrelation, RowExclusiveLock);
14890
14891 /* Install dependencies on new datatype and collation */
14894
14895 /*
14896 * Drop any pg_statistic entry for the column, since it's now wrong type
14897 */
14899
14900 InvokeObjectPostAlterHook(RelationRelationId,
14901 RelationGetRelid(rel), attnum);
14902
14903 /*
14904 * Update the default, if present, by brute force --- remove and re-add
14905 * the default. Probably unsafe to take shortcuts, since the new version
14906 * may well have additional dependencies. (It's okay to do this now,
14907 * rather than after other ALTER TYPE commands, since the default won't
14908 * depend on other column types.)
14909 */
14910 if (defaultexpr)
14911 {
14912 /*
14913 * If it's a GENERATED default, drop its dependency records, in
14914 * particular its INTERNAL dependency on the column, which would
14915 * otherwise cause dependency.c to refuse to perform the deletion.
14916 */
14917 if (attTup->attgenerated)
14918 {
14919 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14920
14921 if (!OidIsValid(attrdefoid))
14922 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14923 RelationGetRelid(rel), attnum);
14924 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14925 }
14926
14927 /*
14928 * Make updates-so-far visible, particularly the new pg_attribute row
14929 * which will be updated again.
14930 */
14932
14933 /*
14934 * We use RESTRICT here for safety, but at present we do not expect
14935 * anything to depend on the default.
14936 */
14938 true);
14939
14940 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
14941 }
14942
14943 ObjectAddressSubSet(address, RelationRelationId,
14944 RelationGetRelid(rel), attnum);
14945
14946 /* Cleanup */
14947 heap_freetuple(heapTup);
14948
14949 return address;
14950}
14951
14952/*
14953 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
14954 * that depends on the column (constraints, indexes, etc), and record enough
14955 * information to let us recreate the objects.
14956 */
14957static void
14959 Relation rel, AttrNumber attnum, const char *colName)
14960{
14961 Relation depRel;
14962 ScanKeyData key[3];
14963 SysScanDesc scan;
14964 HeapTuple depTup;
14965
14966 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14967
14968 depRel = table_open(DependRelationId, RowExclusiveLock);
14969
14970 ScanKeyInit(&key[0],
14971 Anum_pg_depend_refclassid,
14972 BTEqualStrategyNumber, F_OIDEQ,
14973 ObjectIdGetDatum(RelationRelationId));
14974 ScanKeyInit(&key[1],
14975 Anum_pg_depend_refobjid,
14976 BTEqualStrategyNumber, F_OIDEQ,
14978 ScanKeyInit(&key[2],
14979 Anum_pg_depend_refobjsubid,
14980 BTEqualStrategyNumber, F_INT4EQ,
14982
14983 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14984 NULL, 3, key);
14985
14986 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14987 {
14988 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14989 ObjectAddress foundObject;
14990
14991 foundObject.classId = foundDep->classid;
14992 foundObject.objectId = foundDep->objid;
14993 foundObject.objectSubId = foundDep->objsubid;
14994
14995 switch (foundObject.classId)
14996 {
14997 case RelationRelationId:
14998 {
14999 char relKind = get_rel_relkind(foundObject.objectId);
15000
15001 if (relKind == RELKIND_INDEX ||
15002 relKind == RELKIND_PARTITIONED_INDEX)
15003 {
15004 Assert(foundObject.objectSubId == 0);
15005 RememberIndexForRebuilding(foundObject.objectId, tab);
15006 }
15007 else if (relKind == RELKIND_SEQUENCE)
15008 {
15009 /*
15010 * This must be a SERIAL column's sequence. We need
15011 * not do anything to it.
15012 */
15013 Assert(foundObject.objectSubId == 0);
15014 }
15015 else
15016 {
15017 /* Not expecting any other direct dependencies... */
15018 elog(ERROR, "unexpected object depending on column: %s",
15019 getObjectDescription(&foundObject, false));
15020 }
15021 break;
15022 }
15023
15024 case ConstraintRelationId:
15025 Assert(foundObject.objectSubId == 0);
15026 RememberConstraintForRebuilding(foundObject.objectId, tab);
15027 break;
15028
15029 case ProcedureRelationId:
15030
15031 /*
15032 * A new-style SQL function can depend on a column, if that
15033 * column is referenced in the parsed function body. Ideally
15034 * we'd automatically update the function by deparsing and
15035 * reparsing it, but that's risky and might well fail anyhow.
15036 * FIXME someday.
15037 *
15038 * This is only a problem for AT_AlterColumnType, not
15039 * AT_SetExpression.
15040 */
15041 if (subtype == AT_AlterColumnType)
15042 ereport(ERROR,
15043 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15044 errmsg("cannot alter type of a column used by a function or procedure"),
15045 errdetail("%s depends on column \"%s\"",
15046 getObjectDescription(&foundObject, false),
15047 colName)));
15048 break;
15049
15050 case RewriteRelationId:
15051
15052 /*
15053 * View/rule bodies have pretty much the same issues as
15054 * function bodies. FIXME someday.
15055 */
15056 if (subtype == AT_AlterColumnType)
15057 ereport(ERROR,
15058 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15059 errmsg("cannot alter type of a column used by a view or rule"),
15060 errdetail("%s depends on column \"%s\"",
15061 getObjectDescription(&foundObject, false),
15062 colName)));
15063 break;
15064
15065 case TriggerRelationId:
15066
15067 /*
15068 * A trigger can depend on a column because the column is
15069 * specified as an update target, or because the column is
15070 * used in the trigger's WHEN condition. The first case would
15071 * not require any extra work, but the second case would
15072 * require updating the WHEN expression, which has the same
15073 * issues as above. Since we can't easily tell which case
15074 * applies, we punt for both. FIXME someday.
15075 */
15076 if (subtype == AT_AlterColumnType)
15077 ereport(ERROR,
15078 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15079 errmsg("cannot alter type of a column used in a trigger definition"),
15080 errdetail("%s depends on column \"%s\"",
15081 getObjectDescription(&foundObject, false),
15082 colName)));
15083 break;
15084
15085 case PolicyRelationId:
15086
15087 /*
15088 * A policy can depend on a column because the column is
15089 * specified in the policy's USING or WITH CHECK qual
15090 * expressions. It might be possible to rewrite and recheck
15091 * the policy expression, but punt for now. It's certainly
15092 * easy enough to remove and recreate the policy; still, FIXME
15093 * someday.
15094 */
15095 if (subtype == AT_AlterColumnType)
15096 ereport(ERROR,
15097 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15098 errmsg("cannot alter type of a column used in a policy definition"),
15099 errdetail("%s depends on column \"%s\"",
15100 getObjectDescription(&foundObject, false),
15101 colName)));
15102 break;
15103
15104 case AttrDefaultRelationId:
15105 {
15107
15108 if (col.objectId == RelationGetRelid(rel) &&
15109 col.objectSubId == attnum)
15110 {
15111 /*
15112 * Ignore the column's own default expression. The
15113 * caller deals with it.
15114 */
15115 }
15116 else
15117 {
15118 /*
15119 * This must be a reference from the expression of a
15120 * generated column elsewhere in the same table.
15121 * Changing the type/generated expression of a column
15122 * that is used by a generated column is not allowed
15123 * by SQL standard, so just punt for now. It might be
15124 * doable with some thinking and effort.
15125 */
15126 if (subtype == AT_AlterColumnType)
15127 ereport(ERROR,
15128 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15129 errmsg("cannot alter type of a column used by a generated column"),
15130 errdetail("Column \"%s\" is used by generated column \"%s\".",
15131 colName,
15133 col.objectSubId,
15134 false))));
15135 }
15136 break;
15137 }
15138
15139 case StatisticExtRelationId:
15140
15141 /*
15142 * Give the extended-stats machinery a chance to fix anything
15143 * that this column type change would break.
15144 */
15145 RememberStatisticsForRebuilding(foundObject.objectId, tab);
15146 break;
15147
15148 case PublicationRelRelationId:
15149
15150 /*
15151 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15152 * clause. Same issues as above. FIXME someday.
15153 */
15154 if (subtype == AT_AlterColumnType)
15155 ereport(ERROR,
15156 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15157 errmsg("cannot alter type of a column used by a publication WHERE clause"),
15158 errdetail("%s depends on column \"%s\"",
15159 getObjectDescription(&foundObject, false),
15160 colName)));
15161 break;
15162
15163 default:
15164
15165 /*
15166 * We don't expect any other sorts of objects to depend on a
15167 * column.
15168 */
15169 elog(ERROR, "unexpected object depending on column: %s",
15170 getObjectDescription(&foundObject, false));
15171 break;
15172 }
15173 }
15174
15175 systable_endscan(scan);
15176 table_close(depRel, NoLock);
15177}
15178
15179/*
15180 * Subroutine for ATExecAlterColumnType: remember that a replica identity
15181 * needs to be reset.
15182 */
15183static void
15185{
15186 if (!get_index_isreplident(indoid))
15187 return;
15188
15189 if (tab->replicaIdentityIndex)
15190 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15191
15192 tab->replicaIdentityIndex = get_rel_name(indoid);
15193}
15194
15195/*
15196 * Subroutine for ATExecAlterColumnType: remember any clustered index.
15197 */
15198static void
15200{
15201 if (!get_index_isclustered(indoid))
15202 return;
15203
15204 if (tab->clusterOnIndex)
15205 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15206
15207 tab->clusterOnIndex = get_rel_name(indoid);
15208}
15209
15210/*
15211 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15212 * to be rebuilt (which we might already know).
15213 */
15214static void
15216{
15217 /*
15218 * This de-duplication check is critical for two independent reasons: we
15219 * mustn't try to recreate the same constraint twice, and if a constraint
15220 * depends on more than one column whose type is to be altered, we must
15221 * capture its definition string before applying any of the column type
15222 * changes. ruleutils.c will get confused if we ask again later.
15223 */
15224 if (!list_member_oid(tab->changedConstraintOids, conoid))
15225 {
15226 /* OK, capture the constraint's existing definition string */
15227 char *defstring = pg_get_constraintdef_command(conoid);
15228 Oid indoid;
15229
15230 /*
15231 * It is critical to create not-null constraints ahead of primary key
15232 * indexes; otherwise, the not-null constraint would be created by the
15233 * primary key, and the constraint name would be wrong.
15234 */
15235 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15236 {
15237 tab->changedConstraintOids = lcons_oid(conoid,
15239 tab->changedConstraintDefs = lcons(defstring,
15241 }
15242 else
15243 {
15244
15246 conoid);
15248 defstring);
15249 }
15250
15251 /*
15252 * For the index of a constraint, if any, remember if it is used for
15253 * the table's replica identity or if it is a clustered index, so that
15254 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15255 * those properties.
15256 */
15257 indoid = get_constraint_index(conoid);
15258 if (OidIsValid(indoid))
15259 {
15261 RememberClusterOnForRebuilding(indoid, tab);
15262 }
15263 }
15264}
15265
15266/*
15267 * Subroutine for ATExecAlterColumnType: remember that an index needs
15268 * to be rebuilt (which we might already know).
15269 */
15270static void
15272{
15273 /*
15274 * This de-duplication check is critical for two independent reasons: we
15275 * mustn't try to recreate the same index twice, and if an index depends
15276 * on more than one column whose type is to be altered, we must capture
15277 * its definition string before applying any of the column type changes.
15278 * ruleutils.c will get confused if we ask again later.
15279 */
15280 if (!list_member_oid(tab->changedIndexOids, indoid))
15281 {
15282 /*
15283 * Before adding it as an index-to-rebuild, we'd better see if it
15284 * belongs to a constraint, and if so rebuild the constraint instead.
15285 * Typically this check fails, because constraint indexes normally
15286 * have only dependencies on their constraint. But it's possible for
15287 * such an index to also have direct dependencies on table columns,
15288 * for example with a partial exclusion constraint.
15289 */
15290 Oid conoid = get_index_constraint(indoid);
15291
15292 if (OidIsValid(conoid))
15293 {
15295 }
15296 else
15297 {
15298 /* OK, capture the index's existing definition string */
15299 char *defstring = pg_get_indexdef_string(indoid);
15300
15302 indoid);
15304 defstring);
15305
15306 /*
15307 * Remember if this index is used for the table's replica identity
15308 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15309 * can queue up commands necessary to restore those properties.
15310 */
15312 RememberClusterOnForRebuilding(indoid, tab);
15313 }
15314 }
15315}
15316
15317/*
15318 * Subroutine for ATExecAlterColumnType: remember that a statistics object
15319 * needs to be rebuilt (which we might already know).
15320 */
15321static void
15323{
15324 /*
15325 * This de-duplication check is critical for two independent reasons: we
15326 * mustn't try to recreate the same statistics object twice, and if the
15327 * statistics object depends on more than one column whose type is to be
15328 * altered, we must capture its definition string before applying any of
15329 * the type changes. ruleutils.c will get confused if we ask again later.
15330 */
15331 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15332 {
15333 /* OK, capture the statistics object's existing definition string */
15334 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15335
15337 stxoid);
15339 defstring);
15340 }
15341}
15342
15343/*
15344 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15345 * operations for a particular relation. We have to drop and recreate all the
15346 * indexes and constraints that depend on the altered columns. We do the
15347 * actual dropping here, but re-creation is managed by adding work queue
15348 * entries to do those steps later.
15349 */
15350static void
15352{
15353 ObjectAddress obj;
15354 ObjectAddresses *objects;
15355 ListCell *def_item;
15356 ListCell *oid_item;
15357
15358 /*
15359 * Collect all the constraints and indexes to drop so we can process them
15360 * in a single call. That way we don't have to worry about dependencies
15361 * among them.
15362 */
15363 objects = new_object_addresses();
15364
15365 /*
15366 * Re-parse the index and constraint definitions, and attach them to the
15367 * appropriate work queue entries. We do this before dropping because in
15368 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
15369 * lock on the table the constraint is attached to, and we need to get
15370 * that before reparsing/dropping.
15371 *
15372 * We can't rely on the output of deparsing to tell us which relation to
15373 * operate on, because concurrent activity might have made the name
15374 * resolve differently. Instead, we've got to use the OID of the
15375 * constraint or index we're processing to figure out which relation to
15376 * operate on.
15377 */
15378 forboth(oid_item, tab->changedConstraintOids,
15379 def_item, tab->changedConstraintDefs)
15380 {
15381 Oid oldId = lfirst_oid(oid_item);
15382 HeapTuple tup;
15384 Oid relid;
15385 Oid confrelid;
15386 char contype;
15387 bool conislocal;
15388
15389 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15390 if (!HeapTupleIsValid(tup)) /* should not happen */
15391 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15392 con = (Form_pg_constraint) GETSTRUCT(tup);
15393 if (OidIsValid(con->conrelid))
15394 relid = con->conrelid;
15395 else
15396 {
15397 /* must be a domain constraint */
15398 relid = get_typ_typrelid(getBaseType(con->contypid));
15399 if (!OidIsValid(relid))
15400 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15401 }
15402 confrelid = con->confrelid;
15403 contype = con->contype;
15404 conislocal = con->conislocal;
15405 ReleaseSysCache(tup);
15406
15407 ObjectAddressSet(obj, ConstraintRelationId, oldId);
15408 add_exact_object_address(&obj, objects);
15409
15410 /*
15411 * If the constraint is inherited (only), we don't want to inject a
15412 * new definition here; it'll get recreated when
15413 * ATAddCheckNNConstraint recurses from adding the parent table's
15414 * constraint. But we had to carry the info this far so that we can
15415 * drop the constraint below.
15416 */
15417 if (!conislocal)
15418 continue;
15419
15420 /*
15421 * When rebuilding an FK constraint that references the table we're
15422 * modifying, we might not yet have any lock on the FK's table, so get
15423 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
15424 * step, so there's no value in asking for anything weaker.
15425 */
15426 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
15428
15429 ATPostAlterTypeParse(oldId, relid, confrelid,
15430 (char *) lfirst(def_item),
15431 wqueue, lockmode, tab->rewrite);
15432 }
15433 forboth(oid_item, tab->changedIndexOids,
15434 def_item, tab->changedIndexDefs)
15435 {
15436 Oid oldId = lfirst_oid(oid_item);
15437 Oid relid;
15438
15439 relid = IndexGetRelation(oldId, false);
15440 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15441 (char *) lfirst(def_item),
15442 wqueue, lockmode, tab->rewrite);
15443
15444 ObjectAddressSet(obj, RelationRelationId, oldId);
15445 add_exact_object_address(&obj, objects);
15446 }
15447
15448 /* add dependencies for new statistics */
15449 forboth(oid_item, tab->changedStatisticsOids,
15450 def_item, tab->changedStatisticsDefs)
15451 {
15452 Oid oldId = lfirst_oid(oid_item);
15453 Oid relid;
15454
15455 relid = StatisticsGetRelation(oldId, false);
15456 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15457 (char *) lfirst(def_item),
15458 wqueue, lockmode, tab->rewrite);
15459
15460 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15461 add_exact_object_address(&obj, objects);
15462 }
15463
15464 /*
15465 * Queue up command to restore replica identity index marking
15466 */
15467 if (tab->replicaIdentityIndex)
15468 {
15471
15472 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15473 subcmd->name = tab->replicaIdentityIndex;
15475 cmd->def = (Node *) subcmd;
15476
15477 /* do it after indexes and constraints */
15478 tab->subcmds[AT_PASS_OLD_CONSTR] =
15479 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15480 }
15481
15482 /*
15483 * Queue up command to restore marking of index used for cluster.
15484 */
15485 if (tab->clusterOnIndex)
15486 {
15488
15489 cmd->subtype = AT_ClusterOn;
15490 cmd->name = tab->clusterOnIndex;
15491
15492 /* do it after indexes and constraints */
15493 tab->subcmds[AT_PASS_OLD_CONSTR] =
15494 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15495 }
15496
15497 /*
15498 * It should be okay to use DROP_RESTRICT here, since nothing else should
15499 * be depending on these objects.
15500 */
15502
15503 free_object_addresses(objects);
15504
15505 /*
15506 * The objects will get recreated during subsequent passes over the work
15507 * queue.
15508 */
15509}
15510
15511/*
15512 * Parse the previously-saved definition string for a constraint, index or
15513 * statistics object against the newly-established column data type(s), and
15514 * queue up the resulting command parsetrees for execution.
15515 *
15516 * This might fail if, for example, you have a WHERE clause that uses an
15517 * operator that's not available for the new column type.
15518 */
15519static void
15520ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15521 List **wqueue, LOCKMODE lockmode, bool rewrite)
15522{
15523 List *raw_parsetree_list;
15524 List *querytree_list;
15525 ListCell *list_item;
15526 Relation rel;
15527
15528 /*
15529 * We expect that we will get only ALTER TABLE and CREATE INDEX
15530 * statements. Hence, there is no need to pass them through
15531 * parse_analyze_*() or the rewriter, but instead we need to pass them
15532 * through parse_utilcmd.c to make them ready for execution.
15533 */
15534 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15535 querytree_list = NIL;
15536 foreach(list_item, raw_parsetree_list)
15537 {
15538 RawStmt *rs = lfirst_node(RawStmt, list_item);
15539 Node *stmt = rs->stmt;
15540
15541 if (IsA(stmt, IndexStmt))
15542 querytree_list = lappend(querytree_list,
15543 transformIndexStmt(oldRelId,
15544 (IndexStmt *) stmt,
15545 cmd));
15546 else if (IsA(stmt, AlterTableStmt))
15547 {
15548 List *beforeStmts;
15549 List *afterStmts;
15550
15551 stmt = (Node *) transformAlterTableStmt(oldRelId,
15552 (AlterTableStmt *) stmt,
15553 cmd,
15554 &beforeStmts,
15555 &afterStmts);
15556 querytree_list = list_concat(querytree_list, beforeStmts);
15557 querytree_list = lappend(querytree_list, stmt);
15558 querytree_list = list_concat(querytree_list, afterStmts);
15559 }
15560 else if (IsA(stmt, CreateStatsStmt))
15561 querytree_list = lappend(querytree_list,
15562 transformStatsStmt(oldRelId,
15564 cmd));
15565 else
15566 querytree_list = lappend(querytree_list, stmt);
15567 }
15568
15569 /* Caller should already have acquired whatever lock we need. */
15570 rel = relation_open(oldRelId, NoLock);
15571
15572 /*
15573 * Attach each generated command to the proper place in the work queue.
15574 * Note this could result in creation of entirely new work-queue entries.
15575 *
15576 * Also note that we have to tweak the command subtypes, because it turns
15577 * out that re-creation of indexes and constraints has to act a bit
15578 * differently from initial creation.
15579 */
15580 foreach(list_item, querytree_list)
15581 {
15582 Node *stm = (Node *) lfirst(list_item);
15583 AlteredTableInfo *tab;
15584
15585 tab = ATGetQueueEntry(wqueue, rel);
15586
15587 if (IsA(stm, IndexStmt))
15588 {
15589 IndexStmt *stmt = (IndexStmt *) stm;
15590 AlterTableCmd *newcmd;
15591
15592 if (!rewrite)
15593 TryReuseIndex(oldId, stmt);
15594 stmt->reset_default_tblspc = true;
15595 /* keep the index's comment */
15596 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15597
15598 newcmd = makeNode(AlterTableCmd);
15599 newcmd->subtype = AT_ReAddIndex;
15600 newcmd->def = (Node *) stmt;
15602 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15603 }
15604 else if (IsA(stm, AlterTableStmt))
15605 {
15607 ListCell *lcmd;
15608
15609 foreach(lcmd, stmt->cmds)
15610 {
15612
15613 if (cmd->subtype == AT_AddIndex)
15614 {
15615 IndexStmt *indstmt;
15616 Oid indoid;
15617
15618 indstmt = castNode(IndexStmt, cmd->def);
15619 indoid = get_constraint_index(oldId);
15620
15621 if (!rewrite)
15622 TryReuseIndex(indoid, indstmt);
15623 /* keep any comment on the index */
15624 indstmt->idxcomment = GetComment(indoid,
15625 RelationRelationId, 0);
15626 indstmt->reset_default_tblspc = true;
15627
15628 cmd->subtype = AT_ReAddIndex;
15630 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15631
15632 /* recreate any comment on the constraint */
15635 oldId,
15636 rel,
15637 NIL,
15638 indstmt->idxname);
15639 }
15640 else if (cmd->subtype == AT_AddConstraint)
15641 {
15642 Constraint *con = castNode(Constraint, cmd->def);
15643
15644 con->old_pktable_oid = refRelId;
15645 /* rewriting neither side of a FK */
15646 if (con->contype == CONSTR_FOREIGN &&
15647 !rewrite && tab->rewrite == 0)
15648 TryReuseForeignKey(oldId, con);
15649 con->reset_default_tblspc = true;
15653
15654 /*
15655 * Recreate any comment on the constraint. If we have
15656 * recreated a primary key, then transformTableConstraint
15657 * has added an unnamed not-null constraint here; skip
15658 * this in that case.
15659 */
15660 if (con->conname)
15663 oldId,
15664 rel,
15665 NIL,
15666 con->conname);
15667 else
15668 Assert(con->contype == CONSTR_NOTNULL);
15669 }
15670 else
15671 elog(ERROR, "unexpected statement subtype: %d",
15672 (int) cmd->subtype);
15673 }
15674 }
15675 else if (IsA(stm, AlterDomainStmt))
15676 {
15678
15679 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
15680 {
15681 Constraint *con = castNode(Constraint, stmt->def);
15683
15685 cmd->def = (Node *) stmt;
15688
15689 /* recreate any comment on the constraint */
15692 oldId,
15693 NULL,
15694 stmt->typeName,
15695 con->conname);
15696 }
15697 else
15698 elog(ERROR, "unexpected statement subtype: %d",
15699 (int) stmt->subtype);
15700 }
15701 else if (IsA(stm, CreateStatsStmt))
15702 {
15704 AlterTableCmd *newcmd;
15705
15706 /* keep the statistics object's comment */
15707 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15708
15709 newcmd = makeNode(AlterTableCmd);
15710 newcmd->subtype = AT_ReAddStatistics;
15711 newcmd->def = (Node *) stmt;
15712 tab->subcmds[AT_PASS_MISC] =
15713 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15714 }
15715 else
15716 elog(ERROR, "unexpected statement type: %d",
15717 (int) nodeTag(stm));
15718 }
15719
15720 relation_close(rel, NoLock);
15721}
15722
15723/*
15724 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15725 * for a table or domain constraint that is being rebuilt.
15726 *
15727 * objid is the OID of the constraint.
15728 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15729 * as a string list) for a domain constraint.
15730 * (We could dig that info, as well as the conname, out of the pg_constraint
15731 * entry; but callers already have them so might as well pass them.)
15732 */
15733static void
15735 Relation rel, List *domname,
15736 const char *conname)
15737{
15738 CommentStmt *cmd;
15739 char *comment_str;
15740 AlterTableCmd *newcmd;
15741
15742 /* Look for comment for object wanted, and leave if none */
15743 comment_str = GetComment(objid, ConstraintRelationId, 0);
15744 if (comment_str == NULL)
15745 return;
15746
15747 /* Build CommentStmt node, copying all input data for safety */
15748 cmd = makeNode(CommentStmt);
15749 if (rel)
15750 {
15752 cmd->object = (Node *)
15755 makeString(pstrdup(conname)));
15756 }
15757 else
15758 {
15760 cmd->object = (Node *)
15762 makeString(pstrdup(conname)));
15763 }
15764 cmd->comment = comment_str;
15765
15766 /* Append it to list of commands */
15767 newcmd = makeNode(AlterTableCmd);
15768 newcmd->subtype = AT_ReAddComment;
15769 newcmd->def = (Node *) cmd;
15770 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15771}
15772
15773/*
15774 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15775 * for the real analysis, then mutates the IndexStmt based on that verdict.
15776 */
15777static void
15779{
15780 if (CheckIndexCompatible(oldId,
15781 stmt->accessMethod,
15782 stmt->indexParams,
15783 stmt->excludeOpNames,
15784 stmt->iswithoutoverlaps))
15785 {
15786 Relation irel = index_open(oldId, NoLock);
15787
15788 /* If it's a partitioned index, there is no storage to share. */
15789 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15790 {
15791 stmt->oldNumber = irel->rd_locator.relNumber;
15792 stmt->oldCreateSubid = irel->rd_createSubid;
15793 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15794 }
15795 index_close(irel, NoLock);
15796 }
15797}
15798
15799/*
15800 * Subroutine for ATPostAlterTypeParse().
15801 *
15802 * Stash the old P-F equality operator into the Constraint node, for possible
15803 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15804 * this constraint can be skipped.
15805 */
15806static void
15808{
15809 HeapTuple tup;
15810 Datum adatum;
15811 ArrayType *arr;
15812 Oid *rawarr;
15813 int numkeys;
15814 int i;
15815
15816 Assert(con->contype == CONSTR_FOREIGN);
15817 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15818
15819 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15820 if (!HeapTupleIsValid(tup)) /* should not happen */
15821 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15822
15823 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15824 Anum_pg_constraint_conpfeqop);
15825 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15826 numkeys = ARR_DIMS(arr)[0];
15827 /* test follows the one in ri_FetchConstraintInfo() */
15828 if (ARR_NDIM(arr) != 1 ||
15829 ARR_HASNULL(arr) ||
15830 ARR_ELEMTYPE(arr) != OIDOID)
15831 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15832 rawarr = (Oid *) ARR_DATA_PTR(arr);
15833
15834 /* stash a List of the operator Oids in our Constraint node */
15835 for (i = 0; i < numkeys; i++)
15836 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15837
15838 ReleaseSysCache(tup);
15839}
15840
15841/*
15842 * ALTER COLUMN .. OPTIONS ( ... )
15843 *
15844 * Returns the address of the modified column
15845 */
15846static ObjectAddress
15848 const char *colName,
15849 List *options,
15850 LOCKMODE lockmode)
15851{
15852 Relation ftrel;
15853 Relation attrel;
15854 ForeignServer *server;
15855 ForeignDataWrapper *fdw;
15856 HeapTuple tuple;
15857 HeapTuple newtuple;
15858 bool isnull;
15859 Datum repl_val[Natts_pg_attribute];
15860 bool repl_null[Natts_pg_attribute];
15861 bool repl_repl[Natts_pg_attribute];
15862 Datum datum;
15863 Form_pg_foreign_table fttableform;
15864 Form_pg_attribute atttableform;
15866 ObjectAddress address;
15867
15868 if (options == NIL)
15869 return InvalidObjectAddress;
15870
15871 /* First, determine FDW validator associated to the foreign table. */
15872 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15873 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15874 if (!HeapTupleIsValid(tuple))
15875 ereport(ERROR,
15876 (errcode(ERRCODE_UNDEFINED_OBJECT),
15877 errmsg("foreign table \"%s\" does not exist",
15879 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15880 server = GetForeignServer(fttableform->ftserver);
15881 fdw = GetForeignDataWrapper(server->fdwid);
15882
15884 ReleaseSysCache(tuple);
15885
15886 attrel = table_open(AttributeRelationId, RowExclusiveLock);
15887 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15888 if (!HeapTupleIsValid(tuple))
15889 ereport(ERROR,
15890 (errcode(ERRCODE_UNDEFINED_COLUMN),
15891 errmsg("column \"%s\" of relation \"%s\" does not exist",
15892 colName, RelationGetRelationName(rel))));
15893
15894 /* Prevent them from altering a system attribute */
15895 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15896 attnum = atttableform->attnum;
15897 if (attnum <= 0)
15898 ereport(ERROR,
15899 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15900 errmsg("cannot alter system column \"%s\"", colName)));
15901
15902
15903 /* Initialize buffers for new tuple values */
15904 memset(repl_val, 0, sizeof(repl_val));
15905 memset(repl_null, false, sizeof(repl_null));
15906 memset(repl_repl, false, sizeof(repl_repl));
15907
15908 /* Extract the current options */
15909 datum = SysCacheGetAttr(ATTNAME,
15910 tuple,
15911 Anum_pg_attribute_attfdwoptions,
15912 &isnull);
15913 if (isnull)
15914 datum = PointerGetDatum(NULL);
15915
15916 /* Transform the options */
15917 datum = transformGenericOptions(AttributeRelationId,
15918 datum,
15919 options,
15920 fdw->fdwvalidator);
15921
15922 if (PointerIsValid(DatumGetPointer(datum)))
15923 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15924 else
15925 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15926
15927 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15928
15929 /* Everything looks good - update the tuple */
15930
15931 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15932 repl_val, repl_null, repl_repl);
15933
15934 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15935
15936 InvokeObjectPostAlterHook(RelationRelationId,
15937 RelationGetRelid(rel),
15938 atttableform->attnum);
15939 ObjectAddressSubSet(address, RelationRelationId,
15940 RelationGetRelid(rel), attnum);
15941
15942 ReleaseSysCache(tuple);
15943
15945
15946 heap_freetuple(newtuple);
15947
15948 return address;
15949}
15950
15951/*
15952 * ALTER TABLE OWNER
15953 *
15954 * recursing is true if we are recursing from a table to its indexes,
15955 * sequences, or toast table. We don't allow the ownership of those things to
15956 * be changed separately from the parent table. Also, we can skip permission
15957 * checks (this is necessary not just an optimization, else we'd fail to
15958 * handle toast tables properly).
15959 *
15960 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
15961 * free-standing composite type.
15962 */
15963void
15964ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
15965{
15966 Relation target_rel;
15967 Relation class_rel;
15968 HeapTuple tuple;
15969 Form_pg_class tuple_class;
15970
15971 /*
15972 * Get exclusive lock till end of transaction on the target table. Use
15973 * relation_open so that we can work on indexes and sequences.
15974 */
15975 target_rel = relation_open(relationOid, lockmode);
15976
15977 /* Get its pg_class tuple, too */
15978 class_rel = table_open(RelationRelationId, RowExclusiveLock);
15979
15980 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
15981 if (!HeapTupleIsValid(tuple))
15982 elog(ERROR, "cache lookup failed for relation %u", relationOid);
15983 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
15984
15985 /* Can we change the ownership of this tuple? */
15986 switch (tuple_class->relkind)
15987 {
15988 case RELKIND_RELATION:
15989 case RELKIND_VIEW:
15990 case RELKIND_MATVIEW:
15991 case RELKIND_FOREIGN_TABLE:
15992 case RELKIND_PARTITIONED_TABLE:
15993 /* ok to change owner */
15994 break;
15995 case RELKIND_INDEX:
15996 if (!recursing)
15997 {
15998 /*
15999 * Because ALTER INDEX OWNER used to be allowed, and in fact
16000 * is generated by old versions of pg_dump, we give a warning
16001 * and do nothing rather than erroring out. Also, to avoid
16002 * unnecessary chatter while restoring those old dumps, say
16003 * nothing at all if the command would be a no-op anyway.
16004 */
16005 if (tuple_class->relowner != newOwnerId)
16007 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16008 errmsg("cannot change owner of index \"%s\"",
16009 NameStr(tuple_class->relname)),
16010 errhint("Change the ownership of the index's table instead.")));
16011 /* quick hack to exit via the no-op path */
16012 newOwnerId = tuple_class->relowner;
16013 }
16014 break;
16015 case RELKIND_PARTITIONED_INDEX:
16016 if (recursing)
16017 break;
16018 ereport(ERROR,
16019 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16020 errmsg("cannot change owner of index \"%s\"",
16021 NameStr(tuple_class->relname)),
16022 errhint("Change the ownership of the index's table instead.")));
16023 break;
16024 case RELKIND_SEQUENCE:
16025 if (!recursing &&
16026 tuple_class->relowner != newOwnerId)
16027 {
16028 /* if it's an owned sequence, disallow changing it by itself */
16029 Oid tableId;
16030 int32 colId;
16031
16032 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16033 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16034 ereport(ERROR,
16035 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16036 errmsg("cannot change owner of sequence \"%s\"",
16037 NameStr(tuple_class->relname)),
16038 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16039 NameStr(tuple_class->relname),
16040 get_rel_name(tableId))));
16041 }
16042 break;
16043 case RELKIND_COMPOSITE_TYPE:
16044 if (recursing)
16045 break;
16046 ereport(ERROR,
16047 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16048 errmsg("\"%s\" is a composite type",
16049 NameStr(tuple_class->relname)),
16050 /* translator: %s is an SQL ALTER command */
16051 errhint("Use %s instead.",
16052 "ALTER TYPE")));
16053 break;
16054 case RELKIND_TOASTVALUE:
16055 if (recursing)
16056 break;
16057 /* FALL THRU */
16058 default:
16059 ereport(ERROR,
16060 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16061 errmsg("cannot change owner of relation \"%s\"",
16062 NameStr(tuple_class->relname)),
16063 errdetail_relkind_not_supported(tuple_class->relkind)));
16064 }
16065
16066 /*
16067 * If the new owner is the same as the existing owner, consider the
16068 * command to have succeeded. This is for dump restoration purposes.
16069 */
16070 if (tuple_class->relowner != newOwnerId)
16071 {
16072 Datum repl_val[Natts_pg_class];
16073 bool repl_null[Natts_pg_class];
16074 bool repl_repl[Natts_pg_class];
16075 Acl *newAcl;
16076 Datum aclDatum;
16077 bool isNull;
16078 HeapTuple newtuple;
16079
16080 /* skip permission checks when recursing to index or toast table */
16081 if (!recursing)
16082 {
16083 /* Superusers can always do it */
16084 if (!superuser())
16085 {
16086 Oid namespaceOid = tuple_class->relnamespace;
16087 AclResult aclresult;
16088
16089 /* Otherwise, must be owner of the existing object */
16090 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16092 RelationGetRelationName(target_rel));
16093
16094 /* Must be able to become new owner */
16095 check_can_set_role(GetUserId(), newOwnerId);
16096
16097 /* New owner must have CREATE privilege on namespace */
16098 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16099 ACL_CREATE);
16100 if (aclresult != ACLCHECK_OK)
16101 aclcheck_error(aclresult, OBJECT_SCHEMA,
16102 get_namespace_name(namespaceOid));
16103 }
16104 }
16105
16106 memset(repl_null, false, sizeof(repl_null));
16107 memset(repl_repl, false, sizeof(repl_repl));
16108
16109 repl_repl[Anum_pg_class_relowner - 1] = true;
16110 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16111
16112 /*
16113 * Determine the modified ACL for the new owner. This is only
16114 * necessary when the ACL is non-null.
16115 */
16116 aclDatum = SysCacheGetAttr(RELOID, tuple,
16117 Anum_pg_class_relacl,
16118 &isNull);
16119 if (!isNull)
16120 {
16121 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16122 tuple_class->relowner, newOwnerId);
16123 repl_repl[Anum_pg_class_relacl - 1] = true;
16124 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16125 }
16126
16127 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16128
16129 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16130
16131 heap_freetuple(newtuple);
16132
16133 /*
16134 * We must similarly update any per-column ACLs to reflect the new
16135 * owner; for neatness reasons that's split out as a subroutine.
16136 */
16137 change_owner_fix_column_acls(relationOid,
16138 tuple_class->relowner,
16139 newOwnerId);
16140
16141 /*
16142 * Update owner dependency reference, if any. A composite type has
16143 * none, because it's tracked for the pg_type entry instead of here;
16144 * indexes and TOAST tables don't have their own entries either.
16145 */
16146 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16147 tuple_class->relkind != RELKIND_INDEX &&
16148 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16149 tuple_class->relkind != RELKIND_TOASTVALUE)
16150 changeDependencyOnOwner(RelationRelationId, relationOid,
16151 newOwnerId);
16152
16153 /*
16154 * Also change the ownership of the table's row type, if it has one
16155 */
16156 if (OidIsValid(tuple_class->reltype))
16157 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16158
16159 /*
16160 * If we are operating on a table or materialized view, also change
16161 * the ownership of any indexes and sequences that belong to the
16162 * relation, as well as its toast table (if it has one).
16163 */
16164 if (tuple_class->relkind == RELKIND_RELATION ||
16165 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16166 tuple_class->relkind == RELKIND_MATVIEW ||
16167 tuple_class->relkind == RELKIND_TOASTVALUE)
16168 {
16169 List *index_oid_list;
16170 ListCell *i;
16171
16172 /* Find all the indexes belonging to this relation */
16173 index_oid_list = RelationGetIndexList(target_rel);
16174
16175 /* For each index, recursively change its ownership */
16176 foreach(i, index_oid_list)
16177 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16178
16179 list_free(index_oid_list);
16180 }
16181
16182 /* If it has a toast table, recurse to change its ownership */
16183 if (tuple_class->reltoastrelid != InvalidOid)
16184 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16185 true, lockmode);
16186
16187 /* If it has dependent sequences, recurse to change them too */
16188 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16189 }
16190
16191 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16192
16193 ReleaseSysCache(tuple);
16194 table_close(class_rel, RowExclusiveLock);
16195 relation_close(target_rel, NoLock);
16196}
16197
16198/*
16199 * change_owner_fix_column_acls
16200 *
16201 * Helper function for ATExecChangeOwner. Scan the columns of the table
16202 * and fix any non-null column ACLs to reflect the new owner.
16203 */
16204static void
16205change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16206{
16207 Relation attRelation;
16208 SysScanDesc scan;
16209 ScanKeyData key[1];
16210 HeapTuple attributeTuple;
16211
16212 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16213 ScanKeyInit(&key[0],
16214 Anum_pg_attribute_attrelid,
16215 BTEqualStrategyNumber, F_OIDEQ,
16216 ObjectIdGetDatum(relationOid));
16217 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16218 true, NULL, 1, key);
16219 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16220 {
16221 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16222 Datum repl_val[Natts_pg_attribute];
16223 bool repl_null[Natts_pg_attribute];
16224 bool repl_repl[Natts_pg_attribute];
16225 Acl *newAcl;
16226 Datum aclDatum;
16227 bool isNull;
16228 HeapTuple newtuple;
16229
16230 /* Ignore dropped columns */
16231 if (att->attisdropped)
16232 continue;
16233
16234 aclDatum = heap_getattr(attributeTuple,
16235 Anum_pg_attribute_attacl,
16236 RelationGetDescr(attRelation),
16237 &isNull);
16238 /* Null ACLs do not require changes */
16239 if (isNull)
16240 continue;
16241
16242 memset(repl_null, false, sizeof(repl_null));
16243 memset(repl_repl, false, sizeof(repl_repl));
16244
16245 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16246 oldOwnerId, newOwnerId);
16247 repl_repl[Anum_pg_attribute_attacl - 1] = true;
16248 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16249
16250 newtuple = heap_modify_tuple(attributeTuple,
16251 RelationGetDescr(attRelation),
16252 repl_val, repl_null, repl_repl);
16253
16254 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16255
16256 heap_freetuple(newtuple);
16257 }
16258 systable_endscan(scan);
16259 table_close(attRelation, RowExclusiveLock);
16260}
16261
16262/*
16263 * change_owner_recurse_to_sequences
16264 *
16265 * Helper function for ATExecChangeOwner. Examines pg_depend searching
16266 * for sequences that are dependent on serial columns, and changes their
16267 * ownership.
16268 */
16269static void
16270change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16271{
16272 Relation depRel;
16273 SysScanDesc scan;
16274 ScanKeyData key[2];
16275 HeapTuple tup;
16276
16277 /*
16278 * SERIAL sequences are those having an auto dependency on one of the
16279 * table's columns (we don't care *which* column, exactly).
16280 */
16281 depRel = table_open(DependRelationId, AccessShareLock);
16282
16283 ScanKeyInit(&key[0],
16284 Anum_pg_depend_refclassid,
16285 BTEqualStrategyNumber, F_OIDEQ,
16286 ObjectIdGetDatum(RelationRelationId));
16287 ScanKeyInit(&key[1],
16288 Anum_pg_depend_refobjid,
16289 BTEqualStrategyNumber, F_OIDEQ,
16290 ObjectIdGetDatum(relationOid));
16291 /* we leave refobjsubid unspecified */
16292
16293 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16294 NULL, 2, key);
16295
16296 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16297 {
16298 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16299 Relation seqRel;
16300
16301 /* skip dependencies other than auto dependencies on columns */
16302 if (depForm->refobjsubid == 0 ||
16303 depForm->classid != RelationRelationId ||
16304 depForm->objsubid != 0 ||
16305 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16306 continue;
16307
16308 /* Use relation_open just in case it's an index */
16309 seqRel = relation_open(depForm->objid, lockmode);
16310
16311 /* skip non-sequence relations */
16312 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16313 {
16314 /* No need to keep the lock */
16315 relation_close(seqRel, lockmode);
16316 continue;
16317 }
16318
16319 /* We don't need to close the sequence while we alter it. */
16320 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16321
16322 /* Now we can close it. Keep the lock till end of transaction. */
16323 relation_close(seqRel, NoLock);
16324 }
16325
16326 systable_endscan(scan);
16327
16329}
16330
16331/*
16332 * ALTER TABLE CLUSTER ON
16333 *
16334 * The only thing we have to do is to change the indisclustered bits.
16335 *
16336 * Return the address of the new clustering index.
16337 */
16338static ObjectAddress
16339ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16340{
16341 Oid indexOid;
16342 ObjectAddress address;
16343
16344 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16345
16346 if (!OidIsValid(indexOid))
16347 ereport(ERROR,
16348 (errcode(ERRCODE_UNDEFINED_OBJECT),
16349 errmsg("index \"%s\" for table \"%s\" does not exist",
16350 indexName, RelationGetRelationName(rel))));
16351
16352 /* Check index is valid to cluster on */
16353 check_index_is_clusterable(rel, indexOid, lockmode);
16354
16355 /* And do the work */
16356 mark_index_clustered(rel, indexOid, false);
16357
16358 ObjectAddressSet(address,
16359 RelationRelationId, indexOid);
16360
16361 return address;
16362}
16363
16364/*
16365 * ALTER TABLE SET WITHOUT CLUSTER
16366 *
16367 * We have to find any indexes on the table that have indisclustered bit
16368 * set and turn it off.
16369 */
16370static void
16372{
16373 mark_index_clustered(rel, InvalidOid, false);
16374}
16375
16376/*
16377 * Preparation phase for SET ACCESS METHOD
16378 *
16379 * Check that the access method exists and determine whether a change is
16380 * actually needed.
16381 */
16382static void
16384{
16385 Oid amoid;
16386
16387 /*
16388 * Look up the access method name and check that it differs from the
16389 * table's current AM. If DEFAULT was specified for a partitioned table
16390 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16391 */
16392 if (amname != NULL)
16393 amoid = get_table_am_oid(amname, false);
16394 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16395 amoid = InvalidOid;
16396 else
16398
16399 /* if it's a match, phase 3 doesn't need to do anything */
16400 if (rel->rd_rel->relam == amoid)
16401 return;
16402
16403 /* Save info for Phase 3 to do the real work */
16405 tab->newAccessMethod = amoid;
16406 tab->chgAccessMethod = true;
16407}
16408
16409/*
16410 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16411 * storage that have an interest in preserving AM.
16412 *
16413 * Since these have no storage, setting the access method is a catalog only
16414 * operation.
16415 */
16416static void
16418{
16419 Relation pg_class;
16420 Oid oldAccessMethodId;
16421 HeapTuple tuple;
16422 Form_pg_class rd_rel;
16423 Oid reloid = RelationGetRelid(rel);
16424
16425 /*
16426 * Shouldn't be called on relations having storage; these are processed in
16427 * phase 3.
16428 */
16429 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16430
16431 /* Get a modifiable copy of the relation's pg_class row. */
16432 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16433
16434 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16435 if (!HeapTupleIsValid(tuple))
16436 elog(ERROR, "cache lookup failed for relation %u", reloid);
16437 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16438
16439 /* Update the pg_class row. */
16440 oldAccessMethodId = rd_rel->relam;
16441 rd_rel->relam = newAccessMethodId;
16442
16443 /* Leave if no update required */
16444 if (rd_rel->relam == oldAccessMethodId)
16445 {
16446 heap_freetuple(tuple);
16447 table_close(pg_class, RowExclusiveLock);
16448 return;
16449 }
16450
16451 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16452
16453 /*
16454 * Update the dependency on the new access method. No dependency is added
16455 * if the new access method is InvalidOid (default case). Be very careful
16456 * that this has to compare the previous value stored in pg_class with the
16457 * new one.
16458 */
16459 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16460 {
16461 ObjectAddress relobj,
16462 referenced;
16463
16464 /*
16465 * New access method is defined and there was no dependency
16466 * previously, so record a new one.
16467 */
16468 ObjectAddressSet(relobj, RelationRelationId, reloid);
16469 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16470 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16471 }
16472 else if (OidIsValid(oldAccessMethodId) &&
16473 !OidIsValid(rd_rel->relam))
16474 {
16475 /*
16476 * There was an access method defined, and no new one, so just remove
16477 * the existing dependency.
16478 */
16479 deleteDependencyRecordsForClass(RelationRelationId, reloid,
16480 AccessMethodRelationId,
16482 }
16483 else
16484 {
16485 Assert(OidIsValid(oldAccessMethodId) &&
16486 OidIsValid(rd_rel->relam));
16487
16488 /* Both are valid, so update the dependency */
16489 changeDependencyFor(RelationRelationId, reloid,
16490 AccessMethodRelationId,
16491 oldAccessMethodId, rd_rel->relam);
16492 }
16493
16494 /* make the relam and dependency changes visible */
16496
16497 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16498
16499 heap_freetuple(tuple);
16500 table_close(pg_class, RowExclusiveLock);
16501}
16502
16503/*
16504 * ALTER TABLE SET TABLESPACE
16505 */
16506static void
16507ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16508{
16509 Oid tablespaceId;
16510
16511 /* Check that the tablespace exists */
16512 tablespaceId = get_tablespace_oid(tablespacename, false);
16513
16514 /* Check permissions except when moving to database's default */
16515 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16516 {
16517 AclResult aclresult;
16518
16519 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16520 if (aclresult != ACLCHECK_OK)
16521 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16522 }
16523
16524 /* Save info for Phase 3 to do the real work */
16525 if (OidIsValid(tab->newTableSpace))
16526 ereport(ERROR,
16527 (errcode(ERRCODE_SYNTAX_ERROR),
16528 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16529
16530 tab->newTableSpace = tablespaceId;
16531}
16532
16533/*
16534 * Set, reset, or replace reloptions.
16535 */
16536static void
16538 LOCKMODE lockmode)
16539{
16540 Oid relid;
16541 Relation pgclass;
16542 HeapTuple tuple;
16543 HeapTuple newtuple;
16544 Datum datum;
16545 Datum newOptions;
16546 Datum repl_val[Natts_pg_class];
16547 bool repl_null[Natts_pg_class];
16548 bool repl_repl[Natts_pg_class];
16549 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16550
16551 if (defList == NIL && operation != AT_ReplaceRelOptions)
16552 return; /* nothing to do */
16553
16554 pgclass = table_open(RelationRelationId, RowExclusiveLock);
16555
16556 /* Fetch heap tuple */
16557 relid = RelationGetRelid(rel);
16558 tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16559 if (!HeapTupleIsValid(tuple))
16560 elog(ERROR, "cache lookup failed for relation %u", relid);
16561
16562 if (operation == AT_ReplaceRelOptions)
16563 {
16564 /*
16565 * If we're supposed to replace the reloptions list, we just pretend
16566 * there were none before.
16567 */
16568 datum = (Datum) 0;
16569 }
16570 else
16571 {
16572 bool isnull;
16573
16574 /* Get the old reloptions */
16575 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16576 &isnull);
16577 if (isnull)
16578 datum = (Datum) 0;
16579 }
16580
16581 /* Generate new proposed reloptions (text array) */
16582 newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16583 operation == AT_ResetRelOptions);
16584
16585 /* Validate */
16586 switch (rel->rd_rel->relkind)
16587 {
16588 case RELKIND_RELATION:
16589 case RELKIND_MATVIEW:
16590 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16591 break;
16592 case RELKIND_PARTITIONED_TABLE:
16593 (void) partitioned_table_reloptions(newOptions, true);
16594 break;
16595 case RELKIND_VIEW:
16596 (void) view_reloptions(newOptions, true);
16597 break;
16598 case RELKIND_INDEX:
16599 case RELKIND_PARTITIONED_INDEX:
16600 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16601 break;
16602 case RELKIND_TOASTVALUE:
16603 /* fall through to error -- shouldn't ever get here */
16604 default:
16605 ereport(ERROR,
16606 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16607 errmsg("cannot set options for relation \"%s\"",
16609 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16610 break;
16611 }
16612
16613 /* Special-case validation of view options */
16614 if (rel->rd_rel->relkind == RELKIND_VIEW)
16615 {
16616 Query *view_query = get_view_query(rel);
16617 List *view_options = untransformRelOptions(newOptions);
16618 ListCell *cell;
16619 bool check_option = false;
16620
16621 foreach(cell, view_options)
16622 {
16623 DefElem *defel = (DefElem *) lfirst(cell);
16624
16625 if (strcmp(defel->defname, "check_option") == 0)
16626 check_option = true;
16627 }
16628
16629 /*
16630 * If the check option is specified, look to see if the view is
16631 * actually auto-updatable or not.
16632 */
16633 if (check_option)
16634 {
16635 const char *view_updatable_error =
16636 view_query_is_auto_updatable(view_query, true);
16637
16638 if (view_updatable_error)
16639 ereport(ERROR,
16640 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16641 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16642 errhint("%s", _(view_updatable_error))));
16643 }
16644 }
16645
16646 /*
16647 * All we need do here is update the pg_class row; the new options will be
16648 * propagated into relcaches during post-commit cache inval.
16649 */
16650 memset(repl_val, 0, sizeof(repl_val));
16651 memset(repl_null, false, sizeof(repl_null));
16652 memset(repl_repl, false, sizeof(repl_repl));
16653
16654 if (newOptions != (Datum) 0)
16655 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16656 else
16657 repl_null[Anum_pg_class_reloptions - 1] = true;
16658
16659 repl_repl[Anum_pg_class_reloptions - 1] = true;
16660
16661 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16662 repl_val, repl_null, repl_repl);
16663
16664 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16665 UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16666
16667 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16668
16669 heap_freetuple(newtuple);
16670
16671 ReleaseSysCache(tuple);
16672
16673 /* repeat the whole exercise for the toast table, if there's one */
16674 if (OidIsValid(rel->rd_rel->reltoastrelid))
16675 {
16676 Relation toastrel;
16677 Oid toastid = rel->rd_rel->reltoastrelid;
16678
16679 toastrel = table_open(toastid, lockmode);
16680
16681 /* Fetch heap tuple */
16682 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16683 if (!HeapTupleIsValid(tuple))
16684 elog(ERROR, "cache lookup failed for relation %u", toastid);
16685
16686 if (operation == AT_ReplaceRelOptions)
16687 {
16688 /*
16689 * If we're supposed to replace the reloptions list, we just
16690 * pretend there were none before.
16691 */
16692 datum = (Datum) 0;
16693 }
16694 else
16695 {
16696 bool isnull;
16697
16698 /* Get the old reloptions */
16699 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16700 &isnull);
16701 if (isnull)
16702 datum = (Datum) 0;
16703 }
16704
16705 newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16706 false, operation == AT_ResetRelOptions);
16707
16708 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16709
16710 memset(repl_val, 0, sizeof(repl_val));
16711 memset(repl_null, false, sizeof(repl_null));
16712 memset(repl_repl, false, sizeof(repl_repl));
16713
16714 if (newOptions != (Datum) 0)
16715 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16716 else
16717 repl_null[Anum_pg_class_reloptions - 1] = true;
16718
16719 repl_repl[Anum_pg_class_reloptions - 1] = true;
16720
16721 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16722 repl_val, repl_null, repl_repl);
16723
16724 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16725
16726 InvokeObjectPostAlterHookArg(RelationRelationId,
16727 RelationGetRelid(toastrel), 0,
16728 InvalidOid, true);
16729
16730 heap_freetuple(newtuple);
16731
16732 ReleaseSysCache(tuple);
16733
16734 table_close(toastrel, NoLock);
16735 }
16736
16737 table_close(pgclass, RowExclusiveLock);
16738}
16739
16740/*
16741 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16742 * rewriting to be done, so we just want to copy the data as fast as possible.
16743 */
16744static void
16745ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16746{
16747 Relation rel;
16748 Oid reltoastrelid;
16749 RelFileNumber newrelfilenumber;
16750 RelFileLocator newrlocator;
16751 List *reltoastidxids = NIL;
16752 ListCell *lc;
16753
16754 /*
16755 * Need lock here in case we are recursing to toast table or index
16756 */
16757 rel = relation_open(tableOid, lockmode);
16758
16759 /* Check first if relation can be moved to new tablespace */
16760 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16761 {
16762 InvokeObjectPostAlterHook(RelationRelationId,
16763 RelationGetRelid(rel), 0);
16764 relation_close(rel, NoLock);
16765 return;
16766 }
16767
16768 reltoastrelid = rel->rd_rel->reltoastrelid;
16769 /* Fetch the list of indexes on toast relation if necessary */
16770 if (OidIsValid(reltoastrelid))
16771 {
16772 Relation toastRel = relation_open(reltoastrelid, lockmode);
16773
16774 reltoastidxids = RelationGetIndexList(toastRel);
16775 relation_close(toastRel, lockmode);
16776 }
16777
16778 /*
16779 * Relfilenumbers are not unique in databases across tablespaces, so we
16780 * need to allocate a new one in the new tablespace.
16781 */
16782 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16783 rel->rd_rel->relpersistence);
16784
16785 /* Open old and new relation */
16786 newrlocator = rel->rd_locator;
16787 newrlocator.relNumber = newrelfilenumber;
16788 newrlocator.spcOid = newTableSpace;
16789
16790 /* hand off to AM to actually create new rel storage and copy the data */
16791 if (rel->rd_rel->relkind == RELKIND_INDEX)
16792 {
16793 index_copy_data(rel, newrlocator);
16794 }
16795 else
16796 {
16797 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16798 table_relation_copy_data(rel, &newrlocator);
16799 }
16800
16801 /*
16802 * Update the pg_class row.
16803 *
16804 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16805 * executed on pg_class or its indexes (the above copy wouldn't contain
16806 * the updated pg_class entry), but that's forbidden with
16807 * CheckRelationTableSpaceMove().
16808 */
16809 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16810
16811 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16812
16814
16815 relation_close(rel, NoLock);
16816
16817 /* Make sure the reltablespace change is visible */
16819
16820 /* Move associated toast relation and/or indexes, too */
16821 if (OidIsValid(reltoastrelid))
16822 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16823 foreach(lc, reltoastidxids)
16824 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16825
16826 /* Clean up */
16827 list_free(reltoastidxids);
16828}
16829
16830/*
16831 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16832 * storage that have an interest in preserving tablespace.
16833 *
16834 * Since these have no storage the tablespace can be updated with a simple
16835 * metadata only operation to update the tablespace.
16836 */
16837static void
16839{
16840 /*
16841 * Shouldn't be called on relations having storage; these are processed in
16842 * phase 3.
16843 */
16844 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16845
16846 /* check if relation can be moved to its new tablespace */
16847 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16848 {
16849 InvokeObjectPostAlterHook(RelationRelationId,
16850 RelationGetRelid(rel),
16851 0);
16852 return;
16853 }
16854
16855 /* Update can be done, so change reltablespace */
16856 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16857
16858 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16859
16860 /* Make sure the reltablespace change is visible */
16862}
16863
16864/*
16865 * Alter Table ALL ... SET TABLESPACE
16866 *
16867 * Allows a user to move all objects of some type in a given tablespace in the
16868 * current database to another tablespace. Objects can be chosen based on the
16869 * owner of the object also, to allow users to move only their objects.
16870 * The user must have CREATE rights on the new tablespace, as usual. The main
16871 * permissions handling is done by the lower-level table move function.
16872 *
16873 * All to-be-moved objects are locked first. If NOWAIT is specified and the
16874 * lock can't be acquired then we ereport(ERROR).
16875 */
16876Oid
16878{
16879 List *relations = NIL;
16880 ListCell *l;
16881 ScanKeyData key[1];
16882 Relation rel;
16883 TableScanDesc scan;
16884 HeapTuple tuple;
16885 Oid orig_tablespaceoid;
16886 Oid new_tablespaceoid;
16887 List *role_oids = roleSpecsToIds(stmt->roles);
16888
16889 /* Ensure we were not asked to move something we can't */
16890 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16891 stmt->objtype != OBJECT_MATVIEW)
16892 ereport(ERROR,
16893 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16894 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16895
16896 /* Get the orig and new tablespace OIDs */
16897 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16898 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16899
16900 /* Can't move shared relations in to or out of pg_global */
16901 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16902 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16903 new_tablespaceoid == GLOBALTABLESPACE_OID)
16904 ereport(ERROR,
16905 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16906 errmsg("cannot move relations in to or out of pg_global tablespace")));
16907
16908 /*
16909 * Must have CREATE rights on the new tablespace, unless it is the
16910 * database default tablespace (which all users implicitly have CREATE
16911 * rights on).
16912 */
16913 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16914 {
16915 AclResult aclresult;
16916
16917 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16918 ACL_CREATE);
16919 if (aclresult != ACLCHECK_OK)
16921 get_tablespace_name(new_tablespaceoid));
16922 }
16923
16924 /*
16925 * Now that the checks are done, check if we should set either to
16926 * InvalidOid because it is our database's default tablespace.
16927 */
16928 if (orig_tablespaceoid == MyDatabaseTableSpace)
16929 orig_tablespaceoid = InvalidOid;
16930
16931 if (new_tablespaceoid == MyDatabaseTableSpace)
16932 new_tablespaceoid = InvalidOid;
16933
16934 /* no-op */
16935 if (orig_tablespaceoid == new_tablespaceoid)
16936 return new_tablespaceoid;
16937
16938 /*
16939 * Walk the list of objects in the tablespace and move them. This will
16940 * only find objects in our database, of course.
16941 */
16942 ScanKeyInit(&key[0],
16943 Anum_pg_class_reltablespace,
16944 BTEqualStrategyNumber, F_OIDEQ,
16945 ObjectIdGetDatum(orig_tablespaceoid));
16946
16947 rel = table_open(RelationRelationId, AccessShareLock);
16948 scan = table_beginscan_catalog(rel, 1, key);
16949 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16950 {
16951 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16952 Oid relOid = relForm->oid;
16953
16954 /*
16955 * Do not move objects in pg_catalog as part of this, if an admin
16956 * really wishes to do so, they can issue the individual ALTER
16957 * commands directly.
16958 *
16959 * Also, explicitly avoid any shared tables, temp tables, or TOAST
16960 * (TOAST will be moved with the main table).
16961 */
16962 if (IsCatalogNamespace(relForm->relnamespace) ||
16963 relForm->relisshared ||
16964 isAnyTempNamespace(relForm->relnamespace) ||
16965 IsToastNamespace(relForm->relnamespace))
16966 continue;
16967
16968 /* Only move the object type requested */
16969 if ((stmt->objtype == OBJECT_TABLE &&
16970 relForm->relkind != RELKIND_RELATION &&
16971 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
16972 (stmt->objtype == OBJECT_INDEX &&
16973 relForm->relkind != RELKIND_INDEX &&
16974 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16975 (stmt->objtype == OBJECT_MATVIEW &&
16976 relForm->relkind != RELKIND_MATVIEW))
16977 continue;
16978
16979 /* Check if we are only moving objects owned by certain roles */
16980 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
16981 continue;
16982
16983 /*
16984 * Handle permissions-checking here since we are locking the tables
16985 * and also to avoid doing a bunch of work only to fail part-way. Note
16986 * that permissions will also be checked by AlterTableInternal().
16987 *
16988 * Caller must be considered an owner on the table to move it.
16989 */
16990 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16992 NameStr(relForm->relname));
16993
16994 if (stmt->nowait &&
16996 ereport(ERROR,
16997 (errcode(ERRCODE_OBJECT_IN_USE),
16998 errmsg("aborting because lock on relation \"%s.%s\" is not available",
16999 get_namespace_name(relForm->relnamespace),
17000 NameStr(relForm->relname))));
17001 else
17003
17004 /* Add to our list of objects to move */
17005 relations = lappend_oid(relations, relOid);
17006 }
17007
17008 table_endscan(scan);
17010
17011 if (relations == NIL)
17013 (errcode(ERRCODE_NO_DATA_FOUND),
17014 errmsg("no matching relations in tablespace \"%s\" found",
17015 orig_tablespaceoid == InvalidOid ? "(database default)" :
17016 get_tablespace_name(orig_tablespaceoid))));
17017
17018 /* Everything is locked, loop through and move all of the relations. */
17019 foreach(l, relations)
17020 {
17021 List *cmds = NIL;
17023
17025 cmd->name = stmt->new_tablespacename;
17026
17027 cmds = lappend(cmds, cmd);
17028
17030 /* OID is set by AlterTableInternal */
17031 AlterTableInternal(lfirst_oid(l), cmds, false);
17033 }
17034
17035 return new_tablespaceoid;
17036}
17037
17038static void
17040{
17041 SMgrRelation dstrel;
17042
17043 /*
17044 * Since we copy the file directly without looking at the shared buffers,
17045 * we'd better first flush out any pages of the source relation that are
17046 * in shared buffers. We assume no new changes will be made while we are
17047 * holding exclusive lock on the rel.
17048 */
17050
17051 /*
17052 * Create and copy all forks of the relation, and schedule unlinking of
17053 * old physical files.
17054 *
17055 * NOTE: any conflict in relfilenumber value will be caught in
17056 * RelationCreateStorage().
17057 */
17058 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17059
17060 /* copy main fork */
17062 rel->rd_rel->relpersistence);
17063
17064 /* copy those extra forks that exist */
17065 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17066 forkNum <= MAX_FORKNUM; forkNum++)
17067 {
17068 if (smgrexists(RelationGetSmgr(rel), forkNum))
17069 {
17070 smgrcreate(dstrel, forkNum, false);
17071
17072 /*
17073 * WAL log creation if the relation is persistent, or this is the
17074 * init fork of an unlogged relation.
17075 */
17076 if (RelationIsPermanent(rel) ||
17077 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17078 forkNum == INIT_FORKNUM))
17079 log_smgrcreate(&newrlocator, forkNum);
17080 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17081 rel->rd_rel->relpersistence);
17082 }
17083 }
17084
17085 /* drop old relation, and close new one */
17087 smgrclose(dstrel);
17088}
17089
17090/*
17091 * ALTER TABLE ENABLE/DISABLE TRIGGER
17092 *
17093 * We just pass this off to trigger.c.
17094 */
17095static void
17096ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17097 char fires_when, bool skip_system, bool recurse,
17098 LOCKMODE lockmode)
17099{
17100 EnableDisableTrigger(rel, trigname, InvalidOid,
17101 fires_when, skip_system, recurse,
17102 lockmode);
17103
17104 InvokeObjectPostAlterHook(RelationRelationId,
17105 RelationGetRelid(rel), 0);
17106}
17107
17108/*
17109 * ALTER TABLE ENABLE/DISABLE RULE
17110 *
17111 * We just pass this off to rewriteDefine.c.
17112 */
17113static void
17114ATExecEnableDisableRule(Relation rel, const char *rulename,
17115 char fires_when, LOCKMODE lockmode)
17116{
17117 EnableDisableRule(rel, rulename, fires_when);
17118
17119 InvokeObjectPostAlterHook(RelationRelationId,
17120 RelationGetRelid(rel), 0);
17121}
17122
17123/*
17124 * ALTER TABLE INHERIT
17125 *
17126 * Add a parent to the child's parents. This verifies that all the columns and
17127 * check constraints of the parent appear in the child and that they have the
17128 * same data types and expressions.
17129 */
17130static void
17132{
17133 if (child_rel->rd_rel->reloftype)
17134 ereport(ERROR,
17135 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17136 errmsg("cannot change inheritance of typed table")));
17137
17138 if (child_rel->rd_rel->relispartition)
17139 ereport(ERROR,
17140 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17141 errmsg("cannot change inheritance of a partition")));
17142
17143 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17144 ereport(ERROR,
17145 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17146 errmsg("cannot change inheritance of partitioned table")));
17147}
17148
17149/*
17150 * Return the address of the new parent relation.
17151 */
17152static ObjectAddress
17153ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17154{
17155 Relation parent_rel;
17156 List *children;
17157 ObjectAddress address;
17158 const char *trigger_name;
17159
17160 /*
17161 * A self-exclusive lock is needed here. See the similar case in
17162 * MergeAttributes() for a full explanation.
17163 */
17164 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17165
17166 /*
17167 * Must be owner of both parent and child -- child was checked by
17168 * ATSimplePermissions call in ATPrepCmd
17169 */
17172
17173 /* Permanent rels cannot inherit from temporary ones */
17174 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17175 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17176 ereport(ERROR,
17177 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17178 errmsg("cannot inherit from temporary relation \"%s\"",
17179 RelationGetRelationName(parent_rel))));
17180
17181 /* If parent rel is temp, it must belong to this session */
17182 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17183 !parent_rel->rd_islocaltemp)
17184 ereport(ERROR,
17185 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17186 errmsg("cannot inherit from temporary relation of another session")));
17187
17188 /* Ditto for the child */
17189 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17190 !child_rel->rd_islocaltemp)
17191 ereport(ERROR,
17192 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17193 errmsg("cannot inherit to temporary relation of another session")));
17194
17195 /* Prevent partitioned tables from becoming inheritance parents */
17196 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17197 ereport(ERROR,
17198 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17199 errmsg("cannot inherit from partitioned table \"%s\"",
17200 parent->relname)));
17201
17202 /* Likewise for partitions */
17203 if (parent_rel->rd_rel->relispartition)
17204 ereport(ERROR,
17205 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17206 errmsg("cannot inherit from a partition")));
17207
17208 /*
17209 * Prevent circularity by seeing if proposed parent inherits from child.
17210 * (In particular, this disallows making a rel inherit from itself.)
17211 *
17212 * This is not completely bulletproof because of race conditions: in
17213 * multi-level inheritance trees, someone else could concurrently be
17214 * making another inheritance link that closes the loop but does not join
17215 * either of the rels we have locked. Preventing that seems to require
17216 * exclusive locks on the entire inheritance tree, which is a cure worse
17217 * than the disease. find_all_inheritors() will cope with circularity
17218 * anyway, so don't sweat it too much.
17219 *
17220 * We use weakest lock we can on child's children, namely AccessShareLock.
17221 */
17222 children = find_all_inheritors(RelationGetRelid(child_rel),
17223 AccessShareLock, NULL);
17224
17225 if (list_member_oid(children, RelationGetRelid(parent_rel)))
17226 ereport(ERROR,
17227 (errcode(ERRCODE_DUPLICATE_TABLE),
17228 errmsg("circular inheritance not allowed"),
17229 errdetail("\"%s\" is already a child of \"%s\".",
17230 parent->relname,
17231 RelationGetRelationName(child_rel))));
17232
17233 /*
17234 * If child_rel has row-level triggers with transition tables, we
17235 * currently don't allow it to become an inheritance child. See also
17236 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17237 */
17238 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17239 if (trigger_name != NULL)
17240 ereport(ERROR,
17241 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17242 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17243 trigger_name, RelationGetRelationName(child_rel)),
17244 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17245
17246 /* OK to create inheritance */
17247 CreateInheritance(child_rel, parent_rel, false);
17248
17249 ObjectAddressSet(address, RelationRelationId,
17250 RelationGetRelid(parent_rel));
17251
17252 /* keep our lock on the parent relation until commit */
17253 table_close(parent_rel, NoLock);
17254
17255 return address;
17256}
17257
17258/*
17259 * CreateInheritance
17260 * Catalog manipulation portion of creating inheritance between a child
17261 * table and a parent table.
17262 *
17263 * Common to ATExecAddInherit() and ATExecAttachPartition().
17264 */
17265static void
17266CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17267{
17268 Relation catalogRelation;
17269 SysScanDesc scan;
17271 HeapTuple inheritsTuple;
17272 int32 inhseqno;
17273
17274 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17275 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17276
17277 /*
17278 * Check for duplicates in the list of parents, and determine the highest
17279 * inhseqno already present; we'll use the next one for the new parent.
17280 * Also, if proposed child is a partition, it cannot already be
17281 * inheriting.
17282 *
17283 * Note: we do not reject the case where the child already inherits from
17284 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17285 */
17287 Anum_pg_inherits_inhrelid,
17288 BTEqualStrategyNumber, F_OIDEQ,
17290 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17291 true, NULL, 1, &key);
17292
17293 /* inhseqno sequences start at 1 */
17294 inhseqno = 0;
17295 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17296 {
17297 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17298
17299 if (inh->inhparent == RelationGetRelid(parent_rel))
17300 ereport(ERROR,
17301 (errcode(ERRCODE_DUPLICATE_TABLE),
17302 errmsg("relation \"%s\" would be inherited from more than once",
17303 RelationGetRelationName(parent_rel))));
17304
17305 if (inh->inhseqno > inhseqno)
17306 inhseqno = inh->inhseqno;
17307 }
17308 systable_endscan(scan);
17309
17310 /* Match up the columns and bump attinhcount as needed */
17311 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17312
17313 /* Match up the constraints and bump coninhcount as needed */
17314 MergeConstraintsIntoExisting(child_rel, parent_rel);
17315
17316 /*
17317 * OK, it looks valid. Make the catalog entries that show inheritance.
17318 */
17320 RelationGetRelid(parent_rel),
17321 inhseqno + 1,
17322 catalogRelation,
17323 parent_rel->rd_rel->relkind ==
17324 RELKIND_PARTITIONED_TABLE);
17325
17326 /* Now we're done with pg_inherits */
17327 table_close(catalogRelation, RowExclusiveLock);
17328}
17329
17330/*
17331 * Obtain the source-text form of the constraint expression for a check
17332 * constraint, given its pg_constraint tuple
17333 */
17334static char *
17336{
17338 bool isnull;
17339 Datum attr;
17340 Datum expr;
17341
17342 con = (Form_pg_constraint) GETSTRUCT(contup);
17343 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17344 if (isnull)
17345 elog(ERROR, "null conbin for constraint %u", con->oid);
17346
17347 expr = DirectFunctionCall2(pg_get_expr, attr,
17348 ObjectIdGetDatum(con->conrelid));
17349 return TextDatumGetCString(expr);
17350}
17351
17352/*
17353 * Determine whether two check constraints are functionally equivalent
17354 *
17355 * The test we apply is to see whether they reverse-compile to the same
17356 * source string. This insulates us from issues like whether attributes
17357 * have the same physical column numbers in parent and child relations.
17358 *
17359 * Note that we ignore enforceability as there are cases where constraints
17360 * with differing enforceability are allowed.
17361 */
17362static bool
17364{
17367
17368 if (acon->condeferrable != bcon->condeferrable ||
17369 acon->condeferred != bcon->condeferred ||
17370 strcmp(decompile_conbin(a, tupleDesc),
17371 decompile_conbin(b, tupleDesc)) != 0)
17372 return false;
17373 else
17374 return true;
17375}
17376
17377/*
17378 * Check columns in child table match up with columns in parent, and increment
17379 * their attinhcount.
17380 *
17381 * Called by CreateInheritance
17382 *
17383 * Currently all parent columns must be found in child. Missing columns are an
17384 * error. One day we might consider creating new columns like CREATE TABLE
17385 * does. However, that is widely unpopular --- in the common use case of
17386 * partitioned tables it's a foot-gun.
17387 *
17388 * The data type must match exactly. If the parent column is NOT NULL then
17389 * the child must be as well. Defaults are not compared, however.
17390 */
17391static void
17392MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17393{
17394 Relation attrrel;
17395 TupleDesc parent_desc;
17396
17397 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17398 parent_desc = RelationGetDescr(parent_rel);
17399
17400 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17401 {
17402 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17403 char *parent_attname = NameStr(parent_att->attname);
17404 HeapTuple tuple;
17405
17406 /* Ignore dropped columns in the parent. */
17407 if (parent_att->attisdropped)
17408 continue;
17409
17410 /* Find same column in child (matching on column name). */
17411 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17412 if (HeapTupleIsValid(tuple))
17413 {
17414 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17415
17416 if (parent_att->atttypid != child_att->atttypid ||
17417 parent_att->atttypmod != child_att->atttypmod)
17418 ereport(ERROR,
17419 (errcode(ERRCODE_DATATYPE_MISMATCH),
17420 errmsg("child table \"%s\" has different type for column \"%s\"",
17421 RelationGetRelationName(child_rel), parent_attname)));
17422
17423 if (parent_att->attcollation != child_att->attcollation)
17424 ereport(ERROR,
17425 (errcode(ERRCODE_COLLATION_MISMATCH),
17426 errmsg("child table \"%s\" has different collation for column \"%s\"",
17427 RelationGetRelationName(child_rel), parent_attname)));
17428
17429 /*
17430 * If the parent has a not-null constraint that's not NO INHERIT,
17431 * make sure the child has one too.
17432 *
17433 * Other constraints are checked elsewhere.
17434 */
17435 if (parent_att->attnotnull && !child_att->attnotnull)
17436 {
17437 HeapTuple contup;
17438
17439 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17440 parent_att->attnum);
17441 if (HeapTupleIsValid(contup) &&
17442 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17443 ereport(ERROR,
17444 errcode(ERRCODE_DATATYPE_MISMATCH),
17445 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17446 parent_attname, RelationGetRelationName(child_rel)));
17447 }
17448
17449 /*
17450 * Child column must be generated if and only if parent column is.
17451 */
17452 if (parent_att->attgenerated && !child_att->attgenerated)
17453 ereport(ERROR,
17454 (errcode(ERRCODE_DATATYPE_MISMATCH),
17455 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17456 if (child_att->attgenerated && !parent_att->attgenerated)
17457 ereport(ERROR,
17458 (errcode(ERRCODE_DATATYPE_MISMATCH),
17459 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17460
17461 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17462 ereport(ERROR,
17463 (errcode(ERRCODE_DATATYPE_MISMATCH),
17464 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17465 errdetail("Parent column is %s, child column is %s.",
17466 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17467 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17468
17469 /*
17470 * Regular inheritance children are independent enough not to
17471 * inherit identity columns. But partitions are integral part of
17472 * a partitioned table and inherit identity column.
17473 */
17474 if (ispartition)
17475 child_att->attidentity = parent_att->attidentity;
17476
17477 /*
17478 * OK, bump the child column's inheritance count. (If we fail
17479 * later on, this change will just roll back.)
17480 */
17481 if (pg_add_s16_overflow(child_att->attinhcount, 1,
17482 &child_att->attinhcount))
17483 ereport(ERROR,
17484 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17485 errmsg("too many inheritance parents"));
17486
17487 /*
17488 * In case of partitions, we must enforce that value of attislocal
17489 * is same in all partitions. (Note: there are only inherited
17490 * attributes in partitions)
17491 */
17492 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17493 {
17494 Assert(child_att->attinhcount == 1);
17495 child_att->attislocal = false;
17496 }
17497
17498 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17499 heap_freetuple(tuple);
17500 }
17501 else
17502 {
17503 ereport(ERROR,
17504 (errcode(ERRCODE_DATATYPE_MISMATCH),
17505 errmsg("child table is missing column \"%s\"", parent_attname)));
17506 }
17507 }
17508
17509 table_close(attrrel, RowExclusiveLock);
17510}
17511
17512/*
17513 * Check constraints in child table match up with constraints in parent,
17514 * and increment their coninhcount.
17515 *
17516 * Constraints that are marked ONLY in the parent are ignored.
17517 *
17518 * Called by CreateInheritance
17519 *
17520 * Currently all constraints in parent must be present in the child. One day we
17521 * may consider adding new constraints like CREATE TABLE does.
17522 *
17523 * XXX This is O(N^2) which may be an issue with tables with hundreds of
17524 * constraints. As long as tables have more like 10 constraints it shouldn't be
17525 * a problem though. Even 100 constraints ought not be the end of the world.
17526 *
17527 * XXX See MergeWithExistingConstraint too if you change this code.
17528 */
17529static void
17531{
17532 Relation constraintrel;
17533 SysScanDesc parent_scan;
17534 ScanKeyData parent_key;
17535 HeapTuple parent_tuple;
17536 Oid parent_relid = RelationGetRelid(parent_rel);
17537 AttrMap *attmap;
17538
17539 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17540
17541 /* Outer loop scans through the parent's constraint definitions */
17542 ScanKeyInit(&parent_key,
17543 Anum_pg_constraint_conrelid,
17544 BTEqualStrategyNumber, F_OIDEQ,
17545 ObjectIdGetDatum(parent_relid));
17546 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17547 true, NULL, 1, &parent_key);
17548
17549 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17550 RelationGetDescr(child_rel),
17551 true);
17552
17553 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17554 {
17555 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17556 SysScanDesc child_scan;
17557 ScanKeyData child_key;
17558 HeapTuple child_tuple;
17559 AttrNumber parent_attno;
17560 bool found = false;
17561
17562 if (parent_con->contype != CONSTRAINT_CHECK &&
17563 parent_con->contype != CONSTRAINT_NOTNULL)
17564 continue;
17565
17566 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17567 if (parent_con->connoinherit)
17568 continue;
17569
17570 if (parent_con->contype == CONSTRAINT_NOTNULL)
17571 parent_attno = extractNotNullColumn(parent_tuple);
17572 else
17573 parent_attno = InvalidAttrNumber;
17574
17575 /* Search for a child constraint matching this one */
17576 ScanKeyInit(&child_key,
17577 Anum_pg_constraint_conrelid,
17578 BTEqualStrategyNumber, F_OIDEQ,
17580 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17581 true, NULL, 1, &child_key);
17582
17583 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17584 {
17585 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17586 HeapTuple child_copy;
17587
17588 if (child_con->contype != parent_con->contype)
17589 continue;
17590
17591 /*
17592 * CHECK constraint are matched by constraint name, NOT NULL ones
17593 * by attribute number.
17594 */
17595 if (child_con->contype == CONSTRAINT_CHECK)
17596 {
17597 if (strcmp(NameStr(parent_con->conname),
17598 NameStr(child_con->conname)) != 0)
17599 continue;
17600 }
17601 else if (child_con->contype == CONSTRAINT_NOTNULL)
17602 {
17603 Form_pg_attribute parent_attr;
17604 Form_pg_attribute child_attr;
17605 AttrNumber child_attno;
17606
17607 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17608 child_attno = extractNotNullColumn(child_tuple);
17609 if (parent_attno != attmap->attnums[child_attno - 1])
17610 continue;
17611
17612 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17613 /* there shouldn't be constraints on dropped columns */
17614 if (parent_attr->attisdropped || child_attr->attisdropped)
17615 elog(ERROR, "found not-null constraint on dropped columns");
17616 }
17617
17618 if (child_con->contype == CONSTRAINT_CHECK &&
17619 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17620 ereport(ERROR,
17621 (errcode(ERRCODE_DATATYPE_MISMATCH),
17622 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17623 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17624
17625 /*
17626 * If the child constraint is "no inherit" then cannot merge
17627 */
17628 if (child_con->connoinherit)
17629 ereport(ERROR,
17630 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17631 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17632 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17633
17634 /*
17635 * If the child constraint is "not valid" then cannot merge with a
17636 * valid parent constraint
17637 */
17638 if (parent_con->convalidated && child_con->conenforced &&
17639 !child_con->convalidated)
17640 ereport(ERROR,
17641 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17642 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17643 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17644
17645 /*
17646 * A NOT ENFORCED child constraint cannot be merged with an
17647 * ENFORCED parent constraint. However, the reverse is allowed,
17648 * where the child constraint is ENFORCED.
17649 */
17650 if (parent_con->conenforced && !child_con->conenforced)
17651 ereport(ERROR,
17652 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17653 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17654 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17655
17656 /*
17657 * OK, bump the child constraint's inheritance count. (If we fail
17658 * later on, this change will just roll back.)
17659 */
17660 child_copy = heap_copytuple(child_tuple);
17661 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17662
17663 if (pg_add_s16_overflow(child_con->coninhcount, 1,
17664 &child_con->coninhcount))
17665 ereport(ERROR,
17666 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17667 errmsg("too many inheritance parents"));
17668
17669 /*
17670 * In case of partitions, an inherited constraint must be
17671 * inherited only once since it cannot have multiple parents and
17672 * it is never considered local.
17673 */
17674 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17675 {
17676 Assert(child_con->coninhcount == 1);
17677 child_con->conislocal = false;
17678 }
17679
17680 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17681 heap_freetuple(child_copy);
17682
17683 found = true;
17684 break;
17685 }
17686
17687 systable_endscan(child_scan);
17688
17689 if (!found)
17690 {
17691 if (parent_con->contype == CONSTRAINT_NOTNULL)
17692 ereport(ERROR,
17693 errcode(ERRCODE_DATATYPE_MISMATCH),
17694 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17695 get_attname(parent_relid,
17696 extractNotNullColumn(parent_tuple),
17697 false),
17698 RelationGetRelationName(child_rel)));
17699
17700 ereport(ERROR,
17701 (errcode(ERRCODE_DATATYPE_MISMATCH),
17702 errmsg("child table is missing constraint \"%s\"",
17703 NameStr(parent_con->conname))));
17704 }
17705 }
17706
17707 systable_endscan(parent_scan);
17708 table_close(constraintrel, RowExclusiveLock);
17709}
17710
17711/*
17712 * ALTER TABLE NO INHERIT
17713 *
17714 * Return value is the address of the relation that is no longer parent.
17715 */
17716static ObjectAddress
17718{
17719 ObjectAddress address;
17720 Relation parent_rel;
17721
17722 if (rel->rd_rel->relispartition)
17723 ereport(ERROR,
17724 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17725 errmsg("cannot change inheritance of a partition")));
17726
17727 /*
17728 * AccessShareLock on the parent is probably enough, seeing that DROP
17729 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17730 * be inspecting the parent's schema.
17731 */
17732 parent_rel = table_openrv(parent, AccessShareLock);
17733
17734 /*
17735 * We don't bother to check ownership of the parent table --- ownership of
17736 * the child is presumed enough rights.
17737 */
17738
17739 /* Off to RemoveInheritance() where most of the work happens */
17740 RemoveInheritance(rel, parent_rel, false);
17741
17742 ObjectAddressSet(address, RelationRelationId,
17743 RelationGetRelid(parent_rel));
17744
17745 /* keep our lock on the parent relation until commit */
17746 table_close(parent_rel, NoLock);
17747
17748 return address;
17749}
17750
17751/*
17752 * MarkInheritDetached
17753 *
17754 * Set inhdetachpending for a partition, for ATExecDetachPartition
17755 * in concurrent mode. While at it, verify that no other partition is
17756 * already pending detach.
17757 */
17758static void
17760{
17761 Relation catalogRelation;
17762 SysScanDesc scan;
17764 HeapTuple inheritsTuple;
17765 bool found = false;
17766
17767 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17768
17769 /*
17770 * Find pg_inherits entries by inhparent. (We need to scan them all in
17771 * order to verify that no other partition is pending detach.)
17772 */
17773 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17775 Anum_pg_inherits_inhparent,
17776 BTEqualStrategyNumber, F_OIDEQ,
17777 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17778 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17779 true, NULL, 1, &key);
17780
17781 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17782 {
17783 Form_pg_inherits inhForm;
17784
17785 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17786 if (inhForm->inhdetachpending)
17787 ereport(ERROR,
17788 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17789 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17790 get_rel_name(inhForm->inhrelid),
17791 get_namespace_name(parent_rel->rd_rel->relnamespace),
17792 RelationGetRelationName(parent_rel)),
17793 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17794
17795 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17796 {
17797 HeapTuple newtup;
17798
17799 newtup = heap_copytuple(inheritsTuple);
17800 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17801
17802 CatalogTupleUpdate(catalogRelation,
17803 &inheritsTuple->t_self,
17804 newtup);
17805 found = true;
17806 heap_freetuple(newtup);
17807 /* keep looking, to ensure we catch others pending detach */
17808 }
17809 }
17810
17811 /* Done */
17812 systable_endscan(scan);
17813 table_close(catalogRelation, RowExclusiveLock);
17814
17815 if (!found)
17816 ereport(ERROR,
17818 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17819 RelationGetRelationName(child_rel),
17820 RelationGetRelationName(parent_rel))));
17821}
17822
17823/*
17824 * RemoveInheritance
17825 *
17826 * Drop a parent from the child's parents. This just adjusts the attinhcount
17827 * and attislocal of the columns and removes the pg_inherit and pg_depend
17828 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17829 *
17830 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17831 * up attislocal stays true, which means if a child is ever removed from a
17832 * parent then its columns will never be automatically dropped which may
17833 * surprise. But at least we'll never surprise by dropping columns someone
17834 * isn't expecting to be dropped which would actually mean data loss.
17835 *
17836 * coninhcount and conislocal for inherited constraints are adjusted in
17837 * exactly the same way.
17838 *
17839 * Common to ATExecDropInherit() and ATExecDetachPartition().
17840 */
17841static void
17842RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17843{
17844 Relation catalogRelation;
17845 SysScanDesc scan;
17846 ScanKeyData key[3];
17847 HeapTuple attributeTuple,
17848 constraintTuple;
17849 AttrMap *attmap;
17850 List *connames;
17851 List *nncolumns;
17852 bool found;
17853 bool is_partitioning;
17854
17855 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17856
17857 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17858 RelationGetRelid(parent_rel),
17859 expect_detached,
17860 RelationGetRelationName(child_rel));
17861 if (!found)
17862 {
17863 if (is_partitioning)
17864 ereport(ERROR,
17866 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17867 RelationGetRelationName(child_rel),
17868 RelationGetRelationName(parent_rel))));
17869 else
17870 ereport(ERROR,
17872 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17873 RelationGetRelationName(parent_rel),
17874 RelationGetRelationName(child_rel))));
17875 }
17876
17877 /*
17878 * Search through child columns looking for ones matching parent rel
17879 */
17880 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17881 ScanKeyInit(&key[0],
17882 Anum_pg_attribute_attrelid,
17883 BTEqualStrategyNumber, F_OIDEQ,
17885 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17886 true, NULL, 1, key);
17887 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17888 {
17889 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17890
17891 /* Ignore if dropped or not inherited */
17892 if (att->attisdropped)
17893 continue;
17894 if (att->attinhcount <= 0)
17895 continue;
17896
17898 NameStr(att->attname)))
17899 {
17900 /* Decrement inhcount and possibly set islocal to true */
17901 HeapTuple copyTuple = heap_copytuple(attributeTuple);
17902 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17903
17904 copy_att->attinhcount--;
17905 if (copy_att->attinhcount == 0)
17906 copy_att->attislocal = true;
17907
17908 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
17909 heap_freetuple(copyTuple);
17910 }
17911 }
17912 systable_endscan(scan);
17913 table_close(catalogRelation, RowExclusiveLock);
17914
17915 /*
17916 * Likewise, find inherited check and not-null constraints and disinherit
17917 * them. To do this, we first need a list of the names of the parent's
17918 * check constraints. (We cheat a bit by only checking for name matches,
17919 * assuming that the expressions will match.)
17920 *
17921 * For NOT NULL columns, we store column numbers to match, mapping them in
17922 * to the child rel's attribute numbers.
17923 */
17924 attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17925 RelationGetDescr(parent_rel),
17926 false);
17927
17928 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17929 ScanKeyInit(&key[0],
17930 Anum_pg_constraint_conrelid,
17931 BTEqualStrategyNumber, F_OIDEQ,
17932 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17933 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17934 true, NULL, 1, key);
17935
17936 connames = NIL;
17937 nncolumns = NIL;
17938
17939 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17940 {
17941 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17942
17943 if (con->connoinherit)
17944 continue;
17945
17946 if (con->contype == CONSTRAINT_CHECK)
17947 connames = lappend(connames, pstrdup(NameStr(con->conname)));
17948 if (con->contype == CONSTRAINT_NOTNULL)
17949 {
17950 AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
17951
17952 nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
17953 }
17954 }
17955
17956 systable_endscan(scan);
17957
17958 /* Now scan the child's constraints to find matches */
17959 ScanKeyInit(&key[0],
17960 Anum_pg_constraint_conrelid,
17961 BTEqualStrategyNumber, F_OIDEQ,
17963 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17964 true, NULL, 1, key);
17965
17966 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17967 {
17968 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17969 bool match = false;
17970
17971 /*
17972 * Match CHECK constraints by name, not-null constraints by column
17973 * number, and ignore all others.
17974 */
17975 if (con->contype == CONSTRAINT_CHECK)
17976 {
17977 foreach_ptr(char, chkname, connames)
17978 {
17979 if (con->contype == CONSTRAINT_CHECK &&
17980 strcmp(NameStr(con->conname), chkname) == 0)
17981 {
17982 match = true;
17983 connames = foreach_delete_current(connames, chkname);
17984 break;
17985 }
17986 }
17987 }
17988 else if (con->contype == CONSTRAINT_NOTNULL)
17989 {
17990 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
17991
17992 foreach_int(prevattno, nncolumns)
17993 {
17994 if (prevattno == child_attno)
17995 {
17996 match = true;
17997 nncolumns = foreach_delete_current(nncolumns, prevattno);
17998 break;
17999 }
18000 }
18001 }
18002 else
18003 continue;
18004
18005 if (match)
18006 {
18007 /* Decrement inhcount and possibly set islocal to true */
18008 HeapTuple copyTuple = heap_copytuple(constraintTuple);
18009 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18010
18011 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18012 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18013 RelationGetRelid(child_rel), NameStr(copy_con->conname));
18014
18015 copy_con->coninhcount--;
18016 if (copy_con->coninhcount == 0)
18017 copy_con->conislocal = true;
18018
18019 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18020 heap_freetuple(copyTuple);
18021 }
18022 }
18023
18024 /* We should have matched all constraints */
18025 if (connames != NIL || nncolumns != NIL)
18026 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18027 list_length(connames) + list_length(nncolumns),
18028 RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18029
18030 systable_endscan(scan);
18031 table_close(catalogRelation, RowExclusiveLock);
18032
18034 RelationRelationId,
18035 RelationGetRelid(parent_rel),
18036 child_dependency_type(is_partitioning));
18037
18038 /*
18039 * Post alter hook of this inherits. Since object_access_hook doesn't take
18040 * multiple object identifiers, we relay oid of parent relation using
18041 * auxiliary_id argument.
18042 */
18043 InvokeObjectPostAlterHookArg(InheritsRelationId,
18044 RelationGetRelid(child_rel), 0,
18045 RelationGetRelid(parent_rel), false);
18046}
18047
18048/*
18049 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18050 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18051 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18052 * be TypeRelationId). There's no convenient way to do this, so go trawling
18053 * through pg_depend.
18054 */
18055static void
18056drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18057 DependencyType deptype)
18058{
18059 Relation catalogRelation;
18060 SysScanDesc scan;
18061 ScanKeyData key[3];
18062 HeapTuple depTuple;
18063
18064 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18065
18066 ScanKeyInit(&key[0],
18067 Anum_pg_depend_classid,
18068 BTEqualStrategyNumber, F_OIDEQ,
18069 ObjectIdGetDatum(RelationRelationId));
18070 ScanKeyInit(&key[1],
18071 Anum_pg_depend_objid,
18072 BTEqualStrategyNumber, F_OIDEQ,
18073 ObjectIdGetDatum(relid));
18074 ScanKeyInit(&key[2],
18075 Anum_pg_depend_objsubid,
18076 BTEqualStrategyNumber, F_INT4EQ,
18077 Int32GetDatum(0));
18078
18079 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18080 NULL, 3, key);
18081
18082 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18083 {
18084 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18085
18086 if (dep->refclassid == refclassid &&
18087 dep->refobjid == refobjid &&
18088 dep->refobjsubid == 0 &&
18089 dep->deptype == deptype)
18090 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18091 }
18092
18093 systable_endscan(scan);
18094 table_close(catalogRelation, RowExclusiveLock);
18095}
18096
18097/*
18098 * ALTER TABLE OF
18099 *
18100 * Attach a table to a composite type, as though it had been created with CREATE
18101 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18102 * subject table must not have inheritance parents. These restrictions ensure
18103 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18104 *
18105 * The address of the type is returned.
18106 */
18107static ObjectAddress
18108ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18109{
18110 Oid relid = RelationGetRelid(rel);
18111 Type typetuple;
18112 Form_pg_type typeform;
18113 Oid typeid;
18114 Relation inheritsRelation,
18115 relationRelation;
18116 SysScanDesc scan;
18118 AttrNumber table_attno,
18119 type_attno;
18120 TupleDesc typeTupleDesc,
18121 tableTupleDesc;
18122 ObjectAddress tableobj,
18123 typeobj;
18124 HeapTuple classtuple;
18125
18126 /* Validate the type. */
18127 typetuple = typenameType(NULL, ofTypename, NULL);
18128 check_of_type(typetuple);
18129 typeform = (Form_pg_type) GETSTRUCT(typetuple);
18130 typeid = typeform->oid;
18131
18132 /* Fail if the table has any inheritance parents. */
18133 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18135 Anum_pg_inherits_inhrelid,
18136 BTEqualStrategyNumber, F_OIDEQ,
18137 ObjectIdGetDatum(relid));
18138 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18139 true, NULL, 1, &key);
18141 ereport(ERROR,
18142 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18143 errmsg("typed tables cannot inherit")));
18144 systable_endscan(scan);
18145 table_close(inheritsRelation, AccessShareLock);
18146
18147 /*
18148 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18149 * require that the order also match. However, attnotnull need not match.
18150 */
18151 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18152 tableTupleDesc = RelationGetDescr(rel);
18153 table_attno = 1;
18154 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18155 {
18156 Form_pg_attribute type_attr,
18157 table_attr;
18158 const char *type_attname,
18159 *table_attname;
18160
18161 /* Get the next non-dropped type attribute. */
18162 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18163 if (type_attr->attisdropped)
18164 continue;
18165 type_attname = NameStr(type_attr->attname);
18166
18167 /* Get the next non-dropped table attribute. */
18168 do
18169 {
18170 if (table_attno > tableTupleDesc->natts)
18171 ereport(ERROR,
18172 (errcode(ERRCODE_DATATYPE_MISMATCH),
18173 errmsg("table is missing column \"%s\"",
18174 type_attname)));
18175 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18176 table_attno++;
18177 } while (table_attr->attisdropped);
18178 table_attname = NameStr(table_attr->attname);
18179
18180 /* Compare name. */
18181 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18182 ereport(ERROR,
18183 (errcode(ERRCODE_DATATYPE_MISMATCH),
18184 errmsg("table has column \"%s\" where type requires \"%s\"",
18185 table_attname, type_attname)));
18186
18187 /* Compare type. */
18188 if (table_attr->atttypid != type_attr->atttypid ||
18189 table_attr->atttypmod != type_attr->atttypmod ||
18190 table_attr->attcollation != type_attr->attcollation)
18191 ereport(ERROR,
18192 (errcode(ERRCODE_DATATYPE_MISMATCH),
18193 errmsg("table \"%s\" has different type for column \"%s\"",
18194 RelationGetRelationName(rel), type_attname)));
18195 }
18196 ReleaseTupleDesc(typeTupleDesc);
18197
18198 /* Any remaining columns at the end of the table had better be dropped. */
18199 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18200 {
18201 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18202 table_attno - 1);
18203
18204 if (!table_attr->attisdropped)
18205 ereport(ERROR,
18206 (errcode(ERRCODE_DATATYPE_MISMATCH),
18207 errmsg("table has extra column \"%s\"",
18208 NameStr(table_attr->attname))));
18209 }
18210
18211 /* If the table was already typed, drop the existing dependency. */
18212 if (rel->rd_rel->reloftype)
18213 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18215
18216 /* Record a dependency on the new type. */
18217 tableobj.classId = RelationRelationId;
18218 tableobj.objectId = relid;
18219 tableobj.objectSubId = 0;
18220 typeobj.classId = TypeRelationId;
18221 typeobj.objectId = typeid;
18222 typeobj.objectSubId = 0;
18223 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18224
18225 /* Update pg_class.reloftype */
18226 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18227 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18228 if (!HeapTupleIsValid(classtuple))
18229 elog(ERROR, "cache lookup failed for relation %u", relid);
18230 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18231 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18232
18233 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18234
18235 heap_freetuple(classtuple);
18236 table_close(relationRelation, RowExclusiveLock);
18237
18238 ReleaseSysCache(typetuple);
18239
18240 return typeobj;
18241}
18242
18243/*
18244 * ALTER TABLE NOT OF
18245 *
18246 * Detach a typed table from its originating type. Just clear reloftype and
18247 * remove the dependency.
18248 */
18249static void
18251{
18252 Oid relid = RelationGetRelid(rel);
18253 Relation relationRelation;
18254 HeapTuple tuple;
18255
18256 if (!OidIsValid(rel->rd_rel->reloftype))
18257 ereport(ERROR,
18258 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18259 errmsg("\"%s\" is not a typed table",
18261
18262 /*
18263 * We don't bother to check ownership of the type --- ownership of the
18264 * table is presumed enough rights. No lock required on the type, either.
18265 */
18266
18267 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18269
18270 /* Clear pg_class.reloftype */
18271 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18272 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18273 if (!HeapTupleIsValid(tuple))
18274 elog(ERROR, "cache lookup failed for relation %u", relid);
18275 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18276 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18277
18278 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18279
18280 heap_freetuple(tuple);
18281 table_close(relationRelation, RowExclusiveLock);
18282}
18283
18284/*
18285 * relation_mark_replica_identity: Update a table's replica identity
18286 *
18287 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18288 * index. Otherwise, it must be InvalidOid.
18289 *
18290 * Caller had better hold an exclusive lock on the relation, as the results
18291 * of running two of these concurrently wouldn't be pretty.
18292 */
18293static void
18294relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18295 bool is_internal)
18296{
18297 Relation pg_index;
18298 Relation pg_class;
18299 HeapTuple pg_class_tuple;
18300 HeapTuple pg_index_tuple;
18301 Form_pg_class pg_class_form;
18302 Form_pg_index pg_index_form;
18303 ListCell *index;
18304
18305 /*
18306 * Check whether relreplident has changed, and update it if so.
18307 */
18308 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18309 pg_class_tuple = SearchSysCacheCopy1(RELOID,
18311 if (!HeapTupleIsValid(pg_class_tuple))
18312 elog(ERROR, "cache lookup failed for relation \"%s\"",
18314 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18315 if (pg_class_form->relreplident != ri_type)
18316 {
18317 pg_class_form->relreplident = ri_type;
18318 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18319 }
18320 table_close(pg_class, RowExclusiveLock);
18321 heap_freetuple(pg_class_tuple);
18322
18323 /*
18324 * Update the per-index indisreplident flags correctly.
18325 */
18326 pg_index = table_open(IndexRelationId, RowExclusiveLock);
18327 foreach(index, RelationGetIndexList(rel))
18328 {
18329 Oid thisIndexOid = lfirst_oid(index);
18330 bool dirty = false;
18331
18332 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18333 ObjectIdGetDatum(thisIndexOid));
18334 if (!HeapTupleIsValid(pg_index_tuple))
18335 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18336 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18337
18338 if (thisIndexOid == indexOid)
18339 {
18340 /* Set the bit if not already set. */
18341 if (!pg_index_form->indisreplident)
18342 {
18343 dirty = true;
18344 pg_index_form->indisreplident = true;
18345 }
18346 }
18347 else
18348 {
18349 /* Unset the bit if set. */
18350 if (pg_index_form->indisreplident)
18351 {
18352 dirty = true;
18353 pg_index_form->indisreplident = false;
18354 }
18355 }
18356
18357 if (dirty)
18358 {
18359 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18360 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18361 InvalidOid, is_internal);
18362
18363 /*
18364 * Invalidate the relcache for the table, so that after we commit
18365 * all sessions will refresh the table's replica identity index
18366 * before attempting any UPDATE or DELETE on the table. (If we
18367 * changed the table's pg_class row above, then a relcache inval
18368 * is already queued due to that; but we might not have.)
18369 */
18371 }
18372 heap_freetuple(pg_index_tuple);
18373 }
18374
18375 table_close(pg_index, RowExclusiveLock);
18376}
18377
18378/*
18379 * ALTER TABLE <name> REPLICA IDENTITY ...
18380 */
18381static void
18383{
18384 Oid indexOid;
18385 Relation indexRel;
18386 int key;
18387
18388 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18389 {
18390 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18391 return;
18392 }
18393 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18394 {
18395 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18396 return;
18397 }
18398 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18399 {
18400 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18401 return;
18402 }
18403 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18404 {
18405 /* fallthrough */ ;
18406 }
18407 else
18408 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18409
18410 /* Check that the index exists */
18411 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18412 if (!OidIsValid(indexOid))
18413 ereport(ERROR,
18414 (errcode(ERRCODE_UNDEFINED_OBJECT),
18415 errmsg("index \"%s\" for table \"%s\" does not exist",
18416 stmt->name, RelationGetRelationName(rel))));
18417
18418 indexRel = index_open(indexOid, ShareLock);
18419
18420 /* Check that the index is on the relation we're altering. */
18421 if (indexRel->rd_index == NULL ||
18422 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18423 ereport(ERROR,
18424 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18425 errmsg("\"%s\" is not an index for table \"%s\"",
18426 RelationGetRelationName(indexRel),
18428
18429 /*
18430 * The AM must support uniqueness, and the index must in fact be unique.
18431 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18432 * exclusion), we can use that too.
18433 */
18434 if ((!indexRel->rd_indam->amcanunique ||
18435 !indexRel->rd_index->indisunique) &&
18436 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18437 ereport(ERROR,
18438 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18439 errmsg("cannot use non-unique index \"%s\" as replica identity",
18440 RelationGetRelationName(indexRel))));
18441 /* Deferred indexes are not guaranteed to be always unique. */
18442 if (!indexRel->rd_index->indimmediate)
18443 ereport(ERROR,
18444 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18445 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18446 RelationGetRelationName(indexRel))));
18447 /* Expression indexes aren't supported. */
18448 if (RelationGetIndexExpressions(indexRel) != NIL)
18449 ereport(ERROR,
18450 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18451 errmsg("cannot use expression index \"%s\" as replica identity",
18452 RelationGetRelationName(indexRel))));
18453 /* Predicate indexes aren't supported. */
18454 if (RelationGetIndexPredicate(indexRel) != NIL)
18455 ereport(ERROR,
18456 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18457 errmsg("cannot use partial index \"%s\" as replica identity",
18458 RelationGetRelationName(indexRel))));
18459
18460 /* Check index for nullable columns. */
18461 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18462 {
18463 int16 attno = indexRel->rd_index->indkey.values[key];
18464 Form_pg_attribute attr;
18465
18466 /*
18467 * Reject any other system columns. (Going forward, we'll disallow
18468 * indexes containing such columns in the first place, but they might
18469 * exist in older branches.)
18470 */
18471 if (attno <= 0)
18472 ereport(ERROR,
18473 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18474 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18475 RelationGetRelationName(indexRel), attno)));
18476
18477 attr = TupleDescAttr(rel->rd_att, attno - 1);
18478 if (!attr->attnotnull)
18479 ereport(ERROR,
18480 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18481 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18482 RelationGetRelationName(indexRel),
18483 NameStr(attr->attname))));
18484 }
18485
18486 /* This index is suitable for use as a replica identity. Mark it. */
18487 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18488
18489 index_close(indexRel, NoLock);
18490}
18491
18492/*
18493 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18494 */
18495static void
18497{
18498 Relation pg_class;
18499 Oid relid;
18500 HeapTuple tuple;
18501
18502 relid = RelationGetRelid(rel);
18503
18504 /* Pull the record for this relation and update it */
18505 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18506
18507 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18508
18509 if (!HeapTupleIsValid(tuple))
18510 elog(ERROR, "cache lookup failed for relation %u", relid);
18511
18512 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18513 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18514
18515 InvokeObjectPostAlterHook(RelationRelationId,
18516 RelationGetRelid(rel), 0);
18517
18518 table_close(pg_class, RowExclusiveLock);
18519 heap_freetuple(tuple);
18520}
18521
18522/*
18523 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18524 */
18525static void
18527{
18528 Relation pg_class;
18529 Oid relid;
18530 HeapTuple tuple;
18531
18532 relid = RelationGetRelid(rel);
18533
18534 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18535
18536 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18537
18538 if (!HeapTupleIsValid(tuple))
18539 elog(ERROR, "cache lookup failed for relation %u", relid);
18540
18541 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18542 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18543
18544 InvokeObjectPostAlterHook(RelationRelationId,
18545 RelationGetRelid(rel), 0);
18546
18547 table_close(pg_class, RowExclusiveLock);
18548 heap_freetuple(tuple);
18549}
18550
18551/*
18552 * ALTER FOREIGN TABLE <name> OPTIONS (...)
18553 */
18554static void
18556{
18557 Relation ftrel;
18558 ForeignServer *server;
18559 ForeignDataWrapper *fdw;
18560 HeapTuple tuple;
18561 bool isnull;
18562 Datum repl_val[Natts_pg_foreign_table];
18563 bool repl_null[Natts_pg_foreign_table];
18564 bool repl_repl[Natts_pg_foreign_table];
18565 Datum datum;
18566 Form_pg_foreign_table tableform;
18567
18568 if (options == NIL)
18569 return;
18570
18571 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18572
18573 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18574 ObjectIdGetDatum(rel->rd_id));
18575 if (!HeapTupleIsValid(tuple))
18576 ereport(ERROR,
18577 (errcode(ERRCODE_UNDEFINED_OBJECT),
18578 errmsg("foreign table \"%s\" does not exist",
18580 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18581 server = GetForeignServer(tableform->ftserver);
18582 fdw = GetForeignDataWrapper(server->fdwid);
18583
18584 memset(repl_val, 0, sizeof(repl_val));
18585 memset(repl_null, false, sizeof(repl_null));
18586 memset(repl_repl, false, sizeof(repl_repl));
18587
18588 /* Extract the current options */
18589 datum = SysCacheGetAttr(FOREIGNTABLEREL,
18590 tuple,
18591 Anum_pg_foreign_table_ftoptions,
18592 &isnull);
18593 if (isnull)
18594 datum = PointerGetDatum(NULL);
18595
18596 /* Transform the options */
18597 datum = transformGenericOptions(ForeignTableRelationId,
18598 datum,
18599 options,
18600 fdw->fdwvalidator);
18601
18602 if (PointerIsValid(DatumGetPointer(datum)))
18603 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18604 else
18605 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18606
18607 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18608
18609 /* Everything looks good - update the tuple */
18610
18611 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18612 repl_val, repl_null, repl_repl);
18613
18614 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18615
18616 /*
18617 * Invalidate relcache so that all sessions will refresh any cached plans
18618 * that might depend on the old options.
18619 */
18621
18622 InvokeObjectPostAlterHook(ForeignTableRelationId,
18623 RelationGetRelid(rel), 0);
18624
18626
18627 heap_freetuple(tuple);
18628}
18629
18630/*
18631 * ALTER TABLE ALTER COLUMN SET COMPRESSION
18632 *
18633 * Return value is the address of the modified column
18634 */
18635static ObjectAddress
18637 const char *column,
18638 Node *newValue,
18639 LOCKMODE lockmode)
18640{
18641 Relation attrel;
18642 HeapTuple tuple;
18643 Form_pg_attribute atttableform;
18645 char *compression;
18646 char cmethod;
18647 ObjectAddress address;
18648
18649 compression = strVal(newValue);
18650
18651 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18652
18653 /* copy the cache entry so we can scribble on it below */
18654 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18655 if (!HeapTupleIsValid(tuple))
18656 ereport(ERROR,
18657 (errcode(ERRCODE_UNDEFINED_COLUMN),
18658 errmsg("column \"%s\" of relation \"%s\" does not exist",
18659 column, RelationGetRelationName(rel))));
18660
18661 /* prevent them from altering a system attribute */
18662 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18663 attnum = atttableform->attnum;
18664 if (attnum <= 0)
18665 ereport(ERROR,
18666 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18667 errmsg("cannot alter system column \"%s\"", column)));
18668
18669 /*
18670 * Check that column type is compressible, then get the attribute
18671 * compression method code
18672 */
18673 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18674
18675 /* update pg_attribute entry */
18676 atttableform->attcompression = cmethod;
18677 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18678
18679 InvokeObjectPostAlterHook(RelationRelationId,
18680 RelationGetRelid(rel),
18681 attnum);
18682
18683 /*
18684 * Apply the change to indexes as well (only for simple index columns,
18685 * matching behavior of index.c ConstructTupleDescriptor()).
18686 */
18687 SetIndexStorageProperties(rel, attrel, attnum,
18688 false, 0,
18689 true, cmethod,
18690 lockmode);
18691
18692 heap_freetuple(tuple);
18693
18695
18696 /* make changes visible */
18698
18699 ObjectAddressSubSet(address, RelationRelationId,
18700 RelationGetRelid(rel), attnum);
18701 return address;
18702}
18703
18704
18705/*
18706 * Preparation phase for SET LOGGED/UNLOGGED
18707 *
18708 * This verifies that we're not trying to change a temp table. Also,
18709 * existing foreign key constraints are checked to avoid ending up with
18710 * permanent tables referencing unlogged tables.
18711 */
18712static void
18714{
18715 Relation pg_constraint;
18716 HeapTuple tuple;
18717 SysScanDesc scan;
18718 ScanKeyData skey[1];
18719
18720 /*
18721 * Disallow changing status for a temp table. Also verify whether we can
18722 * get away with doing nothing; in such cases we don't need to run the
18723 * checks below, either.
18724 */
18725 switch (rel->rd_rel->relpersistence)
18726 {
18727 case RELPERSISTENCE_TEMP:
18728 ereport(ERROR,
18729 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18730 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18732 errtable(rel)));
18733 break;
18734 case RELPERSISTENCE_PERMANENT:
18735 if (toLogged)
18736 /* nothing to do */
18737 return;
18738 break;
18739 case RELPERSISTENCE_UNLOGGED:
18740 if (!toLogged)
18741 /* nothing to do */
18742 return;
18743 break;
18744 }
18745
18746 /*
18747 * Check that the table is not part of any publication when changing to
18748 * UNLOGGED, as UNLOGGED tables can't be published.
18749 */
18750 if (!toLogged &&
18752 ereport(ERROR,
18753 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18754 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18756 errdetail("Unlogged relations cannot be replicated.")));
18757
18758 /*
18759 * Check existing foreign key constraints to preserve the invariant that
18760 * permanent tables cannot reference unlogged ones. Self-referencing
18761 * foreign keys can safely be ignored.
18762 */
18763 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18764
18765 /*
18766 * Scan conrelid if changing to permanent, else confrelid. This also
18767 * determines whether a useful index exists.
18768 */
18769 ScanKeyInit(&skey[0],
18770 toLogged ? Anum_pg_constraint_conrelid :
18771 Anum_pg_constraint_confrelid,
18772 BTEqualStrategyNumber, F_OIDEQ,
18774 scan = systable_beginscan(pg_constraint,
18775 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18776 true, NULL, 1, skey);
18777
18778 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18779 {
18781
18782 if (con->contype == CONSTRAINT_FOREIGN)
18783 {
18784 Oid foreignrelid;
18785 Relation foreignrel;
18786
18787 /* the opposite end of what we used as scankey */
18788 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18789
18790 /* ignore if self-referencing */
18791 if (RelationGetRelid(rel) == foreignrelid)
18792 continue;
18793
18794 foreignrel = relation_open(foreignrelid, AccessShareLock);
18795
18796 if (toLogged)
18797 {
18798 if (!RelationIsPermanent(foreignrel))
18799 ereport(ERROR,
18800 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18801 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18803 RelationGetRelationName(foreignrel)),
18804 errtableconstraint(rel, NameStr(con->conname))));
18805 }
18806 else
18807 {
18808 if (RelationIsPermanent(foreignrel))
18809 ereport(ERROR,
18810 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18811 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18813 RelationGetRelationName(foreignrel)),
18814 errtableconstraint(rel, NameStr(con->conname))));
18815 }
18816
18817 relation_close(foreignrel, AccessShareLock);
18818 }
18819 }
18820
18821 systable_endscan(scan);
18822
18823 table_close(pg_constraint, AccessShareLock);
18824
18825 /* force rewrite if necessary; see comment in ATRewriteTables */
18827 if (toLogged)
18828 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18829 else
18830 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18831 tab->chgPersistence = true;
18832}
18833
18834/*
18835 * Execute ALTER TABLE SET SCHEMA
18836 */
18839{
18840 Relation rel;
18841 Oid relid;
18842 Oid oldNspOid;
18843 Oid nspOid;
18844 RangeVar *newrv;
18845 ObjectAddresses *objsMoved;
18846 ObjectAddress myself;
18847
18849 stmt->missing_ok ? RVR_MISSING_OK : 0,
18851 stmt);
18852
18853 if (!OidIsValid(relid))
18854 {
18856 (errmsg("relation \"%s\" does not exist, skipping",
18857 stmt->relation->relname)));
18858 return InvalidObjectAddress;
18859 }
18860
18861 rel = relation_open(relid, NoLock);
18862
18863 oldNspOid = RelationGetNamespace(rel);
18864
18865 /* If it's an owned sequence, disallow moving it by itself. */
18866 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18867 {
18868 Oid tableId;
18869 int32 colId;
18870
18871 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18872 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18873 ereport(ERROR,
18874 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18875 errmsg("cannot move an owned sequence into another schema"),
18876 errdetail("Sequence \"%s\" is linked to table \"%s\".",
18878 get_rel_name(tableId))));
18879 }
18880
18881 /* Get and lock schema OID and check its permissions. */
18882 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18883 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18884
18885 /* common checks on switching namespaces */
18886 CheckSetNamespace(oldNspOid, nspOid);
18887
18888 objsMoved = new_object_addresses();
18889 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18890 free_object_addresses(objsMoved);
18891
18892 ObjectAddressSet(myself, RelationRelationId, relid);
18893
18894 if (oldschema)
18895 *oldschema = oldNspOid;
18896
18897 /* close rel, but keep lock until commit */
18898 relation_close(rel, NoLock);
18899
18900 return myself;
18901}
18902
18903/*
18904 * The guts of relocating a table or materialized view to another namespace:
18905 * besides moving the relation itself, its dependent objects are relocated to
18906 * the new schema.
18907 */
18908void
18910 ObjectAddresses *objsMoved)
18911{
18912 Relation classRel;
18913
18914 Assert(objsMoved != NULL);
18915
18916 /* OK, modify the pg_class row and pg_depend entry */
18917 classRel = table_open(RelationRelationId, RowExclusiveLock);
18918
18919 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18920 nspOid, true, objsMoved);
18921
18922 /* Fix the table's row type too, if it has one */
18923 if (OidIsValid(rel->rd_rel->reltype))
18924 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18925 false, /* isImplicitArray */
18926 false, /* ignoreDependent */
18927 false, /* errorOnTableType */
18928 objsMoved);
18929
18930 /* Fix other dependent stuff */
18931 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18932 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18933 objsMoved, AccessExclusiveLock);
18934 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18935 false, objsMoved);
18936
18937 table_close(classRel, RowExclusiveLock);
18938}
18939
18940/*
18941 * The guts of relocating a relation to another namespace: fix the pg_class
18942 * entry, and the pg_depend entry if any. Caller must already have
18943 * opened and write-locked pg_class.
18944 */
18945void
18947 Oid oldNspOid, Oid newNspOid,
18948 bool hasDependEntry,
18949 ObjectAddresses *objsMoved)
18950{
18951 HeapTuple classTup;
18952 Form_pg_class classForm;
18953 ObjectAddress thisobj;
18954 bool already_done = false;
18955
18956 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
18957 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
18958 if (!HeapTupleIsValid(classTup))
18959 elog(ERROR, "cache lookup failed for relation %u", relOid);
18960 classForm = (Form_pg_class) GETSTRUCT(classTup);
18961
18962 Assert(classForm->relnamespace == oldNspOid);
18963
18964 thisobj.classId = RelationRelationId;
18965 thisobj.objectId = relOid;
18966 thisobj.objectSubId = 0;
18967
18968 /*
18969 * If the object has already been moved, don't move it again. If it's
18970 * already in the right place, don't move it, but still fire the object
18971 * access hook.
18972 */
18973 already_done = object_address_present(&thisobj, objsMoved);
18974 if (!already_done && oldNspOid != newNspOid)
18975 {
18976 ItemPointerData otid = classTup->t_self;
18977
18978 /* check for duplicate name (more friendly than unique-index failure) */
18979 if (get_relname_relid(NameStr(classForm->relname),
18980 newNspOid) != InvalidOid)
18981 ereport(ERROR,
18982 (errcode(ERRCODE_DUPLICATE_TABLE),
18983 errmsg("relation \"%s\" already exists in schema \"%s\"",
18984 NameStr(classForm->relname),
18985 get_namespace_name(newNspOid))));
18986
18987 /* classTup is a copy, so OK to scribble on */
18988 classForm->relnamespace = newNspOid;
18989
18990 CatalogTupleUpdate(classRel, &otid, classTup);
18991 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
18992
18993
18994 /* Update dependency on schema if caller said so */
18995 if (hasDependEntry &&
18996 changeDependencyFor(RelationRelationId,
18997 relOid,
18998 NamespaceRelationId,
18999 oldNspOid,
19000 newNspOid) != 1)
19001 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19002 NameStr(classForm->relname));
19003 }
19004 else
19005 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19006 if (!already_done)
19007 {
19008 add_exact_object_address(&thisobj, objsMoved);
19009
19010 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19011 }
19012
19013 heap_freetuple(classTup);
19014}
19015
19016/*
19017 * Move all indexes for the specified relation to another namespace.
19018 *
19019 * Note: we assume adequate permission checking was done by the caller,
19020 * and that the caller has a suitable lock on the owning relation.
19021 */
19022static void
19024 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19025{
19026 List *indexList;
19027 ListCell *l;
19028
19029 indexList = RelationGetIndexList(rel);
19030
19031 foreach(l, indexList)
19032 {
19033 Oid indexOid = lfirst_oid(l);
19034 ObjectAddress thisobj;
19035
19036 thisobj.classId = RelationRelationId;
19037 thisobj.objectId = indexOid;
19038 thisobj.objectSubId = 0;
19039
19040 /*
19041 * Note: currently, the index will not have its own dependency on the
19042 * namespace, so we don't need to do changeDependencyFor(). There's no
19043 * row type in pg_type, either.
19044 *
19045 * XXX this objsMoved test may be pointless -- surely we have a single
19046 * dependency link from a relation to each index?
19047 */
19048 if (!object_address_present(&thisobj, objsMoved))
19049 {
19050 AlterRelationNamespaceInternal(classRel, indexOid,
19051 oldNspOid, newNspOid,
19052 false, objsMoved);
19053 add_exact_object_address(&thisobj, objsMoved);
19054 }
19055 }
19056
19057 list_free(indexList);
19058}
19059
19060/*
19061 * Move all identity and SERIAL-column sequences of the specified relation to another
19062 * namespace.
19063 *
19064 * Note: we assume adequate permission checking was done by the caller,
19065 * and that the caller has a suitable lock on the owning relation.
19066 */
19067static void
19069 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19070 LOCKMODE lockmode)
19071{
19072 Relation depRel;
19073 SysScanDesc scan;
19074 ScanKeyData key[2];
19075 HeapTuple tup;
19076
19077 /*
19078 * SERIAL sequences are those having an auto dependency on one of the
19079 * table's columns (we don't care *which* column, exactly).
19080 */
19081 depRel = table_open(DependRelationId, AccessShareLock);
19082
19083 ScanKeyInit(&key[0],
19084 Anum_pg_depend_refclassid,
19085 BTEqualStrategyNumber, F_OIDEQ,
19086 ObjectIdGetDatum(RelationRelationId));
19087 ScanKeyInit(&key[1],
19088 Anum_pg_depend_refobjid,
19089 BTEqualStrategyNumber, F_OIDEQ,
19091 /* we leave refobjsubid unspecified */
19092
19093 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19094 NULL, 2, key);
19095
19096 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19097 {
19098 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19099 Relation seqRel;
19100
19101 /* skip dependencies other than auto dependencies on columns */
19102 if (depForm->refobjsubid == 0 ||
19103 depForm->classid != RelationRelationId ||
19104 depForm->objsubid != 0 ||
19105 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19106 continue;
19107
19108 /* Use relation_open just in case it's an index */
19109 seqRel = relation_open(depForm->objid, lockmode);
19110
19111 /* skip non-sequence relations */
19112 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19113 {
19114 /* No need to keep the lock */
19115 relation_close(seqRel, lockmode);
19116 continue;
19117 }
19118
19119 /* Fix the pg_class and pg_depend entries */
19120 AlterRelationNamespaceInternal(classRel, depForm->objid,
19121 oldNspOid, newNspOid,
19122 true, objsMoved);
19123
19124 /*
19125 * Sequences used to have entries in pg_type, but no longer do. If we
19126 * ever re-instate that, we'll need to move the pg_type entry to the
19127 * new namespace, too (using AlterTypeNamespaceInternal).
19128 */
19129 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19130
19131 /* Now we can close it. Keep the lock till end of transaction. */
19132 relation_close(seqRel, NoLock);
19133 }
19134
19135 systable_endscan(scan);
19136
19138}
19139
19140
19141/*
19142 * This code supports
19143 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19144 *
19145 * Because we only support this for TEMP tables, it's sufficient to remember
19146 * the state in a backend-local data structure.
19147 */
19148
19149/*
19150 * Register a newly-created relation's ON COMMIT action.
19151 */
19152void
19154{
19155 OnCommitItem *oc;
19156 MemoryContext oldcxt;
19157
19158 /*
19159 * We needn't bother registering the relation unless there is an ON COMMIT
19160 * action we need to take.
19161 */
19163 return;
19164
19166
19167 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19168 oc->relid = relid;
19169 oc->oncommit = action;
19172
19173 /*
19174 * We use lcons() here so that ON COMMIT actions are processed in reverse
19175 * order of registration. That might not be essential but it seems
19176 * reasonable.
19177 */
19179
19180 MemoryContextSwitchTo(oldcxt);
19181}
19182
19183/*
19184 * Unregister any ON COMMIT action when a relation is deleted.
19185 *
19186 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19187 */
19188void
19190{
19191 ListCell *l;
19192
19193 foreach(l, on_commits)
19194 {
19195 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19196
19197 if (oc->relid == relid)
19198 {
19200 break;
19201 }
19202 }
19203}
19204
19205/*
19206 * Perform ON COMMIT actions.
19207 *
19208 * This is invoked just before actually committing, since it's possible
19209 * to encounter errors.
19210 */
19211void
19213{
19214 ListCell *l;
19215 List *oids_to_truncate = NIL;
19216 List *oids_to_drop = NIL;
19217
19218 foreach(l, on_commits)
19219 {
19220 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19221
19222 /* Ignore entry if already dropped in this xact */
19224 continue;
19225
19226 switch (oc->oncommit)
19227 {
19228 case ONCOMMIT_NOOP:
19230 /* Do nothing (there shouldn't be such entries, actually) */
19231 break;
19233
19234 /*
19235 * If this transaction hasn't accessed any temporary
19236 * relations, we can skip truncating ON COMMIT DELETE ROWS
19237 * tables, as they must still be empty.
19238 */
19240 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19241 break;
19242 case ONCOMMIT_DROP:
19243 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19244 break;
19245 }
19246 }
19247
19248 /*
19249 * Truncate relations before dropping so that all dependencies between
19250 * relations are removed after they are worked on. Doing it like this
19251 * might be a waste as it is possible that a relation being truncated will
19252 * be dropped anyway due to its parent being dropped, but this makes the
19253 * code more robust because of not having to re-check that the relation
19254 * exists at truncation time.
19255 */
19256 if (oids_to_truncate != NIL)
19257 heap_truncate(oids_to_truncate);
19258
19259 if (oids_to_drop != NIL)
19260 {
19261 ObjectAddresses *targetObjects = new_object_addresses();
19262
19263 foreach(l, oids_to_drop)
19264 {
19265 ObjectAddress object;
19266
19267 object.classId = RelationRelationId;
19268 object.objectId = lfirst_oid(l);
19269 object.objectSubId = 0;
19270
19271 Assert(!object_address_present(&object, targetObjects));
19272
19273 add_exact_object_address(&object, targetObjects);
19274 }
19275
19276 /*
19277 * Object deletion might involve toast table access (to clean up
19278 * toasted catalog entries), so ensure we have a valid snapshot.
19279 */
19281
19282 /*
19283 * Since this is an automatic drop, rather than one directly initiated
19284 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19285 */
19288
19290
19291#ifdef USE_ASSERT_CHECKING
19292
19293 /*
19294 * Note that table deletion will call remove_on_commit_action, so the
19295 * entry should get marked as deleted.
19296 */
19297 foreach(l, on_commits)
19298 {
19299 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19300
19301 if (oc->oncommit != ONCOMMIT_DROP)
19302 continue;
19303
19305 }
19306#endif
19307 }
19308}
19309
19310/*
19311 * Post-commit or post-abort cleanup for ON COMMIT management.
19312 *
19313 * All we do here is remove no-longer-needed OnCommitItem entries.
19314 *
19315 * During commit, remove entries that were deleted during this transaction;
19316 * during abort, remove those created during this transaction.
19317 */
19318void
19320{
19321 ListCell *cur_item;
19322
19323 foreach(cur_item, on_commits)
19324 {
19325 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19326
19327 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19329 {
19330 /* cur_item must be removed */
19332 pfree(oc);
19333 }
19334 else
19335 {
19336 /* cur_item must be preserved */
19339 }
19340 }
19341}
19342
19343/*
19344 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19345 *
19346 * During subabort, we can immediately remove entries created during this
19347 * subtransaction. During subcommit, just relabel entries marked during
19348 * this subtransaction as being the parent's responsibility.
19349 */
19350void
19352 SubTransactionId parentSubid)
19353{
19354 ListCell *cur_item;
19355
19356 foreach(cur_item, on_commits)
19357 {
19358 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19359
19360 if (!isCommit && oc->creating_subid == mySubid)
19361 {
19362 /* cur_item must be removed */
19364 pfree(oc);
19365 }
19366 else
19367 {
19368 /* cur_item must be preserved */
19369 if (oc->creating_subid == mySubid)
19370 oc->creating_subid = parentSubid;
19371 if (oc->deleting_subid == mySubid)
19372 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19373 }
19374 }
19375}
19376
19377/*
19378 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19379 * the relation to be locked only if (1) it's a plain or partitioned table,
19380 * materialized view, or TOAST table and (2) the current user is the owner (or
19381 * the superuser) or has been granted MAINTAIN. This meets the
19382 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19383 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19384 */
19385void
19387 Oid relId, Oid oldRelId, void *arg)
19388{
19389 char relkind;
19390 AclResult aclresult;
19391
19392 /* Nothing to do if the relation was not found. */
19393 if (!OidIsValid(relId))
19394 return;
19395
19396 /*
19397 * If the relation does exist, check whether it's an index. But note that
19398 * the relation might have been dropped between the time we did the name
19399 * lookup and now. In that case, there's nothing to do.
19400 */
19401 relkind = get_rel_relkind(relId);
19402 if (!relkind)
19403 return;
19404 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19405 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19406 ereport(ERROR,
19407 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19408 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19409
19410 /* Check permissions */
19411 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19412 if (aclresult != ACLCHECK_OK)
19413 aclcheck_error(aclresult,
19415 relation->relname);
19416}
19417
19418/*
19419 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19420 */
19421static void
19423 Oid relId, Oid oldRelId, void *arg)
19424{
19425 HeapTuple tuple;
19426
19427 /* Nothing to do if the relation was not found. */
19428 if (!OidIsValid(relId))
19429 return;
19430
19431 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19432 if (!HeapTupleIsValid(tuple)) /* should not happen */
19433 elog(ERROR, "cache lookup failed for relation %u", relId);
19434
19437
19438 ReleaseSysCache(tuple);
19439}
19440
19441/*
19442 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19443 * the owner of the relation, or superuser.
19444 */
19445void
19447 Oid relId, Oid oldRelId, void *arg)
19448{
19449 HeapTuple tuple;
19450
19451 /* Nothing to do if the relation was not found. */
19452 if (!OidIsValid(relId))
19453 return;
19454
19455 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19456 if (!HeapTupleIsValid(tuple)) /* should not happen */
19457 elog(ERROR, "cache lookup failed for relation %u", relId);
19458
19459 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19461 relation->relname);
19462
19463 if (!allowSystemTableMods &&
19464 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19465 ereport(ERROR,
19466 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19467 errmsg("permission denied: \"%s\" is a system catalog",
19468 relation->relname)));
19469
19470 ReleaseSysCache(tuple);
19471}
19472
19473/*
19474 * Common RangeVarGetRelid callback for rename, set schema, and alter table
19475 * processing.
19476 */
19477static void
19479 void *arg)
19480{
19481 Node *stmt = (Node *) arg;
19482 ObjectType reltype;
19483 HeapTuple tuple;
19484 Form_pg_class classform;
19485 AclResult aclresult;
19486 char relkind;
19487
19488 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19489 if (!HeapTupleIsValid(tuple))
19490 return; /* concurrently dropped */
19491 classform = (Form_pg_class) GETSTRUCT(tuple);
19492 relkind = classform->relkind;
19493
19494 /* Must own relation. */
19495 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19497
19498 /* No system table modifications unless explicitly allowed. */
19499 if (!allowSystemTableMods && IsSystemClass(relid, classform))
19500 ereport(ERROR,
19501 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19502 errmsg("permission denied: \"%s\" is a system catalog",
19503 rv->relname)));
19504
19505 /*
19506 * Extract the specified relation type from the statement parse tree.
19507 *
19508 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19509 * have CREATE rights on the containing namespace.
19510 */
19511 if (IsA(stmt, RenameStmt))
19512 {
19513 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19515 if (aclresult != ACLCHECK_OK)
19516 aclcheck_error(aclresult, OBJECT_SCHEMA,
19517 get_namespace_name(classform->relnamespace));
19518 reltype = ((RenameStmt *) stmt)->renameType;
19519 }
19520 else if (IsA(stmt, AlterObjectSchemaStmt))
19521 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19522
19523 else if (IsA(stmt, AlterTableStmt))
19524 reltype = ((AlterTableStmt *) stmt)->objtype;
19525 else
19526 {
19527 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19528 reltype = OBJECT_TABLE; /* placate compiler */
19529 }
19530
19531 /*
19532 * For compatibility with prior releases, we allow ALTER TABLE to be used
19533 * with most other types of relations (but not composite types). We allow
19534 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19535 * otherwise. Otherwise, the user must select the correct form of the
19536 * command for the relation at issue.
19537 */
19538 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19539 ereport(ERROR,
19540 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19541 errmsg("\"%s\" is not a sequence", rv->relname)));
19542
19543 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19544 ereport(ERROR,
19545 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19546 errmsg("\"%s\" is not a view", rv->relname)));
19547
19548 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19549 ereport(ERROR,
19550 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19551 errmsg("\"%s\" is not a materialized view", rv->relname)));
19552
19553 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19554 ereport(ERROR,
19555 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19556 errmsg("\"%s\" is not a foreign table", rv->relname)));
19557
19558 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19559 ereport(ERROR,
19560 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19561 errmsg("\"%s\" is not a composite type", rv->relname)));
19562
19563 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19564 relkind != RELKIND_PARTITIONED_INDEX
19565 && !IsA(stmt, RenameStmt))
19566 ereport(ERROR,
19567 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19568 errmsg("\"%s\" is not an index", rv->relname)));
19569
19570 /*
19571 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19572 * TYPE for that.
19573 */
19574 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19575 ereport(ERROR,
19576 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19577 errmsg("\"%s\" is a composite type", rv->relname),
19578 /* translator: %s is an SQL ALTER command */
19579 errhint("Use %s instead.",
19580 "ALTER TYPE")));
19581
19582 /*
19583 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19584 * to a different schema, such as indexes and TOAST tables.
19585 */
19587 {
19588 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19589 ereport(ERROR,
19590 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19591 errmsg("cannot change schema of index \"%s\"",
19592 rv->relname),
19593 errhint("Change the schema of the table instead.")));
19594 else if (relkind == RELKIND_COMPOSITE_TYPE)
19595 ereport(ERROR,
19596 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19597 errmsg("cannot change schema of composite type \"%s\"",
19598 rv->relname),
19599 /* translator: %s is an SQL ALTER command */
19600 errhint("Use %s instead.",
19601 "ALTER TYPE")));
19602 else if (relkind == RELKIND_TOASTVALUE)
19603 ereport(ERROR,
19604 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19605 errmsg("cannot change schema of TOAST table \"%s\"",
19606 rv->relname),
19607 errhint("Change the schema of the table instead.")));
19608 }
19609
19610 ReleaseSysCache(tuple);
19611}
19612
19613/*
19614 * Transform any expressions present in the partition key
19615 *
19616 * Returns a transformed PartitionSpec.
19617 */
19618static PartitionSpec *
19620{
19621 PartitionSpec *newspec;
19622 ParseState *pstate;
19623 ParseNamespaceItem *nsitem;
19624 ListCell *l;
19625
19626 newspec = makeNode(PartitionSpec);
19627
19628 newspec->strategy = partspec->strategy;
19629 newspec->partParams = NIL;
19630 newspec->location = partspec->location;
19631
19632 /* Check valid number of columns for strategy */
19633 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19634 list_length(partspec->partParams) != 1)
19635 ereport(ERROR,
19636 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19637 errmsg("cannot use \"list\" partition strategy with more than one column")));
19638
19639 /*
19640 * Create a dummy ParseState and insert the target relation as its sole
19641 * rangetable entry. We need a ParseState for transformExpr.
19642 */
19643 pstate = make_parsestate(NULL);
19644 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19645 NULL, false, true);
19646 addNSItemToQuery(pstate, nsitem, true, true, true);
19647
19648 /* take care of any partition expressions */
19649 foreach(l, partspec->partParams)
19650 {
19652
19653 if (pelem->expr)
19654 {
19655 /* Copy, to avoid scribbling on the input */
19656 pelem = copyObject(pelem);
19657
19658 /* Now do parse transformation of the expression */
19659 pelem->expr = transformExpr(pstate, pelem->expr,
19661
19662 /* we have to fix its collations too */
19663 assign_expr_collations(pstate, pelem->expr);
19664 }
19665
19666 newspec->partParams = lappend(newspec->partParams, pelem);
19667 }
19668
19669 return newspec;
19670}
19671
19672/*
19673 * Compute per-partition-column information from a list of PartitionElems.
19674 * Expressions in the PartitionElems must be parse-analyzed already.
19675 */
19676static void
19677ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19678 List **partexprs, Oid *partopclass, Oid *partcollation,
19679 PartitionStrategy strategy)
19680{
19681 int attn;
19682 ListCell *lc;
19683 Oid am_oid;
19684
19685 attn = 0;
19686 foreach(lc, partParams)
19687 {
19689 Oid atttype;
19690 Oid attcollation;
19691
19692 if (pelem->name != NULL)
19693 {
19694 /* Simple attribute reference */
19695 HeapTuple atttuple;
19696 Form_pg_attribute attform;
19697
19699 pelem->name);
19700 if (!HeapTupleIsValid(atttuple))
19701 ereport(ERROR,
19702 (errcode(ERRCODE_UNDEFINED_COLUMN),
19703 errmsg("column \"%s\" named in partition key does not exist",
19704 pelem->name),
19705 parser_errposition(pstate, pelem->location)));
19706 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19707
19708 if (attform->attnum <= 0)
19709 ereport(ERROR,
19710 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19711 errmsg("cannot use system column \"%s\" in partition key",
19712 pelem->name),
19713 parser_errposition(pstate, pelem->location)));
19714
19715 /*
19716 * Stored generated columns cannot work: They are computed after
19717 * BEFORE triggers, but partition routing is done before all
19718 * triggers. Maybe virtual generated columns could be made to
19719 * work, but then they would need to be handled as an expression
19720 * below.
19721 */
19722 if (attform->attgenerated)
19723 ereport(ERROR,
19724 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19725 errmsg("cannot use generated column in partition key"),
19726 errdetail("Column \"%s\" is a generated column.",
19727 pelem->name),
19728 parser_errposition(pstate, pelem->location)));
19729
19730 partattrs[attn] = attform->attnum;
19731 atttype = attform->atttypid;
19732 attcollation = attform->attcollation;
19733 ReleaseSysCache(atttuple);
19734 }
19735 else
19736 {
19737 /* Expression */
19738 Node *expr = pelem->expr;
19739 char partattname[16];
19740
19741 Assert(expr != NULL);
19742 atttype = exprType(expr);
19743 attcollation = exprCollation(expr);
19744
19745 /*
19746 * The expression must be of a storable type (e.g., not RECORD).
19747 * The test is the same as for whether a table column is of a safe
19748 * type (which is why we needn't check for the non-expression
19749 * case).
19750 */
19751 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19752 CheckAttributeType(partattname,
19753 atttype, attcollation,
19755
19756 /*
19757 * Strip any top-level COLLATE clause. This ensures that we treat
19758 * "x COLLATE y" and "(x COLLATE y)" alike.
19759 */
19760 while (IsA(expr, CollateExpr))
19761 expr = (Node *) ((CollateExpr *) expr)->arg;
19762
19763 if (IsA(expr, Var) &&
19764 ((Var *) expr)->varattno > 0)
19765 {
19766 /*
19767 * User wrote "(column)" or "(column COLLATE something)".
19768 * Treat it like simple attribute anyway.
19769 */
19770 partattrs[attn] = ((Var *) expr)->varattno;
19771 }
19772 else
19773 {
19774 Bitmapset *expr_attrs = NULL;
19775 int i;
19776
19777 partattrs[attn] = 0; /* marks the column as expression */
19778 *partexprs = lappend(*partexprs, expr);
19779
19780 /*
19781 * transformPartitionSpec() should have already rejected
19782 * subqueries, aggregates, window functions, and SRFs, based
19783 * on the EXPR_KIND_ for partition expressions.
19784 */
19785
19786 /*
19787 * Cannot allow system column references, since that would
19788 * make partition routing impossible: their values won't be
19789 * known yet when we need to do that.
19790 */
19791 pull_varattnos(expr, 1, &expr_attrs);
19792 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
19793 {
19795 expr_attrs))
19796 ereport(ERROR,
19797 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19798 errmsg("partition key expressions cannot contain system column references")));
19799 }
19800
19801 /*
19802 * Stored generated columns cannot work: They are computed
19803 * after BEFORE triggers, but partition routing is done before
19804 * all triggers. Virtual generated columns could probably
19805 * work, but it would require more work elsewhere (for example
19806 * SET EXPRESSION would need to check whether the column is
19807 * used in partition keys). Seems safer to prohibit for now.
19808 */
19809 i = -1;
19810 while ((i = bms_next_member(expr_attrs, i)) >= 0)
19811 {
19813
19814 if (attno > 0 &&
19815 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19816 ereport(ERROR,
19817 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19818 errmsg("cannot use generated column in partition key"),
19819 errdetail("Column \"%s\" is a generated column.",
19820 get_attname(RelationGetRelid(rel), attno, false)),
19821 parser_errposition(pstate, pelem->location)));
19822 }
19823
19824 /*
19825 * Preprocess the expression before checking for mutability.
19826 * This is essential for the reasons described in
19827 * contain_mutable_functions_after_planning. However, we call
19828 * expression_planner for ourselves rather than using that
19829 * function, because if constant-folding reduces the
19830 * expression to a constant, we'd like to know that so we can
19831 * complain below.
19832 *
19833 * Like contain_mutable_functions_after_planning, assume that
19834 * expression_planner won't scribble on its input, so this
19835 * won't affect the partexprs entry we saved above.
19836 */
19837 expr = (Node *) expression_planner((Expr *) expr);
19838
19839 /*
19840 * Partition expressions cannot contain mutable functions,
19841 * because a given row must always map to the same partition
19842 * as long as there is no change in the partition boundary
19843 * structure.
19844 */
19845 if (contain_mutable_functions(expr))
19846 ereport(ERROR,
19847 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19848 errmsg("functions in partition key expression must be marked IMMUTABLE")));
19849
19850 /*
19851 * While it is not exactly *wrong* for a partition expression
19852 * to be a constant, it seems better to reject such keys.
19853 */
19854 if (IsA(expr, Const))
19855 ereport(ERROR,
19856 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19857 errmsg("cannot use constant expression as partition key")));
19858 }
19859 }
19860
19861 /*
19862 * Apply collation override if any
19863 */
19864 if (pelem->collation)
19865 attcollation = get_collation_oid(pelem->collation, false);
19866
19867 /*
19868 * Check we have a collation iff it's a collatable type. The only
19869 * expected failures here are (1) COLLATE applied to a noncollatable
19870 * type, or (2) partition expression had an unresolved collation. But
19871 * we might as well code this to be a complete consistency check.
19872 */
19873 if (type_is_collatable(atttype))
19874 {
19875 if (!OidIsValid(attcollation))
19876 ereport(ERROR,
19877 (errcode(ERRCODE_INDETERMINATE_COLLATION),
19878 errmsg("could not determine which collation to use for partition expression"),
19879 errhint("Use the COLLATE clause to set the collation explicitly.")));
19880 }
19881 else
19882 {
19883 if (OidIsValid(attcollation))
19884 ereport(ERROR,
19885 (errcode(ERRCODE_DATATYPE_MISMATCH),
19886 errmsg("collations are not supported by type %s",
19887 format_type_be(atttype))));
19888 }
19889
19890 partcollation[attn] = attcollation;
19891
19892 /*
19893 * Identify the appropriate operator class. For list and range
19894 * partitioning, we use a btree operator class; hash partitioning uses
19895 * a hash operator class.
19896 */
19897 if (strategy == PARTITION_STRATEGY_HASH)
19898 am_oid = HASH_AM_OID;
19899 else
19900 am_oid = BTREE_AM_OID;
19901
19902 if (!pelem->opclass)
19903 {
19904 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19905
19906 if (!OidIsValid(partopclass[attn]))
19907 {
19908 if (strategy == PARTITION_STRATEGY_HASH)
19909 ereport(ERROR,
19910 (errcode(ERRCODE_UNDEFINED_OBJECT),
19911 errmsg("data type %s has no default operator class for access method \"%s\"",
19912 format_type_be(atttype), "hash"),
19913 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19914 else
19915 ereport(ERROR,
19916 (errcode(ERRCODE_UNDEFINED_OBJECT),
19917 errmsg("data type %s has no default operator class for access method \"%s\"",
19918 format_type_be(atttype), "btree"),
19919 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19920 }
19921 }
19922 else
19923 partopclass[attn] = ResolveOpClass(pelem->opclass,
19924 atttype,
19925 am_oid == HASH_AM_OID ? "hash" : "btree",
19926 am_oid);
19927
19928 attn++;
19929 }
19930}
19931
19932/*
19933 * PartConstraintImpliedByRelConstraint
19934 * Do scanrel's existing constraints imply the partition constraint?
19935 *
19936 * "Existing constraints" include its check constraints and column-level
19937 * not-null constraints. partConstraint describes the partition constraint,
19938 * in implicit-AND form.
19939 */
19940bool
19942 List *partConstraint)
19943{
19944 List *existConstraint = NIL;
19945 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19946 int i;
19947
19948 if (constr && constr->has_not_null)
19949 {
19950 int natts = scanrel->rd_att->natts;
19951
19952 for (i = 1; i <= natts; i++)
19953 {
19954 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
19955
19956 /* invalid not-null constraint must be ignored here */
19957 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
19958 {
19959 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
19960 NullTest *ntest = makeNode(NullTest);
19961
19962 ntest->arg = (Expr *) makeVar(1,
19963 i,
19964 wholeatt->atttypid,
19965 wholeatt->atttypmod,
19966 wholeatt->attcollation,
19967 0);
19968 ntest->nulltesttype = IS_NOT_NULL;
19969
19970 /*
19971 * argisrow=false is correct even for a composite column,
19972 * because attnotnull does not represent a SQL-spec IS NOT
19973 * NULL test in such a case, just IS DISTINCT FROM NULL.
19974 */
19975 ntest->argisrow = false;
19976 ntest->location = -1;
19977 existConstraint = lappend(existConstraint, ntest);
19978 }
19979 }
19980 }
19981
19982 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
19983}
19984
19985/*
19986 * ConstraintImpliedByRelConstraint
19987 * Do scanrel's existing constraints imply the given constraint?
19988 *
19989 * testConstraint is the constraint to validate. provenConstraint is a
19990 * caller-provided list of conditions which this function may assume
19991 * to be true. Both provenConstraint and testConstraint must be in
19992 * implicit-AND form, must only contain immutable clauses, and must
19993 * contain only Vars with varno = 1.
19994 */
19995bool
19996ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
19997{
19998 List *existConstraint = list_copy(provenConstraint);
19999 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20000 int num_check,
20001 i;
20002
20003 num_check = (constr != NULL) ? constr->num_check : 0;
20004 for (i = 0; i < num_check; i++)
20005 {
20006 Node *cexpr;
20007
20008 /*
20009 * If this constraint hasn't been fully validated yet, we must ignore
20010 * it here.
20011 */
20012 if (!constr->check[i].ccvalid)
20013 continue;
20014
20015 /*
20016 * NOT ENFORCED constraints are always marked as invalid, which should
20017 * have been ignored.
20018 */
20019 Assert(constr->check[i].ccenforced);
20020
20021 cexpr = stringToNode(constr->check[i].ccbin);
20022
20023 /*
20024 * Run each expression through const-simplification and
20025 * canonicalization. It is necessary, because we will be comparing it
20026 * to similarly-processed partition constraint expressions, and may
20027 * fail to detect valid matches without this.
20028 */
20029 cexpr = eval_const_expressions(NULL, cexpr);
20030 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20031
20032 existConstraint = list_concat(existConstraint,
20033 make_ands_implicit((Expr *) cexpr));
20034 }
20035
20036 /*
20037 * Try to make the proof. Since we are comparing CHECK constraints, we
20038 * need to use weak implication, i.e., we assume existConstraint is
20039 * not-false and try to prove the same for testConstraint.
20040 *
20041 * Note that predicate_implied_by assumes its first argument is known
20042 * immutable. That should always be true for both NOT NULL and partition
20043 * constraints, so we don't test it here.
20044 */
20045 return predicate_implied_by(testConstraint, existConstraint, true);
20046}
20047
20048/*
20049 * QueuePartitionConstraintValidation
20050 *
20051 * Add an entry to wqueue to have the given partition constraint validated by
20052 * Phase 3, for the given relation, and all its children.
20053 *
20054 * We first verify whether the given constraint is implied by pre-existing
20055 * relation constraints; if it is, there's no need to scan the table to
20056 * validate, so don't queue in that case.
20057 */
20058static void
20060 List *partConstraint,
20061 bool validate_default)
20062{
20063 /*
20064 * Based on the table's existing constraints, determine whether or not we
20065 * may skip scanning the table.
20066 */
20067 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20068 {
20069 if (!validate_default)
20071 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20072 RelationGetRelationName(scanrel))));
20073 else
20075 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20076 RelationGetRelationName(scanrel))));
20077 return;
20078 }
20079
20080 /*
20081 * Constraints proved insufficient. For plain relations, queue a
20082 * validation item now; for partitioned tables, recurse to process each
20083 * partition.
20084 */
20085 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20086 {
20087 AlteredTableInfo *tab;
20088
20089 /* Grab a work queue entry. */
20090 tab = ATGetQueueEntry(wqueue, scanrel);
20091 Assert(tab->partition_constraint == NULL);
20092 tab->partition_constraint = (Expr *) linitial(partConstraint);
20093 tab->validate_default = validate_default;
20094 }
20095 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20096 {
20097 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20098 int i;
20099
20100 for (i = 0; i < partdesc->nparts; i++)
20101 {
20102 Relation part_rel;
20103 List *thisPartConstraint;
20104
20105 /*
20106 * This is the minimum lock we need to prevent deadlocks.
20107 */
20108 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20109
20110 /*
20111 * Adjust the constraint for scanrel so that it matches this
20112 * partition's attribute numbers.
20113 */
20114 thisPartConstraint =
20115 map_partition_varattnos(partConstraint, 1,
20116 part_rel, scanrel);
20117
20118 QueuePartitionConstraintValidation(wqueue, part_rel,
20119 thisPartConstraint,
20120 validate_default);
20121 table_close(part_rel, NoLock); /* keep lock till commit */
20122 }
20123 }
20124}
20125
20126/*
20127 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20128 *
20129 * Return the address of the newly attached partition.
20130 */
20131static ObjectAddress
20133 AlterTableUtilityContext *context)
20134{
20135 Relation attachrel,
20136 catalog;
20137 List *attachrel_children;
20138 List *partConstraint;
20139 SysScanDesc scan;
20140 ScanKeyData skey;
20141 AttrNumber attno;
20142 int natts;
20143 TupleDesc tupleDesc;
20144 ObjectAddress address;
20145 const char *trigger_name;
20146 Oid defaultPartOid;
20147 List *partBoundConstraint;
20148 ParseState *pstate = make_parsestate(NULL);
20149
20150 pstate->p_sourcetext = context->queryString;
20151
20152 /*
20153 * We must lock the default partition if one exists, because attaching a
20154 * new partition will change its partition constraint.
20155 */
20156 defaultPartOid =
20158 if (OidIsValid(defaultPartOid))
20159 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20160
20161 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20162
20163 /*
20164 * XXX I think it'd be a good idea to grab locks on all tables referenced
20165 * by FKs at this point also.
20166 */
20167
20168 /*
20169 * Must be owner of both parent and source table -- parent was checked by
20170 * ATSimplePermissions call in ATPrepCmd
20171 */
20174
20175 /* A partition can only have one parent */
20176 if (attachrel->rd_rel->relispartition)
20177 ereport(ERROR,
20178 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20179 errmsg("\"%s\" is already a partition",
20180 RelationGetRelationName(attachrel))));
20181
20182 if (OidIsValid(attachrel->rd_rel->reloftype))
20183 ereport(ERROR,
20184 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20185 errmsg("cannot attach a typed table as partition")));
20186
20187 /*
20188 * Table being attached should not already be part of inheritance; either
20189 * as a child table...
20190 */
20191 catalog = table_open(InheritsRelationId, AccessShareLock);
20192 ScanKeyInit(&skey,
20193 Anum_pg_inherits_inhrelid,
20194 BTEqualStrategyNumber, F_OIDEQ,
20196 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20197 NULL, 1, &skey);
20199 ereport(ERROR,
20200 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20201 errmsg("cannot attach inheritance child as partition")));
20202 systable_endscan(scan);
20203
20204 /* ...or as a parent table (except the case when it is partitioned) */
20205 ScanKeyInit(&skey,
20206 Anum_pg_inherits_inhparent,
20207 BTEqualStrategyNumber, F_OIDEQ,
20209 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20210 1, &skey);
20212 attachrel->rd_rel->relkind == RELKIND_RELATION)
20213 ereport(ERROR,
20214 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20215 errmsg("cannot attach inheritance parent as partition")));
20216 systable_endscan(scan);
20217 table_close(catalog, AccessShareLock);
20218
20219 /*
20220 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20221 * particular, this disallows making a rel a partition of itself.)
20222 *
20223 * We do that by checking if rel is a member of the list of attachrel's
20224 * partitions provided the latter is partitioned at all. We want to avoid
20225 * having to construct this list again, so we request the strongest lock
20226 * on all partitions. We need the strongest lock, because we may decide
20227 * to scan them if we find out that the table being attached (or its leaf
20228 * partitions) may contain rows that violate the partition constraint. If
20229 * the table has a constraint that would prevent such rows, which by
20230 * definition is present in all the partitions, we need not scan the
20231 * table, nor its partitions. But we cannot risk a deadlock by taking a
20232 * weaker lock now and the stronger one only when needed.
20233 */
20234 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20235 AccessExclusiveLock, NULL);
20236 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20237 ereport(ERROR,
20238 (errcode(ERRCODE_DUPLICATE_TABLE),
20239 errmsg("circular inheritance not allowed"),
20240 errdetail("\"%s\" is already a child of \"%s\".",
20242 RelationGetRelationName(attachrel))));
20243
20244 /* If the parent is permanent, so must be all of its partitions. */
20245 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20246 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20247 ereport(ERROR,
20248 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20249 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20251
20252 /* Temp parent cannot have a partition that is itself not a temp */
20253 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20254 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20255 ereport(ERROR,
20256 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20257 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20259
20260 /* If the parent is temp, it must belong to this session */
20261 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20262 !rel->rd_islocaltemp)
20263 ereport(ERROR,
20264 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20265 errmsg("cannot attach as partition of temporary relation of another session")));
20266
20267 /* Ditto for the partition */
20268 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20269 !attachrel->rd_islocaltemp)
20270 ereport(ERROR,
20271 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20272 errmsg("cannot attach temporary relation of another session as partition")));
20273
20274 /*
20275 * Check if attachrel has any identity columns or any columns that aren't
20276 * in the parent.
20277 */
20278 tupleDesc = RelationGetDescr(attachrel);
20279 natts = tupleDesc->natts;
20280 for (attno = 1; attno <= natts; attno++)
20281 {
20282 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20283 char *attributeName = NameStr(attribute->attname);
20284
20285 /* Ignore dropped */
20286 if (attribute->attisdropped)
20287 continue;
20288
20289 if (attribute->attidentity)
20290 ereport(ERROR,
20291 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20292 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20293 RelationGetRelationName(attachrel), attributeName),
20294 errdetail("The new partition may not contain an identity column."));
20295
20296 /* Try to find the column in parent (matching on column name) */
20297 if (!SearchSysCacheExists2(ATTNAME,
20299 CStringGetDatum(attributeName)))
20300 ereport(ERROR,
20301 (errcode(ERRCODE_DATATYPE_MISMATCH),
20302 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20303 RelationGetRelationName(attachrel), attributeName,
20305 errdetail("The new partition may contain only the columns present in parent.")));
20306 }
20307
20308 /*
20309 * If child_rel has row-level triggers with transition tables, we
20310 * currently don't allow it to become a partition. See also prohibitions
20311 * in ATExecAddInherit() and CreateTrigger().
20312 */
20313 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20314 if (trigger_name != NULL)
20315 ereport(ERROR,
20316 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20317 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20318 trigger_name, RelationGetRelationName(attachrel)),
20319 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20320
20321 /*
20322 * Check that the new partition's bound is valid and does not overlap any
20323 * of existing partitions of the parent - note that it does not return on
20324 * error.
20325 */
20327 cmd->bound, pstate);
20328
20329 /* OK to create inheritance. Rest of the checks performed there */
20330 CreateInheritance(attachrel, rel, true);
20331
20332 /* Update the pg_class entry. */
20333 StorePartitionBound(attachrel, rel, cmd->bound);
20334
20335 /* Ensure there exists a correct set of indexes in the partition. */
20336 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20337
20338 /* and triggers */
20339 CloneRowTriggersToPartition(rel, attachrel);
20340
20341 /*
20342 * Clone foreign key constraints. Callee is responsible for setting up
20343 * for phase 3 constraint verification.
20344 */
20345 CloneForeignKeyConstraints(wqueue, rel, attachrel);
20346
20347 /*
20348 * Generate partition constraint from the partition bound specification.
20349 * If the parent itself is a partition, make sure to include its
20350 * constraint as well.
20351 */
20352 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20353
20354 /*
20355 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20356 * since it's needed later to construct the constraint expression for
20357 * validating against the default partition, if any.
20358 */
20359 partConstraint = list_concat_copy(partBoundConstraint,
20361
20362 /* Skip validation if there are no constraints to validate. */
20363 if (partConstraint)
20364 {
20365 /*
20366 * Run the partition quals through const-simplification similar to
20367 * check constraints. We skip canonicalize_qual, though, because
20368 * partition quals should be in canonical form already.
20369 */
20370 partConstraint =
20372 (Node *) partConstraint);
20373
20374 /* XXX this sure looks wrong */
20375 partConstraint = list_make1(make_ands_explicit(partConstraint));
20376
20377 /*
20378 * Adjust the generated constraint to match this partition's attribute
20379 * numbers.
20380 */
20381 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20382 rel);
20383
20384 /* Validate partition constraints against the table being attached. */
20385 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20386 false);
20387 }
20388
20389 /*
20390 * If we're attaching a partition other than the default partition and a
20391 * default one exists, then that partition's partition constraint changes,
20392 * so add an entry to the work queue to validate it, too. (We must not do
20393 * this when the partition being attached is the default one; we already
20394 * did it above!)
20395 */
20396 if (OidIsValid(defaultPartOid))
20397 {
20398 Relation defaultrel;
20399 List *defPartConstraint;
20400
20401 Assert(!cmd->bound->is_default);
20402
20403 /* we already hold a lock on the default partition */
20404 defaultrel = table_open(defaultPartOid, NoLock);
20405 defPartConstraint =
20406 get_proposed_default_constraint(partBoundConstraint);
20407
20408 /*
20409 * Map the Vars in the constraint expression from rel's attnos to
20410 * defaultrel's.
20411 */
20412 defPartConstraint =
20413 map_partition_varattnos(defPartConstraint,
20414 1, defaultrel, rel);
20415 QueuePartitionConstraintValidation(wqueue, defaultrel,
20416 defPartConstraint, true);
20417
20418 /* keep our lock until commit. */
20419 table_close(defaultrel, NoLock);
20420 }
20421
20422 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20423
20424 /*
20425 * If the partition we just attached is partitioned itself, invalidate
20426 * relcache for all descendent partitions too to ensure that their
20427 * rd_partcheck expression trees are rebuilt; partitions already locked at
20428 * the beginning of this function.
20429 */
20430 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20431 {
20432 ListCell *l;
20433
20434 foreach(l, attachrel_children)
20435 {
20437 }
20438 }
20439
20440 /* keep our lock until commit */
20441 table_close(attachrel, NoLock);
20442
20443 return address;
20444}
20445
20446/*
20447 * AttachPartitionEnsureIndexes
20448 * subroutine for ATExecAttachPartition to create/match indexes
20449 *
20450 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20451 * PARTITION: every partition must have an index attached to each index on the
20452 * partitioned table.
20453 */
20454static void
20456{
20457 List *idxes;
20458 List *attachRelIdxs;
20459 Relation *attachrelIdxRels;
20460 IndexInfo **attachInfos;
20461 ListCell *cell;
20462 MemoryContext cxt;
20463 MemoryContext oldcxt;
20464
20466 "AttachPartitionEnsureIndexes",
20468 oldcxt = MemoryContextSwitchTo(cxt);
20469
20470 idxes = RelationGetIndexList(rel);
20471 attachRelIdxs = RelationGetIndexList(attachrel);
20472 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20473 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20474
20475 /* Build arrays of all existing indexes and their IndexInfos */
20476 foreach_oid(cldIdxId, attachRelIdxs)
20477 {
20478 int i = foreach_current_index(cldIdxId);
20479
20480 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20481 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20482 }
20483
20484 /*
20485 * If we're attaching a foreign table, we must fail if any of the indexes
20486 * is a constraint index; otherwise, there's nothing to do here. Do this
20487 * before starting work, to avoid wasting the effort of building a few
20488 * non-unique indexes before coming across a unique one.
20489 */
20490 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20491 {
20492 foreach(cell, idxes)
20493 {
20494 Oid idx = lfirst_oid(cell);
20496
20497 if (idxRel->rd_index->indisunique ||
20498 idxRel->rd_index->indisprimary)
20499 ereport(ERROR,
20500 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20501 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20502 RelationGetRelationName(attachrel),
20504 errdetail("Partitioned table \"%s\" contains unique indexes.",
20507 }
20508
20509 goto out;
20510 }
20511
20512 /*
20513 * For each index on the partitioned table, find a matching one in the
20514 * partition-to-be; if one is not found, create one.
20515 */
20516 foreach(cell, idxes)
20517 {
20518 Oid idx = lfirst_oid(cell);
20520 IndexInfo *info;
20521 AttrMap *attmap;
20522 bool found = false;
20523 Oid constraintOid;
20524
20525 /*
20526 * Ignore indexes in the partitioned table other than partitioned
20527 * indexes.
20528 */
20529 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20530 {
20532 continue;
20533 }
20534
20535 /* construct an indexinfo to compare existing indexes against */
20536 info = BuildIndexInfo(idxRel);
20537 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20538 RelationGetDescr(rel),
20539 false);
20541
20542 /*
20543 * Scan the list of existing indexes in the partition-to-be, and mark
20544 * the first matching, valid, unattached one we find, if any, as
20545 * partition of the parent index. If we find one, we're done.
20546 */
20547 for (int i = 0; i < list_length(attachRelIdxs); i++)
20548 {
20549 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20550 Oid cldConstrOid = InvalidOid;
20551
20552 /* does this index have a parent? if so, can't use it */
20553 if (attachrelIdxRels[i]->rd_rel->relispartition)
20554 continue;
20555
20556 /* If this index is invalid, can't use it */
20557 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20558 continue;
20559
20560 if (CompareIndexInfo(attachInfos[i], info,
20561 attachrelIdxRels[i]->rd_indcollation,
20562 idxRel->rd_indcollation,
20563 attachrelIdxRels[i]->rd_opfamily,
20564 idxRel->rd_opfamily,
20565 attmap))
20566 {
20567 /*
20568 * If this index is being created in the parent because of a
20569 * constraint, then the child needs to have a constraint also,
20570 * so look for one. If there is no such constraint, this
20571 * index is no good, so keep looking.
20572 */
20573 if (OidIsValid(constraintOid))
20574 {
20575 cldConstrOid =
20577 cldIdxId);
20578 /* no dice */
20579 if (!OidIsValid(cldConstrOid))
20580 continue;
20581
20582 /* Ensure they're both the same type of constraint */
20583 if (get_constraint_type(constraintOid) !=
20584 get_constraint_type(cldConstrOid))
20585 continue;
20586 }
20587
20588 /* bingo. */
20589 IndexSetParentIndex(attachrelIdxRels[i], idx);
20590 if (OidIsValid(constraintOid))
20591 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20592 RelationGetRelid(attachrel));
20593 found = true;
20594
20596 break;
20597 }
20598 }
20599
20600 /*
20601 * If no suitable index was found in the partition-to-be, create one
20602 * now. Note that if this is a PK, not-null constraints must already
20603 * exist.
20604 */
20605 if (!found)
20606 {
20607 IndexStmt *stmt;
20608 Oid conOid;
20609
20611 idxRel, attmap,
20612 &conOid);
20614 RelationGetRelid(idxRel),
20615 conOid,
20616 -1,
20617 true, false, false, false, false);
20618 }
20619
20621 }
20622
20623out:
20624 /* Clean up. */
20625 for (int i = 0; i < list_length(attachRelIdxs); i++)
20626 index_close(attachrelIdxRels[i], AccessShareLock);
20627 MemoryContextSwitchTo(oldcxt);
20629}
20630
20631/*
20632 * CloneRowTriggersToPartition
20633 * subroutine for ATExecAttachPartition/DefineRelation to create row
20634 * triggers on partitions
20635 */
20636static void
20638{
20639 Relation pg_trigger;
20641 SysScanDesc scan;
20642 HeapTuple tuple;
20643 MemoryContext perTupCxt;
20644
20645 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20646 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20647 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20648 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20649 true, NULL, 1, &key);
20650
20652 "clone trig", ALLOCSET_SMALL_SIZES);
20653
20654 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20655 {
20656 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20657 CreateTrigStmt *trigStmt;
20658 Node *qual = NULL;
20659 Datum value;
20660 bool isnull;
20661 List *cols = NIL;
20662 List *trigargs = NIL;
20663 MemoryContext oldcxt;
20664
20665 /*
20666 * Ignore statement-level triggers; those are not cloned.
20667 */
20668 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20669 continue;
20670
20671 /*
20672 * Don't clone internal triggers, because the constraint cloning code
20673 * will.
20674 */
20675 if (trigForm->tgisinternal)
20676 continue;
20677
20678 /*
20679 * Complain if we find an unexpected trigger type.
20680 */
20681 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20682 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20683 elog(ERROR, "unexpected trigger \"%s\" found",
20684 NameStr(trigForm->tgname));
20685
20686 /* Use short-lived context for CREATE TRIGGER */
20687 oldcxt = MemoryContextSwitchTo(perTupCxt);
20688
20689 /*
20690 * If there is a WHEN clause, generate a 'cooked' version of it that's
20691 * appropriate for the partition.
20692 */
20693 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20694 RelationGetDescr(pg_trigger), &isnull);
20695 if (!isnull)
20696 {
20698 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20699 partition, parent);
20700 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20701 partition, parent);
20702 }
20703
20704 /*
20705 * If there is a column list, transform it to a list of column names.
20706 * Note we don't need to map this list in any way ...
20707 */
20708 if (trigForm->tgattr.dim1 > 0)
20709 {
20710 int i;
20711
20712 for (i = 0; i < trigForm->tgattr.dim1; i++)
20713 {
20715
20716 col = TupleDescAttr(parent->rd_att,
20717 trigForm->tgattr.values[i] - 1);
20718 cols = lappend(cols,
20719 makeString(pstrdup(NameStr(col->attname))));
20720 }
20721 }
20722
20723 /* Reconstruct trigger arguments list. */
20724 if (trigForm->tgnargs > 0)
20725 {
20726 char *p;
20727
20728 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20729 RelationGetDescr(pg_trigger), &isnull);
20730 if (isnull)
20731 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20732 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20733
20734 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20735
20736 for (int i = 0; i < trigForm->tgnargs; i++)
20737 {
20738 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20739 p += strlen(p) + 1;
20740 }
20741 }
20742
20743 trigStmt = makeNode(CreateTrigStmt);
20744 trigStmt->replace = false;
20745 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20746 trigStmt->trigname = NameStr(trigForm->tgname);
20747 trigStmt->relation = NULL;
20748 trigStmt->funcname = NULL; /* passed separately */
20749 trigStmt->args = trigargs;
20750 trigStmt->row = true;
20751 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20752 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20753 trigStmt->columns = cols;
20754 trigStmt->whenClause = NULL; /* passed separately */
20755 trigStmt->transitionRels = NIL; /* not supported at present */
20756 trigStmt->deferrable = trigForm->tgdeferrable;
20757 trigStmt->initdeferred = trigForm->tginitdeferred;
20758 trigStmt->constrrel = NULL; /* passed separately */
20759
20760 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20761 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20762 trigForm->tgfoid, trigForm->oid, qual,
20763 false, true, trigForm->tgenabled);
20764
20765 MemoryContextSwitchTo(oldcxt);
20766 MemoryContextReset(perTupCxt);
20767 }
20768
20769 MemoryContextDelete(perTupCxt);
20770
20771 systable_endscan(scan);
20772 table_close(pg_trigger, RowExclusiveLock);
20773}
20774
20775/*
20776 * ALTER TABLE DETACH PARTITION
20777 *
20778 * Return the address of the relation that is no longer a partition of rel.
20779 *
20780 * If concurrent mode is requested, we run in two transactions. A side-
20781 * effect is that this command cannot run in a multi-part ALTER TABLE.
20782 * Currently, that's enforced by the grammar.
20783 *
20784 * The strategy for concurrency is to first modify the partition's
20785 * pg_inherit catalog row to make it visible to everyone that the
20786 * partition is detached, lock the partition against writes, and commit
20787 * the transaction; anyone who requests the partition descriptor from
20788 * that point onwards has to ignore such a partition. In a second
20789 * transaction, we wait until all transactions that could have seen the
20790 * partition as attached are gone, then we remove the rest of partition
20791 * metadata (pg_inherits and pg_class.relpartbounds).
20792 */
20793static ObjectAddress
20795 RangeVar *name, bool concurrent)
20796{
20797 Relation partRel;
20798 ObjectAddress address;
20799 Oid defaultPartOid;
20800
20801 /*
20802 * We must lock the default partition, because detaching this partition
20803 * will change its partition constraint.
20804 */
20805 defaultPartOid =
20807 if (OidIsValid(defaultPartOid))
20808 {
20809 /*
20810 * Concurrent detaching when a default partition exists is not
20811 * supported. The main problem is that the default partition
20812 * constraint would change. And there's a definitional problem: what
20813 * should happen to the tuples that are being inserted that belong to
20814 * the partition being detached? Putting them on the partition being
20815 * detached would be wrong, since they'd become "lost" after the
20816 * detaching completes but we cannot put them in the default partition
20817 * either until we alter its partition constraint.
20818 *
20819 * I think we could solve this problem if we effected the constraint
20820 * change before committing the first transaction. But the lock would
20821 * have to remain AEL and it would cause concurrent query planning to
20822 * be blocked, so changing it that way would be even worse.
20823 */
20824 if (concurrent)
20825 ereport(ERROR,
20826 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20827 errmsg("cannot detach partitions concurrently when a default partition exists")));
20828 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20829 }
20830
20831 /*
20832 * In concurrent mode, the partition is locked with share-update-exclusive
20833 * in the first transaction. This allows concurrent transactions to be
20834 * doing DML to the partition.
20835 */
20836 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20838
20839 /*
20840 * Check inheritance conditions and either delete the pg_inherits row (in
20841 * non-concurrent mode) or just set the inhdetachpending flag.
20842 */
20843 if (!concurrent)
20844 RemoveInheritance(partRel, rel, false);
20845 else
20846 MarkInheritDetached(partRel, rel);
20847
20848 /*
20849 * Ensure that foreign keys still hold after this detach. This keeps
20850 * locks on the referencing tables, which prevents concurrent transactions
20851 * from adding rows that we wouldn't see. For this to work in concurrent
20852 * mode, it is critical that the partition appears as no longer attached
20853 * for the RI queries as soon as the first transaction commits.
20854 */
20856
20857 /*
20858 * Concurrent mode has to work harder; first we add a new constraint to
20859 * the partition that matches the partition constraint. Then we close our
20860 * existing transaction, and in a new one wait for all processes to catch
20861 * up on the catalog updates we've done so far; at that point we can
20862 * complete the operation.
20863 */
20864 if (concurrent)
20865 {
20866 Oid partrelid,
20867 parentrelid;
20868 LOCKTAG tag;
20869 char *parentrelname;
20870 char *partrelname;
20871
20872 /*
20873 * Add a new constraint to the partition being detached, which
20874 * supplants the partition constraint (unless there is one already).
20875 */
20876 DetachAddConstraintIfNeeded(wqueue, partRel);
20877
20878 /*
20879 * We're almost done now; the only traces that remain are the
20880 * pg_inherits tuple and the partition's relpartbounds. Before we can
20881 * remove those, we need to wait until all transactions that know that
20882 * this is a partition are gone.
20883 */
20884
20885 /*
20886 * Remember relation OIDs to re-acquire them later; and relation names
20887 * too, for error messages if something is dropped in between.
20888 */
20889 partrelid = RelationGetRelid(partRel);
20890 parentrelid = RelationGetRelid(rel);
20891 parentrelname = MemoryContextStrdup(PortalContext,
20893 partrelname = MemoryContextStrdup(PortalContext,
20894 RelationGetRelationName(partRel));
20895
20896 /* Invalidate relcache entries for the parent -- must be before close */
20898
20899 table_close(partRel, NoLock);
20900 table_close(rel, NoLock);
20901 tab->rel = NULL;
20902
20903 /* Make updated catalog entry visible */
20906
20908
20909 /*
20910 * Now wait. This ensures that all queries that were planned
20911 * including the partition are finished before we remove the rest of
20912 * catalog entries. We don't need or indeed want to acquire this
20913 * lock, though -- that would block later queries.
20914 *
20915 * We don't need to concern ourselves with waiting for a lock on the
20916 * partition itself, since we will acquire AccessExclusiveLock below.
20917 */
20918 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20920
20921 /*
20922 * Now acquire locks in both relations again. Note they may have been
20923 * removed in the meantime, so care is required.
20924 */
20925 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20926 partRel = try_relation_open(partrelid, AccessExclusiveLock);
20927
20928 /* If the relations aren't there, something bad happened; bail out */
20929 if (rel == NULL)
20930 {
20931 if (partRel != NULL) /* shouldn't happen */
20932 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20933 partrelname);
20934 ereport(ERROR,
20935 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20936 errmsg("partitioned table \"%s\" was removed concurrently",
20937 parentrelname)));
20938 }
20939 if (partRel == NULL)
20940 ereport(ERROR,
20941 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20942 errmsg("partition \"%s\" was removed concurrently", partrelname)));
20943
20944 tab->rel = rel;
20945 }
20946
20947 /* Do the final part of detaching */
20948 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20949
20950 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20951
20952 /* keep our lock until commit */
20953 table_close(partRel, NoLock);
20954
20955 return address;
20956}
20957
20958/*
20959 * Second part of ALTER TABLE .. DETACH.
20960 *
20961 * This is separate so that it can be run independently when the second
20962 * transaction of the concurrent algorithm fails (crash or abort).
20963 */
20964static void
20965DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
20966 Oid defaultPartOid)
20967{
20968 Relation classRel;
20969 List *fks;
20970 ListCell *cell;
20971 List *indexes;
20972 Datum new_val[Natts_pg_class];
20973 bool new_null[Natts_pg_class],
20974 new_repl[Natts_pg_class];
20975 HeapTuple tuple,
20976 newtuple;
20977 Relation trigrel = NULL;
20978 List *fkoids = NIL;
20979
20980 if (concurrent)
20981 {
20982 /*
20983 * We can remove the pg_inherits row now. (In the non-concurrent case,
20984 * this was already done).
20985 */
20986 RemoveInheritance(partRel, rel, true);
20987 }
20988
20989 /* Drop any triggers that were cloned on creation/attach. */
20991
20992 /*
20993 * Detach any foreign keys that are inherited. This includes creating
20994 * additional action triggers.
20995 */
20996 fks = copyObject(RelationGetFKeyList(partRel));
20997 if (fks != NIL)
20998 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20999
21000 /*
21001 * It's possible that the partition being detached has a foreign key that
21002 * references a partitioned table. When that happens, there are multiple
21003 * pg_constraint rows for the partition: one points to the partitioned
21004 * table itself, while the others point to each of its partitions. Only
21005 * the topmost one is to be considered here; the child constraints must be
21006 * left alone, because conceptually those aren't coming from our parent
21007 * partitioned table, but from this partition itself.
21008 *
21009 * We implement this by collecting all the constraint OIDs in a first scan
21010 * of the FK array, and skipping in the loop below those constraints whose
21011 * parents are listed here.
21012 */
21014 fkoids = lappend_oid(fkoids, fk->conoid);
21015
21016 foreach(cell, fks)
21017 {
21018 ForeignKeyCacheInfo *fk = lfirst(cell);
21019 HeapTuple contup;
21020 Form_pg_constraint conform;
21021
21022 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21023 if (!HeapTupleIsValid(contup))
21024 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21025 conform = (Form_pg_constraint) GETSTRUCT(contup);
21026
21027 /*
21028 * Consider only inherited foreign keys, and only if their parents
21029 * aren't in the list.
21030 */
21031 if (conform->contype != CONSTRAINT_FOREIGN ||
21032 !OidIsValid(conform->conparentid) ||
21033 list_member_oid(fkoids, conform->conparentid))
21034 {
21035 ReleaseSysCache(contup);
21036 continue;
21037 }
21038
21039 /*
21040 * The constraint on this table must be marked no longer a child of
21041 * the parent's constraint, as do its check triggers.
21042 */
21044
21045 /*
21046 * Also, look up the partition's "check" triggers corresponding to the
21047 * ENFORCED constraint being detached and detach them from the parent
21048 * triggers. NOT ENFORCED constraints do not have these triggers;
21049 * therefore, this step is not needed.
21050 */
21051 if (fk->conenforced)
21052 {
21053 Oid insertTriggerOid,
21054 updateTriggerOid;
21055
21057 fk->conoid, fk->confrelid, fk->conrelid,
21058 &insertTriggerOid, &updateTriggerOid);
21059 Assert(OidIsValid(insertTriggerOid));
21060 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21061 RelationGetRelid(partRel));
21062 Assert(OidIsValid(updateTriggerOid));
21063 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21064 RelationGetRelid(partRel));
21065 }
21066
21067 /*
21068 * Lastly, create the action triggers on the referenced table, using
21069 * addFkRecurseReferenced, which requires some elaborate setup (so put
21070 * it in a separate block). While at it, if the table is partitioned,
21071 * that function will recurse to create the pg_constraint rows and
21072 * action triggers for each partition.
21073 *
21074 * Note there's no need to do addFkConstraint() here, because the
21075 * pg_constraint row already exists.
21076 */
21077 {
21078 Constraint *fkconstraint;
21079 int numfks;
21080 AttrNumber conkey[INDEX_MAX_KEYS];
21081 AttrNumber confkey[INDEX_MAX_KEYS];
21082 Oid conpfeqop[INDEX_MAX_KEYS];
21083 Oid conppeqop[INDEX_MAX_KEYS];
21084 Oid conffeqop[INDEX_MAX_KEYS];
21085 int numfkdelsetcols;
21086 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21087 Relation refdRel;
21088
21090 &numfks,
21091 conkey,
21092 confkey,
21093 conpfeqop,
21094 conppeqop,
21095 conffeqop,
21096 &numfkdelsetcols,
21097 confdelsetcols);
21098
21099 /* Create a synthetic node we'll use throughout */
21100 fkconstraint = makeNode(Constraint);
21101 fkconstraint->contype = CONSTRAINT_FOREIGN;
21102 fkconstraint->conname = pstrdup(NameStr(conform->conname));
21103 fkconstraint->deferrable = conform->condeferrable;
21104 fkconstraint->initdeferred = conform->condeferred;
21105 fkconstraint->is_enforced = conform->conenforced;
21106 fkconstraint->skip_validation = true;
21107 fkconstraint->initially_valid = conform->convalidated;
21108 /* a few irrelevant fields omitted here */
21109 fkconstraint->pktable = NULL;
21110 fkconstraint->fk_attrs = NIL;
21111 fkconstraint->pk_attrs = NIL;
21112 fkconstraint->fk_matchtype = conform->confmatchtype;
21113 fkconstraint->fk_upd_action = conform->confupdtype;
21114 fkconstraint->fk_del_action = conform->confdeltype;
21115 fkconstraint->fk_del_set_cols = NIL;
21116 fkconstraint->old_conpfeqop = NIL;
21117 fkconstraint->old_pktable_oid = InvalidOid;
21118 fkconstraint->location = -1;
21119
21120 /* set up colnames, used to generate the constraint name */
21121 for (int i = 0; i < numfks; i++)
21122 {
21124
21125 att = TupleDescAttr(RelationGetDescr(partRel),
21126 conkey[i] - 1);
21127
21128 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21129 makeString(NameStr(att->attname)));
21130 }
21131
21133
21134 addFkRecurseReferenced(fkconstraint, partRel,
21135 refdRel,
21136 conform->conindid,
21137 fk->conoid,
21138 numfks,
21139 confkey,
21140 conkey,
21141 conpfeqop,
21142 conppeqop,
21143 conffeqop,
21144 numfkdelsetcols,
21145 confdelsetcols,
21146 true,
21148 conform->conperiod);
21149 table_close(refdRel, NoLock); /* keep lock till end of xact */
21150 }
21151
21152 ReleaseSysCache(contup);
21153 }
21154 list_free_deep(fks);
21155 if (trigrel)
21156 table_close(trigrel, RowExclusiveLock);
21157
21158 /*
21159 * Any sub-constraints that are in the referenced-side of a larger
21160 * constraint have to be removed. This partition is no longer part of the
21161 * key space of the constraint.
21162 */
21163 foreach(cell, GetParentedForeignKeyRefs(partRel))
21164 {
21165 Oid constrOid = lfirst_oid(cell);
21166 ObjectAddress constraint;
21167
21169 deleteDependencyRecordsForClass(ConstraintRelationId,
21170 constrOid,
21171 ConstraintRelationId,
21174
21175 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21176 performDeletion(&constraint, DROP_RESTRICT, 0);
21177 }
21178
21179 /* Now we can detach indexes */
21180 indexes = RelationGetIndexList(partRel);
21181 foreach(cell, indexes)
21182 {
21183 Oid idxid = lfirst_oid(cell);
21184 Oid parentidx;
21185 Relation idx;
21186 Oid constrOid;
21187 Oid parentConstrOid;
21188
21189 if (!has_superclass(idxid))
21190 continue;
21191
21192 parentidx = get_partition_parent(idxid, false);
21193 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21194
21197
21198 /*
21199 * If there's a constraint associated with the index, detach it too.
21200 * Careful: it is possible for a constraint index in a partition to be
21201 * the child of a non-constraint index, so verify whether the parent
21202 * index does actually have a constraint.
21203 */
21205 idxid);
21207 parentidx);
21208 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21210
21212 }
21213
21214 /* Update pg_class tuple */
21215 classRel = table_open(RelationRelationId, RowExclusiveLock);
21216 tuple = SearchSysCacheCopy1(RELOID,
21218 if (!HeapTupleIsValid(tuple))
21219 elog(ERROR, "cache lookup failed for relation %u",
21220 RelationGetRelid(partRel));
21221 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21222
21223 /* Clear relpartbound and reset relispartition */
21224 memset(new_val, 0, sizeof(new_val));
21225 memset(new_null, false, sizeof(new_null));
21226 memset(new_repl, false, sizeof(new_repl));
21227 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21228 new_null[Anum_pg_class_relpartbound - 1] = true;
21229 new_repl[Anum_pg_class_relpartbound - 1] = true;
21230 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21231 new_val, new_null, new_repl);
21232
21233 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21234 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21235 heap_freetuple(newtuple);
21236 table_close(classRel, RowExclusiveLock);
21237
21238 /*
21239 * Drop identity property from all identity columns of partition.
21240 */
21241 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21242 {
21243 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21244
21245 if (!attr->attisdropped && attr->attidentity)
21246 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21247 AccessExclusiveLock, true, true);
21248 }
21249
21250 if (OidIsValid(defaultPartOid))
21251 {
21252 /*
21253 * If the relation being detached is the default partition itself,
21254 * remove it from the parent's pg_partitioned_table entry.
21255 *
21256 * If not, we must invalidate default partition's relcache entry, as
21257 * in StorePartitionBound: its partition constraint depends on every
21258 * other partition's partition constraint.
21259 */
21260 if (RelationGetRelid(partRel) == defaultPartOid)
21262 else
21263 CacheInvalidateRelcacheByRelid(defaultPartOid);
21264 }
21265
21266 /*
21267 * Invalidate the parent's relcache so that the partition is no longer
21268 * included in its partition descriptor.
21269 */
21271
21272 /*
21273 * If the partition we just detached is partitioned itself, invalidate
21274 * relcache for all descendent partitions too to ensure that their
21275 * rd_partcheck expression trees are rebuilt; must lock partitions before
21276 * doing so, using the same lockmode as what partRel has been locked with
21277 * by the caller.
21278 */
21279 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21280 {
21281 List *children;
21282
21283 children = find_all_inheritors(RelationGetRelid(partRel),
21284 AccessExclusiveLock, NULL);
21285 foreach(cell, children)
21286 {
21288 }
21289 }
21290}
21291
21292/*
21293 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21294 *
21295 * To use when a DETACH PARTITION command previously did not run to
21296 * completion; this completes the detaching process.
21297 */
21298static ObjectAddress
21300{
21301 Relation partRel;
21302 ObjectAddress address;
21303 Snapshot snap = GetActiveSnapshot();
21304
21306
21307 /*
21308 * Wait until existing snapshots are gone. This is important if the
21309 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21310 * user could immediately run DETACH FINALIZE without actually waiting for
21311 * existing transactions. We must not complete the detach action until
21312 * all such queries are complete (otherwise we would present them with an
21313 * inconsistent view of catalogs).
21314 */
21315 WaitForOlderSnapshots(snap->xmin, false);
21316
21317 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21318
21319 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21320
21321 table_close(partRel, NoLock);
21322
21323 return address;
21324}
21325
21326/*
21327 * DetachAddConstraintIfNeeded
21328 * Subroutine for ATExecDetachPartition. Create a constraint that
21329 * takes the place of the partition constraint, but avoid creating
21330 * a dupe if a constraint already exists which implies the needed
21331 * constraint.
21332 */
21333static void
21335{
21336 List *constraintExpr;
21337
21338 constraintExpr = RelationGetPartitionQual(partRel);
21339 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
21340
21341 /*
21342 * Avoid adding a new constraint if the needed constraint is implied by an
21343 * existing constraint
21344 */
21345 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
21346 {
21347 AlteredTableInfo *tab;
21348 Constraint *n;
21349
21350 tab = ATGetQueueEntry(wqueue, partRel);
21351
21352 /* Add constraint on partition, equivalent to the partition constraint */
21353 n = makeNode(Constraint);
21354 n->contype = CONSTR_CHECK;
21355 n->conname = NULL;
21356 n->location = -1;
21357 n->is_no_inherit = false;
21358 n->raw_expr = NULL;
21359 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
21360 n->is_enforced = true;
21361 n->initially_valid = true;
21362 n->skip_validation = true;
21363 /* It's a re-add, since it nominally already exists */
21364 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
21365 true, false, true, ShareUpdateExclusiveLock);
21366 }
21367}
21368
21369/*
21370 * DropClonedTriggersFromPartition
21371 * subroutine for ATExecDetachPartition to remove any triggers that were
21372 * cloned to the partition when it was created-as-partition or attached.
21373 * This undoes what CloneRowTriggersToPartition did.
21374 */
21375static void
21377{
21378 ScanKeyData skey;
21379 SysScanDesc scan;
21380 HeapTuple trigtup;
21381 Relation tgrel;
21382 ObjectAddresses *objects;
21383
21384 objects = new_object_addresses();
21385
21386 /*
21387 * Scan pg_trigger to search for all triggers on this rel.
21388 */
21389 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21390 F_OIDEQ, ObjectIdGetDatum(partitionId));
21391 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21392 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21393 true, NULL, 1, &skey);
21394 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21395 {
21396 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21397 ObjectAddress trig;
21398
21399 /* Ignore triggers that weren't cloned */
21400 if (!OidIsValid(pg_trigger->tgparentid))
21401 continue;
21402
21403 /*
21404 * Ignore internal triggers that are implementation objects of foreign
21405 * keys, because these will be detached when the foreign keys
21406 * themselves are.
21407 */
21408 if (OidIsValid(pg_trigger->tgconstrrelid))
21409 continue;
21410
21411 /*
21412 * This is ugly, but necessary: remove the dependency markings on the
21413 * trigger so that it can be removed.
21414 */
21415 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21416 TriggerRelationId,
21418 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21419 RelationRelationId,
21421
21422 /* remember this trigger to remove it below */
21423 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21424 add_exact_object_address(&trig, objects);
21425 }
21426
21427 /* make the dependency removal visible to the deletion below */
21430
21431 /* done */
21432 free_object_addresses(objects);
21433 systable_endscan(scan);
21435}
21436
21437/*
21438 * Before acquiring lock on an index, acquire the same lock on the owning
21439 * table.
21440 */
21442{
21446};
21447
21448static void
21449RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21450 void *arg)
21451{
21453 Form_pg_class classform;
21454 HeapTuple tuple;
21455
21456 state = (struct AttachIndexCallbackState *) arg;
21457
21458 if (!state->lockedParentTbl)
21459 {
21460 LockRelationOid(state->parentTblOid, AccessShareLock);
21461 state->lockedParentTbl = true;
21462 }
21463
21464 /*
21465 * If we previously locked some other heap, and the name we're looking up
21466 * no longer refers to an index on that relation, release the now-useless
21467 * lock. XXX maybe we should do *after* we verify whether the index does
21468 * not actually belong to the same relation ...
21469 */
21470 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21471 {
21472 UnlockRelationOid(state->partitionOid, AccessShareLock);
21473 state->partitionOid = InvalidOid;
21474 }
21475
21476 /* Didn't find a relation, so no need for locking or permission checks. */
21477 if (!OidIsValid(relOid))
21478 return;
21479
21480 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21481 if (!HeapTupleIsValid(tuple))
21482 return; /* concurrently dropped, so nothing to do */
21483 classform = (Form_pg_class) GETSTRUCT(tuple);
21484 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21485 classform->relkind != RELKIND_INDEX)
21486 ereport(ERROR,
21487 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21488 errmsg("\"%s\" is not an index", rv->relname)));
21489 ReleaseSysCache(tuple);
21490
21491 /*
21492 * Since we need only examine the heap's tupledesc, an access share lock
21493 * on it (preventing any DDL) is sufficient.
21494 */
21495 state->partitionOid = IndexGetRelation(relOid, false);
21496 LockRelationOid(state->partitionOid, AccessShareLock);
21497}
21498
21499/*
21500 * ALTER INDEX i1 ATTACH PARTITION i2
21501 */
21502static ObjectAddress
21504{
21505 Relation partIdx;
21506 Relation partTbl;
21507 Relation parentTbl;
21508 ObjectAddress address;
21509 Oid partIdxId;
21510 Oid currParent;
21512
21513 /*
21514 * We need to obtain lock on the index 'name' to modify it, but we also
21515 * need to read its owning table's tuple descriptor -- so we need to lock
21516 * both. To avoid deadlocks, obtain lock on the table before doing so on
21517 * the index. Furthermore, we need to examine the parent table of the
21518 * partition, so lock that one too.
21519 */
21520 state.partitionOid = InvalidOid;
21521 state.parentTblOid = parentIdx->rd_index->indrelid;
21522 state.lockedParentTbl = false;
21523 partIdxId =
21526 &state);
21527 /* Not there? */
21528 if (!OidIsValid(partIdxId))
21529 ereport(ERROR,
21530 (errcode(ERRCODE_UNDEFINED_OBJECT),
21531 errmsg("index \"%s\" does not exist", name->relname)));
21532
21533 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21534 partIdx = relation_open(partIdxId, AccessExclusiveLock);
21535
21536 /* we already hold locks on both tables, so this is safe: */
21537 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21538 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21539
21540 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21541
21542 /* Silently do nothing if already in the right state */
21543 currParent = partIdx->rd_rel->relispartition ?
21544 get_partition_parent(partIdxId, false) : InvalidOid;
21545 if (currParent != RelationGetRelid(parentIdx))
21546 {
21547 IndexInfo *childInfo;
21548 IndexInfo *parentInfo;
21549 AttrMap *attmap;
21550 bool found;
21551 int i;
21552 PartitionDesc partDesc;
21553 Oid constraintOid,
21554 cldConstrId = InvalidOid;
21555
21556 /*
21557 * If this partition already has an index attached, refuse the
21558 * operation.
21559 */
21560 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21561
21562 if (OidIsValid(currParent))
21563 ereport(ERROR,
21564 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21565 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21566 RelationGetRelationName(partIdx),
21567 RelationGetRelationName(parentIdx)),
21568 errdetail("Index \"%s\" is already attached to another index.",
21569 RelationGetRelationName(partIdx))));
21570
21571 /* Make sure it indexes a partition of the other index's table */
21572 partDesc = RelationGetPartitionDesc(parentTbl, true);
21573 found = false;
21574 for (i = 0; i < partDesc->nparts; i++)
21575 {
21576 if (partDesc->oids[i] == state.partitionOid)
21577 {
21578 found = true;
21579 break;
21580 }
21581 }
21582 if (!found)
21583 ereport(ERROR,
21584 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21585 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21586 RelationGetRelationName(partIdx),
21587 RelationGetRelationName(parentIdx)),
21588 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21589 RelationGetRelationName(partIdx),
21590 RelationGetRelationName(parentTbl))));
21591
21592 /* Ensure the indexes are compatible */
21593 childInfo = BuildIndexInfo(partIdx);
21594 parentInfo = BuildIndexInfo(parentIdx);
21595 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21596 RelationGetDescr(parentTbl),
21597 false);
21598 if (!CompareIndexInfo(childInfo, parentInfo,
21599 partIdx->rd_indcollation,
21600 parentIdx->rd_indcollation,
21601 partIdx->rd_opfamily,
21602 parentIdx->rd_opfamily,
21603 attmap))
21604 ereport(ERROR,
21605 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21606 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21607 RelationGetRelationName(partIdx),
21608 RelationGetRelationName(parentIdx)),
21609 errdetail("The index definitions do not match.")));
21610
21611 /*
21612 * If there is a constraint in the parent, make sure there is one in
21613 * the child too.
21614 */
21615 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21616 RelationGetRelid(parentIdx));
21617
21618 if (OidIsValid(constraintOid))
21619 {
21621 partIdxId);
21622 if (!OidIsValid(cldConstrId))
21623 ereport(ERROR,
21624 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21625 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21626 RelationGetRelationName(partIdx),
21627 RelationGetRelationName(parentIdx)),
21628 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21629 RelationGetRelationName(parentIdx),
21630 RelationGetRelationName(parentTbl),
21631 RelationGetRelationName(partIdx))));
21632 }
21633
21634 /*
21635 * If it's a primary key, make sure the columns in the partition are
21636 * NOT NULL.
21637 */
21638 if (parentIdx->rd_index->indisprimary)
21639 verifyPartitionIndexNotNull(childInfo, partTbl);
21640
21641 /* All good -- do it */
21642 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21643 if (OidIsValid(constraintOid))
21644 ConstraintSetParentConstraint(cldConstrId, constraintOid,
21645 RelationGetRelid(partTbl));
21646
21647 free_attrmap(attmap);
21648
21649 validatePartitionedIndex(parentIdx, parentTbl);
21650 }
21651
21652 relation_close(parentTbl, AccessShareLock);
21653 /* keep these locks till commit */
21654 relation_close(partTbl, NoLock);
21655 relation_close(partIdx, NoLock);
21656
21657 return address;
21658}
21659
21660/*
21661 * Verify whether the given partition already contains an index attached
21662 * to the given partitioned index. If so, raise an error.
21663 */
21664static void
21665refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21666{
21667 Oid existingIdx;
21668
21669 existingIdx = index_get_partition(partitionTbl,
21670 RelationGetRelid(parentIdx));
21671 if (OidIsValid(existingIdx))
21672 ereport(ERROR,
21673 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21674 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21675 RelationGetRelationName(partIdx),
21676 RelationGetRelationName(parentIdx)),
21677 errdetail("Another index is already attached for partition \"%s\".",
21678 RelationGetRelationName(partitionTbl))));
21679}
21680
21681/*
21682 * Verify whether the set of attached partition indexes to a parent index on
21683 * a partitioned table is complete. If it is, mark the parent index valid.
21684 *
21685 * This should be called each time a partition index is attached.
21686 */
21687static void
21689{
21690 Relation inheritsRel;
21691 SysScanDesc scan;
21693 int tuples = 0;
21694 HeapTuple inhTup;
21695 bool updated = false;
21696
21697 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21698
21699 /*
21700 * Scan pg_inherits for this parent index. Count each valid index we find
21701 * (verifying the pg_index entry for each), and if we reach the total
21702 * amount we expect, we can mark this parent index as valid.
21703 */
21704 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21705 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21706 BTEqualStrategyNumber, F_OIDEQ,
21708 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21709 NULL, 1, &key);
21710 while ((inhTup = systable_getnext(scan)) != NULL)
21711 {
21712 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21713 HeapTuple indTup;
21714 Form_pg_index indexForm;
21715
21716 indTup = SearchSysCache1(INDEXRELID,
21717 ObjectIdGetDatum(inhForm->inhrelid));
21718 if (!HeapTupleIsValid(indTup))
21719 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21720 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21721 if (indexForm->indisvalid)
21722 tuples += 1;
21723 ReleaseSysCache(indTup);
21724 }
21725
21726 /* Done with pg_inherits */
21727 systable_endscan(scan);
21728 table_close(inheritsRel, AccessShareLock);
21729
21730 /*
21731 * If we found as many inherited indexes as the partitioned table has
21732 * partitions, we're good; update pg_index to set indisvalid.
21733 */
21734 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21735 {
21736 Relation idxRel;
21737 HeapTuple indTup;
21738 Form_pg_index indexForm;
21739
21740 idxRel = table_open(IndexRelationId, RowExclusiveLock);
21741 indTup = SearchSysCacheCopy1(INDEXRELID,
21743 if (!HeapTupleIsValid(indTup))
21744 elog(ERROR, "cache lookup failed for index %u",
21745 RelationGetRelid(partedIdx));
21746 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21747
21748 indexForm->indisvalid = true;
21749 updated = true;
21750
21751 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21752
21754 heap_freetuple(indTup);
21755 }
21756
21757 /*
21758 * If this index is in turn a partition of a larger index, validating it
21759 * might cause the parent to become valid also. Try that.
21760 */
21761 if (updated && partedIdx->rd_rel->relispartition)
21762 {
21763 Oid parentIdxId,
21764 parentTblId;
21765 Relation parentIdx,
21766 parentTbl;
21767
21768 /* make sure we see the validation we just did */
21770
21771 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21772 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21773 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21774 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21775 Assert(!parentIdx->rd_index->indisvalid);
21776
21777 validatePartitionedIndex(parentIdx, parentTbl);
21778
21781 }
21782}
21783
21784/*
21785 * When attaching an index as a partition of a partitioned index which is a
21786 * primary key, verify that all the columns in the partition are marked NOT
21787 * NULL.
21788 */
21789static void
21791{
21792 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21793 {
21795 iinfo->ii_IndexAttrNumbers[i] - 1);
21796
21797 if (!att->attnotnull)
21798 ereport(ERROR,
21799 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21800 errmsg("invalid primary key definition"),
21801 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21802 NameStr(att->attname),
21803 RelationGetRelationName(partition)));
21804 }
21805}
21806
21807/*
21808 * Return an OID list of constraints that reference the given relation
21809 * that are marked as having a parent constraints.
21810 */
21811static List *
21813{
21814 Relation pg_constraint;
21815 HeapTuple tuple;
21816 SysScanDesc scan;
21817 ScanKeyData key[2];
21818 List *constraints = NIL;
21819
21820 /*
21821 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21822 * scan.
21823 */
21824 if (RelationGetIndexList(partition) == NIL ||
21827 return NIL;
21828
21829 /* Search for constraints referencing this table */
21830 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21831 ScanKeyInit(&key[0],
21832 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21833 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21834 ScanKeyInit(&key[1],
21835 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21836 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21837
21838 /* XXX This is a seqscan, as we don't have a usable index */
21839 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21840 while ((tuple = systable_getnext(scan)) != NULL)
21841 {
21842 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21843
21844 /*
21845 * We only need to process constraints that are part of larger ones.
21846 */
21847 if (!OidIsValid(constrForm->conparentid))
21848 continue;
21849
21850 constraints = lappend_oid(constraints, constrForm->oid);
21851 }
21852
21853 systable_endscan(scan);
21854 table_close(pg_constraint, AccessShareLock);
21855
21856 return constraints;
21857}
21858
21859/*
21860 * During DETACH PARTITION, verify that any foreign keys pointing to the
21861 * partitioned table would not become invalid. An error is raised if any
21862 * referenced values exist.
21863 */
21864static void
21866{
21867 List *constraints;
21868 ListCell *cell;
21869
21870 constraints = GetParentedForeignKeyRefs(partition);
21871
21872 foreach(cell, constraints)
21873 {
21874 Oid constrOid = lfirst_oid(cell);
21875 HeapTuple tuple;
21876 Form_pg_constraint constrForm;
21877 Relation rel;
21878 Trigger trig = {0};
21879
21880 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21881 if (!HeapTupleIsValid(tuple))
21882 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21883 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21884
21885 Assert(OidIsValid(constrForm->conparentid));
21886 Assert(constrForm->confrelid == RelationGetRelid(partition));
21887
21888 /* prevent data changes into the referencing table until commit */
21889 rel = table_open(constrForm->conrelid, ShareLock);
21890
21891 trig.tgoid = InvalidOid;
21892 trig.tgname = NameStr(constrForm->conname);
21894 trig.tgisinternal = true;
21895 trig.tgconstrrelid = RelationGetRelid(partition);
21896 trig.tgconstrindid = constrForm->conindid;
21897 trig.tgconstraint = constrForm->oid;
21898 trig.tgdeferrable = false;
21899 trig.tginitdeferred = false;
21900 /* we needn't fill in remaining fields */
21901
21902 RI_PartitionRemove_Check(&trig, rel, partition);
21903
21904 ReleaseSysCache(tuple);
21905
21906 table_close(rel, NoLock);
21907 }
21908}
21909
21910/*
21911 * resolve column compression specification to compression method.
21912 */
21913static char
21914GetAttributeCompression(Oid atttypid, const char *compression)
21915{
21916 char cmethod;
21917
21918 if (compression == NULL || strcmp(compression, "default") == 0)
21920
21921 /*
21922 * To specify a nondefault method, the column data type must be toastable.
21923 * Note this says nothing about whether the column's attstorage setting
21924 * permits compression; we intentionally allow attstorage and
21925 * attcompression to be independent. But with a non-toastable type,
21926 * attstorage could not be set to a value that would permit compression.
21927 *
21928 * We don't actually need to enforce this, since nothing bad would happen
21929 * if attcompression were non-default; it would never be consulted. But
21930 * it seems more user-friendly to complain about a certainly-useless
21931 * attempt to set the property.
21932 */
21933 if (!TypeIsToastable(atttypid))
21934 ereport(ERROR,
21935 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21936 errmsg("column data type %s does not support compression",
21937 format_type_be(atttypid))));
21938
21939 cmethod = CompressionNameToMethod(compression);
21940 if (!CompressionMethodIsValid(cmethod))
21941 ereport(ERROR,
21942 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21943 errmsg("invalid compression method \"%s\"", compression)));
21944
21945 return cmethod;
21946}
21947
21948/*
21949 * resolve column storage specification
21950 */
21951static char
21952GetAttributeStorage(Oid atttypid, const char *storagemode)
21953{
21954 char cstorage = 0;
21955
21956 if (pg_strcasecmp(storagemode, "plain") == 0)
21957 cstorage = TYPSTORAGE_PLAIN;
21958 else if (pg_strcasecmp(storagemode, "external") == 0)
21959 cstorage = TYPSTORAGE_EXTERNAL;
21960 else if (pg_strcasecmp(storagemode, "extended") == 0)
21961 cstorage = TYPSTORAGE_EXTENDED;
21962 else if (pg_strcasecmp(storagemode, "main") == 0)
21963 cstorage = TYPSTORAGE_MAIN;
21964 else if (pg_strcasecmp(storagemode, "default") == 0)
21965 cstorage = get_typstorage(atttypid);
21966 else
21967 ereport(ERROR,
21968 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21969 errmsg("invalid storage type \"%s\"",
21970 storagemode)));
21971
21972 /*
21973 * safety check: do not allow toasted storage modes unless column datatype
21974 * is TOAST-aware.
21975 */
21976 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
21977 ereport(ERROR,
21978 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21979 errmsg("column data type %s can only have storage PLAIN",
21980 format_type_be(atttypid))));
21981
21982 return cstorage;
21983}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:262
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition: acl.c:1103
void check_can_set_role(Oid member, Oid role)
Definition: acl.c:5325
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition: acl.c:5570
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
@ ACLCHECK_NOT_OWNER
Definition: acl.h:185
#define DatumGetAclP(X)
Definition: acl.h:120
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2639
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3853
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3821
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4075
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:2958
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4024
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition: amapi.c:148
Oid get_table_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:173
char * get_am_name(Oid amOid)
Definition: amcmds.c:192
#define ARR_NDIM(a)
Definition: array.h:290
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
#define ARR_DIMS(a)
Definition: array.h:294
#define ARR_HASNULL(a)
Definition: array.h:291
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3361
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1820
void free_attrmap(AttrMap *map)
Definition: attmap.c:56
AttrMap * make_attrmap(int maplen)
Definition: attmap.c:40
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:175
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:261
int16 AttrNumber
Definition: attnum.h:21
#define InvalidAttrNumber
Definition: attnum.h:23
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1472
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
Definition: tablespace.c:1426
Oid GetDefaultTablespace(char relpersistence, bool partitioned)
Definition: tablespace.c:1143
List * raw_parser(const char *str, RawParseMode mode)
Definition: parser.c:42
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6435
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
#define bms_is_empty(a)
Definition: bitmapset.h:118
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4873
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define NameStr(name)
Definition: c.h:717
uint16 bits16
Definition: c.h:510
uint32 SubTransactionId
Definition: c.h:627
#define gettext_noop(x)
Definition: c.h:1167
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:224
#define InvalidSubTransactionId
Definition: c.h:629
#define PointerIsValid(pointer)
Definition: c.h:734
int16_t int16
Definition: c.h:497
int32_t int32
Definition: c.h:498
#define MemSet(start, val, len)
Definition: c.h:991
uint32 CommandId
Definition: c.h:637
#define PG_INT16_MAX
Definition: c.h:557
#define OidIsValid(objectId)
Definition: c.h:746
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:230
bool IsSystemRelation(Relation relation)
Definition: catalog.c:73
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition: catalog.c:528
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:212
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:85
bool contain_mutable_functions(Node *clause)
Definition: clauses.c:371
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2256
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:539
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition: cluster.c:494
void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, bool check_constraints, bool is_internal, TransactionId frozenXid, MultiXactId cutoffMulti, char newrelpersistence)
Definition: cluster.c:1445
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
Definition: cluster.c:705
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition: cluster.c:554
CompareType
Definition: cmptype.h:32
@ COMPARE_OVERLAP
Definition: cmptype.h:40
@ COMPARE_EQ
Definition: cmptype.h:36
Oid collid
void ResetSequence(Oid seq_relid)
Definition: sequence.c:262
void SequenceChangePersistence(Oid relid, char newrelpersistence)
Definition: sequence.c:541
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition: comment.c:410
ObjectAddress CommentObject(CommentStmt *stmt)
Definition: comment.c:40
int32 defGetInt32(DefElem *def)
Definition: define.c:149
void performMultipleDeletions(const ObjectAddresses *objects, DropBehavior behavior, int flags)
Definition: dependency.c:332
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:273
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
Definition: dependency.c:2608
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2548
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2502
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2788
#define PERFORM_DELETION_CONCURRENTLY
Definition: dependency.h:93
DependencyType
Definition: dependency.h:32
@ DEPENDENCY_AUTO
Definition: dependency.h:34
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition: dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition: dependency.h:37
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
#define PERFORM_DELETION_QUIETLY
Definition: dependency.h:94
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:92
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
void hash_destroy(HTAB *hashp)
Definition: dynahash.c:865
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1420
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1385
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1158
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int errhint(const char *fmt,...)
Definition: elog.c:1318
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define _(x)
Definition: elog.c:91
#define PG_TRY(...)
Definition: elog.h:372
#define WARNING
Definition: elog.h:36
#define PG_END_TRY(...)
Definition: elog.h:397
#define DEBUG1
Definition: elog.h:30
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define NOTICE
Definition: elog.h:35
#define PG_FINALLY(...)
Definition: elog.h:389
#define ereport(elevel,...)
Definition: elog.h:149
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
void EventTriggerAlterTableStart(Node *parsetree)
void EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
void EventTriggerAlterTableRelid(Oid objectId)
void EventTriggerAlterTableEnd(void)
void EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
#define AT_REWRITE_ALTER_PERSISTENCE
Definition: event_trigger.h:40
#define AT_REWRITE_DEFAULT_VAL
Definition: event_trigger.h:41
#define AT_REWRITE_ACCESS_METHOD
Definition: event_trigger.h:43
#define AT_REWRITE_COLUMN_REWRITE
Definition: event_trigger.h:42
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:143
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:765
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:872
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition: execMain.c:1329
AttrNumber ExecRelGenVirtualNotNull(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, List *notnull_virtual_attrs)
Definition: execMain.c:2170
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1427
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1443
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1741
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1833
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1765
void FreeExecutorState(EState *estate)
Definition: execUtils.c:193
EState * CreateExecutorState(void)
Definition: execUtils.c:88
struct ResultRelInfo ResultRelInfo
#define GetPerTupleExprContext(estate)
Definition: executor.h:678
#define ResetExprContext(econtext)
Definition: executor.h:672
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:683
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:415
#define palloc0_object(type)
Definition: fe_memutils.h:75
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:684
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
#define SizeForFunctionCallInfo(nargs)
Definition: fmgr.h:102
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition: foreign.c:37
FdwRoutine * GetFdwRoutineByServerId(Oid serverid)
Definition: foreign.c:377
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:111
Oid GetForeignServerIdByRelId(Oid relid)
Definition: foreign.c:355
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
Definition: foreigncmds.c:110
char * format_type_with_typemod(Oid type_oid, int32 typemod)
Definition: format_type.c:362
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:388
bool IsBinaryUpgrade
Definition: globals.c:122
bool allowSystemTableMods
Definition: globals.c:131
Oid MyDatabaseTableSpace
Definition: globals.c:97
Oid MyDatabaseId
Definition: globals.c:95
#define newval
Assert(PointerIsAligned(start, uint64))
for(;;)
void RelationClearMissing(Relation rel)
Definition: heap.c:1954
List * heap_truncate_find_FKs(List *relationIds)
Definition: heap.c:3673
void StorePartitionKey(Relation rel, char strategy, int16 partnatts, AttrNumber *partattrs, List *partexprs, Oid *partopclass, Oid *partcollation)
Definition: heap.c:3800
void RemoveStatistics(Oid relid, AttrNumber attnum)
Definition: heap.c:3398
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Definition: heap.c:1112
void heap_truncate(List *relids)
Definition: heap.c:3493
void CheckAttributeType(const char *attname, Oid atttypid, Oid attcollation, List *containing_rowtypes, int flags)
Definition: heap.c:544
void heap_truncate_check_FKs(List *relations, bool tempTables)
Definition: heap.c:3578
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition: heap.c:3956
List * AddRelationNotNullConstraints(Relation rel, List *constraints, List *old_notnulls)
Definition: heap.c:2884
List * AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, bool is_local, bool is_internal, const char *queryString)
Definition: heap.c:2375
void InsertPgAttributeTuples(Relation pg_attribute_rel, TupleDesc tupdesc, Oid new_rel_oid, const FormExtraData_pg_attribute tupdesc_extra[], CatalogIndexState indstate)
Definition: heap.c:708
void heap_truncate_one_rel(Relation rel)
Definition: heap.c:3534
void StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
Definition: heap.c:2020
#define CHKATYPE_IS_PARTKEY
Definition: heap.h:25
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1314
BulkInsertState GetBulkInsertState(void)
Definition: heapam.c:1989
void FreeBulkInsertState(BulkInsertState bistate)
Definition: heapam.c:2006
#define XLOG_HEAP_TRUNCATE
Definition: heapam_xlog.h:36
#define XLH_TRUNCATE_RESTART_SEQS
Definition: heapam_xlog.h:127
#define SizeOfHeapTruncate
Definition: heapam_xlog.h:142
#define XLH_TRUNCATE_CASCADE
Definition: heapam_xlog.h:126
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition: heaptuple.c:1210
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:778
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:456
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:904
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
#define MaxHeapAttributeNumber
Definition: htup_details.h:48
#define stmt
Definition: indent_codes.h:59
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3583
bool CompareIndexInfo(const IndexInfo *info1, const IndexInfo *info2, const Oid *collations1, const Oid *collations2, const Oid *opfamilies1, const Oid *opfamilies2, const AttrMap *attmap)
Definition: index.c:2537
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition: index.c:3948
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2428
void index_check_primary_key(Relation heapRel, const IndexInfo *indexInfo, bool is_alter_table, const IndexStmt *stmt)
Definition: index.c:202
ObjectAddress index_constraint_create(Relation heapRelation, Oid indexRelationId, Oid parentConstraintId, const IndexInfo *indexInfo, const char *constraintName, char constraintType, bits16 constr_flags, bool allow_system_table_mods, bool is_internal)
Definition: index.c:1885
#define REINDEX_REL_PROCESS_TOAST
Definition: index.h:159
#define INDEX_CONSTR_CREATE_UPDATE_INDEX
Definition: index.h:94
#define INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
Definition: index.h:95
#define INDEX_CONSTR_CREATE_DEFERRABLE
Definition: index.h:92
#define INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
Definition: index.h:91
#define INDEX_CONSTR_CREATE_INIT_DEFERRED
Definition: index.h:93
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
ObjectAddress DefineIndex(Oid tableId, IndexStmt *stmt, Oid indexRelationId, Oid parentIndexId, Oid parentConstraintId, int total_parts, bool is_alter_table, bool check_rights, bool check_not_in_use, bool skip_build, bool quiet)
Definition: indexcmds.c:542
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4409
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2345
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition: indexcmds.c:178
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:435
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2260
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
long val
Definition: informix.c:689
static struct @165 value
static bool pg_add_s16_overflow(int16 a, int16 b, int16 *result)
Definition: int.h:67
void AcceptInvalidationMessages(void)
Definition: inval.c:929
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1621
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1677
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1655
int b
Definition: isn.c:74
int a
Definition: isn.c:73
int j
Definition: isn.c:78
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
Datum is_valid(PG_FUNCTION_ARGS)
Definition: isn.c:1110
List * lcons_oid(Oid datum, List *list)
Definition: list.c:531
List * lappend(List *list, void *datum)
Definition: list.c:339
List * list_difference_ptr(const List *list1, const List *list2)
Definition: list.c:1263
List * list_delete_nth_cell(List *list, int n)
Definition: list.c:767
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
List * list_concat_copy(const List *list1, const List *list2)
Definition: list.c:598
List * list_copy(const List *oldlist)
Definition: list.c:1573
List * lappend_int(List *list, int datum)
Definition: list.c:357
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
List * lcons(void *datum, List *list)
Definition: list.c:495
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:1380
void list_free(List *list)
Definition: list.c:1546
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
void list_free_deep(List *list)
Definition: list.c:1560
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:151
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:229
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:905
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:107
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition: lmgr.c:334
bool CheckRelationOidLockedByMe(Oid relid, LOCKMODE lockmode, bool orstronger)
Definition: lmgr.c:351
void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
Definition: lmgr.c:601
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:182
int LOCKMODE
Definition: lockdefs.h:26
#define NoLock
Definition: lockdefs.h:34
#define AccessExclusiveLock
Definition: lockdefs.h:43
#define ShareRowExclusiveLock
Definition: lockdefs.h:41
#define AccessShareLock
Definition: lockdefs.h:36
#define InplaceUpdateTupleLock
Definition: lockdefs.h:48
#define ShareUpdateExclusiveLock
Definition: lockdefs.h:39
#define RowShareLock
Definition: lockdefs.h:37
#define ShareLock
Definition: lockdefs.h:40
#define RowExclusiveLock
Definition: lockdefs.h:38
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2068
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:950
Oid get_constraint_index(Oid conoid)
Definition: lsyscache.c:1205
char get_typstorage(Oid typid)
Definition: lsyscache.c:2559
bool get_index_isreplident(Oid index_oid)
Definition: lsyscache.c:3695
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2143
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3196
char * get_collation_name(Oid colloid)
Definition: lsyscache.c:1127
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3741
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:167
char * get_constraint_name(Oid conoid)
Definition: lsyscache.c:1173
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:919
bool get_collation_isdeterministic(Oid colloid)
Definition: lsyscache.c:1146
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1393
Oid get_rel_relam(Oid relid)
Definition: lsyscache.c:2240
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3221
Oid get_rel_tablespace(Oid relid)
Definition: lsyscache.c:2194
Oid get_typ_typrelid(Oid typid)
Definition: lsyscache.c:2871
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2678
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2661
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3506
char get_constraint_type(Oid conoid)
Definition: lsyscache.c:1235
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:2025
#define TypeIsToastable(typid)
Definition: lsyscache.h:218
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:799
TypeName * makeTypeNameFromNameList(List *names)
Definition: makefuncs.c:531
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
ColumnDef * makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid)
Definition: makefuncs.c:565
Const * makeNullConst(Oid consttype, int32 consttypmod, Oid constcollid)
Definition: makefuncs.c:388
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:473
Constraint * makeNotNullConstraint(String *colname)
Definition: makefuncs.c:493
List * make_ands_implicit(Expr *clause)
Definition: makefuncs.c:810
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:2309
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:414
char * pstrdup(const char *in)
Definition: mcxt.c:2322
void pfree(void *pointer)
Definition: mcxt.c:2147
void * palloc0(Size size)
Definition: mcxt.c:1970
void * palloc(Size size)
Definition: mcxt.c:1940
MemoryContext CurrentMemoryContext
Definition: mcxt.c:159
MemoryContext CacheMemoryContext
Definition: mcxt.c:168
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:485
MemoryContext PortalContext
Definition: mcxt.c:174
#define AllocSetContextCreate
Definition: memutils.h:149
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:180
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:190
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:690
Oid GetUserId(void)
Definition: miscinit.c:520
MultiXactId ReadNextMultiXactId(void)
Definition: multixact.c:771
void namestrcpy(Name name, const char *str)
Definition: name.c:233
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition: namespace.c:739
bool isAnyTempNamespace(Oid namespaceId)
Definition: namespace.c:3687
Oid get_collation_oid(List *collname, bool missing_ok)
Definition: namespace.c:3971
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition: namespace.c:3459
RangeVar * makeRangeVarFromNameList(const List *names)
Definition: namespace.c:3554
Oid LookupNamespaceNoError(const char *nspname)
Definition: namespace.c:3355
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:441
@ RVR_MISSING_OK
Definition: namespace.h:72
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:80
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:821
Node * strip_implicit_coercions(Node *node)
Definition: nodeFuncs.c:705
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
#define copyObject(obj)
Definition: nodes.h:230
#define nodeTag(nodeptr)
Definition: nodes.h:139
#define makeNode(_type_)
Definition: nodes.h:161
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
#define InvokeObjectTruncateHook(objectId)
Definition: objectaccess.h:191
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:200
ObjectType get_relkind_objtype(char relkind)
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define ObjectAddressSubSet(addr, class_id, object_id, object_sub_id)
Definition: objectaddress.h:33
char * nodeToString(const void *obj)
Definition: outfuncs.c:797
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid)
bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, CoercionContext ccontext)
Definition: parse_coerce.c:557
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
CoercionPathType
Definition: parse_coerce.h:25
@ COERCION_PATH_NONE
Definition: parse_coerce.h:26
@ COERCION_PATH_RELABELTYPE
Definition: parse_coerce.h:28
void assign_expr_collations(ParseState *pstate, Node *expr)
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:118
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:39
@ EXPR_KIND_PARTITION_EXPRESSION
Definition: parse_node.h:80
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
Oid attnumCollationId(Relation rd, int attid)
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Oid attnumTypeId(Relation rd, int attid)
const NameData * attnumAttName(Relation rd, int attid)
void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, Oid *typeid_p, int32 *typmod_p)
Definition: parse_type.c:310
Type typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
Definition: parse_type.c:264
Oid GetColumnDefCollation(ParseState *pstate, const ColumnDef *coldef, Oid typeOid)
Definition: parse_type.c:540
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:291
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
AlterTableStmt * transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, const char *queryString, List **beforeStmts, List **afterStmts)
CreateStatsStmt * transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
PartitionBoundSpec * transformPartitionBound(ParseState *pstate, Relation parent, PartitionBoundSpec *spec)
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2809
#define ACL_MAINTAIN
Definition: parsenodes.h:90
#define ACL_USAGE
Definition: parsenodes.h:84
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2812
PartitionStrategy
Definition: parsenodes.h:882
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:885
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:883
ConstrType
Definition: parsenodes.h:2787
@ CONSTR_FOREIGN
Definition: parsenodes.h:2798
@ CONSTR_UNIQUE
Definition: parsenodes.h:2796
@ CONSTR_DEFAULT
Definition: parsenodes.h:2791
@ CONSTR_NOTNULL
Definition: parsenodes.h:2790
@ CONSTR_CHECK
Definition: parsenodes.h:2794
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2797
@ CONSTR_PRIMARY
Definition: parsenodes.h:2795
DropBehavior
Definition: parsenodes.h:2389
@ DROP_CASCADE
Definition: parsenodes.h:2391
@ DROP_RESTRICT
Definition: parsenodes.h:2390
ObjectType
Definition: parsenodes.h:2316
@ OBJECT_MATVIEW
Definition: parsenodes.h:2340
@ OBJECT_SCHEMA
Definition: parsenodes.h:2353
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2335
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2359
@ OBJECT_INDEX
Definition: parsenodes.h:2337
@ OBJECT_SEQUENCE
Definition: parsenodes.h:2354
@ OBJECT_TABLE
Definition: parsenodes.h:2358
@ OBJECT_VIEW
Definition: parsenodes.h:2368
@ OBJECT_TYPE
Definition: parsenodes.h:2366
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2357
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2330
AlterTableType
Definition: parsenodes.h:2408
@ AT_AddIndexConstraint
Definition: parsenodes.h:2430
@ AT_DropOf
Definition: parsenodes.h:2461
@ AT_SetOptions
Definition: parsenodes.h:2418
@ AT_DropIdentity
Definition: parsenodes.h:2473
@ AT_DisableTrigUser
Definition: parsenodes.h:2453
@ AT_DropNotNull
Definition: parsenodes.h:2413
@ AT_AddOf
Definition: parsenodes.h:2460
@ AT_ResetOptions
Definition: parsenodes.h:2419
@ AT_ReplicaIdentity
Definition: parsenodes.h:2462
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2445
@ AT_EnableRowSecurity
Definition: parsenodes.h:2463
@ AT_AddColumnToView
Definition: parsenodes.h:2410
@ AT_ResetRelOptions
Definition: parsenodes.h:2444
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2448
@ AT_DropOids
Definition: parsenodes.h:2440
@ AT_SetIdentity
Definition: parsenodes.h:2472
@ AT_ReAddStatistics
Definition: parsenodes.h:2474
@ AT_SetUnLogged
Definition: parsenodes.h:2439
@ AT_DisableTrig
Definition: parsenodes.h:2449
@ AT_SetCompression
Definition: parsenodes.h:2421
@ AT_DropExpression
Definition: parsenodes.h:2416
@ AT_AddIndex
Definition: parsenodes.h:2423
@ AT_EnableReplicaRule
Definition: parsenodes.h:2456
@ AT_ReAddIndex
Definition: parsenodes.h:2424
@ AT_DropConstraint
Definition: parsenodes.h:2431
@ AT_SetNotNull
Definition: parsenodes.h:2414
@ AT_ClusterOn
Definition: parsenodes.h:2436
@ AT_AddIdentity
Definition: parsenodes.h:2471
@ AT_ForceRowSecurity
Definition: parsenodes.h:2465
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2455
@ AT_SetAccessMethod
Definition: parsenodes.h:2441
@ AT_AlterColumnType
Definition: parsenodes.h:2433
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2470
@ AT_AddInherit
Definition: parsenodes.h:2458
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2427
@ AT_EnableTrig
Definition: parsenodes.h:2446
@ AT_DropColumn
Definition: parsenodes.h:2422
@ AT_ReAddComment
Definition: parsenodes.h:2432
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2434
@ AT_DisableTrigAll
Definition: parsenodes.h:2451
@ AT_EnableRule
Definition: parsenodes.h:2454
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2466
@ AT_DetachPartition
Definition: parsenodes.h:2469
@ AT_SetStatistics
Definition: parsenodes.h:2417
@ AT_AttachPartition
Definition: parsenodes.h:2468
@ AT_AddConstraint
Definition: parsenodes.h:2425
@ AT_DropInherit
Definition: parsenodes.h:2459
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2447
@ AT_SetLogged
Definition: parsenodes.h:2438
@ AT_SetStorage
Definition: parsenodes.h:2420
@ AT_DisableRule
Definition: parsenodes.h:2457
@ AT_DisableRowSecurity
Definition: parsenodes.h:2464
@ AT_SetRelOptions
Definition: parsenodes.h:2443
@ AT_ChangeOwner
Definition: parsenodes.h:2435
@ AT_EnableTrigUser
Definition: parsenodes.h:2452
@ AT_SetExpression
Definition: parsenodes.h:2415
@ AT_ReAddConstraint
Definition: parsenodes.h:2426
@ AT_SetTableSpace
Definition: parsenodes.h:2442
@ AT_GenericOptions
Definition: parsenodes.h:2467
@ AT_ColumnDefault
Definition: parsenodes.h:2411
@ AT_CookedColumnDefault
Definition: parsenodes.h:2412
@ AT_AlterConstraint
Definition: parsenodes.h:2428
@ AT_EnableTrigAll
Definition: parsenodes.h:2450
@ AT_DropCluster
Definition: parsenodes.h:2437
@ AT_ValidateConstraint
Definition: parsenodes.h:2429
@ AT_AddColumn
Definition: parsenodes.h:2409
#define ACL_REFERENCES
Definition: parsenodes.h:81
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2810
#define ACL_TRUNCATE
Definition: parsenodes.h:80
#define ACL_CREATE
Definition: parsenodes.h:85
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2811
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2808
@ RAW_PARSE_DEFAULT
Definition: parser.h:39
List * SystemFuncName(char *name)
void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
Definition: partbounds.c:2896
List * get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:249
void check_default_partition_contents(Relation parent, Relation default_rel, PartitionBoundSpec *new_spec)
Definition: partbounds.c:3251
List * RelationGetPartitionQual(Relation rel)
Definition: partcache.c:277
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:71
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition: partdesc.c:501
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:222
bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
Definition: partition.c:255
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:370
void update_default_partition_oid(Oid parentId, Oid defaultPartId)
Definition: partition.c:340
Oid index_get_partition(Relation partition, Oid indexId)
Definition: partition.c:176
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition: partition.c:53
Oid StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr, bool is_internal)
Definition: pg_attrdef.c:35
Oid GetAttrDefaultOid(Oid relid, AttrNumber attnum)
Definition: pg_attrdef.c:278
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid)
Definition: pg_attrdef.c:320
void RemoveAttrDefault(Oid relid, AttrNumber attnum, DropBehavior behavior, bool complain, bool internal)
Definition: pg_attrdef.c:152
NameData attname
Definition: pg_attribute.h:41
int16 attnum
Definition: pg_attribute.h:74
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
bool attnotnull
Definition: pg_attribute.h:123
void * arg
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
NameData relname
Definition: pg_class.h:38
FormData_pg_class * Form_pg_class
Definition: pg_class.h:156
#define PARTITION_MAX_KEYS
#define INDEX_MAX_KEYS
#define NAMEDATALEN
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isEnforced, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int16 conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
Definition: pg_constraint.c:51
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, const char *conname)
void FindFKPeriodOpers(Oid opclass, Oid *containedbyoperoid, Oid *aggedcontainedbyoperoid, Oid *intersectoperoid)
void RenameConstraintById(Oid conId, const char *newname)
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
void DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, AttrNumber *conkey, AttrNumber *confkey, Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs, int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)
void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType, ObjectAddresses *objsMoved)
List * RelationGetNotNullConstraints(Oid relid, bool cooked, bool include_noinh)
char * ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others)
Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
AttrNumber extractNotNullColumn(HeapTuple constrTup)
Oid get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
FormData_pg_constraint * Form_pg_constraint
@ CONSTRAINT_RELATION
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:45
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition: pg_depend.c:457
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:351
List * getOwnedSequences(Oid relid)
Definition: pg_depend.c:936
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:301
long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype, Oid refclassId, Oid refobjectId)
Definition: pg_depend.c:398
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:945
Oid get_index_constraint(Oid indexId)
Definition: pg_depend.c:988
bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
Definition: pg_depend.c:828
FormData_pg_depend * Form_pg_depend
Definition: pg_depend.h:72
FormData_pg_foreign_table * Form_pg_foreign_table
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
Definition: pg_inherits.c:552
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition: pg_inherits.c:58
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
Definition: pg_inherits.c:508
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:377
bool PartitionHasPendingDetach(Oid partoid)
Definition: pg_inherits.c:620
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h:45
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
#define lfirst_int(lc)
Definition: pg_list.h:173
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
#define list_make1_oid(x1)
Definition: pg_list.h:242
#define list_make1(x1)
Definition: pg_list.h:212
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469
#define for_each_from(cell, lst, N)
Definition: pg_list.h:414
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define linitial(l)
Definition: pg_list.h:178
#define list_make3(x1, x2, x3)
Definition: pg_list.h:216
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
#define foreach_oid(var, lst)
Definition: pg_list.h:471
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define linitial_oid(l)
Definition: pg_list.h:180
#define lfirst_oid(lc)
Definition: pg_list.h:174
#define list_make2(x1, x2)
Definition: pg_list.h:214
#define foreach_int(var, lst)
Definition: pg_list.h:470
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
List * GetRelationPublications(Oid relid)
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
Definition: pg_shdepend.c:316
void changeDependencyOnTablespace(Oid classId, Oid objectId, Oid newTablespaceId)
Definition: pg_shdepend.c:391
static char * buf
Definition: pg_test_fsync.c:72
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
void RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
Definition: pg_type.c:765
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:79
void pgstat_count_truncate(Relation rel)
Expr * expression_planner(Expr *expr)
Definition: planner.c:6645
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
#define snprintf
Definition: port.h:239
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
uintptr_t Datum
Definition: postgres.h:69
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:355
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:217
static Datum CharGetDatum(char X)
Definition: postgres.h:127
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
void CheckTableForSerializableConflictIn(Relation relation)
Definition: predicate.c:4419
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3123
bool predicate_implied_by(List *predicate_list, List *clause_list, bool weak)
Definition: predtest.c:152
Expr * canonicalize_qual(Expr *qual, bool is_check)
Definition: prepqual.c:293
char * c
#define PRS2_OLD_VARNO
Definition: primnodes.h:250
#define PRS2_NEW_VARNO
Definition: primnodes.h:251
OnCommitAction
Definition: primnodes.h:57
@ ONCOMMIT_DELETE_ROWS
Definition: primnodes.h:60
@ ONCOMMIT_NOOP
Definition: primnodes.h:58
@ ONCOMMIT_PRESERVE_ROWS
Definition: primnodes.h:59
@ ONCOMMIT_DROP
Definition: primnodes.h:61
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:753
@ IS_NOT_NULL
Definition: primnodes.h:1957
@ COERCION_ASSIGNMENT
Definition: primnodes.h:732
@ COERCION_IMPLICIT
Definition: primnodes.h:731
void * stringToNode(const char *str)
Definition: read.c:90
#define RelationGetForm(relation)
Definition: rel.h:510
#define RelationGetRelid(relation)
Definition: rel.h:516
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:712
#define RelationIsUsedAsCatalogTable(relation)
Definition: rel.h:397
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:578
#define RelationGetDescr(relation)
Definition: rel.h:542
#define RelationIsMapped(relation)
Definition: rel.h:565
#define RelationGetNumberOfAttributes(relation)
Definition: rel.h:522
#define RelationGetRelationName(relation)
Definition: rel.h:550
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:669
#define RelationGetNamespace(relation)
Definition: rel.h:557
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:535
#define RelationIsPermanent(relation)
Definition: rel.h:628
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4819
Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
Definition: relcache.c:5030
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:6086
int errtablecol(Relation rel, int attnum)
Definition: relcache.c:6049
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5193
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition: relcache.c:5286
void RelationSetNewRelfilenumber(Relation relation, char persistence)
Definition: relcache.c:3759
List * RelationGetFKeyList(Relation relation)
Definition: relcache.c:4714
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5080
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3962
int errtable(Relation rel)
Definition: relcache.c:6032
struct RelationData * Relation
Definition: relcache.h:27
@ INDEX_ATTR_BITMAP_KEY
Definition: relcache.h:61
@ INDEX_ATTR_BITMAP_PRIMARY_KEY
Definition: relcache.h:62
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition: relcache.h:63
Datum transformRelOptions(Datum oldOptions, List *defList, const char *namspace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1167
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1342
bytea * view_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2025
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2081
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2011
LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList)
Definition: reloptions.c:2135
bytea * attribute_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2096
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
Definition: reloptions.c:2046
#define HEAP_RELOPT_NAMESPACES
Definition: reloptions.h:61
Oid RelFileNumber
Definition: relpath.h:25
ForkNumber
Definition: relpath.h:56
@ MAIN_FORKNUM
Definition: relpath.h:58
@ INIT_FORKNUM
Definition: relpath.h:61
#define MAX_FORKNUM
Definition: relpath.h:70
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
void EnableDisableRule(Relation rel, const char *rulename, char fires_when)
#define RULE_FIRES_ON_ORIGIN
Definition: rewriteDefine.h:21
#define RULE_FIRES_ON_REPLICA
Definition: rewriteDefine.h:23
#define RULE_FIRES_ALWAYS
Definition: rewriteDefine.h:22
#define RULE_DISABLED
Definition: rewriteDefine.h:24
Query * get_view_query(Relation view)
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
Node * build_column_default(Relation rel, int attrno)
Node * expand_generated_columns_in_expr(Node *node, Relation rel, int rt_index)
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
Datum RI_FKey_check_ins(PG_FUNCTION_ARGS)
Definition: ri_triggers.c:474
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1519
void RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1813
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3210
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition: ruleutils.c:1627
char * pg_get_indexdef_string(Oid indexrelid)
Definition: ruleutils.c:1225
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:13019
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition: ruleutils.c:2675
char * pg_get_constraintdef_command(Oid constraintId)
Definition: ruleutils.c:2184
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
@ ForwardScanDirection
Definition: sdir.h:28
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition: smgr.c:481
void smgrclose(SMgrRelation reln)
Definition: smgr.c:374
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:462
TransactionId RecentXmin
Definition: snapmgr.c:159
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:342
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:853
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:669
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:811
void PopActiveSnapshot(void)
Definition: snapmgr.c:762
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:787
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:88
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
void check_stack_depth(void)
Definition: stack_depth.c:95
ObjectAddress CreateStatistics(CreateStatsStmt *stmt)
Definition: statscmds.c:62
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:916
void RelationPreserveStorage(RelFileLocator rlocator, bool atCommit)
Definition: storage.c:252
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition: storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition: storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition: storage.c:187
void RelationDropStorage(Relation rel)
Definition: storage.c:207
#define InvalidStrategy
Definition: stratnum.h:24
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
RoleSpec * newowner
Definition: parsenodes.h:2485
DropBehavior behavior
Definition: parsenodes.h:2488
AlterTableType subtype
Definition: parsenodes.h:2480
RangeVar * relation
Definition: parsenodes.h:2401
ObjectType objtype
Definition: parsenodes.h:2403
const char * queryString
Definition: utility.h:33
List * constraints
Definition: tablecmds.c:187
bool verify_new_notnull
Definition: tablecmds.c:190
List * changedConstraintDefs
Definition: tablecmds.c:203
Expr * partition_constraint
Definition: tablecmds.c:198
char newrelpersistence
Definition: tablecmds.c:197
List * changedStatisticsDefs
Definition: tablecmds.c:209
bool chgAccessMethod
Definition: tablecmds.c:192
List * afterStmts
Definition: tablecmds.c:189
char * clusterOnIndex
Definition: tablecmds.c:207
char * replicaIdentityIndex
Definition: tablecmds.c:206
List * changedStatisticsOids
Definition: tablecmds.c:208
List * changedIndexDefs
Definition: tablecmds.c:205
List * changedIndexOids
Definition: tablecmds.c:204
List * changedConstraintOids
Definition: tablecmds.c:202
bool validate_default
Definition: tablecmds.c:200
List * subcmds[AT_NUM_PASSES]
Definition: tablecmds.c:185
TupleDesc oldDesc
Definition: tablecmds.c:173
Relation rel
Definition: tablecmds.c:182
Definition: attmap.h:35
AttrNumber * attnums
Definition: attmap.h:36
bool is_not_null
Definition: parsenodes.h:742
char identity
Definition: parsenodes.h:748
RangeVar * identitySequence
Definition: parsenodes.h:749
List * constraints
Definition: parsenodes.h:754
Node * cooked_default
Definition: parsenodes.h:747
char * storage_name
Definition: parsenodes.h:745
char * colname
Definition: parsenodes.h:737
TypeName * typeName
Definition: parsenodes.h:738
char generated
Definition: parsenodes.h:751
bool is_from_type
Definition: parsenodes.h:743
Node * raw_default
Definition: parsenodes.h:746
char storage
Definition: parsenodes.h:744
bool is_local
Definition: parsenodes.h:741
int16 inhcount
Definition: parsenodes.h:740
char * compression
Definition: parsenodes.h:739
ParseLoc location
Definition: parsenodes.h:756
char * comment
Definition: parsenodes.h:3351
ObjectType objtype
Definition: parsenodes.h:3349
Node * object
Definition: parsenodes.h:3350
bool attisdropped
Definition: tupdesc.h:77
char attnullability
Definition: tupdesc.h:79
char * ccname
Definition: tupdesc.h:30
bool ccenforced
Definition: tupdesc.h:32
bool ccvalid
Definition: tupdesc.h:33
char * ccbin
Definition: tupdesc.h:31
bool initdeferred
Definition: parsenodes.h:2825
ParseLoc location
Definition: parsenodes.h:2866
bool reset_default_tblspc
Definition: parsenodes.h:2847
List * keys
Definition: parsenodes.h:2837
List * pk_attrs
Definition: parsenodes.h:2855
List * fk_del_set_cols
Definition: parsenodes.h:2861
bool fk_with_period
Definition: parsenodes.h:2856
ConstrType contype
Definition: parsenodes.h:2822
Oid old_pktable_oid
Definition: parsenodes.h:2863
bool is_no_inherit
Definition: parsenodes.h:2829
char fk_upd_action
Definition: parsenodes.h:2859
List * old_conpfeqop
Definition: parsenodes.h:2862
bool is_enforced
Definition: parsenodes.h:2826
char fk_matchtype
Definition: parsenodes.h:2858
bool pk_with_period
Definition: parsenodes.h:2857
char * cooked_expr
Definition: parsenodes.h:2832
bool initially_valid
Definition: parsenodes.h:2828
bool skip_validation
Definition: parsenodes.h:2827
bool deferrable
Definition: parsenodes.h:2824
Node * raw_expr
Definition: parsenodes.h:2830
char * conname
Definition: parsenodes.h:2823
RangeVar * pktable
Definition: parsenodes.h:2853
char fk_del_action
Definition: parsenodes.h:2860
List * fk_attrs
Definition: parsenodes.h:2854
Oid conoid
Definition: heap.h:39
char * name
Definition: heap.h:40
AttrNumber attnum
Definition: heap.h:41
bool skip_validation
Definition: heap.h:44
bool is_enforced
Definition: heap.h:43
bool is_no_inherit
Definition: heap.h:47
int16 inhcount
Definition: heap.h:46
bool is_local
Definition: heap.h:45
ConstrType contype
Definition: heap.h:37
Node * expr
Definition: heap.h:42
Node * whenClause
Definition: parsenodes.h:3110
List * transitionRels
Definition: parsenodes.h:3112
RangeVar * constrrel
Definition: parsenodes.h:3116
RangeVar * relation
Definition: parsenodes.h:3101
char * defname
Definition: parsenodes.h:826
bool missing_ok
Definition: parsenodes.h:3326
List * objects
Definition: parsenodes.h:3323
ObjectType removeType
Definition: parsenodes.h:3324
bool concurrent
Definition: parsenodes.h:3327
DropBehavior behavior
Definition: parsenodes.h:3325
int es_instrument
Definition: execnodes.h:718
MemoryContext es_query_cxt
Definition: execnodes.h:708
List * es_opened_result_relations
Definition: execnodes.h:686
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:268
ExecForeignTruncate_function ExecForeignTruncate
Definition: fdwapi.h:263
bool conenforced
Definition: rel.h:288
Oid funcid
Definition: primnodes.h:767
List * args
Definition: primnodes.h:785
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86
Definition: dynahash.c:220
ItemPointerData t_self
Definition: htup.h:65
amoptions_function amoptions
Definition: amapi.h:302
bool amcanunique
Definition: amapi.h:256
bool ii_Unique
Definition: execnodes.h:209
int ii_NumIndexKeyAttrs
Definition: execnodes.h:197
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:198
bool reset_default_tblspc
Definition: parsenodes.h:3471
char * idxname
Definition: parsenodes.h:3445
char * idxcomment
Definition: parsenodes.h:3455
Definition: lock.h:166
Definition: pg_list.h:54
AttrNumber attnum
Definition: tablecmds.c:236
bool is_generated
Definition: tablecmds.c:239
ExprState * exprstate
Definition: tablecmds.c:238
char * name
Definition: tablecmds.c:216
ConstrType contype
Definition: tablecmds.c:217
bool conwithperiod
Definition: tablecmds.c:220
Node * qual
Definition: tablecmds.c:222
ExprState * qualstate
Definition: tablecmds.c:223
Definition: nodes.h:135
NullTestType nulltesttype
Definition: primnodes.h:1964
ParseLoc location
Definition: primnodes.h:1967
Expr * arg
Definition: primnodes.h:1963
SubTransactionId creating_subid
Definition: tablecmds.c:127
SubTransactionId deleting_subid
Definition: tablecmds.c:128
OnCommitAction oncommit
Definition: tablecmds.c:118
const char * p_sourcetext
Definition: parse_node.h:209
PartitionBoundSpec * bound
Definition: parsenodes.h:958
RangeVar * name
Definition: parsenodes.h:957
List * collation
Definition: parsenodes.h:876
ParseLoc location
Definition: parsenodes.h:878
List * opclass
Definition: parsenodes.h:877
List * partParams
Definition: parsenodes.h:897
ParseLoc location
Definition: parsenodes.h:898
PartitionStrategy strategy
Definition: parsenodes.h:896
char * relname
Definition: primnodes.h:83
bool inh
Definition: primnodes.h:86
char * schemaname
Definition: primnodes.h:80
Node * raw_default
Definition: heap.h:31
AttrNumber attnum
Definition: heap.h:30
char generated
Definition: heap.h:32
Node * stmt
Definition: parsenodes.h:2071
RelFileNumber relNumber
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
SubTransactionId rd_firstRelfilelocatorSubid
Definition: rel.h:106
int rd_refcnt
Definition: rel.h:59
TriggerDesc * trigdesc
Definition: rel.h:117
bool rd_islocaltemp
Definition: rel.h:61
TupleDesc rd_att
Definition: rel.h:112
Form_pg_index rd_index
Definition: rel.h:192
bool rd_isnailed
Definition: rel.h:62
Oid rd_id
Definition: rel.h:113
SubTransactionId rd_newRelfilelocatorSubid
Definition: rel.h:104
SubTransactionId rd_createSubid
Definition: rel.h:103
RelFileLocator rd_locator
Definition: rel.h:57
Oid * rd_opfamily
Definition: rel.h:207
Oid * rd_indcollation
Definition: rel.h:217
Form_pg_class rd_rel
Definition: rel.h:111
Relation ri_RelationDesc
Definition: execnodes.h:475
TransactionId xmin
Definition: snapshot.h:153
Definition: value.h:64
NodeTag type
Definition: trigger.h:33
Relation tg_relation
Definition: trigger.h:35
TriggerEvent tg_event
Definition: trigger.h:34
TupleTableSlot * tg_trigslot
Definition: trigger.h:39
Trigger * tg_trigger
Definition: trigger.h:38
HeapTuple tg_trigtuple
Definition: trigger.h:36
char tgenabled
Definition: reltrigger.h:30
Oid tgoid
Definition: reltrigger.h:25
Oid tgconstrindid
Definition: reltrigger.h:34
Oid tgconstraint
Definition: reltrigger.h:35
Oid tgconstrrelid
Definition: reltrigger.h:33
char * tgname
Definition: reltrigger.h:27
bool tgdeferrable
Definition: reltrigger.h:36
bool tginitdeferred
Definition: reltrigger.h:37
bool tgisinternal
Definition: reltrigger.h:31
bool has_generated_virtual
Definition: tupdesc.h:47
bool has_not_null
Definition: tupdesc.h:45
ConstrCheck * check
Definition: tupdesc.h:41
uint16 num_check
Definition: tupdesc.h:44
TupleConstr * constr
Definition: tupdesc.h:141
Oid tts_tableOid
Definition: tuptable.h:130
AttrNumber tts_nvalid
Definition: tuptable.h:120
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
bool setof
Definition: parsenodes.h:281
List * arrayBounds
Definition: parsenodes.h:285
Definition: primnodes.h:262
const char * skipping_msg
Definition: tablecmds.c:250
int nonexistent_code
Definition: tablecmds.c:248
const char * nonexistent_msg
Definition: tablecmds.c:249
const char * drophint_msg
Definition: tablecmds.c:252
const char * nota_msg
Definition: tablecmds.c:251
Definition: type.h:96
Definition: c.h:697
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:704
Definition: regguts.h:323
bool superuser(void)
Definition: superuser.c:46
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname)
Definition: syscache.c:503
HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
Definition: syscache.c:566
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCacheLocked1(int cacheId, Datum key1)
Definition: syscache.c:287
HeapTuple SearchSysCacheLockedCopy1(int cacheId, Datum key1)
Definition: syscache.c:404
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum)
Definition: syscache.c:543
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:600
bool SearchSysCacheExistsAttName(Oid relid, const char *attname)
Definition: syscache.c:522
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:232
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:480
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:631
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:91
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:102
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: table.c:83
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:92
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:113
char * default_table_access_method
Definition: tableam.c:49
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:59
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:870
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:979
#define TABLE_INSERT_SKIP_FSM
Definition: tableam.h:253
static void table_finish_bulk_insert(Relation rel, int options)
Definition: tableam.h:1555
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, int options, struct BulkInsertStateData *bistate)
Definition: tableam.h:1362
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition: tableam.h:1611
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1015
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition: tablecmds.c:3833
void ResetRelRewrite(Oid myrelid)
Definition: tablecmds.c:4352
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
Definition: tablecmds.c:10580
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17392
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17759
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode)
Definition: tablecmds.c:15847
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5291
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17153
ObjectAddress RenameRelation(RenameStmt *stmt)
Definition: tablecmds.c:4195
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6551
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:18250
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12140
static ColumnDef * MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3405
static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, LOCKMODE lockmode, Oid parentInsTrigger, Oid parentUpdTrigger, bool with_period)
Definition: tablecmds.c:10970
#define ATT_TABLE
Definition: tablecmds.c:328
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
Definition: tablecmds.c:19996
static const char * storage_name(char c)
Definition: tablecmds.c:2450
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8766
void AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
Definition: tablecmds.c:19351
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition: tablecmds.c:7688
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8115
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
Definition: tablecmds.c:15520
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16745
static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
Definition: tablecmds.c:18056
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
Definition: tablecmds.c:11945
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17842
#define AT_NUM_PASSES
Definition: tablecmds.c:166
void PreCommit_on_commit_actions(void)
Definition: tablecmds.c:19212
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:13995
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
Definition: tablecmds.c:11863
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8452
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:21914
static int findAttrByName(const char *attributeName, const List *columns)
Definition: tablecmds.c:3599
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15271
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
Definition: tablecmds.c:14594
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9804
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12847
void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4523
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15322
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
Definition: tablecmds.c:13554
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition: tablecmds.c:7706
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7731
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13403
#define ATT_SEQUENCE
Definition: tablecmds.c:335
void RemoveRelations(DropStmt *drop)
Definition: tablecmds.c:1527
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
Definition: tablecmds.c:16507
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6880
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15184
static List * GetParentedForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21812
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18946
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9730
ObjectAddress renameatt(RenameStmt *stmt)
Definition: tablecmds.c:3998
void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:15964
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:19478
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4438
static void DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
Definition: tablecmds.c:1500
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4597
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx)
Definition: tablecmds.c:21790
struct ForeignTruncateInfo ForeignTruncateInfo
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:20794
static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
Definition: tablecmds.c:19068
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:3978
TupleDesc BuildDescForRelation(const List *columns)
Definition: tablecmds.c:1369
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:18526
void AtEOXact_on_commit_actions(bool isCommit)
Definition: tablecmds.c:19319
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:13583
struct OnCommitItem OnCommitItem
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4405
static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12554
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13775
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:17096
static void AttachPartitionForeignKey(List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11735
static List * MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
Definition: tablecmds.c:3156
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition: tablecmds.c:3784
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:21449
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:6850
static void truncate_check_activity(Relation rel)
Definition: tablecmds.c:2427
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:21688
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
Definition: tablecmds.c:12793
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:10001
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15778
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12074
static ObjectAddress addFkConstraint(addFkConstraintSides fkside, char *constraintname, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool is_internal, bool with_period)
Definition: tablecmds.c:10656
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15199
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:9015
static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13138
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *constrname, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:7902
static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode, ObjectAddresses *addrs)
Definition: tablecmds.c:9248
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11392
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9094
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4859
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3682
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:18496
void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName)
Definition: tablecmds.c:6925
static void truncate_check_perms(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2409
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2361
#define ATT_INDEX
Definition: tablecmds.c:331
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:16270
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3554
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
Definition: tablecmds.c:14958
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:8720
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12283
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8200
static ObjectAddress rename_constraint_internal(Oid myrelid, Oid mytypid, const char *oldconname, const char *newconname, bool recurse, bool recursing, int expected_parents)
Definition: tablecmds.c:4036
static void DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
Definition: tablecmds.c:1452
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
Definition: tablecmds.c:15351
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5700
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18909
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9846
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11180
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17530
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9220
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3636
static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3235
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6728
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6585
static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12935
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:16537
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8335
ObjectAddress AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
Definition: tablecmds.c:18838
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15215
addFkConstraintSides
Definition: tablecmds.c:354
@ addFkReferencingSide
Definition: tablecmds.c:356
@ addFkBothSides
Definition: tablecmds.c:357
@ addFkReferencedSide
Definition: tablecmds.c:355
void ExecuteTruncate(TruncateStmt *stmt)
Definition: tablecmds.c:1850
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)
Definition: tablecmds.c:18294
#define ATT_FOREIGN_TABLE
Definition: tablecmds.c:333
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:7182
static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, PartitionStrategy strategy)
Definition: tablecmds.c:19677
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:20637
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:20455
static void StoreCatalogInheritance(Oid relationId, List *supers, bool child_is_partition)
Definition: tablecmds.c:3510
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:18555
static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, const Oid fkrelid, const Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
Definition: tablecmds.c:12354
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15807
struct AlteredTableInfo AlteredTableInfo
Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:4464
static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12751
static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, const Oid fkrelid, const Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
Definition: tablecmds.c:12702
struct NewConstraint NewConstraint
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8870
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6805
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:20965
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15734
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11149
static void addFkRecurseReferenced(Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, Oid parentDelTrigger, Oid parentUpdTrigger, bool with_period)
Definition: tablecmds.c:10832
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition: tablecmds.c:7635
static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd **cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:7206
AlterTablePass
Definition: tablecmds.c:149
@ AT_PASS_ADD_CONSTR
Definition: tablecmds.c:158
@ AT_PASS_ADD_OTHERCONSTR
Definition: tablecmds.c:162
@ AT_PASS_OLD_CONSTR
Definition: tablecmds.c:156
@ AT_PASS_ADD_COL
Definition: tablecmds.c:153
@ AT_PASS_OLD_INDEX
Definition: tablecmds.c:155
@ AT_PASS_ALTER_TYPE
Definition: tablecmds.c:152
@ AT_PASS_ADD_INDEXCONSTR
Definition: tablecmds.c:160
@ AT_PASS_DROP
Definition: tablecmds.c:151
@ AT_PASS_MISC
Definition: tablecmds.c:163
@ AT_PASS_COL_ATTRS
Definition: tablecmds.c:159
@ AT_PASS_SET_EXPRESSION
Definition: tablecmds.c:154
@ AT_PASS_ADD_INDEX
Definition: tablecmds.c:161
@ AT_PASS_UNSET
Definition: tablecmds.c:150
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
Definition: tablecmds.c:14641
static void validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, bool hasperiod)
Definition: tablecmds.c:13612
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17335
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13035
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3739
void remove_on_commit_action(Oid relid)
Definition: tablecmds.c:19189
static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
Definition: tablecmds.c:21334
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16371
#define ATT_PARTITIONED_INDEX
Definition: tablecmds.c:334
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17266
static const struct dropmsgstrings dropmsgstringarray[]
Definition: tablecmds.c:255
static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
Definition: tablecmds.c:18713
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8566
ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, ObjectAddress *typaddress, const char *queryString)
Definition: tablecmds.c:763
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13712
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
Definition: tablecmds.c:12633
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9445
static void DropClonedTriggersFromPartition(Oid partitionId)
Definition: tablecmds.c:21376
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:20059
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:18108
static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:1691
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4552
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12013
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7132
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17717
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21865
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9638
#define ATT_VIEW
Definition: tablecmds.c:329
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19023
#define ATT_PARTITIONED_TABLE
Definition: tablecmds.c:336
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5365
static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12497
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:18382
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13910
static void ATPrepAddInherit(Relation child_rel)
Definition: tablecmds.c:17131
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:21299
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9157
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20132
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:7083
Oid AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
Definition: tablecmds.c:16877
static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids, Oid *attcollids, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13300
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8229
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:16417
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:13930
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9554
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:18636
static PartitionSpec * transformPartitionSpec(Relation rel, PartitionSpec *partspec)
Definition: tablecmds.c:19619
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4894
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:17039
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4259
static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
Definition: tablecmds.c:7829
static bool tryAttachPartitionForeignKey(List **wqueue, ForeignKeyCacheInfo *fk, Relation partition, Oid parentConstrOid, int numfks, AttrNumber *mapped_conkey, AttrNumber *confkey, Oid *conpfeqop, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11639
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16838
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:14291
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
Definition: tablecmds.c:13245
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:16205
#define ATT_COMPOSITE_TYPE
Definition: tablecmds.c:332
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:21503
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:19941
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19386
static List * on_commits
Definition: tablecmds.c:131
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:17363
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9617
ObjectAddress RenameConstraint(RenameStmt *stmt)
Definition: tablecmds.c:4145
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5827
void register_on_commit_action(Oid relid, OnCommitAction action)
Definition: tablecmds.c:19153
static void RangeVarCallbackForTruncate(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19422
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:16339
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:21952
void RangeVarCallbackOwnsRelation(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19446
#define ATT_MATVIEW
Definition: tablecmds.c:330
#define child_dependency_type(child_is_partition)
Definition: tablecmds.c:365
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
Definition: tablecmds.c:21665
void ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
Definition: tablecmds.c:1974
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
Definition: tablecmds.c:6115
static List * MergeAttributes(List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
Definition: tablecmds.c:2535
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:17114
struct NewColumnValue NewColumnValue
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
Definition: tablecmds.c:16383
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:8078
const char * GetCompressionMethodName(char method)
char CompressionNameToMethod(const char *compression)
#define CompressionMethodIsValid(cm)
#define InvalidCompressionMethod
void AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
Definition: toasting.c:58
void ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:3231
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: trigger.c:1726
const char * FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
Definition: trigger.c:2277
void ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:3278
ObjectAddress CreateTrigger(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
Definition: trigger.c:160
void TriggerSetParentTrigger(Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
Definition: trigger.c:1220
ObjectAddress CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
Definition: trigger.c:177
bool AfterTriggerPendingOnRel(Oid relid)
Definition: trigger.c:6024
void AfterTriggerEndQuery(EState *estate)
Definition: trigger.c:5088
void AfterTriggerBeginQuery(void)
Definition: trigger.c:5053
#define RI_TRIGGER_FK
Definition: trigger.h:284
#define TRIGGER_FIRES_ON_ORIGIN
Definition: trigger.h:149
#define TRIGGER_DISABLED
Definition: trigger.h:152
#define TRIGGER_FIRES_ON_REPLICA
Definition: trigger.h:151
#define TRIGGER_EVENT_ROW
Definition: trigger.h:98
#define TRIGGER_FIRES_ALWAYS
Definition: trigger.h:150
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:92
#define RI_TRIGGER_PK
Definition: trigger.h:283
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:333
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition: tupdesc.c:1085
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:175
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition: tupdesc.c:117
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition: tupdesc.c:1019
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:835
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:219
#define ATTNULLABLE_VALID
Definition: tupdesc.h:86
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:175
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:458
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:372
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition: tuptable.h:385
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1922
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1489
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition: typecmds.c:3978
ObjectAddress AlterDomainAddConstraint(List *names, Node *newConstraint, ObjectAddress *constrAddr)
Definition: typecmds.c:2927
void checkDomainOwner(HeapTuple tup)
Definition: typecmds.c:3477
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4147
List * roleSpecsToIds(List *memberNames)
Definition: user.c:1652
void SwitchToUntrustedUser(Oid userid, UserContext *context)
Definition: usercontext.c:33
void RestoreUserContext(UserContext *context)
Definition: usercontext.c:87
void ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
Definition: utility.c:1959
#define MAX_STATISTICS_TARGET
Definition: vacuum.h:324
String * makeString(char *str)
Definition: value.c:63
#define intVal(v)
Definition: value.h:79
#define strVal(v)
Definition: value.h:82
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:296
#define VARDATA_ANY(PTR)
Definition: varatt.h:324
const char * name
SubTransactionId GetCurrentSubTransactionId(void)
Definition: xact.c:791
void CommandCounterIncrement(void)
Definition: xact.c:1100
void StartTransactionCommand(void)
Definition: xact.c:3059
void CommitTransactionCommand(void)
Definition: xact.c:3157
int MyXactFlags
Definition: xact.c:136
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:829
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:102
#define XLogLogicalInfoActive()
Definition: xlog.h:126
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:154
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:474
void XLogRegisterData(const void *data, uint32 len)
Definition: xloginsert.c:364
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:456
void XLogBeginInsert(void)
Definition: xloginsert.c:149