PostgreSQL Source Code git master
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"
47#include "catalog/pg_opclass.h"
48#include "catalog/pg_policy.h"
49#include "catalog/pg_proc.h"
51#include "catalog/pg_rewrite.h"
54#include "catalog/pg_trigger.h"
55#include "catalog/pg_type.h"
56#include "catalog/storage.h"
58#include "catalog/toasting.h"
59#include "commands/cluster.h"
60#include "commands/comment.h"
61#include "commands/defrem.h"
63#include "commands/sequence.h"
64#include "commands/tablecmds.h"
65#include "commands/tablespace.h"
66#include "commands/trigger.h"
67#include "commands/typecmds.h"
68#include "commands/user.h"
69#include "commands/vacuum.h"
70#include "common/int.h"
71#include "executor/executor.h"
72#include "foreign/fdwapi.h"
73#include "foreign/foreign.h"
74#include "miscadmin.h"
75#include "nodes/makefuncs.h"
76#include "nodes/nodeFuncs.h"
77#include "nodes/parsenodes.h"
78#include "optimizer/optimizer.h"
79#include "parser/parse_coerce.h"
81#include "parser/parse_expr.h"
83#include "parser/parse_type.h"
85#include "parser/parser.h"
88#include "pgstat.h"
92#include "storage/bufmgr.h"
93#include "storage/lmgr.h"
94#include "storage/lock.h"
95#include "storage/predicate.h"
96#include "storage/smgr.h"
97#include "tcop/utility.h"
98#include "utils/acl.h"
99#include "utils/builtins.h"
100#include "utils/fmgroids.h"
101#include "utils/inval.h"
102#include "utils/lsyscache.h"
103#include "utils/memutils.h"
104#include "utils/partcache.h"
105#include "utils/relcache.h"
106#include "utils/ruleutils.h"
107#include "utils/snapmgr.h"
108#include "utils/syscache.h"
109#include "utils/timestamp.h"
110#include "utils/typcache.h"
111#include "utils/usercontext.h"
112
113/*
114 * ON COMMIT action list
115 */
116typedef struct OnCommitItem
117{
118 Oid relid; /* relid of relation */
119 OnCommitAction oncommit; /* what to do at end of xact */
120
121 /*
122 * If this entry was created during the current transaction,
123 * creating_subid is the ID of the creating subxact; if created in a prior
124 * transaction, creating_subid is zero. If deleted during the current
125 * transaction, deleting_subid is the ID of the deleting subxact; if no
126 * deletion request is pending, deleting_subid is zero.
127 */
131
133
134
135/*
136 * State information for ALTER TABLE
137 *
138 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
139 * structs, one for each table modified by the operation (the named table
140 * plus any child tables that are affected). We save lists of subcommands
141 * to apply to this table (possibly modified by parse transformation steps);
142 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
143 * necessary information is stored in the constraints and newvals lists.
144 *
145 * Phase 2 is divided into multiple passes; subcommands are executed in
146 * a pass determined by subcommand type.
147 */
148
149typedef enum AlterTablePass
150{
151 AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
152 AT_PASS_DROP, /* DROP (all flavors) */
153 AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
154 AT_PASS_ADD_COL, /* ADD COLUMN */
155 AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
156 AT_PASS_OLD_INDEX, /* re-add existing indexes */
157 AT_PASS_OLD_CONSTR, /* re-add existing constraints */
158 /* We could support a RENAME COLUMN pass here, but not currently used */
159 AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
160 AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
161 AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
162 AT_PASS_ADD_INDEX, /* ADD indexes */
163 AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
164 AT_PASS_MISC, /* other stuff */
166
167#define AT_NUM_PASSES (AT_PASS_MISC + 1)
168
169typedef struct AlteredTableInfo
170{
171 /* Information saved before any work commences: */
172 Oid relid; /* Relation to work on */
173 char relkind; /* Its relkind */
174 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
175
176 /*
177 * Transiently set during Phase 2, normally set to NULL.
178 *
179 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
180 * returns control. This can be exploited by ATExecCmd subroutines to
181 * close/reopen across transaction boundaries.
182 */
184
185 /* Information saved by Phase 1 for Phase 2: */
186 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
187 /* Information saved by Phases 1/2 for Phase 3: */
188 List *constraints; /* List of NewConstraint */
189 List *newvals; /* List of NewColumnValue */
190 List *afterStmts; /* List of utility command parsetrees */
191 bool verify_new_notnull; /* T if we should recheck NOT NULL */
192 int rewrite; /* Reason for forced rewrite, if any */
193 bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
194 Oid newAccessMethod; /* new access method; 0 means no change,
195 * if above is true */
196 Oid newTableSpace; /* new tablespace; 0 means no change */
197 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
198 char newrelpersistence; /* if above is true */
199 Expr *partition_constraint; /* for attach partition validation */
200 /* true, if validating default due to some other attach/detach */
202 /* Objects to rebuild after completing ALTER TYPE operations */
203 List *changedConstraintOids; /* OIDs of constraints to rebuild */
204 List *changedConstraintDefs; /* string definitions of same */
205 List *changedIndexOids; /* OIDs of indexes to rebuild */
206 List *changedIndexDefs; /* string definitions of same */
207 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
208 char *clusterOnIndex; /* index to use for CLUSTER */
209 List *changedStatisticsOids; /* OIDs of statistics to rebuild */
210 List *changedStatisticsDefs; /* string definitions of same */
212
213/* Struct describing one new constraint to check in Phase 3 scan */
214/* Note: new not-null constraints are handled elsewhere */
215typedef struct NewConstraint
216{
217 char *name; /* Constraint name, or NULL if none */
218 ConstrType contype; /* CHECK or FOREIGN */
219 Oid refrelid; /* PK rel, if FOREIGN */
220 Oid refindid; /* OID of PK's index, if FOREIGN */
221 bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
222 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
223 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
224 ExprState *qualstate; /* Execution state for CHECK expr */
226
227/*
228 * Struct describing one new column value that needs to be computed during
229 * Phase 3 copy (this could be either a new column with a non-null default, or
230 * a column that we're changing the type of). Columns without such an entry
231 * are just copied from the old table during ATRewriteTable. Note that the
232 * expr is an expression over *old* table values, except when is_generated
233 * is true; then it is an expression over columns of the *new* tuple.
234 */
235typedef struct NewColumnValue
236{
237 AttrNumber attnum; /* which column */
238 Expr *expr; /* expression to compute */
239 ExprState *exprstate; /* execution state */
240 bool is_generated; /* is it a GENERATED expression? */
242
243/*
244 * Error-reporting support for RemoveRelations
245 */
247{
248 char kind;
250 const char *nonexistent_msg;
251 const char *skipping_msg;
252 const char *nota_msg;
253 const char *drophint_msg;
254};
255
256static const struct dropmsgstrings dropmsgstringarray[] = {
257 {RELKIND_RELATION,
259 gettext_noop("table \"%s\" does not exist"),
260 gettext_noop("table \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a table"),
262 gettext_noop("Use DROP TABLE to remove a table.")},
263 {RELKIND_SEQUENCE,
265 gettext_noop("sequence \"%s\" does not exist"),
266 gettext_noop("sequence \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not a sequence"),
268 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
269 {RELKIND_VIEW,
271 gettext_noop("view \"%s\" does not exist"),
272 gettext_noop("view \"%s\" does not exist, skipping"),
273 gettext_noop("\"%s\" is not a view"),
274 gettext_noop("Use DROP VIEW to remove a view.")},
275 {RELKIND_MATVIEW,
277 gettext_noop("materialized view \"%s\" does not exist"),
278 gettext_noop("materialized view \"%s\" does not exist, skipping"),
279 gettext_noop("\"%s\" is not a materialized view"),
280 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
281 {RELKIND_INDEX,
282 ERRCODE_UNDEFINED_OBJECT,
283 gettext_noop("index \"%s\" does not exist"),
284 gettext_noop("index \"%s\" does not exist, skipping"),
285 gettext_noop("\"%s\" is not an index"),
286 gettext_noop("Use DROP INDEX to remove an index.")},
287 {RELKIND_COMPOSITE_TYPE,
288 ERRCODE_UNDEFINED_OBJECT,
289 gettext_noop("type \"%s\" does not exist"),
290 gettext_noop("type \"%s\" does not exist, skipping"),
291 gettext_noop("\"%s\" is not a type"),
292 gettext_noop("Use DROP TYPE to remove a type.")},
293 {RELKIND_FOREIGN_TABLE,
294 ERRCODE_UNDEFINED_OBJECT,
295 gettext_noop("foreign table \"%s\" does not exist"),
296 gettext_noop("foreign table \"%s\" does not exist, skipping"),
297 gettext_noop("\"%s\" is not a foreign table"),
298 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
299 {RELKIND_PARTITIONED_TABLE,
301 gettext_noop("table \"%s\" does not exist"),
302 gettext_noop("table \"%s\" does not exist, skipping"),
303 gettext_noop("\"%s\" is not a table"),
304 gettext_noop("Use DROP TABLE to remove a table.")},
305 {RELKIND_PARTITIONED_INDEX,
306 ERRCODE_UNDEFINED_OBJECT,
307 gettext_noop("index \"%s\" does not exist"),
308 gettext_noop("index \"%s\" does not exist, skipping"),
309 gettext_noop("\"%s\" is not an index"),
310 gettext_noop("Use DROP INDEX to remove an index.")},
311 {'\0', 0, NULL, NULL, NULL, NULL}
312};
313
314/* communication between RemoveRelations and RangeVarCallbackForDropRelation */
316{
317 /* These fields are set by RemoveRelations: */
320 /* These fields are state to track which subsidiary locks are held: */
323 /* These fields are passed back by RangeVarCallbackForDropRelation: */
326};
327
328/* Alter table target-type flags for ATSimplePermissions */
329#define ATT_TABLE 0x0001
330#define ATT_VIEW 0x0002
331#define ATT_MATVIEW 0x0004
332#define ATT_INDEX 0x0008
333#define ATT_COMPOSITE_TYPE 0x0010
334#define ATT_FOREIGN_TABLE 0x0020
335#define ATT_PARTITIONED_INDEX 0x0040
336#define ATT_SEQUENCE 0x0080
337#define ATT_PARTITIONED_TABLE 0x0100
338
339/*
340 * ForeignTruncateInfo
341 *
342 * Information related to truncation of foreign tables. This is used for
343 * the elements in a hash table. It uses the server OID as lookup key,
344 * and includes a per-server list of all foreign tables involved in the
345 * truncation.
346 */
348{
352
353/* Partial or complete FK creation in addFkConstraint() */
355{
360
361/*
362 * Partition tables are expected to be dropped when the parent partitioned
363 * table gets dropped. Hence for partitioning we use AUTO dependency.
364 * Otherwise, for regular inheritance use NORMAL dependency.
365 */
366#define child_dependency_type(child_is_partition) \
367 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
368
369static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
370static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
371static void truncate_check_activity(Relation rel);
372static void RangeVarCallbackForTruncate(const RangeVar *relation,
373 Oid relId, Oid oldRelId, void *arg);
374static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
375 bool is_partition, List **supconstr,
376 List **supnotnulls);
377static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
378static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
379static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
380static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
381static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
382static void StoreCatalogInheritance(Oid relationId, List *supers,
383 bool child_is_partition);
384static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
385 int32 seqNumber, Relation inhRelation,
386 bool child_is_partition);
387static int findAttrByName(const char *attributeName, const List *columns);
388static void AlterIndexNamespaces(Relation classRel, Relation rel,
389 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
390static void AlterSeqNamespaces(Relation classRel, Relation rel,
391 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
392 LOCKMODE lockmode);
394 ATAlterConstraint *cmdcon,
395 bool recurse, LOCKMODE lockmode);
396static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
397 Relation tgrel, Relation rel, HeapTuple contuple,
398 bool recurse, LOCKMODE lockmode);
399static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
400 Relation conrel, Relation tgrel,
401 Oid fkrelid, Oid pkrelid,
402 HeapTuple contuple, LOCKMODE lockmode,
403 Oid ReferencedParentDelTrigger,
404 Oid ReferencedParentUpdTrigger,
405 Oid ReferencingParentInsTrigger,
406 Oid ReferencingParentUpdTrigger);
407static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
408 Relation conrel, Relation tgrel, Relation rel,
409 HeapTuple contuple, bool recurse,
410 List **otherrelids, LOCKMODE lockmode);
411static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
412 Relation conrel, Relation rel,
413 HeapTuple contuple, LOCKMODE lockmode);
414static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
415 bool deferrable, bool initdeferred,
416 List **otherrelids);
417static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
418 Relation conrel, Relation tgrel,
419 Oid fkrelid, Oid pkrelid,
420 HeapTuple contuple, LOCKMODE lockmode,
421 Oid ReferencedParentDelTrigger,
422 Oid ReferencedParentUpdTrigger,
423 Oid ReferencingParentInsTrigger,
424 Oid ReferencingParentUpdTrigger);
425static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
426 Relation conrel, Relation tgrel, Relation rel,
427 HeapTuple contuple, bool recurse,
428 List **otherrelids, LOCKMODE lockmode);
430 HeapTuple contuple);
432 Relation rel, char *constrName,
433 bool recurse, bool recursing, LOCKMODE lockmode);
434static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
435 Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
436static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
437 char *constrName, HeapTuple contuple,
438 bool recurse, bool recursing, LOCKMODE lockmode);
439static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
440 HeapTuple contuple, bool recurse, bool recursing,
441 LOCKMODE lockmode);
442static int transformColumnNameList(Oid relId, List *colList,
443 int16 *attnums, Oid *atttypids, Oid *attcollids);
444static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
445 List **attnamelist,
446 int16 *attnums, Oid *atttypids, Oid *attcollids,
447 Oid *opclasses, bool *pk_has_without_overlaps);
449 int numattrs, int16 *attnums,
450 bool with_period, Oid *opclasses,
451 bool *pk_has_without_overlaps);
452static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
453static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
454 Oid *funcid);
455static void validateForeignKeyConstraint(char *conname,
456 Relation rel, Relation pkrel,
457 Oid pkindOid, Oid constraintOid, bool hasperiod);
458static void CheckAlterTableIsSafe(Relation rel);
459static void ATController(AlterTableStmt *parsetree,
460 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
461 AlterTableUtilityContext *context);
462static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
463 bool recurse, bool recursing, LOCKMODE lockmode,
464 AlterTableUtilityContext *context);
465static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
466 AlterTableUtilityContext *context);
467static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
468 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
469 AlterTableUtilityContext *context);
471 Relation rel, AlterTableCmd *cmd,
472 bool recurse, LOCKMODE lockmode,
473 AlterTablePass cur_pass,
474 AlterTableUtilityContext *context);
475static void ATRewriteTables(AlterTableStmt *parsetree,
476 List **wqueue, LOCKMODE lockmode,
477 AlterTableUtilityContext *context);
478static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
479static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
480static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
481static void ATSimpleRecursion(List **wqueue, Relation rel,
482 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
483 AlterTableUtilityContext *context);
484static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
485static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
486 LOCKMODE lockmode,
487 AlterTableUtilityContext *context);
488static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
489 DropBehavior behavior);
490static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
491 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
492 AlterTableUtilityContext *context);
494 Relation rel, AlterTableCmd **cmd,
495 bool recurse, bool recursing,
496 LOCKMODE lockmode, AlterTablePass cur_pass,
497 AlterTableUtilityContext *context);
498static bool check_for_column_name_collision(Relation rel, const char *colname,
499 bool if_not_exists);
500static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
502static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
503 LOCKMODE lockmode);
504static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
505 bool is_valid, bool queue_validation);
506static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
507 char *conName, char *colName,
508 bool recurse, bool recursing,
509 LOCKMODE lockmode);
512 List *testConstraint, List *provenConstraint);
513static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
514 Node *newDefault, LOCKMODE lockmode);
516 Node *newDefault);
517static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
518 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
519static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
520 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
521static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
522 bool recurse, bool recursing);
523static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
524 Node *newExpr, LOCKMODE lockmode);
525static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
526static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
527static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
528 Node *newValue, LOCKMODE lockmode);
529static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
530 Node *options, bool isReset, LOCKMODE lockmode);
531static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
532 Node *newValue, LOCKMODE lockmode);
533static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
534 AlterTableCmd *cmd, LOCKMODE lockmode,
535 AlterTableUtilityContext *context);
536static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
537 DropBehavior behavior,
538 bool recurse, bool recursing,
539 bool missing_ok, LOCKMODE lockmode,
540 ObjectAddresses *addrs);
541static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
542 bool recurse, LOCKMODE lockmode,
543 AlterTableUtilityContext *context);
544static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
546 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
548 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
550 AlteredTableInfo *tab, Relation rel,
551 Constraint *newConstraint, bool recurse, bool is_readd,
552 LOCKMODE lockmode);
553static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
555 IndexStmt *stmt, LOCKMODE lockmode);
557 AlteredTableInfo *tab, Relation rel,
558 Constraint *constr,
559 bool recurse, bool recursing, bool is_readd,
560 LOCKMODE lockmode);
562 Relation rel, Constraint *fkconstraint,
563 bool recurse, bool recursing,
564 LOCKMODE lockmode);
565static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
566 int numfksetcols, int16 *fksetcolsattnums,
567 List *fksetcols);
569 char *constraintname,
570 Constraint *fkconstraint, Relation rel,
571 Relation pkrel, Oid indexOid,
572 Oid parentConstr,
573 int numfks, int16 *pkattnum, int16 *fkattnum,
574 Oid *pfeqoperators, Oid *ppeqoperators,
575 Oid *ffeqoperators, int numfkdelsetcols,
576 int16 *fkdelsetcols, bool is_internal,
577 bool with_period);
578static void addFkRecurseReferenced(Constraint *fkconstraint,
579 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
580 int numfks, int16 *pkattnum, int16 *fkattnum,
581 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
582 int numfkdelsetcols, int16 *fkdelsetcols,
583 bool old_check_ok,
584 Oid parentDelTrigger, Oid parentUpdTrigger,
585 bool with_period);
586static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
587 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
588 int numfks, int16 *pkattnum, int16 *fkattnum,
589 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
590 int numfkdelsetcols, int16 *fkdelsetcols,
591 bool old_check_ok, LOCKMODE lockmode,
592 Oid parentInsTrigger, Oid parentUpdTrigger,
593 bool with_period);
594static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
595 Relation partitionRel);
596static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
597static void CloneFkReferencing(List **wqueue, Relation parentRel,
598 Relation partRel);
599static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
600 Constraint *fkconstraint, Oid constraintOid,
601 Oid indexOid,
602 Oid parentInsTrigger, Oid parentUpdTrigger,
603 Oid *insertTrigOid, Oid *updateTrigOid);
604static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
605 Constraint *fkconstraint, Oid constraintOid,
606 Oid indexOid,
607 Oid parentDelTrigger, Oid parentUpdTrigger,
608 Oid *deleteTrigOid, Oid *updateTrigOid);
609static bool tryAttachPartitionForeignKey(List **wqueue,
611 Relation partition,
612 Oid parentConstrOid, int numfks,
613 AttrNumber *mapped_conkey, AttrNumber *confkey,
614 Oid *conpfeqop,
615 Oid parentInsTrigger,
616 Oid parentUpdTrigger,
617 Relation trigrel);
618static void AttachPartitionForeignKey(List **wqueue, Relation partition,
619 Oid partConstrOid, Oid parentConstrOid,
620 Oid parentInsTrigger, Oid parentUpdTrigger,
621 Relation trigrel);
622static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
623 Oid conoid, Oid conrelid);
624static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
625 Oid confrelid, Oid conrelid);
626static void GetForeignKeyActionTriggers(Relation trigrel,
627 Oid conoid, Oid confrelid, Oid conrelid,
628 Oid *deleteTriggerOid,
629 Oid *updateTriggerOid);
630static void GetForeignKeyCheckTriggers(Relation trigrel,
631 Oid conoid, Oid confrelid, Oid conrelid,
632 Oid *insertTriggerOid,
633 Oid *updateTriggerOid);
634static void ATExecDropConstraint(Relation rel, const char *constrName,
635 DropBehavior behavior, bool recurse,
636 bool missing_ok, LOCKMODE lockmode);
638 HeapTuple constraintTup, DropBehavior behavior,
639 bool recurse, bool recursing,
640 bool missing_ok, LOCKMODE lockmode);
641static void ATPrepAlterColumnType(List **wqueue,
642 AlteredTableInfo *tab, Relation rel,
643 bool recurse, bool recursing,
644 AlterTableCmd *cmd, LOCKMODE lockmode,
645 AlterTableUtilityContext *context);
646static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
648 AlterTableCmd *cmd, LOCKMODE lockmode);
650 Relation rel, AttrNumber attnum, const char *colName);
652static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
654static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
655 LOCKMODE lockmode);
656static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
657 char *cmd, List **wqueue, LOCKMODE lockmode,
658 bool rewrite);
660 Oid objid, Relation rel, List *domname,
661 const char *conname);
662static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
663static void TryReuseForeignKey(Oid oldId, Constraint *con);
664static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
665 List *options, LOCKMODE lockmode);
666static void change_owner_fix_column_acls(Oid relationOid,
667 Oid oldOwnerId, Oid newOwnerId);
668static void change_owner_recurse_to_sequences(Oid relationOid,
669 Oid newOwnerId, LOCKMODE lockmode);
670static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
671 LOCKMODE lockmode);
672static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
673static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
674static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
676 bool toLogged);
677static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
678 const char *tablespacename, LOCKMODE lockmode);
679static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
680static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
681static void ATExecSetRelOptions(Relation rel, List *defList,
682 AlterTableType operation,
683 LOCKMODE lockmode);
684static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
685 char fires_when, bool skip_system, bool recurse,
686 LOCKMODE lockmode);
687static void ATExecEnableDisableRule(Relation rel, const char *rulename,
688 char fires_when, LOCKMODE lockmode);
689static void ATPrepAddInherit(Relation child_rel);
690static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
691static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
692static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
693 DependencyType deptype);
694static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
695static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
697static void ATExecGenericOptions(Relation rel, List *options);
698static void ATExecSetRowSecurity(Relation rel, bool rls);
699static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
701 const char *column, Node *newValue, LOCKMODE lockmode);
702
703static void index_copy_data(Relation rel, RelFileLocator newrlocator);
704static const char *storage_name(char c);
705
706static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
707 Oid oldRelOid, void *arg);
708static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
709 Oid oldrelid, void *arg);
711static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
712 List **partexprs, Oid *partopclass, Oid *partcollation,
713 PartitionStrategy strategy);
714static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
715static void RemoveInheritance(Relation child_rel, Relation parent_rel,
716 bool expect_detached);
718 PartitionCmd *cmd,
719 AlterTableUtilityContext *context);
720static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
721static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
722 List *partConstraint,
723 bool validate_default);
724static void CloneRowTriggersToPartition(Relation parent, Relation partition);
725static void DropClonedTriggersFromPartition(Oid partitionId);
727 Relation rel, RangeVar *name,
728 bool concurrent);
729static void DetachPartitionFinalize(Relation rel, Relation partRel,
730 bool concurrent, Oid defaultPartOid);
732static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
733 RangeVar *name);
734static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
735static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
736 Relation partitionTbl);
737static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
738static List *GetParentedForeignKeyRefs(Relation partition);
739static void ATDetachCheckNoForeignKeyRefs(Relation partition);
740static char GetAttributeCompression(Oid atttypid, const char *compression);
741static char GetAttributeStorage(Oid atttypid, const char *storagemode);
742
743
744/* ----------------------------------------------------------------
745 * DefineRelation
746 * Creates a new relation.
747 *
748 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
749 * The other arguments are used to extend the behavior for other cases:
750 * relkind: relkind to assign to the new relation
751 * ownerId: if not InvalidOid, use this as the new relation's owner.
752 * typaddress: if not null, it's set to the pg_type entry's address.
753 * queryString: for error reporting
754 *
755 * Note that permissions checks are done against current user regardless of
756 * ownerId. A nonzero ownerId is used when someone is creating a relation
757 * "on behalf of" someone else, so we still want to see that the current user
758 * has permissions to do it.
759 *
760 * If successful, returns the address of the new relation.
761 * ----------------------------------------------------------------
762 */
764DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
765 ObjectAddress *typaddress, const char *queryString)
766{
767 char relname[NAMEDATALEN];
768 Oid namespaceId;
769 Oid relationId;
770 Oid tablespaceId;
771 Relation rel;
773 List *inheritOids;
774 List *old_constraints;
775 List *old_notnulls;
776 List *rawDefaults;
777 List *cookedDefaults;
778 List *nncols;
779 Datum reloptions;
780 ListCell *listptr;
782 bool partitioned;
783 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
784 Oid ofTypeId;
785 ObjectAddress address;
786 LOCKMODE parentLockmode;
787 Oid accessMethodId = InvalidOid;
788
789 /*
790 * Truncate relname to appropriate length (probably a waste of time, as
791 * parser should have done this already).
792 */
793 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
794
795 /*
796 * Check consistency of arguments
797 */
798 if (stmt->oncommit != ONCOMMIT_NOOP
799 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
801 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
802 errmsg("ON COMMIT can only be used on temporary tables")));
803
804 if (stmt->partspec != NULL)
805 {
806 if (relkind != RELKIND_RELATION)
807 elog(ERROR, "unexpected relkind: %d", (int) relkind);
808
809 relkind = RELKIND_PARTITIONED_TABLE;
810 partitioned = true;
811 }
812 else
813 partitioned = false;
814
815 if (relkind == RELKIND_PARTITIONED_TABLE &&
816 stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
818 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
819 errmsg("partitioned tables cannot be unlogged")));
820
821 /*
822 * Look up the namespace in which we are supposed to create the relation,
823 * check we have permission to create there, lock it against concurrent
824 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
825 * namespace is selected.
826 */
827 namespaceId =
829
830 /*
831 * Security check: disallow creating temp tables from security-restricted
832 * code. This is needed because calling code might not expect untrusted
833 * tables to appear in pg_temp at the front of its search path.
834 */
835 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
838 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
839 errmsg("cannot create temporary table within security-restricted operation")));
840
841 /*
842 * Determine the lockmode to use when scanning parents. A self-exclusive
843 * lock is needed here.
844 *
845 * For regular inheritance, if two backends attempt to add children to the
846 * same parent simultaneously, and that parent has no pre-existing
847 * children, then both will attempt to update the parent's relhassubclass
848 * field, leading to a "tuple concurrently updated" error. Also, this
849 * interlocks against a concurrent ANALYZE on the parent table, which
850 * might otherwise be attempting to clear the parent's relhassubclass
851 * field, if its previous children were recently dropped.
852 *
853 * If the child table is a partition, then we instead grab an exclusive
854 * lock on the parent because its partition descriptor will be changed by
855 * addition of the new partition.
856 */
857 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
859
860 /* Determine the list of OIDs of the parents. */
861 inheritOids = NIL;
862 foreach(listptr, stmt->inhRelations)
863 {
864 RangeVar *rv = (RangeVar *) lfirst(listptr);
865 Oid parentOid;
866
867 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
868
869 /*
870 * Reject duplications in the list of parents.
871 */
872 if (list_member_oid(inheritOids, parentOid))
874 (errcode(ERRCODE_DUPLICATE_TABLE),
875 errmsg("relation \"%s\" would be inherited from more than once",
876 get_rel_name(parentOid))));
877
878 inheritOids = lappend_oid(inheritOids, parentOid);
879 }
880
881 /*
882 * Select tablespace to use: an explicitly indicated one, or (in the case
883 * of a partitioned table) the parent's, if it has one.
884 */
885 if (stmt->tablespacename)
886 {
887 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
888
889 if (partitioned && tablespaceId == MyDatabaseTableSpace)
891 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
892 errmsg("cannot specify default tablespace for partitioned relations")));
893 }
894 else if (stmt->partbound)
895 {
896 Assert(list_length(inheritOids) == 1);
897 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
898 }
899 else
900 tablespaceId = InvalidOid;
901
902 /* still nothing? use the default */
903 if (!OidIsValid(tablespaceId))
904 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
905 partitioned);
906
907 /* Check permissions except when using database's default */
908 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
909 {
910 AclResult aclresult;
911
912 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
913 ACL_CREATE);
914 if (aclresult != ACLCHECK_OK)
916 get_tablespace_name(tablespaceId));
917 }
918
919 /* In all cases disallow placing user relations in pg_global */
920 if (tablespaceId == GLOBALTABLESPACE_OID)
922 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
923 errmsg("only shared relations can be placed in pg_global tablespace")));
924
925 /* Identify user ID that will own the table */
926 if (!OidIsValid(ownerId))
927 ownerId = GetUserId();
928
929 /*
930 * Parse and validate reloptions, if any.
931 */
932 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
933 true, false);
934
935 switch (relkind)
936 {
937 case RELKIND_VIEW:
938 (void) view_reloptions(reloptions, true);
939 break;
940 case RELKIND_PARTITIONED_TABLE:
941 (void) partitioned_table_reloptions(reloptions, true);
942 break;
943 default:
944 (void) heap_reloptions(relkind, reloptions, true);
945 }
946
947 if (stmt->ofTypename)
948 {
949 AclResult aclresult;
950
951 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
952
953 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
954 if (aclresult != ACLCHECK_OK)
955 aclcheck_error_type(aclresult, ofTypeId);
956 }
957 else
958 ofTypeId = InvalidOid;
959
960 /*
961 * Look up inheritance ancestors and generate relation schema, including
962 * inherited attributes. (Note that stmt->tableElts is destructively
963 * modified by MergeAttributes.)
964 */
965 stmt->tableElts =
966 MergeAttributes(stmt->tableElts, inheritOids,
967 stmt->relation->relpersistence,
968 stmt->partbound != NULL,
969 &old_constraints, &old_notnulls);
970
971 /*
972 * Create a tuple descriptor from the relation schema. Note that this
973 * deals with column names, types, and in-descriptor NOT NULL flags, but
974 * not default values, NOT NULL or CHECK constraints; we handle those
975 * below.
976 */
978
979 /*
980 * Find columns with default values and prepare for insertion of the
981 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
982 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
983 * while raw defaults go into a list of RawColumnDefault structs that will
984 * be processed by AddRelationNewConstraints. (We can't deal with raw
985 * expressions until we can do transformExpr.)
986 */
987 rawDefaults = NIL;
988 cookedDefaults = NIL;
989 attnum = 0;
990
991 foreach(listptr, stmt->tableElts)
992 {
993 ColumnDef *colDef = lfirst(listptr);
994
995 attnum++;
996 if (colDef->raw_default != NULL)
997 {
998 RawColumnDefault *rawEnt;
999
1000 Assert(colDef->cooked_default == NULL);
1001
1002 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
1003 rawEnt->attnum = attnum;
1004 rawEnt->raw_default = colDef->raw_default;
1005 rawEnt->generated = colDef->generated;
1006 rawDefaults = lappend(rawDefaults, rawEnt);
1007 }
1008 else if (colDef->cooked_default != NULL)
1009 {
1010 CookedConstraint *cooked;
1011
1012 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
1013 cooked->contype = CONSTR_DEFAULT;
1014 cooked->conoid = InvalidOid; /* until created */
1015 cooked->name = NULL;
1016 cooked->attnum = attnum;
1017 cooked->expr = colDef->cooked_default;
1018 cooked->is_enforced = true;
1019 cooked->skip_validation = false;
1020 cooked->is_local = true; /* not used for defaults */
1021 cooked->inhcount = 0; /* ditto */
1022 cooked->is_no_inherit = false;
1023 cookedDefaults = lappend(cookedDefaults, cooked);
1024 }
1025 }
1026
1027 /*
1028 * For relations with table AM and partitioned tables, select access
1029 * method to use: an explicitly indicated one, or (in the case of a
1030 * partitioned table) the parent's, if it has one.
1031 */
1032 if (stmt->accessMethod != NULL)
1033 {
1034 Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1035 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1036 }
1037 else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1038 {
1039 if (stmt->partbound)
1040 {
1041 Assert(list_length(inheritOids) == 1);
1042 accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1043 }
1044
1045 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1046 accessMethodId = get_table_am_oid(default_table_access_method, false);
1047 }
1048
1049 /*
1050 * Create the relation. Inherited defaults and CHECK constraints are
1051 * passed in for immediate handling --- since they don't need parsing,
1052 * they can be stored immediately.
1053 */
1054 relationId = heap_create_with_catalog(relname,
1055 namespaceId,
1056 tablespaceId,
1057 InvalidOid,
1058 InvalidOid,
1059 ofTypeId,
1060 ownerId,
1061 accessMethodId,
1062 descriptor,
1063 list_concat(cookedDefaults,
1064 old_constraints),
1065 relkind,
1066 stmt->relation->relpersistence,
1067 false,
1068 false,
1069 stmt->oncommit,
1070 reloptions,
1071 true,
1073 false,
1074 InvalidOid,
1075 typaddress);
1076
1077 /*
1078 * We must bump the command counter to make the newly-created relation
1079 * tuple visible for opening.
1080 */
1082
1083 /*
1084 * Open the new relation and acquire exclusive lock on it. This isn't
1085 * really necessary for locking out other backends (since they can't see
1086 * the new rel anyway until we commit), but it keeps the lock manager from
1087 * complaining about deadlock risks.
1088 */
1089 rel = relation_open(relationId, AccessExclusiveLock);
1090
1091 /*
1092 * Now add any newly specified column default and generation expressions
1093 * to the new relation. These are passed to us in the form of raw
1094 * parsetrees; we need to transform them to executable expression trees
1095 * before they can be added. The most convenient way to do that is to
1096 * apply the parser's transformExpr routine, but transformExpr doesn't
1097 * work unless we have a pre-existing relation. So, the transformation has
1098 * to be postponed to this final step of CREATE TABLE.
1099 *
1100 * This needs to be before processing the partitioning clauses because
1101 * those could refer to generated columns.
1102 */
1103 if (rawDefaults)
1104 AddRelationNewConstraints(rel, rawDefaults, NIL,
1105 true, true, false, queryString);
1106
1107 /*
1108 * Make column generation expressions visible for use by partitioning.
1109 */
1111
1112 /* Process and store partition bound, if any. */
1113 if (stmt->partbound)
1114 {
1115 PartitionBoundSpec *bound;
1116 ParseState *pstate;
1117 Oid parentId = linitial_oid(inheritOids),
1118 defaultPartOid;
1119 Relation parent,
1120 defaultRel = NULL;
1121 ParseNamespaceItem *nsitem;
1122
1123 /* Already have strong enough lock on the parent */
1124 parent = table_open(parentId, NoLock);
1125
1126 /*
1127 * We are going to try to validate the partition bound specification
1128 * against the partition key of parentRel, so it better have one.
1129 */
1130 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1131 ereport(ERROR,
1132 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1133 errmsg("\"%s\" is not partitioned",
1134 RelationGetRelationName(parent))));
1135
1136 /*
1137 * The partition constraint of the default partition depends on the
1138 * partition bounds of every other partition. It is possible that
1139 * another backend might be about to execute a query on the default
1140 * partition table, and that the query relies on previously cached
1141 * default partition constraints. We must therefore take a table lock
1142 * strong enough to prevent all queries on the default partition from
1143 * proceeding until we commit and send out a shared-cache-inval notice
1144 * that will make them update their index lists.
1145 *
1146 * Order of locking: The relation being added won't be visible to
1147 * other backends until it is committed, hence here in
1148 * DefineRelation() the order of locking the default partition and the
1149 * relation being added does not matter. But at all other places we
1150 * need to lock the default relation before we lock the relation being
1151 * added or removed i.e. we should take the lock in same order at all
1152 * the places such that lock parent, lock default partition and then
1153 * lock the partition so as to avoid a deadlock.
1154 */
1155 defaultPartOid =
1157 true));
1158 if (OidIsValid(defaultPartOid))
1159 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1160
1161 /* Transform the bound values */
1162 pstate = make_parsestate(NULL);
1163 pstate->p_sourcetext = queryString;
1164
1165 /*
1166 * Add an nsitem containing this relation, so that transformExpr
1167 * called on partition bound expressions is able to report errors
1168 * using a proper context.
1169 */
1170 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1171 NULL, false, false);
1172 addNSItemToQuery(pstate, nsitem, false, true, true);
1173
1174 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1175
1176 /*
1177 * Check first that the new partition's bound is valid and does not
1178 * overlap with any of existing partitions of the parent.
1179 */
1180 check_new_partition_bound(relname, parent, bound, pstate);
1181
1182 /*
1183 * If the default partition exists, its partition constraints will
1184 * change after the addition of this new partition such that it won't
1185 * allow any row that qualifies for this new partition. So, check that
1186 * the existing data in the default partition satisfies the constraint
1187 * as it will exist after adding this partition.
1188 */
1189 if (OidIsValid(defaultPartOid))
1190 {
1191 check_default_partition_contents(parent, defaultRel, bound);
1192 /* Keep the lock until commit. */
1193 table_close(defaultRel, NoLock);
1194 }
1195
1196 /* Update the pg_class entry. */
1197 StorePartitionBound(rel, parent, bound);
1198
1199 table_close(parent, NoLock);
1200 }
1201
1202 /* Store inheritance information for new rel. */
1203 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1204
1205 /*
1206 * Process the partitioning specification (if any) and store the partition
1207 * key information into the catalog.
1208 */
1209 if (partitioned)
1210 {
1211 ParseState *pstate;
1212 int partnatts;
1213 AttrNumber partattrs[PARTITION_MAX_KEYS];
1214 Oid partopclass[PARTITION_MAX_KEYS];
1215 Oid partcollation[PARTITION_MAX_KEYS];
1216 List *partexprs = NIL;
1217
1218 pstate = make_parsestate(NULL);
1219 pstate->p_sourcetext = queryString;
1220
1221 partnatts = list_length(stmt->partspec->partParams);
1222
1223 /* Protect fixed-size arrays here and in executor */
1224 if (partnatts > PARTITION_MAX_KEYS)
1225 ereport(ERROR,
1226 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1227 errmsg("cannot partition using more than %d columns",
1229
1230 /*
1231 * We need to transform the raw parsetrees corresponding to partition
1232 * expressions into executable expression trees. Like column defaults
1233 * and CHECK constraints, we could not have done the transformation
1234 * earlier.
1235 */
1236 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1237
1238 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1239 partattrs, &partexprs, partopclass,
1240 partcollation, stmt->partspec->strategy);
1241
1242 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1243 partexprs,
1244 partopclass, partcollation);
1245
1246 /* make it all visible */
1248 }
1249
1250 /*
1251 * If we're creating a partition, create now all the indexes, triggers,
1252 * FKs defined in the parent.
1253 *
1254 * We can't do it earlier, because DefineIndex wants to know the partition
1255 * key which we just stored.
1256 */
1257 if (stmt->partbound)
1258 {
1259 Oid parentId = linitial_oid(inheritOids);
1260 Relation parent;
1261 List *idxlist;
1262 ListCell *cell;
1263
1264 /* Already have strong enough lock on the parent */
1265 parent = table_open(parentId, NoLock);
1266 idxlist = RelationGetIndexList(parent);
1267
1268 /*
1269 * For each index in the parent table, create one in the partition
1270 */
1271 foreach(cell, idxlist)
1272 {
1274 AttrMap *attmap;
1275 IndexStmt *idxstmt;
1276 Oid constraintOid;
1277
1278 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1279 {
1280 if (idxRel->rd_index->indisunique)
1281 ereport(ERROR,
1282 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1283 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1284 RelationGetRelationName(parent)),
1285 errdetail("Table \"%s\" contains indexes that are unique.",
1286 RelationGetRelationName(parent))));
1287 else
1288 {
1290 continue;
1291 }
1292 }
1293
1295 RelationGetDescr(parent),
1296 false);
1297 idxstmt =
1298 generateClonedIndexStmt(NULL, idxRel,
1299 attmap, &constraintOid);
1301 idxstmt,
1302 InvalidOid,
1303 RelationGetRelid(idxRel),
1304 constraintOid,
1305 -1,
1306 false, false, false, false, false);
1307
1309 }
1310
1311 list_free(idxlist);
1312
1313 /*
1314 * If there are any row-level triggers, clone them to the new
1315 * partition.
1316 */
1317 if (parent->trigdesc != NULL)
1318 CloneRowTriggersToPartition(parent, rel);
1319
1320 /*
1321 * And foreign keys too. Note that because we're freshly creating the
1322 * table, there is no need to verify these new constraints.
1323 */
1324 CloneForeignKeyConstraints(NULL, parent, rel);
1325
1326 table_close(parent, NoLock);
1327 }
1328
1329 /*
1330 * Now add any newly specified CHECK constraints to the new relation. Same
1331 * as for defaults above, but these need to come after partitioning is set
1332 * up.
1333 */
1334 if (stmt->constraints)
1335 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1336 true, true, false, queryString);
1337
1338 /*
1339 * Finally, merge the not-null constraints that are declared directly with
1340 * those that come from parent relations (making sure to count inheritance
1341 * appropriately for each), create them, and set the attnotnull flag on
1342 * columns that don't yet have it.
1343 */
1344 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1345 old_notnulls);
1346 foreach_int(attrnum, nncols)
1347 set_attnotnull(NULL, rel, attrnum, true, false);
1348
1349 ObjectAddressSet(address, RelationRelationId, relationId);
1350
1351 /*
1352 * Clean up. We keep lock on new relation (although it shouldn't be
1353 * visible to anyone else anyway, until commit).
1354 */
1355 relation_close(rel, NoLock);
1356
1357 return address;
1358}
1359
1360/*
1361 * BuildDescForRelation
1362 *
1363 * Given a list of ColumnDef nodes, build a TupleDesc.
1364 *
1365 * Note: This is only for the limited purpose of table and view creation. Not
1366 * everything is filled in. A real tuple descriptor should be obtained from
1367 * the relcache.
1368 */
1371{
1372 int natts;
1374 ListCell *l;
1375 TupleDesc desc;
1376 char *attname;
1377 Oid atttypid;
1378 int32 atttypmod;
1379 Oid attcollation;
1380 int attdim;
1381
1382 /*
1383 * allocate a new tuple descriptor
1384 */
1385 natts = list_length(columns);
1386 desc = CreateTemplateTupleDesc(natts);
1387
1388 attnum = 0;
1389
1390 foreach(l, columns)
1391 {
1392 ColumnDef *entry = lfirst(l);
1393 AclResult aclresult;
1395
1396 /*
1397 * for each entry in the list, get the name and type information from
1398 * the list and have TupleDescInitEntry fill in the attribute
1399 * information we need.
1400 */
1401 attnum++;
1402
1403 attname = entry->colname;
1404 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1405
1406 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1407 if (aclresult != ACLCHECK_OK)
1408 aclcheck_error_type(aclresult, atttypid);
1409
1410 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1411 attdim = list_length(entry->typeName->arrayBounds);
1412 if (attdim > PG_INT16_MAX)
1413 ereport(ERROR,
1414 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1415 errmsg("too many array dimensions"));
1416
1417 if (entry->typeName->setof)
1418 ereport(ERROR,
1419 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1420 errmsg("column \"%s\" cannot be declared SETOF",
1421 attname)));
1422
1424 atttypid, atttypmod, attdim);
1425 att = TupleDescAttr(desc, attnum - 1);
1426
1427 /* Override TupleDescInitEntry's settings as requested */
1428 TupleDescInitEntryCollation(desc, attnum, attcollation);
1429
1430 /* Fill in additional stuff not handled by TupleDescInitEntry */
1431 att->attnotnull = entry->is_not_null;
1432 att->attislocal = entry->is_local;
1433 att->attinhcount = entry->inhcount;
1434 att->attidentity = entry->identity;
1435 att->attgenerated = entry->generated;
1436 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1437 if (entry->storage)
1438 att->attstorage = entry->storage;
1439 else if (entry->storage_name)
1440 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1441
1443 }
1444
1445 return desc;
1446}
1447
1448/*
1449 * Emit the right error or warning message for a "DROP" command issued on a
1450 * non-existent relation
1451 */
1452static void
1453DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1454{
1455 const struct dropmsgstrings *rentry;
1456
1457 if (rel->schemaname != NULL &&
1459 {
1460 if (!missing_ok)
1461 {
1462 ereport(ERROR,
1463 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1464 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1465 }
1466 else
1467 {
1469 (errmsg("schema \"%s\" does not exist, skipping",
1470 rel->schemaname)));
1471 }
1472 return;
1473 }
1474
1475 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1476 {
1477 if (rentry->kind == rightkind)
1478 {
1479 if (!missing_ok)
1480 {
1481 ereport(ERROR,
1482 (errcode(rentry->nonexistent_code),
1483 errmsg(rentry->nonexistent_msg, rel->relname)));
1484 }
1485 else
1486 {
1487 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1488 break;
1489 }
1490 }
1491 }
1492
1493 Assert(rentry->kind != '\0'); /* Should be impossible */
1494}
1495
1496/*
1497 * Emit the right error message for a "DROP" command issued on a
1498 * relation of the wrong type
1499 */
1500static void
1501DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1502{
1503 const struct dropmsgstrings *rentry;
1504 const struct dropmsgstrings *wentry;
1505
1506 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1507 if (rentry->kind == rightkind)
1508 break;
1509 Assert(rentry->kind != '\0');
1510
1511 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1512 if (wentry->kind == wrongkind)
1513 break;
1514 /* wrongkind could be something we don't have in our table... */
1515
1516 ereport(ERROR,
1517 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1518 errmsg(rentry->nota_msg, relname),
1519 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1520}
1521
1522/*
1523 * RemoveRelations
1524 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1525 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1526 */
1527void
1529{
1530 ObjectAddresses *objects;
1531 char relkind;
1532 ListCell *cell;
1533 int flags = 0;
1534 LOCKMODE lockmode = AccessExclusiveLock;
1535
1536 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1537 if (drop->concurrent)
1538 {
1539 /*
1540 * Note that for temporary relations this lock may get upgraded later
1541 * on, but as no other session can access a temporary relation, this
1542 * is actually fine.
1543 */
1544 lockmode = ShareUpdateExclusiveLock;
1545 Assert(drop->removeType == OBJECT_INDEX);
1546 if (list_length(drop->objects) != 1)
1547 ereport(ERROR,
1548 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1549 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1550 if (drop->behavior == DROP_CASCADE)
1551 ereport(ERROR,
1552 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1553 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1554 }
1555
1556 /*
1557 * First we identify all the relations, then we delete them in a single
1558 * performMultipleDeletions() call. This is to avoid unwanted DROP
1559 * RESTRICT errors if one of the relations depends on another.
1560 */
1561
1562 /* Determine required relkind */
1563 switch (drop->removeType)
1564 {
1565 case OBJECT_TABLE:
1566 relkind = RELKIND_RELATION;
1567 break;
1568
1569 case OBJECT_INDEX:
1570 relkind = RELKIND_INDEX;
1571 break;
1572
1573 case OBJECT_SEQUENCE:
1574 relkind = RELKIND_SEQUENCE;
1575 break;
1576
1577 case OBJECT_VIEW:
1578 relkind = RELKIND_VIEW;
1579 break;
1580
1581 case OBJECT_MATVIEW:
1582 relkind = RELKIND_MATVIEW;
1583 break;
1584
1586 relkind = RELKIND_FOREIGN_TABLE;
1587 break;
1588
1589 default:
1590 elog(ERROR, "unrecognized drop object type: %d",
1591 (int) drop->removeType);
1592 relkind = 0; /* keep compiler quiet */
1593 break;
1594 }
1595
1596 /* Lock and validate each relation; build a list of object addresses */
1597 objects = new_object_addresses();
1598
1599 foreach(cell, drop->objects)
1600 {
1601 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1602 Oid relOid;
1603 ObjectAddress obj;
1605
1606 /*
1607 * These next few steps are a great deal like relation_openrv, but we
1608 * don't bother building a relcache entry since we don't need it.
1609 *
1610 * Check for shared-cache-inval messages before trying to access the
1611 * relation. This is needed to cover the case where the name
1612 * identifies a rel that has been dropped and recreated since the
1613 * start of our transaction: if we don't flush the old syscache entry,
1614 * then we'll latch onto that entry and suffer an error later.
1615 */
1617
1618 /* Look up the appropriate relation using namespace search. */
1619 state.expected_relkind = relkind;
1620 state.heap_lockmode = drop->concurrent ?
1622 /* We must initialize these fields to show that no locks are held: */
1623 state.heapOid = InvalidOid;
1624 state.partParentOid = InvalidOid;
1625
1626 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1628 &state);
1629
1630 /* Not there? */
1631 if (!OidIsValid(relOid))
1632 {
1633 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1634 continue;
1635 }
1636
1637 /*
1638 * Decide if concurrent mode needs to be used here or not. The
1639 * callback retrieved the rel's persistence for us.
1640 */
1641 if (drop->concurrent &&
1642 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1643 {
1644 Assert(list_length(drop->objects) == 1 &&
1645 drop->removeType == OBJECT_INDEX);
1647 }
1648
1649 /*
1650 * Concurrent index drop cannot be used with partitioned indexes,
1651 * either.
1652 */
1653 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1654 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1655 ereport(ERROR,
1656 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1657 errmsg("cannot drop partitioned index \"%s\" concurrently",
1658 rel->relname)));
1659
1660 /*
1661 * If we're told to drop a partitioned index, we must acquire lock on
1662 * all the children of its parent partitioned table before proceeding.
1663 * Otherwise we'd try to lock the child index partitions before their
1664 * tables, leading to potential deadlock against other sessions that
1665 * will lock those objects in the other order.
1666 */
1667 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1668 (void) find_all_inheritors(state.heapOid,
1669 state.heap_lockmode,
1670 NULL);
1671
1672 /* OK, we're ready to delete this one */
1673 obj.classId = RelationRelationId;
1674 obj.objectId = relOid;
1675 obj.objectSubId = 0;
1676
1677 add_exact_object_address(&obj, objects);
1678 }
1679
1680 performMultipleDeletions(objects, drop->behavior, flags);
1681
1682 free_object_addresses(objects);
1683}
1684
1685/*
1686 * Before acquiring a table lock, check whether we have sufficient rights.
1687 * In the case of DROP INDEX, also try to lock the table before the index.
1688 * Also, if the table to be dropped is a partition, we try to lock the parent
1689 * first.
1690 */
1691static void
1692RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1693 void *arg)
1694{
1695 HeapTuple tuple;
1697 char expected_relkind;
1698 bool is_partition;
1699 Form_pg_class classform;
1701 bool invalid_system_index = false;
1702
1703 state = (struct DropRelationCallbackState *) arg;
1704 heap_lockmode = state->heap_lockmode;
1705
1706 /*
1707 * If we previously locked some other index's heap, and the name we're
1708 * looking up no longer refers to that relation, release the now-useless
1709 * lock.
1710 */
1711 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1712 {
1714 state->heapOid = InvalidOid;
1715 }
1716
1717 /*
1718 * Similarly, if we previously locked some other partition's heap, and the
1719 * name we're looking up no longer refers to that relation, release the
1720 * now-useless lock.
1721 */
1722 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1723 {
1725 state->partParentOid = InvalidOid;
1726 }
1727
1728 /* Didn't find a relation, so no need for locking or permission checks. */
1729 if (!OidIsValid(relOid))
1730 return;
1731
1732 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1733 if (!HeapTupleIsValid(tuple))
1734 return; /* concurrently dropped, so nothing to do */
1735 classform = (Form_pg_class) GETSTRUCT(tuple);
1736 is_partition = classform->relispartition;
1737
1738 /* Pass back some data to save lookups in RemoveRelations */
1739 state->actual_relkind = classform->relkind;
1740 state->actual_relpersistence = classform->relpersistence;
1741
1742 /*
1743 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1744 * but RemoveRelations() can only pass one relkind for a given relation.
1745 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1746 * That means we must be careful before giving the wrong type error when
1747 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1748 * exists with indexes.
1749 */
1750 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1751 expected_relkind = RELKIND_RELATION;
1752 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1753 expected_relkind = RELKIND_INDEX;
1754 else
1755 expected_relkind = classform->relkind;
1756
1757 if (state->expected_relkind != expected_relkind)
1758 DropErrorMsgWrongType(rel->relname, classform->relkind,
1759 state->expected_relkind);
1760
1761 /* Allow DROP to either table owner or schema owner */
1762 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1763 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1765 get_relkind_objtype(classform->relkind),
1766 rel->relname);
1767
1768 /*
1769 * Check the case of a system index that might have been invalidated by a
1770 * failed concurrent process and allow its drop. For the time being, this
1771 * only concerns indexes of toast relations that became invalid during a
1772 * REINDEX CONCURRENTLY process.
1773 */
1774 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1775 {
1776 HeapTuple locTuple;
1777 Form_pg_index indexform;
1778 bool indisvalid;
1779
1780 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1781 if (!HeapTupleIsValid(locTuple))
1782 {
1783 ReleaseSysCache(tuple);
1784 return;
1785 }
1786
1787 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1788 indisvalid = indexform->indisvalid;
1789 ReleaseSysCache(locTuple);
1790
1791 /* Mark object as being an invalid index of system catalogs */
1792 if (!indisvalid)
1793 invalid_system_index = true;
1794 }
1795
1796 /* In the case of an invalid index, it is fine to bypass this check */
1797 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1798 ereport(ERROR,
1799 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1800 errmsg("permission denied: \"%s\" is a system catalog",
1801 rel->relname)));
1802
1803 ReleaseSysCache(tuple);
1804
1805 /*
1806 * In DROP INDEX, attempt to acquire lock on the parent table before
1807 * locking the index. index_drop() will need this anyway, and since
1808 * regular queries lock tables before their indexes, we risk deadlock if
1809 * we do it the other way around. No error if we don't find a pg_index
1810 * entry, though --- the relation may have been dropped. Note that this
1811 * code will execute for either plain or partitioned indexes.
1812 */
1813 if (expected_relkind == RELKIND_INDEX &&
1814 relOid != oldRelOid)
1815 {
1816 state->heapOid = IndexGetRelation(relOid, true);
1817 if (OidIsValid(state->heapOid))
1819 }
1820
1821 /*
1822 * Similarly, if the relation is a partition, we must acquire lock on its
1823 * parent before locking the partition. That's because queries lock the
1824 * parent before its partitions, so we risk deadlock if we do it the other
1825 * way around.
1826 */
1827 if (is_partition && relOid != oldRelOid)
1828 {
1829 state->partParentOid = get_partition_parent(relOid, true);
1830 if (OidIsValid(state->partParentOid))
1831 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1832 }
1833}
1834
1835/*
1836 * ExecuteTruncate
1837 * Executes a TRUNCATE command.
1838 *
1839 * This is a multi-relation truncate. We first open and grab exclusive
1840 * lock on all relations involved, checking permissions and otherwise
1841 * verifying that the relation is OK for truncation. Note that if relations
1842 * are foreign tables, at this stage, we have not yet checked that their
1843 * foreign data in external data sources are OK for truncation. These are
1844 * checked when foreign data are actually truncated later. In CASCADE mode,
1845 * relations having FK references to the targeted relations are automatically
1846 * added to the group; in RESTRICT mode, we check that all FK references are
1847 * internal to the group that's being truncated. Finally all the relations
1848 * are truncated and reindexed.
1849 */
1850void
1852{
1853 List *rels = NIL;
1854 List *relids = NIL;
1855 List *relids_logged = NIL;
1856 ListCell *cell;
1857
1858 /*
1859 * Open, exclusive-lock, and check all the explicitly-specified relations
1860 */
1861 foreach(cell, stmt->relations)
1862 {
1863 RangeVar *rv = lfirst(cell);
1864 Relation rel;
1865 bool recurse = rv->inh;
1866 Oid myrelid;
1867 LOCKMODE lockmode = AccessExclusiveLock;
1868
1869 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1871 NULL);
1872
1873 /* don't throw error for "TRUNCATE foo, foo" */
1874 if (list_member_oid(relids, myrelid))
1875 continue;
1876
1877 /* open the relation, we already hold a lock on it */
1878 rel = table_open(myrelid, NoLock);
1879
1880 /*
1881 * RangeVarGetRelidExtended() has done most checks with its callback,
1882 * but other checks with the now-opened Relation remain.
1883 */
1885
1886 rels = lappend(rels, rel);
1887 relids = lappend_oid(relids, myrelid);
1888
1889 /* Log this relation only if needed for logical decoding */
1891 relids_logged = lappend_oid(relids_logged, myrelid);
1892
1893 if (recurse)
1894 {
1895 ListCell *child;
1896 List *children;
1897
1898 children = find_all_inheritors(myrelid, lockmode, NULL);
1899
1900 foreach(child, children)
1901 {
1902 Oid childrelid = lfirst_oid(child);
1903
1904 if (list_member_oid(relids, childrelid))
1905 continue;
1906
1907 /* find_all_inheritors already got lock */
1908 rel = table_open(childrelid, NoLock);
1909
1910 /*
1911 * It is possible that the parent table has children that are
1912 * temp tables of other backends. We cannot safely access
1913 * such tables (because of buffering issues), and the best
1914 * thing to do is to silently ignore them. Note that this
1915 * check is the same as one of the checks done in
1916 * truncate_check_activity() called below, still it is kept
1917 * here for simplicity.
1918 */
1919 if (RELATION_IS_OTHER_TEMP(rel))
1920 {
1921 table_close(rel, lockmode);
1922 continue;
1923 }
1924
1925 /*
1926 * Inherited TRUNCATE commands perform access permission
1927 * checks on the parent table only. So we skip checking the
1928 * children's permissions and don't call
1929 * truncate_check_perms() here.
1930 */
1933
1934 rels = lappend(rels, rel);
1935 relids = lappend_oid(relids, childrelid);
1936
1937 /* Log this relation only if needed for logical decoding */
1939 relids_logged = lappend_oid(relids_logged, childrelid);
1940 }
1941 }
1942 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1943 ereport(ERROR,
1944 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1945 errmsg("cannot truncate only a partitioned table"),
1946 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1947 }
1948
1949 ExecuteTruncateGuts(rels, relids, relids_logged,
1950 stmt->behavior, stmt->restart_seqs, false);
1951
1952 /* And close the rels */
1953 foreach(cell, rels)
1954 {
1955 Relation rel = (Relation) lfirst(cell);
1956
1957 table_close(rel, NoLock);
1958 }
1959}
1960
1961/*
1962 * ExecuteTruncateGuts
1963 *
1964 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1965 * command (see above) as well as replication subscribers that execute a
1966 * replicated TRUNCATE action.
1967 *
1968 * explicit_rels is the list of Relations to truncate that the command
1969 * specified. relids is the list of Oids corresponding to explicit_rels.
1970 * relids_logged is the list of Oids (a subset of relids) that require
1971 * WAL-logging. This is all a bit redundant, but the existing callers have
1972 * this information handy in this form.
1973 */
1974void
1976 List *relids,
1977 List *relids_logged,
1978 DropBehavior behavior, bool restart_seqs,
1979 bool run_as_table_owner)
1980{
1981 List *rels;
1982 List *seq_relids = NIL;
1983 HTAB *ft_htab = NULL;
1984 EState *estate;
1985 ResultRelInfo *resultRelInfos;
1986 ResultRelInfo *resultRelInfo;
1987 SubTransactionId mySubid;
1988 ListCell *cell;
1989 Oid *logrelids;
1990
1991 /*
1992 * Check the explicitly-specified relations.
1993 *
1994 * In CASCADE mode, suck in all referencing relations as well. This
1995 * requires multiple iterations to find indirectly-dependent relations. At
1996 * each phase, we need to exclusive-lock new rels before looking for their
1997 * dependencies, else we might miss something. Also, we check each rel as
1998 * soon as we open it, to avoid a faux pas such as holding lock for a long
1999 * time on a rel we have no permissions for.
2000 */
2001 rels = list_copy(explicit_rels);
2002 if (behavior == DROP_CASCADE)
2003 {
2004 for (;;)
2005 {
2006 List *newrelids;
2007
2008 newrelids = heap_truncate_find_FKs(relids);
2009 if (newrelids == NIL)
2010 break; /* nothing else to add */
2011
2012 foreach(cell, newrelids)
2013 {
2014 Oid relid = lfirst_oid(cell);
2015 Relation rel;
2016
2017 rel = table_open(relid, AccessExclusiveLock);
2019 (errmsg("truncate cascades to table \"%s\"",
2021 truncate_check_rel(relid, rel->rd_rel);
2022 truncate_check_perms(relid, rel->rd_rel);
2024 rels = lappend(rels, rel);
2025 relids = lappend_oid(relids, relid);
2026
2027 /* Log this relation only if needed for logical decoding */
2029 relids_logged = lappend_oid(relids_logged, relid);
2030 }
2031 }
2032 }
2033
2034 /*
2035 * Check foreign key references. In CASCADE mode, this should be
2036 * unnecessary since we just pulled in all the references; but as a
2037 * cross-check, do it anyway if in an Assert-enabled build.
2038 */
2039#ifdef USE_ASSERT_CHECKING
2040 heap_truncate_check_FKs(rels, false);
2041#else
2042 if (behavior == DROP_RESTRICT)
2043 heap_truncate_check_FKs(rels, false);
2044#endif
2045
2046 /*
2047 * If we are asked to restart sequences, find all the sequences, lock them
2048 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2049 * We want to do this early since it's pointless to do all the truncation
2050 * work only to fail on sequence permissions.
2051 */
2052 if (restart_seqs)
2053 {
2054 foreach(cell, rels)
2055 {
2056 Relation rel = (Relation) lfirst(cell);
2057 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2058 ListCell *seqcell;
2059
2060 foreach(seqcell, seqlist)
2061 {
2062 Oid seq_relid = lfirst_oid(seqcell);
2063 Relation seq_rel;
2064
2065 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2066
2067 /* This check must match AlterSequence! */
2068 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2070 RelationGetRelationName(seq_rel));
2071
2072 seq_relids = lappend_oid(seq_relids, seq_relid);
2073
2074 relation_close(seq_rel, NoLock);
2075 }
2076 }
2077 }
2078
2079 /* Prepare to catch AFTER triggers. */
2081
2082 /*
2083 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2084 * each relation. We don't need to call ExecOpenIndices, though.
2085 *
2086 * We put the ResultRelInfos in the es_opened_result_relations list, even
2087 * though we don't have a range table and don't populate the
2088 * es_result_relations array. That's a bit bogus, but it's enough to make
2089 * ExecGetTriggerResultRel() find them.
2090 */
2091 estate = CreateExecutorState();
2092 resultRelInfos = (ResultRelInfo *)
2093 palloc(list_length(rels) * sizeof(ResultRelInfo));
2094 resultRelInfo = resultRelInfos;
2095 foreach(cell, rels)
2096 {
2097 Relation rel = (Relation) lfirst(cell);
2098
2099 InitResultRelInfo(resultRelInfo,
2100 rel,
2101 0, /* dummy rangetable index */
2102 NULL,
2103 0);
2105 lappend(estate->es_opened_result_relations, resultRelInfo);
2106 resultRelInfo++;
2107 }
2108
2109 /*
2110 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2111 * truncating (this is because one of them might throw an error). Also, if
2112 * we were to allow them to prevent statement execution, that would need
2113 * to be handled here.
2114 */
2115 resultRelInfo = resultRelInfos;
2116 foreach(cell, rels)
2117 {
2118 UserContext ucxt;
2119
2120 if (run_as_table_owner)
2121 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2122 &ucxt);
2123 ExecBSTruncateTriggers(estate, resultRelInfo);
2124 if (run_as_table_owner)
2125 RestoreUserContext(&ucxt);
2126 resultRelInfo++;
2127 }
2128
2129 /*
2130 * OK, truncate each table.
2131 */
2132 mySubid = GetCurrentSubTransactionId();
2133
2134 foreach(cell, rels)
2135 {
2136 Relation rel = (Relation) lfirst(cell);
2137
2138 /* Skip partitioned tables as there is nothing to do */
2139 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2140 continue;
2141
2142 /*
2143 * Build the lists of foreign tables belonging to each foreign server
2144 * and pass each list to the foreign data wrapper's callback function,
2145 * so that each server can truncate its all foreign tables in bulk.
2146 * Each list is saved as a single entry in a hash table that uses the
2147 * server OID as lookup key.
2148 */
2149 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2150 {
2152 bool found;
2153 ForeignTruncateInfo *ft_info;
2154
2155 /* First time through, initialize hashtable for foreign tables */
2156 if (!ft_htab)
2157 {
2158 HASHCTL hctl;
2159
2160 memset(&hctl, 0, sizeof(HASHCTL));
2161 hctl.keysize = sizeof(Oid);
2162 hctl.entrysize = sizeof(ForeignTruncateInfo);
2164
2165 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2166 32, /* start small and extend */
2167 &hctl,
2169 }
2170
2171 /* Find or create cached entry for the foreign table */
2172 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2173 if (!found)
2174 ft_info->rels = NIL;
2175
2176 /*
2177 * Save the foreign table in the entry of the server that the
2178 * foreign table belongs to.
2179 */
2180 ft_info->rels = lappend(ft_info->rels, rel);
2181 continue;
2182 }
2183
2184 /*
2185 * Normally, we need a transaction-safe truncation here. However, if
2186 * the table was either created in the current (sub)transaction or has
2187 * a new relfilenumber in the current (sub)transaction, then we can
2188 * just truncate it in-place, because a rollback would cause the whole
2189 * table or the current physical file to be thrown away anyway.
2190 */
2191 if (rel->rd_createSubid == mySubid ||
2192 rel->rd_newRelfilelocatorSubid == mySubid)
2193 {
2194 /* Immediate, non-rollbackable truncation is OK */
2196 }
2197 else
2198 {
2199 Oid heap_relid;
2200 Oid toast_relid;
2201 ReindexParams reindex_params = {0};
2202
2203 /*
2204 * This effectively deletes all rows in the table, and may be done
2205 * in a serializable transaction. In that case we must record a
2206 * rw-conflict in to this transaction from each transaction
2207 * holding a predicate lock on the table.
2208 */
2210
2211 /*
2212 * Need the full transaction-safe pushups.
2213 *
2214 * Create a new empty storage file for the relation, and assign it
2215 * as the relfilenumber value. The old storage file is scheduled
2216 * for deletion at commit.
2217 */
2218 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2219
2220 heap_relid = RelationGetRelid(rel);
2221
2222 /*
2223 * The same for the toast table, if any.
2224 */
2225 toast_relid = rel->rd_rel->reltoastrelid;
2226 if (OidIsValid(toast_relid))
2227 {
2228 Relation toastrel = relation_open(toast_relid,
2230
2232 toastrel->rd_rel->relpersistence);
2233 table_close(toastrel, NoLock);
2234 }
2235
2236 /*
2237 * Reconstruct the indexes to match, and we're done.
2238 */
2240 &reindex_params);
2241 }
2242
2244 }
2245
2246 /* Now go through the hash table, and truncate foreign tables */
2247 if (ft_htab)
2248 {
2249 ForeignTruncateInfo *ft_info;
2250 HASH_SEQ_STATUS seq;
2251
2252 hash_seq_init(&seq, ft_htab);
2253
2254 PG_TRY();
2255 {
2256 while ((ft_info = hash_seq_search(&seq)) != NULL)
2257 {
2258 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2259
2260 /* truncate_check_rel() has checked that already */
2261 Assert(routine->ExecForeignTruncate != NULL);
2262
2263 routine->ExecForeignTruncate(ft_info->rels,
2264 behavior,
2265 restart_seqs);
2266 }
2267 }
2268 PG_FINALLY();
2269 {
2270 hash_destroy(ft_htab);
2271 }
2272 PG_END_TRY();
2273 }
2274
2275 /*
2276 * Restart owned sequences if we were asked to.
2277 */
2278 foreach(cell, seq_relids)
2279 {
2280 Oid seq_relid = lfirst_oid(cell);
2281
2282 ResetSequence(seq_relid);
2283 }
2284
2285 /*
2286 * Write a WAL record to allow this set of actions to be logically
2287 * decoded.
2288 *
2289 * Assemble an array of relids so we can write a single WAL record for the
2290 * whole action.
2291 */
2292 if (relids_logged != NIL)
2293 {
2294 xl_heap_truncate xlrec;
2295 int i = 0;
2296
2297 /* should only get here if wal_level >= logical */
2299
2300 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2301 foreach(cell, relids_logged)
2302 logrelids[i++] = lfirst_oid(cell);
2303
2304 xlrec.dbId = MyDatabaseId;
2305 xlrec.nrelids = list_length(relids_logged);
2306 xlrec.flags = 0;
2307 if (behavior == DROP_CASCADE)
2308 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2309 if (restart_seqs)
2311
2314 XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2315
2317
2318 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2319 }
2320
2321 /*
2322 * Process all AFTER STATEMENT TRUNCATE triggers.
2323 */
2324 resultRelInfo = resultRelInfos;
2325 foreach(cell, rels)
2326 {
2327 UserContext ucxt;
2328
2329 if (run_as_table_owner)
2330 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2331 &ucxt);
2332 ExecASTruncateTriggers(estate, resultRelInfo);
2333 if (run_as_table_owner)
2334 RestoreUserContext(&ucxt);
2335 resultRelInfo++;
2336 }
2337
2338 /* Handle queued AFTER triggers */
2339 AfterTriggerEndQuery(estate);
2340
2341 /* We can clean up the EState now */
2342 FreeExecutorState(estate);
2343
2344 /*
2345 * Close any rels opened by CASCADE (can't do this while EState still
2346 * holds refs)
2347 */
2348 rels = list_difference_ptr(rels, explicit_rels);
2349 foreach(cell, rels)
2350 {
2351 Relation rel = (Relation) lfirst(cell);
2352
2353 table_close(rel, NoLock);
2354 }
2355}
2356
2357/*
2358 * Check that a given relation is safe to truncate. Subroutine for
2359 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2360 */
2361static void
2363{
2364 char *relname = NameStr(reltuple->relname);
2365
2366 /*
2367 * Only allow truncate on regular tables, foreign tables using foreign
2368 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2369 * latter are only being included here for the following checks; no
2370 * physical truncation will occur in their case.).
2371 */
2372 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2373 {
2374 Oid serverid = GetForeignServerIdByRelId(relid);
2375 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2376
2377 if (!fdwroutine->ExecForeignTruncate)
2378 ereport(ERROR,
2379 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2380 errmsg("cannot truncate foreign table \"%s\"",
2381 relname)));
2382 }
2383 else if (reltuple->relkind != RELKIND_RELATION &&
2384 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2385 ereport(ERROR,
2386 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2387 errmsg("\"%s\" is not a table", relname)));
2388
2389 /*
2390 * Most system catalogs can't be truncated at all, or at least not unless
2391 * allow_system_table_mods=on. As an exception, however, we allow
2392 * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2393 * pg_upgrade, because we need to change its relfilenode to match the old
2394 * cluster, and allowing a TRUNCATE command to be executed is the easiest
2395 * way of doing that.
2396 */
2397 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2398 && (!IsBinaryUpgrade ||
2399 (relid != LargeObjectRelationId &&
2400 relid != LargeObjectMetadataRelationId)))
2401 ereport(ERROR,
2402 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2403 errmsg("permission denied: \"%s\" is a system catalog",
2404 relname)));
2405
2407}
2408
2409/*
2410 * Check that current user has the permission to truncate given relation.
2411 */
2412static void
2414{
2415 char *relname = NameStr(reltuple->relname);
2416 AclResult aclresult;
2417
2418 /* Permissions checks */
2419 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2420 if (aclresult != ACLCHECK_OK)
2421 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2422 relname);
2423}
2424
2425/*
2426 * Set of extra sanity checks to check if a given relation is safe to
2427 * truncate. This is split with truncate_check_rel() as
2428 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2429 */
2430static void
2432{
2433 /*
2434 * Don't allow truncate on temp tables of other backends ... their local
2435 * buffer manager is not going to cope.
2436 */
2437 if (RELATION_IS_OTHER_TEMP(rel))
2438 ereport(ERROR,
2439 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2440 errmsg("cannot truncate temporary tables of other sessions")));
2441
2442 /*
2443 * Also check for active uses of the relation in the current transaction,
2444 * including open scans and pending AFTER trigger events.
2445 */
2446 CheckTableNotInUse(rel, "TRUNCATE");
2447}
2448
2449/*
2450 * storage_name
2451 * returns the name corresponding to a typstorage/attstorage enum value
2452 */
2453static const char *
2455{
2456 switch (c)
2457 {
2458 case TYPSTORAGE_PLAIN:
2459 return "PLAIN";
2460 case TYPSTORAGE_EXTERNAL:
2461 return "EXTERNAL";
2462 case TYPSTORAGE_EXTENDED:
2463 return "EXTENDED";
2464 case TYPSTORAGE_MAIN:
2465 return "MAIN";
2466 default:
2467 return "???";
2468 }
2469}
2470
2471/*----------
2472 * MergeAttributes
2473 * Returns new schema given initial schema and superclasses.
2474 *
2475 * Input arguments:
2476 * 'columns' is the column/attribute definition for the table. (It's a list
2477 * of ColumnDef's.) It is destructively changed.
2478 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2479 * 'relpersistence' is the persistence type of the table.
2480 * 'is_partition' tells if the table is a partition.
2481 *
2482 * Output arguments:
2483 * 'supconstr' receives a list of CookedConstraint representing
2484 * CHECK constraints belonging to parent relations, updated as
2485 * necessary to be valid for the child.
2486 * 'supnotnulls' receives a list of CookedConstraint representing
2487 * not-null constraints based on those from parent relations.
2488 *
2489 * Return value:
2490 * Completed schema list.
2491 *
2492 * Notes:
2493 * The order in which the attributes are inherited is very important.
2494 * Intuitively, the inherited attributes should come first. If a table
2495 * inherits from multiple parents, the order of those attributes are
2496 * according to the order of the parents specified in CREATE TABLE.
2497 *
2498 * Here's an example:
2499 *
2500 * create table person (name text, age int4, location point);
2501 * create table emp (salary int4, manager text) inherits(person);
2502 * create table student (gpa float8) inherits (person);
2503 * create table stud_emp (percent int4) inherits (emp, student);
2504 *
2505 * The order of the attributes of stud_emp is:
2506 *
2507 * person {1:name, 2:age, 3:location}
2508 * / \
2509 * {6:gpa} student emp {4:salary, 5:manager}
2510 * \ /
2511 * stud_emp {7:percent}
2512 *
2513 * If the same attribute name appears multiple times, then it appears
2514 * in the result table in the proper location for its first appearance.
2515 *
2516 * Constraints (including not-null constraints) for the child table
2517 * are the union of all relevant constraints, from both the child schema
2518 * and parent tables. In addition, in legacy inheritance, each column that
2519 * appears in a primary key in any of the parents also gets a NOT NULL
2520 * constraint (partitioning doesn't need this, because the PK itself gets
2521 * inherited.)
2522 *
2523 * The default value for a child column is defined as:
2524 * (1) If the child schema specifies a default, that value is used.
2525 * (2) If neither the child nor any parent specifies a default, then
2526 * the column will not have a default.
2527 * (3) If conflicting defaults are inherited from different parents
2528 * (and not overridden by the child), an error is raised.
2529 * (4) Otherwise the inherited default is used.
2530 *
2531 * Note that the default-value infrastructure is used for generated
2532 * columns' expressions too, so most of the preceding paragraph applies
2533 * to generation expressions too. We insist that a child column be
2534 * generated if and only if its parent(s) are, but it need not have
2535 * the same generation expression.
2536 *----------
2537 */
2538static List *
2539MergeAttributes(List *columns, const List *supers, char relpersistence,
2540 bool is_partition, List **supconstr, List **supnotnulls)
2541{
2542 List *inh_columns = NIL;
2543 List *constraints = NIL;
2544 List *nnconstraints = NIL;
2545 bool have_bogus_defaults = false;
2546 int child_attno;
2547 static Node bogus_marker = {0}; /* marks conflicting defaults */
2548 List *saved_columns = NIL;
2549 ListCell *lc;
2550
2551 /*
2552 * Check for and reject tables with too many columns. We perform this
2553 * check relatively early for two reasons: (a) we don't run the risk of
2554 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2555 * okay if we're processing <= 1600 columns, but could take minutes to
2556 * execute if the user attempts to create a table with hundreds of
2557 * thousands of columns.
2558 *
2559 * Note that we also need to check that we do not exceed this figure after
2560 * including columns from inherited relations.
2561 */
2562 if (list_length(columns) > MaxHeapAttributeNumber)
2563 ereport(ERROR,
2564 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2565 errmsg("tables can have at most %d columns",
2567
2568 /*
2569 * Check for duplicate names in the explicit list of attributes.
2570 *
2571 * Although we might consider merging such entries in the same way that we
2572 * handle name conflicts for inherited attributes, it seems to make more
2573 * sense to assume such conflicts are errors.
2574 *
2575 * We don't use foreach() here because we have two nested loops over the
2576 * columns list, with possible element deletions in the inner one. If we
2577 * used foreach_delete_current() it could only fix up the state of one of
2578 * the loops, so it seems cleaner to use looping over list indexes for
2579 * both loops. Note that any deletion will happen beyond where the outer
2580 * loop is, so its index never needs adjustment.
2581 */
2582 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2583 {
2584 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2585
2586 if (!is_partition && coldef->typeName == NULL)
2587 {
2588 /*
2589 * Typed table column option that does not belong to a column from
2590 * the type. This works because the columns from the type come
2591 * first in the list. (We omit this check for partition column
2592 * lists; those are processed separately below.)
2593 */
2594 ereport(ERROR,
2595 (errcode(ERRCODE_UNDEFINED_COLUMN),
2596 errmsg("column \"%s\" does not exist",
2597 coldef->colname)));
2598 }
2599
2600 /* restpos scans all entries beyond coldef; incr is in loop body */
2601 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2602 {
2603 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2604
2605 if (strcmp(coldef->colname, restdef->colname) == 0)
2606 {
2607 if (coldef->is_from_type)
2608 {
2609 /*
2610 * merge the column options into the column from the type
2611 */
2612 coldef->is_not_null = restdef->is_not_null;
2613 coldef->raw_default = restdef->raw_default;
2614 coldef->cooked_default = restdef->cooked_default;
2615 coldef->constraints = restdef->constraints;
2616 coldef->is_from_type = false;
2617 columns = list_delete_nth_cell(columns, restpos);
2618 }
2619 else
2620 ereport(ERROR,
2621 (errcode(ERRCODE_DUPLICATE_COLUMN),
2622 errmsg("column \"%s\" specified more than once",
2623 coldef->colname)));
2624 }
2625 else
2626 restpos++;
2627 }
2628 }
2629
2630 /*
2631 * In case of a partition, there are no new column definitions, only dummy
2632 * ColumnDefs created for column constraints. Set them aside for now and
2633 * process them at the end.
2634 */
2635 if (is_partition)
2636 {
2637 saved_columns = columns;
2638 columns = NIL;
2639 }
2640
2641 /*
2642 * Scan the parents left-to-right, and merge their attributes to form a
2643 * list of inherited columns (inh_columns).
2644 */
2645 child_attno = 0;
2646 foreach(lc, supers)
2647 {
2648 Oid parent = lfirst_oid(lc);
2649 Relation relation;
2650 TupleDesc tupleDesc;
2651 TupleConstr *constr;
2652 AttrMap *newattmap;
2653 List *inherited_defaults;
2654 List *cols_with_defaults;
2655 List *nnconstrs;
2656 ListCell *lc1;
2657 ListCell *lc2;
2658 Bitmapset *nncols = NULL;
2659
2660 /* caller already got lock */
2661 relation = table_open(parent, NoLock);
2662
2663 /*
2664 * Check for active uses of the parent partitioned table in the
2665 * current transaction, such as being used in some manner by an
2666 * enclosing command.
2667 */
2668 if (is_partition)
2669 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2670
2671 /*
2672 * We do not allow partitioned tables and partitions to participate in
2673 * regular inheritance.
2674 */
2675 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2676 ereport(ERROR,
2677 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2678 errmsg("cannot inherit from partitioned table \"%s\"",
2679 RelationGetRelationName(relation))));
2680 if (relation->rd_rel->relispartition && !is_partition)
2681 ereport(ERROR,
2682 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2683 errmsg("cannot inherit from partition \"%s\"",
2684 RelationGetRelationName(relation))));
2685
2686 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2687 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2688 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2689 ereport(ERROR,
2690 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2691 errmsg("inherited relation \"%s\" is not a table or foreign table",
2692 RelationGetRelationName(relation))));
2693
2694 /*
2695 * If the parent is permanent, so must be all of its partitions. Note
2696 * that inheritance allows that case.
2697 */
2698 if (is_partition &&
2699 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2700 relpersistence == RELPERSISTENCE_TEMP)
2701 ereport(ERROR,
2702 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2703 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2704 RelationGetRelationName(relation))));
2705
2706 /* Permanent rels cannot inherit from temporary ones */
2707 if (relpersistence != RELPERSISTENCE_TEMP &&
2708 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2709 ereport(ERROR,
2710 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2711 errmsg(!is_partition
2712 ? "cannot inherit from temporary relation \"%s\""
2713 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2714 RelationGetRelationName(relation))));
2715
2716 /* If existing rel is temp, it must belong to this session */
2717 if (RELATION_IS_OTHER_TEMP(relation))
2718 ereport(ERROR,
2719 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2720 errmsg(!is_partition
2721 ? "cannot inherit from temporary relation of another session"
2722 : "cannot create as partition of temporary relation of another session")));
2723
2724 /*
2725 * We should have an UNDER permission flag for this, but for now,
2726 * demand that creator of a child table own the parent.
2727 */
2728 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2730 RelationGetRelationName(relation));
2731
2732 tupleDesc = RelationGetDescr(relation);
2733 constr = tupleDesc->constr;
2734
2735 /*
2736 * newattmap->attnums[] will contain the child-table attribute numbers
2737 * for the attributes of this parent table. (They are not the same
2738 * for parents after the first one, nor if we have dropped columns.)
2739 */
2740 newattmap = make_attrmap(tupleDesc->natts);
2741
2742 /* We can't process inherited defaults until newattmap is complete. */
2743 inherited_defaults = cols_with_defaults = NIL;
2744
2745 /*
2746 * Request attnotnull on columns that have a not-null constraint
2747 * that's not marked NO INHERIT (even if not valid).
2748 */
2749 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2750 true, false);
2751 foreach_ptr(CookedConstraint, cc, nnconstrs)
2752 nncols = bms_add_member(nncols, cc->attnum);
2753
2754 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2755 parent_attno++)
2756 {
2757 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2758 parent_attno - 1);
2759 char *attributeName = NameStr(attribute->attname);
2760 int exist_attno;
2761 ColumnDef *newdef;
2762 ColumnDef *mergeddef;
2763
2764 /*
2765 * Ignore dropped columns in the parent.
2766 */
2767 if (attribute->attisdropped)
2768 continue; /* leave newattmap->attnums entry as zero */
2769
2770 /*
2771 * Create new column definition
2772 */
2773 newdef = makeColumnDef(attributeName, attribute->atttypid,
2774 attribute->atttypmod, attribute->attcollation);
2775 newdef->storage = attribute->attstorage;
2776 newdef->generated = attribute->attgenerated;
2777 if (CompressionMethodIsValid(attribute->attcompression))
2778 newdef->compression =
2779 pstrdup(GetCompressionMethodName(attribute->attcompression));
2780
2781 /*
2782 * Regular inheritance children are independent enough not to
2783 * inherit identity columns. But partitions are integral part of
2784 * a partitioned table and inherit identity column.
2785 */
2786 if (is_partition)
2787 newdef->identity = attribute->attidentity;
2788
2789 /*
2790 * Does it match some previously considered column from another
2791 * parent?
2792 */
2793 exist_attno = findAttrByName(attributeName, inh_columns);
2794 if (exist_attno > 0)
2795 {
2796 /*
2797 * Yes, try to merge the two column definitions.
2798 */
2799 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2800
2801 newattmap->attnums[parent_attno - 1] = exist_attno;
2802
2803 /*
2804 * Partitions have only one parent, so conflict should never
2805 * occur.
2806 */
2807 Assert(!is_partition);
2808 }
2809 else
2810 {
2811 /*
2812 * No, create a new inherited column
2813 */
2814 newdef->inhcount = 1;
2815 newdef->is_local = false;
2816 inh_columns = lappend(inh_columns, newdef);
2817
2818 newattmap->attnums[parent_attno - 1] = ++child_attno;
2819 mergeddef = newdef;
2820 }
2821
2822 /*
2823 * mark attnotnull if parent has it
2824 */
2825 if (bms_is_member(parent_attno, nncols))
2826 mergeddef->is_not_null = true;
2827
2828 /*
2829 * Locate default/generation expression if any
2830 */
2831 if (attribute->atthasdef)
2832 {
2833 Node *this_default;
2834
2835 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2836 if (this_default == NULL)
2837 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2838 parent_attno, RelationGetRelationName(relation));
2839
2840 /*
2841 * If it's a GENERATED default, it might contain Vars that
2842 * need to be mapped to the inherited column(s)' new numbers.
2843 * We can't do that till newattmap is ready, so just remember
2844 * all the inherited default expressions for the moment.
2845 */
2846 inherited_defaults = lappend(inherited_defaults, this_default);
2847 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2848 }
2849 }
2850
2851 /*
2852 * Now process any inherited default expressions, adjusting attnos
2853 * using the completed newattmap map.
2854 */
2855 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2856 {
2857 Node *this_default = (Node *) lfirst(lc1);
2858 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2859 bool found_whole_row;
2860
2861 /* Adjust Vars to match new table's column numbering */
2862 this_default = map_variable_attnos(this_default,
2863 1, 0,
2864 newattmap,
2865 InvalidOid, &found_whole_row);
2866
2867 /*
2868 * For the moment we have to reject whole-row variables. We could
2869 * convert them, if we knew the new table's rowtype OID, but that
2870 * hasn't been assigned yet. (A variable could only appear in a
2871 * generation expression, so the error message is correct.)
2872 */
2873 if (found_whole_row)
2874 ereport(ERROR,
2875 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2876 errmsg("cannot convert whole-row table reference"),
2877 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2878 def->colname,
2879 RelationGetRelationName(relation))));
2880
2881 /*
2882 * If we already had a default from some prior parent, check to
2883 * see if they are the same. If so, no problem; if not, mark the
2884 * column as having a bogus default. Below, we will complain if
2885 * the bogus default isn't overridden by the child columns.
2886 */
2887 Assert(def->raw_default == NULL);
2888 if (def->cooked_default == NULL)
2889 def->cooked_default = this_default;
2890 else if (!equal(def->cooked_default, this_default))
2891 {
2892 def->cooked_default = &bogus_marker;
2893 have_bogus_defaults = true;
2894 }
2895 }
2896
2897 /*
2898 * Now copy the CHECK constraints of this parent, adjusting attnos
2899 * using the completed newattmap map. Identically named constraints
2900 * are merged if possible, else we throw error.
2901 */
2902 if (constr && constr->num_check > 0)
2903 {
2904 ConstrCheck *check = constr->check;
2905
2906 for (int i = 0; i < constr->num_check; i++)
2907 {
2908 char *name = check[i].ccname;
2909 Node *expr;
2910 bool found_whole_row;
2911
2912 /* ignore if the constraint is non-inheritable */
2913 if (check[i].ccnoinherit)
2914 continue;
2915
2916 /* Adjust Vars to match new table's column numbering */
2917 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2918 1, 0,
2919 newattmap,
2920 InvalidOid, &found_whole_row);
2921
2922 /*
2923 * For the moment we have to reject whole-row variables. We
2924 * could convert them, if we knew the new table's rowtype OID,
2925 * but that hasn't been assigned yet.
2926 */
2927 if (found_whole_row)
2928 ereport(ERROR,
2929 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2930 errmsg("cannot convert whole-row table reference"),
2931 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2932 name,
2933 RelationGetRelationName(relation))));
2934
2935 constraints = MergeCheckConstraint(constraints, name, expr,
2936 check[i].ccenforced);
2937 }
2938 }
2939
2940 /*
2941 * Also copy the not-null constraints from this parent. The
2942 * attnotnull markings were already installed above.
2943 */
2944 foreach_ptr(CookedConstraint, nn, nnconstrs)
2945 {
2946 Assert(nn->contype == CONSTR_NOTNULL);
2947
2948 nn->attnum = newattmap->attnums[nn->attnum - 1];
2949
2950 nnconstraints = lappend(nnconstraints, nn);
2951 }
2952
2953 free_attrmap(newattmap);
2954
2955 /*
2956 * Close the parent rel, but keep our lock on it until xact commit.
2957 * That will prevent someone else from deleting or ALTERing the parent
2958 * before the child is committed.
2959 */
2960 table_close(relation, NoLock);
2961 }
2962
2963 /*
2964 * If we had no inherited attributes, the result columns are just the
2965 * explicitly declared columns. Otherwise, we need to merge the declared
2966 * columns into the inherited column list. Although, we never have any
2967 * explicitly declared columns if the table is a partition.
2968 */
2969 if (inh_columns != NIL)
2970 {
2971 int newcol_attno = 0;
2972
2973 foreach(lc, columns)
2974 {
2975 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2976 char *attributeName = newdef->colname;
2977 int exist_attno;
2978
2979 /*
2980 * Partitions have only one parent and have no column definitions
2981 * of their own, so conflict should never occur.
2982 */
2983 Assert(!is_partition);
2984
2985 newcol_attno++;
2986
2987 /*
2988 * Does it match some inherited column?
2989 */
2990 exist_attno = findAttrByName(attributeName, inh_columns);
2991 if (exist_attno > 0)
2992 {
2993 /*
2994 * Yes, try to merge the two column definitions.
2995 */
2996 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2997 }
2998 else
2999 {
3000 /*
3001 * No, attach new column unchanged to result columns.
3002 */
3003 inh_columns = lappend(inh_columns, newdef);
3004 }
3005 }
3006
3007 columns = inh_columns;
3008
3009 /*
3010 * Check that we haven't exceeded the legal # of columns after merging
3011 * in inherited columns.
3012 */
3013 if (list_length(columns) > MaxHeapAttributeNumber)
3014 ereport(ERROR,
3015 (errcode(ERRCODE_TOO_MANY_COLUMNS),
3016 errmsg("tables can have at most %d columns",
3018 }
3019
3020 /*
3021 * Now that we have the column definition list for a partition, we can
3022 * check whether the columns referenced in the column constraint specs
3023 * actually exist. Also, merge column defaults.
3024 */
3025 if (is_partition)
3026 {
3027 foreach(lc, saved_columns)
3028 {
3029 ColumnDef *restdef = lfirst(lc);
3030 bool found = false;
3031 ListCell *l;
3032
3033 foreach(l, columns)
3034 {
3035 ColumnDef *coldef = lfirst(l);
3036
3037 if (strcmp(coldef->colname, restdef->colname) == 0)
3038 {
3039 found = true;
3040
3041 /*
3042 * Check for conflicts related to generated columns.
3043 *
3044 * Same rules as above: generated-ness has to match the
3045 * parent, but the contents of the generation expression
3046 * can be different.
3047 */
3048 if (coldef->generated)
3049 {
3050 if (restdef->raw_default && !restdef->generated)
3051 ereport(ERROR,
3052 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3053 errmsg("column \"%s\" inherits from generated column but specifies default",
3054 restdef->colname)));
3055 if (restdef->identity)
3056 ereport(ERROR,
3057 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3058 errmsg("column \"%s\" inherits from generated column but specifies identity",
3059 restdef->colname)));
3060 }
3061 else
3062 {
3063 if (restdef->generated)
3064 ereport(ERROR,
3065 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3066 errmsg("child column \"%s\" specifies generation expression",
3067 restdef->colname),
3068 errhint("A child table column cannot be generated unless its parent column is.")));
3069 }
3070
3071 if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3072 ereport(ERROR,
3073 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3074 errmsg("column \"%s\" inherits from generated column of different kind",
3075 restdef->colname),
3076 errdetail("Parent column is %s, child column is %s.",
3077 coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3078 restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3079
3080 /*
3081 * Override the parent's default value for this column
3082 * (coldef->cooked_default) with the partition's local
3083 * definition (restdef->raw_default), if there's one. It
3084 * should be physically impossible to get a cooked default
3085 * in the local definition or a raw default in the
3086 * inherited definition, but make sure they're nulls, for
3087 * future-proofing.
3088 */
3089 Assert(restdef->cooked_default == NULL);
3090 Assert(coldef->raw_default == NULL);
3091 if (restdef->raw_default)
3092 {
3093 coldef->raw_default = restdef->raw_default;
3094 coldef->cooked_default = NULL;
3095 }
3096 }
3097 }
3098
3099 /* complain for constraints on columns not in parent */
3100 if (!found)
3101 ereport(ERROR,
3102 (errcode(ERRCODE_UNDEFINED_COLUMN),
3103 errmsg("column \"%s\" does not exist",
3104 restdef->colname)));
3105 }
3106 }
3107
3108 /*
3109 * If we found any conflicting parent default values, check to make sure
3110 * they were overridden by the child.
3111 */
3112 if (have_bogus_defaults)
3113 {
3114 foreach(lc, columns)
3115 {
3116 ColumnDef *def = lfirst(lc);
3117
3118 if (def->cooked_default == &bogus_marker)
3119 {
3120 if (def->generated)
3121 ereport(ERROR,
3122 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3123 errmsg("column \"%s\" inherits conflicting generation expressions",
3124 def->colname),
3125 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3126 else
3127 ereport(ERROR,
3128 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3129 errmsg("column \"%s\" inherits conflicting default values",
3130 def->colname),
3131 errhint("To resolve the conflict, specify a default explicitly.")));
3132 }
3133 }
3134 }
3135
3136 *supconstr = constraints;
3137 *supnotnulls = nnconstraints;
3138
3139 return columns;
3140}
3141
3142
3143/*
3144 * MergeCheckConstraint
3145 * Try to merge an inherited CHECK constraint with previous ones
3146 *
3147 * If we inherit identically-named constraints from multiple parents, we must
3148 * merge them, or throw an error if they don't have identical definitions.
3149 *
3150 * constraints is a list of CookedConstraint structs for previous constraints.
3151 *
3152 * If the new constraint matches an existing one, then the existing
3153 * constraint's inheritance count is updated. If there is a conflict (same
3154 * name but different expression), throw an error. If the constraint neither
3155 * matches nor conflicts with an existing one, a new constraint is appended to
3156 * the list.
3157 */
3158static List *
3159MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3160{
3161 ListCell *lc;
3162 CookedConstraint *newcon;
3163
3164 foreach(lc, constraints)
3165 {
3167
3168 Assert(ccon->contype == CONSTR_CHECK);
3169
3170 /* Non-matching names never conflict */
3171 if (strcmp(ccon->name, name) != 0)
3172 continue;
3173
3174 if (equal(expr, ccon->expr))
3175 {
3176 /* OK to merge constraint with existing */
3177 if (pg_add_s16_overflow(ccon->inhcount, 1,
3178 &ccon->inhcount))
3179 ereport(ERROR,
3180 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3181 errmsg("too many inheritance parents"));
3182
3183 /*
3184 * When enforceability differs, the merged constraint should be
3185 * marked as ENFORCED because one of the parents is ENFORCED.
3186 */
3187 if (!ccon->is_enforced && is_enforced)
3188 {
3189 ccon->is_enforced = true;
3190 ccon->skip_validation = false;
3191 }
3192
3193 return constraints;
3194 }
3195
3196 ereport(ERROR,
3198 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3199 name)));
3200 }
3201
3202 /*
3203 * Constraint couldn't be merged with an existing one and also didn't
3204 * conflict with an existing one, so add it as a new one to the list.
3205 */
3207 newcon->contype = CONSTR_CHECK;
3208 newcon->name = pstrdup(name);
3209 newcon->expr = expr;
3210 newcon->inhcount = 1;
3211 newcon->is_enforced = is_enforced;
3212 newcon->skip_validation = !is_enforced;
3213 return lappend(constraints, newcon);
3214}
3215
3216/*
3217 * MergeChildAttribute
3218 * Merge given child attribute definition into given inherited attribute.
3219 *
3220 * Input arguments:
3221 * 'inh_columns' is the list of inherited ColumnDefs.
3222 * 'exist_attno' is the number of the inherited attribute in inh_columns
3223 * 'newcol_attno' is the attribute number in child table's schema definition
3224 * 'newdef' is the column/attribute definition from the child table.
3225 *
3226 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3227 * ColumnDef remains unchanged.
3228 *
3229 * Notes:
3230 * - The attribute is merged according to the rules laid out in the prologue
3231 * of MergeAttributes().
3232 * - If matching inherited attribute exists but the child attribute can not be
3233 * merged into it, the function throws respective errors.
3234 * - A partition can not have its own column definitions. Hence this function
3235 * is applicable only to a regular inheritance child.
3236 */
3237static void
3238MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3239{
3240 char *attributeName = newdef->colname;
3241 ColumnDef *inhdef;
3242 Oid inhtypeid,
3243 newtypeid;
3244 int32 inhtypmod,
3245 newtypmod;
3246 Oid inhcollid,
3247 newcollid;
3248
3249 if (exist_attno == newcol_attno)
3251 (errmsg("merging column \"%s\" with inherited definition",
3252 attributeName)));
3253 else
3255 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3256 errdetail("User-specified column moved to the position of the inherited column.")));
3257
3258 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3259
3260 /*
3261 * Must have the same type and typmod
3262 */
3263 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3264 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3265 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3266 ereport(ERROR,
3267 (errcode(ERRCODE_DATATYPE_MISMATCH),
3268 errmsg("column \"%s\" has a type conflict",
3269 attributeName),
3270 errdetail("%s versus %s",
3271 format_type_with_typemod(inhtypeid, inhtypmod),
3272 format_type_with_typemod(newtypeid, newtypmod))));
3273
3274 /*
3275 * Must have the same collation
3276 */
3277 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3278 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3279 if (inhcollid != newcollid)
3280 ereport(ERROR,
3281 (errcode(ERRCODE_COLLATION_MISMATCH),
3282 errmsg("column \"%s\" has a collation conflict",
3283 attributeName),
3284 errdetail("\"%s\" versus \"%s\"",
3285 get_collation_name(inhcollid),
3286 get_collation_name(newcollid))));
3287
3288 /*
3289 * Identity is never inherited by a regular inheritance child. Pick
3290 * child's identity definition if there's one.
3291 */
3292 inhdef->identity = newdef->identity;
3293
3294 /*
3295 * Copy storage parameter
3296 */
3297 if (inhdef->storage == 0)
3298 inhdef->storage = newdef->storage;
3299 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3300 ereport(ERROR,
3301 (errcode(ERRCODE_DATATYPE_MISMATCH),
3302 errmsg("column \"%s\" has a storage parameter conflict",
3303 attributeName),
3304 errdetail("%s versus %s",
3305 storage_name(inhdef->storage),
3306 storage_name(newdef->storage))));
3307
3308 /*
3309 * Copy compression parameter
3310 */
3311 if (inhdef->compression == NULL)
3312 inhdef->compression = newdef->compression;
3313 else if (newdef->compression != NULL)
3314 {
3315 if (strcmp(inhdef->compression, newdef->compression) != 0)
3316 ereport(ERROR,
3317 (errcode(ERRCODE_DATATYPE_MISMATCH),
3318 errmsg("column \"%s\" has a compression method conflict",
3319 attributeName),
3320 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3321 }
3322
3323 /*
3324 * Merge of not-null constraints = OR 'em together
3325 */
3326 inhdef->is_not_null |= newdef->is_not_null;
3327
3328 /*
3329 * Check for conflicts related to generated columns.
3330 *
3331 * If the parent column is generated, the child column will be made a
3332 * generated column if it isn't already. If it is a generated column,
3333 * we'll take its generation expression in preference to the parent's. We
3334 * must check that the child column doesn't specify a default value or
3335 * identity, which matches the rules for a single column in
3336 * parse_utilcmd.c.
3337 *
3338 * Conversely, if the parent column is not generated, the child column
3339 * can't be either. (We used to allow that, but it results in being able
3340 * to override the generation expression via UPDATEs through the parent.)
3341 */
3342 if (inhdef->generated)
3343 {
3344 if (newdef->raw_default && !newdef->generated)
3345 ereport(ERROR,
3346 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3347 errmsg("column \"%s\" inherits from generated column but specifies default",
3348 inhdef->colname)));
3349 if (newdef->identity)
3350 ereport(ERROR,
3351 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3352 errmsg("column \"%s\" inherits from generated column but specifies identity",
3353 inhdef->colname)));
3354 }
3355 else
3356 {
3357 if (newdef->generated)
3358 ereport(ERROR,
3359 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3360 errmsg("child column \"%s\" specifies generation expression",
3361 inhdef->colname),
3362 errhint("A child table column cannot be generated unless its parent column is.")));
3363 }
3364
3365 if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3366 ereport(ERROR,
3367 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3368 errmsg("column \"%s\" inherits from generated column of different kind",
3369 inhdef->colname),
3370 errdetail("Parent column is %s, child column is %s.",
3371 inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3372 newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3373
3374 /*
3375 * If new def has a default, override previous default
3376 */
3377 if (newdef->raw_default != NULL)
3378 {
3379 inhdef->raw_default = newdef->raw_default;
3380 inhdef->cooked_default = newdef->cooked_default;
3381 }
3382
3383 /* Mark the column as locally defined */
3384 inhdef->is_local = true;
3385}
3386
3387/*
3388 * MergeInheritedAttribute
3389 * Merge given parent attribute definition into specified attribute
3390 * inherited from the previous parents.
3391 *
3392 * Input arguments:
3393 * 'inh_columns' is the list of previously inherited ColumnDefs.
3394 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3395 * 'newdef' is the new parent column/attribute definition to be merged.
3396 *
3397 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3398 *
3399 * Notes:
3400 * - The attribute is merged according to the rules laid out in the prologue
3401 * of MergeAttributes().
3402 * - If matching inherited attribute exists but the new attribute can not be
3403 * merged into it, the function throws respective errors.
3404 * - A partition inherits from only a single parent. Hence this function is
3405 * applicable only to a regular inheritance.
3406 */
3407static ColumnDef *
3409 int exist_attno,
3410 const ColumnDef *newdef)
3411{
3412 char *attributeName = newdef->colname;
3413 ColumnDef *prevdef;
3414 Oid prevtypeid,
3415 newtypeid;
3416 int32 prevtypmod,
3417 newtypmod;
3418 Oid prevcollid,
3419 newcollid;
3420
3422 (errmsg("merging multiple inherited definitions of column \"%s\"",
3423 attributeName)));
3424 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3425
3426 /*
3427 * Must have the same type and typmod
3428 */
3429 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3430 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3431 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3432 ereport(ERROR,
3433 (errcode(ERRCODE_DATATYPE_MISMATCH),
3434 errmsg("inherited column \"%s\" has a type conflict",
3435 attributeName),
3436 errdetail("%s versus %s",
3437 format_type_with_typemod(prevtypeid, prevtypmod),
3438 format_type_with_typemod(newtypeid, newtypmod))));
3439
3440 /*
3441 * Must have the same collation
3442 */
3443 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3444 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3445 if (prevcollid != newcollid)
3446 ereport(ERROR,
3447 (errcode(ERRCODE_COLLATION_MISMATCH),
3448 errmsg("inherited column \"%s\" has a collation conflict",
3449 attributeName),
3450 errdetail("\"%s\" versus \"%s\"",
3451 get_collation_name(prevcollid),
3452 get_collation_name(newcollid))));
3453
3454 /*
3455 * Copy/check storage parameter
3456 */
3457 if (prevdef->storage == 0)
3458 prevdef->storage = newdef->storage;
3459 else if (prevdef->storage != newdef->storage)
3460 ereport(ERROR,
3461 (errcode(ERRCODE_DATATYPE_MISMATCH),
3462 errmsg("inherited column \"%s\" has a storage parameter conflict",
3463 attributeName),
3464 errdetail("%s versus %s",
3465 storage_name(prevdef->storage),
3466 storage_name(newdef->storage))));
3467
3468 /*
3469 * Copy/check compression parameter
3470 */
3471 if (prevdef->compression == NULL)
3472 prevdef->compression = newdef->compression;
3473 else if (newdef->compression != NULL)
3474 {
3475 if (strcmp(prevdef->compression, newdef->compression) != 0)
3476 ereport(ERROR,
3477 (errcode(ERRCODE_DATATYPE_MISMATCH),
3478 errmsg("column \"%s\" has a compression method conflict",
3479 attributeName),
3480 errdetail("%s versus %s",
3481 prevdef->compression, newdef->compression)));
3482 }
3483
3484 /*
3485 * Check for GENERATED conflicts
3486 */
3487 if (prevdef->generated != newdef->generated)
3488 ereport(ERROR,
3489 (errcode(ERRCODE_DATATYPE_MISMATCH),
3490 errmsg("inherited column \"%s\" has a generation conflict",
3491 attributeName)));
3492
3493 /*
3494 * Default and other constraints are handled by the caller.
3495 */
3496
3497 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3498 &prevdef->inhcount))
3499 ereport(ERROR,
3500 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3501 errmsg("too many inheritance parents"));
3502
3503 return prevdef;
3504}
3505
3506/*
3507 * StoreCatalogInheritance
3508 * Updates the system catalogs with proper inheritance information.
3509 *
3510 * supers is a list of the OIDs of the new relation's direct ancestors.
3511 */
3512static void
3514 bool child_is_partition)
3515{
3516 Relation relation;
3517 int32 seqNumber;
3518 ListCell *entry;
3519
3520 /*
3521 * sanity checks
3522 */
3523 Assert(OidIsValid(relationId));
3524
3525 if (supers == NIL)
3526 return;
3527
3528 /*
3529 * Store INHERITS information in pg_inherits using direct ancestors only.
3530 * Also enter dependencies on the direct ancestors, and make sure they are
3531 * marked with relhassubclass = true.
3532 *
3533 * (Once upon a time, both direct and indirect ancestors were found here
3534 * and then entered into pg_ipl. Since that catalog doesn't exist
3535 * anymore, there's no need to look for indirect ancestors.)
3536 */
3537 relation = table_open(InheritsRelationId, RowExclusiveLock);
3538
3539 seqNumber = 1;
3540 foreach(entry, supers)
3541 {
3542 Oid parentOid = lfirst_oid(entry);
3543
3544 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3545 child_is_partition);
3546 seqNumber++;
3547 }
3548
3549 table_close(relation, RowExclusiveLock);
3550}
3551
3552/*
3553 * Make catalog entries showing relationId as being an inheritance child
3554 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3555 */
3556static void
3557StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3558 int32 seqNumber, Relation inhRelation,
3559 bool child_is_partition)
3560{
3561 ObjectAddress childobject,
3562 parentobject;
3563
3564 /* store the pg_inherits row */
3565 StoreSingleInheritance(relationId, parentOid, seqNumber);
3566
3567 /*
3568 * Store a dependency too
3569 */
3570 parentobject.classId = RelationRelationId;
3571 parentobject.objectId = parentOid;
3572 parentobject.objectSubId = 0;
3573 childobject.classId = RelationRelationId;
3574 childobject.objectId = relationId;
3575 childobject.objectSubId = 0;
3576
3577 recordDependencyOn(&childobject, &parentobject,
3578 child_dependency_type(child_is_partition));
3579
3580 /*
3581 * Post creation hook of this inheritance. Since object_access_hook
3582 * doesn't take multiple object identifiers, we relay oid of parent
3583 * relation using auxiliary_id argument.
3584 */
3585 InvokeObjectPostAlterHookArg(InheritsRelationId,
3586 relationId, 0,
3587 parentOid, false);
3588
3589 /*
3590 * Mark the parent as having subclasses.
3591 */
3592 SetRelationHasSubclass(parentOid, true);
3593}
3594
3595/*
3596 * Look for an existing column entry with the given name.
3597 *
3598 * Returns the index (starting with 1) if attribute already exists in columns,
3599 * 0 if it doesn't.
3600 */
3601static int
3602findAttrByName(const char *attributeName, const List *columns)
3603{
3604 ListCell *lc;
3605 int i = 1;
3606
3607 foreach(lc, columns)
3608 {
3609 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3610 return i;
3611
3612 i++;
3613 }
3614 return 0;
3615}
3616
3617
3618/*
3619 * SetRelationHasSubclass
3620 * Set the value of the relation's relhassubclass field in pg_class.
3621 *
3622 * It's always safe to set this field to true, because all SQL commands are
3623 * ready to see true and then find no children. On the other hand, commands
3624 * generally assume zero children if this is false.
3625 *
3626 * Caller must hold any self-exclusive lock until end of transaction. If the
3627 * new value is false, caller must have acquired that lock before reading the
3628 * evidence that justified the false value. That way, it properly waits if
3629 * another backend is simultaneously concluding no need to change the tuple
3630 * (new and old values are true).
3631 *
3632 * NOTE: an important side-effect of this operation is that an SI invalidation
3633 * message is sent out to all backends --- including me --- causing plans
3634 * referencing the relation to be rebuilt with the new list of children.
3635 * This must happen even if we find that no change is needed in the pg_class
3636 * row.
3637 */
3638void
3639SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3640{
3641 Relation relationRelation;
3642 HeapTuple tuple;
3643 Form_pg_class classtuple;
3644
3646 ShareUpdateExclusiveLock, false) ||
3647 CheckRelationOidLockedByMe(relationId,
3648 ShareRowExclusiveLock, true));
3649
3650 /*
3651 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3652 */
3653 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3654 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3655 if (!HeapTupleIsValid(tuple))
3656 elog(ERROR, "cache lookup failed for relation %u", relationId);
3657 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3658
3659 if (classtuple->relhassubclass != relhassubclass)
3660 {
3661 classtuple->relhassubclass = relhassubclass;
3662 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3663 }
3664 else
3665 {
3666 /* no need to change tuple, but force relcache rebuild anyway */
3668 }
3669
3670 heap_freetuple(tuple);
3671 table_close(relationRelation, RowExclusiveLock);
3672}
3673
3674/*
3675 * CheckRelationTableSpaceMove
3676 * Check if relation can be moved to new tablespace.
3677 *
3678 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3679 *
3680 * Returns true if the relation can be moved to the new tablespace; raises
3681 * an error if it is not possible to do the move; returns false if the move
3682 * would have no effect.
3683 */
3684bool
3686{
3687 Oid oldTableSpaceId;
3688
3689 /*
3690 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3691 * stored as 0.
3692 */
3693 oldTableSpaceId = rel->rd_rel->reltablespace;
3694 if (newTableSpaceId == oldTableSpaceId ||
3695 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3696 return false;
3697
3698 /*
3699 * We cannot support moving mapped relations into different tablespaces.
3700 * (In particular this eliminates all shared catalogs.)
3701 */
3702 if (RelationIsMapped(rel))
3703 ereport(ERROR,
3704 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3705 errmsg("cannot move system relation \"%s\"",
3707
3708 /* Cannot move a non-shared relation into pg_global */
3709 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3710 ereport(ERROR,
3711 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3712 errmsg("only shared relations can be placed in pg_global tablespace")));
3713
3714 /*
3715 * Do not allow moving temp tables of other backends ... their local
3716 * buffer manager is not going to cope.
3717 */
3718 if (RELATION_IS_OTHER_TEMP(rel))
3719 ereport(ERROR,
3720 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3721 errmsg("cannot move temporary tables of other sessions")));
3722
3723 return true;
3724}
3725
3726/*
3727 * SetRelationTableSpace
3728 * Set new reltablespace and relfilenumber in pg_class entry.
3729 *
3730 * newTableSpaceId is the new tablespace for the relation, and
3731 * newRelFilenumber its new filenumber. If newRelFilenumber is
3732 * InvalidRelFileNumber, this field is not updated.
3733 *
3734 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3735 *
3736 * The caller of this routine had better check if a relation can be
3737 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3738 * first, and is responsible for making the change visible with
3739 * CommandCounterIncrement().
3740 */
3741void
3743 Oid newTableSpaceId,
3744 RelFileNumber newRelFilenumber)
3745{
3746 Relation pg_class;
3747 HeapTuple tuple;
3748 ItemPointerData otid;
3749 Form_pg_class rd_rel;
3750 Oid reloid = RelationGetRelid(rel);
3751
3752 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3753
3754 /* Get a modifiable copy of the relation's pg_class row. */
3755 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3756
3757 tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3758 if (!HeapTupleIsValid(tuple))
3759 elog(ERROR, "cache lookup failed for relation %u", reloid);
3760 otid = tuple->t_self;
3761 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3762
3763 /* Update the pg_class row. */
3764 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3765 InvalidOid : newTableSpaceId;
3766 if (RelFileNumberIsValid(newRelFilenumber))
3767 rd_rel->relfilenode = newRelFilenumber;
3768 CatalogTupleUpdate(pg_class, &otid, tuple);
3769 UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3770
3771 /*
3772 * Record dependency on tablespace. This is only required for relations
3773 * that have no physical storage.
3774 */
3775 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3776 changeDependencyOnTablespace(RelationRelationId, reloid,
3777 rd_rel->reltablespace);
3778
3779 heap_freetuple(tuple);
3780 table_close(pg_class, RowExclusiveLock);
3781}
3782
3783/*
3784 * renameatt_check - basic sanity checks before attribute rename
3785 */
3786static void
3787renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3788{
3789 char relkind = classform->relkind;
3790
3791 if (classform->reloftype && !recursing)
3792 ereport(ERROR,
3793 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3794 errmsg("cannot rename column of typed table")));
3795
3796 /*
3797 * Renaming the columns of sequences or toast tables doesn't actually
3798 * break anything from the system's point of view, since internal
3799 * references are by attnum. But it doesn't seem right to allow users to
3800 * change names that are hardcoded into the system, hence the following
3801 * restriction.
3802 */
3803 if (relkind != RELKIND_RELATION &&
3804 relkind != RELKIND_VIEW &&
3805 relkind != RELKIND_MATVIEW &&
3806 relkind != RELKIND_COMPOSITE_TYPE &&
3807 relkind != RELKIND_INDEX &&
3808 relkind != RELKIND_PARTITIONED_INDEX &&
3809 relkind != RELKIND_FOREIGN_TABLE &&
3810 relkind != RELKIND_PARTITIONED_TABLE)
3811 ereport(ERROR,
3812 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3813 errmsg("cannot rename columns of relation \"%s\"",
3814 NameStr(classform->relname)),
3816
3817 /*
3818 * permissions checking. only the owner of a class can change its schema.
3819 */
3820 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3822 NameStr(classform->relname));
3823 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3824 ereport(ERROR,
3825 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3826 errmsg("permission denied: \"%s\" is a system catalog",
3827 NameStr(classform->relname))));
3828}
3829
3830/*
3831 * renameatt_internal - workhorse for renameatt
3832 *
3833 * Return value is the attribute number in the 'myrelid' relation.
3834 */
3835static AttrNumber
3837 const char *oldattname,
3838 const char *newattname,
3839 bool recurse,
3840 bool recursing,
3841 int expected_parents,
3842 DropBehavior behavior)
3843{
3844 Relation targetrelation;
3845 Relation attrelation;
3846 HeapTuple atttup;
3847 Form_pg_attribute attform;
3849
3850 /*
3851 * Grab an exclusive lock on the target table, which we will NOT release
3852 * until end of transaction.
3853 */
3854 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3855 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3856
3857 /*
3858 * if the 'recurse' flag is set then we are supposed to rename this
3859 * attribute in all classes that inherit from 'relname' (as well as in
3860 * 'relname').
3861 *
3862 * any permissions or problems with duplicate attributes will cause the
3863 * whole transaction to abort, which is what we want -- all or nothing.
3864 */
3865 if (recurse)
3866 {
3867 List *child_oids,
3868 *child_numparents;
3869 ListCell *lo,
3870 *li;
3871
3872 /*
3873 * we need the number of parents for each child so that the recursive
3874 * calls to renameatt() can determine whether there are any parents
3875 * outside the inheritance hierarchy being processed.
3876 */
3877 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3878 &child_numparents);
3879
3880 /*
3881 * find_all_inheritors does the recursive search of the inheritance
3882 * hierarchy, so all we have to do is process all of the relids in the
3883 * list that it returns.
3884 */
3885 forboth(lo, child_oids, li, child_numparents)
3886 {
3887 Oid childrelid = lfirst_oid(lo);
3888 int numparents = lfirst_int(li);
3889
3890 if (childrelid == myrelid)
3891 continue;
3892 /* note we need not recurse again */
3893 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3894 }
3895 }
3896 else
3897 {
3898 /*
3899 * If we are told not to recurse, there had better not be any child
3900 * tables; else the rename would put them out of step.
3901 *
3902 * expected_parents will only be 0 if we are not already recursing.
3903 */
3904 if (expected_parents == 0 &&
3906 ereport(ERROR,
3907 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3908 errmsg("inherited column \"%s\" must be renamed in child tables too",
3909 oldattname)));
3910 }
3911
3912 /* rename attributes in typed tables of composite type */
3913 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3914 {
3915 List *child_oids;
3916 ListCell *lo;
3917
3918 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3919 RelationGetRelationName(targetrelation),
3920 behavior);
3921
3922 foreach(lo, child_oids)
3923 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3924 }
3925
3926 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3927
3928 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3929 if (!HeapTupleIsValid(atttup))
3930 ereport(ERROR,
3931 (errcode(ERRCODE_UNDEFINED_COLUMN),
3932 errmsg("column \"%s\" does not exist",
3933 oldattname)));
3934 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3935
3936 attnum = attform->attnum;
3937 if (attnum <= 0)
3938 ereport(ERROR,
3939 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3940 errmsg("cannot rename system column \"%s\"",
3941 oldattname)));
3942
3943 /*
3944 * if the attribute is inherited, forbid the renaming. if this is a
3945 * top-level call to renameatt(), then expected_parents will be 0, so the
3946 * effect of this code will be to prohibit the renaming if the attribute
3947 * is inherited at all. if this is a recursive call to renameatt(),
3948 * expected_parents will be the number of parents the current relation has
3949 * within the inheritance hierarchy being processed, so we'll prohibit the
3950 * renaming only if there are additional parents from elsewhere.
3951 */
3952 if (attform->attinhcount > expected_parents)
3953 ereport(ERROR,
3954 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3955 errmsg("cannot rename inherited column \"%s\"",
3956 oldattname)));
3957
3958 /* new name should not already exist */
3959 (void) check_for_column_name_collision(targetrelation, newattname, false);
3960
3961 /* apply the update */
3962 namestrcpy(&(attform->attname), newattname);
3963
3964 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3965
3966 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3967
3968 heap_freetuple(atttup);
3969
3970 table_close(attrelation, RowExclusiveLock);
3971
3972 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3973
3974 return attnum;
3975}
3976
3977/*
3978 * Perform permissions and integrity checks before acquiring a relation lock.
3979 */
3980static void
3982 void *arg)
3983{
3984 HeapTuple tuple;
3985 Form_pg_class form;
3986
3987 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3988 if (!HeapTupleIsValid(tuple))
3989 return; /* concurrently dropped */
3990 form = (Form_pg_class) GETSTRUCT(tuple);
3991 renameatt_check(relid, form, false);
3992 ReleaseSysCache(tuple);
3993}
3994
3995/*
3996 * renameatt - changes the name of an attribute in a relation
3997 *
3998 * The returned ObjectAddress is that of the renamed column.
3999 */
4002{
4003 Oid relid;
4005 ObjectAddress address;
4006
4007 /* lock level taken here should match renameatt_internal */
4009 stmt->missing_ok ? RVR_MISSING_OK : 0,
4011 NULL);
4012
4013 if (!OidIsValid(relid))
4014 {
4016 (errmsg("relation \"%s\" does not exist, skipping",
4017 stmt->relation->relname)));
4018 return InvalidObjectAddress;
4019 }
4020
4021 attnum =
4022 renameatt_internal(relid,
4023 stmt->subname, /* old att name */
4024 stmt->newname, /* new att name */
4025 stmt->relation->inh, /* recursive? */
4026 false, /* recursing? */
4027 0, /* expected inhcount */
4028 stmt->behavior);
4029
4030 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4031
4032 return address;
4033}
4034
4035/*
4036 * same logic as renameatt_internal
4037 */
4038static ObjectAddress
4040 Oid mytypid,
4041 const char *oldconname,
4042 const char *newconname,
4043 bool recurse,
4044 bool recursing,
4045 int expected_parents)
4046{
4047 Relation targetrelation = NULL;
4048 Oid constraintOid;
4049 HeapTuple tuple;
4051 ObjectAddress address;
4052
4053 Assert(!myrelid || !mytypid);
4054
4055 if (mytypid)
4056 {
4057 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4058 }
4059 else
4060 {
4061 targetrelation = relation_open(myrelid, AccessExclusiveLock);
4062
4063 /*
4064 * don't tell it whether we're recursing; we allow changing typed
4065 * tables here
4066 */
4067 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4068
4069 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4070 }
4071
4072 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4073 if (!HeapTupleIsValid(tuple))
4074 elog(ERROR, "cache lookup failed for constraint %u",
4075 constraintOid);
4076 con = (Form_pg_constraint) GETSTRUCT(tuple);
4077
4078 if (myrelid &&
4079 (con->contype == CONSTRAINT_CHECK ||
4080 con->contype == CONSTRAINT_NOTNULL) &&
4081 !con->connoinherit)
4082 {
4083 if (recurse)
4084 {
4085 List *child_oids,
4086 *child_numparents;
4087 ListCell *lo,
4088 *li;
4089
4090 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4091 &child_numparents);
4092
4093 forboth(lo, child_oids, li, child_numparents)
4094 {
4095 Oid childrelid = lfirst_oid(lo);
4096 int numparents = lfirst_int(li);
4097
4098 if (childrelid == myrelid)
4099 continue;
4100
4101 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4102 }
4103 }
4104 else
4105 {
4106 if (expected_parents == 0 &&
4108 ereport(ERROR,
4109 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4110 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4111 oldconname)));
4112 }
4113
4114 if (con->coninhcount > expected_parents)
4115 ereport(ERROR,
4116 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4117 errmsg("cannot rename inherited constraint \"%s\"",
4118 oldconname)));
4119 }
4120
4121 if (con->conindid
4122 && (con->contype == CONSTRAINT_PRIMARY
4123 || con->contype == CONSTRAINT_UNIQUE
4124 || con->contype == CONSTRAINT_EXCLUSION))
4125 /* rename the index; this renames the constraint as well */
4126 RenameRelationInternal(con->conindid, newconname, false, true);
4127 else
4128 RenameConstraintById(constraintOid, newconname);
4129
4130 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4131
4132 ReleaseSysCache(tuple);
4133
4134 if (targetrelation)
4135 {
4136 /*
4137 * Invalidate relcache so as others can see the new constraint name.
4138 */
4139 CacheInvalidateRelcache(targetrelation);
4140
4141 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4142 }
4143
4144 return address;
4145}
4146
4149{
4150 Oid relid = InvalidOid;
4151 Oid typid = InvalidOid;
4152
4153 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4154 {
4155 Relation rel;
4156 HeapTuple tup;
4157
4158 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4159 rel = table_open(TypeRelationId, RowExclusiveLock);
4160 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4161 if (!HeapTupleIsValid(tup))
4162 elog(ERROR, "cache lookup failed for type %u", typid);
4163 checkDomainOwner(tup);
4164 ReleaseSysCache(tup);
4165 table_close(rel, NoLock);
4166 }
4167 else
4168 {
4169 /* lock level taken here should match rename_constraint_internal */
4171 stmt->missing_ok ? RVR_MISSING_OK : 0,
4173 NULL);
4174 if (!OidIsValid(relid))
4175 {
4177 (errmsg("relation \"%s\" does not exist, skipping",
4178 stmt->relation->relname)));
4179 return InvalidObjectAddress;
4180 }
4181 }
4182
4183 return
4184 rename_constraint_internal(relid, typid,
4185 stmt->subname,
4186 stmt->newname,
4187 (stmt->relation &&
4188 stmt->relation->inh), /* recursive? */
4189 false, /* recursing? */
4190 0 /* expected inhcount */ );
4191}
4192
4193/*
4194 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4195 * RENAME
4196 */
4199{
4200 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4201 Oid relid;
4202 ObjectAddress address;
4203
4204 /*
4205 * Grab an exclusive lock on the target table, index, sequence, view,
4206 * materialized view, or foreign table, which we will NOT release until
4207 * end of transaction.
4208 *
4209 * Lock level used here should match RenameRelationInternal, to avoid lock
4210 * escalation. However, because ALTER INDEX can be used with any relation
4211 * type, we mustn't believe without verification.
4212 */
4213 for (;;)
4214 {
4215 LOCKMODE lockmode;
4216 char relkind;
4217 bool obj_is_index;
4218
4219 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4220
4221 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4222 stmt->missing_ok ? RVR_MISSING_OK : 0,
4224 stmt);
4225
4226 if (!OidIsValid(relid))
4227 {
4229 (errmsg("relation \"%s\" does not exist, skipping",
4230 stmt->relation->relname)));
4231 return InvalidObjectAddress;
4232 }
4233
4234 /*
4235 * We allow mismatched statement and object types (e.g., ALTER INDEX
4236 * to rename a table), but we might've used the wrong lock level. If
4237 * that happens, retry with the correct lock level. We don't bother
4238 * if we already acquired AccessExclusiveLock with an index, however.
4239 */
4240 relkind = get_rel_relkind(relid);
4241 obj_is_index = (relkind == RELKIND_INDEX ||
4242 relkind == RELKIND_PARTITIONED_INDEX);
4243 if (obj_is_index || is_index_stmt == obj_is_index)
4244 break;
4245
4246 UnlockRelationOid(relid, lockmode);
4247 is_index_stmt = obj_is_index;
4248 }
4249
4250 /* Do the work */
4251 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4252
4253 ObjectAddressSet(address, RelationRelationId, relid);
4254
4255 return address;
4256}
4257
4258/*
4259 * RenameRelationInternal - change the name of a relation
4260 */
4261void
4262RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4263{
4264 Relation targetrelation;
4265 Relation relrelation; /* for RELATION relation */
4266 ItemPointerData otid;
4267 HeapTuple reltup;
4268 Form_pg_class relform;
4269 Oid namespaceId;
4270
4271 /*
4272 * Grab a lock on the target relation, which we will NOT release until end
4273 * of transaction. We need at least a self-exclusive lock so that
4274 * concurrent DDL doesn't overwrite the rename if they start updating
4275 * while still seeing the old version. The lock also guards against
4276 * triggering relcache reloads in concurrent sessions, which might not
4277 * handle this information changing under them. For indexes, we can use a
4278 * reduced lock level because RelationReloadIndexInfo() handles indexes
4279 * specially.
4280 */
4281 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4282 namespaceId = RelationGetNamespace(targetrelation);
4283
4284 /*
4285 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4286 */
4287 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4288
4289 reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4290 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4291 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4292 otid = reltup->t_self;
4293 relform = (Form_pg_class) GETSTRUCT(reltup);
4294
4295 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4296 ereport(ERROR,
4297 (errcode(ERRCODE_DUPLICATE_TABLE),
4298 errmsg("relation \"%s\" already exists",
4299 newrelname)));
4300
4301 /*
4302 * RenameRelation is careful not to believe the caller's idea of the
4303 * relation kind being handled. We don't have to worry about this, but
4304 * let's not be totally oblivious to it. We can process an index as
4305 * not-an-index, but not the other way around.
4306 */
4307 Assert(!is_index ||
4308 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4309 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4310
4311 /*
4312 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4313 * because it's a copy...)
4314 */
4315 namestrcpy(&(relform->relname), newrelname);
4316
4317 CatalogTupleUpdate(relrelation, &otid, reltup);
4318 UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4319
4320 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4321 InvalidOid, is_internal);
4322
4323 heap_freetuple(reltup);
4324 table_close(relrelation, RowExclusiveLock);
4325
4326 /*
4327 * Also rename the associated type, if any.
4328 */
4329 if (OidIsValid(targetrelation->rd_rel->reltype))
4330 RenameTypeInternal(targetrelation->rd_rel->reltype,
4331 newrelname, namespaceId);
4332
4333 /*
4334 * Also rename the associated constraint, if any.
4335 */
4336 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4337 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4338 {
4339 Oid constraintId = get_index_constraint(myrelid);
4340
4341 if (OidIsValid(constraintId))
4342 RenameConstraintById(constraintId, newrelname);
4343 }
4344
4345 /*
4346 * Close rel, but keep lock!
4347 */
4348 relation_close(targetrelation, NoLock);
4349}
4350
4351/*
4352 * ResetRelRewrite - reset relrewrite
4353 */
4354void
4356{
4357 Relation relrelation; /* for RELATION relation */
4358 HeapTuple reltup;
4359 Form_pg_class relform;
4360
4361 /*
4362 * Find relation's pg_class tuple.
4363 */
4364 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4365
4366 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4367 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4368 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4369 relform = (Form_pg_class) GETSTRUCT(reltup);
4370
4371 /*
4372 * Update pg_class tuple.
4373 */
4374 relform->relrewrite = InvalidOid;
4375
4376 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4377
4378 heap_freetuple(reltup);
4379 table_close(relrelation, RowExclusiveLock);
4380}
4381
4382/*
4383 * Disallow ALTER TABLE (and similar commands) when the current backend has
4384 * any open reference to the target table besides the one just acquired by
4385 * the calling command; this implies there's an open cursor or active plan.
4386 * We need this check because our lock doesn't protect us against stomping
4387 * on our own foot, only other people's feet!
4388 *
4389 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4390 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4391 * possibly be relaxed to only error out for certain types of alterations.
4392 * But the use-case for allowing any of these things is not obvious, so we
4393 * won't work hard at it for now.
4394 *
4395 * We also reject these commands if there are any pending AFTER trigger events
4396 * for the rel. This is certainly necessary for the rewriting variants of
4397 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4398 * events would try to fetch the wrong tuples. It might be overly cautious
4399 * in other cases, but again it seems better to err on the side of paranoia.
4400 *
4401 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4402 * we are worried about active indexscans on the index. The trigger-event
4403 * check can be skipped, since we are doing no damage to the parent table.
4404 *
4405 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4406 */
4407void
4409{
4410 int expected_refcnt;
4411
4412 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4413 if (rel->rd_refcnt != expected_refcnt)
4414 ereport(ERROR,
4415 (errcode(ERRCODE_OBJECT_IN_USE),
4416 /* translator: first %s is a SQL command, eg ALTER TABLE */
4417 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4419
4420 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4421 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4423 ereport(ERROR,
4424 (errcode(ERRCODE_OBJECT_IN_USE),
4425 /* translator: first %s is a SQL command, eg ALTER TABLE */
4426 errmsg("cannot %s \"%s\" because it has pending trigger events",
4428}
4429
4430/*
4431 * CheckAlterTableIsSafe
4432 * Verify that it's safe to allow ALTER TABLE on this relation.
4433 *
4434 * This consists of CheckTableNotInUse() plus a check that the relation
4435 * isn't another session's temp table. We must split out the temp-table
4436 * check because there are callers of CheckTableNotInUse() that don't want
4437 * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4438 * an orphaned temp schema.) Compare truncate_check_activity().
4439 */
4440static void
4442{
4443 /*
4444 * Don't allow ALTER on temp tables of other backends. Their local buffer
4445 * manager is not going to cope if we need to change the table's contents.
4446 * Even if we don't, there may be optimizations that assume temp tables
4447 * aren't subject to such interference.
4448 */
4449 if (RELATION_IS_OTHER_TEMP(rel))
4450 ereport(ERROR,
4451 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4452 errmsg("cannot alter temporary tables of other sessions")));
4453
4454 /*
4455 * Also check for active uses of the relation in the current transaction,
4456 * including open scans and pending AFTER trigger events.
4457 */
4458 CheckTableNotInUse(rel, "ALTER TABLE");
4459}
4460
4461/*
4462 * AlterTableLookupRelation
4463 * Look up, and lock, the OID for the relation named by an alter table
4464 * statement.
4465 */
4466Oid
4468{
4469 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4470 stmt->missing_ok ? RVR_MISSING_OK : 0,
4472 stmt);
4473}
4474
4475/*
4476 * AlterTable
4477 * Execute ALTER TABLE, which can be a list of subcommands
4478 *
4479 * ALTER TABLE is performed in three phases:
4480 * 1. Examine subcommands and perform pre-transformation checking.
4481 * 2. Validate and transform subcommands, and update system catalogs.
4482 * 3. Scan table(s) to check new constraints, and optionally recopy
4483 * the data into new table(s).
4484 * Phase 3 is not performed unless one or more of the subcommands requires
4485 * it. The intention of this design is to allow multiple independent
4486 * updates of the table schema to be performed with only one pass over the
4487 * data.
4488 *
4489 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4490 * each table to be affected (there may be multiple affected tables if the
4491 * commands traverse a table inheritance hierarchy). Also we do preliminary
4492 * validation of the subcommands. Because earlier subcommands may change
4493 * the catalog state seen by later commands, there are limits to what can
4494 * be done in this phase. Generally, this phase acquires table locks,
4495 * checks permissions and relkind, and recurses to find child tables.
4496 *
4497 * ATRewriteCatalogs performs phase 2 for each affected table.
4498 * Certain subcommands need to be performed before others to avoid
4499 * unnecessary conflicts; for example, DROP COLUMN should come before
4500 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4501 * lists, one for each logical "pass" of phase 2.
4502 *
4503 * ATRewriteTables performs phase 3 for those tables that need it.
4504 *
4505 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4506 * since phase 1 already does it. However, for certain subcommand types
4507 * it is only possible to determine how to recurse at phase 2 time; for
4508 * those cases, phase 1 sets the cmd->recurse flag.
4509 *
4510 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4511 * the whole operation; we don't have to do anything special to clean up.
4512 *
4513 * The caller must lock the relation, with an appropriate lock level
4514 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4515 * or higher. We pass the lock level down
4516 * so that we can apply it recursively to inherited tables. Note that the
4517 * lock level we want as we recurse might well be higher than required for
4518 * that specific subcommand. So we pass down the overall lock requirement,
4519 * rather than reassess it at lower levels.
4520 *
4521 * The caller also provides a "context" which is to be passed back to
4522 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4523 * Some of the fields therein, such as the relid, are used here as well.
4524 */
4525void
4527 AlterTableUtilityContext *context)
4528{
4529 Relation rel;
4530
4531 /* Caller is required to provide an adequate lock. */
4532 rel = relation_open(context->relid, NoLock);
4533
4535
4536 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4537}
4538
4539/*
4540 * AlterTableInternal
4541 *
4542 * ALTER TABLE with target specified by OID
4543 *
4544 * We do not reject if the relation is already open, because it's quite
4545 * likely that one or more layers of caller have it open. That means it
4546 * is unsafe to use this entry point for alterations that could break
4547 * existing query plans. On the assumption it's not used for such, we
4548 * don't have to reject pending AFTER triggers, either.
4549 *
4550 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4551 * used for any subcommand types that require parse transformation or
4552 * could generate subcommands that have to be passed to ProcessUtility.
4553 */
4554void
4555AlterTableInternal(Oid relid, List *cmds, bool recurse)
4556{
4557 Relation rel;
4558 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4559
4560 rel = relation_open(relid, lockmode);
4561
4563
4564 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4565}
4566
4567/*
4568 * AlterTableGetLockLevel
4569 *
4570 * Sets the overall lock level required for the supplied list of subcommands.
4571 * Policy for doing this set according to needs of AlterTable(), see
4572 * comments there for overall explanation.
4573 *
4574 * Function is called before and after parsing, so it must give same
4575 * answer each time it is called. Some subcommands are transformed
4576 * into other subcommand types, so the transform must never be made to a
4577 * lower lock level than previously assigned. All transforms are noted below.
4578 *
4579 * Since this is called before we lock the table we cannot use table metadata
4580 * to influence the type of lock we acquire.
4581 *
4582 * There should be no lockmodes hardcoded into the subcommand functions. All
4583 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4584 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4585 * and does not travel through this section of code and cannot be combined with
4586 * any of the subcommands given here.
4587 *
4588 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4589 * so any changes that might affect SELECTs running on standbys need to use
4590 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4591 * have a solution for that also.
4592 *
4593 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4594 * that takes a lock less than AccessExclusiveLock can change object definitions
4595 * while pg_dump is running. Be careful to check that the appropriate data is
4596 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4597 * otherwise we might end up with an inconsistent dump that can't restore.
4598 */
4601{
4602 /*
4603 * This only works if we read catalog tables using MVCC snapshots.
4604 */
4605 ListCell *lcmd;
4607
4608 foreach(lcmd, cmds)
4609 {
4610 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4611 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4612
4613 switch (cmd->subtype)
4614 {
4615 /*
4616 * These subcommands rewrite the heap, so require full locks.
4617 */
4618 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4619 * to SELECT */
4620 case AT_SetAccessMethod: /* must rewrite heap */
4621 case AT_SetTableSpace: /* must rewrite heap */
4622 case AT_AlterColumnType: /* must rewrite heap */
4623 cmd_lockmode = AccessExclusiveLock;
4624 break;
4625
4626 /*
4627 * These subcommands may require addition of toast tables. If
4628 * we add a toast table to a table currently being scanned, we
4629 * might miss data added to the new toast table by concurrent
4630 * insert transactions.
4631 */
4632 case AT_SetStorage: /* may add toast tables, see
4633 * ATRewriteCatalogs() */
4634 cmd_lockmode = AccessExclusiveLock;
4635 break;
4636
4637 /*
4638 * Removing constraints can affect SELECTs that have been
4639 * optimized assuming the constraint holds true. See also
4640 * CloneFkReferenced.
4641 */
4642 case AT_DropConstraint: /* as DROP INDEX */
4643 case AT_DropNotNull: /* may change some SQL plans */
4644 cmd_lockmode = AccessExclusiveLock;
4645 break;
4646
4647 /*
4648 * Subcommands that may be visible to concurrent SELECTs
4649 */
4650 case AT_DropColumn: /* change visible to SELECT */
4651 case AT_AddColumnToView: /* CREATE VIEW */
4652 case AT_DropOids: /* used to equiv to DropColumn */
4653 case AT_EnableAlwaysRule: /* may change SELECT rules */
4654 case AT_EnableReplicaRule: /* may change SELECT rules */
4655 case AT_EnableRule: /* may change SELECT rules */
4656 case AT_DisableRule: /* may change SELECT rules */
4657 cmd_lockmode = AccessExclusiveLock;
4658 break;
4659
4660 /*
4661 * Changing owner may remove implicit SELECT privileges
4662 */
4663 case AT_ChangeOwner: /* change visible to SELECT */
4664 cmd_lockmode = AccessExclusiveLock;
4665 break;
4666
4667 /*
4668 * Changing foreign table options may affect optimization.
4669 */
4670 case AT_GenericOptions:
4672 cmd_lockmode = AccessExclusiveLock;
4673 break;
4674
4675 /*
4676 * These subcommands affect write operations only.
4677 */
4678 case AT_EnableTrig:
4681 case AT_EnableTrigAll:
4682 case AT_EnableTrigUser:
4683 case AT_DisableTrig:
4684 case AT_DisableTrigAll:
4685 case AT_DisableTrigUser:
4686 cmd_lockmode = ShareRowExclusiveLock;
4687 break;
4688
4689 /*
4690 * These subcommands affect write operations only. XXX
4691 * Theoretically, these could be ShareRowExclusiveLock.
4692 */
4693 case AT_ColumnDefault:
4695 case AT_AlterConstraint:
4696 case AT_AddIndex: /* from ADD CONSTRAINT */
4698 case AT_ReplicaIdentity:
4699 case AT_SetNotNull:
4704 case AT_AddIdentity:
4705 case AT_DropIdentity:
4706 case AT_SetIdentity:
4707 case AT_SetExpression:
4708 case AT_DropExpression:
4709 case AT_SetCompression:
4710 cmd_lockmode = AccessExclusiveLock;
4711 break;
4712
4713 case AT_AddConstraint:
4714 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4715 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4716 if (IsA(cmd->def, Constraint))
4717 {
4718 Constraint *con = (Constraint *) cmd->def;
4719
4720 switch (con->contype)
4721 {
4722 case CONSTR_EXCLUSION:
4723 case CONSTR_PRIMARY:
4724 case CONSTR_UNIQUE:
4725
4726 /*
4727 * Cases essentially the same as CREATE INDEX. We
4728 * could reduce the lock strength to ShareLock if
4729 * we can work out how to allow concurrent catalog
4730 * updates. XXX Might be set down to
4731 * ShareRowExclusiveLock but requires further
4732 * analysis.
4733 */
4734 cmd_lockmode = AccessExclusiveLock;
4735 break;
4736 case CONSTR_FOREIGN:
4737
4738 /*
4739 * We add triggers to both tables when we add a
4740 * Foreign Key, so the lock level must be at least
4741 * as strong as CREATE TRIGGER.
4742 */
4743 cmd_lockmode = ShareRowExclusiveLock;
4744 break;
4745
4746 default:
4747 cmd_lockmode = AccessExclusiveLock;
4748 }
4749 }
4750 break;
4751
4752 /*
4753 * These subcommands affect inheritance behaviour. Queries
4754 * started before us will continue to see the old inheritance
4755 * behaviour, while queries started after we commit will see
4756 * new behaviour. No need to prevent reads or writes to the
4757 * subtable while we hook it up though. Changing the TupDesc
4758 * may be a problem, so keep highest lock.
4759 */
4760 case AT_AddInherit:
4761 case AT_DropInherit:
4762 cmd_lockmode = AccessExclusiveLock;
4763 break;
4764
4765 /*
4766 * These subcommands affect implicit row type conversion. They
4767 * have affects similar to CREATE/DROP CAST on queries. don't
4768 * provide for invalidating parse trees as a result of such
4769 * changes, so we keep these at AccessExclusiveLock.
4770 */
4771 case AT_AddOf:
4772 case AT_DropOf:
4773 cmd_lockmode = AccessExclusiveLock;
4774 break;
4775
4776 /*
4777 * Only used by CREATE OR REPLACE VIEW which must conflict
4778 * with an SELECTs currently using the view.
4779 */
4781 cmd_lockmode = AccessExclusiveLock;
4782 break;
4783
4784 /*
4785 * These subcommands affect general strategies for performance
4786 * and maintenance, though don't change the semantic results
4787 * from normal data reads and writes. Delaying an ALTER TABLE
4788 * behind currently active writes only delays the point where
4789 * the new strategy begins to take effect, so there is no
4790 * benefit in waiting. In this case the minimum restriction
4791 * applies: we don't currently allow concurrent catalog
4792 * updates.
4793 */
4794 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4795 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4796 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4797 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4798 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4799 cmd_lockmode = ShareUpdateExclusiveLock;
4800 break;
4801
4802 case AT_SetLogged:
4803 case AT_SetUnLogged:
4804 cmd_lockmode = AccessExclusiveLock;
4805 break;
4806
4807 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4808 cmd_lockmode = ShareUpdateExclusiveLock;
4809 break;
4810
4811 /*
4812 * Rel options are more complex than first appears. Options
4813 * are set here for tables, views and indexes; for historical
4814 * reasons these can all be used with ALTER TABLE, so we can't
4815 * decide between them using the basic grammar.
4816 */
4817 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4818 * getTables() */
4819 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4820 * getTables() */
4821 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4822 break;
4823
4824 case AT_AttachPartition:
4825 cmd_lockmode = ShareUpdateExclusiveLock;
4826 break;
4827
4828 case AT_DetachPartition:
4829 if (((PartitionCmd *) cmd->def)->concurrent)
4830 cmd_lockmode = ShareUpdateExclusiveLock;
4831 else
4832 cmd_lockmode = AccessExclusiveLock;
4833 break;
4834
4836 cmd_lockmode = ShareUpdateExclusiveLock;
4837 break;
4838
4839 default: /* oops */
4840 elog(ERROR, "unrecognized alter table type: %d",
4841 (int) cmd->subtype);
4842 break;
4843 }
4844
4845 /*
4846 * Take the greatest lockmode from any subcommand
4847 */
4848 if (cmd_lockmode > lockmode)
4849 lockmode = cmd_lockmode;
4850 }
4851
4852 return lockmode;
4853}
4854
4855/*
4856 * ATController provides top level control over the phases.
4857 *
4858 * parsetree is passed in to allow it to be passed to event triggers
4859 * when requested.
4860 */
4861static void
4863 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4864 AlterTableUtilityContext *context)
4865{
4866 List *wqueue = NIL;
4867 ListCell *lcmd;
4868
4869 /* Phase 1: preliminary examination of commands, create work queue */
4870 foreach(lcmd, cmds)
4871 {
4872 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4873
4874 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4875 }
4876
4877 /* Close the relation, but keep lock until commit */
4878 relation_close(rel, NoLock);
4879
4880 /* Phase 2: update system catalogs */
4881 ATRewriteCatalogs(&wqueue, lockmode, context);
4882
4883 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4884 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4885}
4886
4887/*
4888 * ATPrepCmd
4889 *
4890 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4891 * recursion and permission checks.
4892 *
4893 * Caller must have acquired appropriate lock type on relation already.
4894 * This lock should be held until commit.
4895 */
4896static void
4898 bool recurse, bool recursing, LOCKMODE lockmode,
4899 AlterTableUtilityContext *context)
4900{
4901 AlteredTableInfo *tab;
4903
4904 /* Find or create work queue entry for this table */
4905 tab = ATGetQueueEntry(wqueue, rel);
4906
4907 /*
4908 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4909 * partitions that are pending detach.
4910 */
4911 if (rel->rd_rel->relispartition &&
4914 ereport(ERROR,
4915 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4916 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4918 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4919
4920 /*
4921 * Copy the original subcommand for each table, so we can scribble on it.
4922 * This avoids conflicts when different child tables need to make
4923 * different parse transformations (for example, the same column may have
4924 * different column numbers in different children).
4925 */
4926 cmd = copyObject(cmd);
4927
4928 /*
4929 * Do permissions and relkind checking, recursion to child tables if
4930 * needed, and any additional phase-1 processing needed. (But beware of
4931 * adding any processing that looks at table details that another
4932 * subcommand could change. In some cases we reject multiple subcommands
4933 * that could try to change the same state in contrary ways.)
4934 */
4935 switch (cmd->subtype)
4936 {
4937 case AT_AddColumn: /* ADD COLUMN */
4938 ATSimplePermissions(cmd->subtype, rel,
4941 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4942 lockmode, context);
4943 /* Recursion occurs during execution phase */
4944 pass = AT_PASS_ADD_COL;
4945 break;
4946 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4948 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4949 lockmode, context);
4950 /* Recursion occurs during execution phase */
4951 pass = AT_PASS_ADD_COL;
4952 break;
4953 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4954
4955 /*
4956 * We allow defaults on views so that INSERT into a view can have
4957 * default-ish behavior. This works because the rewriter
4958 * substitutes default values into INSERTs before it expands
4959 * rules.
4960 */
4961 ATSimplePermissions(cmd->subtype, rel,
4964 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4965 /* No command-specific prep needed */
4967 break;
4968 case AT_CookedColumnDefault: /* add a pre-cooked default */
4969 /* This is currently used only in CREATE TABLE */
4970 /* (so the permission check really isn't necessary) */
4971 ATSimplePermissions(cmd->subtype, rel,
4973 /* This command never recurses */
4975 break;
4976 case AT_AddIdentity:
4977 ATSimplePermissions(cmd->subtype, rel,
4980 /* Set up recursion for phase 2; no other prep needed */
4981 if (recurse)
4982 cmd->recurse = true;
4984 break;
4985 case AT_SetIdentity:
4986 ATSimplePermissions(cmd->subtype, rel,
4989 /* Set up recursion for phase 2; no other prep needed */
4990 if (recurse)
4991 cmd->recurse = true;
4992 /* This should run after AddIdentity, so do it in MISC pass */
4993 pass = AT_PASS_MISC;
4994 break;
4995 case AT_DropIdentity:
4996 ATSimplePermissions(cmd->subtype, rel,
4999 /* Set up recursion for phase 2; no other prep needed */
5000 if (recurse)
5001 cmd->recurse = true;
5002 pass = AT_PASS_DROP;
5003 break;
5004 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5005 ATSimplePermissions(cmd->subtype, rel,
5007 /* Set up recursion for phase 2; no other prep needed */
5008 if (recurse)
5009 cmd->recurse = true;
5010 pass = AT_PASS_DROP;
5011 break;
5012 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5013 ATSimplePermissions(cmd->subtype, rel,
5015 /* Set up recursion for phase 2; no other prep needed */
5016 if (recurse)
5017 cmd->recurse = true;
5018 pass = AT_PASS_COL_ATTRS;
5019 break;
5020 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5021 ATSimplePermissions(cmd->subtype, rel,
5023 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5025 break;
5026 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5027 ATSimplePermissions(cmd->subtype, rel,
5029 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5030 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5031 pass = AT_PASS_DROP;
5032 break;
5033 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5034 ATSimplePermissions(cmd->subtype, rel,
5037 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5038 /* No command-specific prep needed */
5039 pass = AT_PASS_MISC;
5040 break;
5041 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5042 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5043 ATSimplePermissions(cmd->subtype, rel,
5046 /* This command never recurses */
5047 pass = AT_PASS_MISC;
5048 break;
5049 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5050 ATSimplePermissions(cmd->subtype, rel,
5053 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5054 /* No command-specific prep needed */
5055 pass = AT_PASS_MISC;
5056 break;
5057 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5058 ATSimplePermissions(cmd->subtype, rel,
5060 /* This command never recurses */
5061 /* No command-specific prep needed */
5062 pass = AT_PASS_MISC;
5063 break;
5064 case AT_DropColumn: /* DROP COLUMN */
5065 ATSimplePermissions(cmd->subtype, rel,
5068 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5069 lockmode, context);
5070 /* Recursion occurs during execution phase */
5071 pass = AT_PASS_DROP;
5072 break;
5073 case AT_AddIndex: /* ADD INDEX */
5075 /* This command never recurses */
5076 /* No command-specific prep needed */
5077 pass = AT_PASS_ADD_INDEX;
5078 break;
5079 case AT_AddConstraint: /* ADD CONSTRAINT */
5080 ATSimplePermissions(cmd->subtype, rel,
5082 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5083 if (recurse)
5084 {
5085 /* recurses at exec time; lock descendants and set flag */
5086 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5087 cmd->recurse = true;
5088 }
5089 pass = AT_PASS_ADD_CONSTR;
5090 break;
5091 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5093 /* This command never recurses */
5094 /* No command-specific prep needed */
5096 break;
5097 case AT_DropConstraint: /* DROP CONSTRAINT */
5098 ATSimplePermissions(cmd->subtype, rel,
5100 ATCheckPartitionsNotInUse(rel, lockmode);
5101 /* Other recursion occurs during execution phase */
5102 /* No command-specific prep needed except saving recurse flag */
5103 if (recurse)
5104 cmd->recurse = true;
5105 pass = AT_PASS_DROP;
5106 break;
5107 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5108 ATSimplePermissions(cmd->subtype, rel,
5111 /* See comments for ATPrepAlterColumnType */
5112 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5113 AT_PASS_UNSET, context);
5114 Assert(cmd != NULL);
5115 /* Performs own recursion */
5116 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5117 lockmode, context);
5118 pass = AT_PASS_ALTER_TYPE;
5119 break;
5122 /* This command never recurses */
5123 /* No command-specific prep needed */
5124 pass = AT_PASS_MISC;
5125 break;
5126 case AT_ChangeOwner: /* ALTER OWNER */
5127 /* This command never recurses */
5128 /* No command-specific prep needed */
5129 pass = AT_PASS_MISC;
5130 break;
5131 case AT_ClusterOn: /* CLUSTER ON */
5132 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5133 ATSimplePermissions(cmd->subtype, rel,
5135 /* These commands never recurse */
5136 /* No command-specific prep needed */
5137 pass = AT_PASS_MISC;
5138 break;
5139 case AT_SetLogged: /* SET LOGGED */
5140 case AT_SetUnLogged: /* SET UNLOGGED */
5142 if (tab->chgPersistence)
5143 ereport(ERROR,
5144 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5145 errmsg("cannot change persistence setting twice")));
5146 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5147 pass = AT_PASS_MISC;
5148 break;
5149 case AT_DropOids: /* SET WITHOUT OIDS */
5150 ATSimplePermissions(cmd->subtype, rel,
5152 pass = AT_PASS_DROP;
5153 break;
5154 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5155 ATSimplePermissions(cmd->subtype, rel,
5157
5158 /* check if another access method change was already requested */
5159 if (tab->chgAccessMethod)
5160 ereport(ERROR,
5161 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5162 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5163
5164 ATPrepSetAccessMethod(tab, rel, cmd->name);
5165 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5166 break;
5167 case AT_SetTableSpace: /* SET TABLESPACE */
5170 /* This command never recurses */
5171 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5172 pass = AT_PASS_MISC; /* doesn't actually matter */
5173 break;
5174 case AT_SetRelOptions: /* SET (...) */
5175 case AT_ResetRelOptions: /* RESET (...) */
5176 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5177 ATSimplePermissions(cmd->subtype, rel,
5180 /* This command never recurses */
5181 /* No command-specific prep needed */
5182 pass = AT_PASS_MISC;
5183 break;
5184 case AT_AddInherit: /* INHERIT */
5185 ATSimplePermissions(cmd->subtype, rel,
5187 /* This command never recurses */
5188 ATPrepAddInherit(rel);
5189 pass = AT_PASS_MISC;
5190 break;
5191 case AT_DropInherit: /* NO INHERIT */
5192 ATSimplePermissions(cmd->subtype, rel,
5194 /* This command never recurses */
5195 /* No command-specific prep needed */
5196 pass = AT_PASS_MISC;
5197 break;
5198 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5199 ATSimplePermissions(cmd->subtype, rel,
5201 /* Recursion occurs during execution phase */
5202 if (recurse)
5203 cmd->recurse = true;
5204 pass = AT_PASS_MISC;
5205 break;
5206 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5207 ATSimplePermissions(cmd->subtype, rel,
5209 /* Recursion occurs during execution phase */
5210 /* No command-specific prep needed except saving recurse flag */
5211 if (recurse)
5212 cmd->recurse = true;
5213 pass = AT_PASS_MISC;
5214 break;
5215 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5216 ATSimplePermissions(cmd->subtype, rel,
5218 pass = AT_PASS_MISC;
5219 /* This command never recurses */
5220 /* No command-specific prep needed */
5221 break;
5222 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5225 case AT_EnableTrigAll:
5226 case AT_EnableTrigUser:
5227 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5228 case AT_DisableTrigAll:
5229 case AT_DisableTrigUser:
5230 ATSimplePermissions(cmd->subtype, rel,
5232 /* Set up recursion for phase 2; no other prep needed */
5233 if (recurse)
5234 cmd->recurse = true;
5235 pass = AT_PASS_MISC;
5236 break;
5237 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5240 case AT_DisableRule:
5241 case AT_AddOf: /* OF */
5242 case AT_DropOf: /* NOT OF */
5247 ATSimplePermissions(cmd->subtype, rel,
5249 /* These commands never recurse */
5250 /* No command-specific prep needed */
5251 pass = AT_PASS_MISC;
5252 break;
5253 case AT_GenericOptions:
5255 /* No command-specific prep needed */
5256 pass = AT_PASS_MISC;
5257 break;
5258 case AT_AttachPartition:
5259 ATSimplePermissions(cmd->subtype, rel,
5261 /* No command-specific prep needed */
5262 pass = AT_PASS_MISC;
5263 break;
5264 case AT_DetachPartition:
5266 /* No command-specific prep needed */
5267 pass = AT_PASS_MISC;
5268 break;
5271 /* No command-specific prep needed */
5272 pass = AT_PASS_MISC;
5273 break;
5274 default: /* oops */
5275 elog(ERROR, "unrecognized alter table type: %d",
5276 (int) cmd->subtype);
5277 pass = AT_PASS_UNSET; /* keep compiler quiet */
5278 break;
5279 }
5280 Assert(pass > AT_PASS_UNSET);
5281
5282 /* Add the subcommand to the appropriate list for phase 2 */
5283 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5284}
5285
5286/*
5287 * ATRewriteCatalogs
5288 *
5289 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5290 * dispatched in a "safe" execution order (designed to avoid unnecessary
5291 * conflicts).
5292 */
5293static void
5295 AlterTableUtilityContext *context)
5296{
5297 ListCell *ltab;
5298
5299 /*
5300 * We process all the tables "in parallel", one pass at a time. This is
5301 * needed because we may have to propagate work from one table to another
5302 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5303 * re-adding of the foreign key constraint to the other table). Work can
5304 * only be propagated into later passes, however.
5305 */
5306 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5307 {
5308 /* Go through each table that needs to be processed */
5309 foreach(ltab, *wqueue)
5310 {
5311 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5312 List *subcmds = tab->subcmds[pass];
5313 ListCell *lcmd;
5314
5315 if (subcmds == NIL)
5316 continue;
5317
5318 /*
5319 * Open the relation and store it in tab. This allows subroutines
5320 * close and reopen, if necessary. Appropriate lock was obtained
5321 * by phase 1, needn't get it again.
5322 */
5323 tab->rel = relation_open(tab->relid, NoLock);
5324
5325 foreach(lcmd, subcmds)
5326 ATExecCmd(wqueue, tab,
5328 lockmode, pass, context);
5329
5330 /*
5331 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5332 * (this is not done in ATExecAlterColumnType since it should be
5333 * done only once if multiple columns of a table are altered).
5334 */
5335 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5336 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5337
5338 if (tab->rel)
5339 {
5340 relation_close(tab->rel, NoLock);
5341 tab->rel = NULL;
5342 }
5343 }
5344 }
5345
5346 /* Check to see if a toast table must be added. */
5347 foreach(ltab, *wqueue)
5348 {
5349 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5350
5351 /*
5352 * If the table is source table of ATTACH PARTITION command, we did
5353 * not modify anything about it that will change its toasting
5354 * requirement, so no need to check.
5355 */
5356 if (((tab->relkind == RELKIND_RELATION ||
5357 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5358 tab->partition_constraint == NULL) ||
5359 tab->relkind == RELKIND_MATVIEW)
5360 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5361 }
5362}
5363
5364/*
5365 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5366 */
5367static void
5369 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5370 AlterTableUtilityContext *context)
5371{
5373 Relation rel = tab->rel;
5374
5375 switch (cmd->subtype)
5376 {
5377 case AT_AddColumn: /* ADD COLUMN */
5378 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5379 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5380 cmd->recurse, false,
5381 lockmode, cur_pass, context);
5382 break;
5383 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5384 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5385 break;
5386 case AT_CookedColumnDefault: /* add a pre-cooked default */
5387 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5388 break;
5389 case AT_AddIdentity:
5390 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5391 cur_pass, context);
5392 Assert(cmd != NULL);
5393 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5394 break;
5395 case AT_SetIdentity:
5396 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5397 cur_pass, context);
5398 Assert(cmd != NULL);
5399 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5400 break;
5401 case AT_DropIdentity:
5402 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5403 break;
5404 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5405 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5406 break;
5407 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5408 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5409 cmd->recurse, false, lockmode);
5410 break;
5411 case AT_SetExpression:
5412 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5413 break;
5414 case AT_DropExpression:
5415 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5416 break;
5417 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5418 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5419 break;
5420 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5421 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5422 break;
5423 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5424 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5425 break;
5426 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5427 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5428 break;
5429 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5430 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5431 lockmode);
5432 break;
5433 case AT_DropColumn: /* DROP COLUMN */
5434 address = ATExecDropColumn(wqueue, rel, cmd->name,
5435 cmd->behavior, cmd->recurse, false,
5436 cmd->missing_ok, lockmode,
5437 NULL);
5438 break;
5439 case AT_AddIndex: /* ADD INDEX */
5440 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5441 lockmode);
5442 break;
5443 case AT_ReAddIndex: /* ADD INDEX */
5444 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5445 lockmode);
5446 break;
5447 case AT_ReAddStatistics: /* ADD STATISTICS */
5448 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5449 true, lockmode);
5450 break;
5451 case AT_AddConstraint: /* ADD CONSTRAINT */
5452 /* Transform the command only during initial examination */
5453 if (cur_pass == AT_PASS_ADD_CONSTR)
5454 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5455 cmd->recurse, lockmode,
5456 cur_pass, context);
5457 /* Depending on constraint type, might be no more work to do now */
5458 if (cmd != NULL)
5459 address =
5460 ATExecAddConstraint(wqueue, tab, rel,
5461 (Constraint *) cmd->def,
5462 cmd->recurse, false, lockmode);
5463 break;
5464 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5465 address =
5466 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5467 true, true, lockmode);
5468 break;
5469 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5470 * constraint */
5471 address =
5472 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5473 ((AlterDomainStmt *) cmd->def)->def,
5474 NULL);
5475 break;
5476 case AT_ReAddComment: /* Re-add existing comment */
5477 address = CommentObject((CommentStmt *) cmd->def);
5478 break;
5479 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5480 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5481 lockmode);
5482 break;
5483 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5484 address = ATExecAlterConstraint(wqueue, rel,
5486 cmd->recurse, lockmode);
5487 break;
5488 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5489 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5490 false, lockmode);
5491 break;
5492 case AT_DropConstraint: /* DROP CONSTRAINT */
5493 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5494 cmd->recurse,
5495 cmd->missing_ok, lockmode);
5496 break;
5497 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5498 /* parse transformation was done earlier */
5499 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5500 break;
5501 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5502 address =
5504 (List *) cmd->def, lockmode);
5505 break;
5506 case AT_ChangeOwner: /* ALTER OWNER */
5508 get_rolespec_oid(cmd->newowner, false),
5509 false, lockmode);
5510 break;
5511 case AT_ClusterOn: /* CLUSTER ON */
5512 address = ATExecClusterOn(rel, cmd->name, lockmode);
5513 break;
5514 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5515 ATExecDropCluster(rel, lockmode);
5516 break;
5517 case AT_SetLogged: /* SET LOGGED */
5518 case AT_SetUnLogged: /* SET UNLOGGED */
5519 break;
5520 case AT_DropOids: /* SET WITHOUT OIDS */
5521 /* nothing to do here, oid columns don't exist anymore */
5522 break;
5523 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5524
5525 /*
5526 * Only do this for partitioned tables, for which this is just a
5527 * catalog change. Tables with storage are handled by Phase 3.
5528 */
5529 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5530 tab->chgAccessMethod)
5532 break;
5533 case AT_SetTableSpace: /* SET TABLESPACE */
5534
5535 /*
5536 * Only do this for partitioned tables and indexes, for which this
5537 * is just a catalog change. Other relation types which have
5538 * storage are handled by Phase 3.
5539 */
5540 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5541 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5543
5544 break;
5545 case AT_SetRelOptions: /* SET (...) */
5546 case AT_ResetRelOptions: /* RESET (...) */
5547 case AT_ReplaceRelOptions: /* replace entire option list */
5548 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5549 break;
5550 case AT_EnableTrig: /* ENABLE TRIGGER name */
5553 cmd->recurse,
5554 lockmode);
5555 break;
5556 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5558 TRIGGER_FIRES_ALWAYS, false,
5559 cmd->recurse,
5560 lockmode);
5561 break;
5562 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5565 cmd->recurse,
5566 lockmode);
5567 break;
5568 case AT_DisableTrig: /* DISABLE TRIGGER name */
5570 TRIGGER_DISABLED, false,
5571 cmd->recurse,
5572 lockmode);
5573 break;
5574 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5577 cmd->recurse,
5578 lockmode);
5579 break;
5580 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5582 TRIGGER_DISABLED, false,
5583 cmd->recurse,
5584 lockmode);
5585 break;
5586 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5589 cmd->recurse,
5590 lockmode);
5591 break;
5592 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5594 TRIGGER_DISABLED, true,
5595 cmd->recurse,
5596 lockmode);
5597 break;
5598
5599 case AT_EnableRule: /* ENABLE RULE name */
5600 ATExecEnableDisableRule(rel, cmd->name,
5601 RULE_FIRES_ON_ORIGIN, lockmode);
5602 break;
5603 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5604 ATExecEnableDisableRule(rel, cmd->name,
5605 RULE_FIRES_ALWAYS, lockmode);
5606 break;
5607 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5608 ATExecEnableDisableRule(rel, cmd->name,
5609 RULE_FIRES_ON_REPLICA, lockmode);
5610 break;
5611 case AT_DisableRule: /* DISABLE RULE name */
5612 ATExecEnableDisableRule(rel, cmd->name,
5613 RULE_DISABLED, lockmode);
5614 break;
5615
5616 case AT_AddInherit:
5617 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5618 break;
5619 case AT_DropInherit:
5620 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5621 break;
5622 case AT_AddOf:
5623 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5624 break;
5625 case AT_DropOf:
5626 ATExecDropOf(rel, lockmode);
5627 break;
5628 case AT_ReplicaIdentity:
5629 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5630 break;
5632 ATExecSetRowSecurity(rel, true);
5633 break;
5635 ATExecSetRowSecurity(rel, false);
5636 break;
5639 break;
5642 break;
5643 case AT_GenericOptions:
5644 ATExecGenericOptions(rel, (List *) cmd->def);
5645 break;
5646 case AT_AttachPartition:
5647 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5648 cur_pass, context);
5649 Assert(cmd != NULL);
5650 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5651 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5652 context);
5653 else
5654 address = ATExecAttachPartitionIdx(wqueue, rel,
5655 ((PartitionCmd *) cmd->def)->name);
5656 break;
5657 case AT_DetachPartition:
5658 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5659 cur_pass, context);
5660 Assert(cmd != NULL);
5661 /* ATPrepCmd ensures it must be a table */
5662 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5663 address = ATExecDetachPartition(wqueue, tab, rel,
5664 ((PartitionCmd *) cmd->def)->name,
5665 ((PartitionCmd *) cmd->def)->concurrent);
5666 break;
5668 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5669 break;
5670 default: /* oops */
5671 elog(ERROR, "unrecognized alter table type: %d",
5672 (int) cmd->subtype);
5673 break;
5674 }
5675
5676 /*
5677 * Report the subcommand to interested event triggers.
5678 */
5679 if (cmd)
5680 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5681
5682 /*
5683 * Bump the command counter to ensure the next subcommand in the sequence
5684 * can see the changes so far
5685 */
5687}
5688
5689/*
5690 * ATParseTransformCmd: perform parse transformation for one subcommand
5691 *
5692 * Returns the transformed subcommand tree, if there is one, else NULL.
5693 *
5694 * The parser may hand back additional AlterTableCmd(s) and/or other
5695 * utility statements, either before or after the original subcommand.
5696 * Other AlterTableCmds are scheduled into the appropriate slot of the
5697 * AlteredTableInfo (they had better be for later passes than the current one).
5698 * Utility statements that are supposed to happen before the AlterTableCmd
5699 * are executed immediately. Those that are supposed to happen afterwards
5700 * are added to the tab->afterStmts list to be done at the very end.
5701 */
5702static AlterTableCmd *
5704 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5705 AlterTablePass cur_pass, AlterTableUtilityContext *context)
5706{
5707 AlterTableCmd *newcmd = NULL;
5709 List *beforeStmts;
5710 List *afterStmts;
5711 ListCell *lc;
5712
5713 /* Gin up an AlterTableStmt with just this subcommand and this table */
5714 atstmt->relation =
5717 -1);
5718 atstmt->relation->inh = recurse;
5719 atstmt->cmds = list_make1(cmd);
5720 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5721 atstmt->missing_ok = false;
5722
5723 /* Transform the AlterTableStmt */
5725 atstmt,
5726 context->queryString,
5727 &beforeStmts,
5728 &afterStmts);
5729
5730 /* Execute any statements that should happen before these subcommand(s) */
5731 foreach(lc, beforeStmts)
5732 {
5733 Node *stmt = (Node *) lfirst(lc);
5734
5737 }
5738
5739 /* Examine the transformed subcommands and schedule them appropriately */
5740 foreach(lc, atstmt->cmds)
5741 {
5743 AlterTablePass pass;
5744
5745 /*
5746 * This switch need only cover the subcommand types that can be added
5747 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5748 * executing the subcommand immediately, as a substitute for the
5749 * original subcommand. (Note, however, that this does cause
5750 * AT_AddConstraint subcommands to be rescheduled into later passes,
5751 * which is important for index and foreign key constraints.)
5752 *
5753 * We assume we needn't do any phase-1 checks for added subcommands.
5754 */
5755 switch (cmd2->subtype)
5756 {
5757 case AT_AddIndex:
5758 pass = AT_PASS_ADD_INDEX;
5759 break;
5762 break;
5763 case AT_AddConstraint:
5764 /* Recursion occurs during execution phase */
5765 if (recurse)
5766 cmd2->recurse = true;
5767 switch (castNode(Constraint, cmd2->def)->contype)
5768 {
5769 case CONSTR_NOTNULL:
5770 pass = AT_PASS_COL_ATTRS;
5771 break;
5772 case CONSTR_PRIMARY:
5773 case CONSTR_UNIQUE:
5774 case CONSTR_EXCLUSION:
5776 break;
5777 default:
5779 break;
5780 }
5781 break;
5783 /* This command never recurses */
5784 /* No command-specific prep needed */
5785 pass = AT_PASS_MISC;
5786 break;
5787 default:
5788 pass = cur_pass;
5789 break;
5790 }
5791
5792 if (pass < cur_pass)
5793 {
5794 /* Cannot schedule into a pass we already finished */
5795 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5796 pass);
5797 }
5798 else if (pass > cur_pass)
5799 {
5800 /* OK, queue it up for later */
5801 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5802 }
5803 else
5804 {
5805 /*
5806 * We should see at most one subcommand for the current pass,
5807 * which is the transformed version of the original subcommand.
5808 */
5809 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5810 {
5811 /* Found the transformed version of our subcommand */
5812 newcmd = cmd2;
5813 }
5814 else
5815 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5816 pass);
5817 }
5818 }
5819
5820 /* Queue up any after-statements to happen at the end */
5821 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5822
5823 return newcmd;
5824}
5825
5826/*
5827 * ATRewriteTables: ALTER TABLE phase 3
5828 */
5829static void
5830ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5831 AlterTableUtilityContext *context)
5832{
5833 ListCell *ltab;
5834
5835 /* Go through each table that needs to be checked or rewritten */
5836 foreach(ltab, *wqueue)
5837 {
5838 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5839
5840 /* Relations without storage may be ignored here */
5841 if (!RELKIND_HAS_STORAGE(tab->relkind))
5842 continue;
5843
5844 /*
5845 * If we change column data types, the operation has to be propagated
5846 * to tables that use this table's rowtype as a column type.
5847 * tab->newvals will also be non-NULL in the case where we're adding a
5848 * column with a default. We choose to forbid that case as well,
5849 * since composite types might eventually support defaults.
5850 *
5851 * (Eventually we'll probably need to check for composite type
5852 * dependencies even when we're just scanning the table without a
5853 * rewrite, but at the moment a composite type does not enforce any
5854 * constraints, so it's not necessary/appropriate to enforce them just
5855 * during ALTER.)
5856 */
5857 if (tab->newvals != NIL || tab->rewrite > 0)
5858 {
5859 Relation rel;
5860
5861 rel = table_open(tab->relid, NoLock);
5862 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5863 table_close(rel, NoLock);
5864 }
5865
5866 /*
5867 * We only need to rewrite the table if at least one column needs to
5868 * be recomputed, or we are changing its persistence or access method.
5869 *
5870 * There are two reasons for requiring a rewrite when changing
5871 * persistence: on one hand, we need to ensure that the buffers
5872 * belonging to each of the two relations are marked with or without
5873 * BM_PERMANENT properly. On the other hand, since rewriting creates
5874 * and assigns a new relfilenumber, we automatically create or drop an
5875 * init fork for the relation as appropriate.
5876 */
5877 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5878 {
5879 /* Build a temporary relation and copy data */
5880 Relation OldHeap;
5881 Oid OIDNewHeap;
5882 Oid NewAccessMethod;
5883 Oid NewTableSpace;
5884 char persistence;
5885
5886 OldHeap = table_open(tab->relid, NoLock);
5887
5888 /*
5889 * We don't support rewriting of system catalogs; there are too
5890 * many corner cases and too little benefit. In particular this
5891 * is certainly not going to work for mapped catalogs.
5892 */
5893 if (IsSystemRelation(OldHeap))
5894 ereport(ERROR,
5895 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5896 errmsg("cannot rewrite system relation \"%s\"",
5897 RelationGetRelationName(OldHeap))));
5898
5899 if (RelationIsUsedAsCatalogTable(OldHeap))
5900 ereport(ERROR,
5901 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5902 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5903 RelationGetRelationName(OldHeap))));
5904
5905 /*
5906 * Don't allow rewrite on temp tables of other backends ... their
5907 * local buffer manager is not going to cope. (This is redundant
5908 * with the check in CheckAlterTableIsSafe, but for safety we'll
5909 * check here too.)
5910 */
5911 if (RELATION_IS_OTHER_TEMP(OldHeap))
5912 ereport(ERROR,
5913 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5914 errmsg("cannot rewrite temporary tables of other sessions")));
5915
5916 /*
5917 * Select destination tablespace (same as original unless user
5918 * requested a change)
5919 */
5920 if (tab->newTableSpace)
5921 NewTableSpace = tab->newTableSpace;
5922 else
5923 NewTableSpace = OldHeap->rd_rel->reltablespace;
5924
5925 /*
5926 * Select destination access method (same as original unless user
5927 * requested a change)
5928 */
5929 if (tab->chgAccessMethod)
5930 NewAccessMethod = tab->newAccessMethod;
5931 else
5932 NewAccessMethod = OldHeap->rd_rel->relam;
5933
5934 /*
5935 * Select persistence of transient table (same as original unless
5936 * user requested a change)
5937 */
5938 persistence = tab->chgPersistence ?
5939 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5940
5941 table_close(OldHeap, NoLock);
5942
5943 /*
5944 * Fire off an Event Trigger now, before actually rewriting the
5945 * table.
5946 *
5947 * We don't support Event Trigger for nested commands anywhere,
5948 * here included, and parsetree is given NULL when coming from
5949 * AlterTableInternal.
5950 *
5951 * And fire it only once.
5952 */
5953 if (parsetree)
5954 EventTriggerTableRewrite((Node *) parsetree,
5955 tab->relid,
5956 tab->rewrite);
5957
5958 /*
5959 * Create transient table that will receive the modified data.
5960 *
5961 * Ensure it is marked correctly as logged or unlogged. We have
5962 * to do this here so that buffers for the new relfilenumber will
5963 * have the right persistence set, and at the same time ensure
5964 * that the original filenumbers's buffers will get read in with
5965 * the correct setting (i.e. the original one). Otherwise a
5966 * rollback after the rewrite would possibly result with buffers
5967 * for the original filenumbers having the wrong persistence
5968 * setting.
5969 *
5970 * NB: This relies on swap_relation_files() also swapping the
5971 * persistence. That wouldn't work for pg_class, but that can't be
5972 * unlogged anyway.
5973 */
5974 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5975 persistence, lockmode);
5976
5977 /*
5978 * Copy the heap data into the new table with the desired
5979 * modifications, and test the current data within the table
5980 * against new constraints generated by ALTER TABLE commands.
5981 */
5982 ATRewriteTable(tab, OIDNewHeap);
5983
5984 /*
5985 * Swap the physical files of the old and new heaps, then rebuild
5986 * indexes and discard the old heap. We can use RecentXmin for
5987 * the table's new relfrozenxid because we rewrote all the tuples
5988 * in ATRewriteTable, so no older Xid remains in the table. Also,
5989 * we never try to swap toast tables by content, since we have no
5990 * interest in letting this code work on system catalogs.
5991 */
5992 finish_heap_swap(tab->relid, OIDNewHeap,
5993 false, false, true,
5995 RecentXmin,
5997 persistence);
5998
5999 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
6000 }
6001 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6002 {
6003 if (tab->chgPersistence)
6005 }
6006 else
6007 {
6008 /*
6009 * If required, test the current data within the table against new
6010 * constraints generated by ALTER TABLE commands, but don't
6011 * rebuild data.
6012 */
6013 if (tab->constraints != NIL || tab->verify_new_notnull ||
6014 tab->partition_constraint != NULL)
6016
6017 /*
6018 * If we had SET TABLESPACE but no reason to reconstruct tuples,
6019 * just do a block-by-block copy.
6020 */
6021 if (tab->newTableSpace)
6022 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6023 }
6024
6025 /*
6026 * Also change persistence of owned sequences, so that it matches the
6027 * table persistence.
6028 */
6029 if (tab->chgPersistence)
6030 {
6031 List *seqlist = getOwnedSequences(tab->relid);
6032 ListCell *lc;
6033
6034 foreach(lc, seqlist)
6035 {
6036 Oid seq_relid = lfirst_oid(lc);
6037
6039 }
6040 }
6041 }
6042
6043 /*
6044 * Foreign key constraints are checked in a final pass, since (a) it's
6045 * generally best to examine each one separately, and (b) it's at least
6046 * theoretically possible that we have changed both relations of the
6047 * foreign key, and we'd better have finished both rewrites before we try
6048 * to read the tables.
6049 */
6050 foreach(ltab, *wqueue)
6051 {
6052 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6053 Relation rel = NULL;
6054 ListCell *lcon;
6055
6056 /* Relations without storage may be ignored here too */
6057 if (!RELKIND_HAS_STORAGE(tab->relkind))
6058 continue;
6059
6060 foreach(lcon, tab->constraints)
6061 {
6062 NewConstraint *con = lfirst(lcon);
6063
6064 if (con->contype == CONSTR_FOREIGN)
6065 {
6066 Constraint *fkconstraint = (Constraint *) con->qual;
6067 Relation refrel;
6068
6069 if (rel == NULL)
6070 {
6071 /* Long since locked, no need for another */
6072 rel = table_open(tab->relid, NoLock);
6073 }
6074
6075 refrel = table_open(con->refrelid, RowShareLock);
6076
6077 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6078 con->refindid,
6079 con->conid,
6080 con->conwithperiod);
6081
6082 /*
6083 * No need to mark the constraint row as validated, we did
6084 * that when we inserted the row earlier.
6085 */
6086
6087 table_close(refrel, NoLock);
6088 }
6089 }
6090
6091 if (rel)
6092 table_close(rel, NoLock);
6093 }
6094
6095 /* Finally, run any afterStmts that were queued up */
6096 foreach(ltab, *wqueue)
6097 {
6098 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6099 ListCell *lc;
6100
6101 foreach(lc, tab->afterStmts)
6102 {
6103 Node *stmt = (Node *) lfirst(lc);
6104
6107 }
6108 }
6109}
6110
6111/*
6112 * ATRewriteTable: scan or rewrite one table
6113 *
6114 * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6115 * must already hold AccessExclusiveLock on it.
6116 */
6117static void
6119{
6120 Relation oldrel;
6121 Relation newrel;
6122 TupleDesc oldTupDesc;
6123 TupleDesc newTupDesc;
6124 bool needscan = false;
6125 List *notnull_attrs;
6126 List *notnull_virtual_attrs;
6127 int i;
6128 ListCell *l;
6129 EState *estate;
6130 CommandId mycid;
6131 BulkInsertState bistate;
6132 int ti_options;
6133 ExprState *partqualstate = NULL;
6134
6135 /*
6136 * Open the relation(s). We have surely already locked the existing
6137 * table.
6138 */
6139 oldrel = table_open(tab->relid, NoLock);
6140 oldTupDesc = tab->oldDesc;
6141 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6142
6143 if (OidIsValid(OIDNewHeap))
6144 {
6146 false));
6147 newrel = table_open(OIDNewHeap, NoLock);
6148 }
6149 else
6150 newrel = NULL;
6151
6152 /*
6153 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6154 * is empty, so don't bother using it.
6155 */
6156 if (newrel)
6157 {
6158 mycid = GetCurrentCommandId(true);
6159 bistate = GetBulkInsertState();
6160 ti_options = TABLE_INSERT_SKIP_FSM;
6161 }
6162 else
6163 {
6164 /* keep compiler quiet about using these uninitialized */
6165 mycid = 0;
6166 bistate = NULL;
6167 ti_options = 0;
6168 }
6169
6170 /*
6171 * Generate the constraint and default execution states
6172 */
6173
6174 estate = CreateExecutorState();
6175
6176 /* Build the needed expression execution states */
6177 foreach(l, tab->constraints)
6178 {
6179 NewConstraint *con = lfirst(l);
6180
6181 switch (con->contype)
6182 {
6183 case CONSTR_CHECK:
6184 needscan = true;
6185 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6186 break;
6187 case CONSTR_FOREIGN:
6188 /* Nothing to do here */
6189 break;
6190 default:
6191 elog(ERROR, "unrecognized constraint type: %d",
6192 (int) con->contype);
6193 }
6194 }
6195
6196 /* Build expression execution states for partition check quals */
6197 if (tab->partition_constraint)
6198 {
6199 needscan = true;
6200 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6201 }
6202
6203 foreach(l, tab->newvals)
6204 {
6205 NewColumnValue *ex = lfirst(l);
6206
6207 /* expr already planned */
6208 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6209 }
6210
6211 notnull_attrs = notnull_virtual_attrs = NIL;
6212 if (newrel || tab->verify_new_notnull)
6213 {
6214 /*
6215 * If we are rebuilding the tuples OR if we added any new but not
6216 * verified not-null constraints, check all *valid* not-null
6217 * constraints. This is a bit of overkill but it minimizes risk of
6218 * bugs.
6219 *
6220 * notnull_attrs does *not* collect attribute numbers for valid
6221 * not-null constraints over virtual generated columns; instead, they
6222 * are collected in notnull_virtual_attrs for verification elsewhere.
6223 */
6224 for (i = 0; i < newTupDesc->natts; i++)
6225 {
6226 CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6227
6228 if (attr->attnullability == ATTNULLABLE_VALID &&
6229 !attr->attisdropped)
6230 {
6231 Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6232
6233 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6234 notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6235 else
6236 notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6237 wholeatt->attnum);
6238 }
6239 }
6240 if (notnull_attrs || notnull_virtual_attrs)
6241 needscan = true;
6242 }
6243
6244 if (newrel || needscan)
6245 {
6246 ExprContext *econtext;
6247 TupleTableSlot *oldslot;
6248 TupleTableSlot *newslot;
6249 TableScanDesc scan;
6250 MemoryContext oldCxt;
6251 List *dropped_attrs = NIL;
6252 ListCell *lc;
6253 Snapshot snapshot;
6254 ResultRelInfo *rInfo = NULL;
6255
6256 /*
6257 * When adding or changing a virtual generated column with a not-null
6258 * constraint, we need to evaluate whether the generation expression
6259 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6260 * prepare a dummy ResultRelInfo.
6261 */
6262 if (notnull_virtual_attrs != NIL)
6263 {
6264 MemoryContext oldcontext;
6265
6266 Assert(newTupDesc->constr->has_generated_virtual);
6267 Assert(newTupDesc->constr->has_not_null);
6268 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6269 rInfo = makeNode(ResultRelInfo);
6270 InitResultRelInfo(rInfo,
6271 oldrel,
6272 0, /* dummy rangetable index */
6273 NULL,
6274 estate->es_instrument);
6275 MemoryContextSwitchTo(oldcontext);
6276 }
6277
6278 if (newrel)
6280 (errmsg_internal("rewriting table \"%s\"",
6281 RelationGetRelationName(oldrel))));
6282 else
6284 (errmsg_internal("verifying table \"%s\"",
6285 RelationGetRelationName(oldrel))));
6286
6287 if (newrel)
6288 {
6289 /*
6290 * All predicate locks on the tuples or pages are about to be made
6291 * invalid, because we move tuples around. Promote them to
6292 * relation locks.
6293 */
6295 }
6296
6297 econtext = GetPerTupleExprContext(estate);
6298
6299 /*
6300 * Create necessary tuple slots. When rewriting, two slots are needed,
6301 * otherwise one suffices. In the case where one slot suffices, we
6302 * need to use the new tuple descriptor, otherwise some constraints
6303 * can't be evaluated. Note that even when the tuple layout is the
6304 * same and no rewrite is required, the tupDescs might not be
6305 * (consider ADD COLUMN without a default).
6306 */
6307 if (tab->rewrite)
6308 {
6309 Assert(newrel != NULL);
6310 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6311 table_slot_callbacks(oldrel));
6312 newslot = MakeSingleTupleTableSlot(newTupDesc,
6313 table_slot_callbacks(newrel));
6314
6315 /*
6316 * Set all columns in the new slot to NULL initially, to ensure
6317 * columns added as part of the rewrite are initialized to NULL.
6318 * That is necessary as tab->newvals will not contain an
6319 * expression for columns with a NULL default, e.g. when adding a
6320 * column without a default together with a column with a default
6321 * requiring an actual rewrite.
6322 */
6323 ExecStoreAllNullTuple(newslot);
6324 }
6325 else
6326 {
6327 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6328 table_slot_callbacks(oldrel));
6329 newslot = NULL;
6330 }
6331
6332 /*
6333 * Any attributes that are dropped according to the new tuple
6334 * descriptor can be set to NULL. We precompute the list of dropped
6335 * attributes to avoid needing to do so in the per-tuple loop.
6336 */
6337 for (i = 0; i < newTupDesc->natts; i++)
6338 {
6339 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6340 dropped_attrs = lappend_int(dropped_attrs, i);
6341 }
6342
6343 /*
6344 * Scan through the rows, generating a new row if needed and then
6345 * checking all the constraints.
6346 */
6347 snapshot = RegisterSnapshot(GetLatestSnapshot());
6348 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6349
6350 /*
6351 * Switch to per-tuple memory context and reset it for each tuple
6352 * produced, so we don't leak memory.
6353 */
6355
6356 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6357 {
6358 TupleTableSlot *insertslot;
6359
6360 if (tab->rewrite > 0)
6361 {
6362 /* Extract data from old tuple */
6363 slot_getallattrs(oldslot);
6364 ExecClearTuple(newslot);
6365
6366 /* copy attributes */
6367 memcpy(newslot->tts_values, oldslot->tts_values,
6368 sizeof(Datum) * oldslot->tts_nvalid);
6369 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6370 sizeof(bool) * oldslot->tts_nvalid);
6371
6372 /* Set dropped attributes to null in new tuple */
6373 foreach(lc, dropped_attrs)
6374 newslot->tts_isnull[lfirst_int(lc)] = true;
6375
6376 /*
6377 * Constraints and GENERATED expressions might reference the
6378 * tableoid column, so fill tts_tableOid with the desired
6379 * value. (We must do this each time, because it gets
6380 * overwritten with newrel's OID during storing.)
6381 */
6382 newslot->tts_tableOid = RelationGetRelid(oldrel);
6383
6384 /*
6385 * Process supplied expressions to replace selected columns.
6386 *
6387 * First, evaluate expressions whose inputs come from the old
6388 * tuple.
6389 */
6390 econtext->ecxt_scantuple = oldslot;
6391
6392 foreach(l, tab->newvals)
6393 {
6394 NewColumnValue *ex = lfirst(l);
6395
6396 if (ex->is_generated)
6397 continue;
6398
6399 newslot->tts_values[ex->attnum - 1]
6400 = ExecEvalExpr(ex->exprstate,
6401 econtext,
6402 &newslot->tts_isnull[ex->attnum - 1]);
6403 }
6404
6405 ExecStoreVirtualTuple(newslot);
6406
6407 /*
6408 * Now, evaluate any expressions whose inputs come from the
6409 * new tuple. We assume these columns won't reference each
6410 * other, so that there's no ordering dependency.
6411 */
6412 econtext->ecxt_scantuple = newslot;
6413
6414 foreach(l, tab->newvals)
6415 {
6416 NewColumnValue *ex = lfirst(l);
6417
6418 if (!ex->is_generated)
6419 continue;
6420
6421 newslot->tts_values[ex->attnum - 1]
6422 = ExecEvalExpr(ex->exprstate,
6423 econtext,
6424 &newslot->tts_isnull[ex->attnum - 1]);
6425 }
6426
6427 insertslot = newslot;
6428 }
6429 else
6430 {
6431 /*
6432 * If there's no rewrite, old and new table are guaranteed to
6433 * have the same AM, so we can just use the old slot to verify
6434 * new constraints etc.
6435 */
6436 insertslot = oldslot;
6437 }
6438
6439 /* Now check any constraints on the possibly-changed tuple */
6440 econtext->ecxt_scantuple = insertslot;
6441
6442 foreach_int(attn, notnull_attrs)
6443 {
6444 if (slot_attisnull(insertslot, attn))
6445 {
6446 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6447
6448 ereport(ERROR,
6449 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6450 errmsg("column \"%s\" of relation \"%s\" contains null values",
6451 NameStr(attr->attname),
6452 RelationGetRelationName(oldrel)),
6453 errtablecol(oldrel, attn)));
6454 }
6455 }
6456
6457 if (notnull_virtual_attrs != NIL)
6458 {
6460
6461 attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6462 estate,
6463 notnull_virtual_attrs);
6465 {
6466 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6467
6468 ereport(ERROR,
6469 errcode(ERRCODE_NOT_NULL_VIOLATION),
6470 errmsg("column \"%s\" of relation \"%s\" contains null values",
6471 NameStr(attr->attname),
6472 RelationGetRelationName(oldrel)),
6473 errtablecol(oldrel, attnum));
6474 }
6475 }
6476
6477 foreach(l, tab->constraints)
6478 {
6479 NewConstraint *con = lfirst(l);
6480
6481 switch (con->contype)
6482 {
6483 case CONSTR_CHECK:
6484 if (!ExecCheck(con->qualstate, econtext))
6485 ereport(ERROR,
6486 (errcode(ERRCODE_CHECK_VIOLATION),
6487 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6488 con->name,
6489 RelationGetRelationName(oldrel)),
6490 errtableconstraint(oldrel, con->name)));
6491 break;
6492 case CONSTR_NOTNULL:
6493 case CONSTR_FOREIGN:
6494 /* Nothing to do here */
6495 break;
6496 default:
6497 elog(ERROR, "unrecognized constraint type: %d",
6498 (int) con->contype);
6499 }
6500 }
6501
6502 if (partqualstate && !ExecCheck(partqualstate, econtext))
6503 {
6504 if (tab->validate_default)
6505 ereport(ERROR,
6506 (errcode(ERRCODE_CHECK_VIOLATION),
6507 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6508 RelationGetRelationName(oldrel)),
6509 errtable(oldrel)));
6510 else
6511 ereport(ERROR,
6512 (errcode(ERRCODE_CHECK_VIOLATION),
6513 errmsg("partition constraint of relation \"%s\" is violated by some row",
6514 RelationGetRelationName(oldrel)),
6515 errtable(oldrel)));
6516 }
6517
6518 /* Write the tuple out to the new relation */
6519 if (newrel)
6520 table_tuple_insert(newrel, insertslot, mycid,
6521 ti_options, bistate);
6522
6523 ResetExprContext(econtext);
6524
6526 }
6527
6528 MemoryContextSwitchTo(oldCxt);
6529 table_endscan(scan);
6530 UnregisterSnapshot(snapshot);
6531
6533 if (newslot)
6535 }
6536
6537 FreeExecutorState(estate);
6538
6539 table_close(oldrel, NoLock);
6540 if (newrel)
6541 {
6542 FreeBulkInsertState(bistate);
6543
6544 table_finish_bulk_insert(newrel, ti_options);
6545
6546 table_close(newrel, NoLock);
6547 }
6548}
6549
6550/*
6551 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6552 */
6553static AlteredTableInfo *
6555{
6556 Oid relid = RelationGetRelid(rel);
6557 AlteredTableInfo *tab;
6558 ListCell *ltab;
6559
6560 foreach(ltab, *wqueue)
6561 {
6562 tab = (AlteredTableInfo *) lfirst(ltab);
6563 if (tab->relid == relid)
6564 return tab;
6565 }
6566
6567 /*
6568 * Not there, so add it. Note that we make a copy of the relation's
6569 * existing descriptor before anything interesting can happen to it.
6570 */
6571 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6572 tab->relid = relid;
6573 tab->rel = NULL; /* set later */
6574 tab->relkind = rel->rd_rel->relkind;
6577 tab->chgAccessMethod = false;
6579 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6580 tab->chgPersistence = false;
6581
6582 *wqueue = lappend(*wqueue, tab);
6583
6584 return tab;
6585}
6586
6587static const char *
6589{
6590 switch (cmdtype)
6591 {
6592 case AT_AddColumn:
6593 case AT_AddColumnToView:
6594 return "ADD COLUMN";
6595 case AT_ColumnDefault:
6597 return "ALTER COLUMN ... SET DEFAULT";
6598 case AT_DropNotNull:
6599 return "ALTER COLUMN ... DROP NOT NULL";
6600 case AT_SetNotNull:
6601 return "ALTER COLUMN ... SET NOT NULL";
6602 case AT_SetExpression:
6603 return "ALTER COLUMN ... SET EXPRESSION";
6604 case AT_DropExpression:
6605 return "ALTER COLUMN ... DROP EXPRESSION";
6606 case AT_SetStatistics:
6607 return "ALTER COLUMN ... SET STATISTICS";
6608 case AT_SetOptions:
6609 return "ALTER COLUMN ... SET";
6610 case AT_ResetOptions:
6611 return "ALTER COLUMN ... RESET";
6612 case AT_SetStorage:
6613 return "ALTER COLUMN ... SET STORAGE";
6614 case AT_SetCompression:
6615 return "ALTER COLUMN ... SET COMPRESSION";
6616 case AT_DropColumn:
6617 return "DROP COLUMN";
6618 case AT_AddIndex:
6619 case AT_ReAddIndex:
6620 return NULL; /* not real grammar */
6621 case AT_AddConstraint:
6622 case AT_ReAddConstraint:
6625 return "ADD CONSTRAINT";
6626 case AT_AlterConstraint:
6627 return "ALTER CONSTRAINT";
6629 return "VALIDATE CONSTRAINT";
6630 case AT_DropConstraint:
6631 return "DROP CONSTRAINT";
6632 case AT_ReAddComment:
6633 return NULL; /* not real grammar */
6634 case AT_AlterColumnType:
6635 return "ALTER COLUMN ... SET DATA TYPE";
6637 return "ALTER COLUMN ... OPTIONS";
6638 case AT_ChangeOwner:
6639 return "OWNER TO";
6640 case AT_ClusterOn:
6641 return "CLUSTER ON";
6642 case AT_DropCluster:
6643 return "SET WITHOUT CLUSTER";
6644 case AT_SetAccessMethod:
6645 return "SET ACCESS METHOD";
6646 case AT_SetLogged:
6647 return "SET LOGGED";
6648 case AT_SetUnLogged:
6649 return "SET UNLOGGED";
6650 case AT_DropOids:
6651 return "SET WITHOUT OIDS";
6652 case AT_SetTableSpace:
6653 return "SET TABLESPACE";
6654 case AT_SetRelOptions:
6655 return "SET";
6656 case AT_ResetRelOptions:
6657 return "RESET";
6659 return NULL; /* not real grammar */
6660 case AT_EnableTrig:
6661 return "ENABLE TRIGGER";
6663 return "ENABLE ALWAYS TRIGGER";
6665 return "ENABLE REPLICA TRIGGER";
6666 case AT_DisableTrig:
6667 return "DISABLE TRIGGER";
6668 case AT_EnableTrigAll:
6669 return "ENABLE TRIGGER ALL";
6670 case AT_DisableTrigAll:
6671 return "DISABLE TRIGGER ALL";
6672 case AT_EnableTrigUser:
6673 return "ENABLE TRIGGER USER";
6674 case AT_DisableTrigUser:
6675 return "DISABLE TRIGGER USER";
6676 case AT_EnableRule:
6677 return "ENABLE RULE";
6679 return "ENABLE ALWAYS RULE";
6681 return "ENABLE REPLICA RULE";
6682 case AT_DisableRule:
6683 return "DISABLE RULE";
6684 case AT_AddInherit:
6685 return "INHERIT";
6686 case AT_DropInherit:
6687 return "NO INHERIT";
6688 case AT_AddOf:
6689 return "OF";
6690 case AT_DropOf:
6691 return "NOT OF";
6692 case AT_ReplicaIdentity:
6693 return "REPLICA IDENTITY";
6695 return "ENABLE ROW SECURITY";
6697 return "DISABLE ROW SECURITY";
6699 return "FORCE ROW SECURITY";
6701 return "NO FORCE ROW SECURITY";
6702 case AT_GenericOptions:
6703 return "OPTIONS";
6704 case AT_AttachPartition:
6705 return "ATTACH PARTITION";
6706 case AT_DetachPartition:
6707 return "DETACH PARTITION";
6709 return "DETACH PARTITION ... FINALIZE";
6710 case AT_AddIdentity:
6711 return "ALTER COLUMN ... ADD IDENTITY";
6712 case AT_SetIdentity:
6713 return "ALTER COLUMN ... SET";
6714 case AT_DropIdentity:
6715 return "ALTER COLUMN ... DROP IDENTITY";
6716 case AT_ReAddStatistics:
6717 return NULL; /* not real grammar */
6718 }
6719
6720 return NULL;
6721}
6722
6723/*
6724 * ATSimplePermissions
6725 *
6726 * - Ensure that it is a relation (or possibly a view)
6727 * - Ensure this user is the owner
6728 * - Ensure that it is not a system table
6729 */
6730static void
6731ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6732{
6733 int actual_target;
6734
6735 switch (rel->rd_rel->relkind)
6736 {
6737 case RELKIND_RELATION:
6738 actual_target = ATT_TABLE;
6739 break;
6740 case RELKIND_PARTITIONED_TABLE:
6741 actual_target = ATT_PARTITIONED_TABLE;
6742 break;
6743 case RELKIND_VIEW:
6744 actual_target = ATT_VIEW;
6745 break;
6746 case RELKIND_MATVIEW:
6747 actual_target = ATT_MATVIEW;
6748 break;
6749 case RELKIND_INDEX:
6750 actual_target = ATT_INDEX;
6751 break;
6752 case RELKIND_PARTITIONED_INDEX:
6753 actual_target = ATT_PARTITIONED_INDEX;
6754 break;
6755 case RELKIND_COMPOSITE_TYPE:
6756 actual_target = ATT_COMPOSITE_TYPE;
6757 break;
6758 case RELKIND_FOREIGN_TABLE:
6759 actual_target = ATT_FOREIGN_TABLE;
6760 break;
6761 case RELKIND_SEQUENCE:
6762 actual_target = ATT_SEQUENCE;
6763 break;
6764 default:
6765 actual_target = 0;
6766 break;
6767 }
6768
6769 /* Wrong target type? */
6770 if ((actual_target & allowed_targets) == 0)
6771 {
6772 const char *action_str = alter_table_type_to_string(cmdtype);
6773
6774 if (action_str)
6775 ereport(ERROR,
6776 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6777 /* translator: %s is a group of some SQL keywords */
6778 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6779 action_str, RelationGetRelationName(rel)),
6780 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6781 else
6782 /* internal error? */
6783 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6785 }
6786
6787 /* Permissions checks */
6788 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6791
6793 ereport(ERROR,
6794 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6795 errmsg("permission denied: \"%s\" is a system catalog",
6797}
6798
6799/*
6800 * ATSimpleRecursion
6801 *
6802 * Simple table recursion sufficient for most ALTER TABLE operations.
6803 * All direct and indirect children are processed in an unspecified order.
6804 * Note that if a child inherits from the original table via multiple
6805 * inheritance paths, it will be visited just once.
6806 */
6807static void
6809 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6810 AlterTableUtilityContext *context)
6811{
6812 /*
6813 * Propagate to children, if desired and if there are (or might be) any
6814 * children.
6815 */
6816 if (recurse && rel->rd_rel->relhassubclass)
6817 {
6818 Oid relid = RelationGetRelid(rel);
6819 ListCell *child;
6820 List *children;
6821
6822 children = find_all_inheritors(relid, lockmode, NULL);
6823
6824 /*
6825 * find_all_inheritors does the recursive search of the inheritance
6826 * hierarchy, so all we have to do is process all of the relids in the
6827 * list that it returns.
6828 */
6829 foreach(child, children)
6830 {
6831 Oid childrelid = lfirst_oid(child);
6832 Relation childrel;
6833
6834 if (childrelid == relid)
6835 continue;
6836 /* find_all_inheritors already got lock */
6837 childrel = relation_open(childrelid, NoLock);
6838 CheckAlterTableIsSafe(childrel);
6839 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6840 relation_close(childrel, NoLock);
6841 }
6842 }
6843}
6844
6845/*
6846 * Obtain list of partitions of the given table, locking them all at the given
6847 * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6848 *
6849 * This function is a no-op if the given relation is not a partitioned table;
6850 * in particular, nothing is done if it's a legacy inheritance parent.
6851 */
6852static void
6854{
6855 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6856 {
6857 List *inh;
6858 ListCell *cell;
6859
6860 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6861 /* first element is the parent rel; must ignore it */
6862 for_each_from(cell, inh, 1)
6863 {
6864 Relation childrel;
6865
6866 /* find_all_inheritors already got lock */
6867 childrel = table_open(lfirst_oid(cell), NoLock);
6868 CheckAlterTableIsSafe(childrel);
6869 table_close(childrel, NoLock);
6870 }
6871 list_free(inh);
6872 }
6873}
6874
6875/*
6876 * ATTypedTableRecursion
6877 *
6878 * Propagate ALTER TYPE operations to the typed tables of that type.
6879 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6880 * recursion to inheritance children of the typed tables.
6881 */
6882static void
6884 LOCKMODE lockmode, AlterTableUtilityContext *context)
6885{
6886 ListCell *child;
6887 List *children;
6888
6889 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6890
6891 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6893 cmd->behavior);
6894
6895 foreach(child, children)
6896 {
6897 Oid childrelid = lfirst_oid(child);
6898 Relation childrel;
6899
6900 childrel = relation_open(childrelid, lockmode);
6901 CheckAlterTableIsSafe(childrel);
6902 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6903 relation_close(childrel, NoLock);
6904 }
6905}
6906
6907
6908/*
6909 * find_composite_type_dependencies
6910 *
6911 * Check to see if the type "typeOid" is being used as a column in some table
6912 * (possibly nested several levels deep in composite types, arrays, etc!).
6913 * Eventually, we'd like to propagate the check or rewrite operation
6914 * into such tables, but for now, just error out if we find any.
6915 *
6916 * Caller should provide either the associated relation of a rowtype,
6917 * or a type name (not both) for use in the error message, if any.
6918 *
6919 * Note that "typeOid" is not necessarily a composite type; it could also be
6920 * another container type such as an array or range, or a domain over one of
6921 * these things. The name of this function is therefore somewhat historical,
6922 * but it's not worth changing.
6923 *
6924 * We assume that functions and views depending on the type are not reasons
6925 * to reject the ALTER. (How safe is this really?)
6926 */
6927void
6929 const char *origTypeName)
6930{
6931 Relation depRel;
6932 ScanKeyData key[2];
6933 SysScanDesc depScan;
6934 HeapTuple depTup;
6935
6936 /* since this function recurses, it could be driven to stack overflow */
6938
6939 /*
6940 * We scan pg_depend to find those things that depend on the given type.
6941 * (We assume we can ignore refobjsubid for a type.)
6942 */
6943 depRel = table_open(DependRelationId, AccessShareLock);
6944
6945 ScanKeyInit(&key[0],
6946 Anum_pg_depend_refclassid,
6947 BTEqualStrategyNumber, F_OIDEQ,
6948 ObjectIdGetDatum(TypeRelationId));
6949 ScanKeyInit(&key[1],
6950 Anum_pg_depend_refobjid,
6951 BTEqualStrategyNumber, F_OIDEQ,
6952 ObjectIdGetDatum(typeOid));
6953
6954 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6955 NULL, 2, key);
6956
6957 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6958 {
6959 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6960 Relation rel;
6961 TupleDesc tupleDesc;
6963
6964 /* Check for directly dependent types */
6965 if (pg_depend->classid == TypeRelationId)
6966 {
6967 /*
6968 * This must be an array, domain, or range containing the given
6969 * type, so recursively check for uses of this type. Note that
6970 * any error message will mention the original type not the
6971 * container; this is intentional.
6972 */
6973 find_composite_type_dependencies(pg_depend->objid,
6974 origRelation, origTypeName);
6975 continue;
6976 }
6977
6978 /* Else, ignore dependees that aren't relations */
6979 if (pg_depend->classid != RelationRelationId)
6980 continue;
6981
6982 rel = relation_open(pg_depend->objid, AccessShareLock);
6983 tupleDesc = RelationGetDescr(rel);
6984
6985 /*
6986 * If objsubid identifies a specific column, refer to that in error
6987 * messages. Otherwise, search to see if there's a user column of the
6988 * type. (We assume system columns are never of interesting types.)
6989 * The search is needed because an index containing an expression
6990 * column of the target type will just be recorded as a whole-relation
6991 * dependency. If we do not find a column of the type, the dependency
6992 * must indicate that the type is transiently referenced in an index
6993 * expression but not stored on disk, which we assume is OK, just as
6994 * we do for references in views. (It could also be that the target
6995 * type is embedded in some container type that is stored in an index
6996 * column, but the previous recursion should catch such cases.)
6997 */
6998 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6999 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7000 else
7001 {
7002 att = NULL;
7003 for (int attno = 1; attno <= tupleDesc->natts; attno++)
7004 {
7005 att = TupleDescAttr(tupleDesc, attno - 1);
7006 if (att->atttypid == typeOid && !att->attisdropped)
7007 break;
7008 att = NULL;
7009 }
7010 if (att == NULL)
7011 {
7012 /* No such column, so assume OK */
7014 continue;
7015 }
7016 }
7017
7018 /*
7019 * We definitely should reject if the relation has storage. If it's
7020 * partitioned, then perhaps we don't have to reject: if there are
7021 * partitions then we'll fail when we find one, else there is no
7022 * stored data to worry about. However, it's possible that the type
7023 * change would affect conclusions about whether the type is sortable
7024 * or hashable and thus (if it's a partitioning column) break the
7025 * partitioning rule. For now, reject for partitioned rels too.
7026 */
7027 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7028 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7029 {
7030 if (origTypeName)
7031 ereport(ERROR,
7032 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7033 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7034 origTypeName,
7036 NameStr(att->attname))));
7037 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7038 ereport(ERROR,
7039 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7040 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7041 RelationGetRelationName(origRelation),
7043 NameStr(att->attname))));
7044 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7045 ereport(ERROR,
7046 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7047 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7048 RelationGetRelationName(origRelation),
7050 NameStr(att->attname))));
7051 else
7052 ereport(ERROR,
7053 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7054 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7055 RelationGetRelationName(origRelation),
7057 NameStr(att->attname))));
7058 }
7059 else if (OidIsValid(rel->rd_rel->reltype))
7060 {
7061 /*
7062 * A view or composite type itself isn't a problem, but we must
7063 * recursively check for indirect dependencies via its rowtype.
7064 */
7066 origRelation, origTypeName);
7067 }
7068
7070 }
7071
7072 systable_endscan(depScan);
7073
7075}
7076
7077
7078/*
7079 * find_typed_table_dependencies
7080 *
7081 * Check to see if a composite type is being used as the type of a
7082 * typed table. Abort if any are found and behavior is RESTRICT.
7083 * Else return the list of tables.
7084 */
7085static List *
7086find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7087{
7088 Relation classRel;
7089 ScanKeyData key[1];
7090 TableScanDesc scan;
7091 HeapTuple tuple;
7092 List *result = NIL;
7093
7094 classRel = table_open(RelationRelationId, AccessShareLock);
7095
7096 ScanKeyInit(&key[0],
7097 Anum_pg_class_reloftype,
7098 BTEqualStrategyNumber, F_OIDEQ,
7099 ObjectIdGetDatum(typeOid));
7100
7101 scan = table_beginscan_catalog(classRel, 1, key);
7102
7103 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7104 {
7105 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7106
7107 if (behavior == DROP_RESTRICT)
7108 ereport(ERROR,
7109 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7110 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7111 typeName),
7112 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7113 else
7114 result = lappend_oid(result, classform->oid);
7115 }
7116
7117 table_endscan(scan);
7118 table_close(classRel, AccessShareLock);
7119
7120 return result;
7121}
7122
7123
7124/*
7125 * check_of_type
7126 *
7127 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7128 * isn't suitable, throw an error. Currently, we require that the type
7129 * originated with CREATE TYPE AS. We could support any row type, but doing so
7130 * would require handling a number of extra corner cases in the DDL commands.
7131 * (Also, allowing domain-over-composite would open up a can of worms about
7132 * whether and how the domain's constraints should apply to derived tables.)
7133 */
7134void
7136{
7137 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7138 bool typeOk = false;
7139
7140 if (typ->typtype == TYPTYPE_COMPOSITE)
7141 {
7142 Relation typeRelation;
7143
7144 Assert(OidIsValid(typ->typrelid));
7145 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7146 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7147
7148 /*
7149 * Close the parent rel, but keep our AccessShareLock on it until xact
7150 * commit. That will prevent someone else from deleting or ALTERing
7151 * the type before the typed table creation/conversion commits.
7152 */
7153 relation_close(typeRelation, NoLock);
7154
7155 if (!typeOk)
7156 ereport(ERROR,
7157 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7158 errmsg("type %s is the row type of another table",
7159 format_type_be(typ->oid)),
7160 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7161 }
7162 else
7163 ereport(ERROR,
7164 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7165 errmsg("type %s is not a composite type",
7166 format_type_be(typ->oid))));
7167}
7168
7169
7170/*
7171 * ALTER TABLE ADD COLUMN
7172 *
7173 * Adds an additional attribute to a relation making the assumption that
7174 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7175 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7176 * AlterTableCmd's.
7177 *
7178 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7179 * have to decide at runtime whether to recurse or not depending on whether we
7180 * actually add a column or merely merge with an existing column. (We can't
7181 * check this in a static pre-pass because it won't handle multiple inheritance
7182 * situations correctly.)
7183 */
7184static void
7185ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7186 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7187 AlterTableUtilityContext *context)
7188{
7189 if (rel->rd_rel->reloftype && !recursing)
7190 ereport(ERROR,
7191 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7192 errmsg("cannot add column to typed table")));
7193
7194 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7195 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7196
7197 if (recurse && !is_view)
7198 cmd->recurse = true;
7199}
7200
7201/*
7202 * Add a column to a table. The return value is the address of the
7203 * new column in the parent relation.
7204 *
7205 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7206 * copy (but that happens only after we check for IF NOT EXISTS).
7207 */
7208static ObjectAddress
7210 AlterTableCmd **cmd, bool recurse, bool recursing,
7211 LOCKMODE lockmode, AlterTablePass cur_pass,
7212 AlterTableUtilityContext *context)
7213{
7214 Oid myrelid = RelationGetRelid(rel);
7215 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7216 bool if_not_exists = (*cmd)->missing_ok;
7217 Relation pgclass,
7218 attrdesc;
7219 HeapTuple reltup;
7220 Form_pg_class relform;
7221 Form_pg_attribute attribute;
7222 int newattnum;
7223 char relkind;
7224 Expr *defval;
7225 List *children;
7226 ListCell *child;
7227 AlterTableCmd *childcmd;
7228 ObjectAddress address;
7229 TupleDesc tupdesc;
7230
7231 /* since this function recurses, it could be driven to stack overflow */
7233
7234 /* At top level, permission check was done in ATPrepCmd, else do it */
7235 if (recursing)
7236 ATSimplePermissions((*cmd)->subtype, rel,
7238
7239 if (rel->rd_rel->relispartition && !recursing)
7240 ereport(ERROR,
7241 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7242 errmsg("cannot add column to a partition")));
7243
7244 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7245
7246 /*
7247 * Are we adding the column to a recursion child? If so, check whether to
7248 * merge with an existing definition for the column. If we do merge, we
7249 * must not recurse. Children will already have the column, and recursing
7250 * into them would mess up attinhcount.
7251 */
7252 if (colDef->inhcount > 0)
7253 {
7254 HeapTuple tuple;
7255
7256 /* Does child already have a column by this name? */
7257 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7258 if (HeapTupleIsValid(tuple))
7259 {
7260 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7261 Oid ctypeId;
7262 int32 ctypmod;
7263 Oid ccollid;
7264
7265 /* Child column must match on type, typmod, and collation */
7266 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7267 if (ctypeId != childatt->atttypid ||
7268 ctypmod != childatt->atttypmod)
7269 ereport(ERROR,
7270 (errcode(ERRCODE_DATATYPE_MISMATCH),
7271 errmsg("child table \"%s\" has different type for column \"%s\"",
7272 RelationGetRelationName(rel), colDef->colname)));
7273 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7274 if (ccollid != childatt->attcollation)
7275 ereport(ERROR,
7276 (errcode(ERRCODE_COLLATION_MISMATCH),
7277 errmsg("child table \"%s\" has different collation for column \"%s\"",
7278 RelationGetRelationName(rel), colDef->colname),
7279 errdetail("\"%s\" versus \"%s\"",
7280 get_collation_name(ccollid),
7281 get_collation_name(childatt->attcollation))));
7282
7283 /* Bump the existing child att's inhcount */
7284 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7285 &childatt->attinhcount))
7286 ereport(ERROR,
7287 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7288 errmsg("too many inheritance parents"));
7289 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7290
7291 heap_freetuple(tuple);
7292
7293 /* Inform the user about the merge */
7295 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7296 colDef->colname, RelationGetRelationName(rel))));
7297
7298 table_close(attrdesc, RowExclusiveLock);
7299
7300 /* Make the child column change visible */
7302
7303 return InvalidObjectAddress;
7304 }
7305 }
7306
7307 /* skip if the name already exists and if_not_exists is true */
7308 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7309 {
7310 table_close(attrdesc, RowExclusiveLock);
7311 return InvalidObjectAddress;
7312 }
7313
7314 /*
7315 * Okay, we need to add the column, so go ahead and do parse
7316 * transformation. This can result in queueing up, or even immediately
7317 * executing, subsidiary operations (such as creation of unique indexes);
7318 * so we mustn't do it until we have made the if_not_exists check.
7319 *
7320 * When recursing, the command was already transformed and we needn't do
7321 * so again. Also, if context isn't given we can't transform. (That
7322 * currently happens only for AT_AddColumnToView; we expect that view.c
7323 * passed us a ColumnDef that doesn't need work.)
7324 */
7325 if (context != NULL && !recursing)
7326 {
7327 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7328 cur_pass, context);
7329 Assert(*cmd != NULL);
7330 colDef = castNode(ColumnDef, (*cmd)->def);
7331 }
7332
7333 /*
7334 * Regular inheritance children are independent enough not to inherit the
7335 * identity column from parent hence cannot recursively add identity
7336 * column if the table has inheritance children.
7337 *
7338 * Partitions, on the other hand, are integral part of a partitioned table
7339 * and inherit identity column. Hence propagate identity column down the
7340 * partition hierarchy.
7341 */
7342 if (colDef->identity &&
7343 recurse &&
7344 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7346 ereport(ERROR,
7347 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7348 errmsg("cannot recursively add identity column to table that has child tables")));
7349
7350 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7351
7352 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7353 if (!HeapTupleIsValid(reltup))
7354 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7355 relform = (Form_pg_class) GETSTRUCT(reltup);
7356 relkind = relform->relkind;
7357
7358 /* Determine the new attribute's number */
7359 newattnum = relform->relnatts + 1;
7360 if (newattnum > MaxHeapAttributeNumber)
7361 ereport(ERROR,
7362 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7363 errmsg("tables can have at most %d columns",
7365
7366 /*
7367 * Construct new attribute's pg_attribute entry.
7368 */
7369 tupdesc = BuildDescForRelation(list_make1(colDef));
7370
7371 attribute = TupleDescAttr(tupdesc, 0);
7372
7373 /* Fix up attribute number */
7374 attribute->attnum = newattnum;
7375
7376 /* make sure datatype is legal for a column */
7377 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7378 list_make1_oid(rel->rd_rel->reltype),
7379 (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7380
7381 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7382
7383 table_close(attrdesc, RowExclusiveLock);
7384
7385 /*
7386 * Update pg_class tuple as appropriate
7387 */
7388 relform->relnatts = newattnum;
7389
7390 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7391
7392 heap_freetuple(reltup);
7393
7394 /* Post creation hook for new attribute */
7395 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7396
7397 table_close(pgclass, RowExclusiveLock);
7398
7399 /* Make the attribute's catalog entry visible */
7401
7402 /*
7403 * Store the DEFAULT, if any, in the catalogs
7404 */
7405 if (colDef->raw_default)
7406 {
7407 RawColumnDefault *rawEnt;
7408
7409 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7410 rawEnt->attnum = attribute->attnum;
7411 rawEnt->raw_default = copyObject(colDef->raw_default);
7412 rawEnt->generated = colDef->generated;
7413
7414 /*
7415 * This function is intended for CREATE TABLE, so it processes a
7416 * _list_ of defaults, but we just do one.
7417 */
7419 false, true, false, NULL);
7420
7421 /* Make the additional catalog changes visible */
7423 }
7424
7425 /*
7426 * Tell Phase 3 to fill in the default expression, if there is one.
7427 *
7428 * If there is no default, Phase 3 doesn't have to do anything, because
7429 * that effectively means that the default is NULL. The heap tuple access
7430 * routines always check for attnum > # of attributes in tuple, and return
7431 * NULL if so, so without any modification of the tuple data we will get
7432 * the effect of NULL values in the new column.
7433 *
7434 * An exception occurs when the new column is of a domain type: the domain
7435 * might have a not-null constraint, or a check constraint that indirectly
7436 * rejects nulls. If there are any domain constraints then we construct
7437 * an explicit NULL default value that will be passed through
7438 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7439 * rewriting the table which we really wouldn't have to do; but we do it
7440 * to preserve the historical behavior that such a failure will be raised
7441 * only if the table currently contains some rows.)
7442 *
7443 * Note: we use build_column_default, and not just the cooked default
7444 * returned by AddRelationNewConstraints, so that the right thing happens
7445 * when a datatype's default applies.
7446 *
7447 * Note: it might seem that this should happen at the end of Phase 2, so
7448 * that the effects of subsequent subcommands can be taken into account.
7449 * It's intentional that we do it now, though. The new column should be
7450 * filled according to what is said in the ADD COLUMN subcommand, so that
7451 * the effects are the same as if this subcommand had been run by itself
7452 * and the later subcommands had been issued in new ALTER TABLE commands.
7453 *
7454 * We can skip this entirely for relations without storage, since Phase 3
7455 * is certainly not going to touch them.
7456 */
7457 if (RELKIND_HAS_STORAGE(relkind))
7458 {
7459 bool has_domain_constraints;
7460 bool has_missing = false;
7461
7462 /*
7463 * For an identity column, we can't use build_column_default(),
7464 * because the sequence ownership isn't set yet. So do it manually.
7465 */
7466 if (colDef->identity)
7467 {
7469
7470 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7471 nve->typeId = attribute->atttypid;
7472
7473 defval = (Expr *) nve;
7474 }
7475 else
7476 defval = (Expr *) build_column_default(rel, attribute->attnum);
7477
7478 /* Build CoerceToDomain(NULL) expression if needed */
7479 has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7480 if (!defval && has_domain_constraints)
7481 {
7482 Oid baseTypeId;
7483 int32 baseTypeMod;
7484 Oid baseTypeColl;
7485
7486 baseTypeMod = attribute->atttypmod;
7487 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7488 baseTypeColl = get_typcollation(baseTypeId);
7489 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7490 defval = (Expr *) coerce_to_target_type(NULL,
7491 (Node *) defval,
7492 baseTypeId,
7493 attribute->atttypid,
7494 attribute->atttypmod,
7497 -1);
7498 if (defval == NULL) /* should not happen */
7499 elog(ERROR, "failed to coerce base type to domain");
7500 }
7501
7502 if (defval)
7503 {
7505
7506 /* Prepare defval for execution, either here or in Phase 3 */
7507 defval = expression_planner(defval);
7508
7509 /* Add the new default to the newvals list */
7511 newval->attnum = attribute->attnum;
7512 newval->expr = defval;
7513 newval->is_generated = (colDef->generated != '\0');
7514
7515 tab->newvals = lappend(tab->newvals, newval);
7516
7517 /*
7518 * Attempt to skip a complete table rewrite by storing the
7519 * specified DEFAULT value outside of the heap. This is only
7520 * allowed for plain relations and non-generated columns, and the
7521 * default expression can't be volatile (stable is OK). Note that
7522 * contain_volatile_functions deems CoerceToDomain immutable, but
7523 * here we consider that coercion to a domain with constraints is
7524 * volatile; else it might fail even when the table is empty.
7525 */
7526 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7527 !colDef->generated &&
7528 !has_domain_constraints &&
7529 !contain_volatile_functions((Node *) defval))
7530 {
7531 EState *estate;
7532 ExprState *exprState;
7533 Datum missingval;
7534 bool missingIsNull;
7535
7536 /* Evaluate the default expression */
7537 estate = CreateExecutorState();
7538 exprState = ExecPrepareExpr(defval, estate);
7539 missingval = ExecEvalExpr(exprState,
7540 GetPerTupleExprContext(estate),
7541 &missingIsNull);
7542 /* If it turns out NULL, nothing to do; else store it */
7543 if (!missingIsNull)
7544 {
7545 StoreAttrMissingVal(rel, attribute->attnum, missingval);
7546 /* Make the additional catalog change visible */
7548 has_missing = true;
7549 }
7550 FreeExecutorState(estate);
7551 }
7552 else
7553 {
7554 /*
7555 * Failed to use missing mode. We have to do a table rewrite
7556 * to install the value --- unless it's a virtual generated
7557 * column.
7558 */
7559 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7561 }
7562 }
7563
7564 if (!has_missing)
7565 {
7566 /*
7567 * If the new column is NOT NULL, and there is no missing value,
7568 * tell Phase 3 it needs to check for NULLs.
7569 */
7570 tab->verify_new_notnull |= colDef->is_not_null;
7571 }
7572 }
7573
7574 /*
7575 * Add needed dependency entries for the new column.
7576 */
7577 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7578 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7579
7580 /*
7581 * Propagate to children as appropriate. Unlike most other ALTER
7582 * routines, we have to do this one level of recursion at a time; we can't
7583 * use find_all_inheritors to do it in one pass.
7584 */
7585 children =
7587
7588 /*
7589 * If we are told not to recurse, there had better not be any child
7590 * tables; else the addition would put them out of step.
7591 */
7592 if (children && !recurse)
7593 ereport(ERROR,
7594 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7595 errmsg("column must be added to child tables too")));
7596
7597 /* Children should see column as singly inherited */
7598 if (!recursing)
7599 {
7600 childcmd = copyObject(*cmd);
7601 colDef = castNode(ColumnDef, childcmd->def);
7602 colDef->inhcount = 1;
7603 colDef->is_local = false;
7604 }
7605 else
7606 childcmd = *cmd; /* no need to copy again */
7607
7608 foreach(child, children)
7609 {
7610 Oid childrelid = lfirst_oid(child);
7611 Relation childrel;
7612 AlteredTableInfo *childtab;
7613
7614 /* find_inheritance_children already got lock */
7615 childrel = table_open(childrelid, NoLock);
7616 CheckAlterTableIsSafe(childrel);
7617
7618 /* Find or create work queue entry for this table */
7619 childtab = ATGetQueueEntry(wqueue, childrel);
7620
7621 /* Recurse to child; return value is ignored */
7622 ATExecAddColumn(wqueue, childtab, childrel,
7623 &childcmd, recurse, true,
7624 lockmode, cur_pass, context);
7625
7626 table_close(childrel, NoLock);
7627 }
7628
7629 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7630 return address;
7631}
7632
7633/*
7634 * If a new or renamed column will collide with the name of an existing
7635 * column and if_not_exists is false then error out, else do nothing.
7636 */
7637static bool
7639 bool if_not_exists)
7640{
7641 HeapTuple attTuple;
7642 int attnum;
7643
7644 /*
7645 * this test is deliberately not attisdropped-aware, since if one tries to
7646 * add a column matching a dropped column name, it's gonna fail anyway.
7647 */
7648 attTuple = SearchSysCache2(ATTNAME,
7650 PointerGetDatum(colname));
7651 if (!HeapTupleIsValid(attTuple))
7652 return true;
7653
7654 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7655 ReleaseSysCache(attTuple);
7656
7657 /*
7658 * We throw a different error message for conflicts with system column
7659 * names, since they are normally not shown and the user might otherwise
7660 * be confused about the reason for the conflict.
7661 */
7662 if (attnum <= 0)
7663 ereport(ERROR,
7664 (errcode(ERRCODE_DUPLICATE_COLUMN),
7665 errmsg("column name \"%s\" conflicts with a system column name",
7666 colname)));
7667 else
7668 {
7669 if (if_not_exists)
7670 {
7672 (errcode(ERRCODE_DUPLICATE_COLUMN),
7673 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7674 colname, RelationGetRelationName(rel))));
7675 return false;
7676 }
7677
7678 ereport(ERROR,
7679 (errcode(ERRCODE_DUPLICATE_COLUMN),
7680 errmsg("column \"%s\" of relation \"%s\" already exists",
7681 colname, RelationGetRelationName(rel))));
7682 }
7683
7684 return true;
7685}
7686
7687/*
7688 * Install a column's dependency on its datatype.
7689 */
7690static void
7692{
7693 ObjectAddress myself,
7694 referenced;
7695
7696 myself.classId = RelationRelationId;
7697 myself.objectId = relid;
7698 myself.objectSubId = attnum;
7699 referenced.classId = TypeRelationId;
7700 referenced.objectId = typid;
7701 referenced.objectSubId = 0;
7702 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7703}
7704
7705/*
7706 * Install a column's dependency on its collation.
7707 */
7708static void
7710{
7711 ObjectAddress myself,
7712 referenced;
7713
7714 /* We know the default collation is pinned, so don't bother recording it */
7715 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7716 {
7717 myself.classId = RelationRelationId;
7718 myself.objectId = relid;
7719 myself.objectSubId = attnum;
7720 referenced.classId = CollationRelationId;
7721 referenced.objectId = collid;
7722 referenced.objectSubId = 0;
7723 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7724 }
7725}
7726
7727/*
7728 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7729 *
7730 * Return the address of the modified column. If the column was already
7731 * nullable, InvalidObjectAddress is returned.
7732 */
7733static ObjectAddress
7734ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7735 LOCKMODE lockmode)
7736{
7737 HeapTuple tuple;
7738 HeapTuple conTup;
7739 Form_pg_attribute attTup;
7741 Relation attr_rel;
7742 ObjectAddress address;
7743
7744 /*
7745 * lookup the attribute
7746 */
7747 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7748
7749 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7750 if (!HeapTupleIsValid(tuple))
7751 ereport(ERROR,
7752 (errcode(ERRCODE_UNDEFINED_COLUMN),
7753 errmsg("column \"%s\" of relation \"%s\" does not exist",
7754 colName, RelationGetRelationName(rel))));
7755 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7756 attnum = attTup->attnum;
7757 ObjectAddressSubSet(address, RelationRelationId,
7758 RelationGetRelid(rel), attnum);
7759
7760 /* If the column is already nullable there's nothing to do. */
7761 if (!attTup->attnotnull)
7762 {
7763 table_close(attr_rel, RowExclusiveLock);
7764 return InvalidObjectAddress;
7765 }
7766
7767 /* Prevent them from altering a system attribute */
7768 if (attnum <= 0)
7769 ereport(ERROR,
7770 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7771 errmsg("cannot alter system column \"%s\"",
7772 colName)));
7773
7774 if (attTup->attidentity)
7775 ereport(ERROR,
7776 (errcode(ERRCODE_SYNTAX_ERROR),
7777 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7778 colName, RelationGetRelationName(rel))));
7779
7780 /*
7781 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7782 */
7783 if (rel->rd_rel->relispartition)
7784 {
7785 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7786 Relation parent = table_open(parentId, AccessShareLock);
7787 TupleDesc tupDesc = RelationGetDescr(parent);
7788 AttrNumber parent_attnum;
7789
7790 parent_attnum = get_attnum(parentId, colName);
7791 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7792 ereport(ERROR,
7793 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7794 errmsg("column \"%s\" is marked NOT NULL in parent table",
7795 colName)));
7797 }
7798
7799 /*
7800 * Find the constraint that makes this column NOT NULL, and drop it.
7801 * dropconstraint_internal() resets attnotnull.
7802 */
7804 if (conTup == NULL)
7805 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7806 colName, RelationGetRelationName(rel));
7807
7808 /* The normal case: we have a pg_constraint row, remove it */
7809 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7810 false, lockmode);
7811 heap_freetuple(conTup);
7812
7813 InvokeObjectPostAlterHook(RelationRelationId,
7814 RelationGetRelid(rel), attnum);
7815
7816 table_close(attr_rel, RowExclusiveLock);
7817
7818 return address;
7819}
7820
7821/*
7822 * set_attnotnull
7823 * Helper to update/validate the pg_attribute status of a not-null
7824 * constraint
7825 *
7826 * pg_attribute.attnotnull is set true, if it isn't already.
7827 * If queue_validation is true, also set up wqueue to validate the constraint.
7828 * wqueue may be given as NULL when validation is not needed (e.g., on table
7829 * creation).
7830 */
7831static void
7833 bool is_valid, bool queue_validation)
7834{
7835 Form_pg_attribute attr;
7836 CompactAttribute *thisatt;
7837
7838 Assert(!queue_validation || wqueue);
7839
7841
7842 /*
7843 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7844 * attribute.
7845 */
7846 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7847 if (attr->attisdropped)
7848 return;
7849
7850 if (!attr->attnotnull)
7851 {
7852 Relation attr_rel;
7853 HeapTuple tuple;
7854
7855 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7856
7858 if (!HeapTupleIsValid(tuple))
7859 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7860 attnum, RelationGetRelid(rel));
7861
7862 thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7864
7865 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7866
7867 attr->attnotnull = true;
7868 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7869
7870 /*
7871 * If the nullness isn't already proven by validated constraints, have
7872 * ALTER TABLE phase 3 test for it.
7873 */
7874 if (queue_validation && wqueue &&
7876 {
7877 AlteredTableInfo *tab;
7878
7879 tab = ATGetQueueEntry(wqueue, rel);
7880 tab->verify_new_notnull = true;
7881 }
7882
7884
7885 table_close(attr_rel, RowExclusiveLock);
7886 heap_freetuple(tuple);
7887 }
7888 else
7889 {
7891 }
7892}
7893
7894/*
7895 * ALTER TABLE ALTER COLUMN SET NOT NULL
7896 *
7897 * Add a not-null constraint to a single table and its children. Returns
7898 * the address of the constraint added to the parent relation, if one gets
7899 * added, or InvalidObjectAddress otherwise.
7900 *
7901 * We must recurse to child tables during execution, rather than using
7902 * ALTER TABLE's normal prep-time recursion.
7903 */
7904static ObjectAddress
7905ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7906 bool recurse, bool recursing, LOCKMODE lockmode)
7907{
7908 HeapTuple tuple;
7910 ObjectAddress address;
7911 Constraint *constraint;
7912 CookedConstraint *ccon;
7913 List *cooked;
7914 bool is_no_inherit = false;
7915
7916 /* Guard against stack overflow due to overly deep inheritance tree. */
7918
7919 /* At top level, permission check was done in ATPrepCmd, else do it */
7920 if (recursing)
7921 {
7924 Assert(conName != NULL);
7925 }
7926
7927 attnum = get_attnum(RelationGetRelid(rel), colName);
7929 ereport(ERROR,
7930 (errcode(ERRCODE_UNDEFINED_COLUMN),
7931 errmsg("column \"%s\" of relation \"%s\" does not exist",
7932 colName, RelationGetRelationName(rel))));
7933
7934 /* Prevent them from altering a system attribute */
7935 if (attnum <= 0)
7936 ereport(ERROR,
7937 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7938 errmsg("cannot alter system column \"%s\"",
7939 colName)));
7940
7941 /* See if there's already a constraint */
7943 if (HeapTupleIsValid(tuple))
7944 {
7946 bool changed = false;
7947
7948 /*
7949 * Don't let a NO INHERIT constraint be changed into inherit.
7950 */
7951 if (conForm->connoinherit && recurse)
7952 ereport(ERROR,
7953 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7954 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7955 NameStr(conForm->conname),
7957
7958 /*
7959 * If we find an appropriate constraint, we're almost done, but just
7960 * need to change some properties on it: if we're recursing, increment
7961 * coninhcount; if not, set conislocal if not already set.
7962 */
7963 if (recursing)
7964 {
7965 if (pg_add_s16_overflow(conForm->coninhcount, 1,
7966 &conForm->coninhcount))
7967 ereport(ERROR,
7968 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7969 errmsg("too many inheritance parents"));
7970 changed = true;
7971 }
7972 else if (!conForm->conislocal)
7973 {
7974 conForm->conislocal = true;
7975 changed = true;
7976 }
7977 else if (!conForm->convalidated)
7978 {
7979 /*
7980 * Flip attnotnull and convalidated, and also validate the
7981 * constraint.
7982 */
7983 return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
7984 recurse, recursing, lockmode);
7985 }
7986
7987 if (changed)
7988 {
7989 Relation constr_rel;
7990
7991 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7992
7993 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7994 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7995 table_close(constr_rel, RowExclusiveLock);
7996 }
7997
7998 if (changed)
7999 return address;
8000 else
8001 return InvalidObjectAddress;
8002 }
8003
8004 /*
8005 * If we're asked not to recurse, and children exist, raise an error for
8006 * partitioned tables. For inheritance, we act as if NO INHERIT had been
8007 * specified.
8008 */
8009 if (!recurse &&
8011 NoLock) != NIL)
8012 {
8013 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8014 ereport(ERROR,
8015 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8016 errmsg("constraint must be added to child tables too"),
8017 errhint("Do not specify the ONLY keyword."));
8018 else
8019 is_no_inherit = true;
8020 }
8021
8022 /*
8023 * No constraint exists; we must add one. First determine a name to use,
8024 * if we haven't already.
8025 */
8026 if (!recursing)
8027 {
8028 Assert(conName == NULL);
8030 colName, "not_null",
8032 NIL);
8033 }
8034
8035 constraint = makeNotNullConstraint(makeString(colName));
8036 constraint->is_no_inherit = is_no_inherit;
8037 constraint->conname = conName;
8038
8039 /* and do it */
8040 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8041 false, !recursing, false, NULL);
8042 ccon = linitial(cooked);
8043 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8044
8045 InvokeObjectPostAlterHook(RelationRelationId,
8046 RelationGetRelid(rel), attnum);
8047
8048 /* Mark pg_attribute.attnotnull for the column and queue validation */
8049 set_attnotnull(wqueue, rel, attnum, true, true);
8050
8051 /*
8052 * Recurse to propagate the constraint to children that don't have one.
8053 */
8054 if (recurse)
8055 {
8056 List *children;
8057
8059 lockmode);
8060
8061 foreach_oid(childoid, children)
8062 {
8063 Relation childrel = table_open(childoid, NoLock);
8064
8066
8067 ATExecSetNotNull(wqueue, childrel, conName, colName,
8068 recurse, true, lockmode);
8069 table_close(childrel, NoLock);
8070 }
8071 }
8072
8073 return address;
8074}
8075
8076/*
8077 * NotNullImpliedByRelConstraints
8078 * Does rel's existing constraints imply NOT NULL for the given attribute?
8079 */
8080static bool
8082{
8083 NullTest *nnulltest = makeNode(NullTest);
8084
8085 nnulltest->arg = (Expr *) makeVar(1,
8086 attr->attnum,
8087 attr->atttypid,
8088 attr->atttypmod,
8089 attr->attcollation,
8090 0);
8091 nnulltest->nulltesttype = IS_NOT_NULL;
8092
8093 /*
8094 * argisrow = false is correct even for a composite column, because
8095 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8096 * case, just IS DISTINCT FROM NULL.
8097 */
8098 nnulltest->argisrow = false;
8099 nnulltest->location = -1;
8100
8101 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8102 {
8104 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8105 RelationGetRelationName(rel), NameStr(attr->attname))));
8106 return true;
8107 }
8108
8109 return false;
8110}
8111
8112/*
8113 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8114 *
8115 * Return the address of the affected column.
8116 */
8117static ObjectAddress
8118ATExecColumnDefault(Relation rel, const char *colName,
8119 Node *newDefault, LOCKMODE lockmode)
8120{
8121 TupleDesc tupdesc = RelationGetDescr(rel);
8123 ObjectAddress address;
8124
8125 /*
8126 * get the number of the attribute
8127 */
8128 attnum = get_attnum(RelationGetRelid(rel), colName);
8130 ereport(ERROR,
8131 (errcode(ERRCODE_UNDEFINED_COLUMN),
8132 errmsg("column \"%s\" of relation \"%s\" does not exist",
8133 colName, RelationGetRelationName(rel))));
8134
8135 /* Prevent them from altering a system attribute */
8136 if (attnum <= 0)
8137 ereport(ERROR,
8138 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8139 errmsg("cannot alter system column \"%s\"",
8140 colName)));
8141
8142 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8143 ereport(ERROR,
8144 (errcode(ERRCODE_SYNTAX_ERROR),
8145 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8146 colName, RelationGetRelationName(rel)),
8147 /* translator: %s is an SQL ALTER command */
8148 newDefault ? 0 : errhint("Use %s instead.",
8149 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8150
8151 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8152 ereport(ERROR,
8153 (errcode(ERRCODE_SYNTAX_ERROR),
8154 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8155 colName, RelationGetRelationName(rel)),
8156 newDefault ?
8157 /* translator: %s is an SQL ALTER command */
8158 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8159 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8160 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8161
8162 /*
8163 * Remove any old default for the column. We use RESTRICT here for
8164 * safety, but at present we do not expect anything to depend on the
8165 * default.
8166 *
8167 * We treat removing the existing default as an internal operation when it
8168 * is preparatory to adding a new default, but as a user-initiated
8169 * operation when the user asked for a drop.
8170 */
8172 newDefault != NULL);
8173
8174 if (newDefault)
8175 {
8176 /* SET DEFAULT */
8177 RawColumnDefault *rawEnt;
8178
8179 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8180 rawEnt->attnum = attnum;
8181 rawEnt->raw_default = newDefault;
8182 rawEnt->generated = '\0';
8183
8184 /*
8185 * This function is intended for CREATE TABLE, so it processes a
8186 * _list_ of defaults, but we just do one.
8187 */
8189 false, true, false, NULL);
8190 }
8191
8192 ObjectAddressSubSet(address, RelationRelationId,
8193 RelationGetRelid(rel), attnum);
8194 return address;
8195}
8196
8197/*
8198 * Add a pre-cooked default expression.
8199 *
8200 * Return the address of the affected column.
8201 */
8202static ObjectAddress
8204 Node *newDefault)
8205{
8206 ObjectAddress address;
8207
8208 /* We assume no checking is required */
8209
8210 /*
8211 * Remove any old default for the column. We use RESTRICT here for
8212 * safety, but at present we do not expect anything to depend on the
8213 * default. (In ordinary cases, there could not be a default in place
8214 * anyway, but it's possible when combining LIKE with inheritance.)
8215 */
8217 true);
8218
8219 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8220
8221 ObjectAddressSubSet(address, RelationRelationId,
8222 RelationGetRelid(rel), attnum);
8223 return address;
8224}
8225
8226/*
8227 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8228 *
8229 * Return the address of the affected column.
8230 */
8231static ObjectAddress
8232ATExecAddIdentity(Relation rel, const char *colName,
8233 Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8234{
8235 Relation attrelation;
8236 HeapTuple tuple;
8237 Form_pg_attribute attTup;
8239 ObjectAddress address;
8240 ColumnDef *cdef = castNode(ColumnDef, def);
8241 bool ispartitioned;
8242
8243 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8244 if (ispartitioned && !recurse)
8245 ereport(ERROR,
8246 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8247 errmsg("cannot add identity to a column of only the partitioned table"),
8248 errhint("Do not specify the ONLY keyword.")));
8249
8250 if (rel->rd_rel->relispartition && !recursing)
8251 ereport(ERROR,
8252 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8253 errmsg("cannot add identity to a column of a partition"));
8254
8255 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8256
8257 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8258 if (!HeapTupleIsValid(tuple))
8259 ereport(ERROR,
8260 (errcode(ERRCODE_UNDEFINED_COLUMN),
8261 errmsg("column \"%s\" of relation \"%s\" does not exist",
8262 colName, RelationGetRelationName(rel))));
8263 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8264 attnum = attTup->attnum;
8265
8266 /* Can't alter a system attribute */
8267 if (attnum <= 0)
8268 ereport(ERROR,
8269 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8270 errmsg("cannot alter system column \"%s\"",
8271 colName)));
8272
8273 /*
8274 * Creating a column as identity implies NOT NULL, so adding the identity
8275 * to an existing column that is not NOT NULL would create a state that
8276 * cannot be reproduced without contortions.
8277 */
8278 if (!attTup->attnotnull)
8279 ereport(ERROR,
8280 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8281 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8282 colName, RelationGetRelationName(rel))));
8283
8284 /*
8285 * On the other hand, if a not-null constraint exists, then verify that
8286 * it's compatible.
8287 */
8288 if (attTup->attnotnull)
8289 {
8290 HeapTuple contup;
8291 Form_pg_constraint conForm;
8292
8294 attnum);
8295 if (!HeapTupleIsValid(contup))
8296 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8297 colName, RelationGetRelationName(rel));
8298
8299 conForm = (Form_pg_constraint) GETSTRUCT(contup);
8300 if (!conForm->convalidated)
8301 ereport(ERROR,
8302 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8303 errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8304 NameStr(conForm->conname), RelationGetRelationName(rel)),
8305 errhint("You might need to validate it using %s.",
8306 "ALTER TABLE ... VALIDATE CONSTRAINT"));
8307 }
8308
8309 if (attTup->attidentity)
8310 ereport(ERROR,
8311 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8312 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8313 colName, RelationGetRelationName(rel))));
8314
8315 if (attTup->atthasdef)
8316 ereport(ERROR,
8317 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8318 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8319 colName, RelationGetRelationName(rel))));
8320
8321 attTup->attidentity = cdef->identity;
8322 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8323
8324 InvokeObjectPostAlterHook(RelationRelationId,
8325 RelationGetRelid(rel),
8326 attTup->attnum);
8327 ObjectAddressSubSet(address, RelationRelationId,
8328 RelationGetRelid(rel), attnum);
8329 heap_freetuple(tuple);
8330
8331 table_close(attrelation, RowExclusiveLock);
8332
8333 /*
8334 * Recurse to propagate the identity column to partitions. Identity is
8335 * not inherited in regular inheritance children.
8336 */
8337 if (recurse && ispartitioned)
8338 {
8339 List *children;
8340 ListCell *lc;
8341
8342 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8343
8344 foreach(lc, children)
8345 {
8346 Relation childrel;
8347
8348 childrel = table_open(lfirst_oid(lc), NoLock);
8349 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8350 table_close(childrel, NoLock);
8351 }
8352 }
8353
8354 return address;
8355}
8356
8357/*
8358 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8359 *
8360 * Return the address of the affected column.
8361 */
8362static ObjectAddress
8363ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8364 LOCKMODE lockmode, bool recurse, bool recursing)
8365{
8367 DefElem *generatedEl = NULL;
8368 HeapTuple tuple;
8369 Form_pg_attribute attTup;
8371 Relation attrelation;
8372 ObjectAddress address;
8373 bool ispartitioned;
8374
8375 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8376 if (ispartitioned && !recurse)
8377 ereport(ERROR,
8378 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8379 errmsg("cannot change identity column of only the partitioned table"),
8380 errhint("Do not specify the ONLY keyword.")));
8381
8382 if (rel->rd_rel->relispartition && !recursing)
8383 ereport(ERROR,
8384 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8385 errmsg("cannot change identity column of a partition"));
8386
8387 foreach(option, castNode(List, def))
8388 {
8389 DefElem *defel = lfirst_node(DefElem, option);
8390
8391 if (strcmp(defel->defname, "generated") == 0)
8392 {
8393 if (generatedEl)
8394 ereport(ERROR,
8395 (errcode(ERRCODE_SYNTAX_ERROR),
8396 errmsg("conflicting or redundant options")));
8397 generatedEl = defel;
8398 }
8399 else
8400 elog(ERROR, "option \"%s\" not recognized",
8401 defel->defname);
8402 }
8403
8404 /*
8405 * Even if there is nothing to change here, we run all the checks. There
8406 * will be a subsequent ALTER SEQUENCE that relies on everything being
8407 * there.
8408 */
8409
8410 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8411 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8412 if (!HeapTupleIsValid(tuple))
8413 ereport(ERROR,
8414 (errcode(ERRCODE_UNDEFINED_COLUMN),
8415 errmsg("column \"%s\" of relation \"%s\" does not exist",
8416 colName, RelationGetRelationName(rel))));
8417
8418 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8419 attnum = attTup->attnum;
8420
8421 if (attnum <= 0)
8422 ereport(ERROR,
8423 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8424 errmsg("cannot alter system column \"%s\"",
8425 colName)));
8426
8427 if (!attTup->attidentity)
8428 ereport(ERROR,
8429 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8430 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8431 colName, RelationGetRelationName(rel))));
8432
8433 if (generatedEl)
8434 {
8435 attTup->attidentity = defGetInt32(generatedEl);
8436 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8437
8438 InvokeObjectPostAlterHook(RelationRelationId,
8439 RelationGetRelid(rel),
8440 attTup->attnum);
8441 ObjectAddressSubSet(address, RelationRelationId,
8442 RelationGetRelid(rel), attnum);
8443 }
8444 else
8445 address = InvalidObjectAddress;
8446
8447 heap_freetuple(tuple);
8448 table_close(attrelation, RowExclusiveLock);
8449
8450 /*
8451 * Recurse to propagate the identity change to partitions. Identity is not
8452 * inherited in regular inheritance children.
8453 */
8454 if (generatedEl && recurse && ispartitioned)
8455 {
8456 List *children;
8457 ListCell *lc;
8458
8459 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8460
8461 foreach(lc, children)
8462 {
8463 Relation childrel;
8464
8465 childrel = table_open(lfirst_oid(lc), NoLock);
8466 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8467 table_close(childrel, NoLock);
8468 }
8469 }
8470
8471 return address;
8472}
8473
8474/*
8475 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8476 *
8477 * Return the address of the affected column.
8478 */
8479static ObjectAddress
8480ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8481 bool recurse, bool recursing)
8482{
8483 HeapTuple tuple;
8484 Form_pg_attribute attTup;
8486 Relation attrelation;
8487 ObjectAddress address;
8488 Oid seqid;
8489 ObjectAddress seqaddress;
8490 bool ispartitioned;
8491
8492 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8493 if (ispartitioned && !recurse)
8494 ereport(ERROR,
8495 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8496 errmsg("cannot drop identity from a column of only the partitioned table"),
8497 errhint("Do not specify the ONLY keyword.")));
8498
8499 if (rel->rd_rel->relispartition && !recursing)
8500 ereport(ERROR,
8501 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8502 errmsg("cannot drop identity from a column of a partition"));
8503
8504 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8505 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8506 if (!HeapTupleIsValid(tuple))
8507 ereport(ERROR,
8508 (errcode(ERRCODE_UNDEFINED_COLUMN),
8509 errmsg("column \"%s\" of relation \"%s\" does not exist",
8510 colName, RelationGetRelationName(rel))));
8511
8512 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8513 attnum = attTup->attnum;
8514
8515 if (attnum <= 0)
8516 ereport(ERROR,
8517 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8518 errmsg("cannot alter system column \"%s\"",
8519 colName)));
8520
8521 if (!attTup->attidentity)
8522 {
8523 if (!missing_ok)
8524 ereport(ERROR,
8525 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8526 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8527 colName, RelationGetRelationName(rel))));
8528 else
8529 {
8531 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8532 colName, RelationGetRelationName(rel))));
8533 heap_freetuple(tuple);
8534 table_close(attrelation, RowExclusiveLock);
8535 return InvalidObjectAddress;
8536 }
8537 }
8538
8539 attTup->attidentity = '\0';
8540 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8541
8542 InvokeObjectPostAlterHook(RelationRelationId,
8543 RelationGetRelid(rel),
8544 attTup->attnum);
8545 ObjectAddressSubSet(address, RelationRelationId,
8546 RelationGetRelid(rel), attnum);
8547 heap_freetuple(tuple);
8548
8549 table_close(attrelation, RowExclusiveLock);
8550
8551 /*
8552 * Recurse to drop the identity from column in partitions. Identity is
8553 * not inherited in regular inheritance children so ignore them.
8554 */
8555 if (recurse && ispartitioned)
8556 {
8557 List *children;
8558 ListCell *lc;
8559
8560 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8561
8562 foreach(lc, children)
8563 {
8564 Relation childrel;
8565
8566 childrel = table_open(lfirst_oid(lc), NoLock);
8567 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8568 table_close(childrel, NoLock);
8569 }
8570 }
8571
8572 if (!recursing)
8573 {
8574 /* drop the internal sequence */
8575 seqid = getIdentitySequence(rel, attnum, false);
8576 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8577 RelationRelationId, DEPENDENCY_INTERNAL);
8579 seqaddress.classId = RelationRelationId;
8580 seqaddress.objectId = seqid;
8581 seqaddress.objectSubId = 0;
8583 }
8584
8585 return address;
8586}
8587
8588/*
8589 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8590 *
8591 * Return the address of the affected column.
8592 */
8593static ObjectAddress
8594ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8595 Node *newExpr, LOCKMODE lockmode)
8596{
8597 HeapTuple tuple;
8598 Form_pg_attribute attTup;
8600 char attgenerated;
8601 bool rewrite;
8602 Oid attrdefoid;
8603 ObjectAddress address;
8604 Expr *defval;
8606 RawColumnDefault *rawEnt;
8607
8608 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8609 if (!HeapTupleIsValid(tuple))
8610 ereport(ERROR,
8611 (errcode(ERRCODE_UNDEFINED_COLUMN),
8612 errmsg("column \"%s\" of relation \"%s\" does not exist",
8613 colName, RelationGetRelationName(rel))));
8614
8615 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8616
8617 attnum = attTup->attnum;
8618 if (attnum <= 0)
8619 ereport(ERROR,
8620 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8621 errmsg("cannot alter system column \"%s\"",
8622 colName)));
8623
8624 attgenerated = attTup->attgenerated;
8625 if (!attgenerated)
8626 ereport(ERROR,
8627 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8628 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8629 colName, RelationGetRelationName(rel))));
8630
8631 /*
8632 * TODO: This could be done, just need to recheck any constraints
8633 * afterwards.
8634 */
8635 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8636 rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8637 ereport(ERROR,
8638 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8639 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
8640 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8641 colName, RelationGetRelationName(rel))));
8642
8643 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8644 tab->verify_new_notnull = true;
8645
8646 /*
8647 * We need to prevent this because a change of expression could affect a
8648 * row filter and inject expressions that are not permitted in a row
8649 * filter. XXX We could try to have a more precise check to catch only
8650 * publications with row filters, or even re-verify the row filter
8651 * expressions.
8652 */
8653 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8655 ereport(ERROR,
8656 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8657 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8658 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8659 colName, RelationGetRelationName(rel))));
8660
8661 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8662
8663 ReleaseSysCache(tuple);
8664
8665 if (rewrite)
8666 {
8667 /*
8668 * Clear all the missing values if we're rewriting the table, since
8669 * this renders them pointless.
8670 */
8672
8673 /* make sure we don't conflict with later attribute modifications */
8675
8676 /*
8677 * Find everything that depends on the column (constraints, indexes,
8678 * etc), and record enough information to let us recreate the objects
8679 * after rewrite.
8680 */
8682 }
8683
8684 /*
8685 * Drop the dependency records of the GENERATED expression, in particular
8686 * its INTERNAL dependency on the column, which would otherwise cause
8687 * dependency.c to refuse to perform the deletion.
8688 */
8689 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8690 if (!OidIsValid(attrdefoid))
8691 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8692 RelationGetRelid(rel), attnum);
8693 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8694
8695 /* Make above changes visible */
8697
8698 /*
8699 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8700 * safety, but at present we do not expect anything to depend on the
8701 * expression.
8702 */
8704 false, false);
8705
8706 /* Prepare to store the new expression, in the catalogs */
8707 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8708 rawEnt->attnum = attnum;
8709 rawEnt->raw_default = newExpr;
8710 rawEnt->generated = attgenerated;
8711
8712 /* Store the generated expression */
8714 false, true, false, NULL);
8715
8716 /* Make above new expression visible */
8718
8719 if (rewrite)
8720 {
8721 /* Prepare for table rewrite */
8722 defval = (Expr *) build_column_default(rel, attnum);
8723
8725 newval->attnum = attnum;
8726 newval->expr = expression_planner(defval);
8727 newval->is_generated = true;
8728
8729 tab->newvals = lappend(tab->newvals, newval);
8731 }
8732
8733 /* Drop any pg_statistic entry for the column */
8735
8736 InvokeObjectPostAlterHook(RelationRelationId,
8737 RelationGetRelid(rel), attnum);
8738
8739 ObjectAddressSubSet(address, RelationRelationId,
8740 RelationGetRelid(rel), attnum);
8741 return address;
8742}
8743
8744/*
8745 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8746 */
8747static void
8748ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8749{
8750 /*
8751 * Reject ONLY if there are child tables. We could implement this, but it
8752 * is a bit complicated. GENERATED clauses must be attached to the column
8753 * definition and cannot be added later like DEFAULT, so if a child table
8754 * has a generation expression that the parent does not have, the child
8755 * column will necessarily be an attislocal column. So to implement ONLY
8756 * here, we'd need extra code to update attislocal of the direct child
8757 * tables, somewhat similar to how DROP COLUMN does it, so that the
8758 * resulting state can be properly dumped and restored.
8759 */
8760 if (!recurse &&
8762 ereport(ERROR,
8763 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8764 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8765
8766 /*
8767 * Cannot drop generation expression from inherited columns.
8768 */
8769 if (!recursing)
8770 {
8771 HeapTuple tuple;
8772 Form_pg_attribute attTup;
8773
8775 if (!HeapTupleIsValid(tuple))
8776 ereport(ERROR,
8777 (errcode(ERRCODE_UNDEFINED_COLUMN),
8778 errmsg("column \"%s\" of relation \"%s\" does not exist",
8779 cmd->name, RelationGetRelationName(rel))));
8780
8781 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8782
8783 if (attTup->attinhcount > 0)
8784 ereport(ERROR,
8785 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8786 errmsg("cannot drop generation expression from inherited column")));
8787 }
8788}
8789
8790/*
8791 * Return the address of the affected column.
8792 */
8793static ObjectAddress
8794ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8795{
8796 HeapTuple tuple;
8797 Form_pg_attribute attTup;
8799 Relation attrelation;
8800 Oid attrdefoid;
8801 ObjectAddress address;
8802
8803 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8804 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8805 if (!HeapTupleIsValid(tuple))
8806 ereport(ERROR,
8807 (errcode(ERRCODE_UNDEFINED_COLUMN),
8808 errmsg("column \"%s\" of relation \"%s\" does not exist",
8809 colName, RelationGetRelationName(rel))));
8810
8811 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8812 attnum = attTup->attnum;
8813
8814 if (attnum <= 0)
8815 ereport(ERROR,
8816 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8817 errmsg("cannot alter system column \"%s\"",
8818 colName)));
8819
8820 /*
8821 * TODO: This could be done, but it would need a table rewrite to
8822 * materialize the generated values. Note that for the time being, we
8823 * still error with missing_ok, so that we don't silently leave the column
8824 * as generated.
8825 */
8826 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8827 ereport(ERROR,
8828 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8829 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8830 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8831 colName, RelationGetRelationName(rel))));
8832
8833 if (!attTup->attgenerated)
8834 {
8835 if (!missing_ok)
8836 ereport(ERROR,
8837 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8838 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8839 colName, RelationGetRelationName(rel))));
8840 else
8841 {
8843 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8844 colName, RelationGetRelationName(rel))));
8845 heap_freetuple(tuple);
8846 table_close(attrelation, RowExclusiveLock);
8847 return InvalidObjectAddress;
8848 }
8849 }
8850
8851 /*
8852 * Mark the column as no longer generated. (The atthasdef flag needs to
8853 * get cleared too, but RemoveAttrDefault will handle that.)
8854 */
8855 attTup->attgenerated = '\0';
8856 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8857
8858 InvokeObjectPostAlterHook(RelationRelationId,
8859 RelationGetRelid(rel),
8860 attnum);
8861 heap_freetuple(tuple);
8862
8863 table_close(attrelation, RowExclusiveLock);
8864
8865 /*
8866 * Drop the dependency records of the GENERATED expression, in particular
8867 * its INTERNAL dependency on the column, which would otherwise cause
8868 * dependency.c to refuse to perform the deletion.
8869 */
8870 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8871 if (!OidIsValid(attrdefoid))
8872 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8873 RelationGetRelid(rel), attnum);
8874 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8875
8876 /* Make above changes visible */
8878
8879 /*
8880 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8881 * safety, but at present we do not expect anything to depend on the
8882 * default.
8883 */
8885 false, false);
8886
8887 ObjectAddressSubSet(address, RelationRelationId,
8888 RelationGetRelid(rel), attnum);
8889 return address;
8890}
8891
8892/*
8893 * ALTER TABLE ALTER COLUMN SET STATISTICS
8894 *
8895 * Return value is the address of the modified column
8896 */
8897static ObjectAddress
8898ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8899{
8900 int newtarget = 0;
8901 bool newtarget_default;
8902 Relation attrelation;
8903 HeapTuple tuple,
8904 newtuple;
8905 Form_pg_attribute attrtuple;
8907 ObjectAddress address;
8908 Datum repl_val[Natts_pg_attribute];
8909 bool repl_null[Natts_pg_attribute];
8910 bool repl_repl[Natts_pg_attribute];
8911
8912 /*
8913 * We allow referencing columns by numbers only for indexes, since table
8914 * column numbers could contain gaps if columns are later dropped.
8915 */
8916 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8917 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8918 !colName)
8919 ereport(ERROR,
8920 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8921 errmsg("cannot refer to non-index column by number")));
8922
8923 /* -1 was used in previous versions for the default setting */
8924 if (newValue && intVal(newValue) != -1)
8925 {
8926 newtarget = intVal(newValue);
8927 newtarget_default = false;
8928 }
8929 else
8930 newtarget_default = true;
8931
8932 if (!newtarget_default)
8933 {
8934 /*
8935 * Limit target to a sane range
8936 */
8937 if (newtarget < 0)
8938 {
8939 ereport(ERROR,
8940 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8941 errmsg("statistics target %d is too low",
8942 newtarget)));
8943 }
8944 else if (newtarget > MAX_STATISTICS_TARGET)
8945 {
8946 newtarget = MAX_STATISTICS_TARGET;
8948 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8949 errmsg("lowering statistics target to %d",
8950 newtarget)));
8951 }
8952 }
8953
8954 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8955
8956 if (colName)
8957 {
8958 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8959
8960 if (!HeapTupleIsValid(tuple))
8961 ereport(ERROR,
8962 (errcode(ERRCODE_UNDEFINED_COLUMN),
8963 errmsg("column \"%s\" of relation \"%s\" does not exist",
8964 colName, RelationGetRelationName(rel))));
8965 }
8966 else
8967 {
8968 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8969
8970 if (!HeapTupleIsValid(tuple))
8971 ereport(ERROR,
8972 (errcode(ERRCODE_UNDEFINED_COLUMN),
8973 errmsg("column number %d of relation \"%s\" does not exist",
8974 colNum, RelationGetRelationName(rel))));
8975 }
8976
8977 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8978
8979 attnum = attrtuple->attnum;
8980 if (attnum <= 0)
8981 ereport(ERROR,
8982 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8983 errmsg("cannot alter system column \"%s\"",
8984 colName)));
8985
8986 /*
8987 * Prevent this as long as the ANALYZE code skips virtual generated
8988 * columns.
8989 */
8990 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8991 ereport(ERROR,
8992 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8993 errmsg("cannot alter statistics on virtual generated column \"%s\"",
8994 colName)));
8995
8996 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8997 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8998 {
8999 if (attnum > rel->rd_index->indnkeyatts)
9000 ereport(ERROR,
9001 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9002 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9003 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9004 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9005 ereport(ERROR,
9006 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9007 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9008 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9009 errhint("Alter statistics on table column instead.")));
9010 }
9011
9012 /* Build new tuple. */
9013 memset(repl_null, false, sizeof(repl_null));
9014 memset(repl_repl, false, sizeof(repl_repl));
9015 if (!newtarget_default)
9016 repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
9017 else
9018 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
9019 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
9020 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9021 repl_val, repl_null, repl_repl);
9022 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9023
9024 InvokeObjectPostAlterHook(RelationRelationId,
9025 RelationGetRelid(rel),
9026 attrtuple->attnum);
9027 ObjectAddressSubSet(address, RelationRelationId,
9028 RelationGetRelid(rel), attnum);
9029
9030 heap_freetuple(newtuple);
9031
9032 ReleaseSysCache(tuple);
9033
9034 table_close(attrelation, RowExclusiveLock);
9035
9036 return address;
9037}
9038
9039/*
9040 * Return value is the address of the modified column
9041 */
9042static ObjectAddress
9043ATExecSetOptions(Relation rel, const char *colName, Node *options,
9044 bool isReset, LOCKMODE lockmode)
9045{
9046 Relation attrelation;
9047 HeapTuple tuple,
9048 newtuple;
9049 Form_pg_attribute attrtuple;
9051 Datum datum,
9052 newOptions;
9053 bool isnull;
9054 ObjectAddress address;
9055 Datum repl_val[Natts_pg_attribute];
9056 bool repl_null[Natts_pg_attribute];
9057 bool repl_repl[Natts_pg_attribute];
9058
9059 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9060
9061 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9062
9063 if (!HeapTupleIsValid(tuple))
9064 ereport(ERROR,
9065 (errcode(ERRCODE_UNDEFINED_COLUMN),
9066 errmsg("column \"%s\" of relation \"%s\" does not exist",
9067 colName, RelationGetRelationName(rel))));
9068 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9069
9070 attnum = attrtuple->attnum;
9071 if (attnum <= 0)
9072 ereport(ERROR,
9073 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9074 errmsg("cannot alter system column \"%s\"",
9075 colName)));
9076
9077 /* Generate new proposed attoptions (text array) */
9078 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9079 &isnull);
9080 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9081 castNode(List, options), NULL, NULL,
9082 false, isReset);
9083 /* Validate new options */
9084 (void) attribute_reloptions(newOptions, true);
9085
9086 /* Build new tuple. */
9087 memset(repl_null, false, sizeof(repl_null));
9088 memset(repl_repl, false, sizeof(repl_repl));
9089 if (newOptions != (Datum) 0)
9090 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9091 else
9092 repl_null[Anum_pg_attribute_attoptions - 1] = true;
9093 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9094 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9095 repl_val, repl_null, repl_repl);
9096
9097 /* Update system catalog. */
9098 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9099
9100 InvokeObjectPostAlterHook(RelationRelationId,
9101 RelationGetRelid(rel),
9102 attrtuple->attnum);
9103 ObjectAddressSubSet(address, RelationRelationId,
9104 RelationGetRelid(rel), attnum);
9105
9106 heap_freetuple(newtuple);
9107
9108 ReleaseSysCache(tuple);
9109
9110 table_close(attrelation, RowExclusiveLock);
9111
9112 return address;
9113}
9114
9115/*
9116 * Helper function for ATExecSetStorage and ATExecSetCompression
9117 *
9118 * Set the attstorage and/or attcompression fields for index columns
9119 * associated with the specified table column.
9120 */
9121static void
9124 bool setstorage, char newstorage,
9125 bool setcompression, char newcompression,
9126 LOCKMODE lockmode)
9127{
9128 ListCell *lc;
9129
9130 foreach(lc, RelationGetIndexList(rel))
9131 {
9132 Oid indexoid = lfirst_oid(lc);
9133 Relation indrel;
9134 AttrNumber indattnum = 0;
9135 HeapTuple tuple;
9136
9137 indrel = index_open(indexoid, lockmode);
9138
9139 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9140 {
9141 if (indrel->rd_index->indkey.values[i] == attnum)
9142 {
9143 indattnum = i + 1;
9144 break;
9145 }
9146 }
9147
9148 if (indattnum == 0)
9149 {
9150 index_close(indrel, lockmode);
9151 continue;
9152 }
9153
9154 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9155
9156 if (HeapTupleIsValid(tuple))
9157 {
9158 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9159
9160 if (setstorage)
9161 attrtuple->attstorage = newstorage;
9162
9163 if (setcompression)
9164 attrtuple->attcompression = newcompression;
9165
9166 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9167
9168 InvokeObjectPostAlterHook(RelationRelationId,
9169 RelationGetRelid(rel),
9170 attrtuple->attnum);
9171
9172 heap_freetuple(tuple);
9173 }
9174
9175 index_close(indrel, lockmode);
9176 }
9177}
9178
9179/*
9180 * ALTER TABLE ALTER COLUMN SET STORAGE
9181 *
9182 * Return value is the address of the modified column
9183 */
9184static ObjectAddress
9185ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9186{
9187 Relation attrelation;
9188 HeapTuple tuple;
9189 Form_pg_attribute attrtuple;
9191 ObjectAddress address;
9192
9193 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9194
9195 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9196
9197 if (!HeapTupleIsValid(tuple))
9198 ereport(ERROR,
9199 (errcode(ERRCODE_UNDEFINED_COLUMN),
9200 errmsg("column \"%s\" of relation \"%s\" does not exist",
9201 colName, RelationGetRelationName(rel))));
9202 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9203
9204 attnum = attrtuple->attnum;
9205 if (attnum <= 0)
9206 ereport(ERROR,
9207 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9208 errmsg("cannot alter system column \"%s\"",
9209 colName)));
9210
9211 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9212
9213 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9214
9215 InvokeObjectPostAlterHook(RelationRelationId,
9216 RelationGetRelid(rel),
9217 attrtuple->attnum);
9218
9219 /*
9220 * Apply the change to indexes as well (only for simple index columns,
9221 * matching behavior of index.c ConstructTupleDescriptor()).
9222 */
9223 SetIndexStorageProperties(rel, attrelation, attnum,
9224 true, attrtuple->attstorage,
9225 false, 0,
9226 lockmode);
9227
9228 heap_freetuple(tuple);
9229
9230 table_close(attrelation, RowExclusiveLock);
9231
9232 ObjectAddressSubSet(address, RelationRelationId,
9233 RelationGetRelid(rel), attnum);
9234 return address;
9235}
9236
9237
9238/*
9239 * ALTER TABLE DROP COLUMN
9240 *
9241 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9242 * because we have to decide at runtime whether to recurse or not depending
9243 * on whether attinhcount goes to zero or not. (We can't check this in a
9244 * static pre-pass because it won't handle multiple inheritance situations
9245 * correctly.)
9246 */
9247static void
9248ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9249 AlterTableCmd *cmd, LOCKMODE lockmode,
9250 AlterTableUtilityContext *context)
9251{
9252 if (rel->rd_rel->reloftype && !recursing)
9253 ereport(ERROR,
9254 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9255 errmsg("cannot drop column from typed table")));
9256
9257 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9258 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9259
9260 if (recurse)
9261 cmd->recurse = true;
9262}
9263
9264/*
9265 * Drops column 'colName' from relation 'rel' and returns the address of the
9266 * dropped column. The column is also dropped (or marked as no longer
9267 * inherited from relation) from the relation's inheritance children, if any.
9268 *
9269 * In the recursive invocations for inheritance child relations, instead of
9270 * dropping the column directly (if to be dropped at all), its object address
9271 * is added to 'addrs', which must be non-NULL in such invocations. All
9272 * columns are dropped at the same time after all the children have been
9273 * checked recursively.
9274 */
9275static ObjectAddress
9276ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9277 DropBehavior behavior,
9278 bool recurse, bool recursing,
9279 bool missing_ok, LOCKMODE lockmode,
9280 ObjectAddresses *addrs)
9281{
9282 HeapTuple tuple;
9283 Form_pg_attribute targetatt;
9285 List *children;
9286 ObjectAddress object;
9287 bool is_expr;
9288
9289 /* At top level, permission check was done in ATPrepCmd, else do it */
9290 if (recursing)
9293
9294 /* Initialize addrs on the first invocation */
9295 Assert(!recursing || addrs != NULL);
9296
9297 /* since this function recurses, it could be driven to stack overflow */
9299
9300 if (!recursing)
9301 addrs = new_object_addresses();
9302
9303 /*
9304 * get the number of the attribute
9305 */
9306 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9307 if (!HeapTupleIsValid(tuple))
9308 {
9309 if (!missing_ok)
9310 {
9311 ereport(ERROR,
9312 (errcode(ERRCODE_UNDEFINED_COLUMN),
9313 errmsg("column \"%s\" of relation \"%s\" does not exist",
9314 colName, RelationGetRelationName(rel))));
9315 }
9316 else
9317 {
9319 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9320 colName, RelationGetRelationName(rel))));
9321 return InvalidObjectAddress;
9322 }
9323 }
9324 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9325
9326 attnum = targetatt->attnum;
9327
9328 /* Can't drop a system attribute */
9329 if (attnum <= 0)
9330 ereport(ERROR,
9331 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9332 errmsg("cannot drop system column \"%s\"",
9333 colName)));
9334
9335 /*
9336 * Don't drop inherited columns, unless recursing (presumably from a drop
9337 * of the parent column)
9338 */
9339 if (targetatt->attinhcount > 0 && !recursing)
9340 ereport(ERROR,
9341 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9342 errmsg("cannot drop inherited column \"%s\"",
9343 colName)));
9344
9345 /*
9346 * Don't drop columns used in the partition key, either. (If we let this
9347 * go through, the key column's dependencies would cause a cascaded drop
9348 * of the whole table, which is surely not what the user expected.)
9349 */
9350 if (has_partition_attrs(rel,
9352 &is_expr))
9353 ereport(ERROR,
9354 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9355 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9356 colName, RelationGetRelationName(rel))));
9357
9358 ReleaseSysCache(tuple);
9359
9360 /*
9361 * Propagate to children as appropriate. Unlike most other ALTER
9362 * routines, we have to do this one level of recursion at a time; we can't
9363 * use find_all_inheritors to do it in one pass.
9364 */
9365 children =
9367
9368 if (children)
9369 {
9370 Relation attr_rel;
9371 ListCell *child;
9372
9373 /*
9374 * In case of a partitioned table, the column must be dropped from the
9375 * partitions as well.
9376 */
9377 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9378 ereport(ERROR,
9379 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9380 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9381 errhint("Do not specify the ONLY keyword.")));
9382
9383 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9384 foreach(child, children)
9385 {
9386 Oid childrelid = lfirst_oid(child);
9387 Relation childrel;
9388 Form_pg_attribute childatt;
9389
9390 /* find_inheritance_children already got lock */
9391 childrel = table_open(childrelid, NoLock);
9392 CheckAlterTableIsSafe(childrel);
9393
9394 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9395 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9396 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9397 colName, childrelid);
9398 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9399
9400 if (childatt->attinhcount <= 0) /* shouldn't happen */
9401 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9402 childrelid, colName);
9403
9404 if (recurse)
9405 {
9406 /*
9407 * If the child column has other definition sources, just
9408 * decrement its inheritance count; if not, recurse to delete
9409 * it.
9410 */
9411 if (childatt->attinhcount == 1 && !childatt->attislocal)
9412 {
9413 /* Time to delete this child column, too */
9414 ATExecDropColumn(wqueue, childrel, colName,
9415 behavior, true, true,
9416 false, lockmode, addrs);
9417 }
9418 else
9419 {
9420 /* Child column must survive my deletion */
9421 childatt->attinhcount--;
9422
9423 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9424
9425 /* Make update visible */
9427 }
9428 }
9429 else
9430 {
9431 /*
9432 * If we were told to drop ONLY in this table (no recursion),
9433 * we need to mark the inheritors' attributes as locally
9434 * defined rather than inherited.
9435 */
9436 childatt->attinhcount--;
9437 childatt->attislocal = true;
9438
9439 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9440
9441 /* Make update visible */
9443 }
9444
9445 heap_freetuple(tuple);
9446
9447 table_close(childrel, NoLock);
9448 }
9449 table_close(attr_rel, RowExclusiveLock);
9450 }
9451
9452 /* Add object to delete */
9453 object.classId = RelationRelationId;
9454 object.objectId = RelationGetRelid(rel);
9455 object.objectSubId = attnum;
9456 add_exact_object_address(&object, addrs);
9457
9458 if (!recursing)
9459 {
9460 /* Recursion has ended, drop everything that was collected */
9461 performMultipleDeletions(addrs, behavior, 0);
9462 free_object_addresses(addrs);
9463 }
9464
9465 return object;
9466}
9467
9468/*
9469 * Prepare to add a primary key on a table, by adding not-null constraints
9470 * on all columns.
9471 *
9472 * The not-null constraints for a primary key must cover the whole inheritance
9473 * hierarchy (failing to ensure that leads to funny corner cases). For the
9474 * normal case where we're asked to recurse, this routine checks if the
9475 * not-null constraints exist already, and if not queues a requirement for
9476 * them to be created by phase 2.
9477 *
9478 * For the case where we're asked not to recurse, we verify that a not-null
9479 * constraint exists on each column of each (direct) child table, throwing an
9480 * error if not. Not throwing an error would also work, because a not-null
9481 * constraint would be created anyway, but it'd cause a silent scan of the
9482 * child table to verify absence of nulls. We prefer to let the user know so
9483 * that they can add the constraint manually without having to hold
9484 * AccessExclusiveLock while at it.
9485 *
9486 * However, it's also important that we do not acquire locks on children if
9487 * the not-null constraints already exist on the parent, to avoid risking
9488 * deadlocks during parallel pg_restore of PKs on partitioned tables.
9489 */
9490static void
9492 bool recurse, LOCKMODE lockmode,
9493 AlterTableUtilityContext *context)
9494{
9495 Constraint *pkconstr;
9496 List *children = NIL;
9497 bool got_children = false;
9498
9499 pkconstr = castNode(Constraint, cmd->def);
9500 if (pkconstr->contype != CONSTR_PRIMARY)
9501 return;
9502
9503 /* Verify that columns are not-null, or request that they be made so */
9504 foreach_node(String, column, pkconstr->keys)
9505 {
9506 AlterTableCmd *newcmd;
9507 Constraint *nnconstr;
9508 HeapTuple tuple;
9509
9510 /*
9511 * First check if a suitable constraint exists. If it does, we don't
9512 * need to request another one. We do need to bail out if it's not
9513 * valid, though.
9514 */
9515 tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9516 if (tuple != NULL)
9517 {
9518 verifyNotNullPKCompatible(tuple, strVal(column));
9519
9520 /* All good with this one; don't request another */
9521 heap_freetuple(tuple);
9522 continue;
9523 }
9524 else if (!recurse)
9525 {
9526 /*
9527 * No constraint on this column. Asked not to recurse, we won't
9528 * create one here, but verify that all children have one.
9529 */
9530 if (!got_children)
9531 {
9533 lockmode);
9534 /* only search for children on the first time through */
9535 got_children = true;
9536 }
9537
9538 foreach_oid(childrelid, children)
9539 {
9540 HeapTuple tup;
9541
9542 tup = findNotNullConstraint(childrelid, strVal(column));
9543 if (!tup)
9544 ereport(ERROR,
9545 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9546 strVal(column), get_rel_name(childrelid)));
9547 /* verify it's good enough */
9548 verifyNotNullPKCompatible(tup, strVal(column));
9549 }
9550 }
9551
9552 /* This column is not already not-null, so add it to the queue */
9553 nnconstr = makeNotNullConstraint(column);
9554
9555 newcmd = makeNode(AlterTableCmd);
9556 newcmd->subtype = AT_AddConstraint;
9557 /* note we force recurse=true here; see above */
9558 newcmd->recurse = true;
9559 newcmd->def = (Node *) nnconstr;
9560
9561 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9562 }
9563}
9564
9565/*
9566 * Verify whether the given not-null constraint is compatible with a
9567 * primary key. If not, an error is thrown.
9568 */
9569static void
9570verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9571{
9573
9574 if (conForm->contype != CONSTRAINT_NOTNULL)
9575 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9576
9577 /* a NO INHERIT constraint is no good */
9578 if (conForm->connoinherit)
9579 ereport(ERROR,
9580 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9581 errmsg("cannot create primary key on column \"%s\"", colname),
9582 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9583 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9584 NameStr(conForm->conname), colname,
9585 get_rel_name(conForm->conrelid), "NO INHERIT"),
9586 errhint("You might need to make the existing constraint inheritable using %s.",
9587 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9588
9589 /* an unvalidated constraint is no good */
9590 if (!conForm->convalidated)
9591 ereport(ERROR,
9592 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9593 errmsg("cannot create primary key on column \"%s\"", colname),
9594 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9595 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9596 NameStr(conForm->conname), colname,
9597 get_rel_name(conForm->conrelid), "NOT VALID"),
9598 errhint("You might need to validate it using %s.",
9599 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9600}
9601
9602/*
9603 * ALTER TABLE ADD INDEX
9604 *
9605 * There is no such command in the grammar, but parse_utilcmd.c converts
9606 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9607 * us schedule creation of the index at the appropriate time during ALTER.
9608 *
9609 * Return value is the address of the new index.
9610 */
9611static ObjectAddress
9613 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9614{
9615 bool check_rights;
9616 bool skip_build;
9617 bool quiet;
9618 ObjectAddress address;
9619
9621 Assert(!stmt->concurrent);
9622
9623 /* The IndexStmt has already been through transformIndexStmt */
9624 Assert(stmt->transformed);
9625
9626 /* suppress schema rights check when rebuilding existing index */
9627 check_rights = !is_rebuild;
9628 /* skip index build if phase 3 will do it or we're reusing an old one */
9629 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9630 /* suppress notices when rebuilding existing index */
9631 quiet = is_rebuild;
9632
9633 address = DefineIndex(RelationGetRelid(rel),
9634 stmt,
9635 InvalidOid, /* no predefined OID */
9636 InvalidOid, /* no parent index */
9637 InvalidOid, /* no parent constraint */
9638 -1, /* total_parts unknown */
9639 true, /* is_alter_table */
9640 check_rights,
9641 false, /* check_not_in_use - we did it already */
9642 skip_build,
9643 quiet);
9644
9645 /*
9646 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9647 * new index instead of building from scratch. Restore associated fields.
9648 * This may store InvalidSubTransactionId in both fields, in which case
9649 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9650 * this after the CCI that made catalog rows visible to any rebuild. The
9651 * DROP of the old edition of this index will have scheduled the storage
9652 * for deletion at commit, so cancel that pending deletion.
9653 */
9654 if (RelFileNumberIsValid(stmt->oldNumber))
9655 {
9656 Relation irel = index_open(address.objectId, NoLock);
9657
9658 irel->rd_createSubid = stmt->oldCreateSubid;
9659 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9661 index_close(irel, NoLock);
9662 }
9663
9664 return address;
9665}
9666
9667/*
9668 * ALTER TABLE ADD STATISTICS
9669 *
9670 * This is no such command in the grammar, but we use this internally to add
9671 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9672 * column type change.
9673 */
9674static ObjectAddress
9676 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9677{
9678 ObjectAddress address;
9679
9681
9682 /* The CreateStatsStmt has already been through transformStatsStmt */
9683 Assert(stmt->transformed);
9684
9685 address = CreateStatistics(stmt, !is_rebuild);
9686
9687 return address;
9688}
9689
9690/*
9691 * ALTER TABLE ADD CONSTRAINT USING INDEX
9692 *
9693 * Returns the address of the new constraint.
9694 */
9695static ObjectAddress
9697 IndexStmt *stmt, LOCKMODE lockmode)
9698{
9699 Oid index_oid = stmt->indexOid;
9700 Relation indexRel;
9701 char *indexName;
9702 IndexInfo *indexInfo;
9703 char *constraintName;
9704 char constraintType;
9705 ObjectAddress address;
9706 bits16 flags;
9707
9709 Assert(OidIsValid(index_oid));
9710 Assert(stmt->isconstraint);
9711
9712 /*
9713 * Doing this on partitioned tables is not a simple feature to implement,
9714 * so let's punt for now.
9715 */
9716 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9717 ereport(ERROR,
9718 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9719 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9720
9721 indexRel = index_open(index_oid, AccessShareLock);
9722
9723 indexName = pstrdup(RelationGetRelationName(indexRel));
9724
9725 indexInfo = BuildIndexInfo(indexRel);
9726
9727 /* this should have been checked at parse time */
9728 if (!indexInfo->ii_Unique)
9729 elog(ERROR, "index \"%s\" is not unique", indexName);
9730
9731 /*
9732 * Determine name to assign to constraint. We require a constraint to
9733 * have the same name as the underlying index; therefore, use the index's
9734 * existing name as the default constraint name, and if the user
9735 * explicitly gives some other name for the constraint, rename the index
9736 * to match.
9737 */
9738 constraintName = stmt->idxname;
9739 if (constraintName == NULL)
9740 constraintName = indexName;
9741 else if (strcmp(constraintName, indexName) != 0)
9742 {
9744 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9745 indexName, constraintName)));
9746 RenameRelationInternal(index_oid, constraintName, false, true);
9747 }
9748
9749 /* Extra checks needed if making primary key */
9750 if (stmt->primary)
9751 index_check_primary_key(rel, indexInfo, true, stmt);
9752
9753 /* Note we currently don't support EXCLUSION constraints here */
9754 if (stmt->primary)
9755 constraintType = CONSTRAINT_PRIMARY;
9756 else
9757 constraintType = CONSTRAINT_UNIQUE;
9758
9759 /* Create the catalog entries for the constraint */
9762 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9763 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9765
9766 address = index_constraint_create(rel,
9767 index_oid,
9768 InvalidOid,
9769 indexInfo,
9770 constraintName,
9771 constraintType,
9772 flags,
9774 false); /* is_internal */
9775
9776 index_close(indexRel, NoLock);
9777
9778 return address;
9779}
9780
9781/*
9782 * ALTER TABLE ADD CONSTRAINT
9783 *
9784 * Return value is the address of the new constraint; if no constraint was
9785 * added, InvalidObjectAddress is returned.
9786 */
9787static ObjectAddress
9789 Constraint *newConstraint, bool recurse, bool is_readd,
9790 LOCKMODE lockmode)
9791{
9793
9794 Assert(IsA(newConstraint, Constraint));
9795
9796 /*
9797 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9798 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9799 * parse_utilcmd.c).
9800 */
9801 switch (newConstraint->contype)
9802 {
9803 case CONSTR_CHECK:
9804 case CONSTR_NOTNULL:
9805 address =
9806 ATAddCheckNNConstraint(wqueue, tab, rel,
9807 newConstraint, recurse, false, is_readd,
9808 lockmode);
9809 break;
9810
9811 case CONSTR_FOREIGN:
9812
9813 /*
9814 * Assign or validate constraint name
9815 */
9816 if (newConstraint->conname)
9817 {
9819 RelationGetRelid(rel),
9820 newConstraint->conname))
9821 ereport(ERROR,
9823 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9824 newConstraint->conname,
9826 }
9827 else
9828 newConstraint->conname =
9831 "fkey",
9833 NIL);
9834
9835 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9836 newConstraint,
9837 recurse, false,
9838 lockmode);
9839 break;
9840
9841 default:
9842 elog(ERROR, "unrecognized constraint type: %d",
9843 (int) newConstraint->contype);
9844 }
9845
9846 return address;
9847}
9848
9849/*
9850 * Generate the column-name portion of the constraint name for a new foreign
9851 * key given the list of column names that reference the referenced
9852 * table. This will be passed to ChooseConstraintName along with the parent
9853 * table name and the "fkey" suffix.
9854 *
9855 * We know that less than NAMEDATALEN characters will actually be used, so we
9856 * can truncate the result once we've generated that many.
9857 *
9858 * XXX see also ChooseExtendedStatisticNameAddition and
9859 * ChooseIndexNameAddition.
9860 */
9861static char *
9863{
9864 char buf[NAMEDATALEN * 2];
9865 int buflen = 0;
9866 ListCell *lc;
9867
9868 buf[0] = '\0';
9869 foreach(lc, colnames)
9870 {
9871 const char *name = strVal(lfirst(lc));
9872
9873 if (buflen > 0)
9874 buf[buflen++] = '_'; /* insert _ between names */
9875
9876 /*
9877 * At this point we have buflen <= NAMEDATALEN. name should be less
9878 * than NAMEDATALEN already, but use strlcpy for paranoia.
9879 */
9880 strlcpy(buf + buflen, name, NAMEDATALEN);
9881 buflen += strlen(buf + buflen);
9882 if (buflen >= NAMEDATALEN)
9883 break;
9884 }
9885 return pstrdup(buf);
9886}
9887
9888/*
9889 * Add a check or not-null constraint to a single table and its children.
9890 * Returns the address of the constraint added to the parent relation,
9891 * if one gets added, or InvalidObjectAddress otherwise.
9892 *
9893 * Subroutine for ATExecAddConstraint.
9894 *
9895 * We must recurse to child tables during execution, rather than using
9896 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9897 * constraints *must* be given the same name, else they won't be seen as
9898 * related later. If the user didn't explicitly specify a name, then
9899 * AddRelationNewConstraints would normally assign different names to the
9900 * child constraints. To fix that, we must capture the name assigned at
9901 * the parent table and pass that down.
9902 */
9903static ObjectAddress
9905 Constraint *constr, bool recurse, bool recursing,
9906 bool is_readd, LOCKMODE lockmode)
9907{
9908 List *newcons;
9909 ListCell *lcon;
9910 List *children;
9911 ListCell *child;
9913
9914 /* Guard against stack overflow due to overly deep inheritance tree. */
9916
9917 /* At top level, permission check was done in ATPrepCmd, else do it */
9918 if (recursing)
9921
9922 /*
9923 * Call AddRelationNewConstraints to do the work, making sure it works on
9924 * a copy of the Constraint so transformExpr can't modify the original. It
9925 * returns a list of cooked constraints.
9926 *
9927 * If the constraint ends up getting merged with a pre-existing one, it's
9928 * omitted from the returned list, which is what we want: we do not need
9929 * to do any validation work. That can only happen at child tables,
9930 * though, since we disallow merging at the top level.
9931 */
9932 newcons = AddRelationNewConstraints(rel, NIL,
9933 list_make1(copyObject(constr)),
9934 recursing || is_readd, /* allow_merge */
9935 !recursing, /* is_local */
9936 is_readd, /* is_internal */
9937 NULL); /* queryString not available
9938 * here */
9939
9940 /* we don't expect more than one constraint here */
9941 Assert(list_length(newcons) <= 1);
9942
9943 /* Add each to-be-validated constraint to Phase 3's queue */
9944 foreach(lcon, newcons)
9945 {
9946 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9947
9948 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9949 {
9950 NewConstraint *newcon;
9951
9952 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9953 newcon->name = ccon->name;
9954 newcon->contype = ccon->contype;
9955 newcon->qual = ccon->expr;
9956
9957 tab->constraints = lappend(tab->constraints, newcon);
9958 }
9959
9960 /* Save the actually assigned name if it was defaulted */
9961 if (constr->conname == NULL)
9962 constr->conname = ccon->name;
9963
9964 /*
9965 * If adding a valid not-null constraint, set the pg_attribute flag
9966 * and tell phase 3 to verify existing rows, if needed. For an
9967 * invalid constraint, just set attnotnull, without queueing
9968 * verification.
9969 */
9970 if (constr->contype == CONSTR_NOTNULL)
9971 set_attnotnull(wqueue, rel, ccon->attnum,
9972 !constr->skip_validation,
9973 !constr->skip_validation);
9974
9975 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9976 }
9977
9978 /* At this point we must have a locked-down name to use */
9979 Assert(newcons == NIL || constr->conname != NULL);
9980
9981 /* Advance command counter in case same table is visited multiple times */
9983
9984 /*
9985 * If the constraint got merged with an existing constraint, we're done.
9986 * We mustn't recurse to child tables in this case, because they've
9987 * already got the constraint, and visiting them again would lead to an
9988 * incorrect value for coninhcount.
9989 */
9990 if (newcons == NIL)
9991 return address;
9992
9993 /*
9994 * If adding a NO INHERIT constraint, no need to find our children.
9995 */
9996 if (constr->is_no_inherit)
9997 return address;
9998
9999 /*
10000 * Propagate to children as appropriate. Unlike most other ALTER
10001 * routines, we have to do this one level of recursion at a time; we can't
10002 * use find_all_inheritors to do it in one pass.
10003 */
10004 children =
10006
10007 /*
10008 * Check if ONLY was specified with ALTER TABLE. If so, allow the
10009 * constraint creation only if there are no children currently. Error out
10010 * otherwise.
10011 */
10012 if (!recurse && children != NIL)
10013 ereport(ERROR,
10014 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10015 errmsg("constraint must be added to child tables too")));
10016
10017 /*
10018 * Recurse to create the constraint on each child.
10019 */
10020 foreach(child, children)
10021 {
10022 Oid childrelid = lfirst_oid(child);
10023 Relation childrel;
10024 AlteredTableInfo *childtab;
10025
10026 /* find_inheritance_children already got lock */
10027 childrel = table_open(childrelid, NoLock);
10028 CheckAlterTableIsSafe(childrel);
10029
10030 /* Find or create work queue entry for this table */
10031 childtab = ATGetQueueEntry(wqueue, childrel);
10032
10033 /* Recurse to this child */
10034 ATAddCheckNNConstraint(wqueue, childtab, childrel,
10035 constr, recurse, true, is_readd, lockmode);
10036
10037 table_close(childrel, NoLock);
10038 }
10039
10040 return address;
10041}
10042
10043/*
10044 * Add a foreign-key constraint to a single table; return the new constraint's
10045 * address.
10046 *
10047 * Subroutine for ATExecAddConstraint. Must already hold exclusive
10048 * lock on the rel, and have done appropriate validity checks for it.
10049 * We do permissions checks here, however.
10050 *
10051 * When the referenced or referencing tables (or both) are partitioned,
10052 * multiple pg_constraint rows are required -- one for each partitioned table
10053 * and each partition on each side (fortunately, not one for every combination
10054 * thereof). We also need action triggers on each leaf partition on the
10055 * referenced side, and check triggers on each leaf partition on the
10056 * referencing side.
10057 */
10058static ObjectAddress
10060 Constraint *fkconstraint,
10061 bool recurse, bool recursing, LOCKMODE lockmode)
10062{
10063 Relation pkrel;
10064 int16 pkattnum[INDEX_MAX_KEYS] = {0};
10065 int16 fkattnum[INDEX_MAX_KEYS] = {0};
10066 Oid pktypoid[INDEX_MAX_KEYS] = {0};
10067 Oid fktypoid[INDEX_MAX_KEYS] = {0};
10068 Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10069 Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10070 Oid opclasses[INDEX_MAX_KEYS] = {0};
10071 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10072 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10073 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10074 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10075 bool with_period;
10076 bool pk_has_without_overlaps;
10077 int i;
10078 int numfks,
10079 numpks,
10080 numfkdelsetcols;
10081 Oid indexOid;
10082 bool old_check_ok;
10083 ObjectAddress address;
10084 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10085
10086 /*
10087 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10088 * delete rows out from under us.
10089 */
10090 if (OidIsValid(fkconstraint->old_pktable_oid))
10091 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10092 else
10093 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10094
10095 /*
10096 * Validity checks (permission checks wait till we have the column
10097 * numbers)
10098 */
10099 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10100 ereport(ERROR,
10101 errcode(ERRCODE_WRONG_OBJECT_TYPE),
10102 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10104 RelationGetRelationName(pkrel)));
10105
10106 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10107 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10108 ereport(ERROR,
10109 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10110 errmsg("referenced relation \"%s\" is not a table",
10111 RelationGetRelationName(pkrel))));
10112
10114 ereport(ERROR,
10115 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10116 errmsg("permission denied: \"%s\" is a system catalog",
10117 RelationGetRelationName(pkrel))));
10118
10119 /*
10120 * References from permanent or unlogged tables to temp tables, and from
10121 * permanent tables to unlogged tables, are disallowed because the
10122 * referenced data can vanish out from under us. References from temp
10123 * tables to any other table type are also disallowed, because other
10124 * backends might need to run the RI triggers on the perm table, but they
10125 * can't reliably see tuples in the local buffers of other backends.
10126 */
10127 switch (rel->rd_rel->relpersistence)
10128 {
10129 case RELPERSISTENCE_PERMANENT:
10130 if (!RelationIsPermanent(pkrel))
10131 ereport(ERROR,
10132 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10133 errmsg("constraints on permanent tables may reference only permanent tables")));
10134 break;
10135 case RELPERSISTENCE_UNLOGGED:
10136 if (!RelationIsPermanent(pkrel)
10137 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10138 ereport(ERROR,
10139 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10140 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10141 break;
10142 case RELPERSISTENCE_TEMP:
10143 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10144 ereport(ERROR,
10145 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10146 errmsg("constraints on temporary tables may reference only temporary tables")));
10147 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10148 ereport(ERROR,
10149 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10150 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10151 break;
10152 }
10153
10154 /*
10155 * Look up the referencing attributes to make sure they exist, and record
10156 * their attnums and type and collation OIDs.
10157 */
10159 fkconstraint->fk_attrs,
10160 fkattnum, fktypoid, fkcolloid);
10161 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10162 if (with_period && !fkconstraint->fk_with_period)
10163 ereport(ERROR,
10164 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10165 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10166
10167 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10168 fkconstraint->fk_del_set_cols,
10169 fkdelsetcols, NULL, NULL);
10170 numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10171 numfkdelsetcols,
10172 fkdelsetcols,
10173 fkconstraint->fk_del_set_cols);
10174
10175 /*
10176 * If the attribute list for the referenced table was omitted, lookup the
10177 * definition of the primary key and use it. Otherwise, validate the
10178 * supplied attribute list. In either case, discover the index OID and
10179 * index opclasses, and the attnums and type and collation OIDs of the
10180 * attributes.
10181 */
10182 if (fkconstraint->pk_attrs == NIL)
10183 {
10184 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10185 &fkconstraint->pk_attrs,
10186 pkattnum, pktypoid, pkcolloid,
10187 opclasses, &pk_has_without_overlaps);
10188
10189 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10190 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10191 ereport(ERROR,
10192 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10193 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10194 }
10195 else
10196 {
10198 fkconstraint->pk_attrs,
10199 pkattnum, pktypoid, pkcolloid);
10200
10201 /* Since we got pk_attrs, one should be a period. */
10202 if (with_period && !fkconstraint->pk_with_period)
10203 ereport(ERROR,
10204 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10205 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10206
10207 /* Look for an index matching the column list */
10208 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10209 with_period, opclasses, &pk_has_without_overlaps);
10210 }
10211
10212 /*
10213 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10214 * must use PERIOD.
10215 */
10216 if (pk_has_without_overlaps && !with_period)
10217 ereport(ERROR,
10218 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10219 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10220
10221 /*
10222 * Now we can check permissions.
10223 */
10224 checkFkeyPermissions(pkrel, pkattnum, numpks);
10225
10226 /*
10227 * Check some things for generated columns.
10228 */
10229 for (i = 0; i < numfks; i++)
10230 {
10231 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10232
10233 if (attgenerated)
10234 {
10235 /*
10236 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10237 */
10238 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10239 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10240 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10241 ereport(ERROR,
10242 (errcode(ERRCODE_SYNTAX_ERROR),
10243 errmsg("invalid %s action for foreign key constraint containing generated column",
10244 "ON UPDATE")));
10245 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10247 ereport(ERROR,
10248 (errcode(ERRCODE_SYNTAX_ERROR),
10249 errmsg("invalid %s action for foreign key constraint containing generated column",
10250 "ON DELETE")));
10251 }
10252
10253 /*
10254 * FKs on virtual columns are not supported. This would require
10255 * various additional support in ri_triggers.c, including special
10256 * handling in ri_NullCheck(), ri_KeysEqual(),
10257 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10258 * as NULL there). Also not really practical as long as you can't
10259 * index virtual columns.
10260 */
10261 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10262 ereport(ERROR,
10263 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10264 errmsg("foreign key constraints on virtual generated columns are not supported")));
10265 }
10266
10267 /*
10268 * Some actions are currently unsupported for foreign keys using PERIOD.
10269 */
10270 if (fkconstraint->fk_with_period)
10271 {
10272 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10273 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10274 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10276 ereport(ERROR,
10277 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10278 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10279 "ON UPDATE"));
10280
10281 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10282 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10283 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10285 ereport(ERROR,
10286 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10287 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10288 "ON DELETE"));
10289 }
10290
10291 /*
10292 * Look up the equality operators to use in the constraint.
10293 *
10294 * Note that we have to be careful about the difference between the actual
10295 * PK column type and the opclass' declared input type, which might be
10296 * only binary-compatible with it. The declared opcintype is the right
10297 * thing to probe pg_amop with.
10298 */
10299 if (numfks != numpks)
10300 ereport(ERROR,
10301 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10302 errmsg("number of referencing and referenced columns for foreign key disagree")));
10303
10304 /*
10305 * On the strength of a previous constraint, we might avoid scanning
10306 * tables to validate this one. See below.
10307 */
10308 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10309 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10310
10311 for (i = 0; i < numpks; i++)
10312 {
10313 Oid pktype = pktypoid[i];
10314 Oid fktype = fktypoid[i];
10315 Oid fktyped;
10316 Oid pkcoll = pkcolloid[i];
10317 Oid fkcoll = fkcolloid[i];
10318 HeapTuple cla_ht;
10319 Form_pg_opclass cla_tup;
10320 Oid amid;
10321 Oid opfamily;
10322 Oid opcintype;
10323 bool for_overlaps;
10324 CompareType cmptype;
10325 Oid pfeqop;
10326 Oid ppeqop;
10327 Oid ffeqop;
10328 int16 eqstrategy;
10329 Oid pfeqop_right;
10330
10331 /* We need several fields out of the pg_opclass entry */
10332 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10333 if (!HeapTupleIsValid(cla_ht))
10334 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10335 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10336 amid = cla_tup->opcmethod;
10337 opfamily = cla_tup->opcfamily;
10338 opcintype = cla_tup->opcintype;
10339 ReleaseSysCache(cla_ht);
10340
10341 /*
10342 * Get strategy number from index AM.
10343 *
10344 * For a normal foreign-key constraint, this should not fail, since we
10345 * already checked that the index is unique and should therefore have
10346 * appropriate equal operators. For a period foreign key, this could
10347 * fail if we selected a non-matching exclusion constraint earlier.
10348 * (XXX Maybe we should do these lookups earlier so we don't end up
10349 * doing that.)
10350 */
10351 for_overlaps = with_period && i == numpks - 1;
10352 cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10353 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10354 if (eqstrategy == InvalidStrategy)
10355 ereport(ERROR,
10356 errcode(ERRCODE_UNDEFINED_OBJECT),
10357 for_overlaps
10358 ? errmsg("could not identify an overlaps operator for foreign key")
10359 : errmsg("could not identify an equality operator for foreign key"),
10360 errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10361 cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10362
10363 /*
10364 * There had better be a primary equality operator for the index.
10365 * We'll use it for PK = PK comparisons.
10366 */
10367 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10368 eqstrategy);
10369
10370 if (!OidIsValid(ppeqop))
10371 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10372 eqstrategy, opcintype, opcintype, opfamily);
10373
10374 /*
10375 * Are there equality operators that take exactly the FK type? Assume
10376 * we should look through any domain here.
10377 */
10378 fktyped = getBaseType(fktype);
10379
10380 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10381 eqstrategy);
10382 if (OidIsValid(pfeqop))
10383 {
10384 pfeqop_right = fktyped;
10385 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10386 eqstrategy);
10387 }
10388 else
10389 {
10390 /* keep compiler quiet */
10391 pfeqop_right = InvalidOid;
10392 ffeqop = InvalidOid;
10393 }
10394
10395 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10396 {
10397 /*
10398 * Otherwise, look for an implicit cast from the FK type to the
10399 * opcintype, and if found, use the primary equality operator.
10400 * This is a bit tricky because opcintype might be a polymorphic
10401 * type such as ANYARRAY or ANYENUM; so what we have to test is
10402 * whether the two actual column types can be concurrently cast to
10403 * that type. (Otherwise, we'd fail to reject combinations such
10404 * as int[] and point[].)
10405 */
10406 Oid input_typeids[2];
10407 Oid target_typeids[2];
10408
10409 input_typeids[0] = pktype;
10410 input_typeids[1] = fktype;
10411 target_typeids[0] = opcintype;
10412 target_typeids[1] = opcintype;
10413 if (can_coerce_type(2, input_typeids, target_typeids,
10415 {
10416 pfeqop = ffeqop = ppeqop;
10417 pfeqop_right = opcintype;
10418 }
10419 }
10420
10421 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10422 ereport(ERROR,
10423 (errcode(ERRCODE_DATATYPE_MISMATCH),
10424 errmsg("foreign key constraint \"%s\" cannot be implemented",
10425 fkconstraint->conname),
10426 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10427 "are of incompatible types: %s and %s.",
10428 strVal(list_nth(fkconstraint->fk_attrs, i)),
10429 strVal(list_nth(fkconstraint->pk_attrs, i)),
10430 format_type_be(fktype),
10431 format_type_be(pktype))));
10432
10433 /*
10434 * This shouldn't be possible, but better check to make sure we have a
10435 * consistent state for the check below.
10436 */
10437 if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10438 elog(ERROR, "key columns are not both collatable");
10439
10440 if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10441 {
10442 bool pkcolldet;
10443 bool fkcolldet;
10444
10445 pkcolldet = get_collation_isdeterministic(pkcoll);
10446 fkcolldet = get_collation_isdeterministic(fkcoll);
10447
10448 /*
10449 * SQL requires that both collations are the same. This is
10450 * because we need a consistent notion of equality on both
10451 * columns. We relax this by allowing different collations if
10452 * they are both deterministic. (This is also for backward
10453 * compatibility, because PostgreSQL has always allowed this.)
10454 */
10455 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10456 ereport(ERROR,
10457 (errcode(ERRCODE_COLLATION_MISMATCH),
10458 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10459 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10460 "have incompatible collations: \"%s\" and \"%s\". "
10461 "If either collation is nondeterministic, then both collations have to be the same.",
10462 strVal(list_nth(fkconstraint->fk_attrs, i)),
10463 strVal(list_nth(fkconstraint->pk_attrs, i)),
10464 get_collation_name(fkcoll),
10465 get_collation_name(pkcoll))));
10466 }
10467
10468 if (old_check_ok)
10469 {
10470 /*
10471 * When a pfeqop changes, revalidate the constraint. We could
10472 * permit intra-opfamily changes, but that adds subtle complexity
10473 * without any concrete benefit for core types. We need not
10474 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10475 */
10476 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10477 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10478 old_pfeqop_item);
10479 }
10480 if (old_check_ok)
10481 {
10482 Oid old_fktype;
10483 Oid new_fktype;
10484 CoercionPathType old_pathtype;
10485 CoercionPathType new_pathtype;
10486 Oid old_castfunc;
10487 Oid new_castfunc;
10488 Oid old_fkcoll;
10489 Oid new_fkcoll;
10491 fkattnum[i] - 1);
10492
10493 /*
10494 * Identify coercion pathways from each of the old and new FK-side
10495 * column types to the right (foreign) operand type of the pfeqop.
10496 * We may assume that pg_constraint.conkey is not changing.
10497 */
10498 old_fktype = attr->atttypid;
10499 new_fktype = fktype;
10500 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10501 &old_castfunc);
10502 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10503 &new_castfunc);
10504
10505 old_fkcoll = attr->attcollation;
10506 new_fkcoll = fkcoll;
10507
10508 /*
10509 * Upon a change to the cast from the FK column to its pfeqop
10510 * operand, revalidate the constraint. For this evaluation, a
10511 * binary coercion cast is equivalent to no cast at all. While
10512 * type implementors should design implicit casts with an eye
10513 * toward consistency of operations like equality, we cannot
10514 * assume here that they have done so.
10515 *
10516 * A function with a polymorphic argument could change behavior
10517 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10518 * when the cast destination is polymorphic, we only avoid
10519 * revalidation if the input type has not changed at all. Given
10520 * just the core data types and operator classes, this requirement
10521 * prevents no would-be optimizations.
10522 *
10523 * If the cast converts from a base type to a domain thereon, then
10524 * that domain type must be the opcintype of the unique index.
10525 * Necessarily, the primary key column must then be of the domain
10526 * type. Since the constraint was previously valid, all values on
10527 * the foreign side necessarily exist on the primary side and in
10528 * turn conform to the domain. Consequently, we need not treat
10529 * domains specially here.
10530 *
10531 * If the collation changes, revalidation is required, unless both
10532 * collations are deterministic, because those share the same
10533 * notion of equality (because texteq reduces to bitwise
10534 * equality).
10535 *
10536 * We need not directly consider the PK type. It's necessarily
10537 * binary coercible to the opcintype of the unique index column,
10538 * and ri_triggers.c will only deal with PK datums in terms of
10539 * that opcintype. Changing the opcintype also changes pfeqop.
10540 */
10541 old_check_ok = (new_pathtype == old_pathtype &&
10542 new_castfunc == old_castfunc &&
10543 (!IsPolymorphicType(pfeqop_right) ||
10544 new_fktype == old_fktype) &&
10545 (new_fkcoll == old_fkcoll ||
10546 (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10547 }
10548
10549 pfeqoperators[i] = pfeqop;
10550 ppeqoperators[i] = ppeqop;
10551 ffeqoperators[i] = ffeqop;
10552 }
10553
10554 /*
10555 * For FKs with PERIOD we need additional operators to check whether the
10556 * referencing row's range is contained by the aggregated ranges of the
10557 * referenced row(s). For rangetypes and multirangetypes this is
10558 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10559 * support for now. FKs will look these up at "runtime", but we should
10560 * make sure the lookup works here, even if we don't use the values.
10561 */
10562 if (with_period)
10563 {
10564 Oid periodoperoid;
10565 Oid aggedperiodoperoid;
10566 Oid intersectoperoid;
10567
10568 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10569 &intersectoperoid);
10570 }
10571
10572 /* First, create the constraint catalog entry itself. */
10574 fkconstraint->conname, fkconstraint, rel, pkrel,
10575 indexOid,
10576 InvalidOid, /* no parent constraint */
10577 numfks,
10578 pkattnum,
10579 fkattnum,
10580 pfeqoperators,
10581 ppeqoperators,
10582 ffeqoperators,
10583 numfkdelsetcols,
10584 fkdelsetcols,
10585 false,
10586 with_period);
10587
10588 /* Next process the action triggers at the referenced side and recurse */
10589 addFkRecurseReferenced(fkconstraint, rel, pkrel,
10590 indexOid,
10591 address.objectId,
10592 numfks,
10593 pkattnum,
10594 fkattnum,
10595 pfeqoperators,
10596 ppeqoperators,
10597 ffeqoperators,
10598 numfkdelsetcols,
10599 fkdelsetcols,
10600 old_check_ok,
10602 with_period);
10603
10604 /* Lastly create the check triggers at the referencing side and recurse */
10605 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10606 indexOid,
10607 address.objectId,
10608 numfks,
10609 pkattnum,
10610 fkattnum,
10611 pfeqoperators,
10612 ppeqoperators,
10613 ffeqoperators,
10614 numfkdelsetcols,
10615 fkdelsetcols,
10616 old_check_ok,
10617 lockmode,
10619 with_period);
10620
10621 /*
10622 * Done. Close pk table, but keep lock until we've committed.
10623 */
10624 table_close(pkrel, NoLock);
10625
10626 return address;
10627}
10628
10629/*
10630 * validateFkOnDeleteSetColumns
10631 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10632 * column lists are valid.
10633 *
10634 * If there are duplicates in the fksetcolsattnums[] array, this silently
10635 * removes the dups. The new count of numfksetcols is returned.
10636 */
10637static int
10638validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10639 int numfksetcols, int16 *fksetcolsattnums,
10640 List *fksetcols)
10641{
10642 int numcolsout = 0;
10643
10644 for (int i = 0; i < numfksetcols; i++)
10645 {
10646 int16 setcol_attnum = fksetcolsattnums[i];
10647 bool seen = false;
10648
10649 /* Make sure it's in fkattnums[] */
10650 for (int j = 0; j < numfks; j++)
10651 {
10652 if (fkattnums[j] == setcol_attnum)
10653 {
10654 seen = true;
10655 break;
10656 }
10657 }
10658
10659 if (!seen)
10660 {
10661 char *col = strVal(list_nth(fksetcols, i));
10662
10663 ereport(ERROR,
10664 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10665 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10666 }
10667
10668 /* Now check for dups */
10669 seen = false;
10670 for (int j = 0; j < numcolsout; j++)
10671 {
10672 if (fksetcolsattnums[j] == setcol_attnum)
10673 {
10674 seen = true;
10675 break;
10676 }
10677 }
10678 if (!seen)
10679 fksetcolsattnums[numcolsout++] = setcol_attnum;
10680 }
10681 return numcolsout;
10682}
10683
10684/*
10685 * addFkConstraint
10686 * Install pg_constraint entries to implement a foreign key constraint.
10687 * Caller must separately invoke addFkRecurseReferenced and
10688 * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10689 * and (for partitioned tables) recurse to partitions.
10690 *
10691 * fkside: the side of the FK (or both) to create. Caller should
10692 * call addFkRecurseReferenced if this is addFkReferencedSide,
10693 * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10694 * addFkBothSides.
10695 * constraintname: the base name for the constraint being added,
10696 * copied to fkconstraint->conname if the latter is not set
10697 * fkconstraint: the constraint being added
10698 * rel: the root referencing relation
10699 * pkrel: the referenced relation; might be a partition, if recursing
10700 * indexOid: the OID of the index (on pkrel) implementing this constraint
10701 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10702 * top-level constraint
10703 * numfks: the number of columns in the foreign key
10704 * pkattnum: the attnum array of referenced attributes
10705 * fkattnum: the attnum array of referencing attributes
10706 * pf/pp/ffeqoperators: OID array of operators between columns
10707 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10708 * (...) clause
10709 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10710 * NULL/DEFAULT clause
10711 * with_period: true if this is a temporal FK
10712 */
10713static ObjectAddress
10715 char *constraintname, Constraint *fkconstraint,
10716 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10717 int numfks, int16 *pkattnum,
10718 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10719 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10720 bool is_internal, bool with_period)
10721{
10722 ObjectAddress address;
10723 Oid constrOid;
10724 char *conname;
10725 bool conislocal;
10726 int16 coninhcount;
10727 bool connoinherit;
10728
10729 /*
10730 * Verify relkind for each referenced partition. At the top level, this
10731 * is redundant with a previous check, but we need it when recursing.
10732 */
10733 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10734 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10735 ereport(ERROR,
10736 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10737 errmsg("referenced relation \"%s\" is not a table",
10738 RelationGetRelationName(pkrel))));
10739
10740 /*
10741 * Caller supplies us with a constraint name; however, it may be used in
10742 * this partition, so come up with a different one in that case. Unless
10743 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10744 * supplied name with an underscore and digit(s) appended.
10745 */
10747 RelationGetRelid(rel),
10748 constraintname))
10749 conname = ChooseConstraintName(constraintname,
10750 NULL,
10751 "",
10753 else
10754 conname = constraintname;
10755
10756 if (fkconstraint->conname == NULL)
10757 fkconstraint->conname = pstrdup(conname);
10758
10759 if (OidIsValid(parentConstr))
10760 {
10761 conislocal = false;
10762 coninhcount = 1;
10763 connoinherit = false;
10764 }
10765 else
10766 {
10767 conislocal = true;
10768 coninhcount = 0;
10769
10770 /*
10771 * always inherit for partitioned tables, never for legacy inheritance
10772 */
10773 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10774 }
10775
10776 /*
10777 * Record the FK constraint in pg_constraint.
10778 */
10779 constrOid = CreateConstraintEntry(conname,
10781 CONSTRAINT_FOREIGN,
10782 fkconstraint->deferrable,
10783 fkconstraint->initdeferred,
10784 fkconstraint->is_enforced,
10785 fkconstraint->initially_valid,
10786 parentConstr,
10787 RelationGetRelid(rel),
10788 fkattnum,
10789 numfks,
10790 numfks,
10791 InvalidOid, /* not a domain constraint */
10792 indexOid,
10793 RelationGetRelid(pkrel),
10794 pkattnum,
10795 pfeqoperators,
10796 ppeqoperators,
10797 ffeqoperators,
10798 numfks,
10799 fkconstraint->fk_upd_action,
10800 fkconstraint->fk_del_action,
10801 fkdelsetcols,
10802 numfkdelsetcols,
10803 fkconstraint->fk_matchtype,
10804 NULL, /* no exclusion constraint */
10805 NULL, /* no check constraint */
10806 NULL,
10807 conislocal, /* islocal */
10808 coninhcount, /* inhcount */
10809 connoinherit, /* conNoInherit */
10810 with_period, /* conPeriod */
10811 is_internal); /* is_internal */
10812
10813 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10814
10815 /*
10816 * In partitioning cases, create the dependency entries for this
10817 * constraint. (For non-partitioned cases, relevant entries were created
10818 * by CreateConstraintEntry.)
10819 *
10820 * On the referenced side, we need the constraint to have an internal
10821 * dependency on its parent constraint; this means that this constraint
10822 * cannot be dropped on its own -- only through the parent constraint. It
10823 * also means the containing partition cannot be dropped on its own, but
10824 * it can be detached, at which point this dependency is removed (after
10825 * verifying that no rows are referenced via this FK.)
10826 *
10827 * When processing the referencing side, we link the constraint via the
10828 * special partitioning dependencies: the parent constraint is the primary
10829 * dependent, and the partition on which the foreign key exists is the
10830 * secondary dependency. That way, this constraint is dropped if either
10831 * of these objects is.
10832 *
10833 * Note that this is only necessary for the subsidiary pg_constraint rows
10834 * in partitions; the topmost row doesn't need any of this.
10835 */
10836 if (OidIsValid(parentConstr))
10837 {
10838 ObjectAddress referenced;
10839
10840 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10841
10842 Assert(fkside != addFkBothSides);
10843 if (fkside == addFkReferencedSide)
10844 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10845 else
10846 {
10847 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10848 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10849 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10850 }
10851 }
10852
10853 /* make new constraint visible, in case we add more */
10855
10856 return address;
10857}
10858
10859/*
10860 * addFkRecurseReferenced
10861 * Recursive helper for the referenced side of foreign key creation,
10862 * which creates the action triggers and recurses
10863 *
10864 * If the referenced relation is a plain relation, create the necessary action
10865 * triggers that implement the constraint. If the referenced relation is a
10866 * partitioned table, then we create a pg_constraint row referencing the parent
10867 * of the referencing side for it and recurse on this routine for each
10868 * partition.
10869 *
10870 * fkconstraint: the constraint being added
10871 * rel: the root referencing relation
10872 * pkrel: the referenced relation; might be a partition, if recursing
10873 * indexOid: the OID of the index (on pkrel) implementing this constraint
10874 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10875 * top-level constraint
10876 * numfks: the number of columns in the foreign key
10877 * pkattnum: the attnum array of referenced attributes
10878 * fkattnum: the attnum array of referencing attributes
10879 * numfkdelsetcols: the number of columns in the ON DELETE SET
10880 * NULL/DEFAULT (...) clause
10881 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10882 * NULL/DEFAULT clause
10883 * pf/pp/ffeqoperators: OID array of operators between columns
10884 * old_check_ok: true if this constraint replaces an existing one that
10885 * was already validated (thus this one doesn't need validation)
10886 * parentDelTrigger and parentUpdTrigger: when recursively called on a
10887 * partition, the OIDs of the parent action triggers for DELETE and
10888 * UPDATE respectively.
10889 * with_period: true if this is a temporal FK
10890 */
10891static void
10893 Relation pkrel, Oid indexOid, Oid parentConstr,
10894 int numfks,
10895 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10896 Oid *ppeqoperators, Oid *ffeqoperators,
10897 int numfkdelsetcols, int16 *fkdelsetcols,
10898 bool old_check_ok,
10899 Oid parentDelTrigger, Oid parentUpdTrigger,
10900 bool with_period)
10901{
10902 Oid deleteTriggerOid = InvalidOid,
10903 updateTriggerOid = InvalidOid;
10904
10907
10908 /*
10909 * Create action triggers to enforce the constraint, or skip them if the
10910 * constraint is NOT ENFORCED.
10911 */
10912 if (fkconstraint->is_enforced)
10914 RelationGetRelid(pkrel),
10915 fkconstraint,
10916 parentConstr, indexOid,
10917 parentDelTrigger, parentUpdTrigger,
10918 &deleteTriggerOid, &updateTriggerOid);
10919
10920 /*
10921 * If the referenced table is partitioned, recurse on ourselves to handle
10922 * each partition. We need one pg_constraint row created for each
10923 * partition in addition to the pg_constraint row for the parent table.
10924 */
10925 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10926 {
10927 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10928
10929 for (int i = 0; i < pd->nparts; i++)
10930 {
10931 Relation partRel;
10932 AttrMap *map;
10933 AttrNumber *mapped_pkattnum;
10934 Oid partIndexId;
10935 ObjectAddress address;
10936
10937 /* XXX would it be better to acquire these locks beforehand? */
10938 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10939
10940 /*
10941 * Map the attribute numbers in the referenced side of the FK
10942 * definition to match the partition's column layout.
10943 */
10945 RelationGetDescr(pkrel),
10946 false);
10947 if (map)
10948 {
10949 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10950 for (int j = 0; j < numfks; j++)
10951 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10952 }
10953 else
10954 mapped_pkattnum = pkattnum;
10955
10956 /* Determine the index to use at this level */
10957 partIndexId = index_get_partition(partRel, indexOid);
10958 if (!OidIsValid(partIndexId))
10959 elog(ERROR, "index for %u not found in partition %s",
10960 indexOid, RelationGetRelationName(partRel));
10961
10962 /* Create entry at this level ... */
10964 fkconstraint->conname, fkconstraint, rel,
10965 partRel, partIndexId, parentConstr,
10966 numfks, mapped_pkattnum,
10967 fkattnum, pfeqoperators, ppeqoperators,
10968 ffeqoperators, numfkdelsetcols,
10969 fkdelsetcols, true, with_period);
10970 /* ... and recurse to our children */
10971 addFkRecurseReferenced(fkconstraint, rel, partRel,
10972 partIndexId, address.objectId, numfks,
10973 mapped_pkattnum, fkattnum,
10974 pfeqoperators, ppeqoperators, ffeqoperators,
10975 numfkdelsetcols, fkdelsetcols,
10976 old_check_ok,
10977 deleteTriggerOid, updateTriggerOid,
10978 with_period);
10979
10980 /* Done -- clean up (but keep the lock) */
10981 table_close(partRel, NoLock);
10982 if (map)
10983 {
10984 pfree(mapped_pkattnum);
10985 free_attrmap(map);
10986 }
10987 }
10988 }
10989}
10990
10991/*
10992 * addFkRecurseReferencing
10993 * Recursive helper for the referencing side of foreign key creation,
10994 * which creates the check triggers and recurses
10995 *
10996 * If the referencing relation is a plain relation, create the necessary check
10997 * triggers that implement the constraint, and set up for Phase 3 constraint
10998 * verification. If the referencing relation is a partitioned table, then
10999 * we create a pg_constraint row for it and recurse on this routine for each
11000 * partition.
11001 *
11002 * We assume that the referenced relation is locked against concurrent
11003 * deletions. If it's a partitioned relation, every partition must be so
11004 * locked.
11005 *
11006 * wqueue: the ALTER TABLE work queue; NULL when not running as part
11007 * of an ALTER TABLE sequence.
11008 * fkconstraint: the constraint being added
11009 * rel: the referencing relation; might be a partition, if recursing
11010 * pkrel: the root referenced relation
11011 * indexOid: the OID of the index (on pkrel) implementing this constraint
11012 * parentConstr: the OID of the parent constraint (there is always one)
11013 * numfks: the number of columns in the foreign key
11014 * pkattnum: the attnum array of referenced attributes
11015 * fkattnum: the attnum array of referencing attributes
11016 * pf/pp/ffeqoperators: OID array of operators between columns
11017 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
11018 * (...) clause
11019 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
11020 * NULL/DEFAULT clause
11021 * old_check_ok: true if this constraint replaces an existing one that
11022 * was already validated (thus this one doesn't need validation)
11023 * lockmode: the lockmode to acquire on partitions when recursing
11024 * parentInsTrigger and parentUpdTrigger: when being recursively called on
11025 * a partition, the OIDs of the parent check triggers for INSERT and
11026 * UPDATE respectively.
11027 * with_period: true if this is a temporal FK
11028 */
11029static void
11031 Relation pkrel, Oid indexOid, Oid parentConstr,
11032 int numfks, int16 *pkattnum, int16 *fkattnum,
11033 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11034 int numfkdelsetcols, int16 *fkdelsetcols,
11035 bool old_check_ok, LOCKMODE lockmode,
11036 Oid parentInsTrigger, Oid parentUpdTrigger,
11037 bool with_period)
11038{
11039 Oid insertTriggerOid = InvalidOid,
11040 updateTriggerOid = InvalidOid;
11041
11042 Assert(OidIsValid(parentConstr));
11045
11046 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11047 ereport(ERROR,
11048 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11049 errmsg("foreign key constraints are not supported on foreign tables")));
11050
11051 /*
11052 * Add check triggers if the constraint is ENFORCED, and if needed,
11053 * schedule them to be checked in Phase 3.
11054 *
11055 * If the relation is partitioned, drill down to do it to its partitions.
11056 */
11057 if (fkconstraint->is_enforced)
11059 RelationGetRelid(pkrel),
11060 fkconstraint,
11061 parentConstr,
11062 indexOid,
11063 parentInsTrigger, parentUpdTrigger,
11064 &insertTriggerOid, &updateTriggerOid);
11065
11066 if (rel->rd_rel->relkind == RELKIND_RELATION)
11067 {
11068 /*
11069 * Tell Phase 3 to check that the constraint is satisfied by existing
11070 * rows. We can skip this during table creation, when constraint is
11071 * specified as NOT ENFORCED, or when requested explicitly by
11072 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11073 * recreating a constraint following a SET DATA TYPE operation that
11074 * did not impugn its validity.
11075 */
11076 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11077 fkconstraint->is_enforced)
11078 {
11079 NewConstraint *newcon;
11080 AlteredTableInfo *tab;
11081
11082 tab = ATGetQueueEntry(wqueue, rel);
11083
11084 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11085 newcon->name = get_constraint_name(parentConstr);
11086 newcon->contype = CONSTR_FOREIGN;
11087 newcon->refrelid = RelationGetRelid(pkrel);
11088 newcon->refindid = indexOid;
11089 newcon->conid = parentConstr;
11090 newcon->conwithperiod = fkconstraint->fk_with_period;
11091 newcon->qual = (Node *) fkconstraint;
11092
11093 tab->constraints = lappend(tab->constraints, newcon);
11094 }
11095 }
11096 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11097 {
11099 Relation trigrel;
11100
11101 /*
11102 * Triggers of the foreign keys will be manipulated a bunch of times
11103 * in the loop below. To avoid repeatedly opening/closing the trigger
11104 * catalog relation, we open it here and pass it to the subroutines
11105 * called below.
11106 */
11107 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11108
11109 /*
11110 * Recurse to take appropriate action on each partition; either we
11111 * find an existing constraint to reparent to ours, or we create a new
11112 * one.
11113 */
11114 for (int i = 0; i < pd->nparts; i++)
11115 {
11116 Relation partition = table_open(pd->oids[i], lockmode);
11117 List *partFKs;
11118 AttrMap *attmap;
11119 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11120 bool attached;
11121 ObjectAddress address;
11122
11123 CheckAlterTableIsSafe(partition);
11124
11125 attmap = build_attrmap_by_name(RelationGetDescr(partition),
11126 RelationGetDescr(rel),
11127 false);
11128 for (int j = 0; j < numfks; j++)
11129 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11130
11131 /* Check whether an existing constraint can be repurposed */
11132 partFKs = copyObject(RelationGetFKeyList(partition));
11133 attached = false;
11134 foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11135 {
11137 fk,
11138 partition,
11139 parentConstr,
11140 numfks,
11141 mapped_fkattnum,
11142 pkattnum,
11143 pfeqoperators,
11144 insertTriggerOid,
11145 updateTriggerOid,
11146 trigrel))
11147 {
11148 attached = true;
11149 break;
11150 }
11151 }
11152 if (attached)
11153 {
11154 table_close(partition, NoLock);
11155 continue;
11156 }
11157
11158 /*
11159 * No luck finding a good constraint to reuse; create our own.
11160 */
11162 fkconstraint->conname, fkconstraint,
11163 partition, pkrel, indexOid, parentConstr,
11164 numfks, pkattnum,
11165 mapped_fkattnum, pfeqoperators,
11166 ppeqoperators, ffeqoperators,
11167 numfkdelsetcols, fkdelsetcols, true,
11168 with_period);
11169
11170 /* call ourselves to finalize the creation and we're done */
11171 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11172 indexOid,
11173 address.objectId,
11174 numfks,
11175 pkattnum,
11176 mapped_fkattnum,
11177 pfeqoperators,
11178 ppeqoperators,
11179 ffeqoperators,
11180 numfkdelsetcols,
11181 fkdelsetcols,
11182 old_check_ok,
11183 lockmode,
11184 insertTriggerOid,
11185 updateTriggerOid,
11186 with_period);
11187
11188 table_close(partition, NoLock);
11189 }
11190
11191 table_close(trigrel, RowExclusiveLock);
11192 }
11193}
11194
11195/*
11196 * CloneForeignKeyConstraints
11197 * Clone foreign keys from a partitioned table to a newly acquired
11198 * partition.
11199 *
11200 * partitionRel is a partition of parentRel, so we can be certain that it has
11201 * the same columns with the same datatypes. The columns may be in different
11202 * order, though.
11203 *
11204 * wqueue must be passed to set up phase 3 constraint checking, unless the
11205 * referencing-side partition is known to be empty (such as in CREATE TABLE /
11206 * PARTITION OF).
11207 */
11208static void
11210 Relation partitionRel)
11211{
11212 /* This only works for declarative partitioning */
11213 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11214
11215 /*
11216 * First, clone constraints where the parent is on the referencing side.
11217 */
11218 CloneFkReferencing(wqueue, parentRel, partitionRel);
11219
11220 /*
11221 * Clone constraints for which the parent is on the referenced side.
11222 */
11223 CloneFkReferenced(parentRel, partitionRel);
11224}
11225
11226/*
11227 * CloneFkReferenced
11228 * Subroutine for CloneForeignKeyConstraints
11229 *
11230 * Find all the FKs that have the parent relation on the referenced side;
11231 * clone those constraints to the given partition. This is to be called
11232 * when the partition is being created or attached.
11233 *
11234 * This recurses to partitions, if the relation being attached is partitioned.
11235 * Recursion is done by calling addFkRecurseReferenced.
11236 */
11237static void
11238CloneFkReferenced(Relation parentRel, Relation partitionRel)
11239{
11240 Relation pg_constraint;
11241 AttrMap *attmap;
11242 ListCell *cell;
11243 SysScanDesc scan;
11244 ScanKeyData key[2];
11245 HeapTuple tuple;
11246 List *clone = NIL;
11247 Relation trigrel;
11248
11249 /*
11250 * Search for any constraints where this partition's parent is in the
11251 * referenced side. However, we must not clone any constraint whose
11252 * parent constraint is also going to be cloned, to avoid duplicates. So
11253 * do it in two steps: first construct the list of constraints to clone,
11254 * then go over that list cloning those whose parents are not in the list.
11255 * (We must not rely on the parent being seen first, since the catalog
11256 * scan could return children first.)
11257 */
11258 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11259 ScanKeyInit(&key[0],
11260 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11261 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11262 ScanKeyInit(&key[1],
11263 Anum_pg_constraint_contype, BTEqualStrategyNumber,
11264 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11265 /* This is a seqscan, as we don't have a usable index ... */
11266 scan = systable_beginscan(pg_constraint, InvalidOid, true,
11267 NULL, 2, key);
11268 while ((tuple = systable_getnext(scan)) != NULL)
11269 {
11270 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11271
11272 clone = lappend_oid(clone, constrForm->oid);
11273 }
11274 systable_endscan(scan);
11275 table_close(pg_constraint, RowShareLock);
11276
11277 /*
11278 * Triggers of the foreign keys will be manipulated a bunch of times in
11279 * the loop below. To avoid repeatedly opening/closing the trigger
11280 * catalog relation, we open it here and pass it to the subroutines called
11281 * below.
11282 */
11283 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11284
11285 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11286 RelationGetDescr(parentRel),
11287 false);
11288 foreach(cell, clone)
11289 {
11290 Oid constrOid = lfirst_oid(cell);
11291 Form_pg_constraint constrForm;
11292 Relation fkRel;
11293 Oid indexOid;
11294 Oid partIndexId;
11295 int numfks;
11296 AttrNumber conkey[INDEX_MAX_KEYS];
11297 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11298 AttrNumber confkey[INDEX_MAX_KEYS];
11299 Oid conpfeqop[INDEX_MAX_KEYS];
11300 Oid conppeqop[INDEX_MAX_KEYS];
11301 Oid conffeqop[INDEX_MAX_KEYS];
11302 int numfkdelsetcols;
11303 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11304 Constraint *fkconstraint;
11305 ObjectAddress address;
11306 Oid deleteTriggerOid = InvalidOid,
11307 updateTriggerOid = InvalidOid;
11308
11309 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11310 if (!HeapTupleIsValid(tuple))
11311 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11312 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11313
11314 /*
11315 * As explained above: don't try to clone a constraint for which we're
11316 * going to clone the parent.
11317 */
11318 if (list_member_oid(clone, constrForm->conparentid))
11319 {
11320 ReleaseSysCache(tuple);
11321 continue;
11322 }
11323
11324 /* We need the same lock level that CreateTrigger will acquire */
11325 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11326
11327 indexOid = constrForm->conindid;
11329 &numfks,
11330 conkey,
11331 confkey,
11332 conpfeqop,
11333 conppeqop,
11334 conffeqop,
11335 &numfkdelsetcols,
11336 confdelsetcols);
11337
11338 for (int i = 0; i < numfks; i++)
11339 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11340
11341 fkconstraint = makeNode(Constraint);
11342 fkconstraint->contype = CONSTRAINT_FOREIGN;
11343 fkconstraint->conname = NameStr(constrForm->conname);
11344 fkconstraint->deferrable = constrForm->condeferrable;
11345 fkconstraint->initdeferred = constrForm->condeferred;
11346 fkconstraint->location = -1;
11347 fkconstraint->pktable = NULL;
11348 /* ->fk_attrs determined below */
11349 fkconstraint->pk_attrs = NIL;
11350 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11351 fkconstraint->fk_upd_action = constrForm->confupdtype;
11352 fkconstraint->fk_del_action = constrForm->confdeltype;
11353 fkconstraint->fk_del_set_cols = NIL;
11354 fkconstraint->old_conpfeqop = NIL;
11355 fkconstraint->old_pktable_oid = InvalidOid;
11356 fkconstraint->is_enforced = constrForm->conenforced;
11357 fkconstraint->skip_validation = false;
11358 fkconstraint->initially_valid = constrForm->convalidated;
11359
11360 /* set up colnames that are used to generate the constraint name */
11361 for (int i = 0; i < numfks; i++)
11362 {
11364
11365 att = TupleDescAttr(RelationGetDescr(fkRel),
11366 conkey[i] - 1);
11367 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11368 makeString(NameStr(att->attname)));
11369 }
11370
11371 /*
11372 * Add the new foreign key constraint pointing to the new partition.
11373 * Because this new partition appears in the referenced side of the
11374 * constraint, we don't need to set up for Phase 3 check.
11375 */
11376 partIndexId = index_get_partition(partitionRel, indexOid);
11377 if (!OidIsValid(partIndexId))
11378 elog(ERROR, "index for %u not found in partition %s",
11379 indexOid, RelationGetRelationName(partitionRel));
11380
11381 /*
11382 * Get the "action" triggers belonging to the constraint to pass as
11383 * parent OIDs for similar triggers that will be created on the
11384 * partition in addFkRecurseReferenced().
11385 */
11386 if (constrForm->conenforced)
11387 GetForeignKeyActionTriggers(trigrel, constrOid,
11388 constrForm->confrelid, constrForm->conrelid,
11389 &deleteTriggerOid, &updateTriggerOid);
11390
11391 /* Add this constraint ... */
11393 fkconstraint->conname, fkconstraint, fkRel,
11394 partitionRel, partIndexId, constrOid,
11395 numfks, mapped_confkey,
11396 conkey, conpfeqop, conppeqop, conffeqop,
11397 numfkdelsetcols, confdelsetcols, false,
11398 constrForm->conperiod);
11399 /* ... and recurse */
11400 addFkRecurseReferenced(fkconstraint,
11401 fkRel,
11402 partitionRel,
11403 partIndexId,
11404 address.objectId,
11405 numfks,
11406 mapped_confkey,
11407 conkey,
11408 conpfeqop,
11409 conppeqop,
11410 conffeqop,
11411 numfkdelsetcols,
11412 confdelsetcols,
11413 true,
11414 deleteTriggerOid,
11415 updateTriggerOid,
11416 constrForm->conperiod);
11417
11418 table_close(fkRel, NoLock);
11419 ReleaseSysCache(tuple);
11420 }
11421
11422 table_close(trigrel, RowExclusiveLock);
11423}
11424
11425/*
11426 * CloneFkReferencing
11427 * Subroutine for CloneForeignKeyConstraints
11428 *
11429 * For each FK constraint of the parent relation in the given list, find an
11430 * equivalent constraint in its partition relation that can be reparented;
11431 * if one cannot be found, create a new constraint in the partition as its
11432 * child.
11433 *
11434 * If wqueue is given, it is used to set up phase-3 verification for each
11435 * cloned constraint; omit it if such verification is not needed
11436 * (example: the partition is being created anew).
11437 */
11438static void
11439CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11440{
11441 AttrMap *attmap;
11442 List *partFKs;
11443 List *clone = NIL;
11444 ListCell *cell;
11445 Relation trigrel;
11446
11447 /* obtain a list of constraints that we need to clone */
11448 foreach(cell, RelationGetFKeyList(parentRel))
11449 {
11450 ForeignKeyCacheInfo *fk = lfirst(cell);
11451
11452 /*
11453 * Refuse to attach a table as partition that this partitioned table
11454 * already has a foreign key to. This isn't useful schema, which is
11455 * proven by the fact that there have been no user complaints that
11456 * it's already impossible to achieve this in the opposite direction,
11457 * i.e., creating a foreign key that references a partition. This
11458 * restriction allows us to dodge some complexities around
11459 * pg_constraint and pg_trigger row creations that would be needed
11460 * during ATTACH/DETACH for this kind of relationship.
11461 */
11462 if (fk->confrelid == RelationGetRelid(partRel))
11463 ereport(ERROR,
11464 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11465 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11466 RelationGetRelationName(partRel),
11468
11469 clone = lappend_oid(clone, fk->conoid);
11470 }
11471
11472 /*
11473 * Silently do nothing if there's nothing to do. In particular, this
11474 * avoids throwing a spurious error for foreign tables.
11475 */
11476 if (clone == NIL)
11477 return;
11478
11479 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11480 ereport(ERROR,
11481 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11482 errmsg("foreign key constraints are not supported on foreign tables")));
11483
11484 /*
11485 * Triggers of the foreign keys will be manipulated a bunch of times in
11486 * the loop below. To avoid repeatedly opening/closing the trigger
11487 * catalog relation, we open it here and pass it to the subroutines called
11488 * below.
11489 */
11490 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11491
11492 /*
11493 * The constraint key may differ, if the columns in the partition are
11494 * different. This map is used to convert them.
11495 */
11496 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11497 RelationGetDescr(parentRel),
11498 false);
11499
11500 partFKs = copyObject(RelationGetFKeyList(partRel));
11501
11502 foreach(cell, clone)
11503 {
11504 Oid parentConstrOid = lfirst_oid(cell);
11505 Form_pg_constraint constrForm;
11506 Relation pkrel;
11507 HeapTuple tuple;
11508 int numfks;
11509 AttrNumber conkey[INDEX_MAX_KEYS];
11510 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11511 AttrNumber confkey[INDEX_MAX_KEYS];
11512 Oid conpfeqop[INDEX_MAX_KEYS];
11513 Oid conppeqop[INDEX_MAX_KEYS];
11514 Oid conffeqop[INDEX_MAX_KEYS];
11515 int numfkdelsetcols;
11516 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11517 Constraint *fkconstraint;
11518 bool attached;
11519 Oid indexOid;
11520 ObjectAddress address;
11521 ListCell *lc;
11522 Oid insertTriggerOid = InvalidOid,
11523 updateTriggerOid = InvalidOid;
11524 bool with_period;
11525
11526 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11527 if (!HeapTupleIsValid(tuple))
11528 elog(ERROR, "cache lookup failed for constraint %u",
11529 parentConstrOid);
11530 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11531
11532 /* Don't clone constraints whose parents are being cloned */
11533 if (list_member_oid(clone, constrForm->conparentid))
11534 {
11535 ReleaseSysCache(tuple);
11536 continue;
11537 }
11538
11539 /*
11540 * Need to prevent concurrent deletions. If pkrel is a partitioned
11541 * relation, that means to lock all partitions.
11542 */
11543 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11544 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11546 ShareRowExclusiveLock, NULL);
11547
11548 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11549 conpfeqop, conppeqop, conffeqop,
11550 &numfkdelsetcols, confdelsetcols);
11551 for (int i = 0; i < numfks; i++)
11552 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11553
11554 /*
11555 * Get the "check" triggers belonging to the constraint, if it is
11556 * ENFORCED, to pass as parent OIDs for similar triggers that will be
11557 * created on the partition in addFkRecurseReferencing(). They are
11558 * also passed to tryAttachPartitionForeignKey() below to simply
11559 * assign as parents to the partition's existing "check" triggers,
11560 * that is, if the corresponding constraints is deemed attachable to
11561 * the parent constraint.
11562 */
11563 if (constrForm->conenforced)
11564 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11565 constrForm->confrelid, constrForm->conrelid,
11566 &insertTriggerOid, &updateTriggerOid);
11567
11568 /*
11569 * Before creating a new constraint, see whether any existing FKs are
11570 * fit for the purpose. If one is, attach the parent constraint to
11571 * it, and don't clone anything. This way we avoid the expensive
11572 * verification step and don't end up with a duplicate FK, and we
11573 * don't need to recurse to partitions for this constraint.
11574 */
11575 attached = false;
11576 foreach(lc, partFKs)
11577 {
11579
11581 fk,
11582 partRel,
11583 parentConstrOid,
11584 numfks,
11585 mapped_conkey,
11586 confkey,
11587 conpfeqop,
11588 insertTriggerOid,
11589 updateTriggerOid,
11590 trigrel))
11591 {
11592 attached = true;
11593 table_close(pkrel, NoLock);
11594 break;
11595 }
11596 }
11597 if (attached)
11598 {
11599 ReleaseSysCache(tuple);
11600 continue;
11601 }
11602
11603 /* No dice. Set up to create our own constraint */
11604 fkconstraint = makeNode(Constraint);
11605 fkconstraint->contype = CONSTRAINT_FOREIGN;
11606 /* ->conname determined below */
11607 fkconstraint->deferrable = constrForm->condeferrable;
11608 fkconstraint->initdeferred = constrForm->condeferred;
11609 fkconstraint->location = -1;
11610 fkconstraint->pktable = NULL;
11611 /* ->fk_attrs determined below */
11612 fkconstraint->pk_attrs = NIL;
11613 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11614 fkconstraint->fk_upd_action = constrForm->confupdtype;
11615 fkconstraint->fk_del_action = constrForm->confdeltype;
11616 fkconstraint->fk_del_set_cols = NIL;
11617 fkconstraint->old_conpfeqop = NIL;
11618 fkconstraint->old_pktable_oid = InvalidOid;
11619 fkconstraint->is_enforced = constrForm->conenforced;
11620 fkconstraint->skip_validation = false;
11621 fkconstraint->initially_valid = constrForm->convalidated;
11622 for (int i = 0; i < numfks; i++)
11623 {
11625
11626 att = TupleDescAttr(RelationGetDescr(partRel),
11627 mapped_conkey[i] - 1);
11628 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11629 makeString(NameStr(att->attname)));
11630 }
11631
11632 indexOid = constrForm->conindid;
11633 with_period = constrForm->conperiod;
11634
11635 /* Create the pg_constraint entry at this level */
11637 NameStr(constrForm->conname), fkconstraint,
11638 partRel, pkrel, indexOid, parentConstrOid,
11639 numfks, confkey,
11640 mapped_conkey, conpfeqop,
11641 conppeqop, conffeqop,
11642 numfkdelsetcols, confdelsetcols,
11643 false, with_period);
11644
11645 /* Done with the cloned constraint's tuple */
11646 ReleaseSysCache(tuple);
11647
11648 /* Create the check triggers, and recurse to partitions, if any */
11650 fkconstraint,
11651 partRel,
11652 pkrel,
11653 indexOid,
11654 address.objectId,
11655 numfks,
11656 confkey,
11657 mapped_conkey,
11658 conpfeqop,
11659 conppeqop,
11660 conffeqop,
11661 numfkdelsetcols,
11662 confdelsetcols,
11663 false, /* no old check exists */
11665 insertTriggerOid,
11666 updateTriggerOid,
11667 with_period);
11668 table_close(pkrel, NoLock);
11669 }
11670
11671 table_close(trigrel, RowExclusiveLock);
11672}
11673
11674/*
11675 * When the parent of a partition receives [the referencing side of] a foreign
11676 * key, we must propagate that foreign key to the partition. However, the
11677 * partition might already have an equivalent foreign key; this routine
11678 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11679 * by the other parameters. If they are equivalent, create the link between
11680 * the two constraints and return true.
11681 *
11682 * If the given FK does not match the one defined by rest of the params,
11683 * return false.
11684 */
11685static bool
11688 Relation partition,
11689 Oid parentConstrOid,
11690 int numfks,
11691 AttrNumber *mapped_conkey,
11692 AttrNumber *confkey,
11693 Oid *conpfeqop,
11694 Oid parentInsTrigger,
11695 Oid parentUpdTrigger,
11696 Relation trigrel)
11697{
11698 HeapTuple parentConstrTup;
11699 Form_pg_constraint parentConstr;
11700 HeapTuple partcontup;
11701 Form_pg_constraint partConstr;
11702
11703 parentConstrTup = SearchSysCache1(CONSTROID,
11704 ObjectIdGetDatum(parentConstrOid));
11705 if (!HeapTupleIsValid(parentConstrTup))
11706 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11707 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11708
11709 /*
11710 * Do some quick & easy initial checks. If any of these fail, we cannot
11711 * use this constraint.
11712 */
11713 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11714 {
11715 ReleaseSysCache(parentConstrTup);
11716 return false;
11717 }
11718 for (int i = 0; i < numfks; i++)
11719 {
11720 if (fk->conkey[i] != mapped_conkey[i] ||
11721 fk->confkey[i] != confkey[i] ||
11722 fk->conpfeqop[i] != conpfeqop[i])
11723 {
11724 ReleaseSysCache(parentConstrTup);
11725 return false;
11726 }
11727 }
11728
11729 /* Looks good so far; perform more extensive checks. */
11730 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11731 if (!HeapTupleIsValid(partcontup))
11732 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11733 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11734
11735 /*
11736 * An error should be raised if the constraint enforceability is
11737 * different. Returning false without raising an error, as we do for other
11738 * attributes, could lead to a duplicate constraint with the same
11739 * enforceability as the parent. While this may be acceptable, it may not
11740 * be ideal. Therefore, it's better to raise an error and allow the user
11741 * to correct the enforceability before proceeding.
11742 */
11743 if (partConstr->conenforced != parentConstr->conenforced)
11744 ereport(ERROR,
11745 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11746 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11747 NameStr(parentConstr->conname),
11748 NameStr(partConstr->conname),
11749 RelationGetRelationName(partition))));
11750
11751 if (OidIsValid(partConstr->conparentid) ||
11752 partConstr->condeferrable != parentConstr->condeferrable ||
11753 partConstr->condeferred != parentConstr->condeferred ||
11754 partConstr->confupdtype != parentConstr->confupdtype ||
11755 partConstr->confdeltype != parentConstr->confdeltype ||
11756 partConstr->confmatchtype != parentConstr->confmatchtype)
11757 {
11758 ReleaseSysCache(parentConstrTup);
11759 ReleaseSysCache(partcontup);
11760 return false;
11761 }
11762
11763 ReleaseSysCache(parentConstrTup);
11764 ReleaseSysCache(partcontup);
11765
11766 /* Looks good! Attach this constraint. */
11767 AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11768 parentConstrOid, parentInsTrigger,
11769 parentUpdTrigger, trigrel);
11770
11771 return true;
11772}
11773
11774/*
11775 * AttachPartitionForeignKey
11776 *
11777 * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11778 * attaching the constraint, removing redundant triggers and entries from
11779 * pg_constraint, and setting the constraint's parent.
11780 */
11781static void
11783 Relation partition,
11784 Oid partConstrOid,
11785 Oid parentConstrOid,
11786 Oid parentInsTrigger,
11787 Oid parentUpdTrigger,
11788 Relation trigrel)
11789{
11790 HeapTuple parentConstrTup;
11791 Form_pg_constraint parentConstr;
11792 HeapTuple partcontup;
11793 Form_pg_constraint partConstr;
11794 bool queueValidation;
11795 Oid partConstrFrelid;
11796 Oid partConstrRelid;
11797 bool parentConstrIsEnforced;
11798
11799 /* Fetch the parent constraint tuple */
11800 parentConstrTup = SearchSysCache1(CONSTROID,
11801 ObjectIdGetDatum(parentConstrOid));
11802 if (!HeapTupleIsValid(parentConstrTup))
11803 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11804 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11805 parentConstrIsEnforced = parentConstr->conenforced;
11806
11807 /* Fetch the child constraint tuple */
11808 partcontup = SearchSysCache1(CONSTROID,
11809 ObjectIdGetDatum(partConstrOid));
11810 if (!HeapTupleIsValid(partcontup))
11811 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11812 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11813 partConstrFrelid = partConstr->confrelid;
11814 partConstrRelid = partConstr->conrelid;
11815
11816 /*
11817 * If the referenced table is partitioned, then the partition we're
11818 * attaching now has extra pg_constraint rows and action triggers that are
11819 * no longer needed. Remove those.
11820 */
11821 if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11822 {
11823 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11824
11825 RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11826 partConstrRelid);
11827
11828 table_close(pg_constraint, RowShareLock);
11829 }
11830
11831 /*
11832 * Will we need to validate this constraint? A valid parent constraint
11833 * implies that all child constraints have been validated, so if this one
11834 * isn't, we must trigger phase 3 validation.
11835 */
11836 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11837
11838 ReleaseSysCache(partcontup);
11839 ReleaseSysCache(parentConstrTup);
11840
11841 /*
11842 * The action triggers in the new partition become redundant -- the parent
11843 * table already has equivalent ones, and those will be able to reach the
11844 * partition. Remove the ones in the partition. We identify them because
11845 * they have our constraint OID, as well as being on the referenced rel.
11846 */
11847 DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11848 partConstrRelid);
11849
11850 ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11851 RelationGetRelid(partition));
11852
11853 /*
11854 * Like the constraint, attach partition's "check" triggers to the
11855 * corresponding parent triggers if the constraint is ENFORCED. NOT
11856 * ENFORCED constraints do not have these triggers.
11857 */
11858 if (parentConstrIsEnforced)
11859 {
11860 Oid insertTriggerOid,
11861 updateTriggerOid;
11862
11864 partConstrOid, partConstrFrelid, partConstrRelid,
11865 &insertTriggerOid, &updateTriggerOid);
11866 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11867 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11868 RelationGetRelid(partition));
11869 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11870 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11871 RelationGetRelid(partition));
11872 }
11873
11874 /*
11875 * We updated this pg_constraint row above to set its parent; validating
11876 * it will cause its convalidated flag to change, so we need CCI here. In
11877 * addition, we need it unconditionally for the rare case where the parent
11878 * table has *two* identical constraints; when reaching this function for
11879 * the second one, we must have made our changes visible, otherwise we
11880 * would try to attach both to this one.
11881 */
11883
11884 /* If validation is needed, put it in the queue now. */
11885 if (queueValidation)
11886 {
11887 Relation conrel;
11888 Oid confrelid;
11889
11890 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11891
11892 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11893 if (!HeapTupleIsValid(partcontup))
11894 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11895
11896 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11897
11898 /* Use the same lock as for AT_ValidateConstraint */
11899 QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11900 partcontup, ShareUpdateExclusiveLock);
11901 ReleaseSysCache(partcontup);
11903 }
11904}
11905
11906/*
11907 * RemoveInheritedConstraint
11908 *
11909 * Removes the constraint and its associated trigger from the specified
11910 * relation, which inherited the given constraint.
11911 */
11912static void
11914 Oid conrelid)
11915{
11916 ObjectAddresses *objs;
11917 HeapTuple consttup;
11919 SysScanDesc scan;
11920 HeapTuple trigtup;
11921
11923 Anum_pg_constraint_conrelid,
11924 BTEqualStrategyNumber, F_OIDEQ,
11925 ObjectIdGetDatum(conrelid));
11926
11927 scan = systable_beginscan(conrel,
11928 ConstraintRelidTypidNameIndexId,
11929 true, NULL, 1, &key);
11930 objs = new_object_addresses();
11931 while ((consttup = systable_getnext(scan)) != NULL)
11932 {
11933 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11934
11935 if (conform->conparentid != conoid)
11936 continue;
11937 else
11938 {
11939 ObjectAddress addr;
11940 SysScanDesc scan2;
11941 ScanKeyData key2;
11943
11944 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11945 add_exact_object_address(&addr, objs);
11946
11947 /*
11948 * First we must delete the dependency record that binds the
11949 * constraint records together.
11950 */
11951 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11952 conform->oid,
11954 ConstraintRelationId,
11955 conoid);
11956 Assert(n == 1); /* actually only one is expected */
11957
11958 /*
11959 * Now search for the triggers for this constraint and set them up
11960 * for deletion too
11961 */
11962 ScanKeyInit(&key2,
11963 Anum_pg_trigger_tgconstraint,
11964 BTEqualStrategyNumber, F_OIDEQ,
11965 ObjectIdGetDatum(conform->oid));
11966 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11967 true, NULL, 1, &key2);
11968 while ((trigtup = systable_getnext(scan2)) != NULL)
11969 {
11970 ObjectAddressSet(addr, TriggerRelationId,
11971 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11972 add_exact_object_address(&addr, objs);
11973 }
11974 systable_endscan(scan2);
11975 }
11976 }
11977 /* make the dependency deletions visible */
11981 systable_endscan(scan);
11982}
11983
11984/*
11985 * DropForeignKeyConstraintTriggers
11986 *
11987 * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11988 * action triggers for the foreign key constraint.
11989 *
11990 * If valid confrelid and conrelid values are not provided, the respective
11991 * trigger check will be skipped, and the trigger will be considered for
11992 * removal.
11993 */
11994static void
11996 Oid conrelid)
11997{
11999 SysScanDesc scan;
12000 HeapTuple trigtup;
12001
12003 Anum_pg_trigger_tgconstraint,
12004 BTEqualStrategyNumber, F_OIDEQ,
12005 ObjectIdGetDatum(conoid));
12006 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12007 NULL, 1, &key);
12008 while ((trigtup = systable_getnext(scan)) != NULL)
12009 {
12010 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12011 ObjectAddress trigger;
12012
12013 /* Invalid if trigger is not for a referential integrity constraint */
12014 if (!OidIsValid(trgform->tgconstrrelid))
12015 continue;
12016 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12017 continue;
12018 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12019 continue;
12020
12021 /* We should be dropping trigger related to foreign key constraint */
12022 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12023 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12024 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12025 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12026 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12027 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12028 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12029 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12030 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12031 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12032 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12033 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12034
12035 /*
12036 * The constraint is originally set up to contain this trigger as an
12037 * implementation object, so there's a dependency record that links
12038 * the two; however, since the trigger is no longer needed, we remove
12039 * the dependency link in order to be able to drop the trigger while
12040 * keeping the constraint intact.
12041 */
12042 deleteDependencyRecordsFor(TriggerRelationId,
12043 trgform->oid,
12044 false);
12045 /* make dependency deletion visible to performDeletion */
12047 ObjectAddressSet(trigger, TriggerRelationId,
12048 trgform->oid);
12049 performDeletion(&trigger, DROP_RESTRICT, 0);
12050 /* make trigger drop visible, in case the loop iterates */
12052 }
12053
12054 systable_endscan(scan);
12055}
12056
12057/*
12058 * GetForeignKeyActionTriggers
12059 * Returns delete and update "action" triggers of the given relation
12060 * belonging to the given constraint
12061 */
12062static void
12064 Oid conoid, Oid confrelid, Oid conrelid,
12065 Oid *deleteTriggerOid,
12066 Oid *updateTriggerOid)
12067{
12069 SysScanDesc scan;
12070 HeapTuple trigtup;
12071
12072 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12074 Anum_pg_trigger_tgconstraint,
12075 BTEqualStrategyNumber, F_OIDEQ,
12076 ObjectIdGetDatum(conoid));
12077
12078 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12079 NULL, 1, &key);
12080 while ((trigtup = systable_getnext(scan)) != NULL)
12081 {
12082 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12083
12084 if (trgform->tgconstrrelid != conrelid)
12085 continue;
12086 if (trgform->tgrelid != confrelid)
12087 continue;
12088 /* Only ever look at "action" triggers on the PK side. */
12089 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12090 continue;
12091 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12092 {
12093 Assert(*deleteTriggerOid == InvalidOid);
12094 *deleteTriggerOid = trgform->oid;
12095 }
12096 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12097 {
12098 Assert(*updateTriggerOid == InvalidOid);
12099 *updateTriggerOid = trgform->oid;
12100 }
12101#ifndef USE_ASSERT_CHECKING
12102 /* In an assert-enabled build, continue looking to find duplicates */
12103 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12104 break;
12105#endif
12106 }
12107
12108 if (!OidIsValid(*deleteTriggerOid))
12109 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12110 conoid);
12111 if (!OidIsValid(*updateTriggerOid))
12112 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12113 conoid);
12114
12115 systable_endscan(scan);
12116}
12117
12118/*
12119 * GetForeignKeyCheckTriggers
12120 * Returns insert and update "check" triggers of the given relation
12121 * belonging to the given constraint
12122 */
12123static void
12125 Oid conoid, Oid confrelid, Oid conrelid,
12126 Oid *insertTriggerOid,
12127 Oid *updateTriggerOid)
12128{
12130 SysScanDesc scan;
12131 HeapTuple trigtup;
12132
12133 *insertTriggerOid = *updateTriggerOid = InvalidOid;
12135 Anum_pg_trigger_tgconstraint,
12136 BTEqualStrategyNumber, F_OIDEQ,
12137 ObjectIdGetDatum(conoid));
12138
12139 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12140 NULL, 1, &key);
12141 while ((trigtup = systable_getnext(scan)) != NULL)
12142 {
12143 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12144
12145 if (trgform->tgconstrrelid != confrelid)
12146 continue;
12147 if (trgform->tgrelid != conrelid)
12148 continue;
12149 /* Only ever look at "check" triggers on the FK side. */
12150 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12151 continue;
12152 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12153 {
12154 Assert(*insertTriggerOid == InvalidOid);
12155 *insertTriggerOid = trgform->oid;
12156 }
12157 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12158 {
12159 Assert(*updateTriggerOid == InvalidOid);
12160 *updateTriggerOid = trgform->oid;
12161 }
12162#ifndef USE_ASSERT_CHECKING
12163 /* In an assert-enabled build, continue looking to find duplicates. */
12164 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12165 break;
12166#endif
12167 }
12168
12169 if (!OidIsValid(*insertTriggerOid))
12170 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12171 conoid);
12172 if (!OidIsValid(*updateTriggerOid))
12173 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12174 conoid);
12175
12176 systable_endscan(scan);
12177}
12178
12179/*
12180 * ALTER TABLE ALTER CONSTRAINT
12181 *
12182 * Update the attributes of a constraint.
12183 *
12184 * Currently only works for Foreign Key and not null constraints.
12185 *
12186 * If the constraint is modified, returns its address; otherwise, return
12187 * InvalidObjectAddress.
12188 */
12189static ObjectAddress
12191 bool recurse, LOCKMODE lockmode)
12192{
12193 Relation conrel;
12194 Relation tgrel;
12195 SysScanDesc scan;
12196 ScanKeyData skey[3];
12197 HeapTuple contuple;
12198 Form_pg_constraint currcon;
12199 ObjectAddress address;
12200
12201 /*
12202 * Disallow altering ONLY a partitioned table, as it would make no sense.
12203 * This is okay for legacy inheritance.
12204 */
12205 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12206 ereport(ERROR,
12207 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12208 errmsg("constraint must be altered in child tables too"),
12209 errhint("Do not specify the ONLY keyword."));
12210
12211
12212 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12213 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12214
12215 /*
12216 * Find and check the target constraint
12217 */
12218 ScanKeyInit(&skey[0],
12219 Anum_pg_constraint_conrelid,
12220 BTEqualStrategyNumber, F_OIDEQ,
12222 ScanKeyInit(&skey[1],
12223 Anum_pg_constraint_contypid,
12224 BTEqualStrategyNumber, F_OIDEQ,
12226 ScanKeyInit(&skey[2],
12227 Anum_pg_constraint_conname,
12228 BTEqualStrategyNumber, F_NAMEEQ,
12229 CStringGetDatum(cmdcon->conname));
12230 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12231 true, NULL, 3, skey);
12232
12233 /* There can be at most one matching row */
12234 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12235 ereport(ERROR,
12236 (errcode(ERRCODE_UNDEFINED_OBJECT),
12237 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12238 cmdcon->conname, RelationGetRelationName(rel))));
12239
12240 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12241 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12242 ereport(ERROR,
12243 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12244 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12245 cmdcon->conname, RelationGetRelationName(rel))));
12246 if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12247 ereport(ERROR,
12248 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12249 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12250 cmdcon->conname, RelationGetRelationName(rel))));
12251 if (cmdcon->alterInheritability &&
12252 currcon->contype != CONSTRAINT_NOTNULL)
12253 ereport(ERROR,
12254 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12255 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12256 cmdcon->conname, RelationGetRelationName(rel)));
12257
12258 /* Refuse to modify inheritability of inherited constraints */
12259 if (cmdcon->alterInheritability &&
12260 cmdcon->noinherit && currcon->coninhcount > 0)
12261 ereport(ERROR,
12262 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12263 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12264 NameStr(currcon->conname),
12266
12267 /*
12268 * If it's not the topmost constraint, raise an error.
12269 *
12270 * Altering a non-topmost constraint leaves some triggers untouched, since
12271 * they are not directly connected to this constraint; also, pg_dump would
12272 * ignore the deferrability status of the individual constraint, since it
12273 * only dumps topmost constraints. Avoid these problems by refusing this
12274 * operation and telling the user to alter the parent constraint instead.
12275 */
12276 if (OidIsValid(currcon->conparentid))
12277 {
12278 HeapTuple tp;
12279 Oid parent = currcon->conparentid;
12280 char *ancestorname = NULL;
12281 char *ancestortable = NULL;
12282
12283 /* Loop to find the topmost constraint */
12284 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12285 {
12287
12288 /* If no parent, this is the constraint we want */
12289 if (!OidIsValid(contup->conparentid))
12290 {
12291 ancestorname = pstrdup(NameStr(contup->conname));
12292 ancestortable = get_rel_name(contup->conrelid);
12293 ReleaseSysCache(tp);
12294 break;
12295 }
12296
12297 parent = contup->conparentid;
12298 ReleaseSysCache(tp);
12299 }
12300
12301 ereport(ERROR,
12302 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12303 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12304 cmdcon->conname, RelationGetRelationName(rel)),
12305 ancestorname && ancestortable ?
12306 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12307 cmdcon->conname, ancestorname, ancestortable) : 0,
12308 errhint("You may alter the constraint it derives from instead.")));
12309 }
12310
12311 address = InvalidObjectAddress;
12312
12313 /*
12314 * Do the actual catalog work, and recurse if necessary.
12315 */
12316 if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12317 contuple, recurse, lockmode))
12318 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12319
12320 systable_endscan(scan);
12321
12324
12325 return address;
12326}
12327
12328/*
12329 * A subroutine of ATExecAlterConstraint that calls the respective routines for
12330 * altering constraint's enforceability, deferrability or inheritability.
12331 */
12332static bool
12334 Relation conrel, Relation tgrel, Relation rel,
12335 HeapTuple contuple, bool recurse,
12336 LOCKMODE lockmode)
12337{
12338 Form_pg_constraint currcon;
12339 bool changed = false;
12340 List *otherrelids = NIL;
12341
12342 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12343
12344 /*
12345 * Do the catalog work for the enforceability or deferrability change,
12346 * recurse if necessary.
12347 *
12348 * Note that even if deferrability is requested to be altered along with
12349 * enforceability, we don't need to explicitly update multiple entries in
12350 * pg_trigger related to deferrability.
12351 *
12352 * Modifying enforceability involves either creating or dropping the
12353 * trigger, during which the deferrability setting will be adjusted
12354 * automatically.
12355 */
12356 if (cmdcon->alterEnforceability &&
12357 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12358 currcon->conrelid, currcon->confrelid,
12359 contuple, lockmode, InvalidOid,
12361 changed = true;
12362
12363 else if (cmdcon->alterDeferrability &&
12364 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12365 contuple, recurse, &otherrelids,
12366 lockmode))
12367 {
12368 /*
12369 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12370 * the relations having the constraint itself; here we also invalidate
12371 * for relations that have any triggers that are part of the
12372 * constraint.
12373 */
12374 foreach_oid(relid, otherrelids)
12376
12377 changed = true;
12378 }
12379
12380 /*
12381 * Do the catalog work for the inheritability change.
12382 */
12383 if (cmdcon->alterInheritability &&
12384 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12385 lockmode))
12386 changed = true;
12387
12388 return changed;
12389}
12390
12391/*
12392 * Returns true if the constraint's enforceability is altered.
12393 *
12394 * Depending on whether the constraint is being set to ENFORCED or NOT
12395 * ENFORCED, it creates or drops the trigger accordingly.
12396 *
12397 * Note that we must recurse even when trying to change a constraint to not
12398 * enforced if it is already not enforced, in case descendant constraints
12399 * might be enforced and need to be changed to not enforced. Conversely, we
12400 * should do nothing if a constraint is being set to enforced and is already
12401 * enforced, as descendant constraints cannot be different in that case.
12402 */
12403static bool
12405 Relation conrel, Relation tgrel,
12406 Oid fkrelid, Oid pkrelid,
12407 HeapTuple contuple, LOCKMODE lockmode,
12408 Oid ReferencedParentDelTrigger,
12409 Oid ReferencedParentUpdTrigger,
12410 Oid ReferencingParentInsTrigger,
12411 Oid ReferencingParentUpdTrigger)
12412{
12413 Form_pg_constraint currcon;
12414 Oid conoid;
12415 Relation rel;
12416 bool changed = false;
12417
12418 /* Since this function recurses, it could be driven to stack overflow */
12420
12421 Assert(cmdcon->alterEnforceability);
12422
12423 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12424 conoid = currcon->oid;
12425
12426 /* Should be foreign key constraint */
12427 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12428
12429 rel = table_open(currcon->conrelid, lockmode);
12430
12431 if (currcon->conenforced != cmdcon->is_enforced)
12432 {
12433 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12434 changed = true;
12435 }
12436
12437 /* Drop triggers */
12438 if (!cmdcon->is_enforced)
12439 {
12440 /*
12441 * When setting a constraint to NOT ENFORCED, the constraint triggers
12442 * need to be dropped. Therefore, we must process the child relations
12443 * first, followed by the parent, to account for dependencies.
12444 */
12445 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12446 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12447 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12448 fkrelid, pkrelid, contuple,
12449 lockmode, InvalidOid, InvalidOid,
12451
12452 /* Drop all the triggers */
12454 }
12455 else if (changed) /* Create triggers */
12456 {
12457 Oid ReferencedDelTriggerOid = InvalidOid,
12458 ReferencedUpdTriggerOid = InvalidOid,
12459 ReferencingInsTriggerOid = InvalidOid,
12460 ReferencingUpdTriggerOid = InvalidOid;
12461
12462 /* Prepare the minimal information required for trigger creation. */
12463 Constraint *fkconstraint = makeNode(Constraint);
12464
12465 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12466 fkconstraint->fk_matchtype = currcon->confmatchtype;
12467 fkconstraint->fk_upd_action = currcon->confupdtype;
12468 fkconstraint->fk_del_action = currcon->confdeltype;
12469
12470 /* Create referenced triggers */
12471 if (currcon->conrelid == fkrelid)
12472 createForeignKeyActionTriggers(currcon->conrelid,
12473 currcon->confrelid,
12474 fkconstraint,
12475 conoid,
12476 currcon->conindid,
12477 ReferencedParentDelTrigger,
12478 ReferencedParentUpdTrigger,
12479 &ReferencedDelTriggerOid,
12480 &ReferencedUpdTriggerOid);
12481
12482 /* Create referencing triggers */
12483 if (currcon->confrelid == pkrelid)
12484 createForeignKeyCheckTriggers(currcon->conrelid,
12485 pkrelid,
12486 fkconstraint,
12487 conoid,
12488 currcon->conindid,
12489 ReferencingParentInsTrigger,
12490 ReferencingParentUpdTrigger,
12491 &ReferencingInsTriggerOid,
12492 &ReferencingUpdTriggerOid);
12493
12494 /*
12495 * Tell Phase 3 to check that the constraint is satisfied by existing
12496 * rows. Only applies to leaf partitions, and (for constraints that
12497 * reference a partitioned table) only if this is not one of the
12498 * pg_constraint rows that exist solely to support action triggers.
12499 */
12500 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12501 currcon->confrelid == pkrelid)
12502 {
12503 AlteredTableInfo *tab;
12504 NewConstraint *newcon;
12505
12506 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12507 newcon->name = fkconstraint->conname;
12508 newcon->contype = CONSTR_FOREIGN;
12509 newcon->refrelid = currcon->confrelid;
12510 newcon->refindid = currcon->conindid;
12511 newcon->conid = currcon->oid;
12512 newcon->qual = (Node *) fkconstraint;
12513
12514 /* Find or create work queue entry for this table */
12515 tab = ATGetQueueEntry(wqueue, rel);
12516 tab->constraints = lappend(tab->constraints, newcon);
12517 }
12518
12519 /*
12520 * If the table at either end of the constraint is partitioned, we
12521 * need to recurse and create triggers for each constraint that is a
12522 * child of this one.
12523 */
12524 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12525 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12526 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12527 fkrelid, pkrelid, contuple,
12528 lockmode, ReferencedDelTriggerOid,
12529 ReferencedUpdTriggerOid,
12530 ReferencingInsTriggerOid,
12531 ReferencingUpdTriggerOid);
12532 }
12533
12534 table_close(rel, NoLock);
12535
12536 return changed;
12537}
12538
12539/*
12540 * Returns true if the constraint's deferrability is altered.
12541 *
12542 * *otherrelids is appended OIDs of relations containing affected triggers.
12543 *
12544 * Note that we must recurse even when the values are correct, in case
12545 * indirect descendants have had their constraints altered locally.
12546 * (This could be avoided if we forbade altering constraints in partitions
12547 * but existing releases don't do that.)
12548 */
12549static bool
12551 Relation conrel, Relation tgrel, Relation rel,
12552 HeapTuple contuple, bool recurse,
12553 List **otherrelids, LOCKMODE lockmode)
12554{
12555 Form_pg_constraint currcon;
12556 Oid refrelid;
12557 bool changed = false;
12558
12559 /* since this function recurses, it could be driven to stack overflow */
12561
12562 Assert(cmdcon->alterDeferrability);
12563
12564 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12565 refrelid = currcon->confrelid;
12566
12567 /* Should be foreign key constraint */
12568 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12569
12570 /*
12571 * If called to modify a constraint that's already in the desired state,
12572 * silently do nothing.
12573 */
12574 if (currcon->condeferrable != cmdcon->deferrable ||
12575 currcon->condeferred != cmdcon->initdeferred)
12576 {
12577 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12578 changed = true;
12579
12580 /*
12581 * Now we need to update the multiple entries in pg_trigger that
12582 * implement the constraint.
12583 */
12584 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12585 cmdcon->deferrable,
12586 cmdcon->initdeferred, otherrelids);
12587 }
12588
12589 /*
12590 * If the table at either end of the constraint is partitioned, we need to
12591 * handle every constraint that is a child of this one.
12592 */
12593 if (recurse && changed &&
12594 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12595 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12596 AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12597 contuple, recurse, otherrelids,
12598 lockmode);
12599
12600 return changed;
12601}
12602
12603/*
12604 * Returns true if the constraint's inheritability is altered.
12605 */
12606static bool
12608 Relation conrel, Relation rel,
12609 HeapTuple contuple, LOCKMODE lockmode)
12610{
12611 Form_pg_constraint currcon;
12612 AttrNumber colNum;
12613 char *colName;
12614 List *children;
12615
12616 Assert(cmdcon->alterInheritability);
12617
12618 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12619
12620 /* The current implementation only works for NOT NULL constraints */
12621 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12622
12623 /*
12624 * If called to modify a constraint that's already in the desired state,
12625 * silently do nothing.
12626 */
12627 if (cmdcon->noinherit == currcon->connoinherit)
12628 return false;
12629
12630 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12632
12633 /* Fetch the column number and name */
12634 colNum = extractNotNullColumn(contuple);
12635 colName = get_attname(currcon->conrelid, colNum, false);
12636
12637 /*
12638 * Propagate the change to children. For this subcommand type we don't
12639 * recursively affect children, just the immediate level.
12640 */
12642 lockmode);
12643 foreach_oid(childoid, children)
12644 {
12645 ObjectAddress addr;
12646
12647 if (cmdcon->noinherit)
12648 {
12649 HeapTuple childtup;
12650 Form_pg_constraint childcon;
12651
12652 childtup = findNotNullConstraint(childoid, colName);
12653 if (!childtup)
12654 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12655 colName, childoid);
12656 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12657 Assert(childcon->coninhcount > 0);
12658 childcon->coninhcount--;
12659 childcon->conislocal = true;
12660 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12661 heap_freetuple(childtup);
12662 }
12663 else
12664 {
12665 Relation childrel = table_open(childoid, NoLock);
12666
12667 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12668 colName, true, true, lockmode);
12669 if (OidIsValid(addr.objectId))
12671 table_close(childrel, NoLock);
12672 }
12673 }
12674
12675 return true;
12676}
12677
12678/*
12679 * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12680 * trigger's deferrability.
12681 *
12682 * The arguments to this function have the same meaning as the arguments to
12683 * ATExecAlterConstrDeferrability.
12684 */
12685static void
12687 bool deferrable, bool initdeferred,
12688 List **otherrelids)
12689{
12690 HeapTuple tgtuple;
12691 ScanKeyData tgkey;
12692 SysScanDesc tgscan;
12693
12694 ScanKeyInit(&tgkey,
12695 Anum_pg_trigger_tgconstraint,
12696 BTEqualStrategyNumber, F_OIDEQ,
12697 ObjectIdGetDatum(conoid));
12698 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12699 NULL, 1, &tgkey);
12700 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12701 {
12702 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12703 Form_pg_trigger copy_tg;
12704 HeapTuple tgCopyTuple;
12705
12706 /*
12707 * Remember OIDs of other relation(s) involved in FK constraint.
12708 * (Note: it's likely that we could skip forcing a relcache inval for
12709 * other rels that don't have a trigger whose properties change, but
12710 * let's be conservative.)
12711 */
12712 if (tgform->tgrelid != RelationGetRelid(rel))
12713 *otherrelids = list_append_unique_oid(*otherrelids,
12714 tgform->tgrelid);
12715
12716 /*
12717 * Update enable status and deferrability of RI_FKey_noaction_del,
12718 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12719 * triggers, but not others; see createForeignKeyActionTriggers and
12720 * CreateFKCheckTrigger.
12721 */
12722 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12723 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12724 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12725 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12726 continue;
12727
12728 tgCopyTuple = heap_copytuple(tgtuple);
12729 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12730
12731 copy_tg->tgdeferrable = deferrable;
12732 copy_tg->tginitdeferred = initdeferred;
12733 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12734
12735 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12736
12737 heap_freetuple(tgCopyTuple);
12738 }
12739
12740 systable_endscan(tgscan);
12741}
12742
12743/*
12744 * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12745 * the specified constraint.
12746 *
12747 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12748 * list of child relations and recursing; instead it uses the conparentid
12749 * relationships. This may need to be reconsidered.
12750 *
12751 * The arguments to this function have the same meaning as the arguments to
12752 * ATExecAlterConstrEnforceability.
12753 */
12754static void
12756 Relation conrel, Relation tgrel,
12757 Oid fkrelid, Oid pkrelid,
12758 HeapTuple contuple, LOCKMODE lockmode,
12759 Oid ReferencedParentDelTrigger,
12760 Oid ReferencedParentUpdTrigger,
12761 Oid ReferencingParentInsTrigger,
12762 Oid ReferencingParentUpdTrigger)
12763{
12764 Form_pg_constraint currcon;
12765 Oid conoid;
12766 ScanKeyData pkey;
12767 SysScanDesc pscan;
12768 HeapTuple childtup;
12769
12770 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12771 conoid = currcon->oid;
12772
12773 ScanKeyInit(&pkey,
12774 Anum_pg_constraint_conparentid,
12775 BTEqualStrategyNumber, F_OIDEQ,
12776 ObjectIdGetDatum(conoid));
12777
12778 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12779 true, NULL, 1, &pkey);
12780
12781 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12782 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12783 pkrelid, childtup, lockmode,
12784 ReferencedParentDelTrigger,
12785 ReferencedParentUpdTrigger,
12786 ReferencingParentInsTrigger,
12787 ReferencingParentUpdTrigger);
12788
12789 systable_endscan(pscan);
12790}
12791
12792/*
12793 * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12794 * the specified constraint.
12795 *
12796 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12797 * list of child relations and recursing; instead it uses the conparentid
12798 * relationships. This may need to be reconsidered.
12799 *
12800 * The arguments to this function have the same meaning as the arguments to
12801 * ATExecAlterConstrDeferrability.
12802 */
12803static void
12805 Relation conrel, Relation tgrel, Relation rel,
12806 HeapTuple contuple, bool recurse,
12807 List **otherrelids, LOCKMODE lockmode)
12808{
12809 Form_pg_constraint currcon;
12810 Oid conoid;
12811 ScanKeyData pkey;
12812 SysScanDesc pscan;
12813 HeapTuple childtup;
12814
12815 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12816 conoid = currcon->oid;
12817
12818 ScanKeyInit(&pkey,
12819 Anum_pg_constraint_conparentid,
12820 BTEqualStrategyNumber, F_OIDEQ,
12821 ObjectIdGetDatum(conoid));
12822
12823 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12824 true, NULL, 1, &pkey);
12825
12826 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12827 {
12828 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12829 Relation childrel;
12830
12831 childrel = table_open(childcon->conrelid, lockmode);
12832
12833 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12834 childtup, recurse, otherrelids, lockmode);
12835 table_close(childrel, NoLock);
12836 }
12837
12838 systable_endscan(pscan);
12839}
12840
12841/*
12842 * Update the constraint entry for the given ATAlterConstraint command, and
12843 * invoke the appropriate hooks.
12844 */
12845static void
12847 HeapTuple contuple)
12848{
12849 HeapTuple copyTuple;
12850 Form_pg_constraint copy_con;
12851
12852 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12853 cmdcon->alterInheritability);
12854
12855 copyTuple = heap_copytuple(contuple);
12856 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12857
12858 if (cmdcon->alterEnforceability)
12859 {
12860 copy_con->conenforced = cmdcon->is_enforced;
12861
12862 /*
12863 * NB: The convalidated status is irrelevant when the constraint is
12864 * set to NOT ENFORCED, but for consistency, it should still be set
12865 * appropriately. Similarly, if the constraint is later changed to
12866 * ENFORCED, validation will be performed during phase 3, so it makes
12867 * sense to mark it as valid in that case.
12868 */
12869 copy_con->convalidated = cmdcon->is_enforced;
12870 }
12871 if (cmdcon->alterDeferrability)
12872 {
12873 copy_con->condeferrable = cmdcon->deferrable;
12874 copy_con->condeferred = cmdcon->initdeferred;
12875 }
12876 if (cmdcon->alterInheritability)
12877 copy_con->connoinherit = cmdcon->noinherit;
12878
12879 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12880 InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12881
12882 /* Make new constraint flags visible to others */
12883 CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12884
12885 heap_freetuple(copyTuple);
12886}
12887
12888/*
12889 * ALTER TABLE VALIDATE CONSTRAINT
12890 *
12891 * XXX The reason we handle recursion here rather than at Phase 1 is because
12892 * there's no good way to skip recursing when handling foreign keys: there is
12893 * no need to lock children in that case, yet we wouldn't be able to avoid
12894 * doing so at that level.
12895 *
12896 * Return value is the address of the validated constraint. If the constraint
12897 * was already validated, InvalidObjectAddress is returned.
12898 */
12899static ObjectAddress
12900ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12901 bool recurse, bool recursing, LOCKMODE lockmode)
12902{
12903 Relation conrel;
12904 SysScanDesc scan;
12905 ScanKeyData skey[3];
12906 HeapTuple tuple;
12908 ObjectAddress address;
12909
12910 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12911
12912 /*
12913 * Find and check the target constraint
12914 */
12915 ScanKeyInit(&skey[0],
12916 Anum_pg_constraint_conrelid,
12917 BTEqualStrategyNumber, F_OIDEQ,
12919 ScanKeyInit(&skey[1],
12920 Anum_pg_constraint_contypid,
12921 BTEqualStrategyNumber, F_OIDEQ,
12923 ScanKeyInit(&skey[2],
12924 Anum_pg_constraint_conname,
12925 BTEqualStrategyNumber, F_NAMEEQ,
12926 CStringGetDatum(constrName));
12927 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12928 true, NULL, 3, skey);
12929
12930 /* There can be at most one matching row */
12931 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12932 ereport(ERROR,
12933 (errcode(ERRCODE_UNDEFINED_OBJECT),
12934 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12935 constrName, RelationGetRelationName(rel))));
12936
12937 con = (Form_pg_constraint) GETSTRUCT(tuple);
12938 if (con->contype != CONSTRAINT_FOREIGN &&
12939 con->contype != CONSTRAINT_CHECK &&
12940 con->contype != CONSTRAINT_NOTNULL)
12941 ereport(ERROR,
12942 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12943 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12944 constrName, RelationGetRelationName(rel)),
12945 errdetail("This operation is not supported for this type of constraint."));
12946
12947 if (!con->conenforced)
12948 ereport(ERROR,
12949 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12950 errmsg("cannot validate NOT ENFORCED constraint")));
12951
12952 if (!con->convalidated)
12953 {
12954 if (con->contype == CONSTRAINT_FOREIGN)
12955 {
12956 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12957 tuple, lockmode);
12958 }
12959 else if (con->contype == CONSTRAINT_CHECK)
12960 {
12961 QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12962 tuple, recurse, recursing, lockmode);
12963 }
12964 else if (con->contype == CONSTRAINT_NOTNULL)
12965 {
12966 QueueNNConstraintValidation(wqueue, conrel, rel,
12967 tuple, recurse, recursing, lockmode);
12968 }
12969
12970 ObjectAddressSet(address, ConstraintRelationId, con->oid);
12971 }
12972 else
12973 address = InvalidObjectAddress; /* already validated */
12974
12975 systable_endscan(scan);
12976
12978
12979 return address;
12980}
12981
12982/*
12983 * QueueFKConstraintValidation
12984 *
12985 * Add an entry to the wqueue to validate the given foreign key constraint in
12986 * Phase 3 and update the convalidated field in the pg_constraint catalog
12987 * for the specified relation and all its children.
12988 */
12989static void
12991 Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
12992{
12994 AlteredTableInfo *tab;
12995 HeapTuple copyTuple;
12996 Form_pg_constraint copy_con;
12997
12998 con = (Form_pg_constraint) GETSTRUCT(contuple);
12999 Assert(con->contype == CONSTRAINT_FOREIGN);
13000 Assert(!con->convalidated);
13001
13002 /*
13003 * Add the validation to phase 3's queue; not needed for partitioned
13004 * tables themselves, only for their partitions.
13005 *
13006 * When the referenced table (pkrelid) is partitioned, the referencing
13007 * table (fkrel) has one pg_constraint row pointing to each partition
13008 * thereof. These rows are there only to support action triggers and no
13009 * table scan is needed, therefore skip this for them as well.
13010 */
13011 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13012 con->confrelid == pkrelid)
13013 {
13014 NewConstraint *newcon;
13015 Constraint *fkconstraint;
13016
13017 /* Queue validation for phase 3 */
13018 fkconstraint = makeNode(Constraint);
13019 /* for now this is all we need */
13020 fkconstraint->conname = pstrdup(NameStr(con->conname));
13021
13022 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13023 newcon->name = fkconstraint->conname;
13024 newcon->contype = CONSTR_FOREIGN;
13025 newcon->refrelid = con->confrelid;
13026 newcon->refindid = con->conindid;
13027 newcon->conid = con->oid;
13028 newcon->qual = (Node *) fkconstraint;
13029
13030 /* Find or create work queue entry for this table */
13031 tab = ATGetQueueEntry(wqueue, fkrel);
13032 tab->constraints = lappend(tab->constraints, newcon);
13033 }
13034
13035 /*
13036 * If the table at either end of the constraint is partitioned, we need to
13037 * recurse and handle every unvalidate constraint that is a child of this
13038 * constraint.
13039 */
13040 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13041 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13042 {
13043 ScanKeyData pkey;
13044 SysScanDesc pscan;
13045 HeapTuple childtup;
13046
13047 ScanKeyInit(&pkey,
13048 Anum_pg_constraint_conparentid,
13049 BTEqualStrategyNumber, F_OIDEQ,
13050 ObjectIdGetDatum(con->oid));
13051
13052 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13053 true, NULL, 1, &pkey);
13054
13055 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13056 {
13057 Form_pg_constraint childcon;
13058 Relation childrel;
13059
13060 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13061
13062 /*
13063 * If the child constraint has already been validated, no further
13064 * action is required for it or its descendants, as they are all
13065 * valid.
13066 */
13067 if (childcon->convalidated)
13068 continue;
13069
13070 childrel = table_open(childcon->conrelid, lockmode);
13071
13072 /*
13073 * NB: Note that pkrelid should be passed as-is during recursion,
13074 * as it is required to identify the root referenced table.
13075 */
13076 QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13077 childtup, lockmode);
13078 table_close(childrel, NoLock);
13079 }
13080
13081 systable_endscan(pscan);
13082 }
13083
13084 /*
13085 * Now mark the pg_constraint row as validated (even if we didn't check,
13086 * notably the ones for partitions on the referenced side).
13087 *
13088 * We rely on transaction abort to roll back this change if phase 3
13089 * ultimately finds violating rows. This is a bit ugly.
13090 */
13091 copyTuple = heap_copytuple(contuple);
13092 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13093 copy_con->convalidated = true;
13094 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13095
13096 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13097
13098 heap_freetuple(copyTuple);
13099}
13100
13101/*
13102 * QueueCheckConstraintValidation
13103 *
13104 * Add an entry to the wqueue to validate the given check constraint in Phase 3
13105 * and update the convalidated field in the pg_constraint catalog for the
13106 * specified relation and all its inheriting children.
13107 */
13108static void
13110 char *constrName, HeapTuple contuple,
13111 bool recurse, bool recursing, LOCKMODE lockmode)
13112{
13114 AlteredTableInfo *tab;
13115 HeapTuple copyTuple;
13116 Form_pg_constraint copy_con;
13117
13118 List *children = NIL;
13119 ListCell *child;
13120 NewConstraint *newcon;
13121 Datum val;
13122 char *conbin;
13123
13124 con = (Form_pg_constraint) GETSTRUCT(contuple);
13125 Assert(con->contype == CONSTRAINT_CHECK);
13126
13127 /*
13128 * If we're recursing, the parent has already done this, so skip it. Also,
13129 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13130 * for it in the children.
13131 */
13132 if (!recursing && !con->connoinherit)
13133 children = find_all_inheritors(RelationGetRelid(rel),
13134 lockmode, NULL);
13135
13136 /*
13137 * For CHECK constraints, we must ensure that we only mark the constraint
13138 * as validated on the parent if it's already validated on the children.
13139 *
13140 * We recurse before validating on the parent, to reduce risk of
13141 * deadlocks.
13142 */
13143 foreach(child, children)
13144 {
13145 Oid childoid = lfirst_oid(child);
13146 Relation childrel;
13147
13148 if (childoid == RelationGetRelid(rel))
13149 continue;
13150
13151 /*
13152 * If we are told not to recurse, there had better not be any child
13153 * tables, because we can't mark the constraint on the parent valid
13154 * unless it is valid for all child tables.
13155 */
13156 if (!recurse)
13157 ereport(ERROR,
13158 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13159 errmsg("constraint must be validated on child tables too")));
13160
13161 /* find_all_inheritors already got lock */
13162 childrel = table_open(childoid, NoLock);
13163
13164 ATExecValidateConstraint(wqueue, childrel, constrName, false,
13165 true, lockmode);
13166 table_close(childrel, NoLock);
13167 }
13168
13169 /* Queue validation for phase 3 */
13170 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13171 newcon->name = constrName;
13172 newcon->contype = CONSTR_CHECK;
13173 newcon->refrelid = InvalidOid;
13174 newcon->refindid = InvalidOid;
13175 newcon->conid = con->oid;
13176
13177 val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13178 Anum_pg_constraint_conbin);
13179 conbin = TextDatumGetCString(val);
13180 newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13181
13182 /* Find or create work queue entry for this table */
13183 tab = ATGetQueueEntry(wqueue, rel);
13184 tab->constraints = lappend(tab->constraints, newcon);
13185
13186 /*
13187 * Invalidate relcache so that others see the new validated constraint.
13188 */
13190
13191 /*
13192 * Now update the catalog, while we have the door open.
13193 */
13194 copyTuple = heap_copytuple(contuple);
13195 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13196 copy_con->convalidated = true;
13197 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13198
13199 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13200
13201 heap_freetuple(copyTuple);
13202}
13203
13204/*
13205 * QueueNNConstraintValidation
13206 *
13207 * Add an entry to the wqueue to validate the given not-null constraint in
13208 * Phase 3 and update the convalidated field in the pg_constraint catalog for
13209 * the specified relation and all its inheriting children.
13210 */
13211static void
13213 HeapTuple contuple, bool recurse, bool recursing,
13214 LOCKMODE lockmode)
13215{
13217 AlteredTableInfo *tab;
13218 HeapTuple copyTuple;
13219 Form_pg_constraint copy_con;
13220 List *children = NIL;
13222 char *colname;
13223
13224 con = (Form_pg_constraint) GETSTRUCT(contuple);
13225 Assert(con->contype == CONSTRAINT_NOTNULL);
13226
13227 attnum = extractNotNullColumn(contuple);
13228
13229 /*
13230 * If we're recursing, we've already done this for parent, so skip it.
13231 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13232 * look for it in the children.
13233 *
13234 * We recurse before validating on the parent, to reduce risk of
13235 * deadlocks.
13236 */
13237 if (!recursing && !con->connoinherit)
13238 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13239
13240 colname = get_attname(RelationGetRelid(rel), attnum, false);
13241 foreach_oid(childoid, children)
13242 {
13243 Relation childrel;
13244 HeapTuple contup;
13245 Form_pg_constraint childcon;
13246 char *conname;
13247
13248 if (childoid == RelationGetRelid(rel))
13249 continue;
13250
13251 /*
13252 * If we are told not to recurse, there had better not be any child
13253 * tables, because we can't mark the constraint on the parent valid
13254 * unless it is valid for all child tables.
13255 */
13256 if (!recurse)
13257 ereport(ERROR,
13258 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13259 errmsg("constraint must be validated on child tables too"));
13260
13261 /*
13262 * The column on child might have a different attnum, so search by
13263 * column name.
13264 */
13265 contup = findNotNullConstraint(childoid, colname);
13266 if (!contup)
13267 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13268 colname, get_rel_name(childoid));
13269 childcon = (Form_pg_constraint) GETSTRUCT(contup);
13270 if (childcon->convalidated)
13271 continue;
13272
13273 /* find_all_inheritors already got lock */
13274 childrel = table_open(childoid, NoLock);
13275 conname = pstrdup(NameStr(childcon->conname));
13276
13277 /* XXX improve ATExecValidateConstraint API to avoid double search */
13278 ATExecValidateConstraint(wqueue, childrel, conname,
13279 false, true, lockmode);
13280 table_close(childrel, NoLock);
13281 }
13282
13283 /* Set attnotnull appropriately without queueing another validation */
13284 set_attnotnull(NULL, rel, attnum, true, false);
13285
13286 tab = ATGetQueueEntry(wqueue, rel);
13287 tab->verify_new_notnull = true;
13288
13289 /*
13290 * Invalidate relcache so that others see the new validated constraint.
13291 */
13293
13294 /*
13295 * Now update the catalogs, while we have the door open.
13296 */
13297 copyTuple = heap_copytuple(contuple);
13298 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13299 copy_con->convalidated = true;
13300 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13301
13302 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13303
13304 heap_freetuple(copyTuple);
13305}
13306
13307/*
13308 * transformColumnNameList - transform list of column names
13309 *
13310 * Lookup each name and return its attnum and, optionally, type and collation
13311 * OIDs
13312 *
13313 * Note: the name of this function suggests that it's general-purpose,
13314 * but actually it's only used to look up names appearing in foreign-key
13315 * clauses. The error messages would need work to use it in other cases,
13316 * and perhaps the validity checks as well.
13317 */
13318static int
13320 int16 *attnums, Oid *atttypids, Oid *attcollids)
13321{
13322 ListCell *l;
13323 int attnum;
13324
13325 attnum = 0;
13326 foreach(l, colList)
13327 {
13328 char *attname = strVal(lfirst(l));
13329 HeapTuple atttuple;
13330 Form_pg_attribute attform;
13331
13332 atttuple = SearchSysCacheAttName(relId, attname);
13333 if (!HeapTupleIsValid(atttuple))
13334 ereport(ERROR,
13335 (errcode(ERRCODE_UNDEFINED_COLUMN),
13336 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13337 attname)));
13338 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13339 if (attform->attnum < 0)
13340 ereport(ERROR,
13341 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13342 errmsg("system columns cannot be used in foreign keys")));
13343 if (attnum >= INDEX_MAX_KEYS)
13344 ereport(ERROR,
13345 (errcode(ERRCODE_TOO_MANY_COLUMNS),
13346 errmsg("cannot have more than %d keys in a foreign key",
13347 INDEX_MAX_KEYS)));
13348 attnums[attnum] = attform->attnum;
13349 if (atttypids != NULL)
13350 atttypids[attnum] = attform->atttypid;
13351 if (attcollids != NULL)
13352 attcollids[attnum] = attform->attcollation;
13353 ReleaseSysCache(atttuple);
13354 attnum++;
13355 }
13356
13357 return attnum;
13358}
13359
13360/*
13361 * transformFkeyGetPrimaryKey -
13362 *
13363 * Look up the names, attnums, types, and collations of the primary key attributes
13364 * for the pkrel. Also return the index OID and index opclasses of the
13365 * index supporting the primary key. Also return whether the index has
13366 * WITHOUT OVERLAPS.
13367 *
13368 * All parameters except pkrel are output parameters. Also, the function
13369 * return value is the number of attributes in the primary key.
13370 *
13371 * Used when the column list in the REFERENCES specification is omitted.
13372 */
13373static int
13375 List **attnamelist,
13376 int16 *attnums, Oid *atttypids, Oid *attcollids,
13377 Oid *opclasses, bool *pk_has_without_overlaps)
13378{
13379 List *indexoidlist;
13380 ListCell *indexoidscan;
13381 HeapTuple indexTuple = NULL;
13382 Form_pg_index indexStruct = NULL;
13383 Datum indclassDatum;
13384 oidvector *indclass;
13385 int i;
13386
13387 /*
13388 * Get the list of index OIDs for the table from the relcache, and look up
13389 * each one in the pg_index syscache until we find one marked primary key
13390 * (hopefully there isn't more than one such). Insist it's valid, too.
13391 */
13392 *indexOid = InvalidOid;
13393
13394 indexoidlist = RelationGetIndexList(pkrel);
13395
13396 foreach(indexoidscan, indexoidlist)
13397 {
13398 Oid indexoid = lfirst_oid(indexoidscan);
13399
13400 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13401 if (!HeapTupleIsValid(indexTuple))
13402 elog(ERROR, "cache lookup failed for index %u", indexoid);
13403 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13404 if (indexStruct->indisprimary && indexStruct->indisvalid)
13405 {
13406 /*
13407 * Refuse to use a deferrable primary key. This is per SQL spec,
13408 * and there would be a lot of interesting semantic problems if we
13409 * tried to allow it.
13410 */
13411 if (!indexStruct->indimmediate)
13412 ereport(ERROR,
13413 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13414 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13415 RelationGetRelationName(pkrel))));
13416
13417 *indexOid = indexoid;
13418 break;
13419 }
13420 ReleaseSysCache(indexTuple);
13421 }
13422
13423 list_free(indexoidlist);
13424
13425 /*
13426 * Check that we found it
13427 */
13428 if (!OidIsValid(*indexOid))
13429 ereport(ERROR,
13430 (errcode(ERRCODE_UNDEFINED_OBJECT),
13431 errmsg("there is no primary key for referenced table \"%s\"",
13432 RelationGetRelationName(pkrel))));
13433
13434 /* Must get indclass the hard way */
13435 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13436 Anum_pg_index_indclass);
13437 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13438
13439 /*
13440 * Now build the list of PK attributes from the indkey definition (we
13441 * assume a primary key cannot have expressional elements)
13442 */
13443 *attnamelist = NIL;
13444 for (i = 0; i < indexStruct->indnkeyatts; i++)
13445 {
13446 int pkattno = indexStruct->indkey.values[i];
13447
13448 attnums[i] = pkattno;
13449 atttypids[i] = attnumTypeId(pkrel, pkattno);
13450 attcollids[i] = attnumCollationId(pkrel, pkattno);
13451 opclasses[i] = indclass->values[i];
13452 *attnamelist = lappend(*attnamelist,
13453 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13454 }
13455
13456 *pk_has_without_overlaps = indexStruct->indisexclusion;
13457
13458 ReleaseSysCache(indexTuple);
13459
13460 return i;
13461}
13462
13463/*
13464 * transformFkeyCheckAttrs -
13465 *
13466 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13467 * reference as part of a foreign key constraint.
13468 *
13469 * Returns the OID of the unique index supporting the constraint and
13470 * populates the caller-provided 'opclasses' array with the opclasses
13471 * associated with the index columns. Also sets whether the index
13472 * uses WITHOUT OVERLAPS.
13473 *
13474 * Raises an ERROR on validation failure.
13475 */
13476static Oid
13478 int numattrs, int16 *attnums,
13479 bool with_period, Oid *opclasses,
13480 bool *pk_has_without_overlaps)
13481{
13482 Oid indexoid = InvalidOid;
13483 bool found = false;
13484 bool found_deferrable = false;
13485 List *indexoidlist;
13486 ListCell *indexoidscan;
13487 int i,
13488 j;
13489
13490 /*
13491 * Reject duplicate appearances of columns in the referenced-columns list.
13492 * Such a case is forbidden by the SQL standard, and even if we thought it
13493 * useful to allow it, there would be ambiguity about how to match the
13494 * list to unique indexes (in particular, it'd be unclear which index
13495 * opclass goes with which FK column).
13496 */
13497 for (i = 0; i < numattrs; i++)
13498 {
13499 for (j = i + 1; j < numattrs; j++)
13500 {
13501 if (attnums[i] == attnums[j])
13502 ereport(ERROR,
13503 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13504 errmsg("foreign key referenced-columns list must not contain duplicates")));
13505 }
13506 }
13507
13508 /*
13509 * Get the list of index OIDs for the table from the relcache, and look up
13510 * each one in the pg_index syscache, and match unique indexes to the list
13511 * of attnums we are given.
13512 */
13513 indexoidlist = RelationGetIndexList(pkrel);
13514
13515 foreach(indexoidscan, indexoidlist)
13516 {
13517 HeapTuple indexTuple;
13518 Form_pg_index indexStruct;
13519
13520 indexoid = lfirst_oid(indexoidscan);
13521 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13522 if (!HeapTupleIsValid(indexTuple))
13523 elog(ERROR, "cache lookup failed for index %u", indexoid);
13524 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13525
13526 /*
13527 * Must have the right number of columns; must be unique (or if
13528 * temporal then exclusion instead) and not a partial index; forget it
13529 * if there are any expressions, too. Invalid indexes are out as well.
13530 */
13531 if (indexStruct->indnkeyatts == numattrs &&
13532 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13533 indexStruct->indisvalid &&
13534 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13535 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13536 {
13537 Datum indclassDatum;
13538 oidvector *indclass;
13539
13540 /* Must get indclass the hard way */
13541 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13542 Anum_pg_index_indclass);
13543 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13544
13545 /*
13546 * The given attnum list may match the index columns in any order.
13547 * Check for a match, and extract the appropriate opclasses while
13548 * we're at it.
13549 *
13550 * We know that attnums[] is duplicate-free per the test at the
13551 * start of this function, and we checked above that the number of
13552 * index columns agrees, so if we find a match for each attnums[]
13553 * entry then we must have a one-to-one match in some order.
13554 */
13555 for (i = 0; i < numattrs; i++)
13556 {
13557 found = false;
13558 for (j = 0; j < numattrs; j++)
13559 {
13560 if (attnums[i] == indexStruct->indkey.values[j])
13561 {
13562 opclasses[i] = indclass->values[j];
13563 found = true;
13564 break;
13565 }
13566 }
13567 if (!found)
13568 break;
13569 }
13570 /* The last attribute in the index must be the PERIOD FK part */
13571 if (found && with_period)
13572 {
13573 int16 periodattnum = attnums[numattrs - 1];
13574
13575 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13576 }
13577
13578 /*
13579 * Refuse to use a deferrable unique/primary key. This is per SQL
13580 * spec, and there would be a lot of interesting semantic problems
13581 * if we tried to allow it.
13582 */
13583 if (found && !indexStruct->indimmediate)
13584 {
13585 /*
13586 * Remember that we found an otherwise matching index, so that
13587 * we can generate a more appropriate error message.
13588 */
13589 found_deferrable = true;
13590 found = false;
13591 }
13592
13593 /* We need to know whether the index has WITHOUT OVERLAPS */
13594 if (found)
13595 *pk_has_without_overlaps = indexStruct->indisexclusion;
13596 }
13597 ReleaseSysCache(indexTuple);
13598 if (found)
13599 break;
13600 }
13601
13602 if (!found)
13603 {
13604 if (found_deferrable)
13605 ereport(ERROR,
13606 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13607 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13608 RelationGetRelationName(pkrel))));
13609 else
13610 ereport(ERROR,
13611 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13612 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13613 RelationGetRelationName(pkrel))));
13614 }
13615
13616 list_free(indexoidlist);
13617
13618 return indexoid;
13619}
13620
13621/*
13622 * findFkeyCast -
13623 *
13624 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13625 * Caller has equal regard for binary coercibility and for an exact match.
13626*/
13627static CoercionPathType
13628findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13629{
13630 CoercionPathType ret;
13631
13632 if (targetTypeId == sourceTypeId)
13633 {
13635 *funcid = InvalidOid;
13636 }
13637 else
13638 {
13639 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13640 COERCION_IMPLICIT, funcid);
13641 if (ret == COERCION_PATH_NONE)
13642 /* A previously-relied-upon cast is now gone. */
13643 elog(ERROR, "could not find cast from %u to %u",
13644 sourceTypeId, targetTypeId);
13645 }
13646
13647 return ret;
13648}
13649
13650/*
13651 * Permissions checks on the referenced table for ADD FOREIGN KEY
13652 *
13653 * Note: we have already checked that the user owns the referencing table,
13654 * else we'd have failed much earlier; no additional checks are needed for it.
13655 */
13656static void
13657checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13658{
13659 Oid roleid = GetUserId();
13660 AclResult aclresult;
13661 int i;
13662
13663 /* Okay if we have relation-level REFERENCES permission */
13664 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13666 if (aclresult == ACLCHECK_OK)
13667 return;
13668 /* Else we must have REFERENCES on each column */
13669 for (i = 0; i < natts; i++)
13670 {
13671 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13672 roleid, ACL_REFERENCES);
13673 if (aclresult != ACLCHECK_OK)
13674 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13676 }
13677}
13678
13679/*
13680 * Scan the existing rows in a table to verify they meet a proposed FK
13681 * constraint.
13682 *
13683 * Caller must have opened and locked both relations appropriately.
13684 */
13685static void
13687 Relation rel,
13688 Relation pkrel,
13689 Oid pkindOid,
13690 Oid constraintOid,
13691 bool hasperiod)
13692{
13693 TupleTableSlot *slot;
13694 TableScanDesc scan;
13695 Trigger trig = {0};
13696 Snapshot snapshot;
13697 MemoryContext oldcxt;
13698 MemoryContext perTupCxt;
13699
13701 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13702
13703 /*
13704 * Build a trigger call structure; we'll need it either way.
13705 */
13706 trig.tgoid = InvalidOid;
13707 trig.tgname = conname;
13709 trig.tgisinternal = true;
13710 trig.tgconstrrelid = RelationGetRelid(pkrel);
13711 trig.tgconstrindid = pkindOid;
13712 trig.tgconstraint = constraintOid;
13713 trig.tgdeferrable = false;
13714 trig.tginitdeferred = false;
13715 /* we needn't fill in remaining fields */
13716
13717 /*
13718 * See if we can do it with a single LEFT JOIN query. A false result
13719 * indicates we must proceed with the fire-the-trigger method. We can't do
13720 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13721 * left joins.
13722 */
13723 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13724 return;
13725
13726 /*
13727 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13728 * if that tuple had just been inserted. If any of those fail, it should
13729 * ereport(ERROR) and that's that.
13730 */
13731 snapshot = RegisterSnapshot(GetLatestSnapshot());
13732 slot = table_slot_create(rel, NULL);
13733 scan = table_beginscan(rel, snapshot, 0, NULL);
13734
13736 "validateForeignKeyConstraint",
13738 oldcxt = MemoryContextSwitchTo(perTupCxt);
13739
13740 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13741 {
13742 LOCAL_FCINFO(fcinfo, 0);
13743 TriggerData trigdata = {0};
13744
13746
13747 /*
13748 * Make a call to the trigger function
13749 *
13750 * No parameters are passed, but we do set a context
13751 */
13752 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13753
13754 /*
13755 * We assume RI_FKey_check_ins won't look at flinfo...
13756 */
13757 trigdata.type = T_TriggerData;
13759 trigdata.tg_relation = rel;
13760 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13761 trigdata.tg_trigslot = slot;
13762 trigdata.tg_trigger = &trig;
13763
13764 fcinfo->context = (Node *) &trigdata;
13765
13766 RI_FKey_check_ins(fcinfo);
13767
13768 MemoryContextReset(perTupCxt);
13769 }
13770
13771 MemoryContextSwitchTo(oldcxt);
13772 MemoryContextDelete(perTupCxt);
13773 table_endscan(scan);
13774 UnregisterSnapshot(snapshot);
13776}
13777
13778/*
13779 * CreateFKCheckTrigger
13780 * Creates the insert (on_insert=true) or update "check" trigger that
13781 * implements a given foreign key
13782 *
13783 * Returns the OID of the so created trigger.
13784 */
13785static Oid
13786CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13787 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13788 bool on_insert)
13789{
13790 ObjectAddress trigAddress;
13791 CreateTrigStmt *fk_trigger;
13792
13793 /*
13794 * Note: for a self-referential FK (referencing and referenced tables are
13795 * the same), it is important that the ON UPDATE action fires before the
13796 * CHECK action, since both triggers will fire on the same row during an
13797 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13798 * state of the row. Triggers fire in name order, so we ensure this by
13799 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13800 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13801 */
13802 fk_trigger = makeNode(CreateTrigStmt);
13803 fk_trigger->replace = false;
13804 fk_trigger->isconstraint = true;
13805 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13806 fk_trigger->relation = NULL;
13807
13808 /* Either ON INSERT or ON UPDATE */
13809 if (on_insert)
13810 {
13811 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13812 fk_trigger->events = TRIGGER_TYPE_INSERT;
13813 }
13814 else
13815 {
13816 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13817 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13818 }
13819
13820 fk_trigger->args = NIL;
13821 fk_trigger->row = true;
13822 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13823 fk_trigger->columns = NIL;
13824 fk_trigger->whenClause = NULL;
13825 fk_trigger->transitionRels = NIL;
13826 fk_trigger->deferrable = fkconstraint->deferrable;
13827 fk_trigger->initdeferred = fkconstraint->initdeferred;
13828 fk_trigger->constrrel = NULL;
13829
13830 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13831 constraintOid, indexOid, InvalidOid,
13832 parentTrigOid, NULL, true, false);
13833
13834 /* Make changes-so-far visible */
13836
13837 return trigAddress.objectId;
13838}
13839
13840/*
13841 * createForeignKeyActionTriggers
13842 * Create the referenced-side "action" triggers that implement a foreign
13843 * key.
13844 *
13845 * Returns the OIDs of the so created triggers in *deleteTrigOid and
13846 * *updateTrigOid.
13847 */
13848static void
13849createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13850 Oid constraintOid, Oid indexOid,
13851 Oid parentDelTrigger, Oid parentUpdTrigger,
13852 Oid *deleteTrigOid, Oid *updateTrigOid)
13853{
13854 CreateTrigStmt *fk_trigger;
13855 ObjectAddress trigAddress;
13856
13857 /*
13858 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13859 * DELETE action on the referenced table.
13860 */
13861 fk_trigger = makeNode(CreateTrigStmt);
13862 fk_trigger->replace = false;
13863 fk_trigger->isconstraint = true;
13864 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13865 fk_trigger->relation = NULL;
13866 fk_trigger->args = NIL;
13867 fk_trigger->row = true;
13868 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13869 fk_trigger->events = TRIGGER_TYPE_DELETE;
13870 fk_trigger->columns = NIL;
13871 fk_trigger->whenClause = NULL;
13872 fk_trigger->transitionRels = NIL;
13873 fk_trigger->constrrel = NULL;
13874
13875 switch (fkconstraint->fk_del_action)
13876 {
13878 fk_trigger->deferrable = fkconstraint->deferrable;
13879 fk_trigger->initdeferred = fkconstraint->initdeferred;
13880 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13881 break;
13883 fk_trigger->deferrable = false;
13884 fk_trigger->initdeferred = false;
13885 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13886 break;
13888 fk_trigger->deferrable = false;
13889 fk_trigger->initdeferred = false;
13890 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13891 break;
13893 fk_trigger->deferrable = false;
13894 fk_trigger->initdeferred = false;
13895 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13896 break;
13898 fk_trigger->deferrable = false;
13899 fk_trigger->initdeferred = false;
13900 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13901 break;
13902 default:
13903 elog(ERROR, "unrecognized FK action type: %d",
13904 (int) fkconstraint->fk_del_action);
13905 break;
13906 }
13907
13908 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13909 constraintOid, indexOid, InvalidOid,
13910 parentDelTrigger, NULL, true, false);
13911 if (deleteTrigOid)
13912 *deleteTrigOid = trigAddress.objectId;
13913
13914 /* Make changes-so-far visible */
13916
13917 /*
13918 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13919 * UPDATE action on the referenced table.
13920 */
13921 fk_trigger = makeNode(CreateTrigStmt);
13922 fk_trigger->replace = false;
13923 fk_trigger->isconstraint = true;
13924 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13925 fk_trigger->relation = NULL;
13926 fk_trigger->args = NIL;
13927 fk_trigger->row = true;
13928 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13929 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13930 fk_trigger->columns = NIL;
13931 fk_trigger->whenClause = NULL;
13932 fk_trigger->transitionRels = NIL;
13933 fk_trigger->constrrel = NULL;
13934
13935 switch (fkconstraint->fk_upd_action)
13936 {
13938 fk_trigger->deferrable = fkconstraint->deferrable;
13939 fk_trigger->initdeferred = fkconstraint->initdeferred;
13940 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13941 break;
13943 fk_trigger->deferrable = false;
13944 fk_trigger->initdeferred = false;
13945 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13946 break;
13948 fk_trigger->deferrable = false;
13949 fk_trigger->initdeferred = false;
13950 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13951 break;
13953 fk_trigger->deferrable = false;
13954 fk_trigger->initdeferred = false;
13955 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13956 break;
13958 fk_trigger->deferrable = false;
13959 fk_trigger->initdeferred = false;
13960 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13961 break;
13962 default:
13963 elog(ERROR, "unrecognized FK action type: %d",
13964 (int) fkconstraint->fk_upd_action);
13965 break;
13966 }
13967
13968 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13969 constraintOid, indexOid, InvalidOid,
13970 parentUpdTrigger, NULL, true, false);
13971 if (updateTrigOid)
13972 *updateTrigOid = trigAddress.objectId;
13973}
13974
13975/*
13976 * createForeignKeyCheckTriggers
13977 * Create the referencing-side "check" triggers that implement a foreign
13978 * key.
13979 *
13980 * Returns the OIDs of the so created triggers in *insertTrigOid and
13981 * *updateTrigOid.
13982 */
13983static void
13985 Constraint *fkconstraint, Oid constraintOid,
13986 Oid indexOid,
13987 Oid parentInsTrigger, Oid parentUpdTrigger,
13988 Oid *insertTrigOid, Oid *updateTrigOid)
13989{
13990 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13991 constraintOid, indexOid,
13992 parentInsTrigger, true);
13993 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13994 constraintOid, indexOid,
13995 parentUpdTrigger, false);
13996}
13997
13998/*
13999 * ALTER TABLE DROP CONSTRAINT
14000 *
14001 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
14002 */
14003static void
14004ATExecDropConstraint(Relation rel, const char *constrName,
14005 DropBehavior behavior, bool recurse,
14006 bool missing_ok, LOCKMODE lockmode)
14007{
14008 Relation conrel;
14009 SysScanDesc scan;
14010 ScanKeyData skey[3];
14011 HeapTuple tuple;
14012 bool found = false;
14013
14014 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14015
14016 /*
14017 * Find and drop the target constraint
14018 */
14019 ScanKeyInit(&skey[0],
14020 Anum_pg_constraint_conrelid,
14021 BTEqualStrategyNumber, F_OIDEQ,
14023 ScanKeyInit(&skey[1],
14024 Anum_pg_constraint_contypid,
14025 BTEqualStrategyNumber, F_OIDEQ,
14027 ScanKeyInit(&skey[2],
14028 Anum_pg_constraint_conname,
14029 BTEqualStrategyNumber, F_NAMEEQ,
14030 CStringGetDatum(constrName));
14031 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14032 true, NULL, 3, skey);
14033
14034 /* There can be at most one matching row */
14035 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14036 {
14037 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14038 missing_ok, lockmode);
14039 found = true;
14040 }
14041
14042 systable_endscan(scan);
14043
14044 if (!found)
14045 {
14046 if (!missing_ok)
14047 ereport(ERROR,
14048 errcode(ERRCODE_UNDEFINED_OBJECT),
14049 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14050 constrName, RelationGetRelationName(rel)));
14051 else
14053 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14054 constrName, RelationGetRelationName(rel)));
14055 }
14056
14058}
14059
14060/*
14061 * Remove a constraint, using its pg_constraint tuple
14062 *
14063 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14064 * DROP NOT NULL.
14065 *
14066 * Returns the address of the constraint being removed.
14067 */
14068static ObjectAddress
14070 bool recurse, bool recursing, bool missing_ok,
14071 LOCKMODE lockmode)
14072{
14073 Relation conrel;
14075 ObjectAddress conobj;
14076 List *children;
14077 bool is_no_inherit_constraint = false;
14078 char *constrName;
14079 char *colname = NULL;
14080
14081 /* Guard against stack overflow due to overly deep inheritance tree. */
14083
14084 /* At top level, permission check was done in ATPrepCmd, else do it */
14085 if (recursing)
14088
14089 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14090
14091 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14092 constrName = NameStr(con->conname);
14093
14094 /* Don't allow drop of inherited constraints */
14095 if (con->coninhcount > 0 && !recursing)
14096 ereport(ERROR,
14097 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14098 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14099 constrName, RelationGetRelationName(rel))));
14100
14101 /*
14102 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14103 *
14104 * While doing that, we're in a good position to disallow dropping a not-
14105 * null constraint underneath a primary key, a replica identity index, or
14106 * a generated identity column.
14107 */
14108 if (con->contype == CONSTRAINT_NOTNULL)
14109 {
14110 Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14111 AttrNumber attnum = extractNotNullColumn(constraintTup);
14112 Bitmapset *pkattrs;
14113 Bitmapset *irattrs;
14114 HeapTuple atttup;
14115 Form_pg_attribute attForm;
14116
14117 /* save column name for recursion step */
14118 colname = get_attname(RelationGetRelid(rel), attnum, false);
14119
14120 /*
14121 * Disallow if it's in the primary key. For partitioned tables we
14122 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14123 * return NULL if the primary key is invalid; but we still need to
14124 * protect not-null constraints under such a constraint, so check the
14125 * slow way.
14126 */
14128
14129 if (pkattrs == NULL &&
14130 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14131 {
14132 Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14133
14134 if (OidIsValid(pkindex))
14135 {
14136 Relation pk = relation_open(pkindex, AccessShareLock);
14137
14138 pkattrs = NULL;
14139 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14140 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14141
14143 }
14144 }
14145
14146 if (pkattrs &&
14148 ereport(ERROR,
14149 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14150 errmsg("column \"%s\" is in a primary key",
14151 get_attname(RelationGetRelid(rel), attnum, false)));
14152
14153 /* Disallow if it's in the replica identity */
14156 ereport(ERROR,
14157 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14158 errmsg("column \"%s\" is in index used as replica identity",
14159 get_attname(RelationGetRelid(rel), attnum, false)));
14160
14161 /* Disallow if it's a GENERATED AS IDENTITY column */
14163 if (!HeapTupleIsValid(atttup))
14164 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14165 attnum, RelationGetRelid(rel));
14166 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14167 if (attForm->attidentity != '\0')
14168 ereport(ERROR,
14169 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14170 errmsg("column \"%s\" of relation \"%s\" is an identity column",
14172 false),
14174
14175 /* All good -- reset attnotnull if needed */
14176 if (attForm->attnotnull)
14177 {
14178 attForm->attnotnull = false;
14179 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14180 }
14181
14183 }
14184
14185 is_no_inherit_constraint = con->connoinherit;
14186
14187 /*
14188 * If it's a foreign-key constraint, we'd better lock the referenced table
14189 * and check that that's not in use, just as we've already done for the
14190 * constrained table (else we might, eg, be dropping a trigger that has
14191 * unfired events). But we can/must skip that in the self-referential
14192 * case.
14193 */
14194 if (con->contype == CONSTRAINT_FOREIGN &&
14195 con->confrelid != RelationGetRelid(rel))
14196 {
14197 Relation frel;
14198
14199 /* Must match lock taken by RemoveTriggerById: */
14200 frel = table_open(con->confrelid, AccessExclusiveLock);
14202 table_close(frel, NoLock);
14203 }
14204
14205 /*
14206 * Perform the actual constraint deletion
14207 */
14208 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14209 performDeletion(&conobj, behavior, 0);
14210
14211 /*
14212 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14213 * are dropped via the dependency mechanism, so we're done here.
14214 */
14215 if (con->contype != CONSTRAINT_CHECK &&
14216 con->contype != CONSTRAINT_NOTNULL &&
14217 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14218 {
14220 return conobj;
14221 }
14222
14223 /*
14224 * Propagate to children as appropriate. Unlike most other ALTER
14225 * routines, we have to do this one level of recursion at a time; we can't
14226 * use find_all_inheritors to do it in one pass.
14227 */
14228 if (!is_no_inherit_constraint)
14229 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14230 else
14231 children = NIL;
14232
14233 foreach_oid(childrelid, children)
14234 {
14235 Relation childrel;
14236 HeapTuple tuple;
14237 Form_pg_constraint childcon;
14238
14239 /* find_inheritance_children already got lock */
14240 childrel = table_open(childrelid, NoLock);
14241 CheckAlterTableIsSafe(childrel);
14242
14243 /*
14244 * We search for not-null constraints by column name, and others by
14245 * constraint name.
14246 */
14247 if (con->contype == CONSTRAINT_NOTNULL)
14248 {
14249 tuple = findNotNullConstraint(childrelid, colname);
14250 if (!HeapTupleIsValid(tuple))
14251 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14252 colname, RelationGetRelid(childrel));
14253 }
14254 else
14255 {
14256 SysScanDesc scan;
14257 ScanKeyData skey[3];
14258
14259 ScanKeyInit(&skey[0],
14260 Anum_pg_constraint_conrelid,
14261 BTEqualStrategyNumber, F_OIDEQ,
14262 ObjectIdGetDatum(childrelid));
14263 ScanKeyInit(&skey[1],
14264 Anum_pg_constraint_contypid,
14265 BTEqualStrategyNumber, F_OIDEQ,
14267 ScanKeyInit(&skey[2],
14268 Anum_pg_constraint_conname,
14269 BTEqualStrategyNumber, F_NAMEEQ,
14270 CStringGetDatum(constrName));
14271 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14272 true, NULL, 3, skey);
14273 /* There can only be one, so no need to loop */
14274 tuple = systable_getnext(scan);
14275 if (!HeapTupleIsValid(tuple))
14276 ereport(ERROR,
14277 (errcode(ERRCODE_UNDEFINED_OBJECT),
14278 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14279 constrName,
14280 RelationGetRelationName(childrel))));
14281 tuple = heap_copytuple(tuple);
14282 systable_endscan(scan);
14283 }
14284
14285 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14286
14287 /* Right now only CHECK and not-null constraints can be inherited */
14288 if (childcon->contype != CONSTRAINT_CHECK &&
14289 childcon->contype != CONSTRAINT_NOTNULL)
14290 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14291
14292 if (childcon->coninhcount <= 0) /* shouldn't happen */
14293 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14294 childrelid, NameStr(childcon->conname));
14295
14296 if (recurse)
14297 {
14298 /*
14299 * If the child constraint has other definition sources, just
14300 * decrement its inheritance count; if not, recurse to delete it.
14301 */
14302 if (childcon->coninhcount == 1 && !childcon->conislocal)
14303 {
14304 /* Time to delete this child constraint, too */
14305 dropconstraint_internal(childrel, tuple, behavior,
14306 recurse, true, missing_ok,
14307 lockmode);
14308 }
14309 else
14310 {
14311 /* Child constraint must survive my deletion */
14312 childcon->coninhcount--;
14313 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14314
14315 /* Make update visible */
14317 }
14318 }
14319 else
14320 {
14321 /*
14322 * If we were told to drop ONLY in this table (no recursion) and
14323 * there are no further parents for this constraint, we need to
14324 * mark the inheritors' constraints as locally defined rather than
14325 * inherited.
14326 */
14327 childcon->coninhcount--;
14328 if (childcon->coninhcount == 0)
14329 childcon->conislocal = true;
14330
14331 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14332
14333 /* Make update visible */
14335 }
14336
14337 heap_freetuple(tuple);
14338
14339 table_close(childrel, NoLock);
14340 }
14341
14343
14344 return conobj;
14345}
14346
14347/*
14348 * ALTER COLUMN TYPE
14349 *
14350 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14351 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14352 * transformed (and must be, because we rely on some transformed fields).
14353 *
14354 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14355 * table will be done "in parallel" during phase 3, so all the USING
14356 * expressions should be parsed assuming the original column types. Also,
14357 * this allows a USING expression to refer to a field that will be dropped.
14358 *
14359 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14360 * the first two execution steps in phase 2; they must not see the effects
14361 * of any other subcommand types, since the USING expressions are parsed
14362 * against the unmodified table's state.
14363 */
14364static void
14366 AlteredTableInfo *tab, Relation rel,
14367 bool recurse, bool recursing,
14368 AlterTableCmd *cmd, LOCKMODE lockmode,
14369 AlterTableUtilityContext *context)
14370{
14371 char *colName = cmd->name;
14372 ColumnDef *def = (ColumnDef *) cmd->def;
14373 TypeName *typeName = def->typeName;
14374 Node *transform = def->cooked_default;
14375 HeapTuple tuple;
14376 Form_pg_attribute attTup;
14378 Oid targettype;
14379 int32 targettypmod;
14380 Oid targetcollid;
14382 ParseState *pstate = make_parsestate(NULL);
14383 AclResult aclresult;
14384 bool is_expr;
14385
14386 pstate->p_sourcetext = context->queryString;
14387
14388 if (rel->rd_rel->reloftype && !recursing)
14389 ereport(ERROR,
14390 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14391 errmsg("cannot alter column type of typed table"),
14392 parser_errposition(pstate, def->location)));
14393
14394 /* lookup the attribute so we can check inheritance status */
14395 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14396 if (!HeapTupleIsValid(tuple))
14397 ereport(ERROR,
14398 (errcode(ERRCODE_UNDEFINED_COLUMN),
14399 errmsg("column \"%s\" of relation \"%s\" does not exist",
14400 colName, RelationGetRelationName(rel)),
14401 parser_errposition(pstate, def->location)));
14402 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14403 attnum = attTup->attnum;
14404
14405 /* Can't alter a system attribute */
14406 if (attnum <= 0)
14407 ereport(ERROR,
14408 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14409 errmsg("cannot alter system column \"%s\"", colName),
14410 parser_errposition(pstate, def->location)));
14411
14412 /*
14413 * Cannot specify USING when altering type of a generated column, because
14414 * that would violate the generation expression.
14415 */
14416 if (attTup->attgenerated && def->cooked_default)
14417 ereport(ERROR,
14418 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14419 errmsg("cannot specify USING when altering type of generated column"),
14420 errdetail("Column \"%s\" is a generated column.", colName),
14421 parser_errposition(pstate, def->location)));
14422
14423 /*
14424 * Don't alter inherited columns. At outer level, there had better not be
14425 * any inherited definition; when recursing, we assume this was checked at
14426 * the parent level (see below).
14427 */
14428 if (attTup->attinhcount > 0 && !recursing)
14429 ereport(ERROR,
14430 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14431 errmsg("cannot alter inherited column \"%s\"", colName),
14432 parser_errposition(pstate, def->location)));
14433
14434 /* Don't alter columns used in the partition key */
14435 if (has_partition_attrs(rel,
14437 &is_expr))
14438 ereport(ERROR,
14439 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14440 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14441 colName, RelationGetRelationName(rel)),
14442 parser_errposition(pstate, def->location)));
14443
14444 /* Look up the target type */
14445 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14446
14447 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14448 if (aclresult != ACLCHECK_OK)
14449 aclcheck_error_type(aclresult, targettype);
14450
14451 /* And the collation */
14452 targetcollid = GetColumnDefCollation(pstate, def, targettype);
14453
14454 /* make sure datatype is legal for a column */
14455 CheckAttributeType(colName, targettype, targetcollid,
14456 list_make1_oid(rel->rd_rel->reltype),
14457 (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14458
14459 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14460 {
14461 /* do nothing */
14462 }
14463 else if (tab->relkind == RELKIND_RELATION ||
14464 tab->relkind == RELKIND_PARTITIONED_TABLE)
14465 {
14466 /*
14467 * Set up an expression to transform the old data value to the new
14468 * type. If a USING option was given, use the expression as
14469 * transformed by transformAlterTableStmt, else just take the old
14470 * value and try to coerce it. We do this first so that type
14471 * incompatibility can be detected before we waste effort, and because
14472 * we need the expression to be parsed against the original table row
14473 * type.
14474 */
14475 if (!transform)
14476 {
14477 transform = (Node *) makeVar(1, attnum,
14478 attTup->atttypid, attTup->atttypmod,
14479 attTup->attcollation,
14480 0);
14481 }
14482
14483 transform = coerce_to_target_type(pstate,
14484 transform, exprType(transform),
14485 targettype, targettypmod,
14488 -1);
14489 if (transform == NULL)
14490 {
14491 /* error text depends on whether USING was specified or not */
14492 if (def->cooked_default != NULL)
14493 ereport(ERROR,
14494 (errcode(ERRCODE_DATATYPE_MISMATCH),
14495 errmsg("result of USING clause for column \"%s\""
14496 " cannot be cast automatically to type %s",
14497 colName, format_type_be(targettype)),
14498 errhint("You might need to add an explicit cast.")));
14499 else
14500 ereport(ERROR,
14501 (errcode(ERRCODE_DATATYPE_MISMATCH),
14502 errmsg("column \"%s\" cannot be cast automatically to type %s",
14503 colName, format_type_be(targettype)),
14504 !attTup->attgenerated ?
14505 /* translator: USING is SQL, don't translate it */
14506 errhint("You might need to specify \"USING %s::%s\".",
14507 quote_identifier(colName),
14508 format_type_with_typemod(targettype,
14509 targettypmod)) : 0));
14510 }
14511
14512 /* Fix collations after all else */
14513 assign_expr_collations(pstate, transform);
14514
14515 /* Expand virtual generated columns in the expr. */
14516 transform = expand_generated_columns_in_expr(transform, rel, 1);
14517
14518 /* Plan the expr now so we can accurately assess the need to rewrite. */
14519 transform = (Node *) expression_planner((Expr *) transform);
14520
14521 /*
14522 * Add a work queue item to make ATRewriteTable update the column
14523 * contents.
14524 */
14526 newval->attnum = attnum;
14527 newval->expr = (Expr *) transform;
14528 newval->is_generated = false;
14529
14530 tab->newvals = lappend(tab->newvals, newval);
14531 if (ATColumnChangeRequiresRewrite(transform, attnum))
14533 }
14534 else if (transform)
14535 ereport(ERROR,
14536 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14537 errmsg("\"%s\" is not a table",
14539
14540 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14541 {
14542 /*
14543 * For relations or columns without storage, do this check now.
14544 * Regular tables will check it later when the table is being
14545 * rewritten.
14546 */
14547 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14548 }
14549
14550 ReleaseSysCache(tuple);
14551
14552 /*
14553 * Recurse manually by queueing a new command for each child, if
14554 * necessary. We cannot apply ATSimpleRecursion here because we need to
14555 * remap attribute numbers in the USING expression, if any.
14556 *
14557 * If we are told not to recurse, there had better not be any child
14558 * tables; else the alter would put them out of step.
14559 */
14560 if (recurse)
14561 {
14562 Oid relid = RelationGetRelid(rel);
14563 List *child_oids,
14564 *child_numparents;
14565 ListCell *lo,
14566 *li;
14567
14568 child_oids = find_all_inheritors(relid, lockmode,
14569 &child_numparents);
14570
14571 /*
14572 * find_all_inheritors does the recursive search of the inheritance
14573 * hierarchy, so all we have to do is process all of the relids in the
14574 * list that it returns.
14575 */
14576 forboth(lo, child_oids, li, child_numparents)
14577 {
14578 Oid childrelid = lfirst_oid(lo);
14579 int numparents = lfirst_int(li);
14580 Relation childrel;
14581 HeapTuple childtuple;
14582 Form_pg_attribute childattTup;
14583
14584 if (childrelid == relid)
14585 continue;
14586
14587 /* find_all_inheritors already got lock */
14588 childrel = relation_open(childrelid, NoLock);
14589 CheckAlterTableIsSafe(childrel);
14590
14591 /*
14592 * Verify that the child doesn't have any inherited definitions of
14593 * this column that came from outside this inheritance hierarchy.
14594 * (renameatt makes a similar test, though in a different way
14595 * because of its different recursion mechanism.)
14596 */
14597 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14598 colName);
14599 if (!HeapTupleIsValid(childtuple))
14600 ereport(ERROR,
14601 (errcode(ERRCODE_UNDEFINED_COLUMN),
14602 errmsg("column \"%s\" of relation \"%s\" does not exist",
14603 colName, RelationGetRelationName(childrel))));
14604 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14605
14606 if (childattTup->attinhcount > numparents)
14607 ereport(ERROR,
14608 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14609 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14610 colName, RelationGetRelationName(childrel))));
14611
14612 ReleaseSysCache(childtuple);
14613
14614 /*
14615 * Remap the attribute numbers. If no USING expression was
14616 * specified, there is no need for this step.
14617 */
14618 if (def->cooked_default)
14619 {
14620 AttrMap *attmap;
14621 bool found_whole_row;
14622
14623 /* create a copy to scribble on */
14624 cmd = copyObject(cmd);
14625
14626 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14627 RelationGetDescr(rel),
14628 false);
14629 ((ColumnDef *) cmd->def)->cooked_default =
14631 1, 0,
14632 attmap,
14633 InvalidOid, &found_whole_row);
14634 if (found_whole_row)
14635 ereport(ERROR,
14636 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14637 errmsg("cannot convert whole-row table reference"),
14638 errdetail("USING expression contains a whole-row table reference.")));
14639 pfree(attmap);
14640 }
14641 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14642 relation_close(childrel, NoLock);
14643 }
14644 }
14645 else if (!recursing &&
14647 ereport(ERROR,
14648 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14649 errmsg("type of inherited column \"%s\" must be changed in child tables too",
14650 colName)));
14651
14652 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14653 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14654}
14655
14656/*
14657 * When the data type of a column is changed, a rewrite might not be required
14658 * if the new type is sufficiently identical to the old one, and the USING
14659 * clause isn't trying to insert some other value. It's safe to skip the
14660 * rewrite in these cases:
14661 *
14662 * - the old type is binary coercible to the new type
14663 * - the new type is an unconstrained domain over the old type
14664 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14665 *
14666 * In the case of a constrained domain, we could get by with scanning the
14667 * table and checking the constraint rather than actually rewriting it, but we
14668 * don't currently try to do that.
14669 */
14670static bool
14672{
14673 Assert(expr != NULL);
14674
14675 for (;;)
14676 {
14677 /* only one varno, so no need to check that */
14678 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14679 return false;
14680 else if (IsA(expr, RelabelType))
14681 expr = (Node *) ((RelabelType *) expr)->arg;
14682 else if (IsA(expr, CoerceToDomain))
14683 {
14684 CoerceToDomain *d = (CoerceToDomain *) expr;
14685
14687 return true;
14688 expr = (Node *) d->arg;
14689 }
14690 else if (IsA(expr, FuncExpr))
14691 {
14692 FuncExpr *f = (FuncExpr *) expr;
14693
14694 switch (f->funcid)
14695 {
14696 case F_TIMESTAMPTZ_TIMESTAMP:
14697 case F_TIMESTAMP_TIMESTAMPTZ:
14699 return true;
14700 else
14701 expr = linitial(f->args);
14702 break;
14703 default:
14704 return true;
14705 }
14706 }
14707 else
14708 return true;
14709 }
14710}
14711
14712/*
14713 * ALTER COLUMN .. SET DATA TYPE
14714 *
14715 * Return the address of the modified column.
14716 */
14717static ObjectAddress
14719 AlterTableCmd *cmd, LOCKMODE lockmode)
14720{
14721 char *colName = cmd->name;
14722 ColumnDef *def = (ColumnDef *) cmd->def;
14723 TypeName *typeName = def->typeName;
14724 HeapTuple heapTup;
14725 Form_pg_attribute attTup,
14726 attOldTup;
14728 HeapTuple typeTuple;
14729 Form_pg_type tform;
14730 Oid targettype;
14731 int32 targettypmod;
14732 Oid targetcollid;
14733 Node *defaultexpr;
14734 Relation attrelation;
14735 Relation depRel;
14736 ScanKeyData key[3];
14737 SysScanDesc scan;
14738 HeapTuple depTup;
14739 ObjectAddress address;
14740
14741 /*
14742 * Clear all the missing values if we're rewriting the table, since this
14743 * renders them pointless.
14744 */
14745 if (tab->rewrite)
14746 {
14747 Relation newrel;
14748
14749 newrel = table_open(RelationGetRelid(rel), NoLock);
14750 RelationClearMissing(newrel);
14751 relation_close(newrel, NoLock);
14752 /* make sure we don't conflict with later attribute modifications */
14754 }
14755
14756 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14757
14758 /* Look up the target column */
14759 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14760 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14761 ereport(ERROR,
14762 (errcode(ERRCODE_UNDEFINED_COLUMN),
14763 errmsg("column \"%s\" of relation \"%s\" does not exist",
14764 colName, RelationGetRelationName(rel))));
14765 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14766 attnum = attTup->attnum;
14767 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14768
14769 /* Check for multiple ALTER TYPE on same column --- can't cope */
14770 if (attTup->atttypid != attOldTup->atttypid ||
14771 attTup->atttypmod != attOldTup->atttypmod)
14772 ereport(ERROR,
14773 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14774 errmsg("cannot alter type of column \"%s\" twice",
14775 colName)));
14776
14777 /* Look up the target type (should not fail, since prep found it) */
14778 typeTuple = typenameType(NULL, typeName, &targettypmod);
14779 tform = (Form_pg_type) GETSTRUCT(typeTuple);
14780 targettype = tform->oid;
14781 /* And the collation */
14782 targetcollid = GetColumnDefCollation(NULL, def, targettype);
14783
14784 /*
14785 * If there is a default expression for the column, get it and ensure we
14786 * can coerce it to the new datatype. (We must do this before changing
14787 * the column type, because build_column_default itself will try to
14788 * coerce, and will not issue the error message we want if it fails.)
14789 *
14790 * We remove any implicit coercion steps at the top level of the old
14791 * default expression; this has been agreed to satisfy the principle of
14792 * least surprise. (The conversion to the new column type should act like
14793 * it started from what the user sees as the stored expression, and the
14794 * implicit coercions aren't going to be shown.)
14795 */
14796 if (attTup->atthasdef)
14797 {
14798 defaultexpr = build_column_default(rel, attnum);
14799 Assert(defaultexpr);
14800 defaultexpr = strip_implicit_coercions(defaultexpr);
14801 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14802 defaultexpr, exprType(defaultexpr),
14803 targettype, targettypmod,
14806 -1);
14807 if (defaultexpr == NULL)
14808 {
14809 if (attTup->attgenerated)
14810 ereport(ERROR,
14811 (errcode(ERRCODE_DATATYPE_MISMATCH),
14812 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14813 colName, format_type_be(targettype))));
14814 else
14815 ereport(ERROR,
14816 (errcode(ERRCODE_DATATYPE_MISMATCH),
14817 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14818 colName, format_type_be(targettype))));
14819 }
14820 }
14821 else
14822 defaultexpr = NULL;
14823
14824 /*
14825 * Find everything that depends on the column (constraints, indexes, etc),
14826 * and record enough information to let us recreate the objects.
14827 *
14828 * The actual recreation does not happen here, but only after we have
14829 * performed all the individual ALTER TYPE operations. We have to save
14830 * the info before executing ALTER TYPE, though, else the deparser will
14831 * get confused.
14832 */
14834
14835 /*
14836 * Now scan for dependencies of this column on other things. The only
14837 * things we should find are the dependency on the column datatype and
14838 * possibly a collation dependency. Those can be removed.
14839 */
14840 depRel = table_open(DependRelationId, RowExclusiveLock);
14841
14842 ScanKeyInit(&key[0],
14843 Anum_pg_depend_classid,
14844 BTEqualStrategyNumber, F_OIDEQ,
14845 ObjectIdGetDatum(RelationRelationId));
14846 ScanKeyInit(&key[1],
14847 Anum_pg_depend_objid,
14848 BTEqualStrategyNumber, F_OIDEQ,
14850 ScanKeyInit(&key[2],
14851 Anum_pg_depend_objsubid,
14852 BTEqualStrategyNumber, F_INT4EQ,
14854
14855 scan = systable_beginscan(depRel, DependDependerIndexId, true,
14856 NULL, 3, key);
14857
14858 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14859 {
14860 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14861 ObjectAddress foundObject;
14862
14863 foundObject.classId = foundDep->refclassid;
14864 foundObject.objectId = foundDep->refobjid;
14865 foundObject.objectSubId = foundDep->refobjsubid;
14866
14867 if (foundDep->deptype != DEPENDENCY_NORMAL)
14868 elog(ERROR, "found unexpected dependency type '%c'",
14869 foundDep->deptype);
14870 if (!(foundDep->refclassid == TypeRelationId &&
14871 foundDep->refobjid == attTup->atttypid) &&
14872 !(foundDep->refclassid == CollationRelationId &&
14873 foundDep->refobjid == attTup->attcollation))
14874 elog(ERROR, "found unexpected dependency for column: %s",
14875 getObjectDescription(&foundObject, false));
14876
14877 CatalogTupleDelete(depRel, &depTup->t_self);
14878 }
14879
14880 systable_endscan(scan);
14881
14883
14884 /*
14885 * Here we go --- change the recorded column type and collation. (Note
14886 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14887 * fix up the missing value if any.
14888 */
14889 if (attTup->atthasmissing)
14890 {
14891 Datum missingval;
14892 bool missingNull;
14893
14894 /* if rewrite is true the missing value should already be cleared */
14895 Assert(tab->rewrite == 0);
14896
14897 /* Get the missing value datum */
14898 missingval = heap_getattr(heapTup,
14899 Anum_pg_attribute_attmissingval,
14900 attrelation->rd_att,
14901 &missingNull);
14902
14903 /* if it's a null array there is nothing to do */
14904
14905 if (!missingNull)
14906 {
14907 /*
14908 * Get the datum out of the array and repack it in a new array
14909 * built with the new type data. We assume that since the table
14910 * doesn't need rewriting, the actual Datum doesn't need to be
14911 * changed, only the array metadata.
14912 */
14913
14914 int one = 1;
14915 bool isNull;
14916 Datum valuesAtt[Natts_pg_attribute] = {0};
14917 bool nullsAtt[Natts_pg_attribute] = {0};
14918 bool replacesAtt[Natts_pg_attribute] = {0};
14919 HeapTuple newTup;
14920
14921 missingval = array_get_element(missingval,
14922 1,
14923 &one,
14924 0,
14925 attTup->attlen,
14926 attTup->attbyval,
14927 attTup->attalign,
14928 &isNull);
14929 missingval = PointerGetDatum(construct_array(&missingval,
14930 1,
14931 targettype,
14932 tform->typlen,
14933 tform->typbyval,
14934 tform->typalign));
14935
14936 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14937 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14938 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14939
14940 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14941 valuesAtt, nullsAtt, replacesAtt);
14942 heap_freetuple(heapTup);
14943 heapTup = newTup;
14944 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14945 }
14946 }
14947
14948 attTup->atttypid = targettype;
14949 attTup->atttypmod = targettypmod;
14950 attTup->attcollation = targetcollid;
14951 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14952 ereport(ERROR,
14953 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14954 errmsg("too many array dimensions"));
14955 attTup->attndims = list_length(typeName->arrayBounds);
14956 attTup->attlen = tform->typlen;
14957 attTup->attbyval = tform->typbyval;
14958 attTup->attalign = tform->typalign;
14959 attTup->attstorage = tform->typstorage;
14960 attTup->attcompression = InvalidCompressionMethod;
14961
14962 ReleaseSysCache(typeTuple);
14963
14964 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14965
14966 table_close(attrelation, RowExclusiveLock);
14967
14968 /* Install dependencies on new datatype and collation */
14971
14972 /*
14973 * Drop any pg_statistic entry for the column, since it's now wrong type
14974 */
14976
14977 InvokeObjectPostAlterHook(RelationRelationId,
14978 RelationGetRelid(rel), attnum);
14979
14980 /*
14981 * Update the default, if present, by brute force --- remove and re-add
14982 * the default. Probably unsafe to take shortcuts, since the new version
14983 * may well have additional dependencies. (It's okay to do this now,
14984 * rather than after other ALTER TYPE commands, since the default won't
14985 * depend on other column types.)
14986 */
14987 if (defaultexpr)
14988 {
14989 /*
14990 * If it's a GENERATED default, drop its dependency records, in
14991 * particular its INTERNAL dependency on the column, which would
14992 * otherwise cause dependency.c to refuse to perform the deletion.
14993 */
14994 if (attTup->attgenerated)
14995 {
14996 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14997
14998 if (!OidIsValid(attrdefoid))
14999 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15000 RelationGetRelid(rel), attnum);
15001 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
15002 }
15003
15004 /*
15005 * Make updates-so-far visible, particularly the new pg_attribute row
15006 * which will be updated again.
15007 */
15009
15010 /*
15011 * We use RESTRICT here for safety, but at present we do not expect
15012 * anything to depend on the default.
15013 */
15015 true);
15016
15017 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15018 }
15019
15020 ObjectAddressSubSet(address, RelationRelationId,
15021 RelationGetRelid(rel), attnum);
15022
15023 /* Cleanup */
15024 heap_freetuple(heapTup);
15025
15026 return address;
15027}
15028
15029/*
15030 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
15031 * that depends on the column (constraints, indexes, etc), and record enough
15032 * information to let us recreate the objects.
15033 */
15034static void
15036 Relation rel, AttrNumber attnum, const char *colName)
15037{
15038 Relation depRel;
15039 ScanKeyData key[3];
15040 SysScanDesc scan;
15041 HeapTuple depTup;
15042
15043 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15044
15045 depRel = table_open(DependRelationId, RowExclusiveLock);
15046
15047 ScanKeyInit(&key[0],
15048 Anum_pg_depend_refclassid,
15049 BTEqualStrategyNumber, F_OIDEQ,
15050 ObjectIdGetDatum(RelationRelationId));
15051 ScanKeyInit(&key[1],
15052 Anum_pg_depend_refobjid,
15053 BTEqualStrategyNumber, F_OIDEQ,
15055 ScanKeyInit(&key[2],
15056 Anum_pg_depend_refobjsubid,
15057 BTEqualStrategyNumber, F_INT4EQ,
15059
15060 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15061 NULL, 3, key);
15062
15063 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15064 {
15065 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15066 ObjectAddress foundObject;
15067
15068 foundObject.classId = foundDep->classid;
15069 foundObject.objectId = foundDep->objid;
15070 foundObject.objectSubId = foundDep->objsubid;
15071
15072 switch (foundObject.classId)
15073 {
15074 case RelationRelationId:
15075 {
15076 char relKind = get_rel_relkind(foundObject.objectId);
15077
15078 if (relKind == RELKIND_INDEX ||
15079 relKind == RELKIND_PARTITIONED_INDEX)
15080 {
15081 Assert(foundObject.objectSubId == 0);
15082 RememberIndexForRebuilding(foundObject.objectId, tab);
15083 }
15084 else if (relKind == RELKIND_SEQUENCE)
15085 {
15086 /*
15087 * This must be a SERIAL column's sequence. We need
15088 * not do anything to it.
15089 */
15090 Assert(foundObject.objectSubId == 0);
15091 }
15092 else
15093 {
15094 /* Not expecting any other direct dependencies... */
15095 elog(ERROR, "unexpected object depending on column: %s",
15096 getObjectDescription(&foundObject, false));
15097 }
15098 break;
15099 }
15100
15101 case ConstraintRelationId:
15102 Assert(foundObject.objectSubId == 0);
15103 RememberConstraintForRebuilding(foundObject.objectId, tab);
15104 break;
15105
15106 case ProcedureRelationId:
15107
15108 /*
15109 * A new-style SQL function can depend on a column, if that
15110 * column is referenced in the parsed function body. Ideally
15111 * we'd automatically update the function by deparsing and
15112 * reparsing it, but that's risky and might well fail anyhow.
15113 * FIXME someday.
15114 *
15115 * This is only a problem for AT_AlterColumnType, not
15116 * AT_SetExpression.
15117 */
15118 if (subtype == AT_AlterColumnType)
15119 ereport(ERROR,
15120 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15121 errmsg("cannot alter type of a column used by a function or procedure"),
15122 errdetail("%s depends on column \"%s\"",
15123 getObjectDescription(&foundObject, false),
15124 colName)));
15125 break;
15126
15127 case RewriteRelationId:
15128
15129 /*
15130 * View/rule bodies have pretty much the same issues as
15131 * function bodies. FIXME someday.
15132 */
15133 if (subtype == AT_AlterColumnType)
15134 ereport(ERROR,
15135 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15136 errmsg("cannot alter type of a column used by a view or rule"),
15137 errdetail("%s depends on column \"%s\"",
15138 getObjectDescription(&foundObject, false),
15139 colName)));
15140 break;
15141
15142 case TriggerRelationId:
15143
15144 /*
15145 * A trigger can depend on a column because the column is
15146 * specified as an update target, or because the column is
15147 * used in the trigger's WHEN condition. The first case would
15148 * not require any extra work, but the second case would
15149 * require updating the WHEN expression, which has the same
15150 * issues as above. Since we can't easily tell which case
15151 * applies, we punt for both. FIXME someday.
15152 */
15153 if (subtype == AT_AlterColumnType)
15154 ereport(ERROR,
15155 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15156 errmsg("cannot alter type of a column used in a trigger definition"),
15157 errdetail("%s depends on column \"%s\"",
15158 getObjectDescription(&foundObject, false),
15159 colName)));
15160 break;
15161
15162 case PolicyRelationId:
15163
15164 /*
15165 * A policy can depend on a column because the column is
15166 * specified in the policy's USING or WITH CHECK qual
15167 * expressions. It might be possible to rewrite and recheck
15168 * the policy expression, but punt for now. It's certainly
15169 * easy enough to remove and recreate the policy; still, FIXME
15170 * someday.
15171 */
15172 if (subtype == AT_AlterColumnType)
15173 ereport(ERROR,
15174 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15175 errmsg("cannot alter type of a column used in a policy definition"),
15176 errdetail("%s depends on column \"%s\"",
15177 getObjectDescription(&foundObject, false),
15178 colName)));
15179 break;
15180
15181 case AttrDefaultRelationId:
15182 {
15184
15185 if (col.objectId == RelationGetRelid(rel) &&
15186 col.objectSubId == attnum)
15187 {
15188 /*
15189 * Ignore the column's own default expression. The
15190 * caller deals with it.
15191 */
15192 }
15193 else
15194 {
15195 /*
15196 * This must be a reference from the expression of a
15197 * generated column elsewhere in the same table.
15198 * Changing the type/generated expression of a column
15199 * that is used by a generated column is not allowed
15200 * by SQL standard, so just punt for now. It might be
15201 * doable with some thinking and effort.
15202 */
15203 if (subtype == AT_AlterColumnType)
15204 ereport(ERROR,
15205 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15206 errmsg("cannot alter type of a column used by a generated column"),
15207 errdetail("Column \"%s\" is used by generated column \"%s\".",
15208 colName,
15210 col.objectSubId,
15211 false))));
15212 }
15213 break;
15214 }
15215
15216 case StatisticExtRelationId:
15217
15218 /*
15219 * Give the extended-stats machinery a chance to fix anything
15220 * that this column type change would break.
15221 */
15222 RememberStatisticsForRebuilding(foundObject.objectId, tab);
15223 break;
15224
15225 case PublicationRelRelationId:
15226
15227 /*
15228 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15229 * clause. Same issues as above. FIXME someday.
15230 */
15231 if (subtype == AT_AlterColumnType)
15232 ereport(ERROR,
15233 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15234 errmsg("cannot alter type of a column used by a publication WHERE clause"),
15235 errdetail("%s depends on column \"%s\"",
15236 getObjectDescription(&foundObject, false),
15237 colName)));
15238 break;
15239
15240 default:
15241
15242 /*
15243 * We don't expect any other sorts of objects to depend on a
15244 * column.
15245 */
15246 elog(ERROR, "unexpected object depending on column: %s",
15247 getObjectDescription(&foundObject, false));
15248 break;
15249 }
15250 }
15251
15252 systable_endscan(scan);
15253 table_close(depRel, NoLock);
15254}
15255
15256/*
15257 * Subroutine for ATExecAlterColumnType: remember that a replica identity
15258 * needs to be reset.
15259 */
15260static void
15262{
15263 if (!get_index_isreplident(indoid))
15264 return;
15265
15266 if (tab->replicaIdentityIndex)
15267 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15268
15269 tab->replicaIdentityIndex = get_rel_name(indoid);
15270}
15271
15272/*
15273 * Subroutine for ATExecAlterColumnType: remember any clustered index.
15274 */
15275static void
15277{
15278 if (!get_index_isclustered(indoid))
15279 return;
15280
15281 if (tab->clusterOnIndex)
15282 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15283
15284 tab->clusterOnIndex = get_rel_name(indoid);
15285}
15286
15287/*
15288 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15289 * to be rebuilt (which we might already know).
15290 */
15291static void
15293{
15294 /*
15295 * This de-duplication check is critical for two independent reasons: we
15296 * mustn't try to recreate the same constraint twice, and if a constraint
15297 * depends on more than one column whose type is to be altered, we must
15298 * capture its definition string before applying any of the column type
15299 * changes. ruleutils.c will get confused if we ask again later.
15300 */
15301 if (!list_member_oid(tab->changedConstraintOids, conoid))
15302 {
15303 /* OK, capture the constraint's existing definition string */
15304 char *defstring = pg_get_constraintdef_command(conoid);
15305 Oid indoid;
15306
15307 /*
15308 * It is critical to create not-null constraints ahead of primary key
15309 * indexes; otherwise, the not-null constraint would be created by the
15310 * primary key, and the constraint name would be wrong.
15311 */
15312 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15313 {
15314 tab->changedConstraintOids = lcons_oid(conoid,
15316 tab->changedConstraintDefs = lcons(defstring,
15318 }
15319 else
15320 {
15321
15323 conoid);
15325 defstring);
15326 }
15327
15328 /*
15329 * For the index of a constraint, if any, remember if it is used for
15330 * the table's replica identity or if it is a clustered index, so that
15331 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15332 * those properties.
15333 */
15334 indoid = get_constraint_index(conoid);
15335 if (OidIsValid(indoid))
15336 {
15338 RememberClusterOnForRebuilding(indoid, tab);
15339 }
15340 }
15341}
15342
15343/*
15344 * Subroutine for ATExecAlterColumnType: remember that an index needs
15345 * to be rebuilt (which we might already know).
15346 */
15347static void
15349{
15350 /*
15351 * This de-duplication check is critical for two independent reasons: we
15352 * mustn't try to recreate the same index twice, and if an index depends
15353 * on more than one column whose type is to be altered, we must capture
15354 * its definition string before applying any of the column type changes.
15355 * ruleutils.c will get confused if we ask again later.
15356 */
15357 if (!list_member_oid(tab->changedIndexOids, indoid))
15358 {
15359 /*
15360 * Before adding it as an index-to-rebuild, we'd better see if it
15361 * belongs to a constraint, and if so rebuild the constraint instead.
15362 * Typically this check fails, because constraint indexes normally
15363 * have only dependencies on their constraint. But it's possible for
15364 * such an index to also have direct dependencies on table columns,
15365 * for example with a partial exclusion constraint.
15366 */
15367 Oid conoid = get_index_constraint(indoid);
15368
15369 if (OidIsValid(conoid))
15370 {
15372 }
15373 else
15374 {
15375 /* OK, capture the index's existing definition string */
15376 char *defstring = pg_get_indexdef_string(indoid);
15377
15379 indoid);
15381 defstring);
15382
15383 /*
15384 * Remember if this index is used for the table's replica identity
15385 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15386 * can queue up commands necessary to restore those properties.
15387 */
15389 RememberClusterOnForRebuilding(indoid, tab);
15390 }
15391 }
15392}
15393
15394/*
15395 * Subroutine for ATExecAlterColumnType: remember that a statistics object
15396 * needs to be rebuilt (which we might already know).
15397 */
15398static void
15400{
15401 /*
15402 * This de-duplication check is critical for two independent reasons: we
15403 * mustn't try to recreate the same statistics object twice, and if the
15404 * statistics object depends on more than one column whose type is to be
15405 * altered, we must capture its definition string before applying any of
15406 * the type changes. ruleutils.c will get confused if we ask again later.
15407 */
15408 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15409 {
15410 /* OK, capture the statistics object's existing definition string */
15411 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15412
15414 stxoid);
15416 defstring);
15417 }
15418}
15419
15420/*
15421 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15422 * operations for a particular relation. We have to drop and recreate all the
15423 * indexes and constraints that depend on the altered columns. We do the
15424 * actual dropping here, but re-creation is managed by adding work queue
15425 * entries to do those steps later.
15426 */
15427static void
15429{
15430 ObjectAddress obj;
15431 ObjectAddresses *objects;
15432 ListCell *def_item;
15433 ListCell *oid_item;
15434
15435 /*
15436 * Collect all the constraints and indexes to drop so we can process them
15437 * in a single call. That way we don't have to worry about dependencies
15438 * among them.
15439 */
15440 objects = new_object_addresses();
15441
15442 /*
15443 * Re-parse the index and constraint definitions, and attach them to the
15444 * appropriate work queue entries. We do this before dropping because in
15445 * the case of a constraint on another table, we might not yet have
15446 * exclusive lock on the table the constraint is attached to, and we need
15447 * to get that before reparsing/dropping. (That's possible at least for
15448 * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15449 * requires a dependency on the target table's composite type in the other
15450 * table's constraint expressions.)
15451 *
15452 * We can't rely on the output of deparsing to tell us which relation to
15453 * operate on, because concurrent activity might have made the name
15454 * resolve differently. Instead, we've got to use the OID of the
15455 * constraint or index we're processing to figure out which relation to
15456 * operate on.
15457 */
15458 forboth(oid_item, tab->changedConstraintOids,
15459 def_item, tab->changedConstraintDefs)
15460 {
15461 Oid oldId = lfirst_oid(oid_item);
15462 HeapTuple tup;
15464 Oid relid;
15465 Oid confrelid;
15466 bool conislocal;
15467
15468 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15469 if (!HeapTupleIsValid(tup)) /* should not happen */
15470 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15471 con = (Form_pg_constraint) GETSTRUCT(tup);
15472 if (OidIsValid(con->conrelid))
15473 relid = con->conrelid;
15474 else
15475 {
15476 /* must be a domain constraint */
15477 relid = get_typ_typrelid(getBaseType(con->contypid));
15478 if (!OidIsValid(relid))
15479 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15480 }
15481 confrelid = con->confrelid;
15482 conislocal = con->conislocal;
15483 ReleaseSysCache(tup);
15484
15485 ObjectAddressSet(obj, ConstraintRelationId, oldId);
15486 add_exact_object_address(&obj, objects);
15487
15488 /*
15489 * If the constraint is inherited (only), we don't want to inject a
15490 * new definition here; it'll get recreated when
15491 * ATAddCheckNNConstraint recurses from adding the parent table's
15492 * constraint. But we had to carry the info this far so that we can
15493 * drop the constraint below.
15494 */
15495 if (!conislocal)
15496 continue;
15497
15498 /*
15499 * When rebuilding another table's constraint that references the
15500 * table we're modifying, we might not yet have any lock on the other
15501 * table, so get one now. We'll need AccessExclusiveLock for the DROP
15502 * CONSTRAINT step, so there's no value in asking for anything weaker.
15503 */
15504 if (relid != tab->relid)
15506
15507 ATPostAlterTypeParse(oldId, relid, confrelid,
15508 (char *) lfirst(def_item),
15509 wqueue, lockmode, tab->rewrite);
15510 }
15511 forboth(oid_item, tab->changedIndexOids,
15512 def_item, tab->changedIndexDefs)
15513 {
15514 Oid oldId = lfirst_oid(oid_item);
15515 Oid relid;
15516
15517 relid = IndexGetRelation(oldId, false);
15518
15519 /*
15520 * As above, make sure we have lock on the index's table if it's not
15521 * the same table.
15522 */
15523 if (relid != tab->relid)
15525
15526 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15527 (char *) lfirst(def_item),
15528 wqueue, lockmode, tab->rewrite);
15529
15530 ObjectAddressSet(obj, RelationRelationId, oldId);
15531 add_exact_object_address(&obj, objects);
15532 }
15533
15534 /* add dependencies for new statistics */
15535 forboth(oid_item, tab->changedStatisticsOids,
15536 def_item, tab->changedStatisticsDefs)
15537 {
15538 Oid oldId = lfirst_oid(oid_item);
15539 Oid relid;
15540
15541 relid = StatisticsGetRelation(oldId, false);
15542
15543 /*
15544 * As above, make sure we have lock on the statistics object's table
15545 * if it's not the same table. However, we take
15546 * ShareUpdateExclusiveLock here, aligning with the lock level used in
15547 * CreateStatistics and RemoveStatisticsById.
15548 *
15549 * CAUTION: this should be done after all cases that grab
15550 * AccessExclusiveLock, else we risk causing deadlock due to needing
15551 * to promote our table lock.
15552 */
15553 if (relid != tab->relid)
15555
15556 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15557 (char *) lfirst(def_item),
15558 wqueue, lockmode, tab->rewrite);
15559
15560 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15561 add_exact_object_address(&obj, objects);
15562 }
15563
15564 /*
15565 * Queue up command to restore replica identity index marking
15566 */
15567 if (tab->replicaIdentityIndex)
15568 {
15571
15572 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15573 subcmd->name = tab->replicaIdentityIndex;
15575 cmd->def = (Node *) subcmd;
15576
15577 /* do it after indexes and constraints */
15578 tab->subcmds[AT_PASS_OLD_CONSTR] =
15579 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15580 }
15581
15582 /*
15583 * Queue up command to restore marking of index used for cluster.
15584 */
15585 if (tab->clusterOnIndex)
15586 {
15588
15589 cmd->subtype = AT_ClusterOn;
15590 cmd->name = tab->clusterOnIndex;
15591
15592 /* do it after indexes and constraints */
15593 tab->subcmds[AT_PASS_OLD_CONSTR] =
15594 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15595 }
15596
15597 /*
15598 * It should be okay to use DROP_RESTRICT here, since nothing else should
15599 * be depending on these objects.
15600 */
15602
15603 free_object_addresses(objects);
15604
15605 /*
15606 * The objects will get recreated during subsequent passes over the work
15607 * queue.
15608 */
15609}
15610
15611/*
15612 * Parse the previously-saved definition string for a constraint, index or
15613 * statistics object against the newly-established column data type(s), and
15614 * queue up the resulting command parsetrees for execution.
15615 *
15616 * This might fail if, for example, you have a WHERE clause that uses an
15617 * operator that's not available for the new column type.
15618 */
15619static void
15620ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15621 List **wqueue, LOCKMODE lockmode, bool rewrite)
15622{
15623 List *raw_parsetree_list;
15624 List *querytree_list;
15625 ListCell *list_item;
15626 Relation rel;
15627
15628 /*
15629 * We expect that we will get only ALTER TABLE and CREATE INDEX
15630 * statements. Hence, there is no need to pass them through
15631 * parse_analyze_*() or the rewriter, but instead we need to pass them
15632 * through parse_utilcmd.c to make them ready for execution.
15633 */
15634 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15635 querytree_list = NIL;
15636 foreach(list_item, raw_parsetree_list)
15637 {
15638 RawStmt *rs = lfirst_node(RawStmt, list_item);
15639 Node *stmt = rs->stmt;
15640
15641 if (IsA(stmt, IndexStmt))
15642 querytree_list = lappend(querytree_list,
15643 transformIndexStmt(oldRelId,
15644 (IndexStmt *) stmt,
15645 cmd));
15646 else if (IsA(stmt, AlterTableStmt))
15647 {
15648 List *beforeStmts;
15649 List *afterStmts;
15650
15651 stmt = (Node *) transformAlterTableStmt(oldRelId,
15652 (AlterTableStmt *) stmt,
15653 cmd,
15654 &beforeStmts,
15655 &afterStmts);
15656 querytree_list = list_concat(querytree_list, beforeStmts);
15657 querytree_list = lappend(querytree_list, stmt);
15658 querytree_list = list_concat(querytree_list, afterStmts);
15659 }
15660 else if (IsA(stmt, CreateStatsStmt))
15661 querytree_list = lappend(querytree_list,
15662 transformStatsStmt(oldRelId,
15664 cmd));
15665 else
15666 querytree_list = lappend(querytree_list, stmt);
15667 }
15668
15669 /* Caller should already have acquired whatever lock we need. */
15670 rel = relation_open(oldRelId, NoLock);
15671
15672 /*
15673 * Attach each generated command to the proper place in the work queue.
15674 * Note this could result in creation of entirely new work-queue entries.
15675 *
15676 * Also note that we have to tweak the command subtypes, because it turns
15677 * out that re-creation of indexes and constraints has to act a bit
15678 * differently from initial creation.
15679 */
15680 foreach(list_item, querytree_list)
15681 {
15682 Node *stm = (Node *) lfirst(list_item);
15683 AlteredTableInfo *tab;
15684
15685 tab = ATGetQueueEntry(wqueue, rel);
15686
15687 if (IsA(stm, IndexStmt))
15688 {
15689 IndexStmt *stmt = (IndexStmt *) stm;
15690 AlterTableCmd *newcmd;
15691
15692 if (!rewrite)
15693 TryReuseIndex(oldId, stmt);
15694 stmt->reset_default_tblspc = true;
15695 /* keep the index's comment */
15696 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15697
15698 newcmd = makeNode(AlterTableCmd);
15699 newcmd->subtype = AT_ReAddIndex;
15700 newcmd->def = (Node *) stmt;
15702 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15703 }
15704 else if (IsA(stm, AlterTableStmt))
15705 {
15707 ListCell *lcmd;
15708
15709 foreach(lcmd, stmt->cmds)
15710 {
15712
15713 if (cmd->subtype == AT_AddIndex)
15714 {
15715 IndexStmt *indstmt;
15716 Oid indoid;
15717
15718 indstmt = castNode(IndexStmt, cmd->def);
15719 indoid = get_constraint_index(oldId);
15720
15721 if (!rewrite)
15722 TryReuseIndex(indoid, indstmt);
15723 /* keep any comment on the index */
15724 indstmt->idxcomment = GetComment(indoid,
15725 RelationRelationId, 0);
15726 indstmt->reset_default_tblspc = true;
15727
15728 cmd->subtype = AT_ReAddIndex;
15730 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15731
15732 /* recreate any comment on the constraint */
15735 oldId,
15736 rel,
15737 NIL,
15738 indstmt->idxname);
15739 }
15740 else if (cmd->subtype == AT_AddConstraint)
15741 {
15742 Constraint *con = castNode(Constraint, cmd->def);
15743
15744 con->old_pktable_oid = refRelId;
15745 /* rewriting neither side of a FK */
15746 if (con->contype == CONSTR_FOREIGN &&
15747 !rewrite && tab->rewrite == 0)
15748 TryReuseForeignKey(oldId, con);
15749 con->reset_default_tblspc = true;
15753
15754 /*
15755 * Recreate any comment on the constraint. If we have
15756 * recreated a primary key, then transformTableConstraint
15757 * has added an unnamed not-null constraint here; skip
15758 * this in that case.
15759 */
15760 if (con->conname)
15763 oldId,
15764 rel,
15765 NIL,
15766 con->conname);
15767 else
15768 Assert(con->contype == CONSTR_NOTNULL);
15769 }
15770 else
15771 elog(ERROR, "unexpected statement subtype: %d",
15772 (int) cmd->subtype);
15773 }
15774 }
15775 else if (IsA(stm, AlterDomainStmt))
15776 {
15778
15779 if (stmt->subtype == AD_AddConstraint)
15780 {
15781 Constraint *con = castNode(Constraint, stmt->def);
15783
15785 cmd->def = (Node *) stmt;
15788
15789 /* recreate any comment on the constraint */
15792 oldId,
15793 NULL,
15794 stmt->typeName,
15795 con->conname);
15796 }
15797 else
15798 elog(ERROR, "unexpected statement subtype: %d",
15799 (int) stmt->subtype);
15800 }
15801 else if (IsA(stm, CreateStatsStmt))
15802 {
15804 AlterTableCmd *newcmd;
15805
15806 /* keep the statistics object's comment */
15807 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15808
15809 newcmd = makeNode(AlterTableCmd);
15810 newcmd->subtype = AT_ReAddStatistics;
15811 newcmd->def = (Node *) stmt;
15812 tab->subcmds[AT_PASS_MISC] =
15813 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15814 }
15815 else
15816 elog(ERROR, "unexpected statement type: %d",
15817 (int) nodeTag(stm));
15818 }
15819
15820 relation_close(rel, NoLock);
15821}
15822
15823/*
15824 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15825 * for a table or domain constraint that is being rebuilt.
15826 *
15827 * objid is the OID of the constraint.
15828 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15829 * as a string list) for a domain constraint.
15830 * (We could dig that info, as well as the conname, out of the pg_constraint
15831 * entry; but callers already have them so might as well pass them.)
15832 */
15833static void
15835 Relation rel, List *domname,
15836 const char *conname)
15837{
15838 CommentStmt *cmd;
15839 char *comment_str;
15840 AlterTableCmd *newcmd;
15841
15842 /* Look for comment for object wanted, and leave if none */
15843 comment_str = GetComment(objid, ConstraintRelationId, 0);
15844 if (comment_str == NULL)
15845 return;
15846
15847 /* Build CommentStmt node, copying all input data for safety */
15848 cmd = makeNode(CommentStmt);
15849 if (rel)
15850 {
15852 cmd->object = (Node *)
15855 makeString(pstrdup(conname)));
15856 }
15857 else
15858 {
15860 cmd->object = (Node *)
15862 makeString(pstrdup(conname)));
15863 }
15864 cmd->comment = comment_str;
15865
15866 /* Append it to list of commands */
15867 newcmd = makeNode(AlterTableCmd);
15868 newcmd->subtype = AT_ReAddComment;
15869 newcmd->def = (Node *) cmd;
15870 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15871}
15872
15873/*
15874 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15875 * for the real analysis, then mutates the IndexStmt based on that verdict.
15876 */
15877static void
15879{
15880 if (CheckIndexCompatible(oldId,
15881 stmt->accessMethod,
15882 stmt->indexParams,
15883 stmt->excludeOpNames,
15884 stmt->iswithoutoverlaps))
15885 {
15886 Relation irel = index_open(oldId, NoLock);
15887
15888 /* If it's a partitioned index, there is no storage to share. */
15889 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15890 {
15891 stmt->oldNumber = irel->rd_locator.relNumber;
15892 stmt->oldCreateSubid = irel->rd_createSubid;
15893 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15894 }
15895 index_close(irel, NoLock);
15896 }
15897}
15898
15899/*
15900 * Subroutine for ATPostAlterTypeParse().
15901 *
15902 * Stash the old P-F equality operator into the Constraint node, for possible
15903 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15904 * this constraint can be skipped.
15905 */
15906static void
15908{
15909 HeapTuple tup;
15910 Datum adatum;
15911 ArrayType *arr;
15912 Oid *rawarr;
15913 int numkeys;
15914 int i;
15915
15916 Assert(con->contype == CONSTR_FOREIGN);
15917 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15918
15919 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15920 if (!HeapTupleIsValid(tup)) /* should not happen */
15921 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15922
15923 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15924 Anum_pg_constraint_conpfeqop);
15925 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15926 numkeys = ARR_DIMS(arr)[0];
15927 /* test follows the one in ri_FetchConstraintInfo() */
15928 if (ARR_NDIM(arr) != 1 ||
15929 ARR_HASNULL(arr) ||
15930 ARR_ELEMTYPE(arr) != OIDOID)
15931 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15932 rawarr = (Oid *) ARR_DATA_PTR(arr);
15933
15934 /* stash a List of the operator Oids in our Constraint node */
15935 for (i = 0; i < numkeys; i++)
15936 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15937
15938 ReleaseSysCache(tup);
15939}
15940
15941/*
15942 * ALTER COLUMN .. OPTIONS ( ... )
15943 *
15944 * Returns the address of the modified column
15945 */
15946static ObjectAddress
15948 const char *colName,
15949 List *options,
15950 LOCKMODE lockmode)
15951{
15952 Relation ftrel;
15953 Relation attrel;
15954 ForeignServer *server;
15955 ForeignDataWrapper *fdw;
15956 HeapTuple tuple;
15957 HeapTuple newtuple;
15958 bool isnull;
15959 Datum repl_val[Natts_pg_attribute];
15960 bool repl_null[Natts_pg_attribute];
15961 bool repl_repl[Natts_pg_attribute];
15962 Datum datum;
15963 Form_pg_foreign_table fttableform;
15964 Form_pg_attribute atttableform;
15966 ObjectAddress address;
15967
15968 if (options == NIL)
15969 return InvalidObjectAddress;
15970
15971 /* First, determine FDW validator associated to the foreign table. */
15972 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15973 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15974 if (!HeapTupleIsValid(tuple))
15975 ereport(ERROR,
15976 (errcode(ERRCODE_UNDEFINED_OBJECT),
15977 errmsg("foreign table \"%s\" does not exist",
15979 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15980 server = GetForeignServer(fttableform->ftserver);
15981 fdw = GetForeignDataWrapper(server->fdwid);
15982
15984 ReleaseSysCache(tuple);
15985
15986 attrel = table_open(AttributeRelationId, RowExclusiveLock);
15987 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15988 if (!HeapTupleIsValid(tuple))
15989 ereport(ERROR,
15990 (errcode(ERRCODE_UNDEFINED_COLUMN),
15991 errmsg("column \"%s\" of relation \"%s\" does not exist",
15992 colName, RelationGetRelationName(rel))));
15993
15994 /* Prevent them from altering a system attribute */
15995 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15996 attnum = atttableform->attnum;
15997 if (attnum <= 0)
15998 ereport(ERROR,
15999 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16000 errmsg("cannot alter system column \"%s\"", colName)));
16001
16002
16003 /* Initialize buffers for new tuple values */
16004 memset(repl_val, 0, sizeof(repl_val));
16005 memset(repl_null, false, sizeof(repl_null));
16006 memset(repl_repl, false, sizeof(repl_repl));
16007
16008 /* Extract the current options */
16009 datum = SysCacheGetAttr(ATTNAME,
16010 tuple,
16011 Anum_pg_attribute_attfdwoptions,
16012 &isnull);
16013 if (isnull)
16014 datum = PointerGetDatum(NULL);
16015
16016 /* Transform the options */
16017 datum = transformGenericOptions(AttributeRelationId,
16018 datum,
16019 options,
16020 fdw->fdwvalidator);
16021
16022 if (DatumGetPointer(datum) != NULL)
16023 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16024 else
16025 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16026
16027 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16028
16029 /* Everything looks good - update the tuple */
16030
16031 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16032 repl_val, repl_null, repl_repl);
16033
16034 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16035
16036 InvokeObjectPostAlterHook(RelationRelationId,
16037 RelationGetRelid(rel),
16038 atttableform->attnum);
16039 ObjectAddressSubSet(address, RelationRelationId,
16040 RelationGetRelid(rel), attnum);
16041
16042 ReleaseSysCache(tuple);
16043
16045
16046 heap_freetuple(newtuple);
16047
16048 return address;
16049}
16050
16051/*
16052 * ALTER TABLE OWNER
16053 *
16054 * recursing is true if we are recursing from a table to its indexes,
16055 * sequences, or toast table. We don't allow the ownership of those things to
16056 * be changed separately from the parent table. Also, we can skip permission
16057 * checks (this is necessary not just an optimization, else we'd fail to
16058 * handle toast tables properly).
16059 *
16060 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
16061 * free-standing composite type.
16062 */
16063void
16064ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
16065{
16066 Relation target_rel;
16067 Relation class_rel;
16068 HeapTuple tuple;
16069 Form_pg_class tuple_class;
16070
16071 /*
16072 * Get exclusive lock till end of transaction on the target table. Use
16073 * relation_open so that we can work on indexes and sequences.
16074 */
16075 target_rel = relation_open(relationOid, lockmode);
16076
16077 /* Get its pg_class tuple, too */
16078 class_rel = table_open(RelationRelationId, RowExclusiveLock);
16079
16080 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16081 if (!HeapTupleIsValid(tuple))
16082 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16083 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16084
16085 /* Can we change the ownership of this tuple? */
16086 switch (tuple_class->relkind)
16087 {
16088 case RELKIND_RELATION:
16089 case RELKIND_VIEW:
16090 case RELKIND_MATVIEW:
16091 case RELKIND_FOREIGN_TABLE:
16092 case RELKIND_PARTITIONED_TABLE:
16093 /* ok to change owner */
16094 break;
16095 case RELKIND_INDEX:
16096 if (!recursing)
16097 {
16098 /*
16099 * Because ALTER INDEX OWNER used to be allowed, and in fact
16100 * is generated by old versions of pg_dump, we give a warning
16101 * and do nothing rather than erroring out. Also, to avoid
16102 * unnecessary chatter while restoring those old dumps, say
16103 * nothing at all if the command would be a no-op anyway.
16104 */
16105 if (tuple_class->relowner != newOwnerId)
16107 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16108 errmsg("cannot change owner of index \"%s\"",
16109 NameStr(tuple_class->relname)),
16110 errhint("Change the ownership of the index's table instead.")));
16111 /* quick hack to exit via the no-op path */
16112 newOwnerId = tuple_class->relowner;
16113 }
16114 break;
16115 case RELKIND_PARTITIONED_INDEX:
16116 if (recursing)
16117 break;
16118 ereport(ERROR,
16119 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16120 errmsg("cannot change owner of index \"%s\"",
16121 NameStr(tuple_class->relname)),
16122 errhint("Change the ownership of the index's table instead.")));
16123 break;
16124 case RELKIND_SEQUENCE:
16125 if (!recursing &&
16126 tuple_class->relowner != newOwnerId)
16127 {
16128 /* if it's an owned sequence, disallow changing it by itself */
16129 Oid tableId;
16130 int32 colId;
16131
16132 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16133 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16134 ereport(ERROR,
16135 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16136 errmsg("cannot change owner of sequence \"%s\"",
16137 NameStr(tuple_class->relname)),
16138 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16139 NameStr(tuple_class->relname),
16140 get_rel_name(tableId))));
16141 }
16142 break;
16143 case RELKIND_COMPOSITE_TYPE:
16144 if (recursing)
16145 break;
16146 ereport(ERROR,
16147 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16148 errmsg("\"%s\" is a composite type",
16149 NameStr(tuple_class->relname)),
16150 /* translator: %s is an SQL ALTER command */
16151 errhint("Use %s instead.",
16152 "ALTER TYPE")));
16153 break;
16154 case RELKIND_TOASTVALUE:
16155 if (recursing)
16156 break;
16157 /* FALL THRU */
16158 default:
16159 ereport(ERROR,
16160 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16161 errmsg("cannot change owner of relation \"%s\"",
16162 NameStr(tuple_class->relname)),
16163 errdetail_relkind_not_supported(tuple_class->relkind)));
16164 }
16165
16166 /*
16167 * If the new owner is the same as the existing owner, consider the
16168 * command to have succeeded. This is for dump restoration purposes.
16169 */
16170 if (tuple_class->relowner != newOwnerId)
16171 {
16172 Datum repl_val[Natts_pg_class];
16173 bool repl_null[Natts_pg_class];
16174 bool repl_repl[Natts_pg_class];
16175 Acl *newAcl;
16176 Datum aclDatum;
16177 bool isNull;
16178 HeapTuple newtuple;
16179
16180 /* skip permission checks when recursing to index or toast table */
16181 if (!recursing)
16182 {
16183 /* Superusers can always do it */
16184 if (!superuser())
16185 {
16186 Oid namespaceOid = tuple_class->relnamespace;
16187 AclResult aclresult;
16188
16189 /* Otherwise, must be owner of the existing object */
16190 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16192 RelationGetRelationName(target_rel));
16193
16194 /* Must be able to become new owner */
16195 check_can_set_role(GetUserId(), newOwnerId);
16196
16197 /* New owner must have CREATE privilege on namespace */
16198 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16199 ACL_CREATE);
16200 if (aclresult != ACLCHECK_OK)
16201 aclcheck_error(aclresult, OBJECT_SCHEMA,
16202 get_namespace_name(namespaceOid));
16203 }
16204 }
16205
16206 memset(repl_null, false, sizeof(repl_null));
16207 memset(repl_repl, false, sizeof(repl_repl));
16208
16209 repl_repl[Anum_pg_class_relowner - 1] = true;
16210 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16211
16212 /*
16213 * Determine the modified ACL for the new owner. This is only
16214 * necessary when the ACL is non-null.
16215 */
16216 aclDatum = SysCacheGetAttr(RELOID, tuple,
16217 Anum_pg_class_relacl,
16218 &isNull);
16219 if (!isNull)
16220 {
16221 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16222 tuple_class->relowner, newOwnerId);
16223 repl_repl[Anum_pg_class_relacl - 1] = true;
16224 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16225 }
16226
16227 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16228
16229 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16230
16231 heap_freetuple(newtuple);
16232
16233 /*
16234 * We must similarly update any per-column ACLs to reflect the new
16235 * owner; for neatness reasons that's split out as a subroutine.
16236 */
16237 change_owner_fix_column_acls(relationOid,
16238 tuple_class->relowner,
16239 newOwnerId);
16240
16241 /*
16242 * Update owner dependency reference, if any. A composite type has
16243 * none, because it's tracked for the pg_type entry instead of here;
16244 * indexes and TOAST tables don't have their own entries either.
16245 */
16246 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16247 tuple_class->relkind != RELKIND_INDEX &&
16248 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16249 tuple_class->relkind != RELKIND_TOASTVALUE)
16250 changeDependencyOnOwner(RelationRelationId, relationOid,
16251 newOwnerId);
16252
16253 /*
16254 * Also change the ownership of the table's row type, if it has one
16255 */
16256 if (OidIsValid(tuple_class->reltype))
16257 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16258
16259 /*
16260 * If we are operating on a table or materialized view, also change
16261 * the ownership of any indexes and sequences that belong to the
16262 * relation, as well as its toast table (if it has one).
16263 */
16264 if (tuple_class->relkind == RELKIND_RELATION ||
16265 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16266 tuple_class->relkind == RELKIND_MATVIEW ||
16267 tuple_class->relkind == RELKIND_TOASTVALUE)
16268 {
16269 List *index_oid_list;
16270 ListCell *i;
16271
16272 /* Find all the indexes belonging to this relation */
16273 index_oid_list = RelationGetIndexList(target_rel);
16274
16275 /* For each index, recursively change its ownership */
16276 foreach(i, index_oid_list)
16277 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16278
16279 list_free(index_oid_list);
16280 }
16281
16282 /* If it has a toast table, recurse to change its ownership */
16283 if (tuple_class->reltoastrelid != InvalidOid)
16284 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16285 true, lockmode);
16286
16287 /* If it has dependent sequences, recurse to change them too */
16288 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16289 }
16290
16291 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16292
16293 ReleaseSysCache(tuple);
16294 table_close(class_rel, RowExclusiveLock);
16295 relation_close(target_rel, NoLock);
16296}
16297
16298/*
16299 * change_owner_fix_column_acls
16300 *
16301 * Helper function for ATExecChangeOwner. Scan the columns of the table
16302 * and fix any non-null column ACLs to reflect the new owner.
16303 */
16304static void
16305change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16306{
16307 Relation attRelation;
16308 SysScanDesc scan;
16309 ScanKeyData key[1];
16310 HeapTuple attributeTuple;
16311
16312 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16313 ScanKeyInit(&key[0],
16314 Anum_pg_attribute_attrelid,
16315 BTEqualStrategyNumber, F_OIDEQ,
16316 ObjectIdGetDatum(relationOid));
16317 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16318 true, NULL, 1, key);
16319 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16320 {
16321 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16322 Datum repl_val[Natts_pg_attribute];
16323 bool repl_null[Natts_pg_attribute];
16324 bool repl_repl[Natts_pg_attribute];
16325 Acl *newAcl;
16326 Datum aclDatum;
16327 bool isNull;
16328 HeapTuple newtuple;
16329
16330 /* Ignore dropped columns */
16331 if (att->attisdropped)
16332 continue;
16333
16334 aclDatum = heap_getattr(attributeTuple,
16335 Anum_pg_attribute_attacl,
16336 RelationGetDescr(attRelation),
16337 &isNull);
16338 /* Null ACLs do not require changes */
16339 if (isNull)
16340 continue;
16341
16342 memset(repl_null, false, sizeof(repl_null));
16343 memset(repl_repl, false, sizeof(repl_repl));
16344
16345 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16346 oldOwnerId, newOwnerId);
16347 repl_repl[Anum_pg_attribute_attacl - 1] = true;
16348 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16349
16350 newtuple = heap_modify_tuple(attributeTuple,
16351 RelationGetDescr(attRelation),
16352 repl_val, repl_null, repl_repl);
16353
16354 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16355
16356 heap_freetuple(newtuple);
16357 }
16358 systable_endscan(scan);
16359 table_close(attRelation, RowExclusiveLock);
16360}
16361
16362/*
16363 * change_owner_recurse_to_sequences
16364 *
16365 * Helper function for ATExecChangeOwner. Examines pg_depend searching
16366 * for sequences that are dependent on serial columns, and changes their
16367 * ownership.
16368 */
16369static void
16370change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16371{
16372 Relation depRel;
16373 SysScanDesc scan;
16374 ScanKeyData key[2];
16375 HeapTuple tup;
16376
16377 /*
16378 * SERIAL sequences are those having an auto dependency on one of the
16379 * table's columns (we don't care *which* column, exactly).
16380 */
16381 depRel = table_open(DependRelationId, AccessShareLock);
16382
16383 ScanKeyInit(&key[0],
16384 Anum_pg_depend_refclassid,
16385 BTEqualStrategyNumber, F_OIDEQ,
16386 ObjectIdGetDatum(RelationRelationId));
16387 ScanKeyInit(&key[1],
16388 Anum_pg_depend_refobjid,
16389 BTEqualStrategyNumber, F_OIDEQ,
16390 ObjectIdGetDatum(relationOid));
16391 /* we leave refobjsubid unspecified */
16392
16393 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16394 NULL, 2, key);
16395
16396 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16397 {
16398 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16399 Relation seqRel;
16400
16401 /* skip dependencies other than auto dependencies on columns */
16402 if (depForm->refobjsubid == 0 ||
16403 depForm->classid != RelationRelationId ||
16404 depForm->objsubid != 0 ||
16405 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16406 continue;
16407
16408 /* Use relation_open just in case it's an index */
16409 seqRel = relation_open(depForm->objid, lockmode);
16410
16411 /* skip non-sequence relations */
16412 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16413 {
16414 /* No need to keep the lock */
16415 relation_close(seqRel, lockmode);
16416 continue;
16417 }
16418
16419 /* We don't need to close the sequence while we alter it. */
16420 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16421
16422 /* Now we can close it. Keep the lock till end of transaction. */
16423 relation_close(seqRel, NoLock);
16424 }
16425
16426 systable_endscan(scan);
16427
16429}
16430
16431/*
16432 * ALTER TABLE CLUSTER ON
16433 *
16434 * The only thing we have to do is to change the indisclustered bits.
16435 *
16436 * Return the address of the new clustering index.
16437 */
16438static ObjectAddress
16439ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16440{
16441 Oid indexOid;
16442 ObjectAddress address;
16443
16444 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16445
16446 if (!OidIsValid(indexOid))
16447 ereport(ERROR,
16448 (errcode(ERRCODE_UNDEFINED_OBJECT),
16449 errmsg("index \"%s\" for table \"%s\" does not exist",
16450 indexName, RelationGetRelationName(rel))));
16451
16452 /* Check index is valid to cluster on */
16453 check_index_is_clusterable(rel, indexOid, lockmode);
16454
16455 /* And do the work */
16456 mark_index_clustered(rel, indexOid, false);
16457
16458 ObjectAddressSet(address,
16459 RelationRelationId, indexOid);
16460
16461 return address;
16462}
16463
16464/*
16465 * ALTER TABLE SET WITHOUT CLUSTER
16466 *
16467 * We have to find any indexes on the table that have indisclustered bit
16468 * set and turn it off.
16469 */
16470static void
16472{
16473 mark_index_clustered(rel, InvalidOid, false);
16474}
16475
16476/*
16477 * Preparation phase for SET ACCESS METHOD
16478 *
16479 * Check that the access method exists and determine whether a change is
16480 * actually needed.
16481 */
16482static void
16484{
16485 Oid amoid;
16486
16487 /*
16488 * Look up the access method name and check that it differs from the
16489 * table's current AM. If DEFAULT was specified for a partitioned table
16490 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16491 */
16492 if (amname != NULL)
16493 amoid = get_table_am_oid(amname, false);
16494 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16495 amoid = InvalidOid;
16496 else
16498
16499 /* if it's a match, phase 3 doesn't need to do anything */
16500 if (rel->rd_rel->relam == amoid)
16501 return;
16502
16503 /* Save info for Phase 3 to do the real work */
16505 tab->newAccessMethod = amoid;
16506 tab->chgAccessMethod = true;
16507}
16508
16509/*
16510 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16511 * storage that have an interest in preserving AM.
16512 *
16513 * Since these have no storage, setting the access method is a catalog only
16514 * operation.
16515 */
16516static void
16518{
16519 Relation pg_class;
16520 Oid oldAccessMethodId;
16521 HeapTuple tuple;
16522 Form_pg_class rd_rel;
16523 Oid reloid = RelationGetRelid(rel);
16524
16525 /*
16526 * Shouldn't be called on relations having storage; these are processed in
16527 * phase 3.
16528 */
16529 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16530
16531 /* Get a modifiable copy of the relation's pg_class row. */
16532 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16533
16534 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16535 if (!HeapTupleIsValid(tuple))
16536 elog(ERROR, "cache lookup failed for relation %u", reloid);
16537 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16538
16539 /* Update the pg_class row. */
16540 oldAccessMethodId = rd_rel->relam;
16541 rd_rel->relam = newAccessMethodId;
16542
16543 /* Leave if no update required */
16544 if (rd_rel->relam == oldAccessMethodId)
16545 {
16546 heap_freetuple(tuple);
16547 table_close(pg_class, RowExclusiveLock);
16548 return;
16549 }
16550
16551 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16552
16553 /*
16554 * Update the dependency on the new access method. No dependency is added
16555 * if the new access method is InvalidOid (default case). Be very careful
16556 * that this has to compare the previous value stored in pg_class with the
16557 * new one.
16558 */
16559 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16560 {
16561 ObjectAddress relobj,
16562 referenced;
16563
16564 /*
16565 * New access method is defined and there was no dependency
16566 * previously, so record a new one.
16567 */
16568 ObjectAddressSet(relobj, RelationRelationId, reloid);
16569 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16570 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16571 }
16572 else if (OidIsValid(oldAccessMethodId) &&
16573 !OidIsValid(rd_rel->relam))
16574 {
16575 /*
16576 * There was an access method defined, and no new one, so just remove
16577 * the existing dependency.
16578 */
16579 deleteDependencyRecordsForClass(RelationRelationId, reloid,
16580 AccessMethodRelationId,
16582 }
16583 else
16584 {
16585 Assert(OidIsValid(oldAccessMethodId) &&
16586 OidIsValid(rd_rel->relam));
16587
16588 /* Both are valid, so update the dependency */
16589 changeDependencyFor(RelationRelationId, reloid,
16590 AccessMethodRelationId,
16591 oldAccessMethodId, rd_rel->relam);
16592 }
16593
16594 /* make the relam and dependency changes visible */
16596
16597 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16598
16599 heap_freetuple(tuple);
16600 table_close(pg_class, RowExclusiveLock);
16601}
16602
16603/*
16604 * ALTER TABLE SET TABLESPACE
16605 */
16606static void
16607ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16608{
16609 Oid tablespaceId;
16610
16611 /* Check that the tablespace exists */
16612 tablespaceId = get_tablespace_oid(tablespacename, false);
16613
16614 /* Check permissions except when moving to database's default */
16615 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16616 {
16617 AclResult aclresult;
16618
16619 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16620 if (aclresult != ACLCHECK_OK)
16621 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16622 }
16623
16624 /* Save info for Phase 3 to do the real work */
16625 if (OidIsValid(tab->newTableSpace))
16626 ereport(ERROR,
16627 (errcode(ERRCODE_SYNTAX_ERROR),
16628 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16629
16630 tab->newTableSpace = tablespaceId;
16631}
16632
16633/*
16634 * Set, reset, or replace reloptions.
16635 */
16636static void
16638 LOCKMODE lockmode)
16639{
16640 Oid relid;
16641 Relation pgclass;
16642 HeapTuple tuple;
16643 HeapTuple newtuple;
16644 Datum datum;
16645 Datum newOptions;
16646 Datum repl_val[Natts_pg_class];
16647 bool repl_null[Natts_pg_class];
16648 bool repl_repl[Natts_pg_class];
16649 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16650
16651 if (defList == NIL && operation != AT_ReplaceRelOptions)
16652 return; /* nothing to do */
16653
16654 pgclass = table_open(RelationRelationId, RowExclusiveLock);
16655
16656 /* Fetch heap tuple */
16657 relid = RelationGetRelid(rel);
16658 tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16659 if (!HeapTupleIsValid(tuple))
16660 elog(ERROR, "cache lookup failed for relation %u", relid);
16661
16662 if (operation == AT_ReplaceRelOptions)
16663 {
16664 /*
16665 * If we're supposed to replace the reloptions list, we just pretend
16666 * there were none before.
16667 */
16668 datum = (Datum) 0;
16669 }
16670 else
16671 {
16672 bool isnull;
16673
16674 /* Get the old reloptions */
16675 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16676 &isnull);
16677 if (isnull)
16678 datum = (Datum) 0;
16679 }
16680
16681 /* Generate new proposed reloptions (text array) */
16682 newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16683 operation == AT_ResetRelOptions);
16684
16685 /* Validate */
16686 switch (rel->rd_rel->relkind)
16687 {
16688 case RELKIND_RELATION:
16689 case RELKIND_MATVIEW:
16690 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16691 break;
16692 case RELKIND_PARTITIONED_TABLE:
16693 (void) partitioned_table_reloptions(newOptions, true);
16694 break;
16695 case RELKIND_VIEW:
16696 (void) view_reloptions(newOptions, true);
16697 break;
16698 case RELKIND_INDEX:
16699 case RELKIND_PARTITIONED_INDEX:
16700 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16701 break;
16702 case RELKIND_TOASTVALUE:
16703 /* fall through to error -- shouldn't ever get here */
16704 default:
16705 ereport(ERROR,
16706 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16707 errmsg("cannot set options for relation \"%s\"",
16709 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16710 break;
16711 }
16712
16713 /* Special-case validation of view options */
16714 if (rel->rd_rel->relkind == RELKIND_VIEW)
16715 {
16716 Query *view_query = get_view_query(rel);
16717 List *view_options = untransformRelOptions(newOptions);
16718 ListCell *cell;
16719 bool check_option = false;
16720
16721 foreach(cell, view_options)
16722 {
16723 DefElem *defel = (DefElem *) lfirst(cell);
16724
16725 if (strcmp(defel->defname, "check_option") == 0)
16726 check_option = true;
16727 }
16728
16729 /*
16730 * If the check option is specified, look to see if the view is
16731 * actually auto-updatable or not.
16732 */
16733 if (check_option)
16734 {
16735 const char *view_updatable_error =
16736 view_query_is_auto_updatable(view_query, true);
16737
16738 if (view_updatable_error)
16739 ereport(ERROR,
16740 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16741 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16742 errhint("%s", _(view_updatable_error))));
16743 }
16744 }
16745
16746 /*
16747 * All we need do here is update the pg_class row; the new options will be
16748 * propagated into relcaches during post-commit cache inval.
16749 */
16750 memset(repl_val, 0, sizeof(repl_val));
16751 memset(repl_null, false, sizeof(repl_null));
16752 memset(repl_repl, false, sizeof(repl_repl));
16753
16754 if (newOptions != (Datum) 0)
16755 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16756 else
16757 repl_null[Anum_pg_class_reloptions - 1] = true;
16758
16759 repl_repl[Anum_pg_class_reloptions - 1] = true;
16760
16761 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16762 repl_val, repl_null, repl_repl);
16763
16764 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16765 UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16766
16767 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16768
16769 heap_freetuple(newtuple);
16770
16771 ReleaseSysCache(tuple);
16772
16773 /* repeat the whole exercise for the toast table, if there's one */
16774 if (OidIsValid(rel->rd_rel->reltoastrelid))
16775 {
16776 Relation toastrel;
16777 Oid toastid = rel->rd_rel->reltoastrelid;
16778
16779 toastrel = table_open(toastid, lockmode);
16780
16781 /* Fetch heap tuple */
16782 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16783 if (!HeapTupleIsValid(tuple))
16784 elog(ERROR, "cache lookup failed for relation %u", toastid);
16785
16786 if (operation == AT_ReplaceRelOptions)
16787 {
16788 /*
16789 * If we're supposed to replace the reloptions list, we just
16790 * pretend there were none before.
16791 */
16792 datum = (Datum) 0;
16793 }
16794 else
16795 {
16796 bool isnull;
16797
16798 /* Get the old reloptions */
16799 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16800 &isnull);
16801 if (isnull)
16802 datum = (Datum) 0;
16803 }
16804
16805 newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16806 false, operation == AT_ResetRelOptions);
16807
16808 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16809
16810 memset(repl_val, 0, sizeof(repl_val));
16811 memset(repl_null, false, sizeof(repl_null));
16812 memset(repl_repl, false, sizeof(repl_repl));
16813
16814 if (newOptions != (Datum) 0)
16815 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16816 else
16817 repl_null[Anum_pg_class_reloptions - 1] = true;
16818
16819 repl_repl[Anum_pg_class_reloptions - 1] = true;
16820
16821 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16822 repl_val, repl_null, repl_repl);
16823
16824 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16825
16826 InvokeObjectPostAlterHookArg(RelationRelationId,
16827 RelationGetRelid(toastrel), 0,
16828 InvalidOid, true);
16829
16830 heap_freetuple(newtuple);
16831
16832 ReleaseSysCache(tuple);
16833
16834 table_close(toastrel, NoLock);
16835 }
16836
16837 table_close(pgclass, RowExclusiveLock);
16838}
16839
16840/*
16841 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16842 * rewriting to be done, so we just want to copy the data as fast as possible.
16843 */
16844static void
16845ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16846{
16847 Relation rel;
16848 Oid reltoastrelid;
16849 RelFileNumber newrelfilenumber;
16850 RelFileLocator newrlocator;
16851 List *reltoastidxids = NIL;
16852 ListCell *lc;
16853
16854 /*
16855 * Need lock here in case we are recursing to toast table or index
16856 */
16857 rel = relation_open(tableOid, lockmode);
16858
16859 /* Check first if relation can be moved to new tablespace */
16860 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16861 {
16862 InvokeObjectPostAlterHook(RelationRelationId,
16863 RelationGetRelid(rel), 0);
16864 relation_close(rel, NoLock);
16865 return;
16866 }
16867
16868 reltoastrelid = rel->rd_rel->reltoastrelid;
16869 /* Fetch the list of indexes on toast relation if necessary */
16870 if (OidIsValid(reltoastrelid))
16871 {
16872 Relation toastRel = relation_open(reltoastrelid, lockmode);
16873
16874 reltoastidxids = RelationGetIndexList(toastRel);
16875 relation_close(toastRel, lockmode);
16876 }
16877
16878 /*
16879 * Relfilenumbers are not unique in databases across tablespaces, so we
16880 * need to allocate a new one in the new tablespace.
16881 */
16882 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16883 rel->rd_rel->relpersistence);
16884
16885 /* Open old and new relation */
16886 newrlocator = rel->rd_locator;
16887 newrlocator.relNumber = newrelfilenumber;
16888 newrlocator.spcOid = newTableSpace;
16889
16890 /* hand off to AM to actually create new rel storage and copy the data */
16891 if (rel->rd_rel->relkind == RELKIND_INDEX)
16892 {
16893 index_copy_data(rel, newrlocator);
16894 }
16895 else
16896 {
16897 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16898 table_relation_copy_data(rel, &newrlocator);
16899 }
16900
16901 /*
16902 * Update the pg_class row.
16903 *
16904 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16905 * executed on pg_class or its indexes (the above copy wouldn't contain
16906 * the updated pg_class entry), but that's forbidden with
16907 * CheckRelationTableSpaceMove().
16908 */
16909 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16910
16911 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16912
16914
16915 relation_close(rel, NoLock);
16916
16917 /* Make sure the reltablespace change is visible */
16919
16920 /* Move associated toast relation and/or indexes, too */
16921 if (OidIsValid(reltoastrelid))
16922 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16923 foreach(lc, reltoastidxids)
16924 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16925
16926 /* Clean up */
16927 list_free(reltoastidxids);
16928}
16929
16930/*
16931 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16932 * storage that have an interest in preserving tablespace.
16933 *
16934 * Since these have no storage the tablespace can be updated with a simple
16935 * metadata only operation to update the tablespace.
16936 */
16937static void
16939{
16940 /*
16941 * Shouldn't be called on relations having storage; these are processed in
16942 * phase 3.
16943 */
16944 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16945
16946 /* check if relation can be moved to its new tablespace */
16947 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16948 {
16949 InvokeObjectPostAlterHook(RelationRelationId,
16950 RelationGetRelid(rel),
16951 0);
16952 return;
16953 }
16954
16955 /* Update can be done, so change reltablespace */
16956 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16957
16958 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16959
16960 /* Make sure the reltablespace change is visible */
16962}
16963
16964/*
16965 * Alter Table ALL ... SET TABLESPACE
16966 *
16967 * Allows a user to move all objects of some type in a given tablespace in the
16968 * current database to another tablespace. Objects can be chosen based on the
16969 * owner of the object also, to allow users to move only their objects.
16970 * The user must have CREATE rights on the new tablespace, as usual. The main
16971 * permissions handling is done by the lower-level table move function.
16972 *
16973 * All to-be-moved objects are locked first. If NOWAIT is specified and the
16974 * lock can't be acquired then we ereport(ERROR).
16975 */
16976Oid
16978{
16979 List *relations = NIL;
16980 ListCell *l;
16981 ScanKeyData key[1];
16982 Relation rel;
16983 TableScanDesc scan;
16984 HeapTuple tuple;
16985 Oid orig_tablespaceoid;
16986 Oid new_tablespaceoid;
16987 List *role_oids = roleSpecsToIds(stmt->roles);
16988
16989 /* Ensure we were not asked to move something we can't */
16990 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16991 stmt->objtype != OBJECT_MATVIEW)
16992 ereport(ERROR,
16993 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16994 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16995
16996 /* Get the orig and new tablespace OIDs */
16997 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16998 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16999
17000 /* Can't move shared relations in to or out of pg_global */
17001 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17002 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
17003 new_tablespaceoid == GLOBALTABLESPACE_OID)
17004 ereport(ERROR,
17005 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17006 errmsg("cannot move relations in to or out of pg_global tablespace")));
17007
17008 /*
17009 * Must have CREATE rights on the new tablespace, unless it is the
17010 * database default tablespace (which all users implicitly have CREATE
17011 * rights on).
17012 */
17013 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
17014 {
17015 AclResult aclresult;
17016
17017 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
17018 ACL_CREATE);
17019 if (aclresult != ACLCHECK_OK)
17021 get_tablespace_name(new_tablespaceoid));
17022 }
17023
17024 /*
17025 * Now that the checks are done, check if we should set either to
17026 * InvalidOid because it is our database's default tablespace.
17027 */
17028 if (orig_tablespaceoid == MyDatabaseTableSpace)
17029 orig_tablespaceoid = InvalidOid;
17030
17031 if (new_tablespaceoid == MyDatabaseTableSpace)
17032 new_tablespaceoid = InvalidOid;
17033
17034 /* no-op */
17035 if (orig_tablespaceoid == new_tablespaceoid)
17036 return new_tablespaceoid;
17037
17038 /*
17039 * Walk the list of objects in the tablespace and move them. This will
17040 * only find objects in our database, of course.
17041 */
17042 ScanKeyInit(&key[0],
17043 Anum_pg_class_reltablespace,
17044 BTEqualStrategyNumber, F_OIDEQ,
17045 ObjectIdGetDatum(orig_tablespaceoid));
17046
17047 rel = table_open(RelationRelationId, AccessShareLock);
17048 scan = table_beginscan_catalog(rel, 1, key);
17049 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17050 {
17051 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17052 Oid relOid = relForm->oid;
17053
17054 /*
17055 * Do not move objects in pg_catalog as part of this, if an admin
17056 * really wishes to do so, they can issue the individual ALTER
17057 * commands directly.
17058 *
17059 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17060 * (TOAST will be moved with the main table).
17061 */
17062 if (IsCatalogNamespace(relForm->relnamespace) ||
17063 relForm->relisshared ||
17064 isAnyTempNamespace(relForm->relnamespace) ||
17065 IsToastNamespace(relForm->relnamespace))
17066 continue;
17067
17068 /* Only move the object type requested */
17069 if ((stmt->objtype == OBJECT_TABLE &&
17070 relForm->relkind != RELKIND_RELATION &&
17071 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17072 (stmt->objtype == OBJECT_INDEX &&
17073 relForm->relkind != RELKIND_INDEX &&
17074 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17075 (stmt->objtype == OBJECT_MATVIEW &&
17076 relForm->relkind != RELKIND_MATVIEW))
17077 continue;
17078
17079 /* Check if we are only moving objects owned by certain roles */
17080 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17081 continue;
17082
17083 /*
17084 * Handle permissions-checking here since we are locking the tables
17085 * and also to avoid doing a bunch of work only to fail part-way. Note
17086 * that permissions will also be checked by AlterTableInternal().
17087 *
17088 * Caller must be considered an owner on the table to move it.
17089 */
17090 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17092 NameStr(relForm->relname));
17093
17094 if (stmt->nowait &&
17096 ereport(ERROR,
17097 (errcode(ERRCODE_OBJECT_IN_USE),
17098 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17099 get_namespace_name(relForm->relnamespace),
17100 NameStr(relForm->relname))));
17101 else
17103
17104 /* Add to our list of objects to move */
17105 relations = lappend_oid(relations, relOid);
17106 }
17107
17108 table_endscan(scan);
17110
17111 if (relations == NIL)
17113 (errcode(ERRCODE_NO_DATA_FOUND),
17114 errmsg("no matching relations in tablespace \"%s\" found",
17115 orig_tablespaceoid == InvalidOid ? "(database default)" :
17116 get_tablespace_name(orig_tablespaceoid))));
17117
17118 /* Everything is locked, loop through and move all of the relations. */
17119 foreach(l, relations)
17120 {
17121 List *cmds = NIL;
17123
17125 cmd->name = stmt->new_tablespacename;
17126
17127 cmds = lappend(cmds, cmd);
17128
17130 /* OID is set by AlterTableInternal */
17131 AlterTableInternal(lfirst_oid(l), cmds, false);
17133 }
17134
17135 return new_tablespaceoid;
17136}
17137
17138static void
17140{
17141 SMgrRelation dstrel;
17142
17143 /*
17144 * Since we copy the file directly without looking at the shared buffers,
17145 * we'd better first flush out any pages of the source relation that are
17146 * in shared buffers. We assume no new changes will be made while we are
17147 * holding exclusive lock on the rel.
17148 */
17150
17151 /*
17152 * Create and copy all forks of the relation, and schedule unlinking of
17153 * old physical files.
17154 *
17155 * NOTE: any conflict in relfilenumber value will be caught in
17156 * RelationCreateStorage().
17157 */
17158 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17159
17160 /* copy main fork */
17162 rel->rd_rel->relpersistence);
17163
17164 /* copy those extra forks that exist */
17165 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17166 forkNum <= MAX_FORKNUM; forkNum++)
17167 {
17168 if (smgrexists(RelationGetSmgr(rel), forkNum))
17169 {
17170 smgrcreate(dstrel, forkNum, false);
17171
17172 /*
17173 * WAL log creation if the relation is persistent, or this is the
17174 * init fork of an unlogged relation.
17175 */
17176 if (RelationIsPermanent(rel) ||
17177 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17178 forkNum == INIT_FORKNUM))
17179 log_smgrcreate(&newrlocator, forkNum);
17180 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17181 rel->rd_rel->relpersistence);
17182 }
17183 }
17184
17185 /* drop old relation, and close new one */
17187 smgrclose(dstrel);
17188}
17189
17190/*
17191 * ALTER TABLE ENABLE/DISABLE TRIGGER
17192 *
17193 * We just pass this off to trigger.c.
17194 */
17195static void
17196ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17197 char fires_when, bool skip_system, bool recurse,
17198 LOCKMODE lockmode)
17199{
17200 EnableDisableTrigger(rel, trigname, InvalidOid,
17201 fires_when, skip_system, recurse,
17202 lockmode);
17203
17204 InvokeObjectPostAlterHook(RelationRelationId,
17205 RelationGetRelid(rel), 0);
17206}
17207
17208/*
17209 * ALTER TABLE ENABLE/DISABLE RULE
17210 *
17211 * We just pass this off to rewriteDefine.c.
17212 */
17213static void
17214ATExecEnableDisableRule(Relation rel, const char *rulename,
17215 char fires_when, LOCKMODE lockmode)
17216{
17217 EnableDisableRule(rel, rulename, fires_when);
17218
17219 InvokeObjectPostAlterHook(RelationRelationId,
17220 RelationGetRelid(rel), 0);
17221}
17222
17223/*
17224 * ALTER TABLE INHERIT
17225 *
17226 * Add a parent to the child's parents. This verifies that all the columns and
17227 * check constraints of the parent appear in the child and that they have the
17228 * same data types and expressions.
17229 */
17230static void
17232{
17233 if (child_rel->rd_rel->reloftype)
17234 ereport(ERROR,
17235 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17236 errmsg("cannot change inheritance of typed table")));
17237
17238 if (child_rel->rd_rel->relispartition)
17239 ereport(ERROR,
17240 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17241 errmsg("cannot change inheritance of a partition")));
17242
17243 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17244 ereport(ERROR,
17245 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17246 errmsg("cannot change inheritance of partitioned table")));
17247}
17248
17249/*
17250 * Return the address of the new parent relation.
17251 */
17252static ObjectAddress
17253ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17254{
17255 Relation parent_rel;
17256 List *children;
17257 ObjectAddress address;
17258 const char *trigger_name;
17259
17260 /*
17261 * A self-exclusive lock is needed here. See the similar case in
17262 * MergeAttributes() for a full explanation.
17263 */
17264 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17265
17266 /*
17267 * Must be owner of both parent and child -- child was checked by
17268 * ATSimplePermissions call in ATPrepCmd
17269 */
17272
17273 /* Permanent rels cannot inherit from temporary ones */
17274 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17275 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17276 ereport(ERROR,
17277 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17278 errmsg("cannot inherit from temporary relation \"%s\"",
17279 RelationGetRelationName(parent_rel))));
17280
17281 /* If parent rel is temp, it must belong to this session */
17282 if (RELATION_IS_OTHER_TEMP(parent_rel))
17283 ereport(ERROR,
17284 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17285 errmsg("cannot inherit from temporary relation of another session")));
17286
17287 /* Ditto for the child */
17288 if (RELATION_IS_OTHER_TEMP(child_rel))
17289 ereport(ERROR,
17290 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17291 errmsg("cannot inherit to temporary relation of another session")));
17292
17293 /* Prevent partitioned tables from becoming inheritance parents */
17294 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17295 ereport(ERROR,
17296 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17297 errmsg("cannot inherit from partitioned table \"%s\"",
17298 parent->relname)));
17299
17300 /* Likewise for partitions */
17301 if (parent_rel->rd_rel->relispartition)
17302 ereport(ERROR,
17303 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17304 errmsg("cannot inherit from a partition")));
17305
17306 /*
17307 * Prevent circularity by seeing if proposed parent inherits from child.
17308 * (In particular, this disallows making a rel inherit from itself.)
17309 *
17310 * This is not completely bulletproof because of race conditions: in
17311 * multi-level inheritance trees, someone else could concurrently be
17312 * making another inheritance link that closes the loop but does not join
17313 * either of the rels we have locked. Preventing that seems to require
17314 * exclusive locks on the entire inheritance tree, which is a cure worse
17315 * than the disease. find_all_inheritors() will cope with circularity
17316 * anyway, so don't sweat it too much.
17317 *
17318 * We use weakest lock we can on child's children, namely AccessShareLock.
17319 */
17320 children = find_all_inheritors(RelationGetRelid(child_rel),
17321 AccessShareLock, NULL);
17322
17323 if (list_member_oid(children, RelationGetRelid(parent_rel)))
17324 ereport(ERROR,
17325 (errcode(ERRCODE_DUPLICATE_TABLE),
17326 errmsg("circular inheritance not allowed"),
17327 errdetail("\"%s\" is already a child of \"%s\".",
17328 parent->relname,
17329 RelationGetRelationName(child_rel))));
17330
17331 /*
17332 * If child_rel has row-level triggers with transition tables, we
17333 * currently don't allow it to become an inheritance child. See also
17334 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17335 */
17336 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17337 if (trigger_name != NULL)
17338 ereport(ERROR,
17339 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17340 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17341 trigger_name, RelationGetRelationName(child_rel)),
17342 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17343
17344 /* OK to create inheritance */
17345 CreateInheritance(child_rel, parent_rel, false);
17346
17347 ObjectAddressSet(address, RelationRelationId,
17348 RelationGetRelid(parent_rel));
17349
17350 /* keep our lock on the parent relation until commit */
17351 table_close(parent_rel, NoLock);
17352
17353 return address;
17354}
17355
17356/*
17357 * CreateInheritance
17358 * Catalog manipulation portion of creating inheritance between a child
17359 * table and a parent table.
17360 *
17361 * Common to ATExecAddInherit() and ATExecAttachPartition().
17362 */
17363static void
17364CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17365{
17366 Relation catalogRelation;
17367 SysScanDesc scan;
17369 HeapTuple inheritsTuple;
17370 int32 inhseqno;
17371
17372 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17373 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17374
17375 /*
17376 * Check for duplicates in the list of parents, and determine the highest
17377 * inhseqno already present; we'll use the next one for the new parent.
17378 * Also, if proposed child is a partition, it cannot already be
17379 * inheriting.
17380 *
17381 * Note: we do not reject the case where the child already inherits from
17382 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17383 */
17385 Anum_pg_inherits_inhrelid,
17386 BTEqualStrategyNumber, F_OIDEQ,
17388 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17389 true, NULL, 1, &key);
17390
17391 /* inhseqno sequences start at 1 */
17392 inhseqno = 0;
17393 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17394 {
17395 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17396
17397 if (inh->inhparent == RelationGetRelid(parent_rel))
17398 ereport(ERROR,
17399 (errcode(ERRCODE_DUPLICATE_TABLE),
17400 errmsg("relation \"%s\" would be inherited from more than once",
17401 RelationGetRelationName(parent_rel))));
17402
17403 if (inh->inhseqno > inhseqno)
17404 inhseqno = inh->inhseqno;
17405 }
17406 systable_endscan(scan);
17407
17408 /* Match up the columns and bump attinhcount as needed */
17409 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17410
17411 /* Match up the constraints and bump coninhcount as needed */
17412 MergeConstraintsIntoExisting(child_rel, parent_rel);
17413
17414 /*
17415 * OK, it looks valid. Make the catalog entries that show inheritance.
17416 */
17418 RelationGetRelid(parent_rel),
17419 inhseqno + 1,
17420 catalogRelation,
17421 parent_rel->rd_rel->relkind ==
17422 RELKIND_PARTITIONED_TABLE);
17423
17424 /* Now we're done with pg_inherits */
17425 table_close(catalogRelation, RowExclusiveLock);
17426}
17427
17428/*
17429 * Obtain the source-text form of the constraint expression for a check
17430 * constraint, given its pg_constraint tuple
17431 */
17432static char *
17434{
17436 bool isnull;
17437 Datum attr;
17438 Datum expr;
17439
17440 con = (Form_pg_constraint) GETSTRUCT(contup);
17441 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17442 if (isnull)
17443 elog(ERROR, "null conbin for constraint %u", con->oid);
17444
17445 expr = DirectFunctionCall2(pg_get_expr, attr,
17446 ObjectIdGetDatum(con->conrelid));
17447 return TextDatumGetCString(expr);
17448}
17449
17450/*
17451 * Determine whether two check constraints are functionally equivalent
17452 *
17453 * The test we apply is to see whether they reverse-compile to the same
17454 * source string. This insulates us from issues like whether attributes
17455 * have the same physical column numbers in parent and child relations.
17456 *
17457 * Note that we ignore enforceability as there are cases where constraints
17458 * with differing enforceability are allowed.
17459 */
17460static bool
17462{
17465
17466 if (acon->condeferrable != bcon->condeferrable ||
17467 acon->condeferred != bcon->condeferred ||
17468 strcmp(decompile_conbin(a, tupleDesc),
17469 decompile_conbin(b, tupleDesc)) != 0)
17470 return false;
17471 else
17472 return true;
17473}
17474
17475/*
17476 * Check columns in child table match up with columns in parent, and increment
17477 * their attinhcount.
17478 *
17479 * Called by CreateInheritance
17480 *
17481 * Currently all parent columns must be found in child. Missing columns are an
17482 * error. One day we might consider creating new columns like CREATE TABLE
17483 * does. However, that is widely unpopular --- in the common use case of
17484 * partitioned tables it's a foot-gun.
17485 *
17486 * The data type must match exactly. If the parent column is NOT NULL then
17487 * the child must be as well. Defaults are not compared, however.
17488 */
17489static void
17490MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17491{
17492 Relation attrrel;
17493 TupleDesc parent_desc;
17494
17495 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17496 parent_desc = RelationGetDescr(parent_rel);
17497
17498 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17499 {
17500 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17501 char *parent_attname = NameStr(parent_att->attname);
17502 HeapTuple tuple;
17503
17504 /* Ignore dropped columns in the parent. */
17505 if (parent_att->attisdropped)
17506 continue;
17507
17508 /* Find same column in child (matching on column name). */
17509 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17510 if (HeapTupleIsValid(tuple))
17511 {
17512 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17513
17514 if (parent_att->atttypid != child_att->atttypid ||
17515 parent_att->atttypmod != child_att->atttypmod)
17516 ereport(ERROR,
17517 (errcode(ERRCODE_DATATYPE_MISMATCH),
17518 errmsg("child table \"%s\" has different type for column \"%s\"",
17519 RelationGetRelationName(child_rel), parent_attname)));
17520
17521 if (parent_att->attcollation != child_att->attcollation)
17522 ereport(ERROR,
17523 (errcode(ERRCODE_COLLATION_MISMATCH),
17524 errmsg("child table \"%s\" has different collation for column \"%s\"",
17525 RelationGetRelationName(child_rel), parent_attname)));
17526
17527 /*
17528 * If the parent has a not-null constraint that's not NO INHERIT,
17529 * make sure the child has one too.
17530 *
17531 * Other constraints are checked elsewhere.
17532 */
17533 if (parent_att->attnotnull && !child_att->attnotnull)
17534 {
17535 HeapTuple contup;
17536
17537 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17538 parent_att->attnum);
17539 if (HeapTupleIsValid(contup) &&
17540 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17541 ereport(ERROR,
17542 errcode(ERRCODE_DATATYPE_MISMATCH),
17543 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17544 parent_attname, RelationGetRelationName(child_rel)));
17545 }
17546
17547 /*
17548 * Child column must be generated if and only if parent column is.
17549 */
17550 if (parent_att->attgenerated && !child_att->attgenerated)
17551 ereport(ERROR,
17552 (errcode(ERRCODE_DATATYPE_MISMATCH),
17553 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17554 if (child_att->attgenerated && !parent_att->attgenerated)
17555 ereport(ERROR,
17556 (errcode(ERRCODE_DATATYPE_MISMATCH),
17557 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17558
17559 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17560 ereport(ERROR,
17561 (errcode(ERRCODE_DATATYPE_MISMATCH),
17562 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17563 errdetail("Parent column is %s, child column is %s.",
17564 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17565 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17566
17567 /*
17568 * Regular inheritance children are independent enough not to
17569 * inherit identity columns. But partitions are integral part of
17570 * a partitioned table and inherit identity column.
17571 */
17572 if (ispartition)
17573 child_att->attidentity = parent_att->attidentity;
17574
17575 /*
17576 * OK, bump the child column's inheritance count. (If we fail
17577 * later on, this change will just roll back.)
17578 */
17579 if (pg_add_s16_overflow(child_att->attinhcount, 1,
17580 &child_att->attinhcount))
17581 ereport(ERROR,
17582 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17583 errmsg("too many inheritance parents"));
17584
17585 /*
17586 * In case of partitions, we must enforce that value of attislocal
17587 * is same in all partitions. (Note: there are only inherited
17588 * attributes in partitions)
17589 */
17590 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17591 {
17592 Assert(child_att->attinhcount == 1);
17593 child_att->attislocal = false;
17594 }
17595
17596 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17597 heap_freetuple(tuple);
17598 }
17599 else
17600 {
17601 ereport(ERROR,
17602 (errcode(ERRCODE_DATATYPE_MISMATCH),
17603 errmsg("child table is missing column \"%s\"", parent_attname)));
17604 }
17605 }
17606
17607 table_close(attrrel, RowExclusiveLock);
17608}
17609
17610/*
17611 * Check constraints in child table match up with constraints in parent,
17612 * and increment their coninhcount.
17613 *
17614 * Constraints that are marked ONLY in the parent are ignored.
17615 *
17616 * Called by CreateInheritance
17617 *
17618 * Currently all constraints in parent must be present in the child. One day we
17619 * may consider adding new constraints like CREATE TABLE does.
17620 *
17621 * XXX This is O(N^2) which may be an issue with tables with hundreds of
17622 * constraints. As long as tables have more like 10 constraints it shouldn't be
17623 * a problem though. Even 100 constraints ought not be the end of the world.
17624 *
17625 * XXX See MergeWithExistingConstraint too if you change this code.
17626 */
17627static void
17629{
17630 Relation constraintrel;
17631 SysScanDesc parent_scan;
17632 ScanKeyData parent_key;
17633 HeapTuple parent_tuple;
17634 Oid parent_relid = RelationGetRelid(parent_rel);
17635 AttrMap *attmap;
17636
17637 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17638
17639 /* Outer loop scans through the parent's constraint definitions */
17640 ScanKeyInit(&parent_key,
17641 Anum_pg_constraint_conrelid,
17642 BTEqualStrategyNumber, F_OIDEQ,
17643 ObjectIdGetDatum(parent_relid));
17644 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17645 true, NULL, 1, &parent_key);
17646
17647 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17648 RelationGetDescr(child_rel),
17649 true);
17650
17651 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17652 {
17653 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17654 SysScanDesc child_scan;
17655 ScanKeyData child_key;
17656 HeapTuple child_tuple;
17657 AttrNumber parent_attno;
17658 bool found = false;
17659
17660 if (parent_con->contype != CONSTRAINT_CHECK &&
17661 parent_con->contype != CONSTRAINT_NOTNULL)
17662 continue;
17663
17664 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17665 if (parent_con->connoinherit)
17666 continue;
17667
17668 if (parent_con->contype == CONSTRAINT_NOTNULL)
17669 parent_attno = extractNotNullColumn(parent_tuple);
17670 else
17671 parent_attno = InvalidAttrNumber;
17672
17673 /* Search for a child constraint matching this one */
17674 ScanKeyInit(&child_key,
17675 Anum_pg_constraint_conrelid,
17676 BTEqualStrategyNumber, F_OIDEQ,
17678 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17679 true, NULL, 1, &child_key);
17680
17681 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17682 {
17683 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17684 HeapTuple child_copy;
17685
17686 if (child_con->contype != parent_con->contype)
17687 continue;
17688
17689 /*
17690 * CHECK constraint are matched by constraint name, NOT NULL ones
17691 * by attribute number.
17692 */
17693 if (child_con->contype == CONSTRAINT_CHECK)
17694 {
17695 if (strcmp(NameStr(parent_con->conname),
17696 NameStr(child_con->conname)) != 0)
17697 continue;
17698 }
17699 else if (child_con->contype == CONSTRAINT_NOTNULL)
17700 {
17701 Form_pg_attribute parent_attr;
17702 Form_pg_attribute child_attr;
17703 AttrNumber child_attno;
17704
17705 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17706 child_attno = extractNotNullColumn(child_tuple);
17707 if (parent_attno != attmap->attnums[child_attno - 1])
17708 continue;
17709
17710 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17711 /* there shouldn't be constraints on dropped columns */
17712 if (parent_attr->attisdropped || child_attr->attisdropped)
17713 elog(ERROR, "found not-null constraint on dropped columns");
17714 }
17715
17716 if (child_con->contype == CONSTRAINT_CHECK &&
17717 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17718 ereport(ERROR,
17719 (errcode(ERRCODE_DATATYPE_MISMATCH),
17720 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17721 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17722
17723 /*
17724 * If the child constraint is "no inherit" then cannot merge
17725 */
17726 if (child_con->connoinherit)
17727 ereport(ERROR,
17728 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17729 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17730 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17731
17732 /*
17733 * If the child constraint is "not valid" then cannot merge with a
17734 * valid parent constraint
17735 */
17736 if (parent_con->convalidated && child_con->conenforced &&
17737 !child_con->convalidated)
17738 ereport(ERROR,
17739 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17740 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17741 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17742
17743 /*
17744 * A NOT ENFORCED child constraint cannot be merged with an
17745 * ENFORCED parent constraint. However, the reverse is allowed,
17746 * where the child constraint is ENFORCED.
17747 */
17748 if (parent_con->conenforced && !child_con->conenforced)
17749 ereport(ERROR,
17750 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17751 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17752 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17753
17754 /*
17755 * OK, bump the child constraint's inheritance count. (If we fail
17756 * later on, this change will just roll back.)
17757 */
17758 child_copy = heap_copytuple(child_tuple);
17759 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17760
17761 if (pg_add_s16_overflow(child_con->coninhcount, 1,
17762 &child_con->coninhcount))
17763 ereport(ERROR,
17764 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17765 errmsg("too many inheritance parents"));
17766
17767 /*
17768 * In case of partitions, an inherited constraint must be
17769 * inherited only once since it cannot have multiple parents and
17770 * it is never considered local.
17771 */
17772 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17773 {
17774 Assert(child_con->coninhcount == 1);
17775 child_con->conislocal = false;
17776 }
17777
17778 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17779 heap_freetuple(child_copy);
17780
17781 found = true;
17782 break;
17783 }
17784
17785 systable_endscan(child_scan);
17786
17787 if (!found)
17788 {
17789 if (parent_con->contype == CONSTRAINT_NOTNULL)
17790 ereport(ERROR,
17791 errcode(ERRCODE_DATATYPE_MISMATCH),
17792 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17793 get_attname(parent_relid,
17794 extractNotNullColumn(parent_tuple),
17795 false),
17796 RelationGetRelationName(child_rel)));
17797
17798 ereport(ERROR,
17799 (errcode(ERRCODE_DATATYPE_MISMATCH),
17800 errmsg("child table is missing constraint \"%s\"",
17801 NameStr(parent_con->conname))));
17802 }
17803 }
17804
17805 systable_endscan(parent_scan);
17806 table_close(constraintrel, RowExclusiveLock);
17807}
17808
17809/*
17810 * ALTER TABLE NO INHERIT
17811 *
17812 * Return value is the address of the relation that is no longer parent.
17813 */
17814static ObjectAddress
17816{
17817 ObjectAddress address;
17818 Relation parent_rel;
17819
17820 if (rel->rd_rel->relispartition)
17821 ereport(ERROR,
17822 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17823 errmsg("cannot change inheritance of a partition")));
17824
17825 /*
17826 * AccessShareLock on the parent is probably enough, seeing that DROP
17827 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17828 * be inspecting the parent's schema.
17829 */
17830 parent_rel = table_openrv(parent, AccessShareLock);
17831
17832 /*
17833 * We don't bother to check ownership of the parent table --- ownership of
17834 * the child is presumed enough rights.
17835 */
17836
17837 /* Off to RemoveInheritance() where most of the work happens */
17838 RemoveInheritance(rel, parent_rel, false);
17839
17840 ObjectAddressSet(address, RelationRelationId,
17841 RelationGetRelid(parent_rel));
17842
17843 /* keep our lock on the parent relation until commit */
17844 table_close(parent_rel, NoLock);
17845
17846 return address;
17847}
17848
17849/*
17850 * MarkInheritDetached
17851 *
17852 * Set inhdetachpending for a partition, for ATExecDetachPartition
17853 * in concurrent mode. While at it, verify that no other partition is
17854 * already pending detach.
17855 */
17856static void
17858{
17859 Relation catalogRelation;
17860 SysScanDesc scan;
17862 HeapTuple inheritsTuple;
17863 bool found = false;
17864
17865 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17866
17867 /*
17868 * Find pg_inherits entries by inhparent. (We need to scan them all in
17869 * order to verify that no other partition is pending detach.)
17870 */
17871 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17873 Anum_pg_inherits_inhparent,
17874 BTEqualStrategyNumber, F_OIDEQ,
17875 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17876 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17877 true, NULL, 1, &key);
17878
17879 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17880 {
17881 Form_pg_inherits inhForm;
17882
17883 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17884 if (inhForm->inhdetachpending)
17885 ereport(ERROR,
17886 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17887 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17888 get_rel_name(inhForm->inhrelid),
17889 get_namespace_name(parent_rel->rd_rel->relnamespace),
17890 RelationGetRelationName(parent_rel)),
17891 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17892
17893 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17894 {
17895 HeapTuple newtup;
17896
17897 newtup = heap_copytuple(inheritsTuple);
17898 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17899
17900 CatalogTupleUpdate(catalogRelation,
17901 &inheritsTuple->t_self,
17902 newtup);
17903 found = true;
17904 heap_freetuple(newtup);
17905 /* keep looking, to ensure we catch others pending detach */
17906 }
17907 }
17908
17909 /* Done */
17910 systable_endscan(scan);
17911 table_close(catalogRelation, RowExclusiveLock);
17912
17913 if (!found)
17914 ereport(ERROR,
17916 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17917 RelationGetRelationName(child_rel),
17918 RelationGetRelationName(parent_rel))));
17919}
17920
17921/*
17922 * RemoveInheritance
17923 *
17924 * Drop a parent from the child's parents. This just adjusts the attinhcount
17925 * and attislocal of the columns and removes the pg_inherit and pg_depend
17926 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17927 *
17928 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17929 * up attislocal stays true, which means if a child is ever removed from a
17930 * parent then its columns will never be automatically dropped which may
17931 * surprise. But at least we'll never surprise by dropping columns someone
17932 * isn't expecting to be dropped which would actually mean data loss.
17933 *
17934 * coninhcount and conislocal for inherited constraints are adjusted in
17935 * exactly the same way.
17936 *
17937 * Common to ATExecDropInherit() and ATExecDetachPartition().
17938 */
17939static void
17940RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17941{
17942 Relation catalogRelation;
17943 SysScanDesc scan;
17944 ScanKeyData key[3];
17945 HeapTuple attributeTuple,
17946 constraintTuple;
17947 AttrMap *attmap;
17948 List *connames;
17949 List *nncolumns;
17950 bool found;
17951 bool is_partitioning;
17952
17953 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17954
17955 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17956 RelationGetRelid(parent_rel),
17957 expect_detached,
17958 RelationGetRelationName(child_rel));
17959 if (!found)
17960 {
17961 if (is_partitioning)
17962 ereport(ERROR,
17964 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17965 RelationGetRelationName(child_rel),
17966 RelationGetRelationName(parent_rel))));
17967 else
17968 ereport(ERROR,
17970 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17971 RelationGetRelationName(parent_rel),
17972 RelationGetRelationName(child_rel))));
17973 }
17974
17975 /*
17976 * Search through child columns looking for ones matching parent rel
17977 */
17978 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17979 ScanKeyInit(&key[0],
17980 Anum_pg_attribute_attrelid,
17981 BTEqualStrategyNumber, F_OIDEQ,
17983 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17984 true, NULL, 1, key);
17985 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17986 {
17987 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17988
17989 /* Ignore if dropped or not inherited */
17990 if (att->attisdropped)
17991 continue;
17992 if (att->attinhcount <= 0)
17993 continue;
17994
17996 NameStr(att->attname)))
17997 {
17998 /* Decrement inhcount and possibly set islocal to true */
17999 HeapTuple copyTuple = heap_copytuple(attributeTuple);
18000 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
18001
18002 copy_att->attinhcount--;
18003 if (copy_att->attinhcount == 0)
18004 copy_att->attislocal = true;
18005
18006 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18007 heap_freetuple(copyTuple);
18008 }
18009 }
18010 systable_endscan(scan);
18011 table_close(catalogRelation, RowExclusiveLock);
18012
18013 /*
18014 * Likewise, find inherited check and not-null constraints and disinherit
18015 * them. To do this, we first need a list of the names of the parent's
18016 * check constraints. (We cheat a bit by only checking for name matches,
18017 * assuming that the expressions will match.)
18018 *
18019 * For NOT NULL columns, we store column numbers to match, mapping them in
18020 * to the child rel's attribute numbers.
18021 */
18022 attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
18023 RelationGetDescr(parent_rel),
18024 false);
18025
18026 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
18027 ScanKeyInit(&key[0],
18028 Anum_pg_constraint_conrelid,
18029 BTEqualStrategyNumber, F_OIDEQ,
18030 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18031 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18032 true, NULL, 1, key);
18033
18034 connames = NIL;
18035 nncolumns = NIL;
18036
18037 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18038 {
18039 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18040
18041 if (con->connoinherit)
18042 continue;
18043
18044 if (con->contype == CONSTRAINT_CHECK)
18045 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18046 if (con->contype == CONSTRAINT_NOTNULL)
18047 {
18048 AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
18049
18050 nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18051 }
18052 }
18053
18054 systable_endscan(scan);
18055
18056 /* Now scan the child's constraints to find matches */
18057 ScanKeyInit(&key[0],
18058 Anum_pg_constraint_conrelid,
18059 BTEqualStrategyNumber, F_OIDEQ,
18061 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18062 true, NULL, 1, key);
18063
18064 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18065 {
18066 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18067 bool match = false;
18068
18069 /*
18070 * Match CHECK constraints by name, not-null constraints by column
18071 * number, and ignore all others.
18072 */
18073 if (con->contype == CONSTRAINT_CHECK)
18074 {
18075 foreach_ptr(char, chkname, connames)
18076 {
18077 if (con->contype == CONSTRAINT_CHECK &&
18078 strcmp(NameStr(con->conname), chkname) == 0)
18079 {
18080 match = true;
18081 connames = foreach_delete_current(connames, chkname);
18082 break;
18083 }
18084 }
18085 }
18086 else if (con->contype == CONSTRAINT_NOTNULL)
18087 {
18088 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18089
18090 foreach_int(prevattno, nncolumns)
18091 {
18092 if (prevattno == child_attno)
18093 {
18094 match = true;
18095 nncolumns = foreach_delete_current(nncolumns, prevattno);
18096 break;
18097 }
18098 }
18099 }
18100 else
18101 continue;
18102
18103 if (match)
18104 {
18105 /* Decrement inhcount and possibly set islocal to true */
18106 HeapTuple copyTuple = heap_copytuple(constraintTuple);
18107 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18108
18109 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18110 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18111 RelationGetRelid(child_rel), NameStr(copy_con->conname));
18112
18113 copy_con->coninhcount--;
18114 if (copy_con->coninhcount == 0)
18115 copy_con->conislocal = true;
18116
18117 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18118 heap_freetuple(copyTuple);
18119 }
18120 }
18121
18122 /* We should have matched all constraints */
18123 if (connames != NIL || nncolumns != NIL)
18124 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18125 list_length(connames) + list_length(nncolumns),
18126 RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18127
18128 systable_endscan(scan);
18129 table_close(catalogRelation, RowExclusiveLock);
18130
18132 RelationRelationId,
18133 RelationGetRelid(parent_rel),
18134 child_dependency_type(is_partitioning));
18135
18136 /*
18137 * Post alter hook of this inherits. Since object_access_hook doesn't take
18138 * multiple object identifiers, we relay oid of parent relation using
18139 * auxiliary_id argument.
18140 */
18141 InvokeObjectPostAlterHookArg(InheritsRelationId,
18142 RelationGetRelid(child_rel), 0,
18143 RelationGetRelid(parent_rel), false);
18144}
18145
18146/*
18147 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18148 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18149 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18150 * be TypeRelationId). There's no convenient way to do this, so go trawling
18151 * through pg_depend.
18152 */
18153static void
18154drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18155 DependencyType deptype)
18156{
18157 Relation catalogRelation;
18158 SysScanDesc scan;
18159 ScanKeyData key[3];
18160 HeapTuple depTuple;
18161
18162 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18163
18164 ScanKeyInit(&key[0],
18165 Anum_pg_depend_classid,
18166 BTEqualStrategyNumber, F_OIDEQ,
18167 ObjectIdGetDatum(RelationRelationId));
18168 ScanKeyInit(&key[1],
18169 Anum_pg_depend_objid,
18170 BTEqualStrategyNumber, F_OIDEQ,
18171 ObjectIdGetDatum(relid));
18172 ScanKeyInit(&key[2],
18173 Anum_pg_depend_objsubid,
18174 BTEqualStrategyNumber, F_INT4EQ,
18175 Int32GetDatum(0));
18176
18177 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18178 NULL, 3, key);
18179
18180 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18181 {
18182 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18183
18184 if (dep->refclassid == refclassid &&
18185 dep->refobjid == refobjid &&
18186 dep->refobjsubid == 0 &&
18187 dep->deptype == deptype)
18188 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18189 }
18190
18191 systable_endscan(scan);
18192 table_close(catalogRelation, RowExclusiveLock);
18193}
18194
18195/*
18196 * ALTER TABLE OF
18197 *
18198 * Attach a table to a composite type, as though it had been created with CREATE
18199 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18200 * subject table must not have inheritance parents. These restrictions ensure
18201 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18202 *
18203 * The address of the type is returned.
18204 */
18205static ObjectAddress
18206ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18207{
18208 Oid relid = RelationGetRelid(rel);
18209 Type typetuple;
18210 Form_pg_type typeform;
18211 Oid typeid;
18212 Relation inheritsRelation,
18213 relationRelation;
18214 SysScanDesc scan;
18216 AttrNumber table_attno,
18217 type_attno;
18218 TupleDesc typeTupleDesc,
18219 tableTupleDesc;
18220 ObjectAddress tableobj,
18221 typeobj;
18222 HeapTuple classtuple;
18223
18224 /* Validate the type. */
18225 typetuple = typenameType(NULL, ofTypename, NULL);
18226 check_of_type(typetuple);
18227 typeform = (Form_pg_type) GETSTRUCT(typetuple);
18228 typeid = typeform->oid;
18229
18230 /* Fail if the table has any inheritance parents. */
18231 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18233 Anum_pg_inherits_inhrelid,
18234 BTEqualStrategyNumber, F_OIDEQ,
18235 ObjectIdGetDatum(relid));
18236 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18237 true, NULL, 1, &key);
18239 ereport(ERROR,
18240 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18241 errmsg("typed tables cannot inherit")));
18242 systable_endscan(scan);
18243 table_close(inheritsRelation, AccessShareLock);
18244
18245 /*
18246 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18247 * require that the order also match. However, attnotnull need not match.
18248 */
18249 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18250 tableTupleDesc = RelationGetDescr(rel);
18251 table_attno = 1;
18252 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18253 {
18254 Form_pg_attribute type_attr,
18255 table_attr;
18256 const char *type_attname,
18257 *table_attname;
18258
18259 /* Get the next non-dropped type attribute. */
18260 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18261 if (type_attr->attisdropped)
18262 continue;
18263 type_attname = NameStr(type_attr->attname);
18264
18265 /* Get the next non-dropped table attribute. */
18266 do
18267 {
18268 if (table_attno > tableTupleDesc->natts)
18269 ereport(ERROR,
18270 (errcode(ERRCODE_DATATYPE_MISMATCH),
18271 errmsg("table is missing column \"%s\"",
18272 type_attname)));
18273 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18274 table_attno++;
18275 } while (table_attr->attisdropped);
18276 table_attname = NameStr(table_attr->attname);
18277
18278 /* Compare name. */
18279 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18280 ereport(ERROR,
18281 (errcode(ERRCODE_DATATYPE_MISMATCH),
18282 errmsg("table has column \"%s\" where type requires \"%s\"",
18283 table_attname, type_attname)));
18284
18285 /* Compare type. */
18286 if (table_attr->atttypid != type_attr->atttypid ||
18287 table_attr->atttypmod != type_attr->atttypmod ||
18288 table_attr->attcollation != type_attr->attcollation)
18289 ereport(ERROR,
18290 (errcode(ERRCODE_DATATYPE_MISMATCH),
18291 errmsg("table \"%s\" has different type for column \"%s\"",
18292 RelationGetRelationName(rel), type_attname)));
18293 }
18294 ReleaseTupleDesc(typeTupleDesc);
18295
18296 /* Any remaining columns at the end of the table had better be dropped. */
18297 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18298 {
18299 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18300 table_attno - 1);
18301
18302 if (!table_attr->attisdropped)
18303 ereport(ERROR,
18304 (errcode(ERRCODE_DATATYPE_MISMATCH),
18305 errmsg("table has extra column \"%s\"",
18306 NameStr(table_attr->attname))));
18307 }
18308
18309 /* If the table was already typed, drop the existing dependency. */
18310 if (rel->rd_rel->reloftype)
18311 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18313
18314 /* Record a dependency on the new type. */
18315 tableobj.classId = RelationRelationId;
18316 tableobj.objectId = relid;
18317 tableobj.objectSubId = 0;
18318 typeobj.classId = TypeRelationId;
18319 typeobj.objectId = typeid;
18320 typeobj.objectSubId = 0;
18321 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18322
18323 /* Update pg_class.reloftype */
18324 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18325 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18326 if (!HeapTupleIsValid(classtuple))
18327 elog(ERROR, "cache lookup failed for relation %u", relid);
18328 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18329 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18330
18331 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18332
18333 heap_freetuple(classtuple);
18334 table_close(relationRelation, RowExclusiveLock);
18335
18336 ReleaseSysCache(typetuple);
18337
18338 return typeobj;
18339}
18340
18341/*
18342 * ALTER TABLE NOT OF
18343 *
18344 * Detach a typed table from its originating type. Just clear reloftype and
18345 * remove the dependency.
18346 */
18347static void
18349{
18350 Oid relid = RelationGetRelid(rel);
18351 Relation relationRelation;
18352 HeapTuple tuple;
18353
18354 if (!OidIsValid(rel->rd_rel->reloftype))
18355 ereport(ERROR,
18356 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18357 errmsg("\"%s\" is not a typed table",
18359
18360 /*
18361 * We don't bother to check ownership of the type --- ownership of the
18362 * table is presumed enough rights. No lock required on the type, either.
18363 */
18364
18365 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18367
18368 /* Clear pg_class.reloftype */
18369 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18370 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18371 if (!HeapTupleIsValid(tuple))
18372 elog(ERROR, "cache lookup failed for relation %u", relid);
18373 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18374 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18375
18376 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18377
18378 heap_freetuple(tuple);
18379 table_close(relationRelation, RowExclusiveLock);
18380}
18381
18382/*
18383 * relation_mark_replica_identity: Update a table's replica identity
18384 *
18385 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18386 * index. Otherwise, it must be InvalidOid.
18387 *
18388 * Caller had better hold an exclusive lock on the relation, as the results
18389 * of running two of these concurrently wouldn't be pretty.
18390 */
18391static void
18392relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18393 bool is_internal)
18394{
18395 Relation pg_index;
18396 Relation pg_class;
18397 HeapTuple pg_class_tuple;
18398 HeapTuple pg_index_tuple;
18399 Form_pg_class pg_class_form;
18400 Form_pg_index pg_index_form;
18401 ListCell *index;
18402
18403 /*
18404 * Check whether relreplident has changed, and update it if so.
18405 */
18406 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18407 pg_class_tuple = SearchSysCacheCopy1(RELOID,
18409 if (!HeapTupleIsValid(pg_class_tuple))
18410 elog(ERROR, "cache lookup failed for relation \"%s\"",
18412 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18413 if (pg_class_form->relreplident != ri_type)
18414 {
18415 pg_class_form->relreplident = ri_type;
18416 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18417 }
18418 table_close(pg_class, RowExclusiveLock);
18419 heap_freetuple(pg_class_tuple);
18420
18421 /*
18422 * Update the per-index indisreplident flags correctly.
18423 */
18424 pg_index = table_open(IndexRelationId, RowExclusiveLock);
18425 foreach(index, RelationGetIndexList(rel))
18426 {
18427 Oid thisIndexOid = lfirst_oid(index);
18428 bool dirty = false;
18429
18430 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18431 ObjectIdGetDatum(thisIndexOid));
18432 if (!HeapTupleIsValid(pg_index_tuple))
18433 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18434 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18435
18436 if (thisIndexOid == indexOid)
18437 {
18438 /* Set the bit if not already set. */
18439 if (!pg_index_form->indisreplident)
18440 {
18441 dirty = true;
18442 pg_index_form->indisreplident = true;
18443 }
18444 }
18445 else
18446 {
18447 /* Unset the bit if set. */
18448 if (pg_index_form->indisreplident)
18449 {
18450 dirty = true;
18451 pg_index_form->indisreplident = false;
18452 }
18453 }
18454
18455 if (dirty)
18456 {
18457 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18458 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18459 InvalidOid, is_internal);
18460
18461 /*
18462 * Invalidate the relcache for the table, so that after we commit
18463 * all sessions will refresh the table's replica identity index
18464 * before attempting any UPDATE or DELETE on the table. (If we
18465 * changed the table's pg_class row above, then a relcache inval
18466 * is already queued due to that; but we might not have.)
18467 */
18469 }
18470 heap_freetuple(pg_index_tuple);
18471 }
18472
18473 table_close(pg_index, RowExclusiveLock);
18474}
18475
18476/*
18477 * ALTER TABLE <name> REPLICA IDENTITY ...
18478 */
18479static void
18481{
18482 Oid indexOid;
18483 Relation indexRel;
18484 int key;
18485
18486 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18487 {
18488 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18489 return;
18490 }
18491 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18492 {
18493 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18494 return;
18495 }
18496 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18497 {
18498 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18499 return;
18500 }
18501 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18502 {
18503 /* fallthrough */ ;
18504 }
18505 else
18506 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18507
18508 /* Check that the index exists */
18509 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18510 if (!OidIsValid(indexOid))
18511 ereport(ERROR,
18512 (errcode(ERRCODE_UNDEFINED_OBJECT),
18513 errmsg("index \"%s\" for table \"%s\" does not exist",
18514 stmt->name, RelationGetRelationName(rel))));
18515
18516 indexRel = index_open(indexOid, ShareLock);
18517
18518 /* Check that the index is on the relation we're altering. */
18519 if (indexRel->rd_index == NULL ||
18520 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18521 ereport(ERROR,
18522 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18523 errmsg("\"%s\" is not an index for table \"%s\"",
18524 RelationGetRelationName(indexRel),
18526
18527 /*
18528 * The AM must support uniqueness, and the index must in fact be unique.
18529 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18530 * exclusion), we can use that too.
18531 */
18532 if ((!indexRel->rd_indam->amcanunique ||
18533 !indexRel->rd_index->indisunique) &&
18534 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18535 ereport(ERROR,
18536 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18537 errmsg("cannot use non-unique index \"%s\" as replica identity",
18538 RelationGetRelationName(indexRel))));
18539 /* Deferred indexes are not guaranteed to be always unique. */
18540 if (!indexRel->rd_index->indimmediate)
18541 ereport(ERROR,
18542 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18543 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18544 RelationGetRelationName(indexRel))));
18545 /* Expression indexes aren't supported. */
18546 if (RelationGetIndexExpressions(indexRel) != NIL)
18547 ereport(ERROR,
18548 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18549 errmsg("cannot use expression index \"%s\" as replica identity",
18550 RelationGetRelationName(indexRel))));
18551 /* Predicate indexes aren't supported. */
18552 if (RelationGetIndexPredicate(indexRel) != NIL)
18553 ereport(ERROR,
18554 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18555 errmsg("cannot use partial index \"%s\" as replica identity",
18556 RelationGetRelationName(indexRel))));
18557
18558 /* Check index for nullable columns. */
18559 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18560 {
18561 int16 attno = indexRel->rd_index->indkey.values[key];
18562 Form_pg_attribute attr;
18563
18564 /*
18565 * Reject any other system columns. (Going forward, we'll disallow
18566 * indexes containing such columns in the first place, but they might
18567 * exist in older branches.)
18568 */
18569 if (attno <= 0)
18570 ereport(ERROR,
18571 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18572 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18573 RelationGetRelationName(indexRel), attno)));
18574
18575 attr = TupleDescAttr(rel->rd_att, attno - 1);
18576 if (!attr->attnotnull)
18577 ereport(ERROR,
18578 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18579 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18580 RelationGetRelationName(indexRel),
18581 NameStr(attr->attname))));
18582 }
18583
18584 /* This index is suitable for use as a replica identity. Mark it. */
18585 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18586
18587 index_close(indexRel, NoLock);
18588}
18589
18590/*
18591 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18592 */
18593static void
18595{
18596 Relation pg_class;
18597 Oid relid;
18598 HeapTuple tuple;
18599
18600 relid = RelationGetRelid(rel);
18601
18602 /* Pull the record for this relation and update it */
18603 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18604
18605 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18606
18607 if (!HeapTupleIsValid(tuple))
18608 elog(ERROR, "cache lookup failed for relation %u", relid);
18609
18610 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18611 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18612
18613 InvokeObjectPostAlterHook(RelationRelationId,
18614 RelationGetRelid(rel), 0);
18615
18616 table_close(pg_class, RowExclusiveLock);
18617 heap_freetuple(tuple);
18618}
18619
18620/*
18621 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18622 */
18623static void
18625{
18626 Relation pg_class;
18627 Oid relid;
18628 HeapTuple tuple;
18629
18630 relid = RelationGetRelid(rel);
18631
18632 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18633
18634 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18635
18636 if (!HeapTupleIsValid(tuple))
18637 elog(ERROR, "cache lookup failed for relation %u", relid);
18638
18639 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18640 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18641
18642 InvokeObjectPostAlterHook(RelationRelationId,
18643 RelationGetRelid(rel), 0);
18644
18645 table_close(pg_class, RowExclusiveLock);
18646 heap_freetuple(tuple);
18647}
18648
18649/*
18650 * ALTER FOREIGN TABLE <name> OPTIONS (...)
18651 */
18652static void
18654{
18655 Relation ftrel;
18656 ForeignServer *server;
18657 ForeignDataWrapper *fdw;
18658 HeapTuple tuple;
18659 bool isnull;
18660 Datum repl_val[Natts_pg_foreign_table];
18661 bool repl_null[Natts_pg_foreign_table];
18662 bool repl_repl[Natts_pg_foreign_table];
18663 Datum datum;
18664 Form_pg_foreign_table tableform;
18665
18666 if (options == NIL)
18667 return;
18668
18669 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18670
18671 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18672 ObjectIdGetDatum(rel->rd_id));
18673 if (!HeapTupleIsValid(tuple))
18674 ereport(ERROR,
18675 (errcode(ERRCODE_UNDEFINED_OBJECT),
18676 errmsg("foreign table \"%s\" does not exist",
18678 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18679 server = GetForeignServer(tableform->ftserver);
18680 fdw = GetForeignDataWrapper(server->fdwid);
18681
18682 memset(repl_val, 0, sizeof(repl_val));
18683 memset(repl_null, false, sizeof(repl_null));
18684 memset(repl_repl, false, sizeof(repl_repl));
18685
18686 /* Extract the current options */
18687 datum = SysCacheGetAttr(FOREIGNTABLEREL,
18688 tuple,
18689 Anum_pg_foreign_table_ftoptions,
18690 &isnull);
18691 if (isnull)
18692 datum = PointerGetDatum(NULL);
18693
18694 /* Transform the options */
18695 datum = transformGenericOptions(ForeignTableRelationId,
18696 datum,
18697 options,
18698 fdw->fdwvalidator);
18699
18700 if (DatumGetPointer(datum) != NULL)
18701 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18702 else
18703 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18704
18705 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18706
18707 /* Everything looks good - update the tuple */
18708
18709 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18710 repl_val, repl_null, repl_repl);
18711
18712 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18713
18714 /*
18715 * Invalidate relcache so that all sessions will refresh any cached plans
18716 * that might depend on the old options.
18717 */
18719
18720 InvokeObjectPostAlterHook(ForeignTableRelationId,
18721 RelationGetRelid(rel), 0);
18722
18724
18725 heap_freetuple(tuple);
18726}
18727
18728/*
18729 * ALTER TABLE ALTER COLUMN SET COMPRESSION
18730 *
18731 * Return value is the address of the modified column
18732 */
18733static ObjectAddress
18735 const char *column,
18736 Node *newValue,
18737 LOCKMODE lockmode)
18738{
18739 Relation attrel;
18740 HeapTuple tuple;
18741 Form_pg_attribute atttableform;
18743 char *compression;
18744 char cmethod;
18745 ObjectAddress address;
18746
18747 compression = strVal(newValue);
18748
18749 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18750
18751 /* copy the cache entry so we can scribble on it below */
18752 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18753 if (!HeapTupleIsValid(tuple))
18754 ereport(ERROR,
18755 (errcode(ERRCODE_UNDEFINED_COLUMN),
18756 errmsg("column \"%s\" of relation \"%s\" does not exist",
18757 column, RelationGetRelationName(rel))));
18758
18759 /* prevent them from altering a system attribute */
18760 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18761 attnum = atttableform->attnum;
18762 if (attnum <= 0)
18763 ereport(ERROR,
18764 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18765 errmsg("cannot alter system column \"%s\"", column)));
18766
18767 /*
18768 * Check that column type is compressible, then get the attribute
18769 * compression method code
18770 */
18771 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18772
18773 /* update pg_attribute entry */
18774 atttableform->attcompression = cmethod;
18775 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18776
18777 InvokeObjectPostAlterHook(RelationRelationId,
18778 RelationGetRelid(rel),
18779 attnum);
18780
18781 /*
18782 * Apply the change to indexes as well (only for simple index columns,
18783 * matching behavior of index.c ConstructTupleDescriptor()).
18784 */
18785 SetIndexStorageProperties(rel, attrel, attnum,
18786 false, 0,
18787 true, cmethod,
18788 lockmode);
18789
18790 heap_freetuple(tuple);
18791
18793
18794 /* make changes visible */
18796
18797 ObjectAddressSubSet(address, RelationRelationId,
18798 RelationGetRelid(rel), attnum);
18799 return address;
18800}
18801
18802
18803/*
18804 * Preparation phase for SET LOGGED/UNLOGGED
18805 *
18806 * This verifies that we're not trying to change a temp table. Also,
18807 * existing foreign key constraints are checked to avoid ending up with
18808 * permanent tables referencing unlogged tables.
18809 */
18810static void
18812{
18813 Relation pg_constraint;
18814 HeapTuple tuple;
18815 SysScanDesc scan;
18816 ScanKeyData skey[1];
18817
18818 /*
18819 * Disallow changing status for a temp table. Also verify whether we can
18820 * get away with doing nothing; in such cases we don't need to run the
18821 * checks below, either.
18822 */
18823 switch (rel->rd_rel->relpersistence)
18824 {
18825 case RELPERSISTENCE_TEMP:
18826 ereport(ERROR,
18827 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18828 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18830 errtable(rel)));
18831 break;
18832 case RELPERSISTENCE_PERMANENT:
18833 if (toLogged)
18834 /* nothing to do */
18835 return;
18836 break;
18837 case RELPERSISTENCE_UNLOGGED:
18838 if (!toLogged)
18839 /* nothing to do */
18840 return;
18841 break;
18842 }
18843
18844 /*
18845 * Check that the table is not part of any publication when changing to
18846 * UNLOGGED, as UNLOGGED tables can't be published.
18847 */
18848 if (!toLogged &&
18850 ereport(ERROR,
18851 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18852 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18854 errdetail("Unlogged relations cannot be replicated.")));
18855
18856 /*
18857 * Check existing foreign key constraints to preserve the invariant that
18858 * permanent tables cannot reference unlogged ones. Self-referencing
18859 * foreign keys can safely be ignored.
18860 */
18861 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18862
18863 /*
18864 * Scan conrelid if changing to permanent, else confrelid. This also
18865 * determines whether a useful index exists.
18866 */
18867 ScanKeyInit(&skey[0],
18868 toLogged ? Anum_pg_constraint_conrelid :
18869 Anum_pg_constraint_confrelid,
18870 BTEqualStrategyNumber, F_OIDEQ,
18872 scan = systable_beginscan(pg_constraint,
18873 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18874 true, NULL, 1, skey);
18875
18876 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18877 {
18879
18880 if (con->contype == CONSTRAINT_FOREIGN)
18881 {
18882 Oid foreignrelid;
18883 Relation foreignrel;
18884
18885 /* the opposite end of what we used as scankey */
18886 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18887
18888 /* ignore if self-referencing */
18889 if (RelationGetRelid(rel) == foreignrelid)
18890 continue;
18891
18892 foreignrel = relation_open(foreignrelid, AccessShareLock);
18893
18894 if (toLogged)
18895 {
18896 if (!RelationIsPermanent(foreignrel))
18897 ereport(ERROR,
18898 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18899 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18901 RelationGetRelationName(foreignrel)),
18902 errtableconstraint(rel, NameStr(con->conname))));
18903 }
18904 else
18905 {
18906 if (RelationIsPermanent(foreignrel))
18907 ereport(ERROR,
18908 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18909 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18911 RelationGetRelationName(foreignrel)),
18912 errtableconstraint(rel, NameStr(con->conname))));
18913 }
18914
18915 relation_close(foreignrel, AccessShareLock);
18916 }
18917 }
18918
18919 systable_endscan(scan);
18920
18921 table_close(pg_constraint, AccessShareLock);
18922
18923 /* force rewrite if necessary; see comment in ATRewriteTables */
18925 if (toLogged)
18926 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18927 else
18928 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18929 tab->chgPersistence = true;
18930}
18931
18932/*
18933 * Execute ALTER TABLE SET SCHEMA
18934 */
18937{
18938 Relation rel;
18939 Oid relid;
18940 Oid oldNspOid;
18941 Oid nspOid;
18942 RangeVar *newrv;
18943 ObjectAddresses *objsMoved;
18944 ObjectAddress myself;
18945
18947 stmt->missing_ok ? RVR_MISSING_OK : 0,
18949 stmt);
18950
18951 if (!OidIsValid(relid))
18952 {
18954 (errmsg("relation \"%s\" does not exist, skipping",
18955 stmt->relation->relname)));
18956 return InvalidObjectAddress;
18957 }
18958
18959 rel = relation_open(relid, NoLock);
18960
18961 oldNspOid = RelationGetNamespace(rel);
18962
18963 /* If it's an owned sequence, disallow moving it by itself. */
18964 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18965 {
18966 Oid tableId;
18967 int32 colId;
18968
18969 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18970 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18971 ereport(ERROR,
18972 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18973 errmsg("cannot move an owned sequence into another schema"),
18974 errdetail("Sequence \"%s\" is linked to table \"%s\".",
18976 get_rel_name(tableId))));
18977 }
18978
18979 /* Get and lock schema OID and check its permissions. */
18980 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18981 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18982
18983 /* common checks on switching namespaces */
18984 CheckSetNamespace(oldNspOid, nspOid);
18985
18986 objsMoved = new_object_addresses();
18987 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18988 free_object_addresses(objsMoved);
18989
18990 ObjectAddressSet(myself, RelationRelationId, relid);
18991
18992 if (oldschema)
18993 *oldschema = oldNspOid;
18994
18995 /* close rel, but keep lock until commit */
18996 relation_close(rel, NoLock);
18997
18998 return myself;
18999}
19000
19001/*
19002 * The guts of relocating a table or materialized view to another namespace:
19003 * besides moving the relation itself, its dependent objects are relocated to
19004 * the new schema.
19005 */
19006void
19008 ObjectAddresses *objsMoved)
19009{
19010 Relation classRel;
19011
19012 Assert(objsMoved != NULL);
19013
19014 /* OK, modify the pg_class row and pg_depend entry */
19015 classRel = table_open(RelationRelationId, RowExclusiveLock);
19016
19017 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
19018 nspOid, true, objsMoved);
19019
19020 /* Fix the table's row type too, if it has one */
19021 if (OidIsValid(rel->rd_rel->reltype))
19022 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
19023 false, /* isImplicitArray */
19024 false, /* ignoreDependent */
19025 false, /* errorOnTableType */
19026 objsMoved);
19027
19028 /* Fix other dependent stuff */
19029 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19030 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19031 objsMoved, AccessExclusiveLock);
19032 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19033 false, objsMoved);
19034
19035 table_close(classRel, RowExclusiveLock);
19036}
19037
19038/*
19039 * The guts of relocating a relation to another namespace: fix the pg_class
19040 * entry, and the pg_depend entry if any. Caller must already have
19041 * opened and write-locked pg_class.
19042 */
19043void
19045 Oid oldNspOid, Oid newNspOid,
19046 bool hasDependEntry,
19047 ObjectAddresses *objsMoved)
19048{
19049 HeapTuple classTup;
19050 Form_pg_class classForm;
19051 ObjectAddress thisobj;
19052 bool already_done = false;
19053
19054 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19055 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19056 if (!HeapTupleIsValid(classTup))
19057 elog(ERROR, "cache lookup failed for relation %u", relOid);
19058 classForm = (Form_pg_class) GETSTRUCT(classTup);
19059
19060 Assert(classForm->relnamespace == oldNspOid);
19061
19062 thisobj.classId = RelationRelationId;
19063 thisobj.objectId = relOid;
19064 thisobj.objectSubId = 0;
19065
19066 /*
19067 * If the object has already been moved, don't move it again. If it's
19068 * already in the right place, don't move it, but still fire the object
19069 * access hook.
19070 */
19071 already_done = object_address_present(&thisobj, objsMoved);
19072 if (!already_done && oldNspOid != newNspOid)
19073 {
19074 ItemPointerData otid = classTup->t_self;
19075
19076 /* check for duplicate name (more friendly than unique-index failure) */
19077 if (get_relname_relid(NameStr(classForm->relname),
19078 newNspOid) != InvalidOid)
19079 ereport(ERROR,
19080 (errcode(ERRCODE_DUPLICATE_TABLE),
19081 errmsg("relation \"%s\" already exists in schema \"%s\"",
19082 NameStr(classForm->relname),
19083 get_namespace_name(newNspOid))));
19084
19085 /* classTup is a copy, so OK to scribble on */
19086 classForm->relnamespace = newNspOid;
19087
19088 CatalogTupleUpdate(classRel, &otid, classTup);
19089 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19090
19091
19092 /* Update dependency on schema if caller said so */
19093 if (hasDependEntry &&
19094 changeDependencyFor(RelationRelationId,
19095 relOid,
19096 NamespaceRelationId,
19097 oldNspOid,
19098 newNspOid) != 1)
19099 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19100 NameStr(classForm->relname));
19101 }
19102 else
19103 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19104 if (!already_done)
19105 {
19106 add_exact_object_address(&thisobj, objsMoved);
19107
19108 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19109 }
19110
19111 heap_freetuple(classTup);
19112}
19113
19114/*
19115 * Move all indexes for the specified relation to another namespace.
19116 *
19117 * Note: we assume adequate permission checking was done by the caller,
19118 * and that the caller has a suitable lock on the owning relation.
19119 */
19120static void
19122 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19123{
19124 List *indexList;
19125 ListCell *l;
19126
19127 indexList = RelationGetIndexList(rel);
19128
19129 foreach(l, indexList)
19130 {
19131 Oid indexOid = lfirst_oid(l);
19132 ObjectAddress thisobj;
19133
19134 thisobj.classId = RelationRelationId;
19135 thisobj.objectId = indexOid;
19136 thisobj.objectSubId = 0;
19137
19138 /*
19139 * Note: currently, the index will not have its own dependency on the
19140 * namespace, so we don't need to do changeDependencyFor(). There's no
19141 * row type in pg_type, either.
19142 *
19143 * XXX this objsMoved test may be pointless -- surely we have a single
19144 * dependency link from a relation to each index?
19145 */
19146 if (!object_address_present(&thisobj, objsMoved))
19147 {
19148 AlterRelationNamespaceInternal(classRel, indexOid,
19149 oldNspOid, newNspOid,
19150 false, objsMoved);
19151 add_exact_object_address(&thisobj, objsMoved);
19152 }
19153 }
19154
19155 list_free(indexList);
19156}
19157
19158/*
19159 * Move all identity and SERIAL-column sequences of the specified relation to another
19160 * namespace.
19161 *
19162 * Note: we assume adequate permission checking was done by the caller,
19163 * and that the caller has a suitable lock on the owning relation.
19164 */
19165static void
19167 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19168 LOCKMODE lockmode)
19169{
19170 Relation depRel;
19171 SysScanDesc scan;
19172 ScanKeyData key[2];
19173 HeapTuple tup;
19174
19175 /*
19176 * SERIAL sequences are those having an auto dependency on one of the
19177 * table's columns (we don't care *which* column, exactly).
19178 */
19179 depRel = table_open(DependRelationId, AccessShareLock);
19180
19181 ScanKeyInit(&key[0],
19182 Anum_pg_depend_refclassid,
19183 BTEqualStrategyNumber, F_OIDEQ,
19184 ObjectIdGetDatum(RelationRelationId));
19185 ScanKeyInit(&key[1],
19186 Anum_pg_depend_refobjid,
19187 BTEqualStrategyNumber, F_OIDEQ,
19189 /* we leave refobjsubid unspecified */
19190
19191 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19192 NULL, 2, key);
19193
19194 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19195 {
19196 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19197 Relation seqRel;
19198
19199 /* skip dependencies other than auto dependencies on columns */
19200 if (depForm->refobjsubid == 0 ||
19201 depForm->classid != RelationRelationId ||
19202 depForm->objsubid != 0 ||
19203 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19204 continue;
19205
19206 /* Use relation_open just in case it's an index */
19207 seqRel = relation_open(depForm->objid, lockmode);
19208
19209 /* skip non-sequence relations */
19210 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19211 {
19212 /* No need to keep the lock */
19213 relation_close(seqRel, lockmode);
19214 continue;
19215 }
19216
19217 /* Fix the pg_class and pg_depend entries */
19218 AlterRelationNamespaceInternal(classRel, depForm->objid,
19219 oldNspOid, newNspOid,
19220 true, objsMoved);
19221
19222 /*
19223 * Sequences used to have entries in pg_type, but no longer do. If we
19224 * ever re-instate that, we'll need to move the pg_type entry to the
19225 * new namespace, too (using AlterTypeNamespaceInternal).
19226 */
19227 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19228
19229 /* Now we can close it. Keep the lock till end of transaction. */
19230 relation_close(seqRel, NoLock);
19231 }
19232
19233 systable_endscan(scan);
19234
19236}
19237
19238
19239/*
19240 * This code supports
19241 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19242 *
19243 * Because we only support this for TEMP tables, it's sufficient to remember
19244 * the state in a backend-local data structure.
19245 */
19246
19247/*
19248 * Register a newly-created relation's ON COMMIT action.
19249 */
19250void
19252{
19253 OnCommitItem *oc;
19254 MemoryContext oldcxt;
19255
19256 /*
19257 * We needn't bother registering the relation unless there is an ON COMMIT
19258 * action we need to take.
19259 */
19261 return;
19262
19264
19265 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19266 oc->relid = relid;
19267 oc->oncommit = action;
19270
19271 /*
19272 * We use lcons() here so that ON COMMIT actions are processed in reverse
19273 * order of registration. That might not be essential but it seems
19274 * reasonable.
19275 */
19277
19278 MemoryContextSwitchTo(oldcxt);
19279}
19280
19281/*
19282 * Unregister any ON COMMIT action when a relation is deleted.
19283 *
19284 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19285 */
19286void
19288{
19289 ListCell *l;
19290
19291 foreach(l, on_commits)
19292 {
19293 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19294
19295 if (oc->relid == relid)
19296 {
19298 break;
19299 }
19300 }
19301}
19302
19303/*
19304 * Perform ON COMMIT actions.
19305 *
19306 * This is invoked just before actually committing, since it's possible
19307 * to encounter errors.
19308 */
19309void
19311{
19312 ListCell *l;
19313 List *oids_to_truncate = NIL;
19314 List *oids_to_drop = NIL;
19315
19316 foreach(l, on_commits)
19317 {
19318 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19319
19320 /* Ignore entry if already dropped in this xact */
19322 continue;
19323
19324 switch (oc->oncommit)
19325 {
19326 case ONCOMMIT_NOOP:
19328 /* Do nothing (there shouldn't be such entries, actually) */
19329 break;
19331
19332 /*
19333 * If this transaction hasn't accessed any temporary
19334 * relations, we can skip truncating ON COMMIT DELETE ROWS
19335 * tables, as they must still be empty.
19336 */
19338 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19339 break;
19340 case ONCOMMIT_DROP:
19341 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19342 break;
19343 }
19344 }
19345
19346 /*
19347 * Truncate relations before dropping so that all dependencies between
19348 * relations are removed after they are worked on. Doing it like this
19349 * might be a waste as it is possible that a relation being truncated will
19350 * be dropped anyway due to its parent being dropped, but this makes the
19351 * code more robust because of not having to re-check that the relation
19352 * exists at truncation time.
19353 */
19354 if (oids_to_truncate != NIL)
19355 heap_truncate(oids_to_truncate);
19356
19357 if (oids_to_drop != NIL)
19358 {
19359 ObjectAddresses *targetObjects = new_object_addresses();
19360
19361 foreach(l, oids_to_drop)
19362 {
19363 ObjectAddress object;
19364
19365 object.classId = RelationRelationId;
19366 object.objectId = lfirst_oid(l);
19367 object.objectSubId = 0;
19368
19369 Assert(!object_address_present(&object, targetObjects));
19370
19371 add_exact_object_address(&object, targetObjects);
19372 }
19373
19374 /*
19375 * Object deletion might involve toast table access (to clean up
19376 * toasted catalog entries), so ensure we have a valid snapshot.
19377 */
19379
19380 /*
19381 * Since this is an automatic drop, rather than one directly initiated
19382 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19383 */
19386
19388
19389#ifdef USE_ASSERT_CHECKING
19390
19391 /*
19392 * Note that table deletion will call remove_on_commit_action, so the
19393 * entry should get marked as deleted.
19394 */
19395 foreach(l, on_commits)
19396 {
19397 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19398
19399 if (oc->oncommit != ONCOMMIT_DROP)
19400 continue;
19401
19403 }
19404#endif
19405 }
19406}
19407
19408/*
19409 * Post-commit or post-abort cleanup for ON COMMIT management.
19410 *
19411 * All we do here is remove no-longer-needed OnCommitItem entries.
19412 *
19413 * During commit, remove entries that were deleted during this transaction;
19414 * during abort, remove those created during this transaction.
19415 */
19416void
19418{
19419 ListCell *cur_item;
19420
19421 foreach(cur_item, on_commits)
19422 {
19423 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19424
19425 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19427 {
19428 /* cur_item must be removed */
19430 pfree(oc);
19431 }
19432 else
19433 {
19434 /* cur_item must be preserved */
19437 }
19438 }
19439}
19440
19441/*
19442 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19443 *
19444 * During subabort, we can immediately remove entries created during this
19445 * subtransaction. During subcommit, just relabel entries marked during
19446 * this subtransaction as being the parent's responsibility.
19447 */
19448void
19450 SubTransactionId parentSubid)
19451{
19452 ListCell *cur_item;
19453
19454 foreach(cur_item, on_commits)
19455 {
19456 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19457
19458 if (!isCommit && oc->creating_subid == mySubid)
19459 {
19460 /* cur_item must be removed */
19462 pfree(oc);
19463 }
19464 else
19465 {
19466 /* cur_item must be preserved */
19467 if (oc->creating_subid == mySubid)
19468 oc->creating_subid = parentSubid;
19469 if (oc->deleting_subid == mySubid)
19470 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19471 }
19472 }
19473}
19474
19475/*
19476 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19477 * the relation to be locked only if (1) it's a plain or partitioned table,
19478 * materialized view, or TOAST table and (2) the current user is the owner (or
19479 * the superuser) or has been granted MAINTAIN. This meets the
19480 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19481 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19482 */
19483void
19485 Oid relId, Oid oldRelId, void *arg)
19486{
19487 char relkind;
19488 AclResult aclresult;
19489
19490 /* Nothing to do if the relation was not found. */
19491 if (!OidIsValid(relId))
19492 return;
19493
19494 /*
19495 * If the relation does exist, check whether it's an index. But note that
19496 * the relation might have been dropped between the time we did the name
19497 * lookup and now. In that case, there's nothing to do.
19498 */
19499 relkind = get_rel_relkind(relId);
19500 if (!relkind)
19501 return;
19502 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19503 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19504 ereport(ERROR,
19505 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19506 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19507
19508 /* Check permissions */
19509 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19510 if (aclresult != ACLCHECK_OK)
19511 aclcheck_error(aclresult,
19513 relation->relname);
19514}
19515
19516/*
19517 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19518 */
19519static void
19521 Oid relId, Oid oldRelId, void *arg)
19522{
19523 HeapTuple tuple;
19524
19525 /* Nothing to do if the relation was not found. */
19526 if (!OidIsValid(relId))
19527 return;
19528
19529 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19530 if (!HeapTupleIsValid(tuple)) /* should not happen */
19531 elog(ERROR, "cache lookup failed for relation %u", relId);
19532
19535
19536 ReleaseSysCache(tuple);
19537}
19538
19539/*
19540 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19541 * the owner of the relation, or superuser.
19542 */
19543void
19545 Oid relId, Oid oldRelId, void *arg)
19546{
19547 HeapTuple tuple;
19548
19549 /* Nothing to do if the relation was not found. */
19550 if (!OidIsValid(relId))
19551 return;
19552
19553 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19554 if (!HeapTupleIsValid(tuple)) /* should not happen */
19555 elog(ERROR, "cache lookup failed for relation %u", relId);
19556
19557 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19559 relation->relname);
19560
19561 if (!allowSystemTableMods &&
19562 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19563 ereport(ERROR,
19564 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19565 errmsg("permission denied: \"%s\" is a system catalog",
19566 relation->relname)));
19567
19568 ReleaseSysCache(tuple);
19569}
19570
19571/*
19572 * Common RangeVarGetRelid callback for rename, set schema, and alter table
19573 * processing.
19574 */
19575static void
19577 void *arg)
19578{
19579 Node *stmt = (Node *) arg;
19580 ObjectType reltype;
19581 HeapTuple tuple;
19582 Form_pg_class classform;
19583 AclResult aclresult;
19584 char relkind;
19585
19586 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19587 if (!HeapTupleIsValid(tuple))
19588 return; /* concurrently dropped */
19589 classform = (Form_pg_class) GETSTRUCT(tuple);
19590 relkind = classform->relkind;
19591
19592 /* Must own relation. */
19593 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19595
19596 /* No system table modifications unless explicitly allowed. */
19597 if (!allowSystemTableMods && IsSystemClass(relid, classform))
19598 ereport(ERROR,
19599 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19600 errmsg("permission denied: \"%s\" is a system catalog",
19601 rv->relname)));
19602
19603 /*
19604 * Extract the specified relation type from the statement parse tree.
19605 *
19606 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19607 * have CREATE rights on the containing namespace.
19608 */
19609 if (IsA(stmt, RenameStmt))
19610 {
19611 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19613 if (aclresult != ACLCHECK_OK)
19614 aclcheck_error(aclresult, OBJECT_SCHEMA,
19615 get_namespace_name(classform->relnamespace));
19616 reltype = ((RenameStmt *) stmt)->renameType;
19617 }
19618 else if (IsA(stmt, AlterObjectSchemaStmt))
19619 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19620
19621 else if (IsA(stmt, AlterTableStmt))
19622 reltype = ((AlterTableStmt *) stmt)->objtype;
19623 else
19624 {
19625 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19626 reltype = OBJECT_TABLE; /* placate compiler */
19627 }
19628
19629 /*
19630 * For compatibility with prior releases, we allow ALTER TABLE to be used
19631 * with most other types of relations (but not composite types). We allow
19632 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19633 * otherwise. Otherwise, the user must select the correct form of the
19634 * command for the relation at issue.
19635 */
19636 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19637 ereport(ERROR,
19638 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19639 errmsg("\"%s\" is not a sequence", rv->relname)));
19640
19641 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19642 ereport(ERROR,
19643 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19644 errmsg("\"%s\" is not a view", rv->relname)));
19645
19646 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19647 ereport(ERROR,
19648 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19649 errmsg("\"%s\" is not a materialized view", rv->relname)));
19650
19651 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19652 ereport(ERROR,
19653 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19654 errmsg("\"%s\" is not a foreign table", rv->relname)));
19655
19656 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19657 ereport(ERROR,
19658 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19659 errmsg("\"%s\" is not a composite type", rv->relname)));
19660
19661 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19662 relkind != RELKIND_PARTITIONED_INDEX
19663 && !IsA(stmt, RenameStmt))
19664 ereport(ERROR,
19665 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19666 errmsg("\"%s\" is not an index", rv->relname)));
19667
19668 /*
19669 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19670 * TYPE for that.
19671 */
19672 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19673 ereport(ERROR,
19674 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19675 errmsg("\"%s\" is a composite type", rv->relname),
19676 /* translator: %s is an SQL ALTER command */
19677 errhint("Use %s instead.",
19678 "ALTER TYPE")));
19679
19680 /*
19681 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19682 * to a different schema, such as indexes and TOAST tables.
19683 */
19685 {
19686 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19687 ereport(ERROR,
19688 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19689 errmsg("cannot change schema of index \"%s\"",
19690 rv->relname),
19691 errhint("Change the schema of the table instead.")));
19692 else if (relkind == RELKIND_COMPOSITE_TYPE)
19693 ereport(ERROR,
19694 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19695 errmsg("cannot change schema of composite type \"%s\"",
19696 rv->relname),
19697 /* translator: %s is an SQL ALTER command */
19698 errhint("Use %s instead.",
19699 "ALTER TYPE")));
19700 else if (relkind == RELKIND_TOASTVALUE)
19701 ereport(ERROR,
19702 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19703 errmsg("cannot change schema of TOAST table \"%s\"",
19704 rv->relname),
19705 errhint("Change the schema of the table instead.")));
19706 }
19707
19708 ReleaseSysCache(tuple);
19709}
19710
19711/*
19712 * Transform any expressions present in the partition key
19713 *
19714 * Returns a transformed PartitionSpec.
19715 */
19716static PartitionSpec *
19718{
19719 PartitionSpec *newspec;
19720 ParseState *pstate;
19721 ParseNamespaceItem *nsitem;
19722 ListCell *l;
19723
19724 newspec = makeNode(PartitionSpec);
19725
19726 newspec->strategy = partspec->strategy;
19727 newspec->partParams = NIL;
19728 newspec->location = partspec->location;
19729
19730 /* Check valid number of columns for strategy */
19731 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19732 list_length(partspec->partParams) != 1)
19733 ereport(ERROR,
19734 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19735 errmsg("cannot use \"list\" partition strategy with more than one column")));
19736
19737 /*
19738 * Create a dummy ParseState and insert the target relation as its sole
19739 * rangetable entry. We need a ParseState for transformExpr.
19740 */
19741 pstate = make_parsestate(NULL);
19742 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19743 NULL, false, true);
19744 addNSItemToQuery(pstate, nsitem, true, true, true);
19745
19746 /* take care of any partition expressions */
19747 foreach(l, partspec->partParams)
19748 {
19750
19751 if (pelem->expr)
19752 {
19753 /* Copy, to avoid scribbling on the input */
19754 pelem = copyObject(pelem);
19755
19756 /* Now do parse transformation of the expression */
19757 pelem->expr = transformExpr(pstate, pelem->expr,
19759
19760 /* we have to fix its collations too */
19761 assign_expr_collations(pstate, pelem->expr);
19762 }
19763
19764 newspec->partParams = lappend(newspec->partParams, pelem);
19765 }
19766
19767 return newspec;
19768}
19769
19770/*
19771 * Compute per-partition-column information from a list of PartitionElems.
19772 * Expressions in the PartitionElems must be parse-analyzed already.
19773 */
19774static void
19775ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19776 List **partexprs, Oid *partopclass, Oid *partcollation,
19777 PartitionStrategy strategy)
19778{
19779 int attn;
19780 ListCell *lc;
19781 Oid am_oid;
19782
19783 attn = 0;
19784 foreach(lc, partParams)
19785 {
19787 Oid atttype;
19788 Oid attcollation;
19789
19790 if (pelem->name != NULL)
19791 {
19792 /* Simple attribute reference */
19793 HeapTuple atttuple;
19794 Form_pg_attribute attform;
19795
19797 pelem->name);
19798 if (!HeapTupleIsValid(atttuple))
19799 ereport(ERROR,
19800 (errcode(ERRCODE_UNDEFINED_COLUMN),
19801 errmsg("column \"%s\" named in partition key does not exist",
19802 pelem->name),
19803 parser_errposition(pstate, pelem->location)));
19804 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19805
19806 if (attform->attnum <= 0)
19807 ereport(ERROR,
19808 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19809 errmsg("cannot use system column \"%s\" in partition key",
19810 pelem->name),
19811 parser_errposition(pstate, pelem->location)));
19812
19813 /*
19814 * Stored generated columns cannot work: They are computed after
19815 * BEFORE triggers, but partition routing is done before all
19816 * triggers. Maybe virtual generated columns could be made to
19817 * work, but then they would need to be handled as an expression
19818 * below.
19819 */
19820 if (attform->attgenerated)
19821 ereport(ERROR,
19822 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19823 errmsg("cannot use generated column in partition key"),
19824 errdetail("Column \"%s\" is a generated column.",
19825 pelem->name),
19826 parser_errposition(pstate, pelem->location)));
19827
19828 partattrs[attn] = attform->attnum;
19829 atttype = attform->atttypid;
19830 attcollation = attform->attcollation;
19831 ReleaseSysCache(atttuple);
19832 }
19833 else
19834 {
19835 /* Expression */
19836 Node *expr = pelem->expr;
19837 char partattname[16];
19838 Bitmapset *expr_attrs = NULL;
19839 int i;
19840
19841 Assert(expr != NULL);
19842 atttype = exprType(expr);
19843 attcollation = exprCollation(expr);
19844
19845 /*
19846 * The expression must be of a storable type (e.g., not RECORD).
19847 * The test is the same as for whether a table column is of a safe
19848 * type (which is why we needn't check for the non-expression
19849 * case).
19850 */
19851 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19852 CheckAttributeType(partattname,
19853 atttype, attcollation,
19855
19856 /*
19857 * Strip any top-level COLLATE clause. This ensures that we treat
19858 * "x COLLATE y" and "(x COLLATE y)" alike.
19859 */
19860 while (IsA(expr, CollateExpr))
19861 expr = (Node *) ((CollateExpr *) expr)->arg;
19862
19863 /*
19864 * Examine all the columns in the partition key expression. When
19865 * the whole-row reference is present, examine all the columns of
19866 * the partitioned table.
19867 */
19868 pull_varattnos(expr, 1, &expr_attrs);
19870 {
19871 expr_attrs = bms_add_range(expr_attrs,
19874 expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
19875 }
19876
19877 i = -1;
19878 while ((i = bms_next_member(expr_attrs, i)) >= 0)
19879 {
19881
19882 Assert(attno != 0);
19883
19884 /*
19885 * Cannot allow system column references, since that would
19886 * make partition routing impossible: their values won't be
19887 * known yet when we need to do that.
19888 */
19889 if (attno < 0)
19890 ereport(ERROR,
19891 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19892 errmsg("partition key expressions cannot contain system column references")));
19893
19894 /*
19895 * Stored generated columns cannot work: They are computed
19896 * after BEFORE triggers, but partition routing is done before
19897 * all triggers. Virtual generated columns could probably
19898 * work, but it would require more work elsewhere (for example
19899 * SET EXPRESSION would need to check whether the column is
19900 * used in partition keys). Seems safer to prohibit for now.
19901 */
19902 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19903 ereport(ERROR,
19904 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19905 errmsg("cannot use generated column in partition key"),
19906 errdetail("Column \"%s\" is a generated column.",
19907 get_attname(RelationGetRelid(rel), attno, false)),
19908 parser_errposition(pstate, pelem->location)));
19909 }
19910
19911 if (IsA(expr, Var) &&
19912 ((Var *) expr)->varattno > 0)
19913 {
19914
19915 /*
19916 * User wrote "(column)" or "(column COLLATE something)".
19917 * Treat it like simple attribute anyway.
19918 */
19919 partattrs[attn] = ((Var *) expr)->varattno;
19920 }
19921 else
19922 {
19923 partattrs[attn] = 0; /* marks the column as expression */
19924 *partexprs = lappend(*partexprs, expr);
19925
19926 /*
19927 * transformPartitionSpec() should have already rejected
19928 * subqueries, aggregates, window functions, and SRFs, based
19929 * on the EXPR_KIND_ for partition expressions.
19930 */
19931
19932 /*
19933 * Preprocess the expression before checking for mutability.
19934 * This is essential for the reasons described in
19935 * contain_mutable_functions_after_planning. However, we call
19936 * expression_planner for ourselves rather than using that
19937 * function, because if constant-folding reduces the
19938 * expression to a constant, we'd like to know that so we can
19939 * complain below.
19940 *
19941 * Like contain_mutable_functions_after_planning, assume that
19942 * expression_planner won't scribble on its input, so this
19943 * won't affect the partexprs entry we saved above.
19944 */
19945 expr = (Node *) expression_planner((Expr *) expr);
19946
19947 /*
19948 * Partition expressions cannot contain mutable functions,
19949 * because a given row must always map to the same partition
19950 * as long as there is no change in the partition boundary
19951 * structure.
19952 */
19953 if (contain_mutable_functions(expr))
19954 ereport(ERROR,
19955 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19956 errmsg("functions in partition key expression must be marked IMMUTABLE")));
19957
19958 /*
19959 * While it is not exactly *wrong* for a partition expression
19960 * to be a constant, it seems better to reject such keys.
19961 */
19962 if (IsA(expr, Const))
19963 ereport(ERROR,
19964 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19965 errmsg("cannot use constant expression as partition key")));
19966 }
19967 }
19968
19969 /*
19970 * Apply collation override if any
19971 */
19972 if (pelem->collation)
19973 attcollation = get_collation_oid(pelem->collation, false);
19974
19975 /*
19976 * Check we have a collation iff it's a collatable type. The only
19977 * expected failures here are (1) COLLATE applied to a noncollatable
19978 * type, or (2) partition expression had an unresolved collation. But
19979 * we might as well code this to be a complete consistency check.
19980 */
19981 if (type_is_collatable(atttype))
19982 {
19983 if (!OidIsValid(attcollation))
19984 ereport(ERROR,
19985 (errcode(ERRCODE_INDETERMINATE_COLLATION),
19986 errmsg("could not determine which collation to use for partition expression"),
19987 errhint("Use the COLLATE clause to set the collation explicitly.")));
19988 }
19989 else
19990 {
19991 if (OidIsValid(attcollation))
19992 ereport(ERROR,
19993 (errcode(ERRCODE_DATATYPE_MISMATCH),
19994 errmsg("collations are not supported by type %s",
19995 format_type_be(atttype))));
19996 }
19997
19998 partcollation[attn] = attcollation;
19999
20000 /*
20001 * Identify the appropriate operator class. For list and range
20002 * partitioning, we use a btree operator class; hash partitioning uses
20003 * a hash operator class.
20004 */
20005 if (strategy == PARTITION_STRATEGY_HASH)
20006 am_oid = HASH_AM_OID;
20007 else
20008 am_oid = BTREE_AM_OID;
20009
20010 if (!pelem->opclass)
20011 {
20012 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20013
20014 if (!OidIsValid(partopclass[attn]))
20015 {
20016 if (strategy == PARTITION_STRATEGY_HASH)
20017 ereport(ERROR,
20018 (errcode(ERRCODE_UNDEFINED_OBJECT),
20019 errmsg("data type %s has no default operator class for access method \"%s\"",
20020 format_type_be(atttype), "hash"),
20021 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20022 else
20023 ereport(ERROR,
20024 (errcode(ERRCODE_UNDEFINED_OBJECT),
20025 errmsg("data type %s has no default operator class for access method \"%s\"",
20026 format_type_be(atttype), "btree"),
20027 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20028 }
20029 }
20030 else
20031 partopclass[attn] = ResolveOpClass(pelem->opclass,
20032 atttype,
20033 am_oid == HASH_AM_OID ? "hash" : "btree",
20034 am_oid);
20035
20036 attn++;
20037 }
20038}
20039
20040/*
20041 * PartConstraintImpliedByRelConstraint
20042 * Do scanrel's existing constraints imply the partition constraint?
20043 *
20044 * "Existing constraints" include its check constraints and column-level
20045 * not-null constraints. partConstraint describes the partition constraint,
20046 * in implicit-AND form.
20047 */
20048bool
20050 List *partConstraint)
20051{
20052 List *existConstraint = NIL;
20053 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20054 int i;
20055
20056 if (constr && constr->has_not_null)
20057 {
20058 int natts = scanrel->rd_att->natts;
20059
20060 for (i = 1; i <= natts; i++)
20061 {
20062 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20063
20064 /* invalid not-null constraint must be ignored here */
20065 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20066 {
20067 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20068 NullTest *ntest = makeNode(NullTest);
20069
20070 ntest->arg = (Expr *) makeVar(1,
20071 i,
20072 wholeatt->atttypid,
20073 wholeatt->atttypmod,
20074 wholeatt->attcollation,
20075 0);
20076 ntest->nulltesttype = IS_NOT_NULL;
20077
20078 /*
20079 * argisrow=false is correct even for a composite column,
20080 * because attnotnull does not represent a SQL-spec IS NOT
20081 * NULL test in such a case, just IS DISTINCT FROM NULL.
20082 */
20083 ntest->argisrow = false;
20084 ntest->location = -1;
20085 existConstraint = lappend(existConstraint, ntest);
20086 }
20087 }
20088 }
20089
20090 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20091}
20092
20093/*
20094 * ConstraintImpliedByRelConstraint
20095 * Do scanrel's existing constraints imply the given constraint?
20096 *
20097 * testConstraint is the constraint to validate. provenConstraint is a
20098 * caller-provided list of conditions which this function may assume
20099 * to be true. Both provenConstraint and testConstraint must be in
20100 * implicit-AND form, must only contain immutable clauses, and must
20101 * contain only Vars with varno = 1.
20102 */
20103bool
20104ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20105{
20106 List *existConstraint = list_copy(provenConstraint);
20107 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20108 int num_check,
20109 i;
20110
20111 num_check = (constr != NULL) ? constr->num_check : 0;
20112 for (i = 0; i < num_check; i++)
20113 {
20114 Node *cexpr;
20115
20116 /*
20117 * If this constraint hasn't been fully validated yet, we must ignore
20118 * it here.
20119 */
20120 if (!constr->check[i].ccvalid)
20121 continue;
20122
20123 /*
20124 * NOT ENFORCED constraints are always marked as invalid, which should
20125 * have been ignored.
20126 */
20127 Assert(constr->check[i].ccenforced);
20128
20129 cexpr = stringToNode(constr->check[i].ccbin);
20130
20131 /*
20132 * Run each expression through const-simplification and
20133 * canonicalization. It is necessary, because we will be comparing it
20134 * to similarly-processed partition constraint expressions, and may
20135 * fail to detect valid matches without this.
20136 */
20137 cexpr = eval_const_expressions(NULL, cexpr);
20138 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20139
20140 existConstraint = list_concat(existConstraint,
20141 make_ands_implicit((Expr *) cexpr));
20142 }
20143
20144 /*
20145 * Try to make the proof. Since we are comparing CHECK constraints, we
20146 * need to use weak implication, i.e., we assume existConstraint is
20147 * not-false and try to prove the same for testConstraint.
20148 *
20149 * Note that predicate_implied_by assumes its first argument is known
20150 * immutable. That should always be true for both NOT NULL and partition
20151 * constraints, so we don't test it here.
20152 */
20153 return predicate_implied_by(testConstraint, existConstraint, true);
20154}
20155
20156/*
20157 * QueuePartitionConstraintValidation
20158 *
20159 * Add an entry to wqueue to have the given partition constraint validated by
20160 * Phase 3, for the given relation, and all its children.
20161 *
20162 * We first verify whether the given constraint is implied by pre-existing
20163 * relation constraints; if it is, there's no need to scan the table to
20164 * validate, so don't queue in that case.
20165 */
20166static void
20168 List *partConstraint,
20169 bool validate_default)
20170{
20171 /*
20172 * Based on the table's existing constraints, determine whether or not we
20173 * may skip scanning the table.
20174 */
20175 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20176 {
20177 if (!validate_default)
20179 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20180 RelationGetRelationName(scanrel))));
20181 else
20183 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20184 RelationGetRelationName(scanrel))));
20185 return;
20186 }
20187
20188 /*
20189 * Constraints proved insufficient. For plain relations, queue a
20190 * validation item now; for partitioned tables, recurse to process each
20191 * partition.
20192 */
20193 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20194 {
20195 AlteredTableInfo *tab;
20196
20197 /* Grab a work queue entry. */
20198 tab = ATGetQueueEntry(wqueue, scanrel);
20199 Assert(tab->partition_constraint == NULL);
20200 tab->partition_constraint = (Expr *) linitial(partConstraint);
20201 tab->validate_default = validate_default;
20202 }
20203 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20204 {
20205 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20206 int i;
20207
20208 for (i = 0; i < partdesc->nparts; i++)
20209 {
20210 Relation part_rel;
20211 List *thisPartConstraint;
20212
20213 /*
20214 * This is the minimum lock we need to prevent deadlocks.
20215 */
20216 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20217
20218 /*
20219 * Adjust the constraint for scanrel so that it matches this
20220 * partition's attribute numbers.
20221 */
20222 thisPartConstraint =
20223 map_partition_varattnos(partConstraint, 1,
20224 part_rel, scanrel);
20225
20226 QueuePartitionConstraintValidation(wqueue, part_rel,
20227 thisPartConstraint,
20228 validate_default);
20229 table_close(part_rel, NoLock); /* keep lock till commit */
20230 }
20231 }
20232}
20233
20234/*
20235 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20236 *
20237 * Return the address of the newly attached partition.
20238 */
20239static ObjectAddress
20241 AlterTableUtilityContext *context)
20242{
20243 Relation attachrel,
20244 catalog;
20245 List *attachrel_children;
20246 List *partConstraint;
20247 SysScanDesc scan;
20248 ScanKeyData skey;
20249 AttrNumber attno;
20250 int natts;
20251 TupleDesc tupleDesc;
20252 ObjectAddress address;
20253 const char *trigger_name;
20254 Oid defaultPartOid;
20255 List *partBoundConstraint;
20256 ParseState *pstate = make_parsestate(NULL);
20257
20258 pstate->p_sourcetext = context->queryString;
20259
20260 /*
20261 * We must lock the default partition if one exists, because attaching a
20262 * new partition will change its partition constraint.
20263 */
20264 defaultPartOid =
20266 if (OidIsValid(defaultPartOid))
20267 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20268
20269 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20270
20271 /*
20272 * XXX I think it'd be a good idea to grab locks on all tables referenced
20273 * by FKs at this point also.
20274 */
20275
20276 /*
20277 * Must be owner of both parent and source table -- parent was checked by
20278 * ATSimplePermissions call in ATPrepCmd
20279 */
20282
20283 /* A partition can only have one parent */
20284 if (attachrel->rd_rel->relispartition)
20285 ereport(ERROR,
20286 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20287 errmsg("\"%s\" is already a partition",
20288 RelationGetRelationName(attachrel))));
20289
20290 if (OidIsValid(attachrel->rd_rel->reloftype))
20291 ereport(ERROR,
20292 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20293 errmsg("cannot attach a typed table as partition")));
20294
20295 /*
20296 * Table being attached should not already be part of inheritance; either
20297 * as a child table...
20298 */
20299 catalog = table_open(InheritsRelationId, AccessShareLock);
20300 ScanKeyInit(&skey,
20301 Anum_pg_inherits_inhrelid,
20302 BTEqualStrategyNumber, F_OIDEQ,
20304 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20305 NULL, 1, &skey);
20307 ereport(ERROR,
20308 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20309 errmsg("cannot attach inheritance child as partition")));
20310 systable_endscan(scan);
20311
20312 /* ...or as a parent table (except the case when it is partitioned) */
20313 ScanKeyInit(&skey,
20314 Anum_pg_inherits_inhparent,
20315 BTEqualStrategyNumber, F_OIDEQ,
20317 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20318 1, &skey);
20320 attachrel->rd_rel->relkind == RELKIND_RELATION)
20321 ereport(ERROR,
20322 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20323 errmsg("cannot attach inheritance parent as partition")));
20324 systable_endscan(scan);
20325 table_close(catalog, AccessShareLock);
20326
20327 /*
20328 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20329 * particular, this disallows making a rel a partition of itself.)
20330 *
20331 * We do that by checking if rel is a member of the list of attachrel's
20332 * partitions provided the latter is partitioned at all. We want to avoid
20333 * having to construct this list again, so we request the strongest lock
20334 * on all partitions. We need the strongest lock, because we may decide
20335 * to scan them if we find out that the table being attached (or its leaf
20336 * partitions) may contain rows that violate the partition constraint. If
20337 * the table has a constraint that would prevent such rows, which by
20338 * definition is present in all the partitions, we need not scan the
20339 * table, nor its partitions. But we cannot risk a deadlock by taking a
20340 * weaker lock now and the stronger one only when needed.
20341 */
20342 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20343 AccessExclusiveLock, NULL);
20344 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20345 ereport(ERROR,
20346 (errcode(ERRCODE_DUPLICATE_TABLE),
20347 errmsg("circular inheritance not allowed"),
20348 errdetail("\"%s\" is already a child of \"%s\".",
20350 RelationGetRelationName(attachrel))));
20351
20352 /* If the parent is permanent, so must be all of its partitions. */
20353 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20354 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20355 ereport(ERROR,
20356 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20357 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20359
20360 /* Temp parent cannot have a partition that is itself not a temp */
20361 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20362 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20363 ereport(ERROR,
20364 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20365 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20367
20368 /* If the parent is temp, it must belong to this session */
20369 if (RELATION_IS_OTHER_TEMP(rel))
20370 ereport(ERROR,
20371 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20372 errmsg("cannot attach as partition of temporary relation of another session")));
20373
20374 /* Ditto for the partition */
20375 if (RELATION_IS_OTHER_TEMP(attachrel))
20376 ereport(ERROR,
20377 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20378 errmsg("cannot attach temporary relation of another session as partition")));
20379
20380 /*
20381 * Check if attachrel has any identity columns or any columns that aren't
20382 * in the parent.
20383 */
20384 tupleDesc = RelationGetDescr(attachrel);
20385 natts = tupleDesc->natts;
20386 for (attno = 1; attno <= natts; attno++)
20387 {
20388 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20389 char *attributeName = NameStr(attribute->attname);
20390
20391 /* Ignore dropped */
20392 if (attribute->attisdropped)
20393 continue;
20394
20395 if (attribute->attidentity)
20396 ereport(ERROR,
20397 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20398 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20399 RelationGetRelationName(attachrel), attributeName),
20400 errdetail("The new partition may not contain an identity column."));
20401
20402 /* Try to find the column in parent (matching on column name) */
20403 if (!SearchSysCacheExists2(ATTNAME,
20405 CStringGetDatum(attributeName)))
20406 ereport(ERROR,
20407 (errcode(ERRCODE_DATATYPE_MISMATCH),
20408 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20409 RelationGetRelationName(attachrel), attributeName,
20411 errdetail("The new partition may contain only the columns present in parent.")));
20412 }
20413
20414 /*
20415 * If child_rel has row-level triggers with transition tables, we
20416 * currently don't allow it to become a partition. See also prohibitions
20417 * in ATExecAddInherit() and CreateTrigger().
20418 */
20419 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20420 if (trigger_name != NULL)
20421 ereport(ERROR,
20422 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20423 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20424 trigger_name, RelationGetRelationName(attachrel)),
20425 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20426
20427 /*
20428 * Check that the new partition's bound is valid and does not overlap any
20429 * of existing partitions of the parent - note that it does not return on
20430 * error.
20431 */
20433 cmd->bound, pstate);
20434
20435 /* OK to create inheritance. Rest of the checks performed there */
20436 CreateInheritance(attachrel, rel, true);
20437
20438 /* Update the pg_class entry. */
20439 StorePartitionBound(attachrel, rel, cmd->bound);
20440
20441 /* Ensure there exists a correct set of indexes in the partition. */
20442 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20443
20444 /* and triggers */
20445 CloneRowTriggersToPartition(rel, attachrel);
20446
20447 /*
20448 * Clone foreign key constraints. Callee is responsible for setting up
20449 * for phase 3 constraint verification.
20450 */
20451 CloneForeignKeyConstraints(wqueue, rel, attachrel);
20452
20453 /*
20454 * Generate partition constraint from the partition bound specification.
20455 * If the parent itself is a partition, make sure to include its
20456 * constraint as well.
20457 */
20458 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20459
20460 /*
20461 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20462 * since it's needed later to construct the constraint expression for
20463 * validating against the default partition, if any.
20464 */
20465 partConstraint = list_concat_copy(partBoundConstraint,
20467
20468 /* Skip validation if there are no constraints to validate. */
20469 if (partConstraint)
20470 {
20471 /*
20472 * Run the partition quals through const-simplification similar to
20473 * check constraints. We skip canonicalize_qual, though, because
20474 * partition quals should be in canonical form already.
20475 */
20476 partConstraint =
20478 (Node *) partConstraint);
20479
20480 /* XXX this sure looks wrong */
20481 partConstraint = list_make1(make_ands_explicit(partConstraint));
20482
20483 /*
20484 * Adjust the generated constraint to match this partition's attribute
20485 * numbers.
20486 */
20487 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20488 rel);
20489
20490 /* Validate partition constraints against the table being attached. */
20491 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20492 false);
20493 }
20494
20495 /*
20496 * If we're attaching a partition other than the default partition and a
20497 * default one exists, then that partition's partition constraint changes,
20498 * so add an entry to the work queue to validate it, too. (We must not do
20499 * this when the partition being attached is the default one; we already
20500 * did it above!)
20501 */
20502 if (OidIsValid(defaultPartOid))
20503 {
20504 Relation defaultrel;
20505 List *defPartConstraint;
20506
20507 Assert(!cmd->bound->is_default);
20508
20509 /* we already hold a lock on the default partition */
20510 defaultrel = table_open(defaultPartOid, NoLock);
20511 defPartConstraint =
20512 get_proposed_default_constraint(partBoundConstraint);
20513
20514 /*
20515 * Map the Vars in the constraint expression from rel's attnos to
20516 * defaultrel's.
20517 */
20518 defPartConstraint =
20519 map_partition_varattnos(defPartConstraint,
20520 1, defaultrel, rel);
20521 QueuePartitionConstraintValidation(wqueue, defaultrel,
20522 defPartConstraint, true);
20523
20524 /* keep our lock until commit. */
20525 table_close(defaultrel, NoLock);
20526 }
20527
20528 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20529
20530 /*
20531 * If the partition we just attached is partitioned itself, invalidate
20532 * relcache for all descendent partitions too to ensure that their
20533 * rd_partcheck expression trees are rebuilt; partitions already locked at
20534 * the beginning of this function.
20535 */
20536 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20537 {
20538 ListCell *l;
20539
20540 foreach(l, attachrel_children)
20541 {
20543 }
20544 }
20545
20546 /* keep our lock until commit */
20547 table_close(attachrel, NoLock);
20548
20549 return address;
20550}
20551
20552/*
20553 * AttachPartitionEnsureIndexes
20554 * subroutine for ATExecAttachPartition to create/match indexes
20555 *
20556 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20557 * PARTITION: every partition must have an index attached to each index on the
20558 * partitioned table.
20559 */
20560static void
20562{
20563 List *idxes;
20564 List *attachRelIdxs;
20565 Relation *attachrelIdxRels;
20566 IndexInfo **attachInfos;
20567 ListCell *cell;
20568 MemoryContext cxt;
20569 MemoryContext oldcxt;
20570
20572 "AttachPartitionEnsureIndexes",
20574 oldcxt = MemoryContextSwitchTo(cxt);
20575
20576 idxes = RelationGetIndexList(rel);
20577 attachRelIdxs = RelationGetIndexList(attachrel);
20578 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20579 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20580
20581 /* Build arrays of all existing indexes and their IndexInfos */
20582 foreach_oid(cldIdxId, attachRelIdxs)
20583 {
20584 int i = foreach_current_index(cldIdxId);
20585
20586 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20587 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20588 }
20589
20590 /*
20591 * If we're attaching a foreign table, we must fail if any of the indexes
20592 * is a constraint index; otherwise, there's nothing to do here. Do this
20593 * before starting work, to avoid wasting the effort of building a few
20594 * non-unique indexes before coming across a unique one.
20595 */
20596 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20597 {
20598 foreach(cell, idxes)
20599 {
20600 Oid idx = lfirst_oid(cell);
20602
20603 if (idxRel->rd_index->indisunique ||
20604 idxRel->rd_index->indisprimary)
20605 ereport(ERROR,
20606 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20607 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20608 RelationGetRelationName(attachrel),
20610 errdetail("Partitioned table \"%s\" contains unique indexes.",
20613 }
20614
20615 goto out;
20616 }
20617
20618 /*
20619 * For each index on the partitioned table, find a matching one in the
20620 * partition-to-be; if one is not found, create one.
20621 */
20622 foreach(cell, idxes)
20623 {
20624 Oid idx = lfirst_oid(cell);
20626 IndexInfo *info;
20627 AttrMap *attmap;
20628 bool found = false;
20629 Oid constraintOid;
20630
20631 /*
20632 * Ignore indexes in the partitioned table other than partitioned
20633 * indexes.
20634 */
20635 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20636 {
20638 continue;
20639 }
20640
20641 /* construct an indexinfo to compare existing indexes against */
20642 info = BuildIndexInfo(idxRel);
20643 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20644 RelationGetDescr(rel),
20645 false);
20647
20648 /*
20649 * Scan the list of existing indexes in the partition-to-be, and mark
20650 * the first matching, valid, unattached one we find, if any, as
20651 * partition of the parent index. If we find one, we're done.
20652 */
20653 for (int i = 0; i < list_length(attachRelIdxs); i++)
20654 {
20655 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20656 Oid cldConstrOid = InvalidOid;
20657
20658 /* does this index have a parent? if so, can't use it */
20659 if (attachrelIdxRels[i]->rd_rel->relispartition)
20660 continue;
20661
20662 /* If this index is invalid, can't use it */
20663 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20664 continue;
20665
20666 if (CompareIndexInfo(attachInfos[i], info,
20667 attachrelIdxRels[i]->rd_indcollation,
20668 idxRel->rd_indcollation,
20669 attachrelIdxRels[i]->rd_opfamily,
20670 idxRel->rd_opfamily,
20671 attmap))
20672 {
20673 /*
20674 * If this index is being created in the parent because of a
20675 * constraint, then the child needs to have a constraint also,
20676 * so look for one. If there is no such constraint, this
20677 * index is no good, so keep looking.
20678 */
20679 if (OidIsValid(constraintOid))
20680 {
20681 cldConstrOid =
20683 cldIdxId);
20684 /* no dice */
20685 if (!OidIsValid(cldConstrOid))
20686 continue;
20687
20688 /* Ensure they're both the same type of constraint */
20689 if (get_constraint_type(constraintOid) !=
20690 get_constraint_type(cldConstrOid))
20691 continue;
20692 }
20693
20694 /* bingo. */
20695 IndexSetParentIndex(attachrelIdxRels[i], idx);
20696 if (OidIsValid(constraintOid))
20697 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20698 RelationGetRelid(attachrel));
20699 found = true;
20700
20702 break;
20703 }
20704 }
20705
20706 /*
20707 * If no suitable index was found in the partition-to-be, create one
20708 * now. Note that if this is a PK, not-null constraints must already
20709 * exist.
20710 */
20711 if (!found)
20712 {
20713 IndexStmt *stmt;
20714 Oid conOid;
20715
20717 idxRel, attmap,
20718 &conOid);
20720 RelationGetRelid(idxRel),
20721 conOid,
20722 -1,
20723 true, false, false, false, false);
20724 }
20725
20727 }
20728
20729out:
20730 /* Clean up. */
20731 for (int i = 0; i < list_length(attachRelIdxs); i++)
20732 index_close(attachrelIdxRels[i], AccessShareLock);
20733 MemoryContextSwitchTo(oldcxt);
20735}
20736
20737/*
20738 * CloneRowTriggersToPartition
20739 * subroutine for ATExecAttachPartition/DefineRelation to create row
20740 * triggers on partitions
20741 */
20742static void
20744{
20745 Relation pg_trigger;
20747 SysScanDesc scan;
20748 HeapTuple tuple;
20749 MemoryContext perTupCxt;
20750
20751 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20752 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20753 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20754 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20755 true, NULL, 1, &key);
20756
20758 "clone trig", ALLOCSET_SMALL_SIZES);
20759
20760 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20761 {
20762 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20763 CreateTrigStmt *trigStmt;
20764 Node *qual = NULL;
20765 Datum value;
20766 bool isnull;
20767 List *cols = NIL;
20768 List *trigargs = NIL;
20769 MemoryContext oldcxt;
20770
20771 /*
20772 * Ignore statement-level triggers; those are not cloned.
20773 */
20774 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20775 continue;
20776
20777 /*
20778 * Don't clone internal triggers, because the constraint cloning code
20779 * will.
20780 */
20781 if (trigForm->tgisinternal)
20782 continue;
20783
20784 /*
20785 * Complain if we find an unexpected trigger type.
20786 */
20787 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20788 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20789 elog(ERROR, "unexpected trigger \"%s\" found",
20790 NameStr(trigForm->tgname));
20791
20792 /* Use short-lived context for CREATE TRIGGER */
20793 oldcxt = MemoryContextSwitchTo(perTupCxt);
20794
20795 /*
20796 * If there is a WHEN clause, generate a 'cooked' version of it that's
20797 * appropriate for the partition.
20798 */
20799 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20800 RelationGetDescr(pg_trigger), &isnull);
20801 if (!isnull)
20802 {
20804 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20805 partition, parent);
20806 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20807 partition, parent);
20808 }
20809
20810 /*
20811 * If there is a column list, transform it to a list of column names.
20812 * Note we don't need to map this list in any way ...
20813 */
20814 if (trigForm->tgattr.dim1 > 0)
20815 {
20816 int i;
20817
20818 for (i = 0; i < trigForm->tgattr.dim1; i++)
20819 {
20821
20822 col = TupleDescAttr(parent->rd_att,
20823 trigForm->tgattr.values[i] - 1);
20824 cols = lappend(cols,
20825 makeString(pstrdup(NameStr(col->attname))));
20826 }
20827 }
20828
20829 /* Reconstruct trigger arguments list. */
20830 if (trigForm->tgnargs > 0)
20831 {
20832 char *p;
20833
20834 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20835 RelationGetDescr(pg_trigger), &isnull);
20836 if (isnull)
20837 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20838 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20839
20840 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20841
20842 for (int i = 0; i < trigForm->tgnargs; i++)
20843 {
20844 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20845 p += strlen(p) + 1;
20846 }
20847 }
20848
20849 trigStmt = makeNode(CreateTrigStmt);
20850 trigStmt->replace = false;
20851 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20852 trigStmt->trigname = NameStr(trigForm->tgname);
20853 trigStmt->relation = NULL;
20854 trigStmt->funcname = NULL; /* passed separately */
20855 trigStmt->args = trigargs;
20856 trigStmt->row = true;
20857 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20858 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20859 trigStmt->columns = cols;
20860 trigStmt->whenClause = NULL; /* passed separately */
20861 trigStmt->transitionRels = NIL; /* not supported at present */
20862 trigStmt->deferrable = trigForm->tgdeferrable;
20863 trigStmt->initdeferred = trigForm->tginitdeferred;
20864 trigStmt->constrrel = NULL; /* passed separately */
20865
20866 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20867 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20868 trigForm->tgfoid, trigForm->oid, qual,
20869 false, true, trigForm->tgenabled);
20870
20871 MemoryContextSwitchTo(oldcxt);
20872 MemoryContextReset(perTupCxt);
20873 }
20874
20875 MemoryContextDelete(perTupCxt);
20876
20877 systable_endscan(scan);
20878 table_close(pg_trigger, RowExclusiveLock);
20879}
20880
20881/*
20882 * ALTER TABLE DETACH PARTITION
20883 *
20884 * Return the address of the relation that is no longer a partition of rel.
20885 *
20886 * If concurrent mode is requested, we run in two transactions. A side-
20887 * effect is that this command cannot run in a multi-part ALTER TABLE.
20888 * Currently, that's enforced by the grammar.
20889 *
20890 * The strategy for concurrency is to first modify the partition's
20891 * pg_inherit catalog row to make it visible to everyone that the
20892 * partition is detached, lock the partition against writes, and commit
20893 * the transaction; anyone who requests the partition descriptor from
20894 * that point onwards has to ignore such a partition. In a second
20895 * transaction, we wait until all transactions that could have seen the
20896 * partition as attached are gone, then we remove the rest of partition
20897 * metadata (pg_inherits and pg_class.relpartbounds).
20898 */
20899static ObjectAddress
20901 RangeVar *name, bool concurrent)
20902{
20903 Relation partRel;
20904 ObjectAddress address;
20905 Oid defaultPartOid;
20906
20907 /*
20908 * We must lock the default partition, because detaching this partition
20909 * will change its partition constraint.
20910 */
20911 defaultPartOid =
20913 if (OidIsValid(defaultPartOid))
20914 {
20915 /*
20916 * Concurrent detaching when a default partition exists is not
20917 * supported. The main problem is that the default partition
20918 * constraint would change. And there's a definitional problem: what
20919 * should happen to the tuples that are being inserted that belong to
20920 * the partition being detached? Putting them on the partition being
20921 * detached would be wrong, since they'd become "lost" after the
20922 * detaching completes but we cannot put them in the default partition
20923 * either until we alter its partition constraint.
20924 *
20925 * I think we could solve this problem if we effected the constraint
20926 * change before committing the first transaction. But the lock would
20927 * have to remain AEL and it would cause concurrent query planning to
20928 * be blocked, so changing it that way would be even worse.
20929 */
20930 if (concurrent)
20931 ereport(ERROR,
20932 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20933 errmsg("cannot detach partitions concurrently when a default partition exists")));
20934 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20935 }
20936
20937 /*
20938 * In concurrent mode, the partition is locked with share-update-exclusive
20939 * in the first transaction. This allows concurrent transactions to be
20940 * doing DML to the partition.
20941 */
20942 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20944
20945 /*
20946 * Check inheritance conditions and either delete the pg_inherits row (in
20947 * non-concurrent mode) or just set the inhdetachpending flag.
20948 */
20949 if (!concurrent)
20950 RemoveInheritance(partRel, rel, false);
20951 else
20952 MarkInheritDetached(partRel, rel);
20953
20954 /*
20955 * Ensure that foreign keys still hold after this detach. This keeps
20956 * locks on the referencing tables, which prevents concurrent transactions
20957 * from adding rows that we wouldn't see. For this to work in concurrent
20958 * mode, it is critical that the partition appears as no longer attached
20959 * for the RI queries as soon as the first transaction commits.
20960 */
20962
20963 /*
20964 * Concurrent mode has to work harder; first we add a new constraint to
20965 * the partition that matches the partition constraint. Then we close our
20966 * existing transaction, and in a new one wait for all processes to catch
20967 * up on the catalog updates we've done so far; at that point we can
20968 * complete the operation.
20969 */
20970 if (concurrent)
20971 {
20972 Oid partrelid,
20973 parentrelid;
20974 LOCKTAG tag;
20975 char *parentrelname;
20976 char *partrelname;
20977
20978 /*
20979 * We're almost done now; the only traces that remain are the
20980 * pg_inherits tuple and the partition's relpartbounds. Before we can
20981 * remove those, we need to wait until all transactions that know that
20982 * this is a partition are gone.
20983 */
20984
20985 /*
20986 * Remember relation OIDs to re-acquire them later; and relation names
20987 * too, for error messages if something is dropped in between.
20988 */
20989 partrelid = RelationGetRelid(partRel);
20990 parentrelid = RelationGetRelid(rel);
20991 parentrelname = MemoryContextStrdup(PortalContext,
20993 partrelname = MemoryContextStrdup(PortalContext,
20994 RelationGetRelationName(partRel));
20995
20996 /* Invalidate relcache entries for the parent -- must be before close */
20998
20999 table_close(partRel, NoLock);
21000 table_close(rel, NoLock);
21001 tab->rel = NULL;
21002
21003 /* Make updated catalog entry visible */
21006
21008
21009 /*
21010 * Now wait. This ensures that all queries that were planned
21011 * including the partition are finished before we remove the rest of
21012 * catalog entries. We don't need or indeed want to acquire this
21013 * lock, though -- that would block later queries.
21014 *
21015 * We don't need to concern ourselves with waiting for a lock on the
21016 * partition itself, since we will acquire AccessExclusiveLock below.
21017 */
21018 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
21020
21021 /*
21022 * Now acquire locks in both relations again. Note they may have been
21023 * removed in the meantime, so care is required.
21024 */
21025 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
21026 partRel = try_relation_open(partrelid, AccessExclusiveLock);
21027
21028 /* If the relations aren't there, something bad happened; bail out */
21029 if (rel == NULL)
21030 {
21031 if (partRel != NULL) /* shouldn't happen */
21032 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21033 partrelname);
21034 ereport(ERROR,
21035 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21036 errmsg("partitioned table \"%s\" was removed concurrently",
21037 parentrelname)));
21038 }
21039 if (partRel == NULL)
21040 ereport(ERROR,
21041 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21042 errmsg("partition \"%s\" was removed concurrently", partrelname)));
21043
21044 tab->rel = rel;
21045 }
21046
21047 /*
21048 * Detaching the partition might involve TOAST table access, so ensure we
21049 * have a valid snapshot.
21050 */
21052
21053 /* Do the final part of detaching */
21054 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21055
21057
21058 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21059
21060 /* keep our lock until commit */
21061 table_close(partRel, NoLock);
21062
21063 return address;
21064}
21065
21066/*
21067 * Second part of ALTER TABLE .. DETACH.
21068 *
21069 * This is separate so that it can be run independently when the second
21070 * transaction of the concurrent algorithm fails (crash or abort).
21071 */
21072static void
21073DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
21074 Oid defaultPartOid)
21075{
21076 Relation classRel;
21077 List *fks;
21078 ListCell *cell;
21079 List *indexes;
21080 Datum new_val[Natts_pg_class];
21081 bool new_null[Natts_pg_class],
21082 new_repl[Natts_pg_class];
21083 HeapTuple tuple,
21084 newtuple;
21085 Relation trigrel = NULL;
21086 List *fkoids = NIL;
21087
21088 if (concurrent)
21089 {
21090 /*
21091 * We can remove the pg_inherits row now. (In the non-concurrent case,
21092 * this was already done).
21093 */
21094 RemoveInheritance(partRel, rel, true);
21095 }
21096
21097 /* Drop any triggers that were cloned on creation/attach. */
21099
21100 /*
21101 * Detach any foreign keys that are inherited. This includes creating
21102 * additional action triggers.
21103 */
21104 fks = copyObject(RelationGetFKeyList(partRel));
21105 if (fks != NIL)
21106 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21107
21108 /*
21109 * It's possible that the partition being detached has a foreign key that
21110 * references a partitioned table. When that happens, there are multiple
21111 * pg_constraint rows for the partition: one points to the partitioned
21112 * table itself, while the others point to each of its partitions. Only
21113 * the topmost one is to be considered here; the child constraints must be
21114 * left alone, because conceptually those aren't coming from our parent
21115 * partitioned table, but from this partition itself.
21116 *
21117 * We implement this by collecting all the constraint OIDs in a first scan
21118 * of the FK array, and skipping in the loop below those constraints whose
21119 * parents are listed here.
21120 */
21122 fkoids = lappend_oid(fkoids, fk->conoid);
21123
21124 foreach(cell, fks)
21125 {
21126 ForeignKeyCacheInfo *fk = lfirst(cell);
21127 HeapTuple contup;
21128 Form_pg_constraint conform;
21129
21130 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21131 if (!HeapTupleIsValid(contup))
21132 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21133 conform = (Form_pg_constraint) GETSTRUCT(contup);
21134
21135 /*
21136 * Consider only inherited foreign keys, and only if their parents
21137 * aren't in the list.
21138 */
21139 if (conform->contype != CONSTRAINT_FOREIGN ||
21140 !OidIsValid(conform->conparentid) ||
21141 list_member_oid(fkoids, conform->conparentid))
21142 {
21143 ReleaseSysCache(contup);
21144 continue;
21145 }
21146
21147 /*
21148 * The constraint on this table must be marked no longer a child of
21149 * the parent's constraint, as do its check triggers.
21150 */
21152
21153 /*
21154 * Also, look up the partition's "check" triggers corresponding to the
21155 * ENFORCED constraint being detached and detach them from the parent
21156 * triggers. NOT ENFORCED constraints do not have these triggers;
21157 * therefore, this step is not needed.
21158 */
21159 if (fk->conenforced)
21160 {
21161 Oid insertTriggerOid,
21162 updateTriggerOid;
21163
21165 fk->conoid, fk->confrelid, fk->conrelid,
21166 &insertTriggerOid, &updateTriggerOid);
21167 Assert(OidIsValid(insertTriggerOid));
21168 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21169 RelationGetRelid(partRel));
21170 Assert(OidIsValid(updateTriggerOid));
21171 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21172 RelationGetRelid(partRel));
21173 }
21174
21175 /*
21176 * Lastly, create the action triggers on the referenced table, using
21177 * addFkRecurseReferenced, which requires some elaborate setup (so put
21178 * it in a separate block). While at it, if the table is partitioned,
21179 * that function will recurse to create the pg_constraint rows and
21180 * action triggers for each partition.
21181 *
21182 * Note there's no need to do addFkConstraint() here, because the
21183 * pg_constraint row already exists.
21184 */
21185 {
21186 Constraint *fkconstraint;
21187 int numfks;
21188 AttrNumber conkey[INDEX_MAX_KEYS];
21189 AttrNumber confkey[INDEX_MAX_KEYS];
21190 Oid conpfeqop[INDEX_MAX_KEYS];
21191 Oid conppeqop[INDEX_MAX_KEYS];
21192 Oid conffeqop[INDEX_MAX_KEYS];
21193 int numfkdelsetcols;
21194 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21195 Relation refdRel;
21196
21198 &numfks,
21199 conkey,
21200 confkey,
21201 conpfeqop,
21202 conppeqop,
21203 conffeqop,
21204 &numfkdelsetcols,
21205 confdelsetcols);
21206
21207 /* Create a synthetic node we'll use throughout */
21208 fkconstraint = makeNode(Constraint);
21209 fkconstraint->contype = CONSTRAINT_FOREIGN;
21210 fkconstraint->conname = pstrdup(NameStr(conform->conname));
21211 fkconstraint->deferrable = conform->condeferrable;
21212 fkconstraint->initdeferred = conform->condeferred;
21213 fkconstraint->is_enforced = conform->conenforced;
21214 fkconstraint->skip_validation = true;
21215 fkconstraint->initially_valid = conform->convalidated;
21216 /* a few irrelevant fields omitted here */
21217 fkconstraint->pktable = NULL;
21218 fkconstraint->fk_attrs = NIL;
21219 fkconstraint->pk_attrs = NIL;
21220 fkconstraint->fk_matchtype = conform->confmatchtype;
21221 fkconstraint->fk_upd_action = conform->confupdtype;
21222 fkconstraint->fk_del_action = conform->confdeltype;
21223 fkconstraint->fk_del_set_cols = NIL;
21224 fkconstraint->old_conpfeqop = NIL;
21225 fkconstraint->old_pktable_oid = InvalidOid;
21226 fkconstraint->location = -1;
21227
21228 /* set up colnames, used to generate the constraint name */
21229 for (int i = 0; i < numfks; i++)
21230 {
21232
21233 att = TupleDescAttr(RelationGetDescr(partRel),
21234 conkey[i] - 1);
21235
21236 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21237 makeString(NameStr(att->attname)));
21238 }
21239
21241
21242 addFkRecurseReferenced(fkconstraint, partRel,
21243 refdRel,
21244 conform->conindid,
21245 fk->conoid,
21246 numfks,
21247 confkey,
21248 conkey,
21249 conpfeqop,
21250 conppeqop,
21251 conffeqop,
21252 numfkdelsetcols,
21253 confdelsetcols,
21254 true,
21256 conform->conperiod);
21257 table_close(refdRel, NoLock); /* keep lock till end of xact */
21258 }
21259
21260 ReleaseSysCache(contup);
21261 }
21262 list_free_deep(fks);
21263 if (trigrel)
21264 table_close(trigrel, RowExclusiveLock);
21265
21266 /*
21267 * Any sub-constraints that are in the referenced-side of a larger
21268 * constraint have to be removed. This partition is no longer part of the
21269 * key space of the constraint.
21270 */
21271 foreach(cell, GetParentedForeignKeyRefs(partRel))
21272 {
21273 Oid constrOid = lfirst_oid(cell);
21274 ObjectAddress constraint;
21275
21277 deleteDependencyRecordsForClass(ConstraintRelationId,
21278 constrOid,
21279 ConstraintRelationId,
21282
21283 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21284 performDeletion(&constraint, DROP_RESTRICT, 0);
21285 }
21286
21287 /* Now we can detach indexes */
21288 indexes = RelationGetIndexList(partRel);
21289 foreach(cell, indexes)
21290 {
21291 Oid idxid = lfirst_oid(cell);
21292 Oid parentidx;
21293 Relation idx;
21294 Oid constrOid;
21295 Oid parentConstrOid;
21296
21297 if (!has_superclass(idxid))
21298 continue;
21299
21300 parentidx = get_partition_parent(idxid, false);
21301 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21302
21305
21306 /*
21307 * If there's a constraint associated with the index, detach it too.
21308 * Careful: it is possible for a constraint index in a partition to be
21309 * the child of a non-constraint index, so verify whether the parent
21310 * index does actually have a constraint.
21311 */
21313 idxid);
21315 parentidx);
21316 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21318
21320 }
21321
21322 /* Update pg_class tuple */
21323 classRel = table_open(RelationRelationId, RowExclusiveLock);
21324 tuple = SearchSysCacheCopy1(RELOID,
21326 if (!HeapTupleIsValid(tuple))
21327 elog(ERROR, "cache lookup failed for relation %u",
21328 RelationGetRelid(partRel));
21329 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21330
21331 /* Clear relpartbound and reset relispartition */
21332 memset(new_val, 0, sizeof(new_val));
21333 memset(new_null, false, sizeof(new_null));
21334 memset(new_repl, false, sizeof(new_repl));
21335 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21336 new_null[Anum_pg_class_relpartbound - 1] = true;
21337 new_repl[Anum_pg_class_relpartbound - 1] = true;
21338 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21339 new_val, new_null, new_repl);
21340
21341 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21342 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21343 heap_freetuple(newtuple);
21344 table_close(classRel, RowExclusiveLock);
21345
21346 /*
21347 * Drop identity property from all identity columns of partition.
21348 */
21349 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21350 {
21351 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21352
21353 if (!attr->attisdropped && attr->attidentity)
21354 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21355 AccessExclusiveLock, true, true);
21356 }
21357
21358 if (OidIsValid(defaultPartOid))
21359 {
21360 /*
21361 * If the relation being detached is the default partition itself,
21362 * remove it from the parent's pg_partitioned_table entry.
21363 *
21364 * If not, we must invalidate default partition's relcache entry, as
21365 * in StorePartitionBound: its partition constraint depends on every
21366 * other partition's partition constraint.
21367 */
21368 if (RelationGetRelid(partRel) == defaultPartOid)
21370 else
21371 CacheInvalidateRelcacheByRelid(defaultPartOid);
21372 }
21373
21374 /*
21375 * Invalidate the parent's relcache so that the partition is no longer
21376 * included in its partition descriptor.
21377 */
21379
21380 /*
21381 * If the partition we just detached is partitioned itself, invalidate
21382 * relcache for all descendent partitions too to ensure that their
21383 * rd_partcheck expression trees are rebuilt; must lock partitions before
21384 * doing so, using the same lockmode as what partRel has been locked with
21385 * by the caller.
21386 */
21387 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21388 {
21389 List *children;
21390
21391 children = find_all_inheritors(RelationGetRelid(partRel),
21392 AccessExclusiveLock, NULL);
21393 foreach(cell, children)
21394 {
21396 }
21397 }
21398}
21399
21400/*
21401 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21402 *
21403 * To use when a DETACH PARTITION command previously did not run to
21404 * completion; this completes the detaching process.
21405 */
21406static ObjectAddress
21408{
21409 Relation partRel;
21410 ObjectAddress address;
21411 Snapshot snap = GetActiveSnapshot();
21412
21414
21415 /*
21416 * Wait until existing snapshots are gone. This is important if the
21417 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21418 * user could immediately run DETACH FINALIZE without actually waiting for
21419 * existing transactions. We must not complete the detach action until
21420 * all such queries are complete (otherwise we would present them with an
21421 * inconsistent view of catalogs).
21422 */
21423 WaitForOlderSnapshots(snap->xmin, false);
21424
21425 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21426
21427 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21428
21429 table_close(partRel, NoLock);
21430
21431 return address;
21432}
21433
21434/*
21435 * DropClonedTriggersFromPartition
21436 * subroutine for ATExecDetachPartition to remove any triggers that were
21437 * cloned to the partition when it was created-as-partition or attached.
21438 * This undoes what CloneRowTriggersToPartition did.
21439 */
21440static void
21442{
21443 ScanKeyData skey;
21444 SysScanDesc scan;
21445 HeapTuple trigtup;
21446 Relation tgrel;
21447 ObjectAddresses *objects;
21448
21449 objects = new_object_addresses();
21450
21451 /*
21452 * Scan pg_trigger to search for all triggers on this rel.
21453 */
21454 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21455 F_OIDEQ, ObjectIdGetDatum(partitionId));
21456 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21457 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21458 true, NULL, 1, &skey);
21459 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21460 {
21461 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21462 ObjectAddress trig;
21463
21464 /* Ignore triggers that weren't cloned */
21465 if (!OidIsValid(pg_trigger->tgparentid))
21466 continue;
21467
21468 /*
21469 * Ignore internal triggers that are implementation objects of foreign
21470 * keys, because these will be detached when the foreign keys
21471 * themselves are.
21472 */
21473 if (OidIsValid(pg_trigger->tgconstrrelid))
21474 continue;
21475
21476 /*
21477 * This is ugly, but necessary: remove the dependency markings on the
21478 * trigger so that it can be removed.
21479 */
21480 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21481 TriggerRelationId,
21483 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21484 RelationRelationId,
21486
21487 /* remember this trigger to remove it below */
21488 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21489 add_exact_object_address(&trig, objects);
21490 }
21491
21492 /* make the dependency removal visible to the deletion below */
21495
21496 /* done */
21497 free_object_addresses(objects);
21498 systable_endscan(scan);
21500}
21501
21502/*
21503 * Before acquiring lock on an index, acquire the same lock on the owning
21504 * table.
21505 */
21507{
21511};
21512
21513static void
21514RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21515 void *arg)
21516{
21518 Form_pg_class classform;
21519 HeapTuple tuple;
21520
21521 state = (struct AttachIndexCallbackState *) arg;
21522
21523 if (!state->lockedParentTbl)
21524 {
21525 LockRelationOid(state->parentTblOid, AccessShareLock);
21526 state->lockedParentTbl = true;
21527 }
21528
21529 /*
21530 * If we previously locked some other heap, and the name we're looking up
21531 * no longer refers to an index on that relation, release the now-useless
21532 * lock. XXX maybe we should do *after* we verify whether the index does
21533 * not actually belong to the same relation ...
21534 */
21535 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21536 {
21537 UnlockRelationOid(state->partitionOid, AccessShareLock);
21538 state->partitionOid = InvalidOid;
21539 }
21540
21541 /* Didn't find a relation, so no need for locking or permission checks. */
21542 if (!OidIsValid(relOid))
21543 return;
21544
21545 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21546 if (!HeapTupleIsValid(tuple))
21547 return; /* concurrently dropped, so nothing to do */
21548 classform = (Form_pg_class) GETSTRUCT(tuple);
21549 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21550 classform->relkind != RELKIND_INDEX)
21551 ereport(ERROR,
21552 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21553 errmsg("\"%s\" is not an index", rv->relname)));
21554 ReleaseSysCache(tuple);
21555
21556 /*
21557 * Since we need only examine the heap's tupledesc, an access share lock
21558 * on it (preventing any DDL) is sufficient.
21559 */
21560 state->partitionOid = IndexGetRelation(relOid, false);
21561 LockRelationOid(state->partitionOid, AccessShareLock);
21562}
21563
21564/*
21565 * ALTER INDEX i1 ATTACH PARTITION i2
21566 */
21567static ObjectAddress
21569{
21570 Relation partIdx;
21571 Relation partTbl;
21572 Relation parentTbl;
21573 ObjectAddress address;
21574 Oid partIdxId;
21575 Oid currParent;
21577
21578 /*
21579 * We need to obtain lock on the index 'name' to modify it, but we also
21580 * need to read its owning table's tuple descriptor -- so we need to lock
21581 * both. To avoid deadlocks, obtain lock on the table before doing so on
21582 * the index. Furthermore, we need to examine the parent table of the
21583 * partition, so lock that one too.
21584 */
21585 state.partitionOid = InvalidOid;
21586 state.parentTblOid = parentIdx->rd_index->indrelid;
21587 state.lockedParentTbl = false;
21588 partIdxId =
21591 &state);
21592 /* Not there? */
21593 if (!OidIsValid(partIdxId))
21594 ereport(ERROR,
21595 (errcode(ERRCODE_UNDEFINED_OBJECT),
21596 errmsg("index \"%s\" does not exist", name->relname)));
21597
21598 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21599 partIdx = relation_open(partIdxId, AccessExclusiveLock);
21600
21601 /* we already hold locks on both tables, so this is safe: */
21602 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21603 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21604
21605 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21606
21607 /* Silently do nothing if already in the right state */
21608 currParent = partIdx->rd_rel->relispartition ?
21609 get_partition_parent(partIdxId, false) : InvalidOid;
21610 if (currParent != RelationGetRelid(parentIdx))
21611 {
21612 IndexInfo *childInfo;
21613 IndexInfo *parentInfo;
21614 AttrMap *attmap;
21615 bool found;
21616 int i;
21617 PartitionDesc partDesc;
21618 Oid constraintOid,
21619 cldConstrId = InvalidOid;
21620
21621 /*
21622 * If this partition already has an index attached, refuse the
21623 * operation.
21624 */
21625 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21626
21627 if (OidIsValid(currParent))
21628 ereport(ERROR,
21629 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21630 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21631 RelationGetRelationName(partIdx),
21632 RelationGetRelationName(parentIdx)),
21633 errdetail("Index \"%s\" is already attached to another index.",
21634 RelationGetRelationName(partIdx))));
21635
21636 /* Make sure it indexes a partition of the other index's table */
21637 partDesc = RelationGetPartitionDesc(parentTbl, true);
21638 found = false;
21639 for (i = 0; i < partDesc->nparts; i++)
21640 {
21641 if (partDesc->oids[i] == state.partitionOid)
21642 {
21643 found = true;
21644 break;
21645 }
21646 }
21647 if (!found)
21648 ereport(ERROR,
21649 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21650 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21651 RelationGetRelationName(partIdx),
21652 RelationGetRelationName(parentIdx)),
21653 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21654 RelationGetRelationName(partIdx),
21655 RelationGetRelationName(parentTbl))));
21656
21657 /* Ensure the indexes are compatible */
21658 childInfo = BuildIndexInfo(partIdx);
21659 parentInfo = BuildIndexInfo(parentIdx);
21660 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21661 RelationGetDescr(parentTbl),
21662 false);
21663 if (!CompareIndexInfo(childInfo, parentInfo,
21664 partIdx->rd_indcollation,
21665 parentIdx->rd_indcollation,
21666 partIdx->rd_opfamily,
21667 parentIdx->rd_opfamily,
21668 attmap))
21669 ereport(ERROR,
21670 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21671 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21672 RelationGetRelationName(partIdx),
21673 RelationGetRelationName(parentIdx)),
21674 errdetail("The index definitions do not match.")));
21675
21676 /*
21677 * If there is a constraint in the parent, make sure there is one in
21678 * the child too.
21679 */
21680 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21681 RelationGetRelid(parentIdx));
21682
21683 if (OidIsValid(constraintOid))
21684 {
21686 partIdxId);
21687 if (!OidIsValid(cldConstrId))
21688 ereport(ERROR,
21689 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21690 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21691 RelationGetRelationName(partIdx),
21692 RelationGetRelationName(parentIdx)),
21693 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21694 RelationGetRelationName(parentIdx),
21695 RelationGetRelationName(parentTbl),
21696 RelationGetRelationName(partIdx))));
21697 }
21698
21699 /*
21700 * If it's a primary key, make sure the columns in the partition are
21701 * NOT NULL.
21702 */
21703 if (parentIdx->rd_index->indisprimary)
21704 verifyPartitionIndexNotNull(childInfo, partTbl);
21705
21706 /* All good -- do it */
21707 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21708 if (OidIsValid(constraintOid))
21709 ConstraintSetParentConstraint(cldConstrId, constraintOid,
21710 RelationGetRelid(partTbl));
21711
21712 free_attrmap(attmap);
21713
21714 validatePartitionedIndex(parentIdx, parentTbl);
21715 }
21716
21717 relation_close(parentTbl, AccessShareLock);
21718 /* keep these locks till commit */
21719 relation_close(partTbl, NoLock);
21720 relation_close(partIdx, NoLock);
21721
21722 return address;
21723}
21724
21725/*
21726 * Verify whether the given partition already contains an index attached
21727 * to the given partitioned index. If so, raise an error.
21728 */
21729static void
21730refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21731{
21732 Oid existingIdx;
21733
21734 existingIdx = index_get_partition(partitionTbl,
21735 RelationGetRelid(parentIdx));
21736 if (OidIsValid(existingIdx))
21737 ereport(ERROR,
21738 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21739 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21740 RelationGetRelationName(partIdx),
21741 RelationGetRelationName(parentIdx)),
21742 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21743 get_rel_name(existingIdx),
21744 RelationGetRelationName(partitionTbl))));
21745}
21746
21747/*
21748 * Verify whether the set of attached partition indexes to a parent index on
21749 * a partitioned table is complete. If it is, mark the parent index valid.
21750 *
21751 * This should be called each time a partition index is attached.
21752 */
21753static void
21755{
21756 Relation inheritsRel;
21757 SysScanDesc scan;
21759 int tuples = 0;
21760 HeapTuple inhTup;
21761 bool updated = false;
21762
21763 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21764
21765 /*
21766 * Scan pg_inherits for this parent index. Count each valid index we find
21767 * (verifying the pg_index entry for each), and if we reach the total
21768 * amount we expect, we can mark this parent index as valid.
21769 */
21770 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21771 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21772 BTEqualStrategyNumber, F_OIDEQ,
21774 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21775 NULL, 1, &key);
21776 while ((inhTup = systable_getnext(scan)) != NULL)
21777 {
21778 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21779 HeapTuple indTup;
21780 Form_pg_index indexForm;
21781
21782 indTup = SearchSysCache1(INDEXRELID,
21783 ObjectIdGetDatum(inhForm->inhrelid));
21784 if (!HeapTupleIsValid(indTup))
21785 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21786 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21787 if (indexForm->indisvalid)
21788 tuples += 1;
21789 ReleaseSysCache(indTup);
21790 }
21791
21792 /* Done with pg_inherits */
21793 systable_endscan(scan);
21794 table_close(inheritsRel, AccessShareLock);
21795
21796 /*
21797 * If we found as many inherited indexes as the partitioned table has
21798 * partitions, we're good; update pg_index to set indisvalid.
21799 */
21800 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21801 {
21802 Relation idxRel;
21803 HeapTuple indTup;
21804 Form_pg_index indexForm;
21805
21806 idxRel = table_open(IndexRelationId, RowExclusiveLock);
21807 indTup = SearchSysCacheCopy1(INDEXRELID,
21809 if (!HeapTupleIsValid(indTup))
21810 elog(ERROR, "cache lookup failed for index %u",
21811 RelationGetRelid(partedIdx));
21812 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21813
21814 indexForm->indisvalid = true;
21815 updated = true;
21816
21817 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21818
21820 heap_freetuple(indTup);
21821 }
21822
21823 /*
21824 * If this index is in turn a partition of a larger index, validating it
21825 * might cause the parent to become valid also. Try that.
21826 */
21827 if (updated && partedIdx->rd_rel->relispartition)
21828 {
21829 Oid parentIdxId,
21830 parentTblId;
21831 Relation parentIdx,
21832 parentTbl;
21833
21834 /* make sure we see the validation we just did */
21836
21837 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21838 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21839 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21840 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21841 Assert(!parentIdx->rd_index->indisvalid);
21842
21843 validatePartitionedIndex(parentIdx, parentTbl);
21844
21847 }
21848}
21849
21850/*
21851 * When attaching an index as a partition of a partitioned index which is a
21852 * primary key, verify that all the columns in the partition are marked NOT
21853 * NULL.
21854 */
21855static void
21857{
21858 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21859 {
21861 iinfo->ii_IndexAttrNumbers[i] - 1);
21862
21863 if (!att->attnotnull)
21864 ereport(ERROR,
21865 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21866 errmsg("invalid primary key definition"),
21867 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21868 NameStr(att->attname),
21869 RelationGetRelationName(partition)));
21870 }
21871}
21872
21873/*
21874 * Return an OID list of constraints that reference the given relation
21875 * that are marked as having a parent constraints.
21876 */
21877static List *
21879{
21880 Relation pg_constraint;
21881 HeapTuple tuple;
21882 SysScanDesc scan;
21883 ScanKeyData key[2];
21884 List *constraints = NIL;
21885
21886 /*
21887 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21888 * scan.
21889 */
21890 if (RelationGetIndexList(partition) == NIL ||
21893 return NIL;
21894
21895 /* Search for constraints referencing this table */
21896 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21897 ScanKeyInit(&key[0],
21898 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21899 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21900 ScanKeyInit(&key[1],
21901 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21902 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21903
21904 /* XXX This is a seqscan, as we don't have a usable index */
21905 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21906 while ((tuple = systable_getnext(scan)) != NULL)
21907 {
21908 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21909
21910 /*
21911 * We only need to process constraints that are part of larger ones.
21912 */
21913 if (!OidIsValid(constrForm->conparentid))
21914 continue;
21915
21916 constraints = lappend_oid(constraints, constrForm->oid);
21917 }
21918
21919 systable_endscan(scan);
21920 table_close(pg_constraint, AccessShareLock);
21921
21922 return constraints;
21923}
21924
21925/*
21926 * During DETACH PARTITION, verify that any foreign keys pointing to the
21927 * partitioned table would not become invalid. An error is raised if any
21928 * referenced values exist.
21929 */
21930static void
21932{
21933 List *constraints;
21934 ListCell *cell;
21935
21936 constraints = GetParentedForeignKeyRefs(partition);
21937
21938 foreach(cell, constraints)
21939 {
21940 Oid constrOid = lfirst_oid(cell);
21941 HeapTuple tuple;
21942 Form_pg_constraint constrForm;
21943 Relation rel;
21944 Trigger trig = {0};
21945
21946 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21947 if (!HeapTupleIsValid(tuple))
21948 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21949 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21950
21951 Assert(OidIsValid(constrForm->conparentid));
21952 Assert(constrForm->confrelid == RelationGetRelid(partition));
21953
21954 /* prevent data changes into the referencing table until commit */
21955 rel = table_open(constrForm->conrelid, ShareLock);
21956
21957 trig.tgoid = InvalidOid;
21958 trig.tgname = NameStr(constrForm->conname);
21960 trig.tgisinternal = true;
21961 trig.tgconstrrelid = RelationGetRelid(partition);
21962 trig.tgconstrindid = constrForm->conindid;
21963 trig.tgconstraint = constrForm->oid;
21964 trig.tgdeferrable = false;
21965 trig.tginitdeferred = false;
21966 /* we needn't fill in remaining fields */
21967
21968 RI_PartitionRemove_Check(&trig, rel, partition);
21969
21970 ReleaseSysCache(tuple);
21971
21972 table_close(rel, NoLock);
21973 }
21974}
21975
21976/*
21977 * resolve column compression specification to compression method.
21978 */
21979static char
21980GetAttributeCompression(Oid atttypid, const char *compression)
21981{
21982 char cmethod;
21983
21984 if (compression == NULL || strcmp(compression, "default") == 0)
21986
21987 /*
21988 * To specify a nondefault method, the column data type must be toastable.
21989 * Note this says nothing about whether the column's attstorage setting
21990 * permits compression; we intentionally allow attstorage and
21991 * attcompression to be independent. But with a non-toastable type,
21992 * attstorage could not be set to a value that would permit compression.
21993 *
21994 * We don't actually need to enforce this, since nothing bad would happen
21995 * if attcompression were non-default; it would never be consulted. But
21996 * it seems more user-friendly to complain about a certainly-useless
21997 * attempt to set the property.
21998 */
21999 if (!TypeIsToastable(atttypid))
22000 ereport(ERROR,
22001 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22002 errmsg("column data type %s does not support compression",
22003 format_type_be(atttypid))));
22004
22005 cmethod = CompressionNameToMethod(compression);
22006 if (!CompressionMethodIsValid(cmethod))
22007 ereport(ERROR,
22008 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22009 errmsg("invalid compression method \"%s\"", compression)));
22010
22011 return cmethod;
22012}
22013
22014/*
22015 * resolve column storage specification
22016 */
22017static char
22018GetAttributeStorage(Oid atttypid, const char *storagemode)
22019{
22020 char cstorage = 0;
22021
22022 if (pg_strcasecmp(storagemode, "plain") == 0)
22023 cstorage = TYPSTORAGE_PLAIN;
22024 else if (pg_strcasecmp(storagemode, "external") == 0)
22025 cstorage = TYPSTORAGE_EXTERNAL;
22026 else if (pg_strcasecmp(storagemode, "extended") == 0)
22027 cstorage = TYPSTORAGE_EXTENDED;
22028 else if (pg_strcasecmp(storagemode, "main") == 0)
22029 cstorage = TYPSTORAGE_MAIN;
22030 else if (pg_strcasecmp(storagemode, "default") == 0)
22031 cstorage = get_typstorage(atttypid);
22032 else
22033 ereport(ERROR,
22034 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22035 errmsg("invalid storage type \"%s\"",
22036 storagemode)));
22037
22038 /*
22039 * safety check: do not allow toasted storage modes unless column datatype
22040 * is TOAST-aware.
22041 */
22042 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22043 ereport(ERROR,
22044 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22045 errmsg("column data type %s can only have storage PLAIN",
22046 format_type_be(atttypid))));
22047
22048 return cstorage;
22049}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:262
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition: acl.c:1119
void check_can_set_role(Oid member, Oid role)
Definition: acl.c:5341
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition: acl.c:5586
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:2652
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3866
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3834
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4088
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:2971
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4037
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition: amapi.c:161
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:3362
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1821
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:6414
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:1019
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:868
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:4942
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define NameStr(name)
Definition: c.h:756
uint16 bits16
Definition: c.h:551
uint32 SubTransactionId
Definition: c.h:666
#define gettext_noop(x)
Definition: c.h:1200
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:228
#define InvalidSubTransactionId
Definition: c.h:668
int16_t int16
Definition: c.h:538
int32_t int32
Definition: c.h:539
#define MemSet(start, val, len)
Definition: c.h:1024
uint32 CommandId
Definition: c.h:676
#define PG_INT16_MAX
Definition: c.h:596
#define OidIsValid(objectId)
Definition: c.h:779
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:261
bool IsSystemRelation(Relation relation)
Definition: catalog.c:74
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition: catalog.c:557
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:243
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:86
bool contain_mutable_functions(Node *clause)
Definition: clauses.c:374
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2262
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:542
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:266
void SequenceChangePersistence(Oid relid, char newrelpersistence)
Definition: sequence.c:553
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:2619
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2559
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2513
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2799
#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:952
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:358
void hash_destroy(HTAB *hashp)
Definition: dynahash.c:865
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1415
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1380
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1170
int errdetail(const char *fmt,...)
Definition: elog.c:1216
int errhint(const char *fmt,...)
Definition: elog.c:1330
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#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:150
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:1243
AttrNumber ExecRelGenVirtualNotNull(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, List *notnull_virtual_attrs)
Definition: execMain.c:2094
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:192
EState * CreateExecutorState(void)
Definition: execUtils.c:88
struct ResultRelInfo ResultRelInfo
#define GetPerTupleExprContext(estate)
Definition: executor.h:656
#define ResetExprContext(econtext)
Definition: executor.h:650
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:661
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:393
#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:38
FdwRoutine * GetFdwRoutineByServerId(Oid serverid)
Definition: foreign.c:378
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:112
Oid GetForeignServerIdByRelId(Oid relid)
Definition: foreign.c:356
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
Definition: foreigncmds.c:121
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
struct RelationData * Relation
Definition: genam.h:30
bool IsBinaryUpgrade
Definition: globals.c:121
bool allowSystemTableMods
Definition: globals.c:130
Oid MyDatabaseTableSpace
Definition: globals.c:96
Oid MyDatabaseId
Definition: globals.c:94
#define newval
Assert(PointerIsAligned(start, uint64))
for(;;)
void RelationClearMissing(Relation rel)
Definition: heap.c:1964
List * heap_truncate_find_FKs(List *relationIds)
Definition: heap.c:3767
void StorePartitionKey(Relation rel, char strategy, int16 partnatts, AttrNumber *partattrs, List *partexprs, Oid *partopclass, Oid *partcollation)
Definition: heap.c:3894
void RemoveStatistics(Oid relid, AttrNumber attnum)
Definition: heap.c:3492
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:1122
void heap_truncate(List *relids)
Definition: heap.c:3587
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:3672
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition: heap.c:4050
List * AddRelationNotNullConstraints(Relation rel, List *constraints, List *old_notnulls)
Definition: heap.c:2894
List * AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, bool is_local, bool is_internal, const char *queryString)
Definition: heap.c:2385
void InsertPgAttributeTuples(Relation pg_attribute_rel, TupleDesc tupdesc, Oid new_rel_oid, const FormExtraData_pg_attribute tupdesc_extra[], CatalogIndexState indstate)
Definition: heap.c:717
void heap_truncate_one_rel(Relation rel)
Definition: heap.c:3628
void StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
Definition: heap.c:2030
#define CHKATYPE_IS_VIRTUAL
Definition: heap.h:26
#define CHKATYPE_IS_PARTKEY
Definition: heap.h:25
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1346
BulkInsertState GetBulkInsertState(void)
Definition: heapam.c:2021
void FreeBulkInsertState(BulkInsertState bistate)
Definition: heapam.c:2038
#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:541
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4442
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2344
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition: indexcmds.c:177
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:434
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2259
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition: indexing.c:313
void CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
Definition: indexing.c:365
long val
Definition: informix.c:689
static struct @171 value
static bool pg_add_s16_overflow(int16 a, int16 b, int16 *result)
Definition: int.h:67
void AcceptInvalidationMessages(void)
Definition: inval.c:930
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1631
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1687
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1665
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 UnlockTuple(Relation relation, const ItemPointerData *tid, LOCKMODE lockmode)
Definition: lmgr.c:601
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:229
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:911
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
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:183
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:2095
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:951
Oid get_constraint_index(Oid conoid)
Definition: lsyscache.c:1206
char get_typstorage(Oid typid)
Definition: lsyscache.c:2586
bool get_index_isreplident(Oid index_oid)
Definition: lsyscache.c:3722
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2170
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3223
char * get_collation_name(Oid colloid)
Definition: lsyscache.c:1128
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3768
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:168
char * get_constraint_name(Oid conoid)
Definition: lsyscache.c:1174
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:920
bool get_collation_isdeterministic(Oid colloid)
Definition: lsyscache.c:1147
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1420
Oid get_rel_relam(Oid relid)
Definition: lsyscache.c:2267
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3248
Oid get_rel_tablespace(Oid relid)
Definition: lsyscache.c:2221
Oid get_typ_typrelid(Oid typid)
Definition: lsyscache.c:2898
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2705
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2688
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3533
char get_constraint_type(Oid conoid)
Definition: lsyscache.c:1236
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:2052
#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:1746
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:400
char * pstrdup(const char *in)
Definition: mcxt.c:1759
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc0(Size size)
Definition: mcxt.c:1395
void * palloc(Size size)
Definition: mcxt.c:1365
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
MemoryContext CacheMemoryContext
Definition: mcxt.c:169
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
MemoryContext PortalContext
Definition: mcxt.c:175
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:639
Oid GetUserId(void)
Definition: miscinit.c:469
MultiXactId ReadNextMultiXactId(void)
Definition: multixact.c:762
void namestrcpy(Name name, const char *str)
Definition: name.c:233
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition: namespace.c:738
bool isAnyTempNamespace(Oid namespaceId)
Definition: namespace.c:3757
Oid get_collation_oid(List *collname, bool missing_ok)
Definition: namespace.c:4041
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition: namespace.c:3529
RangeVar * makeRangeVarFromNameList(const List *names)
Definition: namespace.c:3624
Oid LookupNamespaceNoError(const char *nspname)
Definition: namespace.c:3425
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:440
@ RVR_MISSING_OK
Definition: namespace.h:90
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:98
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:232
#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
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:558
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:79
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:119
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)
@ AD_AddConstraint
Definition: parsenodes.h:2547
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2820
#define ACL_MAINTAIN
Definition: parsenodes.h:90
#define ACL_USAGE
Definition: parsenodes.h:84
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2823
PartitionStrategy
Definition: parsenodes.h:899
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:902
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:900
ConstrType
Definition: parsenodes.h:2798
@ CONSTR_FOREIGN
Definition: parsenodes.h:2809
@ CONSTR_UNIQUE
Definition: parsenodes.h:2807
@ CONSTR_DEFAULT
Definition: parsenodes.h:2802
@ CONSTR_NOTNULL
Definition: parsenodes.h:2801
@ CONSTR_CHECK
Definition: parsenodes.h:2805
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2808
@ CONSTR_PRIMARY
Definition: parsenodes.h:2806
DropBehavior
Definition: parsenodes.h:2397
@ DROP_CASCADE
Definition: parsenodes.h:2399
@ DROP_RESTRICT
Definition: parsenodes.h:2398
ObjectType
Definition: parsenodes.h:2324
@ OBJECT_MATVIEW
Definition: parsenodes.h:2348
@ OBJECT_SCHEMA
Definition: parsenodes.h:2361
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2343
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2367
@ OBJECT_INDEX
Definition: parsenodes.h:2345
@ OBJECT_SEQUENCE
Definition: parsenodes.h:2362
@ OBJECT_TABLE
Definition: parsenodes.h:2366
@ OBJECT_VIEW
Definition: parsenodes.h:2376
@ OBJECT_TYPE
Definition: parsenodes.h:2374
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2365
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2338
AlterTableType
Definition: parsenodes.h:2416
@ AT_AddIndexConstraint
Definition: parsenodes.h:2438
@ AT_DropOf
Definition: parsenodes.h:2469
@ AT_SetOptions
Definition: parsenodes.h:2426
@ AT_DropIdentity
Definition: parsenodes.h:2481
@ AT_DisableTrigUser
Definition: parsenodes.h:2461
@ AT_DropNotNull
Definition: parsenodes.h:2421
@ AT_AddOf
Definition: parsenodes.h:2468
@ AT_ResetOptions
Definition: parsenodes.h:2427
@ AT_ReplicaIdentity
Definition: parsenodes.h:2470
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2453
@ AT_EnableRowSecurity
Definition: parsenodes.h:2471
@ AT_AddColumnToView
Definition: parsenodes.h:2418
@ AT_ResetRelOptions
Definition: parsenodes.h:2452
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2456
@ AT_DropOids
Definition: parsenodes.h:2448
@ AT_SetIdentity
Definition: parsenodes.h:2480
@ AT_ReAddStatistics
Definition: parsenodes.h:2482
@ AT_SetUnLogged
Definition: parsenodes.h:2447
@ AT_DisableTrig
Definition: parsenodes.h:2457
@ AT_SetCompression
Definition: parsenodes.h:2429
@ AT_DropExpression
Definition: parsenodes.h:2424
@ AT_AddIndex
Definition: parsenodes.h:2431
@ AT_EnableReplicaRule
Definition: parsenodes.h:2464
@ AT_ReAddIndex
Definition: parsenodes.h:2432
@ AT_DropConstraint
Definition: parsenodes.h:2439
@ AT_SetNotNull
Definition: parsenodes.h:2422
@ AT_ClusterOn
Definition: parsenodes.h:2444
@ AT_AddIdentity
Definition: parsenodes.h:2479
@ AT_ForceRowSecurity
Definition: parsenodes.h:2473
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2463
@ AT_SetAccessMethod
Definition: parsenodes.h:2449
@ AT_AlterColumnType
Definition: parsenodes.h:2441
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2478
@ AT_AddInherit
Definition: parsenodes.h:2466
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2435
@ AT_EnableTrig
Definition: parsenodes.h:2454
@ AT_DropColumn
Definition: parsenodes.h:2430
@ AT_ReAddComment
Definition: parsenodes.h:2440
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2442
@ AT_DisableTrigAll
Definition: parsenodes.h:2459
@ AT_EnableRule
Definition: parsenodes.h:2462
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2474
@ AT_DetachPartition
Definition: parsenodes.h:2477
@ AT_SetStatistics
Definition: parsenodes.h:2425
@ AT_AttachPartition
Definition: parsenodes.h:2476
@ AT_AddConstraint
Definition: parsenodes.h:2433
@ AT_DropInherit
Definition: parsenodes.h:2467
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2455
@ AT_SetLogged
Definition: parsenodes.h:2446
@ AT_SetStorage
Definition: parsenodes.h:2428
@ AT_DisableRule
Definition: parsenodes.h:2465
@ AT_DisableRowSecurity
Definition: parsenodes.h:2472
@ AT_SetRelOptions
Definition: parsenodes.h:2451
@ AT_ChangeOwner
Definition: parsenodes.h:2443
@ AT_EnableTrigUser
Definition: parsenodes.h:2460
@ AT_SetExpression
Definition: parsenodes.h:2423
@ AT_ReAddConstraint
Definition: parsenodes.h:2434
@ AT_SetTableSpace
Definition: parsenodes.h:2450
@ AT_GenericOptions
Definition: parsenodes.h:2475
@ AT_ColumnDefault
Definition: parsenodes.h:2419
@ AT_CookedColumnDefault
Definition: parsenodes.h:2420
@ AT_AlterConstraint
Definition: parsenodes.h:2436
@ AT_EnableTrigAll
Definition: parsenodes.h:2458
@ AT_DropCluster
Definition: parsenodes.h:2445
@ AT_ValidateConstraint
Definition: parsenodes.h:2437
@ AT_AddColumn
Definition: parsenodes.h:2417
#define ACL_REFERENCES
Definition: parsenodes.h:81
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2821
#define ACL_TRUNCATE
Definition: parsenodes.h:80
#define ACL_CREATE
Definition: parsenodes.h:85
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2822
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2819
@ 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:2897
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:3252
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:36
Oid GetAttrDefaultOid(Oid relid, AttrNumber attnum)
Definition: pg_attrdef.c:279
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid)
Definition: pg_attrdef.c:321
void RemoveAttrDefault(Oid relid, AttrNumber attnum, DropBehavior behavior, bool complain, bool internal)
Definition: pg_attrdef.c:153
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:763
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:6763
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:332
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:182
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:262
uint64_t Datum
Definition: postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:360
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:222
static Datum CharGetDatum(char X)
Definition: postgres.h:132
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
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:768
@ IS_NOT_NULL
Definition: primnodes.h:1977
@ COERCION_ASSIGNMENT
Definition: primnodes.h:747
@ COERCION_IMPLICIT
Definition: primnodes.h:746
void * stringToNode(const char *str)
Definition: read.c:90
#define RelationGetForm(relation)
Definition: rel.h:509
#define RelationGetRelid(relation)
Definition: rel.h:515
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:711
#define RelationIsUsedAsCatalogTable(relation)
Definition: rel.h:398
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:577
#define RelationGetDescr(relation)
Definition: rel.h:541
#define RelationIsMapped(relation)
Definition: rel.h:564
#define RelationGetNumberOfAttributes(relation)
Definition: rel.h:521
#define RelationGetRelationName(relation)
Definition: rel.h:549
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:668
#define RelationGetNamespace(relation)
Definition: rel.h:556
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:534
#define RelationIsPermanent(relation)
Definition: rel.h:627
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4836
Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
Definition: relcache.c:5047
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:6103
int errtablecol(Relation rel, int attnum)
Definition: relcache.c:6066
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5210
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition: relcache.c:5303
void RelationSetNewRelfilenumber(Relation relation, char persistence)
Definition: relcache.c:3773
List * RelationGetFKeyList(Relation relation)
Definition: relcache.c:4731
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5097
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3976
int errtable(Relation rel)
Definition: relcache.c:6049
@ INDEX_ATTR_BITMAP_KEY
Definition: relcache.h:69
@ INDEX_ATTR_BITMAP_PRIMARY_KEY
Definition: relcache.h:70
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition: relcache.h:71
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1360
bytea * view_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2045
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2101
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2031
Datum transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1176
LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList)
Definition: reloptions.c:2155
bytea * attribute_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2116
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
Definition: reloptions.c:2066
#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:472
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1517
void RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1811
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3208
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition: ruleutils.c:1626
char * pg_get_indexdef_string(Oid indexrelid)
Definition: ruleutils.c:1225
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:13062
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition: ruleutils.c:2674
char * pg_get_constraintdef_command(Oid constraintId)
Definition: ruleutils.c:2183
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:353
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:864
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:680
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:822
void PopActiveSnapshot(void)
Definition: snapmgr.c:773
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:798
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, bool check_rights)
Definition: statscmds.c:63
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:938
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:2493
DropBehavior behavior
Definition: parsenodes.h:2496
AlterTableType subtype
Definition: parsenodes.h:2488
RangeVar * relation
Definition: parsenodes.h:2409
ObjectType objtype
Definition: parsenodes.h:2411
const char * queryString
Definition: utility.h:33
List * constraints
Definition: tablecmds.c:188
bool verify_new_notnull
Definition: tablecmds.c:191
List * changedConstraintDefs
Definition: tablecmds.c:204
Expr * partition_constraint
Definition: tablecmds.c:199
char newrelpersistence
Definition: tablecmds.c:198
List * changedStatisticsDefs
Definition: tablecmds.c:210
bool chgAccessMethod
Definition: tablecmds.c:193
List * afterStmts
Definition: tablecmds.c:190
char * clusterOnIndex
Definition: tablecmds.c:208
char * replicaIdentityIndex
Definition: tablecmds.c:207
List * changedStatisticsOids
Definition: tablecmds.c:209
List * changedIndexDefs
Definition: tablecmds.c:206
List * changedIndexOids
Definition: tablecmds.c:205
List * changedConstraintOids
Definition: tablecmds.c:203
bool validate_default
Definition: tablecmds.c:201
List * subcmds[AT_NUM_PASSES]
Definition: tablecmds.c:186
TupleDesc oldDesc
Definition: tablecmds.c:174
Relation rel
Definition: tablecmds.c:183
Definition: attmap.h:35
AttrNumber * attnums
Definition: attmap.h:36
bool is_not_null
Definition: parsenodes.h:759
char identity
Definition: parsenodes.h:765
RangeVar * identitySequence
Definition: parsenodes.h:766
List * constraints
Definition: parsenodes.h:771
Node * cooked_default
Definition: parsenodes.h:764
char * storage_name
Definition: parsenodes.h:762
char * colname
Definition: parsenodes.h:754
TypeName * typeName
Definition: parsenodes.h:755
char generated
Definition: parsenodes.h:768
bool is_from_type
Definition: parsenodes.h:760
Node * raw_default
Definition: parsenodes.h:763
char storage
Definition: parsenodes.h:761
bool is_local
Definition: parsenodes.h:758
int16 inhcount
Definition: parsenodes.h:757
char * compression
Definition: parsenodes.h:756
ParseLoc location
Definition: parsenodes.h:773
char * comment
Definition: parsenodes.h:3362
ObjectType objtype
Definition: parsenodes.h:3360
Node * object
Definition: parsenodes.h:3361
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:2836
ParseLoc location
Definition: parsenodes.h:2877
bool reset_default_tblspc
Definition: parsenodes.h:2858
List * keys
Definition: parsenodes.h:2848
List * pk_attrs
Definition: parsenodes.h:2866
List * fk_del_set_cols
Definition: parsenodes.h:2872
bool fk_with_period
Definition: parsenodes.h:2867
ConstrType contype
Definition: parsenodes.h:2833
Oid old_pktable_oid
Definition: parsenodes.h:2874
bool is_no_inherit
Definition: parsenodes.h:2840
char fk_upd_action
Definition: parsenodes.h:2870
List * old_conpfeqop
Definition: parsenodes.h:2873
bool is_enforced
Definition: parsenodes.h:2837
char fk_matchtype
Definition: parsenodes.h:2869
bool pk_with_period
Definition: parsenodes.h:2868
bool initially_valid
Definition: parsenodes.h:2839
bool skip_validation
Definition: parsenodes.h:2838
bool deferrable
Definition: parsenodes.h:2835
char * conname
Definition: parsenodes.h:2834
RangeVar * pktable
Definition: parsenodes.h:2864
char fk_del_action
Definition: parsenodes.h:2871
List * fk_attrs
Definition: parsenodes.h:2865
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:3121
List * transitionRels
Definition: parsenodes.h:3123
RangeVar * constrrel
Definition: parsenodes.h:3127
RangeVar * relation
Definition: parsenodes.h:3112
char * defname
Definition: parsenodes.h:843
bool missing_ok
Definition: parsenodes.h:3337
List * objects
Definition: parsenodes.h:3334
ObjectType removeType
Definition: parsenodes.h:3335
bool concurrent
Definition: parsenodes.h:3338
DropBehavior behavior
Definition: parsenodes.h:3336
int es_instrument
Definition: execnodes.h:720
MemoryContext es_query_cxt
Definition: execnodes.h:710
List * es_opened_result_relations
Definition: execnodes.h:688
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:273
ExecForeignTruncate_function ExecForeignTruncate
Definition: fdwapi.h:263
bool conenforced
Definition: rel.h:288
Oid funcid
Definition: primnodes.h:782
List * args
Definition: primnodes.h:800
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86
Definition: dynahash.c:222
ItemPointerData t_self
Definition: htup.h:65
amoptions_function amoptions
Definition: amapi.h:304
bool amcanunique
Definition: amapi.h:258
bool ii_Unique
Definition: execnodes.h:200
int ii_NumIndexKeyAttrs
Definition: execnodes.h:169
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:175
bool reset_default_tblspc
Definition: parsenodes.h:3511
char * idxname
Definition: parsenodes.h:3485
char * idxcomment
Definition: parsenodes.h:3495
Definition: lock.h:167
Definition: pg_list.h:54
AttrNumber attnum
Definition: tablecmds.c:237
bool is_generated
Definition: tablecmds.c:240
ExprState * exprstate
Definition: tablecmds.c:239
char * name
Definition: tablecmds.c:217
ConstrType contype
Definition: tablecmds.c:218
bool conwithperiod
Definition: tablecmds.c:221
Node * qual
Definition: tablecmds.c:223
ExprState * qualstate
Definition: tablecmds.c:224
Definition: nodes.h:135
NullTestType nulltesttype
Definition: primnodes.h:1984
ParseLoc location
Definition: primnodes.h:1987
Expr * arg
Definition: primnodes.h:1983
SubTransactionId creating_subid
Definition: tablecmds.c:128
SubTransactionId deleting_subid
Definition: tablecmds.c:129
OnCommitAction oncommit
Definition: tablecmds.c:119
const char * p_sourcetext
Definition: parse_node.h:195
PartitionBoundSpec * bound
Definition: parsenodes.h:975
RangeVar * name
Definition: parsenodes.h:974
List * collation
Definition: parsenodes.h:893
ParseLoc location
Definition: parsenodes.h:895
List * opclass
Definition: parsenodes.h:894
List * partParams
Definition: parsenodes.h:914
ParseLoc location
Definition: parsenodes.h:915
PartitionStrategy strategy
Definition: parsenodes.h:913
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:2088
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:480
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:129
AttrNumber tts_nvalid
Definition: tuptable.h:119
bool * tts_isnull
Definition: tuptable.h:126
Datum * tts_values
Definition: tuptable.h:124
bool setof
Definition: parsenodes.h:287
List * arrayBounds
Definition: parsenodes.h:291
Definition: primnodes.h:262
const char * skipping_msg
Definition: tablecmds.c:251
int nonexistent_code
Definition: tablecmds.c:249
const char * nonexistent_msg
Definition: tablecmds.c:250
const char * drophint_msg
Definition: tablecmds.c:253
const char * nota_msg
Definition: tablecmds.c:252
Definition: type.h:96
Definition: c.h:736
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:743
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:498
HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
Definition: syscache.c:561
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:264
HeapTuple SearchSysCacheLocked1(int cacheId, Datum key1)
Definition: syscache.c:282
HeapTuple SearchSysCacheLockedCopy1(int cacheId, Datum key1)
Definition: syscache.c:399
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:220
HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum)
Definition: syscache.c:538
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:595
bool SearchSysCacheExistsAttName(Oid relid, const char *attname)
Definition: syscache.c:517
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:230
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:475
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:625
#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
char * default_table_access_method
Definition: tableam.c:49
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, ScanKeyData *key)
Definition: tableam.c:113
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:59
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:985
#define TABLE_INSERT_SKIP_FSM
Definition: tableam.h:259
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, int options, BulkInsertStateData *bistate)
Definition: tableam.h:1367
static void table_finish_bulk_insert(Relation rel, int options)
Definition: tableam.h:1564
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition: tableam.h:1620
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1020
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key)
Definition: tableam.h:876
static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12990
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition: tablecmds.c:3836
void ResetRelRewrite(Oid myrelid)
Definition: tablecmds.c:4355
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
Definition: tablecmds.c:10638
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17490
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17857
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode)
Definition: tablecmds.c:15947
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5294
static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
Definition: tablecmds.c:12755
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17253
ObjectAddress RenameRelation(RenameStmt *stmt)
Definition: tablecmds.c:4198
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6554
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition: tablecmds.c:9570
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:18348
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12190
static ColumnDef * MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3408
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:11030
#define ATT_TABLE
Definition: tablecmds.c:329
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
Definition: tablecmds.c:20104
static const char * storage_name(char c)
Definition: tablecmds.c:2454
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8794
void AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
Definition: tablecmds.c:19449
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition: tablecmds.c:7691
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8118
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
Definition: tablecmds.c:15620
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16845
static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
Definition: tablecmds.c:18154
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
Definition: tablecmds.c:11995
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17940
#define AT_NUM_PASSES
Definition: tablecmds.c:167
void PreCommit_on_commit_actions(void)
Definition: tablecmds.c:19310
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14069
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
Definition: tablecmds.c:11913
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8480
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:21980
static int findAttrByName(const char *attributeName, const List *columns)
Definition: tablecmds.c:3602
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15348
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
Definition: tablecmds.c:14671
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9862
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12900
void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4526
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15399
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
Definition: tablecmds.c:13628
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition: tablecmds.c:7709
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7734
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13477
#define ATT_SEQUENCE
Definition: tablecmds.c:336
void RemoveRelations(DropStmt *drop)
Definition: tablecmds.c:1528
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
Definition: tablecmds.c:16607
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6883
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15261
static List * GetParentedForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21878
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19044
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9788
ObjectAddress renameatt(RenameStmt *stmt)
Definition: tablecmds.c:4001
void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:16064
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:19576
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4441
static void DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
Definition: tablecmds.c:1501
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4600
struct ForeignTruncateInfo ForeignTruncateInfo
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:20900
static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
Definition: tablecmds.c:19166
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:3981
TupleDesc BuildDescForRelation(const List *columns)
Definition: tablecmds.c:1370
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:18624
void AtEOXact_on_commit_actions(bool isCommit)
Definition: tablecmds.c:19417
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:13657
struct OnCommitItem OnCommitItem
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4408
static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12607
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13849
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:17196
static void AttachPartitionForeignKey(List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11782
static List * MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
Definition: tablecmds.c:3159
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition: tablecmds.c:3787
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:21514
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:6853
static void truncate_check_activity(Relation rel)
Definition: tablecmds.c:2431
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:21754
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
Definition: tablecmds.c:12846
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:10059
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15878
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12124
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:10714
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15276
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:9043
static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
Definition: tablecmds.c:12404
static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13212
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:9276
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11439
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9122
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4862
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3685
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:18594
void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName)
Definition: tablecmds.c:6928
static void truncate_check_perms(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2413
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2362
#define ATT_INDEX
Definition: tablecmds.c:332
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:16370
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3557
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
Definition: tablecmds.c:15035
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:8748
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12333
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
Definition: tablecmds.c:21856
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8203
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:4039
static void DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
Definition: tablecmds.c:1453
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
Definition: tablecmds.c:15428
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5703
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19007
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9904
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11238
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17628
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9248
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3639
static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3238
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6731
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6588
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:16637
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8363
ObjectAddress AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
Definition: tablecmds.c:18936
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15292
addFkConstraintSides
Definition: tablecmds.c:355
@ addFkReferencingSide
Definition: tablecmds.c:357
@ addFkBothSides
Definition: tablecmds.c:358
@ addFkReferencedSide
Definition: tablecmds.c:356
void ExecuteTruncate(TruncateStmt *stmt)
Definition: tablecmds.c:1851
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)
Definition: tablecmds.c:18392
#define ATT_FOREIGN_TABLE
Definition: tablecmds.c:334
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:7185
static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, PartitionStrategy strategy)
Definition: tablecmds.c:19775
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:20743
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:20561
static void StoreCatalogInheritance(Oid relationId, List *supers, bool child_is_partition)
Definition: tablecmds.c:3513
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:18653
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15907
struct AlteredTableInfo AlteredTableInfo
Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:4467
static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12804
struct NewConstraint NewConstraint
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8898
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6808
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:21073
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15834
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11209
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:10892
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition: tablecmds.c:7638
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:7209
AlterTablePass
Definition: tablecmds.c:150
@ AT_PASS_ADD_CONSTR
Definition: tablecmds.c:159
@ AT_PASS_ADD_OTHERCONSTR
Definition: tablecmds.c:163
@ AT_PASS_OLD_CONSTR
Definition: tablecmds.c:157
@ AT_PASS_ADD_COL
Definition: tablecmds.c:154
@ AT_PASS_OLD_INDEX
Definition: tablecmds.c:156
@ AT_PASS_ALTER_TYPE
Definition: tablecmds.c:153
@ AT_PASS_ADD_INDEXCONSTR
Definition: tablecmds.c:161
@ AT_PASS_DROP
Definition: tablecmds.c:152
@ AT_PASS_MISC
Definition: tablecmds.c:164
@ AT_PASS_COL_ATTRS
Definition: tablecmds.c:160
@ AT_PASS_SET_EXPRESSION
Definition: tablecmds.c:155
@ AT_PASS_ADD_INDEX
Definition: tablecmds.c:162
@ AT_PASS_UNSET
Definition: tablecmds.c:151
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
Definition: tablecmds.c:14718
static void validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, bool hasperiod)
Definition: tablecmds.c:13686
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17433
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13109
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3742
void remove_on_commit_action(Oid relid)
Definition: tablecmds.c:19287
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16471
#define ATT_PARTITIONED_INDEX
Definition: tablecmds.c:335
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17364
static const struct dropmsgstrings dropmsgstringarray[]
Definition: tablecmds.c:256
static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
Definition: tablecmds.c:18811
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8594
ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, ObjectAddress *typaddress, const char *queryString)
Definition: tablecmds.c:764
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13786
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
Definition: tablecmds.c:12686
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9491
static void DropClonedTriggersFromPartition(Oid partitionId)
Definition: tablecmds.c:21441
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:20167
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:18206
static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:1692
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4555
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12063
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7135
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17815
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21931
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9696
#define ATT_VIEW
Definition: tablecmds.c:330
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19121
#define ATT_PARTITIONED_TABLE
Definition: tablecmds.c:337
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5368
static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12550
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:18480
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13984
static void ATPrepAddInherit(Relation child_rel)
Definition: tablecmds.c:17231
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:21407
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9185
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20240
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:7086
Oid AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
Definition: tablecmds.c:16977
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:13374
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8232
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:16517
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14004
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9612
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:18734
static PartitionSpec * transformPartitionSpec(Relation rel, PartitionSpec *partspec)
Definition: tablecmds.c:19717
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4897
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:17139
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:7905
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4262
static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
Definition: tablecmds.c:7832
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:11686
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16938
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:14365
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
Definition: tablecmds.c:13319
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:16305
#define ATT_COMPOSITE_TYPE
Definition: tablecmds.c:333
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:21568
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:20049
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19484
static List * on_commits
Definition: tablecmds.c:132
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:17461
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9675
ObjectAddress RenameConstraint(RenameStmt *stmt)
Definition: tablecmds.c:4148
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5830
void register_on_commit_action(Oid relid, OnCommitAction action)
Definition: tablecmds.c:19251
static void RangeVarCallbackForTruncate(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19520
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:16439
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:22018
void RangeVarCallbackOwnsRelation(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19544
#define ATT_MATVIEW
Definition: tablecmds.c:331
#define child_dependency_type(child_is_partition)
Definition: tablecmds.c:366
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
Definition: tablecmds.c:21730
void ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
Definition: tablecmds.c:1975
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
Definition: tablecmds.c:6118
static List * MergeAttributes(List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
Definition: tablecmds.c:2539
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:17214
struct NewColumnValue NewColumnValue
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
Definition: tablecmds.c:16483
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:8081
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:3280
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:3327
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:6060
void AfterTriggerEndQuery(EState *estate)
Definition: trigger.c:5124
void AfterTriggerBeginQuery(void)
Definition: trigger.c:5104
#define RI_TRIGGER_FK
Definition: trigger.h:285
#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:284
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:340
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition: tupdesc.c:1092
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:182
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition: tupdesc.c:117
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition: tupdesc.c:1026
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:842
#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:457
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:371
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition: tuptable.h:384
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1921
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1488
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition: typecmds.c:3997
ObjectAddress AlterDomainAddConstraint(List *names, Node *newConstraint, ObjectAddress *constrAddr)
Definition: typecmds.c:2935
void checkDomainOwner(HeapTuple tup)
Definition: typecmds.c:3496
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4166
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:1958
#define MAX_STATISTICS_TARGET
Definition: vacuum.h:329
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
static char * VARDATA_ANY(const void *PTR)
Definition: varatt.h:486
const char * name
SubTransactionId GetCurrentSubTransactionId(void)
Definition: xact.c:792
void CommandCounterIncrement(void)
Definition: xact.c:1101
void StartTransactionCommand(void)
Definition: xact.c:3077
void CommitTransactionCommand(void)
Definition: xact.c:3175
int MyXactFlags
Definition: xact.c:137
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:830
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:103
#define XLogLogicalInfoActive()
Definition: xlog.h:126
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:154
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:478
void XLogRegisterData(const void *data, uint32 len)
Definition: xloginsert.c:368
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:460
void XLogBeginInsert(void)
Definition: xloginsert.c:152