Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

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 DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
726static void DropClonedTriggersFromPartition(Oid partitionId);
728 Relation rel, RangeVar *name,
729 bool concurrent);
730static void DetachPartitionFinalize(Relation rel, Relation partRel,
731 bool concurrent, Oid defaultPartOid);
733static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
734 RangeVar *name);
735static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
736static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
737 Relation partitionTbl);
738static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
739static List *GetParentedForeignKeyRefs(Relation partition);
740static void ATDetachCheckNoForeignKeyRefs(Relation partition);
741static char GetAttributeCompression(Oid atttypid, const char *compression);
742static char GetAttributeStorage(Oid atttypid, const char *storagemode);
743
744
745/* ----------------------------------------------------------------
746 * DefineRelation
747 * Creates a new relation.
748 *
749 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
750 * The other arguments are used to extend the behavior for other cases:
751 * relkind: relkind to assign to the new relation
752 * ownerId: if not InvalidOid, use this as the new relation's owner.
753 * typaddress: if not null, it's set to the pg_type entry's address.
754 * queryString: for error reporting
755 *
756 * Note that permissions checks are done against current user regardless of
757 * ownerId. A nonzero ownerId is used when someone is creating a relation
758 * "on behalf of" someone else, so we still want to see that the current user
759 * has permissions to do it.
760 *
761 * If successful, returns the address of the new relation.
762 * ----------------------------------------------------------------
763 */
765DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
766 ObjectAddress *typaddress, const char *queryString)
767{
768 char relname[NAMEDATALEN];
769 Oid namespaceId;
770 Oid relationId;
771 Oid tablespaceId;
772 Relation rel;
774 List *inheritOids;
775 List *old_constraints;
776 List *old_notnulls;
777 List *rawDefaults;
778 List *cookedDefaults;
779 List *nncols;
780 Datum reloptions;
781 ListCell *listptr;
783 bool partitioned;
784 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
785 Oid ofTypeId;
786 ObjectAddress address;
787 LOCKMODE parentLockmode;
788 Oid accessMethodId = InvalidOid;
789
790 /*
791 * Truncate relname to appropriate length (probably a waste of time, as
792 * parser should have done this already).
793 */
794 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
795
796 /*
797 * Check consistency of arguments
798 */
799 if (stmt->oncommit != ONCOMMIT_NOOP
800 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
802 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
803 errmsg("ON COMMIT can only be used on temporary tables")));
804
805 if (stmt->partspec != NULL)
806 {
807 if (relkind != RELKIND_RELATION)
808 elog(ERROR, "unexpected relkind: %d", (int) relkind);
809
810 relkind = RELKIND_PARTITIONED_TABLE;
811 partitioned = true;
812 }
813 else
814 partitioned = false;
815
816 if (relkind == RELKIND_PARTITIONED_TABLE &&
817 stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
819 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
820 errmsg("partitioned tables cannot be unlogged")));
821
822 /*
823 * Look up the namespace in which we are supposed to create the relation,
824 * check we have permission to create there, lock it against concurrent
825 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
826 * namespace is selected.
827 */
828 namespaceId =
830
831 /*
832 * Security check: disallow creating temp tables from security-restricted
833 * code. This is needed because calling code might not expect untrusted
834 * tables to appear in pg_temp at the front of its search path.
835 */
836 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
839 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
840 errmsg("cannot create temporary table within security-restricted operation")));
841
842 /*
843 * Determine the lockmode to use when scanning parents. A self-exclusive
844 * lock is needed here.
845 *
846 * For regular inheritance, if two backends attempt to add children to the
847 * same parent simultaneously, and that parent has no pre-existing
848 * children, then both will attempt to update the parent's relhassubclass
849 * field, leading to a "tuple concurrently updated" error. Also, this
850 * interlocks against a concurrent ANALYZE on the parent table, which
851 * might otherwise be attempting to clear the parent's relhassubclass
852 * field, if its previous children were recently dropped.
853 *
854 * If the child table is a partition, then we instead grab an exclusive
855 * lock on the parent because its partition descriptor will be changed by
856 * addition of the new partition.
857 */
858 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
860
861 /* Determine the list of OIDs of the parents. */
862 inheritOids = NIL;
863 foreach(listptr, stmt->inhRelations)
864 {
865 RangeVar *rv = (RangeVar *) lfirst(listptr);
866 Oid parentOid;
867
868 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
869
870 /*
871 * Reject duplications in the list of parents.
872 */
873 if (list_member_oid(inheritOids, parentOid))
875 (errcode(ERRCODE_DUPLICATE_TABLE),
876 errmsg("relation \"%s\" would be inherited from more than once",
877 get_rel_name(parentOid))));
878
879 inheritOids = lappend_oid(inheritOids, parentOid);
880 }
881
882 /*
883 * Select tablespace to use: an explicitly indicated one, or (in the case
884 * of a partitioned table) the parent's, if it has one.
885 */
886 if (stmt->tablespacename)
887 {
888 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
889
890 if (partitioned && tablespaceId == MyDatabaseTableSpace)
892 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
893 errmsg("cannot specify default tablespace for partitioned relations")));
894 }
895 else if (stmt->partbound)
896 {
897 Assert(list_length(inheritOids) == 1);
898 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
899 }
900 else
901 tablespaceId = InvalidOid;
902
903 /* still nothing? use the default */
904 if (!OidIsValid(tablespaceId))
905 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
906 partitioned);
907
908 /* Check permissions except when using database's default */
909 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
910 {
911 AclResult aclresult;
912
913 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
914 ACL_CREATE);
915 if (aclresult != ACLCHECK_OK)
917 get_tablespace_name(tablespaceId));
918 }
919
920 /* In all cases disallow placing user relations in pg_global */
921 if (tablespaceId == GLOBALTABLESPACE_OID)
923 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
924 errmsg("only shared relations can be placed in pg_global tablespace")));
925
926 /* Identify user ID that will own the table */
927 if (!OidIsValid(ownerId))
928 ownerId = GetUserId();
929
930 /*
931 * Parse and validate reloptions, if any.
932 */
933 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
934 true, false);
935
936 switch (relkind)
937 {
938 case RELKIND_VIEW:
939 (void) view_reloptions(reloptions, true);
940 break;
941 case RELKIND_PARTITIONED_TABLE:
942 (void) partitioned_table_reloptions(reloptions, true);
943 break;
944 default:
945 (void) heap_reloptions(relkind, reloptions, true);
946 }
947
948 if (stmt->ofTypename)
949 {
950 AclResult aclresult;
951
952 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
953
954 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
955 if (aclresult != ACLCHECK_OK)
956 aclcheck_error_type(aclresult, ofTypeId);
957 }
958 else
959 ofTypeId = InvalidOid;
960
961 /*
962 * Look up inheritance ancestors and generate relation schema, including
963 * inherited attributes. (Note that stmt->tableElts is destructively
964 * modified by MergeAttributes.)
965 */
966 stmt->tableElts =
967 MergeAttributes(stmt->tableElts, inheritOids,
968 stmt->relation->relpersistence,
969 stmt->partbound != NULL,
970 &old_constraints, &old_notnulls);
971
972 /*
973 * Create a tuple descriptor from the relation schema. Note that this
974 * deals with column names, types, and in-descriptor NOT NULL flags, but
975 * not default values, NOT NULL or CHECK constraints; we handle those
976 * below.
977 */
979
980 /*
981 * Find columns with default values and prepare for insertion of the
982 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
983 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
984 * while raw defaults go into a list of RawColumnDefault structs that will
985 * be processed by AddRelationNewConstraints. (We can't deal with raw
986 * expressions until we can do transformExpr.)
987 */
988 rawDefaults = NIL;
989 cookedDefaults = NIL;
990 attnum = 0;
991
992 foreach(listptr, stmt->tableElts)
993 {
994 ColumnDef *colDef = lfirst(listptr);
995
996 attnum++;
997 if (colDef->raw_default != NULL)
998 {
999 RawColumnDefault *rawEnt;
1000
1001 Assert(colDef->cooked_default == NULL);
1002
1003 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
1004 rawEnt->attnum = attnum;
1005 rawEnt->raw_default = colDef->raw_default;
1006 rawEnt->generated = colDef->generated;
1007 rawDefaults = lappend(rawDefaults, rawEnt);
1008 }
1009 else if (colDef->cooked_default != NULL)
1010 {
1011 CookedConstraint *cooked;
1012
1013 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
1014 cooked->contype = CONSTR_DEFAULT;
1015 cooked->conoid = InvalidOid; /* until created */
1016 cooked->name = NULL;
1017 cooked->attnum = attnum;
1018 cooked->expr = colDef->cooked_default;
1019 cooked->is_enforced = true;
1020 cooked->skip_validation = false;
1021 cooked->is_local = true; /* not used for defaults */
1022 cooked->inhcount = 0; /* ditto */
1023 cooked->is_no_inherit = false;
1024 cookedDefaults = lappend(cookedDefaults, cooked);
1025 }
1026 }
1027
1028 /*
1029 * For relations with table AM and partitioned tables, select access
1030 * method to use: an explicitly indicated one, or (in the case of a
1031 * partitioned table) the parent's, if it has one.
1032 */
1033 if (stmt->accessMethod != NULL)
1034 {
1035 Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1036 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1037 }
1038 else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1039 {
1040 if (stmt->partbound)
1041 {
1042 Assert(list_length(inheritOids) == 1);
1043 accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1044 }
1045
1046 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1047 accessMethodId = get_table_am_oid(default_table_access_method, false);
1048 }
1049
1050 /*
1051 * Create the relation. Inherited defaults and CHECK constraints are
1052 * passed in for immediate handling --- since they don't need parsing,
1053 * they can be stored immediately.
1054 */
1055 relationId = heap_create_with_catalog(relname,
1056 namespaceId,
1057 tablespaceId,
1058 InvalidOid,
1059 InvalidOid,
1060 ofTypeId,
1061 ownerId,
1062 accessMethodId,
1063 descriptor,
1064 list_concat(cookedDefaults,
1065 old_constraints),
1066 relkind,
1067 stmt->relation->relpersistence,
1068 false,
1069 false,
1070 stmt->oncommit,
1071 reloptions,
1072 true,
1074 false,
1075 InvalidOid,
1076 typaddress);
1077
1078 /*
1079 * We must bump the command counter to make the newly-created relation
1080 * tuple visible for opening.
1081 */
1083
1084 /*
1085 * Open the new relation and acquire exclusive lock on it. This isn't
1086 * really necessary for locking out other backends (since they can't see
1087 * the new rel anyway until we commit), but it keeps the lock manager from
1088 * complaining about deadlock risks.
1089 */
1090 rel = relation_open(relationId, AccessExclusiveLock);
1091
1092 /*
1093 * Now add any newly specified column default and generation expressions
1094 * to the new relation. These are passed to us in the form of raw
1095 * parsetrees; we need to transform them to executable expression trees
1096 * before they can be added. The most convenient way to do that is to
1097 * apply the parser's transformExpr routine, but transformExpr doesn't
1098 * work unless we have a pre-existing relation. So, the transformation has
1099 * to be postponed to this final step of CREATE TABLE.
1100 *
1101 * This needs to be before processing the partitioning clauses because
1102 * those could refer to generated columns.
1103 */
1104 if (rawDefaults)
1105 AddRelationNewConstraints(rel, rawDefaults, NIL,
1106 true, true, false, queryString);
1107
1108 /*
1109 * Make column generation expressions visible for use by partitioning.
1110 */
1112
1113 /* Process and store partition bound, if any. */
1114 if (stmt->partbound)
1115 {
1116 PartitionBoundSpec *bound;
1117 ParseState *pstate;
1118 Oid parentId = linitial_oid(inheritOids),
1119 defaultPartOid;
1120 Relation parent,
1121 defaultRel = NULL;
1122 ParseNamespaceItem *nsitem;
1123
1124 /* Already have strong enough lock on the parent */
1125 parent = table_open(parentId, NoLock);
1126
1127 /*
1128 * We are going to try to validate the partition bound specification
1129 * against the partition key of parentRel, so it better have one.
1130 */
1131 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1132 ereport(ERROR,
1133 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1134 errmsg("\"%s\" is not partitioned",
1135 RelationGetRelationName(parent))));
1136
1137 /*
1138 * The partition constraint of the default partition depends on the
1139 * partition bounds of every other partition. It is possible that
1140 * another backend might be about to execute a query on the default
1141 * partition table, and that the query relies on previously cached
1142 * default partition constraints. We must therefore take a table lock
1143 * strong enough to prevent all queries on the default partition from
1144 * proceeding until we commit and send out a shared-cache-inval notice
1145 * that will make them update their index lists.
1146 *
1147 * Order of locking: The relation being added won't be visible to
1148 * other backends until it is committed, hence here in
1149 * DefineRelation() the order of locking the default partition and the
1150 * relation being added does not matter. But at all other places we
1151 * need to lock the default relation before we lock the relation being
1152 * added or removed i.e. we should take the lock in same order at all
1153 * the places such that lock parent, lock default partition and then
1154 * lock the partition so as to avoid a deadlock.
1155 */
1156 defaultPartOid =
1158 true));
1159 if (OidIsValid(defaultPartOid))
1160 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1161
1162 /* Transform the bound values */
1163 pstate = make_parsestate(NULL);
1164 pstate->p_sourcetext = queryString;
1165
1166 /*
1167 * Add an nsitem containing this relation, so that transformExpr
1168 * called on partition bound expressions is able to report errors
1169 * using a proper context.
1170 */
1171 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1172 NULL, false, false);
1173 addNSItemToQuery(pstate, nsitem, false, true, true);
1174
1175 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1176
1177 /*
1178 * Check first that the new partition's bound is valid and does not
1179 * overlap with any of existing partitions of the parent.
1180 */
1181 check_new_partition_bound(relname, parent, bound, pstate);
1182
1183 /*
1184 * If the default partition exists, its partition constraints will
1185 * change after the addition of this new partition such that it won't
1186 * allow any row that qualifies for this new partition. So, check that
1187 * the existing data in the default partition satisfies the constraint
1188 * as it will exist after adding this partition.
1189 */
1190 if (OidIsValid(defaultPartOid))
1191 {
1192 check_default_partition_contents(parent, defaultRel, bound);
1193 /* Keep the lock until commit. */
1194 table_close(defaultRel, NoLock);
1195 }
1196
1197 /* Update the pg_class entry. */
1198 StorePartitionBound(rel, parent, bound);
1199
1200 table_close(parent, NoLock);
1201 }
1202
1203 /* Store inheritance information for new rel. */
1204 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1205
1206 /*
1207 * Process the partitioning specification (if any) and store the partition
1208 * key information into the catalog.
1209 */
1210 if (partitioned)
1211 {
1212 ParseState *pstate;
1213 int partnatts;
1214 AttrNumber partattrs[PARTITION_MAX_KEYS];
1215 Oid partopclass[PARTITION_MAX_KEYS];
1216 Oid partcollation[PARTITION_MAX_KEYS];
1217 List *partexprs = NIL;
1218
1219 pstate = make_parsestate(NULL);
1220 pstate->p_sourcetext = queryString;
1221
1222 partnatts = list_length(stmt->partspec->partParams);
1223
1224 /* Protect fixed-size arrays here and in executor */
1225 if (partnatts > PARTITION_MAX_KEYS)
1226 ereport(ERROR,
1227 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1228 errmsg("cannot partition using more than %d columns",
1230
1231 /*
1232 * We need to transform the raw parsetrees corresponding to partition
1233 * expressions into executable expression trees. Like column defaults
1234 * and CHECK constraints, we could not have done the transformation
1235 * earlier.
1236 */
1237 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1238
1239 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1240 partattrs, &partexprs, partopclass,
1241 partcollation, stmt->partspec->strategy);
1242
1243 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1244 partexprs,
1245 partopclass, partcollation);
1246
1247 /* make it all visible */
1249 }
1250
1251 /*
1252 * If we're creating a partition, create now all the indexes, triggers,
1253 * FKs defined in the parent.
1254 *
1255 * We can't do it earlier, because DefineIndex wants to know the partition
1256 * key which we just stored.
1257 */
1258 if (stmt->partbound)
1259 {
1260 Oid parentId = linitial_oid(inheritOids);
1261 Relation parent;
1262 List *idxlist;
1263 ListCell *cell;
1264
1265 /* Already have strong enough lock on the parent */
1266 parent = table_open(parentId, NoLock);
1267 idxlist = RelationGetIndexList(parent);
1268
1269 /*
1270 * For each index in the parent table, create one in the partition
1271 */
1272 foreach(cell, idxlist)
1273 {
1275 AttrMap *attmap;
1276 IndexStmt *idxstmt;
1277 Oid constraintOid;
1278
1279 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1280 {
1281 if (idxRel->rd_index->indisunique)
1282 ereport(ERROR,
1283 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1284 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1285 RelationGetRelationName(parent)),
1286 errdetail("Table \"%s\" contains indexes that are unique.",
1287 RelationGetRelationName(parent))));
1288 else
1289 {
1291 continue;
1292 }
1293 }
1294
1296 RelationGetDescr(parent),
1297 false);
1298 idxstmt =
1299 generateClonedIndexStmt(NULL, idxRel,
1300 attmap, &constraintOid);
1302 idxstmt,
1303 InvalidOid,
1304 RelationGetRelid(idxRel),
1305 constraintOid,
1306 -1,
1307 false, false, false, false, false);
1308
1310 }
1311
1312 list_free(idxlist);
1313
1314 /*
1315 * If there are any row-level triggers, clone them to the new
1316 * partition.
1317 */
1318 if (parent->trigdesc != NULL)
1319 CloneRowTriggersToPartition(parent, rel);
1320
1321 /*
1322 * And foreign keys too. Note that because we're freshly creating the
1323 * table, there is no need to verify these new constraints.
1324 */
1325 CloneForeignKeyConstraints(NULL, parent, rel);
1326
1327 table_close(parent, NoLock);
1328 }
1329
1330 /*
1331 * Now add any newly specified CHECK constraints to the new relation. Same
1332 * as for defaults above, but these need to come after partitioning is set
1333 * up.
1334 */
1335 if (stmt->constraints)
1336 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1337 true, true, false, queryString);
1338
1339 /*
1340 * Finally, merge the not-null constraints that are declared directly with
1341 * those that come from parent relations (making sure to count inheritance
1342 * appropriately for each), create them, and set the attnotnull flag on
1343 * columns that don't yet have it.
1344 */
1345 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1346 old_notnulls);
1347 foreach_int(attrnum, nncols)
1348 set_attnotnull(NULL, rel, attrnum, true, false);
1349
1350 ObjectAddressSet(address, RelationRelationId, relationId);
1351
1352 /*
1353 * Clean up. We keep lock on new relation (although it shouldn't be
1354 * visible to anyone else anyway, until commit).
1355 */
1356 relation_close(rel, NoLock);
1357
1358 return address;
1359}
1360
1361/*
1362 * BuildDescForRelation
1363 *
1364 * Given a list of ColumnDef nodes, build a TupleDesc.
1365 *
1366 * Note: This is only for the limited purpose of table and view creation. Not
1367 * everything is filled in. A real tuple descriptor should be obtained from
1368 * the relcache.
1369 */
1372{
1373 int natts;
1375 ListCell *l;
1376 TupleDesc desc;
1377 char *attname;
1378 Oid atttypid;
1379 int32 atttypmod;
1380 Oid attcollation;
1381 int attdim;
1382
1383 /*
1384 * allocate a new tuple descriptor
1385 */
1386 natts = list_length(columns);
1387 desc = CreateTemplateTupleDesc(natts);
1388
1389 attnum = 0;
1390
1391 foreach(l, columns)
1392 {
1393 ColumnDef *entry = lfirst(l);
1394 AclResult aclresult;
1396
1397 /*
1398 * for each entry in the list, get the name and type information from
1399 * the list and have TupleDescInitEntry fill in the attribute
1400 * information we need.
1401 */
1402 attnum++;
1403
1404 attname = entry->colname;
1405 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1406
1407 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1408 if (aclresult != ACLCHECK_OK)
1409 aclcheck_error_type(aclresult, atttypid);
1410
1411 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1412 attdim = list_length(entry->typeName->arrayBounds);
1413 if (attdim > PG_INT16_MAX)
1414 ereport(ERROR,
1415 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1416 errmsg("too many array dimensions"));
1417
1418 if (entry->typeName->setof)
1419 ereport(ERROR,
1420 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1421 errmsg("column \"%s\" cannot be declared SETOF",
1422 attname)));
1423
1425 atttypid, atttypmod, attdim);
1426 att = TupleDescAttr(desc, attnum - 1);
1427
1428 /* Override TupleDescInitEntry's settings as requested */
1429 TupleDescInitEntryCollation(desc, attnum, attcollation);
1430
1431 /* Fill in additional stuff not handled by TupleDescInitEntry */
1432 att->attnotnull = entry->is_not_null;
1433 att->attislocal = entry->is_local;
1434 att->attinhcount = entry->inhcount;
1435 att->attidentity = entry->identity;
1436 att->attgenerated = entry->generated;
1437 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1438 if (entry->storage)
1439 att->attstorage = entry->storage;
1440 else if (entry->storage_name)
1441 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1442
1444 }
1445
1446 return desc;
1447}
1448
1449/*
1450 * Emit the right error or warning message for a "DROP" command issued on a
1451 * non-existent relation
1452 */
1453static void
1454DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1455{
1456 const struct dropmsgstrings *rentry;
1457
1458 if (rel->schemaname != NULL &&
1460 {
1461 if (!missing_ok)
1462 {
1463 ereport(ERROR,
1464 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1465 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1466 }
1467 else
1468 {
1470 (errmsg("schema \"%s\" does not exist, skipping",
1471 rel->schemaname)));
1472 }
1473 return;
1474 }
1475
1476 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1477 {
1478 if (rentry->kind == rightkind)
1479 {
1480 if (!missing_ok)
1481 {
1482 ereport(ERROR,
1483 (errcode(rentry->nonexistent_code),
1484 errmsg(rentry->nonexistent_msg, rel->relname)));
1485 }
1486 else
1487 {
1488 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1489 break;
1490 }
1491 }
1492 }
1493
1494 Assert(rentry->kind != '\0'); /* Should be impossible */
1495}
1496
1497/*
1498 * Emit the right error message for a "DROP" command issued on a
1499 * relation of the wrong type
1500 */
1501static void
1502DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1503{
1504 const struct dropmsgstrings *rentry;
1505 const struct dropmsgstrings *wentry;
1506
1507 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1508 if (rentry->kind == rightkind)
1509 break;
1510 Assert(rentry->kind != '\0');
1511
1512 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1513 if (wentry->kind == wrongkind)
1514 break;
1515 /* wrongkind could be something we don't have in our table... */
1516
1517 ereport(ERROR,
1518 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1519 errmsg(rentry->nota_msg, relname),
1520 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1521}
1522
1523/*
1524 * RemoveRelations
1525 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1526 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1527 */
1528void
1530{
1531 ObjectAddresses *objects;
1532 char relkind;
1533 ListCell *cell;
1534 int flags = 0;
1535 LOCKMODE lockmode = AccessExclusiveLock;
1536
1537 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1538 if (drop->concurrent)
1539 {
1540 /*
1541 * Note that for temporary relations this lock may get upgraded later
1542 * on, but as no other session can access a temporary relation, this
1543 * is actually fine.
1544 */
1545 lockmode = ShareUpdateExclusiveLock;
1546 Assert(drop->removeType == OBJECT_INDEX);
1547 if (list_length(drop->objects) != 1)
1548 ereport(ERROR,
1549 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1550 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1551 if (drop->behavior == DROP_CASCADE)
1552 ereport(ERROR,
1553 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1554 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1555 }
1556
1557 /*
1558 * First we identify all the relations, then we delete them in a single
1559 * performMultipleDeletions() call. This is to avoid unwanted DROP
1560 * RESTRICT errors if one of the relations depends on another.
1561 */
1562
1563 /* Determine required relkind */
1564 switch (drop->removeType)
1565 {
1566 case OBJECT_TABLE:
1567 relkind = RELKIND_RELATION;
1568 break;
1569
1570 case OBJECT_INDEX:
1571 relkind = RELKIND_INDEX;
1572 break;
1573
1574 case OBJECT_SEQUENCE:
1575 relkind = RELKIND_SEQUENCE;
1576 break;
1577
1578 case OBJECT_VIEW:
1579 relkind = RELKIND_VIEW;
1580 break;
1581
1582 case OBJECT_MATVIEW:
1583 relkind = RELKIND_MATVIEW;
1584 break;
1585
1587 relkind = RELKIND_FOREIGN_TABLE;
1588 break;
1589
1590 default:
1591 elog(ERROR, "unrecognized drop object type: %d",
1592 (int) drop->removeType);
1593 relkind = 0; /* keep compiler quiet */
1594 break;
1595 }
1596
1597 /* Lock and validate each relation; build a list of object addresses */
1598 objects = new_object_addresses();
1599
1600 foreach(cell, drop->objects)
1601 {
1602 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1603 Oid relOid;
1604 ObjectAddress obj;
1606
1607 /*
1608 * These next few steps are a great deal like relation_openrv, but we
1609 * don't bother building a relcache entry since we don't need it.
1610 *
1611 * Check for shared-cache-inval messages before trying to access the
1612 * relation. This is needed to cover the case where the name
1613 * identifies a rel that has been dropped and recreated since the
1614 * start of our transaction: if we don't flush the old syscache entry,
1615 * then we'll latch onto that entry and suffer an error later.
1616 */
1618
1619 /* Look up the appropriate relation using namespace search. */
1620 state.expected_relkind = relkind;
1621 state.heap_lockmode = drop->concurrent ?
1623 /* We must initialize these fields to show that no locks are held: */
1624 state.heapOid = InvalidOid;
1625 state.partParentOid = InvalidOid;
1626
1627 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1629 &state);
1630
1631 /* Not there? */
1632 if (!OidIsValid(relOid))
1633 {
1634 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1635 continue;
1636 }
1637
1638 /*
1639 * Decide if concurrent mode needs to be used here or not. The
1640 * callback retrieved the rel's persistence for us.
1641 */
1642 if (drop->concurrent &&
1643 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1644 {
1645 Assert(list_length(drop->objects) == 1 &&
1646 drop->removeType == OBJECT_INDEX);
1648 }
1649
1650 /*
1651 * Concurrent index drop cannot be used with partitioned indexes,
1652 * either.
1653 */
1654 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1655 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1656 ereport(ERROR,
1657 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1658 errmsg("cannot drop partitioned index \"%s\" concurrently",
1659 rel->relname)));
1660
1661 /*
1662 * If we're told to drop a partitioned index, we must acquire lock on
1663 * all the children of its parent partitioned table before proceeding.
1664 * Otherwise we'd try to lock the child index partitions before their
1665 * tables, leading to potential deadlock against other sessions that
1666 * will lock those objects in the other order.
1667 */
1668 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1669 (void) find_all_inheritors(state.heapOid,
1670 state.heap_lockmode,
1671 NULL);
1672
1673 /* OK, we're ready to delete this one */
1674 obj.classId = RelationRelationId;
1675 obj.objectId = relOid;
1676 obj.objectSubId = 0;
1677
1678 add_exact_object_address(&obj, objects);
1679 }
1680
1681 performMultipleDeletions(objects, drop->behavior, flags);
1682
1683 free_object_addresses(objects);
1684}
1685
1686/*
1687 * Before acquiring a table lock, check whether we have sufficient rights.
1688 * In the case of DROP INDEX, also try to lock the table before the index.
1689 * Also, if the table to be dropped is a partition, we try to lock the parent
1690 * first.
1691 */
1692static void
1693RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1694 void *arg)
1695{
1696 HeapTuple tuple;
1698 char expected_relkind;
1699 bool is_partition;
1700 Form_pg_class classform;
1702 bool invalid_system_index = false;
1703
1704 state = (struct DropRelationCallbackState *) arg;
1705 heap_lockmode = state->heap_lockmode;
1706
1707 /*
1708 * If we previously locked some other index's heap, and the name we're
1709 * looking up no longer refers to that relation, release the now-useless
1710 * lock.
1711 */
1712 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1713 {
1715 state->heapOid = InvalidOid;
1716 }
1717
1718 /*
1719 * Similarly, if we previously locked some other partition's heap, and the
1720 * name we're looking up no longer refers to that relation, release the
1721 * now-useless lock.
1722 */
1723 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1724 {
1726 state->partParentOid = InvalidOid;
1727 }
1728
1729 /* Didn't find a relation, so no need for locking or permission checks. */
1730 if (!OidIsValid(relOid))
1731 return;
1732
1733 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1734 if (!HeapTupleIsValid(tuple))
1735 return; /* concurrently dropped, so nothing to do */
1736 classform = (Form_pg_class) GETSTRUCT(tuple);
1737 is_partition = classform->relispartition;
1738
1739 /* Pass back some data to save lookups in RemoveRelations */
1740 state->actual_relkind = classform->relkind;
1741 state->actual_relpersistence = classform->relpersistence;
1742
1743 /*
1744 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1745 * but RemoveRelations() can only pass one relkind for a given relation.
1746 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1747 * That means we must be careful before giving the wrong type error when
1748 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1749 * exists with indexes.
1750 */
1751 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1752 expected_relkind = RELKIND_RELATION;
1753 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1754 expected_relkind = RELKIND_INDEX;
1755 else
1756 expected_relkind = classform->relkind;
1757
1758 if (state->expected_relkind != expected_relkind)
1759 DropErrorMsgWrongType(rel->relname, classform->relkind,
1760 state->expected_relkind);
1761
1762 /* Allow DROP to either table owner or schema owner */
1763 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1764 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1766 get_relkind_objtype(classform->relkind),
1767 rel->relname);
1768
1769 /*
1770 * Check the case of a system index that might have been invalidated by a
1771 * failed concurrent process and allow its drop. For the time being, this
1772 * only concerns indexes of toast relations that became invalid during a
1773 * REINDEX CONCURRENTLY process.
1774 */
1775 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1776 {
1777 HeapTuple locTuple;
1778 Form_pg_index indexform;
1779 bool indisvalid;
1780
1781 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1782 if (!HeapTupleIsValid(locTuple))
1783 {
1784 ReleaseSysCache(tuple);
1785 return;
1786 }
1787
1788 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1789 indisvalid = indexform->indisvalid;
1790 ReleaseSysCache(locTuple);
1791
1792 /* Mark object as being an invalid index of system catalogs */
1793 if (!indisvalid)
1794 invalid_system_index = true;
1795 }
1796
1797 /* In the case of an invalid index, it is fine to bypass this check */
1798 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1799 ereport(ERROR,
1800 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1801 errmsg("permission denied: \"%s\" is a system catalog",
1802 rel->relname)));
1803
1804 ReleaseSysCache(tuple);
1805
1806 /*
1807 * In DROP INDEX, attempt to acquire lock on the parent table before
1808 * locking the index. index_drop() will need this anyway, and since
1809 * regular queries lock tables before their indexes, we risk deadlock if
1810 * we do it the other way around. No error if we don't find a pg_index
1811 * entry, though --- the relation may have been dropped. Note that this
1812 * code will execute for either plain or partitioned indexes.
1813 */
1814 if (expected_relkind == RELKIND_INDEX &&
1815 relOid != oldRelOid)
1816 {
1817 state->heapOid = IndexGetRelation(relOid, true);
1818 if (OidIsValid(state->heapOid))
1820 }
1821
1822 /*
1823 * Similarly, if the relation is a partition, we must acquire lock on its
1824 * parent before locking the partition. That's because queries lock the
1825 * parent before its partitions, so we risk deadlock if we do it the other
1826 * way around.
1827 */
1828 if (is_partition && relOid != oldRelOid)
1829 {
1830 state->partParentOid = get_partition_parent(relOid, true);
1831 if (OidIsValid(state->partParentOid))
1832 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1833 }
1834}
1835
1836/*
1837 * ExecuteTruncate
1838 * Executes a TRUNCATE command.
1839 *
1840 * This is a multi-relation truncate. We first open and grab exclusive
1841 * lock on all relations involved, checking permissions and otherwise
1842 * verifying that the relation is OK for truncation. Note that if relations
1843 * are foreign tables, at this stage, we have not yet checked that their
1844 * foreign data in external data sources are OK for truncation. These are
1845 * checked when foreign data are actually truncated later. In CASCADE mode,
1846 * relations having FK references to the targeted relations are automatically
1847 * added to the group; in RESTRICT mode, we check that all FK references are
1848 * internal to the group that's being truncated. Finally all the relations
1849 * are truncated and reindexed.
1850 */
1851void
1853{
1854 List *rels = NIL;
1855 List *relids = NIL;
1856 List *relids_logged = NIL;
1857 ListCell *cell;
1858
1859 /*
1860 * Open, exclusive-lock, and check all the explicitly-specified relations
1861 */
1862 foreach(cell, stmt->relations)
1863 {
1864 RangeVar *rv = lfirst(cell);
1865 Relation rel;
1866 bool recurse = rv->inh;
1867 Oid myrelid;
1868 LOCKMODE lockmode = AccessExclusiveLock;
1869
1870 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1872 NULL);
1873
1874 /* don't throw error for "TRUNCATE foo, foo" */
1875 if (list_member_oid(relids, myrelid))
1876 continue;
1877
1878 /* open the relation, we already hold a lock on it */
1879 rel = table_open(myrelid, NoLock);
1880
1881 /*
1882 * RangeVarGetRelidExtended() has done most checks with its callback,
1883 * but other checks with the now-opened Relation remain.
1884 */
1886
1887 rels = lappend(rels, rel);
1888 relids = lappend_oid(relids, myrelid);
1889
1890 /* Log this relation only if needed for logical decoding */
1892 relids_logged = lappend_oid(relids_logged, myrelid);
1893
1894 if (recurse)
1895 {
1896 ListCell *child;
1897 List *children;
1898
1899 children = find_all_inheritors(myrelid, lockmode, NULL);
1900
1901 foreach(child, children)
1902 {
1903 Oid childrelid = lfirst_oid(child);
1904
1905 if (list_member_oid(relids, childrelid))
1906 continue;
1907
1908 /* find_all_inheritors already got lock */
1909 rel = table_open(childrelid, NoLock);
1910
1911 /*
1912 * It is possible that the parent table has children that are
1913 * temp tables of other backends. We cannot safely access
1914 * such tables (because of buffering issues), and the best
1915 * thing to do is to silently ignore them. Note that this
1916 * check is the same as one of the checks done in
1917 * truncate_check_activity() called below, still it is kept
1918 * here for simplicity.
1919 */
1920 if (RELATION_IS_OTHER_TEMP(rel))
1921 {
1922 table_close(rel, lockmode);
1923 continue;
1924 }
1925
1926 /*
1927 * Inherited TRUNCATE commands perform access permission
1928 * checks on the parent table only. So we skip checking the
1929 * children's permissions and don't call
1930 * truncate_check_perms() here.
1931 */
1934
1935 rels = lappend(rels, rel);
1936 relids = lappend_oid(relids, childrelid);
1937
1938 /* Log this relation only if needed for logical decoding */
1940 relids_logged = lappend_oid(relids_logged, childrelid);
1941 }
1942 }
1943 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1944 ereport(ERROR,
1945 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1946 errmsg("cannot truncate only a partitioned table"),
1947 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1948 }
1949
1950 ExecuteTruncateGuts(rels, relids, relids_logged,
1951 stmt->behavior, stmt->restart_seqs, false);
1952
1953 /* And close the rels */
1954 foreach(cell, rels)
1955 {
1956 Relation rel = (Relation) lfirst(cell);
1957
1958 table_close(rel, NoLock);
1959 }
1960}
1961
1962/*
1963 * ExecuteTruncateGuts
1964 *
1965 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1966 * command (see above) as well as replication subscribers that execute a
1967 * replicated TRUNCATE action.
1968 *
1969 * explicit_rels is the list of Relations to truncate that the command
1970 * specified. relids is the list of Oids corresponding to explicit_rels.
1971 * relids_logged is the list of Oids (a subset of relids) that require
1972 * WAL-logging. This is all a bit redundant, but the existing callers have
1973 * this information handy in this form.
1974 */
1975void
1977 List *relids,
1978 List *relids_logged,
1979 DropBehavior behavior, bool restart_seqs,
1980 bool run_as_table_owner)
1981{
1982 List *rels;
1983 List *seq_relids = NIL;
1984 HTAB *ft_htab = NULL;
1985 EState *estate;
1986 ResultRelInfo *resultRelInfos;
1987 ResultRelInfo *resultRelInfo;
1988 SubTransactionId mySubid;
1989 ListCell *cell;
1990 Oid *logrelids;
1991
1992 /*
1993 * Check the explicitly-specified relations.
1994 *
1995 * In CASCADE mode, suck in all referencing relations as well. This
1996 * requires multiple iterations to find indirectly-dependent relations. At
1997 * each phase, we need to exclusive-lock new rels before looking for their
1998 * dependencies, else we might miss something. Also, we check each rel as
1999 * soon as we open it, to avoid a faux pas such as holding lock for a long
2000 * time on a rel we have no permissions for.
2001 */
2002 rels = list_copy(explicit_rels);
2003 if (behavior == DROP_CASCADE)
2004 {
2005 for (;;)
2006 {
2007 List *newrelids;
2008
2009 newrelids = heap_truncate_find_FKs(relids);
2010 if (newrelids == NIL)
2011 break; /* nothing else to add */
2012
2013 foreach(cell, newrelids)
2014 {
2015 Oid relid = lfirst_oid(cell);
2016 Relation rel;
2017
2018 rel = table_open(relid, AccessExclusiveLock);
2020 (errmsg("truncate cascades to table \"%s\"",
2022 truncate_check_rel(relid, rel->rd_rel);
2023 truncate_check_perms(relid, rel->rd_rel);
2025 rels = lappend(rels, rel);
2026 relids = lappend_oid(relids, relid);
2027
2028 /* Log this relation only if needed for logical decoding */
2030 relids_logged = lappend_oid(relids_logged, relid);
2031 }
2032 }
2033 }
2034
2035 /*
2036 * Check foreign key references. In CASCADE mode, this should be
2037 * unnecessary since we just pulled in all the references; but as a
2038 * cross-check, do it anyway if in an Assert-enabled build.
2039 */
2040#ifdef USE_ASSERT_CHECKING
2041 heap_truncate_check_FKs(rels, false);
2042#else
2043 if (behavior == DROP_RESTRICT)
2044 heap_truncate_check_FKs(rels, false);
2045#endif
2046
2047 /*
2048 * If we are asked to restart sequences, find all the sequences, lock them
2049 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2050 * We want to do this early since it's pointless to do all the truncation
2051 * work only to fail on sequence permissions.
2052 */
2053 if (restart_seqs)
2054 {
2055 foreach(cell, rels)
2056 {
2057 Relation rel = (Relation) lfirst(cell);
2058 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2059 ListCell *seqcell;
2060
2061 foreach(seqcell, seqlist)
2062 {
2063 Oid seq_relid = lfirst_oid(seqcell);
2064 Relation seq_rel;
2065
2066 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2067
2068 /* This check must match AlterSequence! */
2069 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2071 RelationGetRelationName(seq_rel));
2072
2073 seq_relids = lappend_oid(seq_relids, seq_relid);
2074
2075 relation_close(seq_rel, NoLock);
2076 }
2077 }
2078 }
2079
2080 /* Prepare to catch AFTER triggers. */
2082
2083 /*
2084 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2085 * each relation. We don't need to call ExecOpenIndices, though.
2086 *
2087 * We put the ResultRelInfos in the es_opened_result_relations list, even
2088 * though we don't have a range table and don't populate the
2089 * es_result_relations array. That's a bit bogus, but it's enough to make
2090 * ExecGetTriggerResultRel() find them.
2091 */
2092 estate = CreateExecutorState();
2093 resultRelInfos = (ResultRelInfo *)
2094 palloc(list_length(rels) * sizeof(ResultRelInfo));
2095 resultRelInfo = resultRelInfos;
2096 foreach(cell, rels)
2097 {
2098 Relation rel = (Relation) lfirst(cell);
2099
2100 InitResultRelInfo(resultRelInfo,
2101 rel,
2102 0, /* dummy rangetable index */
2103 NULL,
2104 0);
2106 lappend(estate->es_opened_result_relations, resultRelInfo);
2107 resultRelInfo++;
2108 }
2109
2110 /*
2111 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2112 * truncating (this is because one of them might throw an error). Also, if
2113 * we were to allow them to prevent statement execution, that would need
2114 * to be handled here.
2115 */
2116 resultRelInfo = resultRelInfos;
2117 foreach(cell, rels)
2118 {
2119 UserContext ucxt;
2120
2121 if (run_as_table_owner)
2122 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2123 &ucxt);
2124 ExecBSTruncateTriggers(estate, resultRelInfo);
2125 if (run_as_table_owner)
2126 RestoreUserContext(&ucxt);
2127 resultRelInfo++;
2128 }
2129
2130 /*
2131 * OK, truncate each table.
2132 */
2133 mySubid = GetCurrentSubTransactionId();
2134
2135 foreach(cell, rels)
2136 {
2137 Relation rel = (Relation) lfirst(cell);
2138
2139 /* Skip partitioned tables as there is nothing to do */
2140 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2141 continue;
2142
2143 /*
2144 * Build the lists of foreign tables belonging to each foreign server
2145 * and pass each list to the foreign data wrapper's callback function,
2146 * so that each server can truncate its all foreign tables in bulk.
2147 * Each list is saved as a single entry in a hash table that uses the
2148 * server OID as lookup key.
2149 */
2150 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2151 {
2153 bool found;
2154 ForeignTruncateInfo *ft_info;
2155
2156 /* First time through, initialize hashtable for foreign tables */
2157 if (!ft_htab)
2158 {
2159 HASHCTL hctl;
2160
2161 memset(&hctl, 0, sizeof(HASHCTL));
2162 hctl.keysize = sizeof(Oid);
2163 hctl.entrysize = sizeof(ForeignTruncateInfo);
2165
2166 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2167 32, /* start small and extend */
2168 &hctl,
2170 }
2171
2172 /* Find or create cached entry for the foreign table */
2173 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2174 if (!found)
2175 ft_info->rels = NIL;
2176
2177 /*
2178 * Save the foreign table in the entry of the server that the
2179 * foreign table belongs to.
2180 */
2181 ft_info->rels = lappend(ft_info->rels, rel);
2182 continue;
2183 }
2184
2185 /*
2186 * Normally, we need a transaction-safe truncation here. However, if
2187 * the table was either created in the current (sub)transaction or has
2188 * a new relfilenumber in the current (sub)transaction, then we can
2189 * just truncate it in-place, because a rollback would cause the whole
2190 * table or the current physical file to be thrown away anyway.
2191 */
2192 if (rel->rd_createSubid == mySubid ||
2193 rel->rd_newRelfilelocatorSubid == mySubid)
2194 {
2195 /* Immediate, non-rollbackable truncation is OK */
2197 }
2198 else
2199 {
2200 Oid heap_relid;
2201 Oid toast_relid;
2202 ReindexParams reindex_params = {0};
2203
2204 /*
2205 * This effectively deletes all rows in the table, and may be done
2206 * in a serializable transaction. In that case we must record a
2207 * rw-conflict in to this transaction from each transaction
2208 * holding a predicate lock on the table.
2209 */
2211
2212 /*
2213 * Need the full transaction-safe pushups.
2214 *
2215 * Create a new empty storage file for the relation, and assign it
2216 * as the relfilenumber value. The old storage file is scheduled
2217 * for deletion at commit.
2218 */
2219 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2220
2221 heap_relid = RelationGetRelid(rel);
2222
2223 /*
2224 * The same for the toast table, if any.
2225 */
2226 toast_relid = rel->rd_rel->reltoastrelid;
2227 if (OidIsValid(toast_relid))
2228 {
2229 Relation toastrel = relation_open(toast_relid,
2231
2233 toastrel->rd_rel->relpersistence);
2234 table_close(toastrel, NoLock);
2235 }
2236
2237 /*
2238 * Reconstruct the indexes to match, and we're done.
2239 */
2241 &reindex_params);
2242 }
2243
2245 }
2246
2247 /* Now go through the hash table, and truncate foreign tables */
2248 if (ft_htab)
2249 {
2250 ForeignTruncateInfo *ft_info;
2251 HASH_SEQ_STATUS seq;
2252
2253 hash_seq_init(&seq, ft_htab);
2254
2255 PG_TRY();
2256 {
2257 while ((ft_info = hash_seq_search(&seq)) != NULL)
2258 {
2259 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2260
2261 /* truncate_check_rel() has checked that already */
2262 Assert(routine->ExecForeignTruncate != NULL);
2263
2264 routine->ExecForeignTruncate(ft_info->rels,
2265 behavior,
2266 restart_seqs);
2267 }
2268 }
2269 PG_FINALLY();
2270 {
2271 hash_destroy(ft_htab);
2272 }
2273 PG_END_TRY();
2274 }
2275
2276 /*
2277 * Restart owned sequences if we were asked to.
2278 */
2279 foreach(cell, seq_relids)
2280 {
2281 Oid seq_relid = lfirst_oid(cell);
2282
2283 ResetSequence(seq_relid);
2284 }
2285
2286 /*
2287 * Write a WAL record to allow this set of actions to be logically
2288 * decoded.
2289 *
2290 * Assemble an array of relids so we can write a single WAL record for the
2291 * whole action.
2292 */
2293 if (relids_logged != NIL)
2294 {
2295 xl_heap_truncate xlrec;
2296 int i = 0;
2297
2298 /* should only get here if wal_level >= logical */
2300
2301 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2302 foreach(cell, relids_logged)
2303 logrelids[i++] = lfirst_oid(cell);
2304
2305 xlrec.dbId = MyDatabaseId;
2306 xlrec.nrelids = list_length(relids_logged);
2307 xlrec.flags = 0;
2308 if (behavior == DROP_CASCADE)
2309 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2310 if (restart_seqs)
2312
2315 XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2316
2318
2319 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2320 }
2321
2322 /*
2323 * Process all AFTER STATEMENT TRUNCATE triggers.
2324 */
2325 resultRelInfo = resultRelInfos;
2326 foreach(cell, rels)
2327 {
2328 UserContext ucxt;
2329
2330 if (run_as_table_owner)
2331 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2332 &ucxt);
2333 ExecASTruncateTriggers(estate, resultRelInfo);
2334 if (run_as_table_owner)
2335 RestoreUserContext(&ucxt);
2336 resultRelInfo++;
2337 }
2338
2339 /* Handle queued AFTER triggers */
2340 AfterTriggerEndQuery(estate);
2341
2342 /* We can clean up the EState now */
2343 FreeExecutorState(estate);
2344
2345 /*
2346 * Close any rels opened by CASCADE (can't do this while EState still
2347 * holds refs)
2348 */
2349 rels = list_difference_ptr(rels, explicit_rels);
2350 foreach(cell, rels)
2351 {
2352 Relation rel = (Relation) lfirst(cell);
2353
2354 table_close(rel, NoLock);
2355 }
2356}
2357
2358/*
2359 * Check that a given relation is safe to truncate. Subroutine for
2360 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2361 */
2362static void
2364{
2365 char *relname = NameStr(reltuple->relname);
2366
2367 /*
2368 * Only allow truncate on regular tables, foreign tables using foreign
2369 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2370 * latter are only being included here for the following checks; no
2371 * physical truncation will occur in their case.).
2372 */
2373 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2374 {
2375 Oid serverid = GetForeignServerIdByRelId(relid);
2376 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2377
2378 if (!fdwroutine->ExecForeignTruncate)
2379 ereport(ERROR,
2380 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2381 errmsg("cannot truncate foreign table \"%s\"",
2382 relname)));
2383 }
2384 else if (reltuple->relkind != RELKIND_RELATION &&
2385 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2386 ereport(ERROR,
2387 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2388 errmsg("\"%s\" is not a table", relname)));
2389
2390 /*
2391 * Most system catalogs can't be truncated at all, or at least not unless
2392 * allow_system_table_mods=on. As an exception, however, we allow
2393 * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2394 * pg_upgrade, because we need to change its relfilenode to match the old
2395 * cluster, and allowing a TRUNCATE command to be executed is the easiest
2396 * way of doing that.
2397 */
2398 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2399 && (!IsBinaryUpgrade ||
2400 (relid != LargeObjectRelationId &&
2401 relid != LargeObjectMetadataRelationId)))
2402 ereport(ERROR,
2403 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2404 errmsg("permission denied: \"%s\" is a system catalog",
2405 relname)));
2406
2408}
2409
2410/*
2411 * Check that current user has the permission to truncate given relation.
2412 */
2413static void
2415{
2416 char *relname = NameStr(reltuple->relname);
2417 AclResult aclresult;
2418
2419 /* Permissions checks */
2420 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2421 if (aclresult != ACLCHECK_OK)
2422 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2423 relname);
2424}
2425
2426/*
2427 * Set of extra sanity checks to check if a given relation is safe to
2428 * truncate. This is split with truncate_check_rel() as
2429 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2430 */
2431static void
2433{
2434 /*
2435 * Don't allow truncate on temp tables of other backends ... their local
2436 * buffer manager is not going to cope.
2437 */
2438 if (RELATION_IS_OTHER_TEMP(rel))
2439 ereport(ERROR,
2440 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2441 errmsg("cannot truncate temporary tables of other sessions")));
2442
2443 /*
2444 * Also check for active uses of the relation in the current transaction,
2445 * including open scans and pending AFTER trigger events.
2446 */
2447 CheckTableNotInUse(rel, "TRUNCATE");
2448}
2449
2450/*
2451 * storage_name
2452 * returns the name corresponding to a typstorage/attstorage enum value
2453 */
2454static const char *
2456{
2457 switch (c)
2458 {
2459 case TYPSTORAGE_PLAIN:
2460 return "PLAIN";
2461 case TYPSTORAGE_EXTERNAL:
2462 return "EXTERNAL";
2463 case TYPSTORAGE_EXTENDED:
2464 return "EXTENDED";
2465 case TYPSTORAGE_MAIN:
2466 return "MAIN";
2467 default:
2468 return "???";
2469 }
2470}
2471
2472/*----------
2473 * MergeAttributes
2474 * Returns new schema given initial schema and superclasses.
2475 *
2476 * Input arguments:
2477 * 'columns' is the column/attribute definition for the table. (It's a list
2478 * of ColumnDef's.) It is destructively changed.
2479 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2480 * 'relpersistence' is the persistence type of the table.
2481 * 'is_partition' tells if the table is a partition.
2482 *
2483 * Output arguments:
2484 * 'supconstr' receives a list of CookedConstraint representing
2485 * CHECK constraints belonging to parent relations, updated as
2486 * necessary to be valid for the child.
2487 * 'supnotnulls' receives a list of CookedConstraint representing
2488 * not-null constraints based on those from parent relations.
2489 *
2490 * Return value:
2491 * Completed schema list.
2492 *
2493 * Notes:
2494 * The order in which the attributes are inherited is very important.
2495 * Intuitively, the inherited attributes should come first. If a table
2496 * inherits from multiple parents, the order of those attributes are
2497 * according to the order of the parents specified in CREATE TABLE.
2498 *
2499 * Here's an example:
2500 *
2501 * create table person (name text, age int4, location point);
2502 * create table emp (salary int4, manager text) inherits(person);
2503 * create table student (gpa float8) inherits (person);
2504 * create table stud_emp (percent int4) inherits (emp, student);
2505 *
2506 * The order of the attributes of stud_emp is:
2507 *
2508 * person {1:name, 2:age, 3:location}
2509 * / \
2510 * {6:gpa} student emp {4:salary, 5:manager}
2511 * \ /
2512 * stud_emp {7:percent}
2513 *
2514 * If the same attribute name appears multiple times, then it appears
2515 * in the result table in the proper location for its first appearance.
2516 *
2517 * Constraints (including not-null constraints) for the child table
2518 * are the union of all relevant constraints, from both the child schema
2519 * and parent tables. In addition, in legacy inheritance, each column that
2520 * appears in a primary key in any of the parents also gets a NOT NULL
2521 * constraint (partitioning doesn't need this, because the PK itself gets
2522 * inherited.)
2523 *
2524 * The default value for a child column is defined as:
2525 * (1) If the child schema specifies a default, that value is used.
2526 * (2) If neither the child nor any parent specifies a default, then
2527 * the column will not have a default.
2528 * (3) If conflicting defaults are inherited from different parents
2529 * (and not overridden by the child), an error is raised.
2530 * (4) Otherwise the inherited default is used.
2531 *
2532 * Note that the default-value infrastructure is used for generated
2533 * columns' expressions too, so most of the preceding paragraph applies
2534 * to generation expressions too. We insist that a child column be
2535 * generated if and only if its parent(s) are, but it need not have
2536 * the same generation expression.
2537 *----------
2538 */
2539static List *
2540MergeAttributes(List *columns, const List *supers, char relpersistence,
2541 bool is_partition, List **supconstr, List **supnotnulls)
2542{
2543 List *inh_columns = NIL;
2544 List *constraints = NIL;
2545 List *nnconstraints = NIL;
2546 bool have_bogus_defaults = false;
2547 int child_attno;
2548 static Node bogus_marker = {0}; /* marks conflicting defaults */
2549 List *saved_columns = NIL;
2550 ListCell *lc;
2551
2552 /*
2553 * Check for and reject tables with too many columns. We perform this
2554 * check relatively early for two reasons: (a) we don't run the risk of
2555 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2556 * okay if we're processing <= 1600 columns, but could take minutes to
2557 * execute if the user attempts to create a table with hundreds of
2558 * thousands of columns.
2559 *
2560 * Note that we also need to check that we do not exceed this figure after
2561 * including columns from inherited relations.
2562 */
2563 if (list_length(columns) > MaxHeapAttributeNumber)
2564 ereport(ERROR,
2565 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2566 errmsg("tables can have at most %d columns",
2568
2569 /*
2570 * Check for duplicate names in the explicit list of attributes.
2571 *
2572 * Although we might consider merging such entries in the same way that we
2573 * handle name conflicts for inherited attributes, it seems to make more
2574 * sense to assume such conflicts are errors.
2575 *
2576 * We don't use foreach() here because we have two nested loops over the
2577 * columns list, with possible element deletions in the inner one. If we
2578 * used foreach_delete_current() it could only fix up the state of one of
2579 * the loops, so it seems cleaner to use looping over list indexes for
2580 * both loops. Note that any deletion will happen beyond where the outer
2581 * loop is, so its index never needs adjustment.
2582 */
2583 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2584 {
2585 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2586
2587 if (!is_partition && coldef->typeName == NULL)
2588 {
2589 /*
2590 * Typed table column option that does not belong to a column from
2591 * the type. This works because the columns from the type come
2592 * first in the list. (We omit this check for partition column
2593 * lists; those are processed separately below.)
2594 */
2595 ereport(ERROR,
2596 (errcode(ERRCODE_UNDEFINED_COLUMN),
2597 errmsg("column \"%s\" does not exist",
2598 coldef->colname)));
2599 }
2600
2601 /* restpos scans all entries beyond coldef; incr is in loop body */
2602 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2603 {
2604 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2605
2606 if (strcmp(coldef->colname, restdef->colname) == 0)
2607 {
2608 if (coldef->is_from_type)
2609 {
2610 /*
2611 * merge the column options into the column from the type
2612 */
2613 coldef->is_not_null = restdef->is_not_null;
2614 coldef->raw_default = restdef->raw_default;
2615 coldef->cooked_default = restdef->cooked_default;
2616 coldef->constraints = restdef->constraints;
2617 coldef->is_from_type = false;
2618 columns = list_delete_nth_cell(columns, restpos);
2619 }
2620 else
2621 ereport(ERROR,
2622 (errcode(ERRCODE_DUPLICATE_COLUMN),
2623 errmsg("column \"%s\" specified more than once",
2624 coldef->colname)));
2625 }
2626 else
2627 restpos++;
2628 }
2629 }
2630
2631 /*
2632 * In case of a partition, there are no new column definitions, only dummy
2633 * ColumnDefs created for column constraints. Set them aside for now and
2634 * process them at the end.
2635 */
2636 if (is_partition)
2637 {
2638 saved_columns = columns;
2639 columns = NIL;
2640 }
2641
2642 /*
2643 * Scan the parents left-to-right, and merge their attributes to form a
2644 * list of inherited columns (inh_columns).
2645 */
2646 child_attno = 0;
2647 foreach(lc, supers)
2648 {
2649 Oid parent = lfirst_oid(lc);
2650 Relation relation;
2651 TupleDesc tupleDesc;
2652 TupleConstr *constr;
2653 AttrMap *newattmap;
2654 List *inherited_defaults;
2655 List *cols_with_defaults;
2656 List *nnconstrs;
2657 ListCell *lc1;
2658 ListCell *lc2;
2659 Bitmapset *nncols = NULL;
2660
2661 /* caller already got lock */
2662 relation = table_open(parent, NoLock);
2663
2664 /*
2665 * Check for active uses of the parent partitioned table in the
2666 * current transaction, such as being used in some manner by an
2667 * enclosing command.
2668 */
2669 if (is_partition)
2670 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2671
2672 /*
2673 * We do not allow partitioned tables and partitions to participate in
2674 * regular inheritance.
2675 */
2676 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2677 ereport(ERROR,
2678 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2679 errmsg("cannot inherit from partitioned table \"%s\"",
2680 RelationGetRelationName(relation))));
2681 if (relation->rd_rel->relispartition && !is_partition)
2682 ereport(ERROR,
2683 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2684 errmsg("cannot inherit from partition \"%s\"",
2685 RelationGetRelationName(relation))));
2686
2687 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2688 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2689 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2690 ereport(ERROR,
2691 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2692 errmsg("inherited relation \"%s\" is not a table or foreign table",
2693 RelationGetRelationName(relation))));
2694
2695 /*
2696 * If the parent is permanent, so must be all of its partitions. Note
2697 * that inheritance allows that case.
2698 */
2699 if (is_partition &&
2700 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2701 relpersistence == RELPERSISTENCE_TEMP)
2702 ereport(ERROR,
2703 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2704 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2705 RelationGetRelationName(relation))));
2706
2707 /* Permanent rels cannot inherit from temporary ones */
2708 if (relpersistence != RELPERSISTENCE_TEMP &&
2709 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2710 ereport(ERROR,
2711 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2712 errmsg(!is_partition
2713 ? "cannot inherit from temporary relation \"%s\""
2714 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2715 RelationGetRelationName(relation))));
2716
2717 /* If existing rel is temp, it must belong to this session */
2718 if (RELATION_IS_OTHER_TEMP(relation))
2719 ereport(ERROR,
2720 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2721 errmsg(!is_partition
2722 ? "cannot inherit from temporary relation of another session"
2723 : "cannot create as partition of temporary relation of another session")));
2724
2725 /*
2726 * We should have an UNDER permission flag for this, but for now,
2727 * demand that creator of a child table own the parent.
2728 */
2729 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2731 RelationGetRelationName(relation));
2732
2733 tupleDesc = RelationGetDescr(relation);
2734 constr = tupleDesc->constr;
2735
2736 /*
2737 * newattmap->attnums[] will contain the child-table attribute numbers
2738 * for the attributes of this parent table. (They are not the same
2739 * for parents after the first one, nor if we have dropped columns.)
2740 */
2741 newattmap = make_attrmap(tupleDesc->natts);
2742
2743 /* We can't process inherited defaults until newattmap is complete. */
2744 inherited_defaults = cols_with_defaults = NIL;
2745
2746 /*
2747 * Request attnotnull on columns that have a not-null constraint
2748 * that's not marked NO INHERIT (even if not valid).
2749 */
2750 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2751 true, false);
2752 foreach_ptr(CookedConstraint, cc, nnconstrs)
2753 nncols = bms_add_member(nncols, cc->attnum);
2754
2755 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2756 parent_attno++)
2757 {
2758 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2759 parent_attno - 1);
2760 char *attributeName = NameStr(attribute->attname);
2761 int exist_attno;
2762 ColumnDef *newdef;
2763 ColumnDef *mergeddef;
2764
2765 /*
2766 * Ignore dropped columns in the parent.
2767 */
2768 if (attribute->attisdropped)
2769 continue; /* leave newattmap->attnums entry as zero */
2770
2771 /*
2772 * Create new column definition
2773 */
2774 newdef = makeColumnDef(attributeName, attribute->atttypid,
2775 attribute->atttypmod, attribute->attcollation);
2776 newdef->storage = attribute->attstorage;
2777 newdef->generated = attribute->attgenerated;
2778 if (CompressionMethodIsValid(attribute->attcompression))
2779 newdef->compression =
2780 pstrdup(GetCompressionMethodName(attribute->attcompression));
2781
2782 /*
2783 * Regular inheritance children are independent enough not to
2784 * inherit identity columns. But partitions are integral part of
2785 * a partitioned table and inherit identity column.
2786 */
2787 if (is_partition)
2788 newdef->identity = attribute->attidentity;
2789
2790 /*
2791 * Does it match some previously considered column from another
2792 * parent?
2793 */
2794 exist_attno = findAttrByName(attributeName, inh_columns);
2795 if (exist_attno > 0)
2796 {
2797 /*
2798 * Yes, try to merge the two column definitions.
2799 */
2800 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2801
2802 newattmap->attnums[parent_attno - 1] = exist_attno;
2803
2804 /*
2805 * Partitions have only one parent, so conflict should never
2806 * occur.
2807 */
2808 Assert(!is_partition);
2809 }
2810 else
2811 {
2812 /*
2813 * No, create a new inherited column
2814 */
2815 newdef->inhcount = 1;
2816 newdef->is_local = false;
2817 inh_columns = lappend(inh_columns, newdef);
2818
2819 newattmap->attnums[parent_attno - 1] = ++child_attno;
2820 mergeddef = newdef;
2821 }
2822
2823 /*
2824 * mark attnotnull if parent has it
2825 */
2826 if (bms_is_member(parent_attno, nncols))
2827 mergeddef->is_not_null = true;
2828
2829 /*
2830 * Locate default/generation expression if any
2831 */
2832 if (attribute->atthasdef)
2833 {
2834 Node *this_default;
2835
2836 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2837 if (this_default == NULL)
2838 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2839 parent_attno, RelationGetRelationName(relation));
2840
2841 /*
2842 * If it's a GENERATED default, it might contain Vars that
2843 * need to be mapped to the inherited column(s)' new numbers.
2844 * We can't do that till newattmap is ready, so just remember
2845 * all the inherited default expressions for the moment.
2846 */
2847 inherited_defaults = lappend(inherited_defaults, this_default);
2848 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2849 }
2850 }
2851
2852 /*
2853 * Now process any inherited default expressions, adjusting attnos
2854 * using the completed newattmap map.
2855 */
2856 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2857 {
2858 Node *this_default = (Node *) lfirst(lc1);
2859 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2860 bool found_whole_row;
2861
2862 /* Adjust Vars to match new table's column numbering */
2863 this_default = map_variable_attnos(this_default,
2864 1, 0,
2865 newattmap,
2866 InvalidOid, &found_whole_row);
2867
2868 /*
2869 * For the moment we have to reject whole-row variables. We could
2870 * convert them, if we knew the new table's rowtype OID, but that
2871 * hasn't been assigned yet. (A variable could only appear in a
2872 * generation expression, so the error message is correct.)
2873 */
2874 if (found_whole_row)
2875 ereport(ERROR,
2876 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2877 errmsg("cannot convert whole-row table reference"),
2878 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2879 def->colname,
2880 RelationGetRelationName(relation))));
2881
2882 /*
2883 * If we already had a default from some prior parent, check to
2884 * see if they are the same. If so, no problem; if not, mark the
2885 * column as having a bogus default. Below, we will complain if
2886 * the bogus default isn't overridden by the child columns.
2887 */
2888 Assert(def->raw_default == NULL);
2889 if (def->cooked_default == NULL)
2890 def->cooked_default = this_default;
2891 else if (!equal(def->cooked_default, this_default))
2892 {
2893 def->cooked_default = &bogus_marker;
2894 have_bogus_defaults = true;
2895 }
2896 }
2897
2898 /*
2899 * Now copy the CHECK constraints of this parent, adjusting attnos
2900 * using the completed newattmap map. Identically named constraints
2901 * are merged if possible, else we throw error.
2902 */
2903 if (constr && constr->num_check > 0)
2904 {
2905 ConstrCheck *check = constr->check;
2906
2907 for (int i = 0; i < constr->num_check; i++)
2908 {
2909 char *name = check[i].ccname;
2910 Node *expr;
2911 bool found_whole_row;
2912
2913 /* ignore if the constraint is non-inheritable */
2914 if (check[i].ccnoinherit)
2915 continue;
2916
2917 /* Adjust Vars to match new table's column numbering */
2918 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2919 1, 0,
2920 newattmap,
2921 InvalidOid, &found_whole_row);
2922
2923 /*
2924 * For the moment we have to reject whole-row variables. We
2925 * could convert them, if we knew the new table's rowtype OID,
2926 * but that hasn't been assigned yet.
2927 */
2928 if (found_whole_row)
2929 ereport(ERROR,
2930 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2931 errmsg("cannot convert whole-row table reference"),
2932 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2933 name,
2934 RelationGetRelationName(relation))));
2935
2936 constraints = MergeCheckConstraint(constraints, name, expr,
2937 check[i].ccenforced);
2938 }
2939 }
2940
2941 /*
2942 * Also copy the not-null constraints from this parent. The
2943 * attnotnull markings were already installed above.
2944 */
2945 foreach_ptr(CookedConstraint, nn, nnconstrs)
2946 {
2947 Assert(nn->contype == CONSTR_NOTNULL);
2948
2949 nn->attnum = newattmap->attnums[nn->attnum - 1];
2950
2951 nnconstraints = lappend(nnconstraints, nn);
2952 }
2953
2954 free_attrmap(newattmap);
2955
2956 /*
2957 * Close the parent rel, but keep our lock on it until xact commit.
2958 * That will prevent someone else from deleting or ALTERing the parent
2959 * before the child is committed.
2960 */
2961 table_close(relation, NoLock);
2962 }
2963
2964 /*
2965 * If we had no inherited attributes, the result columns are just the
2966 * explicitly declared columns. Otherwise, we need to merge the declared
2967 * columns into the inherited column list. Although, we never have any
2968 * explicitly declared columns if the table is a partition.
2969 */
2970 if (inh_columns != NIL)
2971 {
2972 int newcol_attno = 0;
2973
2974 foreach(lc, columns)
2975 {
2976 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2977 char *attributeName = newdef->colname;
2978 int exist_attno;
2979
2980 /*
2981 * Partitions have only one parent and have no column definitions
2982 * of their own, so conflict should never occur.
2983 */
2984 Assert(!is_partition);
2985
2986 newcol_attno++;
2987
2988 /*
2989 * Does it match some inherited column?
2990 */
2991 exist_attno = findAttrByName(attributeName, inh_columns);
2992 if (exist_attno > 0)
2993 {
2994 /*
2995 * Yes, try to merge the two column definitions.
2996 */
2997 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2998 }
2999 else
3000 {
3001 /*
3002 * No, attach new column unchanged to result columns.
3003 */
3004 inh_columns = lappend(inh_columns, newdef);
3005 }
3006 }
3007
3008 columns = inh_columns;
3009
3010 /*
3011 * Check that we haven't exceeded the legal # of columns after merging
3012 * in inherited columns.
3013 */
3014 if (list_length(columns) > MaxHeapAttributeNumber)
3015 ereport(ERROR,
3016 (errcode(ERRCODE_TOO_MANY_COLUMNS),
3017 errmsg("tables can have at most %d columns",
3019 }
3020
3021 /*
3022 * Now that we have the column definition list for a partition, we can
3023 * check whether the columns referenced in the column constraint specs
3024 * actually exist. Also, merge column defaults.
3025 */
3026 if (is_partition)
3027 {
3028 foreach(lc, saved_columns)
3029 {
3030 ColumnDef *restdef = lfirst(lc);
3031 bool found = false;
3032 ListCell *l;
3033
3034 foreach(l, columns)
3035 {
3036 ColumnDef *coldef = lfirst(l);
3037
3038 if (strcmp(coldef->colname, restdef->colname) == 0)
3039 {
3040 found = true;
3041
3042 /*
3043 * Check for conflicts related to generated columns.
3044 *
3045 * Same rules as above: generated-ness has to match the
3046 * parent, but the contents of the generation expression
3047 * can be different.
3048 */
3049 if (coldef->generated)
3050 {
3051 if (restdef->raw_default && !restdef->generated)
3052 ereport(ERROR,
3053 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3054 errmsg("column \"%s\" inherits from generated column but specifies default",
3055 restdef->colname)));
3056 if (restdef->identity)
3057 ereport(ERROR,
3058 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3059 errmsg("column \"%s\" inherits from generated column but specifies identity",
3060 restdef->colname)));
3061 }
3062 else
3063 {
3064 if (restdef->generated)
3065 ereport(ERROR,
3066 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3067 errmsg("child column \"%s\" specifies generation expression",
3068 restdef->colname),
3069 errhint("A child table column cannot be generated unless its parent column is.")));
3070 }
3071
3072 if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3073 ereport(ERROR,
3074 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3075 errmsg("column \"%s\" inherits from generated column of different kind",
3076 restdef->colname),
3077 errdetail("Parent column is %s, child column is %s.",
3078 coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3079 restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3080
3081 /*
3082 * Override the parent's default value for this column
3083 * (coldef->cooked_default) with the partition's local
3084 * definition (restdef->raw_default), if there's one. It
3085 * should be physically impossible to get a cooked default
3086 * in the local definition or a raw default in the
3087 * inherited definition, but make sure they're nulls, for
3088 * future-proofing.
3089 */
3090 Assert(restdef->cooked_default == NULL);
3091 Assert(coldef->raw_default == NULL);
3092 if (restdef->raw_default)
3093 {
3094 coldef->raw_default = restdef->raw_default;
3095 coldef->cooked_default = NULL;
3096 }
3097 }
3098 }
3099
3100 /* complain for constraints on columns not in parent */
3101 if (!found)
3102 ereport(ERROR,
3103 (errcode(ERRCODE_UNDEFINED_COLUMN),
3104 errmsg("column \"%s\" does not exist",
3105 restdef->colname)));
3106 }
3107 }
3108
3109 /*
3110 * If we found any conflicting parent default values, check to make sure
3111 * they were overridden by the child.
3112 */
3113 if (have_bogus_defaults)
3114 {
3115 foreach(lc, columns)
3116 {
3117 ColumnDef *def = lfirst(lc);
3118
3119 if (def->cooked_default == &bogus_marker)
3120 {
3121 if (def->generated)
3122 ereport(ERROR,
3123 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3124 errmsg("column \"%s\" inherits conflicting generation expressions",
3125 def->colname),
3126 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3127 else
3128 ereport(ERROR,
3129 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3130 errmsg("column \"%s\" inherits conflicting default values",
3131 def->colname),
3132 errhint("To resolve the conflict, specify a default explicitly.")));
3133 }
3134 }
3135 }
3136
3137 *supconstr = constraints;
3138 *supnotnulls = nnconstraints;
3139
3140 return columns;
3141}
3142
3143
3144/*
3145 * MergeCheckConstraint
3146 * Try to merge an inherited CHECK constraint with previous ones
3147 *
3148 * If we inherit identically-named constraints from multiple parents, we must
3149 * merge them, or throw an error if they don't have identical definitions.
3150 *
3151 * constraints is a list of CookedConstraint structs for previous constraints.
3152 *
3153 * If the new constraint matches an existing one, then the existing
3154 * constraint's inheritance count is updated. If there is a conflict (same
3155 * name but different expression), throw an error. If the constraint neither
3156 * matches nor conflicts with an existing one, a new constraint is appended to
3157 * the list.
3158 */
3159static List *
3160MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3161{
3162 ListCell *lc;
3163 CookedConstraint *newcon;
3164
3165 foreach(lc, constraints)
3166 {
3168
3169 Assert(ccon->contype == CONSTR_CHECK);
3170
3171 /* Non-matching names never conflict */
3172 if (strcmp(ccon->name, name) != 0)
3173 continue;
3174
3175 if (equal(expr, ccon->expr))
3176 {
3177 /* OK to merge constraint with existing */
3178 if (pg_add_s16_overflow(ccon->inhcount, 1,
3179 &ccon->inhcount))
3180 ereport(ERROR,
3181 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3182 errmsg("too many inheritance parents"));
3183
3184 /*
3185 * When enforceability differs, the merged constraint should be
3186 * marked as ENFORCED because one of the parents is ENFORCED.
3187 */
3188 if (!ccon->is_enforced && is_enforced)
3189 {
3190 ccon->is_enforced = true;
3191 ccon->skip_validation = false;
3192 }
3193
3194 return constraints;
3195 }
3196
3197 ereport(ERROR,
3199 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3200 name)));
3201 }
3202
3203 /*
3204 * Constraint couldn't be merged with an existing one and also didn't
3205 * conflict with an existing one, so add it as a new one to the list.
3206 */
3208 newcon->contype = CONSTR_CHECK;
3209 newcon->name = pstrdup(name);
3210 newcon->expr = expr;
3211 newcon->inhcount = 1;
3212 newcon->is_enforced = is_enforced;
3213 newcon->skip_validation = !is_enforced;
3214 return lappend(constraints, newcon);
3215}
3216
3217/*
3218 * MergeChildAttribute
3219 * Merge given child attribute definition into given inherited attribute.
3220 *
3221 * Input arguments:
3222 * 'inh_columns' is the list of inherited ColumnDefs.
3223 * 'exist_attno' is the number of the inherited attribute in inh_columns
3224 * 'newcol_attno' is the attribute number in child table's schema definition
3225 * 'newdef' is the column/attribute definition from the child table.
3226 *
3227 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3228 * ColumnDef remains unchanged.
3229 *
3230 * Notes:
3231 * - The attribute is merged according to the rules laid out in the prologue
3232 * of MergeAttributes().
3233 * - If matching inherited attribute exists but the child attribute can not be
3234 * merged into it, the function throws respective errors.
3235 * - A partition can not have its own column definitions. Hence this function
3236 * is applicable only to a regular inheritance child.
3237 */
3238static void
3239MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3240{
3241 char *attributeName = newdef->colname;
3242 ColumnDef *inhdef;
3243 Oid inhtypeid,
3244 newtypeid;
3245 int32 inhtypmod,
3246 newtypmod;
3247 Oid inhcollid,
3248 newcollid;
3249
3250 if (exist_attno == newcol_attno)
3252 (errmsg("merging column \"%s\" with inherited definition",
3253 attributeName)));
3254 else
3256 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3257 errdetail("User-specified column moved to the position of the inherited column.")));
3258
3259 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3260
3261 /*
3262 * Must have the same type and typmod
3263 */
3264 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3265 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3266 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3267 ereport(ERROR,
3268 (errcode(ERRCODE_DATATYPE_MISMATCH),
3269 errmsg("column \"%s\" has a type conflict",
3270 attributeName),
3271 errdetail("%s versus %s",
3272 format_type_with_typemod(inhtypeid, inhtypmod),
3273 format_type_with_typemod(newtypeid, newtypmod))));
3274
3275 /*
3276 * Must have the same collation
3277 */
3278 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3279 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3280 if (inhcollid != newcollid)
3281 ereport(ERROR,
3282 (errcode(ERRCODE_COLLATION_MISMATCH),
3283 errmsg("column \"%s\" has a collation conflict",
3284 attributeName),
3285 errdetail("\"%s\" versus \"%s\"",
3286 get_collation_name(inhcollid),
3287 get_collation_name(newcollid))));
3288
3289 /*
3290 * Identity is never inherited by a regular inheritance child. Pick
3291 * child's identity definition if there's one.
3292 */
3293 inhdef->identity = newdef->identity;
3294
3295 /*
3296 * Copy storage parameter
3297 */
3298 if (inhdef->storage == 0)
3299 inhdef->storage = newdef->storage;
3300 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3301 ereport(ERROR,
3302 (errcode(ERRCODE_DATATYPE_MISMATCH),
3303 errmsg("column \"%s\" has a storage parameter conflict",
3304 attributeName),
3305 errdetail("%s versus %s",
3306 storage_name(inhdef->storage),
3307 storage_name(newdef->storage))));
3308
3309 /*
3310 * Copy compression parameter
3311 */
3312 if (inhdef->compression == NULL)
3313 inhdef->compression = newdef->compression;
3314 else if (newdef->compression != NULL)
3315 {
3316 if (strcmp(inhdef->compression, newdef->compression) != 0)
3317 ereport(ERROR,
3318 (errcode(ERRCODE_DATATYPE_MISMATCH),
3319 errmsg("column \"%s\" has a compression method conflict",
3320 attributeName),
3321 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3322 }
3323
3324 /*
3325 * Merge of not-null constraints = OR 'em together
3326 */
3327 inhdef->is_not_null |= newdef->is_not_null;
3328
3329 /*
3330 * Check for conflicts related to generated columns.
3331 *
3332 * If the parent column is generated, the child column will be made a
3333 * generated column if it isn't already. If it is a generated column,
3334 * we'll take its generation expression in preference to the parent's. We
3335 * must check that the child column doesn't specify a default value or
3336 * identity, which matches the rules for a single column in
3337 * parse_utilcmd.c.
3338 *
3339 * Conversely, if the parent column is not generated, the child column
3340 * can't be either. (We used to allow that, but it results in being able
3341 * to override the generation expression via UPDATEs through the parent.)
3342 */
3343 if (inhdef->generated)
3344 {
3345 if (newdef->raw_default && !newdef->generated)
3346 ereport(ERROR,
3347 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3348 errmsg("column \"%s\" inherits from generated column but specifies default",
3349 inhdef->colname)));
3350 if (newdef->identity)
3351 ereport(ERROR,
3352 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3353 errmsg("column \"%s\" inherits from generated column but specifies identity",
3354 inhdef->colname)));
3355 }
3356 else
3357 {
3358 if (newdef->generated)
3359 ereport(ERROR,
3360 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3361 errmsg("child column \"%s\" specifies generation expression",
3362 inhdef->colname),
3363 errhint("A child table column cannot be generated unless its parent column is.")));
3364 }
3365
3366 if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3367 ereport(ERROR,
3368 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3369 errmsg("column \"%s\" inherits from generated column of different kind",
3370 inhdef->colname),
3371 errdetail("Parent column is %s, child column is %s.",
3372 inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3373 newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3374
3375 /*
3376 * If new def has a default, override previous default
3377 */
3378 if (newdef->raw_default != NULL)
3379 {
3380 inhdef->raw_default = newdef->raw_default;
3381 inhdef->cooked_default = newdef->cooked_default;
3382 }
3383
3384 /* Mark the column as locally defined */
3385 inhdef->is_local = true;
3386}
3387
3388/*
3389 * MergeInheritedAttribute
3390 * Merge given parent attribute definition into specified attribute
3391 * inherited from the previous parents.
3392 *
3393 * Input arguments:
3394 * 'inh_columns' is the list of previously inherited ColumnDefs.
3395 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3396 * 'newdef' is the new parent column/attribute definition to be merged.
3397 *
3398 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3399 *
3400 * Notes:
3401 * - The attribute is merged according to the rules laid out in the prologue
3402 * of MergeAttributes().
3403 * - If matching inherited attribute exists but the new attribute can not be
3404 * merged into it, the function throws respective errors.
3405 * - A partition inherits from only a single parent. Hence this function is
3406 * applicable only to a regular inheritance.
3407 */
3408static ColumnDef *
3410 int exist_attno,
3411 const ColumnDef *newdef)
3412{
3413 char *attributeName = newdef->colname;
3414 ColumnDef *prevdef;
3415 Oid prevtypeid,
3416 newtypeid;
3417 int32 prevtypmod,
3418 newtypmod;
3419 Oid prevcollid,
3420 newcollid;
3421
3423 (errmsg("merging multiple inherited definitions of column \"%s\"",
3424 attributeName)));
3425 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3426
3427 /*
3428 * Must have the same type and typmod
3429 */
3430 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3431 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3432 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3433 ereport(ERROR,
3434 (errcode(ERRCODE_DATATYPE_MISMATCH),
3435 errmsg("inherited column \"%s\" has a type conflict",
3436 attributeName),
3437 errdetail("%s versus %s",
3438 format_type_with_typemod(prevtypeid, prevtypmod),
3439 format_type_with_typemod(newtypeid, newtypmod))));
3440
3441 /*
3442 * Must have the same collation
3443 */
3444 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3445 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3446 if (prevcollid != newcollid)
3447 ereport(ERROR,
3448 (errcode(ERRCODE_COLLATION_MISMATCH),
3449 errmsg("inherited column \"%s\" has a collation conflict",
3450 attributeName),
3451 errdetail("\"%s\" versus \"%s\"",
3452 get_collation_name(prevcollid),
3453 get_collation_name(newcollid))));
3454
3455 /*
3456 * Copy/check storage parameter
3457 */
3458 if (prevdef->storage == 0)
3459 prevdef->storage = newdef->storage;
3460 else if (prevdef->storage != newdef->storage)
3461 ereport(ERROR,
3462 (errcode(ERRCODE_DATATYPE_MISMATCH),
3463 errmsg("inherited column \"%s\" has a storage parameter conflict",
3464 attributeName),
3465 errdetail("%s versus %s",
3466 storage_name(prevdef->storage),
3467 storage_name(newdef->storage))));
3468
3469 /*
3470 * Copy/check compression parameter
3471 */
3472 if (prevdef->compression == NULL)
3473 prevdef->compression = newdef->compression;
3474 else if (newdef->compression != NULL)
3475 {
3476 if (strcmp(prevdef->compression, newdef->compression) != 0)
3477 ereport(ERROR,
3478 (errcode(ERRCODE_DATATYPE_MISMATCH),
3479 errmsg("column \"%s\" has a compression method conflict",
3480 attributeName),
3481 errdetail("%s versus %s",
3482 prevdef->compression, newdef->compression)));
3483 }
3484
3485 /*
3486 * Check for GENERATED conflicts
3487 */
3488 if (prevdef->generated != newdef->generated)
3489 ereport(ERROR,
3490 (errcode(ERRCODE_DATATYPE_MISMATCH),
3491 errmsg("inherited column \"%s\" has a generation conflict",
3492 attributeName)));
3493
3494 /*
3495 * Default and other constraints are handled by the caller.
3496 */
3497
3498 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3499 &prevdef->inhcount))
3500 ereport(ERROR,
3501 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3502 errmsg("too many inheritance parents"));
3503
3504 return prevdef;
3505}
3506
3507/*
3508 * StoreCatalogInheritance
3509 * Updates the system catalogs with proper inheritance information.
3510 *
3511 * supers is a list of the OIDs of the new relation's direct ancestors.
3512 */
3513static void
3515 bool child_is_partition)
3516{
3517 Relation relation;
3518 int32 seqNumber;
3519 ListCell *entry;
3520
3521 /*
3522 * sanity checks
3523 */
3524 Assert(OidIsValid(relationId));
3525
3526 if (supers == NIL)
3527 return;
3528
3529 /*
3530 * Store INHERITS information in pg_inherits using direct ancestors only.
3531 * Also enter dependencies on the direct ancestors, and make sure they are
3532 * marked with relhassubclass = true.
3533 *
3534 * (Once upon a time, both direct and indirect ancestors were found here
3535 * and then entered into pg_ipl. Since that catalog doesn't exist
3536 * anymore, there's no need to look for indirect ancestors.)
3537 */
3538 relation = table_open(InheritsRelationId, RowExclusiveLock);
3539
3540 seqNumber = 1;
3541 foreach(entry, supers)
3542 {
3543 Oid parentOid = lfirst_oid(entry);
3544
3545 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3546 child_is_partition);
3547 seqNumber++;
3548 }
3549
3550 table_close(relation, RowExclusiveLock);
3551}
3552
3553/*
3554 * Make catalog entries showing relationId as being an inheritance child
3555 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3556 */
3557static void
3558StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3559 int32 seqNumber, Relation inhRelation,
3560 bool child_is_partition)
3561{
3562 ObjectAddress childobject,
3563 parentobject;
3564
3565 /* store the pg_inherits row */
3566 StoreSingleInheritance(relationId, parentOid, seqNumber);
3567
3568 /*
3569 * Store a dependency too
3570 */
3571 parentobject.classId = RelationRelationId;
3572 parentobject.objectId = parentOid;
3573 parentobject.objectSubId = 0;
3574 childobject.classId = RelationRelationId;
3575 childobject.objectId = relationId;
3576 childobject.objectSubId = 0;
3577
3578 recordDependencyOn(&childobject, &parentobject,
3579 child_dependency_type(child_is_partition));
3580
3581 /*
3582 * Post creation hook of this inheritance. Since object_access_hook
3583 * doesn't take multiple object identifiers, we relay oid of parent
3584 * relation using auxiliary_id argument.
3585 */
3586 InvokeObjectPostAlterHookArg(InheritsRelationId,
3587 relationId, 0,
3588 parentOid, false);
3589
3590 /*
3591 * Mark the parent as having subclasses.
3592 */
3593 SetRelationHasSubclass(parentOid, true);
3594}
3595
3596/*
3597 * Look for an existing column entry with the given name.
3598 *
3599 * Returns the index (starting with 1) if attribute already exists in columns,
3600 * 0 if it doesn't.
3601 */
3602static int
3603findAttrByName(const char *attributeName, const List *columns)
3604{
3605 ListCell *lc;
3606 int i = 1;
3607
3608 foreach(lc, columns)
3609 {
3610 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3611 return i;
3612
3613 i++;
3614 }
3615 return 0;
3616}
3617
3618
3619/*
3620 * SetRelationHasSubclass
3621 * Set the value of the relation's relhassubclass field in pg_class.
3622 *
3623 * It's always safe to set this field to true, because all SQL commands are
3624 * ready to see true and then find no children. On the other hand, commands
3625 * generally assume zero children if this is false.
3626 *
3627 * Caller must hold any self-exclusive lock until end of transaction. If the
3628 * new value is false, caller must have acquired that lock before reading the
3629 * evidence that justified the false value. That way, it properly waits if
3630 * another backend is simultaneously concluding no need to change the tuple
3631 * (new and old values are true).
3632 *
3633 * NOTE: an important side-effect of this operation is that an SI invalidation
3634 * message is sent out to all backends --- including me --- causing plans
3635 * referencing the relation to be rebuilt with the new list of children.
3636 * This must happen even if we find that no change is needed in the pg_class
3637 * row.
3638 */
3639void
3640SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3641{
3642 Relation relationRelation;
3643 HeapTuple tuple;
3644 Form_pg_class classtuple;
3645
3647 ShareUpdateExclusiveLock, false) ||
3648 CheckRelationOidLockedByMe(relationId,
3649 ShareRowExclusiveLock, true));
3650
3651 /*
3652 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3653 */
3654 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3655 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3656 if (!HeapTupleIsValid(tuple))
3657 elog(ERROR, "cache lookup failed for relation %u", relationId);
3658 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3659
3660 if (classtuple->relhassubclass != relhassubclass)
3661 {
3662 classtuple->relhassubclass = relhassubclass;
3663 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3664 }
3665 else
3666 {
3667 /* no need to change tuple, but force relcache rebuild anyway */
3669 }
3670
3671 heap_freetuple(tuple);
3672 table_close(relationRelation, RowExclusiveLock);
3673}
3674
3675/*
3676 * CheckRelationTableSpaceMove
3677 * Check if relation can be moved to new tablespace.
3678 *
3679 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3680 *
3681 * Returns true if the relation can be moved to the new tablespace; raises
3682 * an error if it is not possible to do the move; returns false if the move
3683 * would have no effect.
3684 */
3685bool
3687{
3688 Oid oldTableSpaceId;
3689
3690 /*
3691 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3692 * stored as 0.
3693 */
3694 oldTableSpaceId = rel->rd_rel->reltablespace;
3695 if (newTableSpaceId == oldTableSpaceId ||
3696 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3697 return false;
3698
3699 /*
3700 * We cannot support moving mapped relations into different tablespaces.
3701 * (In particular this eliminates all shared catalogs.)
3702 */
3703 if (RelationIsMapped(rel))
3704 ereport(ERROR,
3705 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3706 errmsg("cannot move system relation \"%s\"",
3708
3709 /* Cannot move a non-shared relation into pg_global */
3710 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3711 ereport(ERROR,
3712 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3713 errmsg("only shared relations can be placed in pg_global tablespace")));
3714
3715 /*
3716 * Do not allow moving temp tables of other backends ... their local
3717 * buffer manager is not going to cope.
3718 */
3719 if (RELATION_IS_OTHER_TEMP(rel))
3720 ereport(ERROR,
3721 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3722 errmsg("cannot move temporary tables of other sessions")));
3723
3724 return true;
3725}
3726
3727/*
3728 * SetRelationTableSpace
3729 * Set new reltablespace and relfilenumber in pg_class entry.
3730 *
3731 * newTableSpaceId is the new tablespace for the relation, and
3732 * newRelFilenumber its new filenumber. If newRelFilenumber is
3733 * InvalidRelFileNumber, this field is not updated.
3734 *
3735 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3736 *
3737 * The caller of this routine had better check if a relation can be
3738 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3739 * first, and is responsible for making the change visible with
3740 * CommandCounterIncrement().
3741 */
3742void
3744 Oid newTableSpaceId,
3745 RelFileNumber newRelFilenumber)
3746{
3747 Relation pg_class;
3748 HeapTuple tuple;
3749 ItemPointerData otid;
3750 Form_pg_class rd_rel;
3751 Oid reloid = RelationGetRelid(rel);
3752
3753 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3754
3755 /* Get a modifiable copy of the relation's pg_class row. */
3756 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3757
3758 tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3759 if (!HeapTupleIsValid(tuple))
3760 elog(ERROR, "cache lookup failed for relation %u", reloid);
3761 otid = tuple->t_self;
3762 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3763
3764 /* Update the pg_class row. */
3765 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3766 InvalidOid : newTableSpaceId;
3767 if (RelFileNumberIsValid(newRelFilenumber))
3768 rd_rel->relfilenode = newRelFilenumber;
3769 CatalogTupleUpdate(pg_class, &otid, tuple);
3770 UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3771
3772 /*
3773 * Record dependency on tablespace. This is only required for relations
3774 * that have no physical storage.
3775 */
3776 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3777 changeDependencyOnTablespace(RelationRelationId, reloid,
3778 rd_rel->reltablespace);
3779
3780 heap_freetuple(tuple);
3781 table_close(pg_class, RowExclusiveLock);
3782}
3783
3784/*
3785 * renameatt_check - basic sanity checks before attribute rename
3786 */
3787static void
3788renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3789{
3790 char relkind = classform->relkind;
3791
3792 if (classform->reloftype && !recursing)
3793 ereport(ERROR,
3794 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3795 errmsg("cannot rename column of typed table")));
3796
3797 /*
3798 * Renaming the columns of sequences or toast tables doesn't actually
3799 * break anything from the system's point of view, since internal
3800 * references are by attnum. But it doesn't seem right to allow users to
3801 * change names that are hardcoded into the system, hence the following
3802 * restriction.
3803 */
3804 if (relkind != RELKIND_RELATION &&
3805 relkind != RELKIND_VIEW &&
3806 relkind != RELKIND_MATVIEW &&
3807 relkind != RELKIND_COMPOSITE_TYPE &&
3808 relkind != RELKIND_INDEX &&
3809 relkind != RELKIND_PARTITIONED_INDEX &&
3810 relkind != RELKIND_FOREIGN_TABLE &&
3811 relkind != RELKIND_PARTITIONED_TABLE)
3812 ereport(ERROR,
3813 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3814 errmsg("cannot rename columns of relation \"%s\"",
3815 NameStr(classform->relname)),
3817
3818 /*
3819 * permissions checking. only the owner of a class can change its schema.
3820 */
3821 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3823 NameStr(classform->relname));
3824 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3825 ereport(ERROR,
3826 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3827 errmsg("permission denied: \"%s\" is a system catalog",
3828 NameStr(classform->relname))));
3829}
3830
3831/*
3832 * renameatt_internal - workhorse for renameatt
3833 *
3834 * Return value is the attribute number in the 'myrelid' relation.
3835 */
3836static AttrNumber
3838 const char *oldattname,
3839 const char *newattname,
3840 bool recurse,
3841 bool recursing,
3842 int expected_parents,
3843 DropBehavior behavior)
3844{
3845 Relation targetrelation;
3846 Relation attrelation;
3847 HeapTuple atttup;
3848 Form_pg_attribute attform;
3850
3851 /*
3852 * Grab an exclusive lock on the target table, which we will NOT release
3853 * until end of transaction.
3854 */
3855 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3856 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3857
3858 /*
3859 * if the 'recurse' flag is set then we are supposed to rename this
3860 * attribute in all classes that inherit from 'relname' (as well as in
3861 * 'relname').
3862 *
3863 * any permissions or problems with duplicate attributes will cause the
3864 * whole transaction to abort, which is what we want -- all or nothing.
3865 */
3866 if (recurse)
3867 {
3868 List *child_oids,
3869 *child_numparents;
3870 ListCell *lo,
3871 *li;
3872
3873 /*
3874 * we need the number of parents for each child so that the recursive
3875 * calls to renameatt() can determine whether there are any parents
3876 * outside the inheritance hierarchy being processed.
3877 */
3878 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3879 &child_numparents);
3880
3881 /*
3882 * find_all_inheritors does the recursive search of the inheritance
3883 * hierarchy, so all we have to do is process all of the relids in the
3884 * list that it returns.
3885 */
3886 forboth(lo, child_oids, li, child_numparents)
3887 {
3888 Oid childrelid = lfirst_oid(lo);
3889 int numparents = lfirst_int(li);
3890
3891 if (childrelid == myrelid)
3892 continue;
3893 /* note we need not recurse again */
3894 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3895 }
3896 }
3897 else
3898 {
3899 /*
3900 * If we are told not to recurse, there had better not be any child
3901 * tables; else the rename would put them out of step.
3902 *
3903 * expected_parents will only be 0 if we are not already recursing.
3904 */
3905 if (expected_parents == 0 &&
3907 ereport(ERROR,
3908 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3909 errmsg("inherited column \"%s\" must be renamed in child tables too",
3910 oldattname)));
3911 }
3912
3913 /* rename attributes in typed tables of composite type */
3914 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3915 {
3916 List *child_oids;
3917 ListCell *lo;
3918
3919 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3920 RelationGetRelationName(targetrelation),
3921 behavior);
3922
3923 foreach(lo, child_oids)
3924 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3925 }
3926
3927 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3928
3929 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3930 if (!HeapTupleIsValid(atttup))
3931 ereport(ERROR,
3932 (errcode(ERRCODE_UNDEFINED_COLUMN),
3933 errmsg("column \"%s\" does not exist",
3934 oldattname)));
3935 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3936
3937 attnum = attform->attnum;
3938 if (attnum <= 0)
3939 ereport(ERROR,
3940 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3941 errmsg("cannot rename system column \"%s\"",
3942 oldattname)));
3943
3944 /*
3945 * if the attribute is inherited, forbid the renaming. if this is a
3946 * top-level call to renameatt(), then expected_parents will be 0, so the
3947 * effect of this code will be to prohibit the renaming if the attribute
3948 * is inherited at all. if this is a recursive call to renameatt(),
3949 * expected_parents will be the number of parents the current relation has
3950 * within the inheritance hierarchy being processed, so we'll prohibit the
3951 * renaming only if there are additional parents from elsewhere.
3952 */
3953 if (attform->attinhcount > expected_parents)
3954 ereport(ERROR,
3955 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3956 errmsg("cannot rename inherited column \"%s\"",
3957 oldattname)));
3958
3959 /* new name should not already exist */
3960 (void) check_for_column_name_collision(targetrelation, newattname, false);
3961
3962 /* apply the update */
3963 namestrcpy(&(attform->attname), newattname);
3964
3965 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3966
3967 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3968
3969 heap_freetuple(atttup);
3970
3971 table_close(attrelation, RowExclusiveLock);
3972
3973 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3974
3975 return attnum;
3976}
3977
3978/*
3979 * Perform permissions and integrity checks before acquiring a relation lock.
3980 */
3981static void
3983 void *arg)
3984{
3985 HeapTuple tuple;
3986 Form_pg_class form;
3987
3988 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3989 if (!HeapTupleIsValid(tuple))
3990 return; /* concurrently dropped */
3991 form = (Form_pg_class) GETSTRUCT(tuple);
3992 renameatt_check(relid, form, false);
3993 ReleaseSysCache(tuple);
3994}
3995
3996/*
3997 * renameatt - changes the name of an attribute in a relation
3998 *
3999 * The returned ObjectAddress is that of the renamed column.
4000 */
4003{
4004 Oid relid;
4006 ObjectAddress address;
4007
4008 /* lock level taken here should match renameatt_internal */
4010 stmt->missing_ok ? RVR_MISSING_OK : 0,
4012 NULL);
4013
4014 if (!OidIsValid(relid))
4015 {
4017 (errmsg("relation \"%s\" does not exist, skipping",
4018 stmt->relation->relname)));
4019 return InvalidObjectAddress;
4020 }
4021
4022 attnum =
4023 renameatt_internal(relid,
4024 stmt->subname, /* old att name */
4025 stmt->newname, /* new att name */
4026 stmt->relation->inh, /* recursive? */
4027 false, /* recursing? */
4028 0, /* expected inhcount */
4029 stmt->behavior);
4030
4031 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4032
4033 return address;
4034}
4035
4036/*
4037 * same logic as renameatt_internal
4038 */
4039static ObjectAddress
4041 Oid mytypid,
4042 const char *oldconname,
4043 const char *newconname,
4044 bool recurse,
4045 bool recursing,
4046 int expected_parents)
4047{
4048 Relation targetrelation = NULL;
4049 Oid constraintOid;
4050 HeapTuple tuple;
4052 ObjectAddress address;
4053
4054 Assert(!myrelid || !mytypid);
4055
4056 if (mytypid)
4057 {
4058 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4059 }
4060 else
4061 {
4062 targetrelation = relation_open(myrelid, AccessExclusiveLock);
4063
4064 /*
4065 * don't tell it whether we're recursing; we allow changing typed
4066 * tables here
4067 */
4068 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4069
4070 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4071 }
4072
4073 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4074 if (!HeapTupleIsValid(tuple))
4075 elog(ERROR, "cache lookup failed for constraint %u",
4076 constraintOid);
4077 con = (Form_pg_constraint) GETSTRUCT(tuple);
4078
4079 if (myrelid &&
4080 (con->contype == CONSTRAINT_CHECK ||
4081 con->contype == CONSTRAINT_NOTNULL) &&
4082 !con->connoinherit)
4083 {
4084 if (recurse)
4085 {
4086 List *child_oids,
4087 *child_numparents;
4088 ListCell *lo,
4089 *li;
4090
4091 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4092 &child_numparents);
4093
4094 forboth(lo, child_oids, li, child_numparents)
4095 {
4096 Oid childrelid = lfirst_oid(lo);
4097 int numparents = lfirst_int(li);
4098
4099 if (childrelid == myrelid)
4100 continue;
4101
4102 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4103 }
4104 }
4105 else
4106 {
4107 if (expected_parents == 0 &&
4109 ereport(ERROR,
4110 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4111 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4112 oldconname)));
4113 }
4114
4115 if (con->coninhcount > expected_parents)
4116 ereport(ERROR,
4117 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4118 errmsg("cannot rename inherited constraint \"%s\"",
4119 oldconname)));
4120 }
4121
4122 if (con->conindid
4123 && (con->contype == CONSTRAINT_PRIMARY
4124 || con->contype == CONSTRAINT_UNIQUE
4125 || con->contype == CONSTRAINT_EXCLUSION))
4126 /* rename the index; this renames the constraint as well */
4127 RenameRelationInternal(con->conindid, newconname, false, true);
4128 else
4129 RenameConstraintById(constraintOid, newconname);
4130
4131 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4132
4133 ReleaseSysCache(tuple);
4134
4135 if (targetrelation)
4136 {
4137 /*
4138 * Invalidate relcache so as others can see the new constraint name.
4139 */
4140 CacheInvalidateRelcache(targetrelation);
4141
4142 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4143 }
4144
4145 return address;
4146}
4147
4150{
4151 Oid relid = InvalidOid;
4152 Oid typid = InvalidOid;
4153
4154 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4155 {
4156 Relation rel;
4157 HeapTuple tup;
4158
4159 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4160 rel = table_open(TypeRelationId, RowExclusiveLock);
4161 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4162 if (!HeapTupleIsValid(tup))
4163 elog(ERROR, "cache lookup failed for type %u", typid);
4164 checkDomainOwner(tup);
4165 ReleaseSysCache(tup);
4166 table_close(rel, NoLock);
4167 }
4168 else
4169 {
4170 /* lock level taken here should match rename_constraint_internal */
4172 stmt->missing_ok ? RVR_MISSING_OK : 0,
4174 NULL);
4175 if (!OidIsValid(relid))
4176 {
4178 (errmsg("relation \"%s\" does not exist, skipping",
4179 stmt->relation->relname)));
4180 return InvalidObjectAddress;
4181 }
4182 }
4183
4184 return
4185 rename_constraint_internal(relid, typid,
4186 stmt->subname,
4187 stmt->newname,
4188 (stmt->relation &&
4189 stmt->relation->inh), /* recursive? */
4190 false, /* recursing? */
4191 0 /* expected inhcount */ );
4192}
4193
4194/*
4195 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4196 * RENAME
4197 */
4200{
4201 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4202 Oid relid;
4203 ObjectAddress address;
4204
4205 /*
4206 * Grab an exclusive lock on the target table, index, sequence, view,
4207 * materialized view, or foreign table, which we will NOT release until
4208 * end of transaction.
4209 *
4210 * Lock level used here should match RenameRelationInternal, to avoid lock
4211 * escalation. However, because ALTER INDEX can be used with any relation
4212 * type, we mustn't believe without verification.
4213 */
4214 for (;;)
4215 {
4216 LOCKMODE lockmode;
4217 char relkind;
4218 bool obj_is_index;
4219
4220 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4221
4222 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4223 stmt->missing_ok ? RVR_MISSING_OK : 0,
4225 stmt);
4226
4227 if (!OidIsValid(relid))
4228 {
4230 (errmsg("relation \"%s\" does not exist, skipping",
4231 stmt->relation->relname)));
4232 return InvalidObjectAddress;
4233 }
4234
4235 /*
4236 * We allow mismatched statement and object types (e.g., ALTER INDEX
4237 * to rename a table), but we might've used the wrong lock level. If
4238 * that happens, retry with the correct lock level. We don't bother
4239 * if we already acquired AccessExclusiveLock with an index, however.
4240 */
4241 relkind = get_rel_relkind(relid);
4242 obj_is_index = (relkind == RELKIND_INDEX ||
4243 relkind == RELKIND_PARTITIONED_INDEX);
4244 if (obj_is_index || is_index_stmt == obj_is_index)
4245 break;
4246
4247 UnlockRelationOid(relid, lockmode);
4248 is_index_stmt = obj_is_index;
4249 }
4250
4251 /* Do the work */
4252 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4253
4254 ObjectAddressSet(address, RelationRelationId, relid);
4255
4256 return address;
4257}
4258
4259/*
4260 * RenameRelationInternal - change the name of a relation
4261 */
4262void
4263RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4264{
4265 Relation targetrelation;
4266 Relation relrelation; /* for RELATION relation */
4267 ItemPointerData otid;
4268 HeapTuple reltup;
4269 Form_pg_class relform;
4270 Oid namespaceId;
4271
4272 /*
4273 * Grab a lock on the target relation, which we will NOT release until end
4274 * of transaction. We need at least a self-exclusive lock so that
4275 * concurrent DDL doesn't overwrite the rename if they start updating
4276 * while still seeing the old version. The lock also guards against
4277 * triggering relcache reloads in concurrent sessions, which might not
4278 * handle this information changing under them. For indexes, we can use a
4279 * reduced lock level because RelationReloadIndexInfo() handles indexes
4280 * specially.
4281 */
4282 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4283 namespaceId = RelationGetNamespace(targetrelation);
4284
4285 /*
4286 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4287 */
4288 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4289
4290 reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4291 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4292 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4293 otid = reltup->t_self;
4294 relform = (Form_pg_class) GETSTRUCT(reltup);
4295
4296 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4297 ereport(ERROR,
4298 (errcode(ERRCODE_DUPLICATE_TABLE),
4299 errmsg("relation \"%s\" already exists",
4300 newrelname)));
4301
4302 /*
4303 * RenameRelation is careful not to believe the caller's idea of the
4304 * relation kind being handled. We don't have to worry about this, but
4305 * let's not be totally oblivious to it. We can process an index as
4306 * not-an-index, but not the other way around.
4307 */
4308 Assert(!is_index ||
4309 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4310 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4311
4312 /*
4313 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4314 * because it's a copy...)
4315 */
4316 namestrcpy(&(relform->relname), newrelname);
4317
4318 CatalogTupleUpdate(relrelation, &otid, reltup);
4319 UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4320
4321 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4322 InvalidOid, is_internal);
4323
4324 heap_freetuple(reltup);
4325 table_close(relrelation, RowExclusiveLock);
4326
4327 /*
4328 * Also rename the associated type, if any.
4329 */
4330 if (OidIsValid(targetrelation->rd_rel->reltype))
4331 RenameTypeInternal(targetrelation->rd_rel->reltype,
4332 newrelname, namespaceId);
4333
4334 /*
4335 * Also rename the associated constraint, if any.
4336 */
4337 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4338 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4339 {
4340 Oid constraintId = get_index_constraint(myrelid);
4341
4342 if (OidIsValid(constraintId))
4343 RenameConstraintById(constraintId, newrelname);
4344 }
4345
4346 /*
4347 * Close rel, but keep lock!
4348 */
4349 relation_close(targetrelation, NoLock);
4350}
4351
4352/*
4353 * ResetRelRewrite - reset relrewrite
4354 */
4355void
4357{
4358 Relation relrelation; /* for RELATION relation */
4359 HeapTuple reltup;
4360 Form_pg_class relform;
4361
4362 /*
4363 * Find relation's pg_class tuple.
4364 */
4365 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4366
4367 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4368 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4369 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4370 relform = (Form_pg_class) GETSTRUCT(reltup);
4371
4372 /*
4373 * Update pg_class tuple.
4374 */
4375 relform->relrewrite = InvalidOid;
4376
4377 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4378
4379 heap_freetuple(reltup);
4380 table_close(relrelation, RowExclusiveLock);
4381}
4382
4383/*
4384 * Disallow ALTER TABLE (and similar commands) when the current backend has
4385 * any open reference to the target table besides the one just acquired by
4386 * the calling command; this implies there's an open cursor or active plan.
4387 * We need this check because our lock doesn't protect us against stomping
4388 * on our own foot, only other people's feet!
4389 *
4390 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4391 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4392 * possibly be relaxed to only error out for certain types of alterations.
4393 * But the use-case for allowing any of these things is not obvious, so we
4394 * won't work hard at it for now.
4395 *
4396 * We also reject these commands if there are any pending AFTER trigger events
4397 * for the rel. This is certainly necessary for the rewriting variants of
4398 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4399 * events would try to fetch the wrong tuples. It might be overly cautious
4400 * in other cases, but again it seems better to err on the side of paranoia.
4401 *
4402 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4403 * we are worried about active indexscans on the index. The trigger-event
4404 * check can be skipped, since we are doing no damage to the parent table.
4405 *
4406 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4407 */
4408void
4410{
4411 int expected_refcnt;
4412
4413 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4414 if (rel->rd_refcnt != expected_refcnt)
4415 ereport(ERROR,
4416 (errcode(ERRCODE_OBJECT_IN_USE),
4417 /* translator: first %s is a SQL command, eg ALTER TABLE */
4418 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4420
4421 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4422 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4424 ereport(ERROR,
4425 (errcode(ERRCODE_OBJECT_IN_USE),
4426 /* translator: first %s is a SQL command, eg ALTER TABLE */
4427 errmsg("cannot %s \"%s\" because it has pending trigger events",
4429}
4430
4431/*
4432 * CheckAlterTableIsSafe
4433 * Verify that it's safe to allow ALTER TABLE on this relation.
4434 *
4435 * This consists of CheckTableNotInUse() plus a check that the relation
4436 * isn't another session's temp table. We must split out the temp-table
4437 * check because there are callers of CheckTableNotInUse() that don't want
4438 * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4439 * an orphaned temp schema.) Compare truncate_check_activity().
4440 */
4441static void
4443{
4444 /*
4445 * Don't allow ALTER on temp tables of other backends. Their local buffer
4446 * manager is not going to cope if we need to change the table's contents.
4447 * Even if we don't, there may be optimizations that assume temp tables
4448 * aren't subject to such interference.
4449 */
4450 if (RELATION_IS_OTHER_TEMP(rel))
4451 ereport(ERROR,
4452 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4453 errmsg("cannot alter temporary tables of other sessions")));
4454
4455 /*
4456 * Also check for active uses of the relation in the current transaction,
4457 * including open scans and pending AFTER trigger events.
4458 */
4459 CheckTableNotInUse(rel, "ALTER TABLE");
4460}
4461
4462/*
4463 * AlterTableLookupRelation
4464 * Look up, and lock, the OID for the relation named by an alter table
4465 * statement.
4466 */
4467Oid
4469{
4470 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4471 stmt->missing_ok ? RVR_MISSING_OK : 0,
4473 stmt);
4474}
4475
4476/*
4477 * AlterTable
4478 * Execute ALTER TABLE, which can be a list of subcommands
4479 *
4480 * ALTER TABLE is performed in three phases:
4481 * 1. Examine subcommands and perform pre-transformation checking.
4482 * 2. Validate and transform subcommands, and update system catalogs.
4483 * 3. Scan table(s) to check new constraints, and optionally recopy
4484 * the data into new table(s).
4485 * Phase 3 is not performed unless one or more of the subcommands requires
4486 * it. The intention of this design is to allow multiple independent
4487 * updates of the table schema to be performed with only one pass over the
4488 * data.
4489 *
4490 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4491 * each table to be affected (there may be multiple affected tables if the
4492 * commands traverse a table inheritance hierarchy). Also we do preliminary
4493 * validation of the subcommands. Because earlier subcommands may change
4494 * the catalog state seen by later commands, there are limits to what can
4495 * be done in this phase. Generally, this phase acquires table locks,
4496 * checks permissions and relkind, and recurses to find child tables.
4497 *
4498 * ATRewriteCatalogs performs phase 2 for each affected table.
4499 * Certain subcommands need to be performed before others to avoid
4500 * unnecessary conflicts; for example, DROP COLUMN should come before
4501 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4502 * lists, one for each logical "pass" of phase 2.
4503 *
4504 * ATRewriteTables performs phase 3 for those tables that need it.
4505 *
4506 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4507 * since phase 1 already does it. However, for certain subcommand types
4508 * it is only possible to determine how to recurse at phase 2 time; for
4509 * those cases, phase 1 sets the cmd->recurse flag.
4510 *
4511 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4512 * the whole operation; we don't have to do anything special to clean up.
4513 *
4514 * The caller must lock the relation, with an appropriate lock level
4515 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4516 * or higher. We pass the lock level down
4517 * so that we can apply it recursively to inherited tables. Note that the
4518 * lock level we want as we recurse might well be higher than required for
4519 * that specific subcommand. So we pass down the overall lock requirement,
4520 * rather than reassess it at lower levels.
4521 *
4522 * The caller also provides a "context" which is to be passed back to
4523 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4524 * Some of the fields therein, such as the relid, are used here as well.
4525 */
4526void
4528 AlterTableUtilityContext *context)
4529{
4530 Relation rel;
4531
4532 /* Caller is required to provide an adequate lock. */
4533 rel = relation_open(context->relid, NoLock);
4534
4536
4537 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4538}
4539
4540/*
4541 * AlterTableInternal
4542 *
4543 * ALTER TABLE with target specified by OID
4544 *
4545 * We do not reject if the relation is already open, because it's quite
4546 * likely that one or more layers of caller have it open. That means it
4547 * is unsafe to use this entry point for alterations that could break
4548 * existing query plans. On the assumption it's not used for such, we
4549 * don't have to reject pending AFTER triggers, either.
4550 *
4551 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4552 * used for any subcommand types that require parse transformation or
4553 * could generate subcommands that have to be passed to ProcessUtility.
4554 */
4555void
4556AlterTableInternal(Oid relid, List *cmds, bool recurse)
4557{
4558 Relation rel;
4559 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4560
4561 rel = relation_open(relid, lockmode);
4562
4564
4565 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4566}
4567
4568/*
4569 * AlterTableGetLockLevel
4570 *
4571 * Sets the overall lock level required for the supplied list of subcommands.
4572 * Policy for doing this set according to needs of AlterTable(), see
4573 * comments there for overall explanation.
4574 *
4575 * Function is called before and after parsing, so it must give same
4576 * answer each time it is called. Some subcommands are transformed
4577 * into other subcommand types, so the transform must never be made to a
4578 * lower lock level than previously assigned. All transforms are noted below.
4579 *
4580 * Since this is called before we lock the table we cannot use table metadata
4581 * to influence the type of lock we acquire.
4582 *
4583 * There should be no lockmodes hardcoded into the subcommand functions. All
4584 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4585 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4586 * and does not travel through this section of code and cannot be combined with
4587 * any of the subcommands given here.
4588 *
4589 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4590 * so any changes that might affect SELECTs running on standbys need to use
4591 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4592 * have a solution for that also.
4593 *
4594 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4595 * that takes a lock less than AccessExclusiveLock can change object definitions
4596 * while pg_dump is running. Be careful to check that the appropriate data is
4597 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4598 * otherwise we might end up with an inconsistent dump that can't restore.
4599 */
4602{
4603 /*
4604 * This only works if we read catalog tables using MVCC snapshots.
4605 */
4606 ListCell *lcmd;
4608
4609 foreach(lcmd, cmds)
4610 {
4611 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4612 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4613
4614 switch (cmd->subtype)
4615 {
4616 /*
4617 * These subcommands rewrite the heap, so require full locks.
4618 */
4619 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4620 * to SELECT */
4621 case AT_SetAccessMethod: /* must rewrite heap */
4622 case AT_SetTableSpace: /* must rewrite heap */
4623 case AT_AlterColumnType: /* must rewrite heap */
4624 cmd_lockmode = AccessExclusiveLock;
4625 break;
4626
4627 /*
4628 * These subcommands may require addition of toast tables. If
4629 * we add a toast table to a table currently being scanned, we
4630 * might miss data added to the new toast table by concurrent
4631 * insert transactions.
4632 */
4633 case AT_SetStorage: /* may add toast tables, see
4634 * ATRewriteCatalogs() */
4635 cmd_lockmode = AccessExclusiveLock;
4636 break;
4637
4638 /*
4639 * Removing constraints can affect SELECTs that have been
4640 * optimized assuming the constraint holds true. See also
4641 * CloneFkReferenced.
4642 */
4643 case AT_DropConstraint: /* as DROP INDEX */
4644 case AT_DropNotNull: /* may change some SQL plans */
4645 cmd_lockmode = AccessExclusiveLock;
4646 break;
4647
4648 /*
4649 * Subcommands that may be visible to concurrent SELECTs
4650 */
4651 case AT_DropColumn: /* change visible to SELECT */
4652 case AT_AddColumnToView: /* CREATE VIEW */
4653 case AT_DropOids: /* used to equiv to DropColumn */
4654 case AT_EnableAlwaysRule: /* may change SELECT rules */
4655 case AT_EnableReplicaRule: /* may change SELECT rules */
4656 case AT_EnableRule: /* may change SELECT rules */
4657 case AT_DisableRule: /* may change SELECT rules */
4658 cmd_lockmode = AccessExclusiveLock;
4659 break;
4660
4661 /*
4662 * Changing owner may remove implicit SELECT privileges
4663 */
4664 case AT_ChangeOwner: /* change visible to SELECT */
4665 cmd_lockmode = AccessExclusiveLock;
4666 break;
4667
4668 /*
4669 * Changing foreign table options may affect optimization.
4670 */
4671 case AT_GenericOptions:
4673 cmd_lockmode = AccessExclusiveLock;
4674 break;
4675
4676 /*
4677 * These subcommands affect write operations only.
4678 */
4679 case AT_EnableTrig:
4682 case AT_EnableTrigAll:
4683 case AT_EnableTrigUser:
4684 case AT_DisableTrig:
4685 case AT_DisableTrigAll:
4686 case AT_DisableTrigUser:
4687 cmd_lockmode = ShareRowExclusiveLock;
4688 break;
4689
4690 /*
4691 * These subcommands affect write operations only. XXX
4692 * Theoretically, these could be ShareRowExclusiveLock.
4693 */
4694 case AT_ColumnDefault:
4696 case AT_AlterConstraint:
4697 case AT_AddIndex: /* from ADD CONSTRAINT */
4699 case AT_ReplicaIdentity:
4700 case AT_SetNotNull:
4705 case AT_AddIdentity:
4706 case AT_DropIdentity:
4707 case AT_SetIdentity:
4708 case AT_SetExpression:
4709 case AT_DropExpression:
4710 case AT_SetCompression:
4711 cmd_lockmode = AccessExclusiveLock;
4712 break;
4713
4714 case AT_AddConstraint:
4715 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4716 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4717 if (IsA(cmd->def, Constraint))
4718 {
4719 Constraint *con = (Constraint *) cmd->def;
4720
4721 switch (con->contype)
4722 {
4723 case CONSTR_EXCLUSION:
4724 case CONSTR_PRIMARY:
4725 case CONSTR_UNIQUE:
4726
4727 /*
4728 * Cases essentially the same as CREATE INDEX. We
4729 * could reduce the lock strength to ShareLock if
4730 * we can work out how to allow concurrent catalog
4731 * updates. XXX Might be set down to
4732 * ShareRowExclusiveLock but requires further
4733 * analysis.
4734 */
4735 cmd_lockmode = AccessExclusiveLock;
4736 break;
4737 case CONSTR_FOREIGN:
4738
4739 /*
4740 * We add triggers to both tables when we add a
4741 * Foreign Key, so the lock level must be at least
4742 * as strong as CREATE TRIGGER.
4743 */
4744 cmd_lockmode = ShareRowExclusiveLock;
4745 break;
4746
4747 default:
4748 cmd_lockmode = AccessExclusiveLock;
4749 }
4750 }
4751 break;
4752
4753 /*
4754 * These subcommands affect inheritance behaviour. Queries
4755 * started before us will continue to see the old inheritance
4756 * behaviour, while queries started after we commit will see
4757 * new behaviour. No need to prevent reads or writes to the
4758 * subtable while we hook it up though. Changing the TupDesc
4759 * may be a problem, so keep highest lock.
4760 */
4761 case AT_AddInherit:
4762 case AT_DropInherit:
4763 cmd_lockmode = AccessExclusiveLock;
4764 break;
4765
4766 /*
4767 * These subcommands affect implicit row type conversion. They
4768 * have affects similar to CREATE/DROP CAST on queries. don't
4769 * provide for invalidating parse trees as a result of such
4770 * changes, so we keep these at AccessExclusiveLock.
4771 */
4772 case AT_AddOf:
4773 case AT_DropOf:
4774 cmd_lockmode = AccessExclusiveLock;
4775 break;
4776
4777 /*
4778 * Only used by CREATE OR REPLACE VIEW which must conflict
4779 * with an SELECTs currently using the view.
4780 */
4782 cmd_lockmode = AccessExclusiveLock;
4783 break;
4784
4785 /*
4786 * These subcommands affect general strategies for performance
4787 * and maintenance, though don't change the semantic results
4788 * from normal data reads and writes. Delaying an ALTER TABLE
4789 * behind currently active writes only delays the point where
4790 * the new strategy begins to take effect, so there is no
4791 * benefit in waiting. In this case the minimum restriction
4792 * applies: we don't currently allow concurrent catalog
4793 * updates.
4794 */
4795 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4796 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4797 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4798 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4799 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4800 cmd_lockmode = ShareUpdateExclusiveLock;
4801 break;
4802
4803 case AT_SetLogged:
4804 case AT_SetUnLogged:
4805 cmd_lockmode = AccessExclusiveLock;
4806 break;
4807
4808 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4809 cmd_lockmode = ShareUpdateExclusiveLock;
4810 break;
4811
4812 /*
4813 * Rel options are more complex than first appears. Options
4814 * are set here for tables, views and indexes; for historical
4815 * reasons these can all be used with ALTER TABLE, so we can't
4816 * decide between them using the basic grammar.
4817 */
4818 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4819 * getTables() */
4820 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4821 * getTables() */
4822 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4823 break;
4824
4825 case AT_AttachPartition:
4826 cmd_lockmode = ShareUpdateExclusiveLock;
4827 break;
4828
4829 case AT_DetachPartition:
4830 if (((PartitionCmd *) cmd->def)->concurrent)
4831 cmd_lockmode = ShareUpdateExclusiveLock;
4832 else
4833 cmd_lockmode = AccessExclusiveLock;
4834 break;
4835
4837 cmd_lockmode = ShareUpdateExclusiveLock;
4838 break;
4839
4840 default: /* oops */
4841 elog(ERROR, "unrecognized alter table type: %d",
4842 (int) cmd->subtype);
4843 break;
4844 }
4845
4846 /*
4847 * Take the greatest lockmode from any subcommand
4848 */
4849 if (cmd_lockmode > lockmode)
4850 lockmode = cmd_lockmode;
4851 }
4852
4853 return lockmode;
4854}
4855
4856/*
4857 * ATController provides top level control over the phases.
4858 *
4859 * parsetree is passed in to allow it to be passed to event triggers
4860 * when requested.
4861 */
4862static void
4864 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4865 AlterTableUtilityContext *context)
4866{
4867 List *wqueue = NIL;
4868 ListCell *lcmd;
4869
4870 /* Phase 1: preliminary examination of commands, create work queue */
4871 foreach(lcmd, cmds)
4872 {
4873 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4874
4875 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4876 }
4877
4878 /* Close the relation, but keep lock until commit */
4879 relation_close(rel, NoLock);
4880
4881 /* Phase 2: update system catalogs */
4882 ATRewriteCatalogs(&wqueue, lockmode, context);
4883
4884 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4885 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4886}
4887
4888/*
4889 * ATPrepCmd
4890 *
4891 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4892 * recursion and permission checks.
4893 *
4894 * Caller must have acquired appropriate lock type on relation already.
4895 * This lock should be held until commit.
4896 */
4897static void
4899 bool recurse, bool recursing, LOCKMODE lockmode,
4900 AlterTableUtilityContext *context)
4901{
4902 AlteredTableInfo *tab;
4904
4905 /* Find or create work queue entry for this table */
4906 tab = ATGetQueueEntry(wqueue, rel);
4907
4908 /*
4909 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4910 * partitions that are pending detach.
4911 */
4912 if (rel->rd_rel->relispartition &&
4915 ereport(ERROR,
4916 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4917 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4919 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4920
4921 /*
4922 * Copy the original subcommand for each table, so we can scribble on it.
4923 * This avoids conflicts when different child tables need to make
4924 * different parse transformations (for example, the same column may have
4925 * different column numbers in different children).
4926 */
4927 cmd = copyObject(cmd);
4928
4929 /*
4930 * Do permissions and relkind checking, recursion to child tables if
4931 * needed, and any additional phase-1 processing needed. (But beware of
4932 * adding any processing that looks at table details that another
4933 * subcommand could change. In some cases we reject multiple subcommands
4934 * that could try to change the same state in contrary ways.)
4935 */
4936 switch (cmd->subtype)
4937 {
4938 case AT_AddColumn: /* ADD COLUMN */
4939 ATSimplePermissions(cmd->subtype, rel,
4942 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4943 lockmode, context);
4944 /* Recursion occurs during execution phase */
4945 pass = AT_PASS_ADD_COL;
4946 break;
4947 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4949 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4950 lockmode, context);
4951 /* Recursion occurs during execution phase */
4952 pass = AT_PASS_ADD_COL;
4953 break;
4954 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4955
4956 /*
4957 * We allow defaults on views so that INSERT into a view can have
4958 * default-ish behavior. This works because the rewriter
4959 * substitutes default values into INSERTs before it expands
4960 * rules.
4961 */
4962 ATSimplePermissions(cmd->subtype, rel,
4965 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4966 /* No command-specific prep needed */
4968 break;
4969 case AT_CookedColumnDefault: /* add a pre-cooked default */
4970 /* This is currently used only in CREATE TABLE */
4971 /* (so the permission check really isn't necessary) */
4972 ATSimplePermissions(cmd->subtype, rel,
4974 /* This command never recurses */
4976 break;
4977 case AT_AddIdentity:
4978 ATSimplePermissions(cmd->subtype, rel,
4981 /* Set up recursion for phase 2; no other prep needed */
4982 if (recurse)
4983 cmd->recurse = true;
4985 break;
4986 case AT_SetIdentity:
4987 ATSimplePermissions(cmd->subtype, rel,
4990 /* Set up recursion for phase 2; no other prep needed */
4991 if (recurse)
4992 cmd->recurse = true;
4993 /* This should run after AddIdentity, so do it in MISC pass */
4994 pass = AT_PASS_MISC;
4995 break;
4996 case AT_DropIdentity:
4997 ATSimplePermissions(cmd->subtype, rel,
5000 /* Set up recursion for phase 2; no other prep needed */
5001 if (recurse)
5002 cmd->recurse = true;
5003 pass = AT_PASS_DROP;
5004 break;
5005 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5006 ATSimplePermissions(cmd->subtype, rel,
5008 /* Set up recursion for phase 2; no other prep needed */
5009 if (recurse)
5010 cmd->recurse = true;
5011 pass = AT_PASS_DROP;
5012 break;
5013 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5014 ATSimplePermissions(cmd->subtype, rel,
5016 /* Set up recursion for phase 2; no other prep needed */
5017 if (recurse)
5018 cmd->recurse = true;
5019 pass = AT_PASS_COL_ATTRS;
5020 break;
5021 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5022 ATSimplePermissions(cmd->subtype, rel,
5024 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5026 break;
5027 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5028 ATSimplePermissions(cmd->subtype, rel,
5030 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5031 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5032 pass = AT_PASS_DROP;
5033 break;
5034 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5035 ATSimplePermissions(cmd->subtype, rel,
5038 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5039 /* No command-specific prep needed */
5040 pass = AT_PASS_MISC;
5041 break;
5042 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5043 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5044 ATSimplePermissions(cmd->subtype, rel,
5047 /* This command never recurses */
5048 pass = AT_PASS_MISC;
5049 break;
5050 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5051 ATSimplePermissions(cmd->subtype, rel,
5054 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5055 /* No command-specific prep needed */
5056 pass = AT_PASS_MISC;
5057 break;
5058 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5059 ATSimplePermissions(cmd->subtype, rel,
5061 /* This command never recurses */
5062 /* No command-specific prep needed */
5063 pass = AT_PASS_MISC;
5064 break;
5065 case AT_DropColumn: /* DROP COLUMN */
5066 ATSimplePermissions(cmd->subtype, rel,
5069 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5070 lockmode, context);
5071 /* Recursion occurs during execution phase */
5072 pass = AT_PASS_DROP;
5073 break;
5074 case AT_AddIndex: /* ADD INDEX */
5076 /* This command never recurses */
5077 /* No command-specific prep needed */
5078 pass = AT_PASS_ADD_INDEX;
5079 break;
5080 case AT_AddConstraint: /* ADD CONSTRAINT */
5081 ATSimplePermissions(cmd->subtype, rel,
5083 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5084 if (recurse)
5085 {
5086 /* recurses at exec time; lock descendants and set flag */
5087 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5088 cmd->recurse = true;
5089 }
5090 pass = AT_PASS_ADD_CONSTR;
5091 break;
5092 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5094 /* This command never recurses */
5095 /* No command-specific prep needed */
5097 break;
5098 case AT_DropConstraint: /* DROP CONSTRAINT */
5099 ATSimplePermissions(cmd->subtype, rel,
5101 ATCheckPartitionsNotInUse(rel, lockmode);
5102 /* Other recursion occurs during execution phase */
5103 /* No command-specific prep needed except saving recurse flag */
5104 if (recurse)
5105 cmd->recurse = true;
5106 pass = AT_PASS_DROP;
5107 break;
5108 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5109 ATSimplePermissions(cmd->subtype, rel,
5112 /* See comments for ATPrepAlterColumnType */
5113 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5114 AT_PASS_UNSET, context);
5115 Assert(cmd != NULL);
5116 /* Performs own recursion */
5117 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5118 lockmode, context);
5119 pass = AT_PASS_ALTER_TYPE;
5120 break;
5123 /* This command never recurses */
5124 /* No command-specific prep needed */
5125 pass = AT_PASS_MISC;
5126 break;
5127 case AT_ChangeOwner: /* ALTER OWNER */
5128 /* This command never recurses */
5129 /* No command-specific prep needed */
5130 pass = AT_PASS_MISC;
5131 break;
5132 case AT_ClusterOn: /* CLUSTER ON */
5133 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5134 ATSimplePermissions(cmd->subtype, rel,
5136 /* These commands never recurse */
5137 /* No command-specific prep needed */
5138 pass = AT_PASS_MISC;
5139 break;
5140 case AT_SetLogged: /* SET LOGGED */
5141 case AT_SetUnLogged: /* SET UNLOGGED */
5143 if (tab->chgPersistence)
5144 ereport(ERROR,
5145 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5146 errmsg("cannot change persistence setting twice")));
5147 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5148 pass = AT_PASS_MISC;
5149 break;
5150 case AT_DropOids: /* SET WITHOUT OIDS */
5151 ATSimplePermissions(cmd->subtype, rel,
5153 pass = AT_PASS_DROP;
5154 break;
5155 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5156 ATSimplePermissions(cmd->subtype, rel,
5158
5159 /* check if another access method change was already requested */
5160 if (tab->chgAccessMethod)
5161 ereport(ERROR,
5162 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5163 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5164
5165 ATPrepSetAccessMethod(tab, rel, cmd->name);
5166 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5167 break;
5168 case AT_SetTableSpace: /* SET TABLESPACE */
5171 /* This command never recurses */
5172 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5173 pass = AT_PASS_MISC; /* doesn't actually matter */
5174 break;
5175 case AT_SetRelOptions: /* SET (...) */
5176 case AT_ResetRelOptions: /* RESET (...) */
5177 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5178 ATSimplePermissions(cmd->subtype, rel,
5181 /* This command never recurses */
5182 /* No command-specific prep needed */
5183 pass = AT_PASS_MISC;
5184 break;
5185 case AT_AddInherit: /* INHERIT */
5186 ATSimplePermissions(cmd->subtype, rel,
5188 /* This command never recurses */
5189 ATPrepAddInherit(rel);
5190 pass = AT_PASS_MISC;
5191 break;
5192 case AT_DropInherit: /* NO INHERIT */
5193 ATSimplePermissions(cmd->subtype, rel,
5195 /* This command never recurses */
5196 /* No command-specific prep needed */
5197 pass = AT_PASS_MISC;
5198 break;
5199 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5200 ATSimplePermissions(cmd->subtype, rel,
5202 /* Recursion occurs during execution phase */
5203 if (recurse)
5204 cmd->recurse = true;
5205 pass = AT_PASS_MISC;
5206 break;
5207 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5208 ATSimplePermissions(cmd->subtype, rel,
5210 /* Recursion occurs during execution phase */
5211 /* No command-specific prep needed except saving recurse flag */
5212 if (recurse)
5213 cmd->recurse = true;
5214 pass = AT_PASS_MISC;
5215 break;
5216 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5217 ATSimplePermissions(cmd->subtype, rel,
5219 pass = AT_PASS_MISC;
5220 /* This command never recurses */
5221 /* No command-specific prep needed */
5222 break;
5223 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5226 case AT_EnableTrigAll:
5227 case AT_EnableTrigUser:
5228 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5229 case AT_DisableTrigAll:
5230 case AT_DisableTrigUser:
5231 ATSimplePermissions(cmd->subtype, rel,
5233 /* Set up recursion for phase 2; no other prep needed */
5234 if (recurse)
5235 cmd->recurse = true;
5236 pass = AT_PASS_MISC;
5237 break;
5238 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5241 case AT_DisableRule:
5242 case AT_AddOf: /* OF */
5243 case AT_DropOf: /* NOT OF */
5248 ATSimplePermissions(cmd->subtype, rel,
5250 /* These commands never recurse */
5251 /* No command-specific prep needed */
5252 pass = AT_PASS_MISC;
5253 break;
5254 case AT_GenericOptions:
5256 /* No command-specific prep needed */
5257 pass = AT_PASS_MISC;
5258 break;
5259 case AT_AttachPartition:
5260 ATSimplePermissions(cmd->subtype, rel,
5262 /* No command-specific prep needed */
5263 pass = AT_PASS_MISC;
5264 break;
5265 case AT_DetachPartition:
5267 /* No command-specific prep needed */
5268 pass = AT_PASS_MISC;
5269 break;
5272 /* No command-specific prep needed */
5273 pass = AT_PASS_MISC;
5274 break;
5275 default: /* oops */
5276 elog(ERROR, "unrecognized alter table type: %d",
5277 (int) cmd->subtype);
5278 pass = AT_PASS_UNSET; /* keep compiler quiet */
5279 break;
5280 }
5281 Assert(pass > AT_PASS_UNSET);
5282
5283 /* Add the subcommand to the appropriate list for phase 2 */
5284 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5285}
5286
5287/*
5288 * ATRewriteCatalogs
5289 *
5290 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5291 * dispatched in a "safe" execution order (designed to avoid unnecessary
5292 * conflicts).
5293 */
5294static void
5296 AlterTableUtilityContext *context)
5297{
5298 ListCell *ltab;
5299
5300 /*
5301 * We process all the tables "in parallel", one pass at a time. This is
5302 * needed because we may have to propagate work from one table to another
5303 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5304 * re-adding of the foreign key constraint to the other table). Work can
5305 * only be propagated into later passes, however.
5306 */
5307 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5308 {
5309 /* Go through each table that needs to be processed */
5310 foreach(ltab, *wqueue)
5311 {
5312 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5313 List *subcmds = tab->subcmds[pass];
5314 ListCell *lcmd;
5315
5316 if (subcmds == NIL)
5317 continue;
5318
5319 /*
5320 * Open the relation and store it in tab. This allows subroutines
5321 * close and reopen, if necessary. Appropriate lock was obtained
5322 * by phase 1, needn't get it again.
5323 */
5324 tab->rel = relation_open(tab->relid, NoLock);
5325
5326 foreach(lcmd, subcmds)
5327 ATExecCmd(wqueue, tab,
5329 lockmode, pass, context);
5330
5331 /*
5332 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5333 * (this is not done in ATExecAlterColumnType since it should be
5334 * done only once if multiple columns of a table are altered).
5335 */
5336 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5337 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5338
5339 if (tab->rel)
5340 {
5341 relation_close(tab->rel, NoLock);
5342 tab->rel = NULL;
5343 }
5344 }
5345 }
5346
5347 /* Check to see if a toast table must be added. */
5348 foreach(ltab, *wqueue)
5349 {
5350 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5351
5352 /*
5353 * If the table is source table of ATTACH PARTITION command, we did
5354 * not modify anything about it that will change its toasting
5355 * requirement, so no need to check.
5356 */
5357 if (((tab->relkind == RELKIND_RELATION ||
5358 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5359 tab->partition_constraint == NULL) ||
5360 tab->relkind == RELKIND_MATVIEW)
5361 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5362 }
5363}
5364
5365/*
5366 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5367 */
5368static void
5370 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5371 AlterTableUtilityContext *context)
5372{
5374 Relation rel = tab->rel;
5375
5376 switch (cmd->subtype)
5377 {
5378 case AT_AddColumn: /* ADD COLUMN */
5379 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5380 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5381 cmd->recurse, false,
5382 lockmode, cur_pass, context);
5383 break;
5384 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5385 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5386 break;
5387 case AT_CookedColumnDefault: /* add a pre-cooked default */
5388 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5389 break;
5390 case AT_AddIdentity:
5391 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5392 cur_pass, context);
5393 Assert(cmd != NULL);
5394 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5395 break;
5396 case AT_SetIdentity:
5397 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5398 cur_pass, context);
5399 Assert(cmd != NULL);
5400 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5401 break;
5402 case AT_DropIdentity:
5403 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5404 break;
5405 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5406 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5407 break;
5408 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5409 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5410 cmd->recurse, false, lockmode);
5411 break;
5412 case AT_SetExpression:
5413 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5414 break;
5415 case AT_DropExpression:
5416 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5417 break;
5418 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5419 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5420 break;
5421 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5422 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5423 break;
5424 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5425 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5426 break;
5427 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5428 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5429 break;
5430 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5431 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5432 lockmode);
5433 break;
5434 case AT_DropColumn: /* DROP COLUMN */
5435 address = ATExecDropColumn(wqueue, rel, cmd->name,
5436 cmd->behavior, cmd->recurse, false,
5437 cmd->missing_ok, lockmode,
5438 NULL);
5439 break;
5440 case AT_AddIndex: /* ADD INDEX */
5441 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5442 lockmode);
5443 break;
5444 case AT_ReAddIndex: /* ADD INDEX */
5445 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5446 lockmode);
5447 break;
5448 case AT_ReAddStatistics: /* ADD STATISTICS */
5449 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5450 true, lockmode);
5451 break;
5452 case AT_AddConstraint: /* ADD CONSTRAINT */
5453 /* Transform the command only during initial examination */
5454 if (cur_pass == AT_PASS_ADD_CONSTR)
5455 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5456 cmd->recurse, lockmode,
5457 cur_pass, context);
5458 /* Depending on constraint type, might be no more work to do now */
5459 if (cmd != NULL)
5460 address =
5461 ATExecAddConstraint(wqueue, tab, rel,
5462 (Constraint *) cmd->def,
5463 cmd->recurse, false, lockmode);
5464 break;
5465 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5466 address =
5467 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5468 true, true, lockmode);
5469 break;
5470 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5471 * constraint */
5472 address =
5473 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5474 ((AlterDomainStmt *) cmd->def)->def,
5475 NULL);
5476 break;
5477 case AT_ReAddComment: /* Re-add existing comment */
5478 address = CommentObject((CommentStmt *) cmd->def);
5479 break;
5480 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5481 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5482 lockmode);
5483 break;
5484 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5485 address = ATExecAlterConstraint(wqueue, rel,
5487 cmd->recurse, lockmode);
5488 break;
5489 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5490 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5491 false, lockmode);
5492 break;
5493 case AT_DropConstraint: /* DROP CONSTRAINT */
5494 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5495 cmd->recurse,
5496 cmd->missing_ok, lockmode);
5497 break;
5498 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5499 /* parse transformation was done earlier */
5500 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5501 break;
5502 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5503 address =
5505 (List *) cmd->def, lockmode);
5506 break;
5507 case AT_ChangeOwner: /* ALTER OWNER */
5509 get_rolespec_oid(cmd->newowner, false),
5510 false, lockmode);
5511 break;
5512 case AT_ClusterOn: /* CLUSTER ON */
5513 address = ATExecClusterOn(rel, cmd->name, lockmode);
5514 break;
5515 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5516 ATExecDropCluster(rel, lockmode);
5517 break;
5518 case AT_SetLogged: /* SET LOGGED */
5519 case AT_SetUnLogged: /* SET UNLOGGED */
5520 break;
5521 case AT_DropOids: /* SET WITHOUT OIDS */
5522 /* nothing to do here, oid columns don't exist anymore */
5523 break;
5524 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5525
5526 /*
5527 * Only do this for partitioned tables, for which this is just a
5528 * catalog change. Tables with storage are handled by Phase 3.
5529 */
5530 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5531 tab->chgAccessMethod)
5533 break;
5534 case AT_SetTableSpace: /* SET TABLESPACE */
5535
5536 /*
5537 * Only do this for partitioned tables and indexes, for which this
5538 * is just a catalog change. Other relation types which have
5539 * storage are handled by Phase 3.
5540 */
5541 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5542 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5544
5545 break;
5546 case AT_SetRelOptions: /* SET (...) */
5547 case AT_ResetRelOptions: /* RESET (...) */
5548 case AT_ReplaceRelOptions: /* replace entire option list */
5549 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5550 break;
5551 case AT_EnableTrig: /* ENABLE TRIGGER name */
5554 cmd->recurse,
5555 lockmode);
5556 break;
5557 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5559 TRIGGER_FIRES_ALWAYS, false,
5560 cmd->recurse,
5561 lockmode);
5562 break;
5563 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5566 cmd->recurse,
5567 lockmode);
5568 break;
5569 case AT_DisableTrig: /* DISABLE TRIGGER name */
5571 TRIGGER_DISABLED, false,
5572 cmd->recurse,
5573 lockmode);
5574 break;
5575 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5578 cmd->recurse,
5579 lockmode);
5580 break;
5581 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5583 TRIGGER_DISABLED, false,
5584 cmd->recurse,
5585 lockmode);
5586 break;
5587 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5590 cmd->recurse,
5591 lockmode);
5592 break;
5593 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5595 TRIGGER_DISABLED, true,
5596 cmd->recurse,
5597 lockmode);
5598 break;
5599
5600 case AT_EnableRule: /* ENABLE RULE name */
5601 ATExecEnableDisableRule(rel, cmd->name,
5602 RULE_FIRES_ON_ORIGIN, lockmode);
5603 break;
5604 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5605 ATExecEnableDisableRule(rel, cmd->name,
5606 RULE_FIRES_ALWAYS, lockmode);
5607 break;
5608 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5609 ATExecEnableDisableRule(rel, cmd->name,
5610 RULE_FIRES_ON_REPLICA, lockmode);
5611 break;
5612 case AT_DisableRule: /* DISABLE RULE name */
5613 ATExecEnableDisableRule(rel, cmd->name,
5614 RULE_DISABLED, lockmode);
5615 break;
5616
5617 case AT_AddInherit:
5618 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5619 break;
5620 case AT_DropInherit:
5621 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5622 break;
5623 case AT_AddOf:
5624 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5625 break;
5626 case AT_DropOf:
5627 ATExecDropOf(rel, lockmode);
5628 break;
5629 case AT_ReplicaIdentity:
5630 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5631 break;
5633 ATExecSetRowSecurity(rel, true);
5634 break;
5636 ATExecSetRowSecurity(rel, false);
5637 break;
5640 break;
5643 break;
5644 case AT_GenericOptions:
5645 ATExecGenericOptions(rel, (List *) cmd->def);
5646 break;
5647 case AT_AttachPartition:
5648 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5649 cur_pass, context);
5650 Assert(cmd != NULL);
5651 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5652 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5653 context);
5654 else
5655 address = ATExecAttachPartitionIdx(wqueue, rel,
5656 ((PartitionCmd *) cmd->def)->name);
5657 break;
5658 case AT_DetachPartition:
5659 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5660 cur_pass, context);
5661 Assert(cmd != NULL);
5662 /* ATPrepCmd ensures it must be a table */
5663 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5664 address = ATExecDetachPartition(wqueue, tab, rel,
5665 ((PartitionCmd *) cmd->def)->name,
5666 ((PartitionCmd *) cmd->def)->concurrent);
5667 break;
5669 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5670 break;
5671 default: /* oops */
5672 elog(ERROR, "unrecognized alter table type: %d",
5673 (int) cmd->subtype);
5674 break;
5675 }
5676
5677 /*
5678 * Report the subcommand to interested event triggers.
5679 */
5680 if (cmd)
5681 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5682
5683 /*
5684 * Bump the command counter to ensure the next subcommand in the sequence
5685 * can see the changes so far
5686 */
5688}
5689
5690/*
5691 * ATParseTransformCmd: perform parse transformation for one subcommand
5692 *
5693 * Returns the transformed subcommand tree, if there is one, else NULL.
5694 *
5695 * The parser may hand back additional AlterTableCmd(s) and/or other
5696 * utility statements, either before or after the original subcommand.
5697 * Other AlterTableCmds are scheduled into the appropriate slot of the
5698 * AlteredTableInfo (they had better be for later passes than the current one).
5699 * Utility statements that are supposed to happen before the AlterTableCmd
5700 * are executed immediately. Those that are supposed to happen afterwards
5701 * are added to the tab->afterStmts list to be done at the very end.
5702 */
5703static AlterTableCmd *
5705 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5706 AlterTablePass cur_pass, AlterTableUtilityContext *context)
5707{
5708 AlterTableCmd *newcmd = NULL;
5710 List *beforeStmts;
5711 List *afterStmts;
5712 ListCell *lc;
5713
5714 /* Gin up an AlterTableStmt with just this subcommand and this table */
5715 atstmt->relation =
5718 -1);
5719 atstmt->relation->inh = recurse;
5720 atstmt->cmds = list_make1(cmd);
5721 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5722 atstmt->missing_ok = false;
5723
5724 /* Transform the AlterTableStmt */
5726 atstmt,
5727 context->queryString,
5728 &beforeStmts,
5729 &afterStmts);
5730
5731 /* Execute any statements that should happen before these subcommand(s) */
5732 foreach(lc, beforeStmts)
5733 {
5734 Node *stmt = (Node *) lfirst(lc);
5735
5738 }
5739
5740 /* Examine the transformed subcommands and schedule them appropriately */
5741 foreach(lc, atstmt->cmds)
5742 {
5744 AlterTablePass pass;
5745
5746 /*
5747 * This switch need only cover the subcommand types that can be added
5748 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5749 * executing the subcommand immediately, as a substitute for the
5750 * original subcommand. (Note, however, that this does cause
5751 * AT_AddConstraint subcommands to be rescheduled into later passes,
5752 * which is important for index and foreign key constraints.)
5753 *
5754 * We assume we needn't do any phase-1 checks for added subcommands.
5755 */
5756 switch (cmd2->subtype)
5757 {
5758 case AT_AddIndex:
5759 pass = AT_PASS_ADD_INDEX;
5760 break;
5763 break;
5764 case AT_AddConstraint:
5765 /* Recursion occurs during execution phase */
5766 if (recurse)
5767 cmd2->recurse = true;
5768 switch (castNode(Constraint, cmd2->def)->contype)
5769 {
5770 case CONSTR_NOTNULL:
5771 pass = AT_PASS_COL_ATTRS;
5772 break;
5773 case CONSTR_PRIMARY:
5774 case CONSTR_UNIQUE:
5775 case CONSTR_EXCLUSION:
5777 break;
5778 default:
5780 break;
5781 }
5782 break;
5784 /* This command never recurses */
5785 /* No command-specific prep needed */
5786 pass = AT_PASS_MISC;
5787 break;
5788 default:
5789 pass = cur_pass;
5790 break;
5791 }
5792
5793 if (pass < cur_pass)
5794 {
5795 /* Cannot schedule into a pass we already finished */
5796 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5797 pass);
5798 }
5799 else if (pass > cur_pass)
5800 {
5801 /* OK, queue it up for later */
5802 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5803 }
5804 else
5805 {
5806 /*
5807 * We should see at most one subcommand for the current pass,
5808 * which is the transformed version of the original subcommand.
5809 */
5810 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5811 {
5812 /* Found the transformed version of our subcommand */
5813 newcmd = cmd2;
5814 }
5815 else
5816 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5817 pass);
5818 }
5819 }
5820
5821 /* Queue up any after-statements to happen at the end */
5822 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5823
5824 return newcmd;
5825}
5826
5827/*
5828 * ATRewriteTables: ALTER TABLE phase 3
5829 */
5830static void
5831ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5832 AlterTableUtilityContext *context)
5833{
5834 ListCell *ltab;
5835
5836 /* Go through each table that needs to be checked or rewritten */
5837 foreach(ltab, *wqueue)
5838 {
5839 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5840
5841 /* Relations without storage may be ignored here */
5842 if (!RELKIND_HAS_STORAGE(tab->relkind))
5843 continue;
5844
5845 /*
5846 * If we change column data types, the operation has to be propagated
5847 * to tables that use this table's rowtype as a column type.
5848 * tab->newvals will also be non-NULL in the case where we're adding a
5849 * column with a default. We choose to forbid that case as well,
5850 * since composite types might eventually support defaults.
5851 *
5852 * (Eventually we'll probably need to check for composite type
5853 * dependencies even when we're just scanning the table without a
5854 * rewrite, but at the moment a composite type does not enforce any
5855 * constraints, so it's not necessary/appropriate to enforce them just
5856 * during ALTER.)
5857 */
5858 if (tab->newvals != NIL || tab->rewrite > 0)
5859 {
5860 Relation rel;
5861
5862 rel = table_open(tab->relid, NoLock);
5863 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5864 table_close(rel, NoLock);
5865 }
5866
5867 /*
5868 * We only need to rewrite the table if at least one column needs to
5869 * be recomputed, or we are changing its persistence or access method.
5870 *
5871 * There are two reasons for requiring a rewrite when changing
5872 * persistence: on one hand, we need to ensure that the buffers
5873 * belonging to each of the two relations are marked with or without
5874 * BM_PERMANENT properly. On the other hand, since rewriting creates
5875 * and assigns a new relfilenumber, we automatically create or drop an
5876 * init fork for the relation as appropriate.
5877 */
5878 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5879 {
5880 /* Build a temporary relation and copy data */
5881 Relation OldHeap;
5882 Oid OIDNewHeap;
5883 Oid NewAccessMethod;
5884 Oid NewTableSpace;
5885 char persistence;
5886
5887 OldHeap = table_open(tab->relid, NoLock);
5888
5889 /*
5890 * We don't support rewriting of system catalogs; there are too
5891 * many corner cases and too little benefit. In particular this
5892 * is certainly not going to work for mapped catalogs.
5893 */
5894 if (IsSystemRelation(OldHeap))
5895 ereport(ERROR,
5896 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5897 errmsg("cannot rewrite system relation \"%s\"",
5898 RelationGetRelationName(OldHeap))));
5899
5900 if (RelationIsUsedAsCatalogTable(OldHeap))
5901 ereport(ERROR,
5902 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5903 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5904 RelationGetRelationName(OldHeap))));
5905
5906 /*
5907 * Don't allow rewrite on temp tables of other backends ... their
5908 * local buffer manager is not going to cope. (This is redundant
5909 * with the check in CheckAlterTableIsSafe, but for safety we'll
5910 * check here too.)
5911 */
5912 if (RELATION_IS_OTHER_TEMP(OldHeap))
5913 ereport(ERROR,
5914 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5915 errmsg("cannot rewrite temporary tables of other sessions")));
5916
5917 /*
5918 * Select destination tablespace (same as original unless user
5919 * requested a change)
5920 */
5921 if (tab->newTableSpace)
5922 NewTableSpace = tab->newTableSpace;
5923 else
5924 NewTableSpace = OldHeap->rd_rel->reltablespace;
5925
5926 /*
5927 * Select destination access method (same as original unless user
5928 * requested a change)
5929 */
5930 if (tab->chgAccessMethod)
5931 NewAccessMethod = tab->newAccessMethod;
5932 else
5933 NewAccessMethod = OldHeap->rd_rel->relam;
5934
5935 /*
5936 * Select persistence of transient table (same as original unless
5937 * user requested a change)
5938 */
5939 persistence = tab->chgPersistence ?
5940 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5941
5942 table_close(OldHeap, NoLock);
5943
5944 /*
5945 * Fire off an Event Trigger now, before actually rewriting the
5946 * table.
5947 *
5948 * We don't support Event Trigger for nested commands anywhere,
5949 * here included, and parsetree is given NULL when coming from
5950 * AlterTableInternal.
5951 *
5952 * And fire it only once.
5953 */
5954 if (parsetree)
5955 EventTriggerTableRewrite((Node *) parsetree,
5956 tab->relid,
5957 tab->rewrite);
5958
5959 /*
5960 * Create transient table that will receive the modified data.
5961 *
5962 * Ensure it is marked correctly as logged or unlogged. We have
5963 * to do this here so that buffers for the new relfilenumber will
5964 * have the right persistence set, and at the same time ensure
5965 * that the original filenumbers's buffers will get read in with
5966 * the correct setting (i.e. the original one). Otherwise a
5967 * rollback after the rewrite would possibly result with buffers
5968 * for the original filenumbers having the wrong persistence
5969 * setting.
5970 *
5971 * NB: This relies on swap_relation_files() also swapping the
5972 * persistence. That wouldn't work for pg_class, but that can't be
5973 * unlogged anyway.
5974 */
5975 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5976 persistence, lockmode);
5977
5978 /*
5979 * Copy the heap data into the new table with the desired
5980 * modifications, and test the current data within the table
5981 * against new constraints generated by ALTER TABLE commands.
5982 */
5983 ATRewriteTable(tab, OIDNewHeap);
5984
5985 /*
5986 * Swap the physical files of the old and new heaps, then rebuild
5987 * indexes and discard the old heap. We can use RecentXmin for
5988 * the table's new relfrozenxid because we rewrote all the tuples
5989 * in ATRewriteTable, so no older Xid remains in the table. Also,
5990 * we never try to swap toast tables by content, since we have no
5991 * interest in letting this code work on system catalogs.
5992 */
5993 finish_heap_swap(tab->relid, OIDNewHeap,
5994 false, false, true,
5996 RecentXmin,
5998 persistence);
5999
6000 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
6001 }
6002 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6003 {
6004 if (tab->chgPersistence)
6006 }
6007 else
6008 {
6009 /*
6010 * If required, test the current data within the table against new
6011 * constraints generated by ALTER TABLE commands, but don't
6012 * rebuild data.
6013 */
6014 if (tab->constraints != NIL || tab->verify_new_notnull ||
6015 tab->partition_constraint != NULL)
6017
6018 /*
6019 * If we had SET TABLESPACE but no reason to reconstruct tuples,
6020 * just do a block-by-block copy.
6021 */
6022 if (tab->newTableSpace)
6023 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6024 }
6025
6026 /*
6027 * Also change persistence of owned sequences, so that it matches the
6028 * table persistence.
6029 */
6030 if (tab->chgPersistence)
6031 {
6032 List *seqlist = getOwnedSequences(tab->relid);
6033 ListCell *lc;
6034
6035 foreach(lc, seqlist)
6036 {
6037 Oid seq_relid = lfirst_oid(lc);
6038
6040 }
6041 }
6042 }
6043
6044 /*
6045 * Foreign key constraints are checked in a final pass, since (a) it's
6046 * generally best to examine each one separately, and (b) it's at least
6047 * theoretically possible that we have changed both relations of the
6048 * foreign key, and we'd better have finished both rewrites before we try
6049 * to read the tables.
6050 */
6051 foreach(ltab, *wqueue)
6052 {
6053 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6054 Relation rel = NULL;
6055 ListCell *lcon;
6056
6057 /* Relations without storage may be ignored here too */
6058 if (!RELKIND_HAS_STORAGE(tab->relkind))
6059 continue;
6060
6061 foreach(lcon, tab->constraints)
6062 {
6063 NewConstraint *con = lfirst(lcon);
6064
6065 if (con->contype == CONSTR_FOREIGN)
6066 {
6067 Constraint *fkconstraint = (Constraint *) con->qual;
6068 Relation refrel;
6069
6070 if (rel == NULL)
6071 {
6072 /* Long since locked, no need for another */
6073 rel = table_open(tab->relid, NoLock);
6074 }
6075
6076 refrel = table_open(con->refrelid, RowShareLock);
6077
6078 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6079 con->refindid,
6080 con->conid,
6081 con->conwithperiod);
6082
6083 /*
6084 * No need to mark the constraint row as validated, we did
6085 * that when we inserted the row earlier.
6086 */
6087
6088 table_close(refrel, NoLock);
6089 }
6090 }
6091
6092 if (rel)
6093 table_close(rel, NoLock);
6094 }
6095
6096 /* Finally, run any afterStmts that were queued up */
6097 foreach(ltab, *wqueue)
6098 {
6099 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6100 ListCell *lc;
6101
6102 foreach(lc, tab->afterStmts)
6103 {
6104 Node *stmt = (Node *) lfirst(lc);
6105
6108 }
6109 }
6110}
6111
6112/*
6113 * ATRewriteTable: scan or rewrite one table
6114 *
6115 * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6116 * must already hold AccessExclusiveLock on it.
6117 */
6118static void
6120{
6121 Relation oldrel;
6122 Relation newrel;
6123 TupleDesc oldTupDesc;
6124 TupleDesc newTupDesc;
6125 bool needscan = false;
6126 List *notnull_attrs;
6127 List *notnull_virtual_attrs;
6128 int i;
6129 ListCell *l;
6130 EState *estate;
6131 CommandId mycid;
6132 BulkInsertState bistate;
6133 int ti_options;
6134 ExprState *partqualstate = NULL;
6135
6136 /*
6137 * Open the relation(s). We have surely already locked the existing
6138 * table.
6139 */
6140 oldrel = table_open(tab->relid, NoLock);
6141 oldTupDesc = tab->oldDesc;
6142 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6143
6144 if (OidIsValid(OIDNewHeap))
6145 {
6147 false));
6148 newrel = table_open(OIDNewHeap, NoLock);
6149 }
6150 else
6151 newrel = NULL;
6152
6153 /*
6154 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6155 * is empty, so don't bother using it.
6156 */
6157 if (newrel)
6158 {
6159 mycid = GetCurrentCommandId(true);
6160 bistate = GetBulkInsertState();
6161 ti_options = TABLE_INSERT_SKIP_FSM;
6162 }
6163 else
6164 {
6165 /* keep compiler quiet about using these uninitialized */
6166 mycid = 0;
6167 bistate = NULL;
6168 ti_options = 0;
6169 }
6170
6171 /*
6172 * Generate the constraint and default execution states
6173 */
6174
6175 estate = CreateExecutorState();
6176
6177 /* Build the needed expression execution states */
6178 foreach(l, tab->constraints)
6179 {
6180 NewConstraint *con = lfirst(l);
6181
6182 switch (con->contype)
6183 {
6184 case CONSTR_CHECK:
6185 needscan = true;
6186 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6187 break;
6188 case CONSTR_FOREIGN:
6189 /* Nothing to do here */
6190 break;
6191 default:
6192 elog(ERROR, "unrecognized constraint type: %d",
6193 (int) con->contype);
6194 }
6195 }
6196
6197 /* Build expression execution states for partition check quals */
6198 if (tab->partition_constraint)
6199 {
6200 needscan = true;
6201 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6202 }
6203
6204 foreach(l, tab->newvals)
6205 {
6206 NewColumnValue *ex = lfirst(l);
6207
6208 /* expr already planned */
6209 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6210 }
6211
6212 notnull_attrs = notnull_virtual_attrs = NIL;
6213 if (newrel || tab->verify_new_notnull)
6214 {
6215 /*
6216 * If we are rebuilding the tuples OR if we added any new but not
6217 * verified not-null constraints, check all *valid* not-null
6218 * constraints. This is a bit of overkill but it minimizes risk of
6219 * bugs.
6220 *
6221 * notnull_attrs does *not* collect attribute numbers for valid
6222 * not-null constraints over virtual generated columns; instead, they
6223 * are collected in notnull_virtual_attrs for verification elsewhere.
6224 */
6225 for (i = 0; i < newTupDesc->natts; i++)
6226 {
6227 CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6228
6229 if (attr->attnullability == ATTNULLABLE_VALID &&
6230 !attr->attisdropped)
6231 {
6232 Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6233
6234 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6235 notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6236 else
6237 notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6238 wholeatt->attnum);
6239 }
6240 }
6241 if (notnull_attrs || notnull_virtual_attrs)
6242 needscan = true;
6243 }
6244
6245 if (newrel || needscan)
6246 {
6247 ExprContext *econtext;
6248 TupleTableSlot *oldslot;
6249 TupleTableSlot *newslot;
6250 TableScanDesc scan;
6251 MemoryContext oldCxt;
6252 List *dropped_attrs = NIL;
6253 ListCell *lc;
6254 Snapshot snapshot;
6255 ResultRelInfo *rInfo = NULL;
6256
6257 /*
6258 * When adding or changing a virtual generated column with a not-null
6259 * constraint, we need to evaluate whether the generation expression
6260 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6261 * prepare a dummy ResultRelInfo.
6262 */
6263 if (notnull_virtual_attrs != NIL)
6264 {
6265 MemoryContext oldcontext;
6266
6267 Assert(newTupDesc->constr->has_generated_virtual);
6268 Assert(newTupDesc->constr->has_not_null);
6269 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6270 rInfo = makeNode(ResultRelInfo);
6271 InitResultRelInfo(rInfo,
6272 oldrel,
6273 0, /* dummy rangetable index */
6274 NULL,
6275 estate->es_instrument);
6276 MemoryContextSwitchTo(oldcontext);
6277 }
6278
6279 if (newrel)
6281 (errmsg_internal("rewriting table \"%s\"",
6282 RelationGetRelationName(oldrel))));
6283 else
6285 (errmsg_internal("verifying table \"%s\"",
6286 RelationGetRelationName(oldrel))));
6287
6288 if (newrel)
6289 {
6290 /*
6291 * All predicate locks on the tuples or pages are about to be made
6292 * invalid, because we move tuples around. Promote them to
6293 * relation locks.
6294 */
6296 }
6297
6298 econtext = GetPerTupleExprContext(estate);
6299
6300 /*
6301 * Create necessary tuple slots. When rewriting, two slots are needed,
6302 * otherwise one suffices. In the case where one slot suffices, we
6303 * need to use the new tuple descriptor, otherwise some constraints
6304 * can't be evaluated. Note that even when the tuple layout is the
6305 * same and no rewrite is required, the tupDescs might not be
6306 * (consider ADD COLUMN without a default).
6307 */
6308 if (tab->rewrite)
6309 {
6310 Assert(newrel != NULL);
6311 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6312 table_slot_callbacks(oldrel));
6313 newslot = MakeSingleTupleTableSlot(newTupDesc,
6314 table_slot_callbacks(newrel));
6315
6316 /*
6317 * Set all columns in the new slot to NULL initially, to ensure
6318 * columns added as part of the rewrite are initialized to NULL.
6319 * That is necessary as tab->newvals will not contain an
6320 * expression for columns with a NULL default, e.g. when adding a
6321 * column without a default together with a column with a default
6322 * requiring an actual rewrite.
6323 */
6324 ExecStoreAllNullTuple(newslot);
6325 }
6326 else
6327 {
6328 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6329 table_slot_callbacks(oldrel));
6330 newslot = NULL;
6331 }
6332
6333 /*
6334 * Any attributes that are dropped according to the new tuple
6335 * descriptor can be set to NULL. We precompute the list of dropped
6336 * attributes to avoid needing to do so in the per-tuple loop.
6337 */
6338 for (i = 0; i < newTupDesc->natts; i++)
6339 {
6340 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6341 dropped_attrs = lappend_int(dropped_attrs, i);
6342 }
6343
6344 /*
6345 * Scan through the rows, generating a new row if needed and then
6346 * checking all the constraints.
6347 */
6348 snapshot = RegisterSnapshot(GetLatestSnapshot());
6349 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6350
6351 /*
6352 * Switch to per-tuple memory context and reset it for each tuple
6353 * produced, so we don't leak memory.
6354 */
6356
6357 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6358 {
6359 TupleTableSlot *insertslot;
6360
6361 if (tab->rewrite > 0)
6362 {
6363 /* Extract data from old tuple */
6364 slot_getallattrs(oldslot);
6365 ExecClearTuple(newslot);
6366
6367 /* copy attributes */
6368 memcpy(newslot->tts_values, oldslot->tts_values,
6369 sizeof(Datum) * oldslot->tts_nvalid);
6370 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6371 sizeof(bool) * oldslot->tts_nvalid);
6372
6373 /* Set dropped attributes to null in new tuple */
6374 foreach(lc, dropped_attrs)
6375 newslot->tts_isnull[lfirst_int(lc)] = true;
6376
6377 /*
6378 * Constraints and GENERATED expressions might reference the
6379 * tableoid column, so fill tts_tableOid with the desired
6380 * value. (We must do this each time, because it gets
6381 * overwritten with newrel's OID during storing.)
6382 */
6383 newslot->tts_tableOid = RelationGetRelid(oldrel);
6384
6385 /*
6386 * Process supplied expressions to replace selected columns.
6387 *
6388 * First, evaluate expressions whose inputs come from the old
6389 * tuple.
6390 */
6391 econtext->ecxt_scantuple = oldslot;
6392
6393 foreach(l, tab->newvals)
6394 {
6395 NewColumnValue *ex = lfirst(l);
6396
6397 if (ex->is_generated)
6398 continue;
6399
6400 newslot->tts_values[ex->attnum - 1]
6401 = ExecEvalExpr(ex->exprstate,
6402 econtext,
6403 &newslot->tts_isnull[ex->attnum - 1]);
6404 }
6405
6406 ExecStoreVirtualTuple(newslot);
6407
6408 /*
6409 * Now, evaluate any expressions whose inputs come from the
6410 * new tuple. We assume these columns won't reference each
6411 * other, so that there's no ordering dependency.
6412 */
6413 econtext->ecxt_scantuple = newslot;
6414
6415 foreach(l, tab->newvals)
6416 {
6417 NewColumnValue *ex = lfirst(l);
6418
6419 if (!ex->is_generated)
6420 continue;
6421
6422 newslot->tts_values[ex->attnum - 1]
6423 = ExecEvalExpr(ex->exprstate,
6424 econtext,
6425 &newslot->tts_isnull[ex->attnum - 1]);
6426 }
6427
6428 insertslot = newslot;
6429 }
6430 else
6431 {
6432 /*
6433 * If there's no rewrite, old and new table are guaranteed to
6434 * have the same AM, so we can just use the old slot to verify
6435 * new constraints etc.
6436 */
6437 insertslot = oldslot;
6438 }
6439
6440 /* Now check any constraints on the possibly-changed tuple */
6441 econtext->ecxt_scantuple = insertslot;
6442
6443 foreach_int(attn, notnull_attrs)
6444 {
6445 if (slot_attisnull(insertslot, attn))
6446 {
6447 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6448
6449 ereport(ERROR,
6450 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6451 errmsg("column \"%s\" of relation \"%s\" contains null values",
6452 NameStr(attr->attname),
6453 RelationGetRelationName(oldrel)),
6454 errtablecol(oldrel, attn)));
6455 }
6456 }
6457
6458 if (notnull_virtual_attrs != NIL)
6459 {
6461
6462 attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6463 estate,
6464 notnull_virtual_attrs);
6466 {
6467 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6468
6469 ereport(ERROR,
6470 errcode(ERRCODE_NOT_NULL_VIOLATION),
6471 errmsg("column \"%s\" of relation \"%s\" contains null values",
6472 NameStr(attr->attname),
6473 RelationGetRelationName(oldrel)),
6474 errtablecol(oldrel, attnum));
6475 }
6476 }
6477
6478 foreach(l, tab->constraints)
6479 {
6480 NewConstraint *con = lfirst(l);
6481
6482 switch (con->contype)
6483 {
6484 case CONSTR_CHECK:
6485 if (!ExecCheck(con->qualstate, econtext))
6486 ereport(ERROR,
6487 (errcode(ERRCODE_CHECK_VIOLATION),
6488 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6489 con->name,
6490 RelationGetRelationName(oldrel)),
6491 errtableconstraint(oldrel, con->name)));
6492 break;
6493 case CONSTR_NOTNULL:
6494 case CONSTR_FOREIGN:
6495 /* Nothing to do here */
6496 break;
6497 default:
6498 elog(ERROR, "unrecognized constraint type: %d",
6499 (int) con->contype);
6500 }
6501 }
6502
6503 if (partqualstate && !ExecCheck(partqualstate, econtext))
6504 {
6505 if (tab->validate_default)
6506 ereport(ERROR,
6507 (errcode(ERRCODE_CHECK_VIOLATION),
6508 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6509 RelationGetRelationName(oldrel)),
6510 errtable(oldrel)));
6511 else
6512 ereport(ERROR,
6513 (errcode(ERRCODE_CHECK_VIOLATION),
6514 errmsg("partition constraint of relation \"%s\" is violated by some row",
6515 RelationGetRelationName(oldrel)),
6516 errtable(oldrel)));
6517 }
6518
6519 /* Write the tuple out to the new relation */
6520 if (newrel)
6521 table_tuple_insert(newrel, insertslot, mycid,
6522 ti_options, bistate);
6523
6524 ResetExprContext(econtext);
6525
6527 }
6528
6529 MemoryContextSwitchTo(oldCxt);
6530 table_endscan(scan);
6531 UnregisterSnapshot(snapshot);
6532
6534 if (newslot)
6536 }
6537
6538 FreeExecutorState(estate);
6539
6540 table_close(oldrel, NoLock);
6541 if (newrel)
6542 {
6543 FreeBulkInsertState(bistate);
6544
6545 table_finish_bulk_insert(newrel, ti_options);
6546
6547 table_close(newrel, NoLock);
6548 }
6549}
6550
6551/*
6552 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6553 */
6554static AlteredTableInfo *
6556{
6557 Oid relid = RelationGetRelid(rel);
6558 AlteredTableInfo *tab;
6559 ListCell *ltab;
6560
6561 foreach(ltab, *wqueue)
6562 {
6563 tab = (AlteredTableInfo *) lfirst(ltab);
6564 if (tab->relid == relid)
6565 return tab;
6566 }
6567
6568 /*
6569 * Not there, so add it. Note that we make a copy of the relation's
6570 * existing descriptor before anything interesting can happen to it.
6571 */
6572 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6573 tab->relid = relid;
6574 tab->rel = NULL; /* set later */
6575 tab->relkind = rel->rd_rel->relkind;
6578 tab->chgAccessMethod = false;
6580 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6581 tab->chgPersistence = false;
6582
6583 *wqueue = lappend(*wqueue, tab);
6584
6585 return tab;
6586}
6587
6588static const char *
6590{
6591 switch (cmdtype)
6592 {
6593 case AT_AddColumn:
6594 case AT_AddColumnToView:
6595 return "ADD COLUMN";
6596 case AT_ColumnDefault:
6598 return "ALTER COLUMN ... SET DEFAULT";
6599 case AT_DropNotNull:
6600 return "ALTER COLUMN ... DROP NOT NULL";
6601 case AT_SetNotNull:
6602 return "ALTER COLUMN ... SET NOT NULL";
6603 case AT_SetExpression:
6604 return "ALTER COLUMN ... SET EXPRESSION";
6605 case AT_DropExpression:
6606 return "ALTER COLUMN ... DROP EXPRESSION";
6607 case AT_SetStatistics:
6608 return "ALTER COLUMN ... SET STATISTICS";
6609 case AT_SetOptions:
6610 return "ALTER COLUMN ... SET";
6611 case AT_ResetOptions:
6612 return "ALTER COLUMN ... RESET";
6613 case AT_SetStorage:
6614 return "ALTER COLUMN ... SET STORAGE";
6615 case AT_SetCompression:
6616 return "ALTER COLUMN ... SET COMPRESSION";
6617 case AT_DropColumn:
6618 return "DROP COLUMN";
6619 case AT_AddIndex:
6620 case AT_ReAddIndex:
6621 return NULL; /* not real grammar */
6622 case AT_AddConstraint:
6623 case AT_ReAddConstraint:
6626 return "ADD CONSTRAINT";
6627 case AT_AlterConstraint:
6628 return "ALTER CONSTRAINT";
6630 return "VALIDATE CONSTRAINT";
6631 case AT_DropConstraint:
6632 return "DROP CONSTRAINT";
6633 case AT_ReAddComment:
6634 return NULL; /* not real grammar */
6635 case AT_AlterColumnType:
6636 return "ALTER COLUMN ... SET DATA TYPE";
6638 return "ALTER COLUMN ... OPTIONS";
6639 case AT_ChangeOwner:
6640 return "OWNER TO";
6641 case AT_ClusterOn:
6642 return "CLUSTER ON";
6643 case AT_DropCluster:
6644 return "SET WITHOUT CLUSTER";
6645 case AT_SetAccessMethod:
6646 return "SET ACCESS METHOD";
6647 case AT_SetLogged:
6648 return "SET LOGGED";
6649 case AT_SetUnLogged:
6650 return "SET UNLOGGED";
6651 case AT_DropOids:
6652 return "SET WITHOUT OIDS";
6653 case AT_SetTableSpace:
6654 return "SET TABLESPACE";
6655 case AT_SetRelOptions:
6656 return "SET";
6657 case AT_ResetRelOptions:
6658 return "RESET";
6660 return NULL; /* not real grammar */
6661 case AT_EnableTrig:
6662 return "ENABLE TRIGGER";
6664 return "ENABLE ALWAYS TRIGGER";
6666 return "ENABLE REPLICA TRIGGER";
6667 case AT_DisableTrig:
6668 return "DISABLE TRIGGER";
6669 case AT_EnableTrigAll:
6670 return "ENABLE TRIGGER ALL";
6671 case AT_DisableTrigAll:
6672 return "DISABLE TRIGGER ALL";
6673 case AT_EnableTrigUser:
6674 return "ENABLE TRIGGER USER";
6675 case AT_DisableTrigUser:
6676 return "DISABLE TRIGGER USER";
6677 case AT_EnableRule:
6678 return "ENABLE RULE";
6680 return "ENABLE ALWAYS RULE";
6682 return "ENABLE REPLICA RULE";
6683 case AT_DisableRule:
6684 return "DISABLE RULE";
6685 case AT_AddInherit:
6686 return "INHERIT";
6687 case AT_DropInherit:
6688 return "NO INHERIT";
6689 case AT_AddOf:
6690 return "OF";
6691 case AT_DropOf:
6692 return "NOT OF";
6693 case AT_ReplicaIdentity:
6694 return "REPLICA IDENTITY";
6696 return "ENABLE ROW SECURITY";
6698 return "DISABLE ROW SECURITY";
6700 return "FORCE ROW SECURITY";
6702 return "NO FORCE ROW SECURITY";
6703 case AT_GenericOptions:
6704 return "OPTIONS";
6705 case AT_AttachPartition:
6706 return "ATTACH PARTITION";
6707 case AT_DetachPartition:
6708 return "DETACH PARTITION";
6710 return "DETACH PARTITION ... FINALIZE";
6711 case AT_AddIdentity:
6712 return "ALTER COLUMN ... ADD IDENTITY";
6713 case AT_SetIdentity:
6714 return "ALTER COLUMN ... SET";
6715 case AT_DropIdentity:
6716 return "ALTER COLUMN ... DROP IDENTITY";
6717 case AT_ReAddStatistics:
6718 return NULL; /* not real grammar */
6719 }
6720
6721 return NULL;
6722}
6723
6724/*
6725 * ATSimplePermissions
6726 *
6727 * - Ensure that it is a relation (or possibly a view)
6728 * - Ensure this user is the owner
6729 * - Ensure that it is not a system table
6730 */
6731static void
6732ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6733{
6734 int actual_target;
6735
6736 switch (rel->rd_rel->relkind)
6737 {
6738 case RELKIND_RELATION:
6739 actual_target = ATT_TABLE;
6740 break;
6741 case RELKIND_PARTITIONED_TABLE:
6742 actual_target = ATT_PARTITIONED_TABLE;
6743 break;
6744 case RELKIND_VIEW:
6745 actual_target = ATT_VIEW;
6746 break;
6747 case RELKIND_MATVIEW:
6748 actual_target = ATT_MATVIEW;
6749 break;
6750 case RELKIND_INDEX:
6751 actual_target = ATT_INDEX;
6752 break;
6753 case RELKIND_PARTITIONED_INDEX:
6754 actual_target = ATT_PARTITIONED_INDEX;
6755 break;
6756 case RELKIND_COMPOSITE_TYPE:
6757 actual_target = ATT_COMPOSITE_TYPE;
6758 break;
6759 case RELKIND_FOREIGN_TABLE:
6760 actual_target = ATT_FOREIGN_TABLE;
6761 break;
6762 case RELKIND_SEQUENCE:
6763 actual_target = ATT_SEQUENCE;
6764 break;
6765 default:
6766 actual_target = 0;
6767 break;
6768 }
6769
6770 /* Wrong target type? */
6771 if ((actual_target & allowed_targets) == 0)
6772 {
6773 const char *action_str = alter_table_type_to_string(cmdtype);
6774
6775 if (action_str)
6776 ereport(ERROR,
6777 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6778 /* translator: %s is a group of some SQL keywords */
6779 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6780 action_str, RelationGetRelationName(rel)),
6781 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6782 else
6783 /* internal error? */
6784 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6786 }
6787
6788 /* Permissions checks */
6789 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6792
6794 ereport(ERROR,
6795 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6796 errmsg("permission denied: \"%s\" is a system catalog",
6798}
6799
6800/*
6801 * ATSimpleRecursion
6802 *
6803 * Simple table recursion sufficient for most ALTER TABLE operations.
6804 * All direct and indirect children are processed in an unspecified order.
6805 * Note that if a child inherits from the original table via multiple
6806 * inheritance paths, it will be visited just once.
6807 */
6808static void
6810 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6811 AlterTableUtilityContext *context)
6812{
6813 /*
6814 * Propagate to children, if desired and if there are (or might be) any
6815 * children.
6816 */
6817 if (recurse && rel->rd_rel->relhassubclass)
6818 {
6819 Oid relid = RelationGetRelid(rel);
6820 ListCell *child;
6821 List *children;
6822
6823 children = find_all_inheritors(relid, lockmode, NULL);
6824
6825 /*
6826 * find_all_inheritors does the recursive search of the inheritance
6827 * hierarchy, so all we have to do is process all of the relids in the
6828 * list that it returns.
6829 */
6830 foreach(child, children)
6831 {
6832 Oid childrelid = lfirst_oid(child);
6833 Relation childrel;
6834
6835 if (childrelid == relid)
6836 continue;
6837 /* find_all_inheritors already got lock */
6838 childrel = relation_open(childrelid, NoLock);
6839 CheckAlterTableIsSafe(childrel);
6840 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6841 relation_close(childrel, NoLock);
6842 }
6843 }
6844}
6845
6846/*
6847 * Obtain list of partitions of the given table, locking them all at the given
6848 * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6849 *
6850 * This function is a no-op if the given relation is not a partitioned table;
6851 * in particular, nothing is done if it's a legacy inheritance parent.
6852 */
6853static void
6855{
6856 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6857 {
6858 List *inh;
6859 ListCell *cell;
6860
6861 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6862 /* first element is the parent rel; must ignore it */
6863 for_each_from(cell, inh, 1)
6864 {
6865 Relation childrel;
6866
6867 /* find_all_inheritors already got lock */
6868 childrel = table_open(lfirst_oid(cell), NoLock);
6869 CheckAlterTableIsSafe(childrel);
6870 table_close(childrel, NoLock);
6871 }
6872 list_free(inh);
6873 }
6874}
6875
6876/*
6877 * ATTypedTableRecursion
6878 *
6879 * Propagate ALTER TYPE operations to the typed tables of that type.
6880 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6881 * recursion to inheritance children of the typed tables.
6882 */
6883static void
6885 LOCKMODE lockmode, AlterTableUtilityContext *context)
6886{
6887 ListCell *child;
6888 List *children;
6889
6890 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6891
6892 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6894 cmd->behavior);
6895
6896 foreach(child, children)
6897 {
6898 Oid childrelid = lfirst_oid(child);
6899 Relation childrel;
6900
6901 childrel = relation_open(childrelid, lockmode);
6902 CheckAlterTableIsSafe(childrel);
6903 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6904 relation_close(childrel, NoLock);
6905 }
6906}
6907
6908
6909/*
6910 * find_composite_type_dependencies
6911 *
6912 * Check to see if the type "typeOid" is being used as a column in some table
6913 * (possibly nested several levels deep in composite types, arrays, etc!).
6914 * Eventually, we'd like to propagate the check or rewrite operation
6915 * into such tables, but for now, just error out if we find any.
6916 *
6917 * Caller should provide either the associated relation of a rowtype,
6918 * or a type name (not both) for use in the error message, if any.
6919 *
6920 * Note that "typeOid" is not necessarily a composite type; it could also be
6921 * another container type such as an array or range, or a domain over one of
6922 * these things. The name of this function is therefore somewhat historical,
6923 * but it's not worth changing.
6924 *
6925 * We assume that functions and views depending on the type are not reasons
6926 * to reject the ALTER. (How safe is this really?)
6927 */
6928void
6930 const char *origTypeName)
6931{
6932 Relation depRel;
6933 ScanKeyData key[2];
6934 SysScanDesc depScan;
6935 HeapTuple depTup;
6936
6937 /* since this function recurses, it could be driven to stack overflow */
6939
6940 /*
6941 * We scan pg_depend to find those things that depend on the given type.
6942 * (We assume we can ignore refobjsubid for a type.)
6943 */
6944 depRel = table_open(DependRelationId, AccessShareLock);
6945
6946 ScanKeyInit(&key[0],
6947 Anum_pg_depend_refclassid,
6948 BTEqualStrategyNumber, F_OIDEQ,
6949 ObjectIdGetDatum(TypeRelationId));
6950 ScanKeyInit(&key[1],
6951 Anum_pg_depend_refobjid,
6952 BTEqualStrategyNumber, F_OIDEQ,
6953 ObjectIdGetDatum(typeOid));
6954
6955 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6956 NULL, 2, key);
6957
6958 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6959 {
6960 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6961 Relation rel;
6962 TupleDesc tupleDesc;
6964
6965 /* Check for directly dependent types */
6966 if (pg_depend->classid == TypeRelationId)
6967 {
6968 /*
6969 * This must be an array, domain, or range containing the given
6970 * type, so recursively check for uses of this type. Note that
6971 * any error message will mention the original type not the
6972 * container; this is intentional.
6973 */
6974 find_composite_type_dependencies(pg_depend->objid,
6975 origRelation, origTypeName);
6976 continue;
6977 }
6978
6979 /* Else, ignore dependees that aren't relations */
6980 if (pg_depend->classid != RelationRelationId)
6981 continue;
6982
6983 rel = relation_open(pg_depend->objid, AccessShareLock);
6984 tupleDesc = RelationGetDescr(rel);
6985
6986 /*
6987 * If objsubid identifies a specific column, refer to that in error
6988 * messages. Otherwise, search to see if there's a user column of the
6989 * type. (We assume system columns are never of interesting types.)
6990 * The search is needed because an index containing an expression
6991 * column of the target type will just be recorded as a whole-relation
6992 * dependency. If we do not find a column of the type, the dependency
6993 * must indicate that the type is transiently referenced in an index
6994 * expression but not stored on disk, which we assume is OK, just as
6995 * we do for references in views. (It could also be that the target
6996 * type is embedded in some container type that is stored in an index
6997 * column, but the previous recursion should catch such cases.)
6998 */
6999 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
7000 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7001 else
7002 {
7003 att = NULL;
7004 for (int attno = 1; attno <= tupleDesc->natts; attno++)
7005 {
7006 att = TupleDescAttr(tupleDesc, attno - 1);
7007 if (att->atttypid == typeOid && !att->attisdropped)
7008 break;
7009 att = NULL;
7010 }
7011 if (att == NULL)
7012 {
7013 /* No such column, so assume OK */
7015 continue;
7016 }
7017 }
7018
7019 /*
7020 * We definitely should reject if the relation has storage. If it's
7021 * partitioned, then perhaps we don't have to reject: if there are
7022 * partitions then we'll fail when we find one, else there is no
7023 * stored data to worry about. However, it's possible that the type
7024 * change would affect conclusions about whether the type is sortable
7025 * or hashable and thus (if it's a partitioning column) break the
7026 * partitioning rule. For now, reject for partitioned rels too.
7027 */
7028 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7029 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7030 {
7031 if (origTypeName)
7032 ereport(ERROR,
7033 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7034 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7035 origTypeName,
7037 NameStr(att->attname))));
7038 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7039 ereport(ERROR,
7040 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7041 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7042 RelationGetRelationName(origRelation),
7044 NameStr(att->attname))));
7045 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7046 ereport(ERROR,
7047 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7048 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7049 RelationGetRelationName(origRelation),
7051 NameStr(att->attname))));
7052 else
7053 ereport(ERROR,
7054 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7055 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7056 RelationGetRelationName(origRelation),
7058 NameStr(att->attname))));
7059 }
7060 else if (OidIsValid(rel->rd_rel->reltype))
7061 {
7062 /*
7063 * A view or composite type itself isn't a problem, but we must
7064 * recursively check for indirect dependencies via its rowtype.
7065 */
7067 origRelation, origTypeName);
7068 }
7069
7071 }
7072
7073 systable_endscan(depScan);
7074
7076}
7077
7078
7079/*
7080 * find_typed_table_dependencies
7081 *
7082 * Check to see if a composite type is being used as the type of a
7083 * typed table. Abort if any are found and behavior is RESTRICT.
7084 * Else return the list of tables.
7085 */
7086static List *
7087find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7088{
7089 Relation classRel;
7090 ScanKeyData key[1];
7091 TableScanDesc scan;
7092 HeapTuple tuple;
7093 List *result = NIL;
7094
7095 classRel = table_open(RelationRelationId, AccessShareLock);
7096
7097 ScanKeyInit(&key[0],
7098 Anum_pg_class_reloftype,
7099 BTEqualStrategyNumber, F_OIDEQ,
7100 ObjectIdGetDatum(typeOid));
7101
7102 scan = table_beginscan_catalog(classRel, 1, key);
7103
7104 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7105 {
7106 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7107
7108 if (behavior == DROP_RESTRICT)
7109 ereport(ERROR,
7110 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7111 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7112 typeName),
7113 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7114 else
7115 result = lappend_oid(result, classform->oid);
7116 }
7117
7118 table_endscan(scan);
7119 table_close(classRel, AccessShareLock);
7120
7121 return result;
7122}
7123
7124
7125/*
7126 * check_of_type
7127 *
7128 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7129 * isn't suitable, throw an error. Currently, we require that the type
7130 * originated with CREATE TYPE AS. We could support any row type, but doing so
7131 * would require handling a number of extra corner cases in the DDL commands.
7132 * (Also, allowing domain-over-composite would open up a can of worms about
7133 * whether and how the domain's constraints should apply to derived tables.)
7134 */
7135void
7137{
7138 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7139 bool typeOk = false;
7140
7141 if (typ->typtype == TYPTYPE_COMPOSITE)
7142 {
7143 Relation typeRelation;
7144
7145 Assert(OidIsValid(typ->typrelid));
7146 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7147 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7148
7149 /*
7150 * Close the parent rel, but keep our AccessShareLock on it until xact
7151 * commit. That will prevent someone else from deleting or ALTERing
7152 * the type before the typed table creation/conversion commits.
7153 */
7154 relation_close(typeRelation, NoLock);
7155
7156 if (!typeOk)
7157 ereport(ERROR,
7158 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7159 errmsg("type %s is the row type of another table",
7160 format_type_be(typ->oid)),
7161 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7162 }
7163 else
7164 ereport(ERROR,
7165 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7166 errmsg("type %s is not a composite type",
7167 format_type_be(typ->oid))));
7168}
7169
7170
7171/*
7172 * ALTER TABLE ADD COLUMN
7173 *
7174 * Adds an additional attribute to a relation making the assumption that
7175 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7176 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7177 * AlterTableCmd's.
7178 *
7179 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7180 * have to decide at runtime whether to recurse or not depending on whether we
7181 * actually add a column or merely merge with an existing column. (We can't
7182 * check this in a static pre-pass because it won't handle multiple inheritance
7183 * situations correctly.)
7184 */
7185static void
7186ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7187 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7188 AlterTableUtilityContext *context)
7189{
7190 if (rel->rd_rel->reloftype && !recursing)
7191 ereport(ERROR,
7192 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7193 errmsg("cannot add column to typed table")));
7194
7195 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7196 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7197
7198 if (recurse && !is_view)
7199 cmd->recurse = true;
7200}
7201
7202/*
7203 * Add a column to a table. The return value is the address of the
7204 * new column in the parent relation.
7205 *
7206 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7207 * copy (but that happens only after we check for IF NOT EXISTS).
7208 */
7209static ObjectAddress
7211 AlterTableCmd **cmd, bool recurse, bool recursing,
7212 LOCKMODE lockmode, AlterTablePass cur_pass,
7213 AlterTableUtilityContext *context)
7214{
7215 Oid myrelid = RelationGetRelid(rel);
7216 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7217 bool if_not_exists = (*cmd)->missing_ok;
7218 Relation pgclass,
7219 attrdesc;
7220 HeapTuple reltup;
7221 Form_pg_class relform;
7222 Form_pg_attribute attribute;
7223 int newattnum;
7224 char relkind;
7225 Expr *defval;
7226 List *children;
7227 ListCell *child;
7228 AlterTableCmd *childcmd;
7229 ObjectAddress address;
7230 TupleDesc tupdesc;
7231
7232 /* since this function recurses, it could be driven to stack overflow */
7234
7235 /* At top level, permission check was done in ATPrepCmd, else do it */
7236 if (recursing)
7237 ATSimplePermissions((*cmd)->subtype, rel,
7239
7240 if (rel->rd_rel->relispartition && !recursing)
7241 ereport(ERROR,
7242 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7243 errmsg("cannot add column to a partition")));
7244
7245 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7246
7247 /*
7248 * Are we adding the column to a recursion child? If so, check whether to
7249 * merge with an existing definition for the column. If we do merge, we
7250 * must not recurse. Children will already have the column, and recursing
7251 * into them would mess up attinhcount.
7252 */
7253 if (colDef->inhcount > 0)
7254 {
7255 HeapTuple tuple;
7256
7257 /* Does child already have a column by this name? */
7258 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7259 if (HeapTupleIsValid(tuple))
7260 {
7261 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7262 Oid ctypeId;
7263 int32 ctypmod;
7264 Oid ccollid;
7265
7266 /* Child column must match on type, typmod, and collation */
7267 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7268 if (ctypeId != childatt->atttypid ||
7269 ctypmod != childatt->atttypmod)
7270 ereport(ERROR,
7271 (errcode(ERRCODE_DATATYPE_MISMATCH),
7272 errmsg("child table \"%s\" has different type for column \"%s\"",
7273 RelationGetRelationName(rel), colDef->colname)));
7274 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7275 if (ccollid != childatt->attcollation)
7276 ereport(ERROR,
7277 (errcode(ERRCODE_COLLATION_MISMATCH),
7278 errmsg("child table \"%s\" has different collation for column \"%s\"",
7279 RelationGetRelationName(rel), colDef->colname),
7280 errdetail("\"%s\" versus \"%s\"",
7281 get_collation_name(ccollid),
7282 get_collation_name(childatt->attcollation))));
7283
7284 /* Bump the existing child att's inhcount */
7285 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7286 &childatt->attinhcount))
7287 ereport(ERROR,
7288 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7289 errmsg("too many inheritance parents"));
7290 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7291
7292 heap_freetuple(tuple);
7293
7294 /* Inform the user about the merge */
7296 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7297 colDef->colname, RelationGetRelationName(rel))));
7298
7299 table_close(attrdesc, RowExclusiveLock);
7300
7301 /* Make the child column change visible */
7303
7304 return InvalidObjectAddress;
7305 }
7306 }
7307
7308 /* skip if the name already exists and if_not_exists is true */
7309 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7310 {
7311 table_close(attrdesc, RowExclusiveLock);
7312 return InvalidObjectAddress;
7313 }
7314
7315 /*
7316 * Okay, we need to add the column, so go ahead and do parse
7317 * transformation. This can result in queueing up, or even immediately
7318 * executing, subsidiary operations (such as creation of unique indexes);
7319 * so we mustn't do it until we have made the if_not_exists check.
7320 *
7321 * When recursing, the command was already transformed and we needn't do
7322 * so again. Also, if context isn't given we can't transform. (That
7323 * currently happens only for AT_AddColumnToView; we expect that view.c
7324 * passed us a ColumnDef that doesn't need work.)
7325 */
7326 if (context != NULL && !recursing)
7327 {
7328 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7329 cur_pass, context);
7330 Assert(*cmd != NULL);
7331 colDef = castNode(ColumnDef, (*cmd)->def);
7332 }
7333
7334 /*
7335 * Regular inheritance children are independent enough not to inherit the
7336 * identity column from parent hence cannot recursively add identity
7337 * column if the table has inheritance children.
7338 *
7339 * Partitions, on the other hand, are integral part of a partitioned table
7340 * and inherit identity column. Hence propagate identity column down the
7341 * partition hierarchy.
7342 */
7343 if (colDef->identity &&
7344 recurse &&
7345 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7347 ereport(ERROR,
7348 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7349 errmsg("cannot recursively add identity column to table that has child tables")));
7350
7351 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7352
7353 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7354 if (!HeapTupleIsValid(reltup))
7355 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7356 relform = (Form_pg_class) GETSTRUCT(reltup);
7357 relkind = relform->relkind;
7358
7359 /* Determine the new attribute's number */
7360 newattnum = relform->relnatts + 1;
7361 if (newattnum > MaxHeapAttributeNumber)
7362 ereport(ERROR,
7363 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7364 errmsg("tables can have at most %d columns",
7366
7367 /*
7368 * Construct new attribute's pg_attribute entry.
7369 */
7370 tupdesc = BuildDescForRelation(list_make1(colDef));
7371
7372 attribute = TupleDescAttr(tupdesc, 0);
7373
7374 /* Fix up attribute number */
7375 attribute->attnum = newattnum;
7376
7377 /* make sure datatype is legal for a column */
7378 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7379 list_make1_oid(rel->rd_rel->reltype),
7380 (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7381
7382 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7383
7384 table_close(attrdesc, RowExclusiveLock);
7385
7386 /*
7387 * Update pg_class tuple as appropriate
7388 */
7389 relform->relnatts = newattnum;
7390
7391 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7392
7393 heap_freetuple(reltup);
7394
7395 /* Post creation hook for new attribute */
7396 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7397
7398 table_close(pgclass, RowExclusiveLock);
7399
7400 /* Make the attribute's catalog entry visible */
7402
7403 /*
7404 * Store the DEFAULT, if any, in the catalogs
7405 */
7406 if (colDef->raw_default)
7407 {
7408 RawColumnDefault *rawEnt;
7409
7410 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7411 rawEnt->attnum = attribute->attnum;
7412 rawEnt->raw_default = copyObject(colDef->raw_default);
7413 rawEnt->generated = colDef->generated;
7414
7415 /*
7416 * This function is intended for CREATE TABLE, so it processes a
7417 * _list_ of defaults, but we just do one.
7418 */
7420 false, true, false, NULL);
7421
7422 /* Make the additional catalog changes visible */
7424 }
7425
7426 /*
7427 * Tell Phase 3 to fill in the default expression, if there is one.
7428 *
7429 * If there is no default, Phase 3 doesn't have to do anything, because
7430 * that effectively means that the default is NULL. The heap tuple access
7431 * routines always check for attnum > # of attributes in tuple, and return
7432 * NULL if so, so without any modification of the tuple data we will get
7433 * the effect of NULL values in the new column.
7434 *
7435 * An exception occurs when the new column is of a domain type: the domain
7436 * might have a not-null constraint, or a check constraint that indirectly
7437 * rejects nulls. If there are any domain constraints then we construct
7438 * an explicit NULL default value that will be passed through
7439 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7440 * rewriting the table which we really wouldn't have to do; but we do it
7441 * to preserve the historical behavior that such a failure will be raised
7442 * only if the table currently contains some rows.)
7443 *
7444 * Note: we use build_column_default, and not just the cooked default
7445 * returned by AddRelationNewConstraints, so that the right thing happens
7446 * when a datatype's default applies.
7447 *
7448 * Note: it might seem that this should happen at the end of Phase 2, so
7449 * that the effects of subsequent subcommands can be taken into account.
7450 * It's intentional that we do it now, though. The new column should be
7451 * filled according to what is said in the ADD COLUMN subcommand, so that
7452 * the effects are the same as if this subcommand had been run by itself
7453 * and the later subcommands had been issued in new ALTER TABLE commands.
7454 *
7455 * We can skip this entirely for relations without storage, since Phase 3
7456 * is certainly not going to touch them.
7457 */
7458 if (RELKIND_HAS_STORAGE(relkind))
7459 {
7460 bool has_domain_constraints;
7461 bool has_missing = false;
7462
7463 /*
7464 * For an identity column, we can't use build_column_default(),
7465 * because the sequence ownership isn't set yet. So do it manually.
7466 */
7467 if (colDef->identity)
7468 {
7470
7471 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7472 nve->typeId = attribute->atttypid;
7473
7474 defval = (Expr *) nve;
7475 }
7476 else
7477 defval = (Expr *) build_column_default(rel, attribute->attnum);
7478
7479 /* Build CoerceToDomain(NULL) expression if needed */
7480 has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7481 if (!defval && has_domain_constraints)
7482 {
7483 Oid baseTypeId;
7484 int32 baseTypeMod;
7485 Oid baseTypeColl;
7486
7487 baseTypeMod = attribute->atttypmod;
7488 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7489 baseTypeColl = get_typcollation(baseTypeId);
7490 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7491 defval = (Expr *) coerce_to_target_type(NULL,
7492 (Node *) defval,
7493 baseTypeId,
7494 attribute->atttypid,
7495 attribute->atttypmod,
7498 -1);
7499 if (defval == NULL) /* should not happen */
7500 elog(ERROR, "failed to coerce base type to domain");
7501 }
7502
7503 if (defval)
7504 {
7506
7507 /* Prepare defval for execution, either here or in Phase 3 */
7508 defval = expression_planner(defval);
7509
7510 /* Add the new default to the newvals list */
7512 newval->attnum = attribute->attnum;
7513 newval->expr = defval;
7514 newval->is_generated = (colDef->generated != '\0');
7515
7516 tab->newvals = lappend(tab->newvals, newval);
7517
7518 /*
7519 * Attempt to skip a complete table rewrite by storing the
7520 * specified DEFAULT value outside of the heap. This is only
7521 * allowed for plain relations and non-generated columns, and the
7522 * default expression can't be volatile (stable is OK). Note that
7523 * contain_volatile_functions deems CoerceToDomain immutable, but
7524 * here we consider that coercion to a domain with constraints is
7525 * volatile; else it might fail even when the table is empty.
7526 */
7527 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7528 !colDef->generated &&
7529 !has_domain_constraints &&
7530 !contain_volatile_functions((Node *) defval))
7531 {
7532 EState *estate;
7533 ExprState *exprState;
7534 Datum missingval;
7535 bool missingIsNull;
7536
7537 /* Evaluate the default expression */
7538 estate = CreateExecutorState();
7539 exprState = ExecPrepareExpr(defval, estate);
7540 missingval = ExecEvalExpr(exprState,
7541 GetPerTupleExprContext(estate),
7542 &missingIsNull);
7543 /* If it turns out NULL, nothing to do; else store it */
7544 if (!missingIsNull)
7545 {
7546 StoreAttrMissingVal(rel, attribute->attnum, missingval);
7547 /* Make the additional catalog change visible */
7549 has_missing = true;
7550 }
7551 FreeExecutorState(estate);
7552 }
7553 else
7554 {
7555 /*
7556 * Failed to use missing mode. We have to do a table rewrite
7557 * to install the value --- unless it's a virtual generated
7558 * column.
7559 */
7560 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7562 }
7563 }
7564
7565 if (!has_missing)
7566 {
7567 /*
7568 * If the new column is NOT NULL, and there is no missing value,
7569 * tell Phase 3 it needs to check for NULLs.
7570 */
7571 tab->verify_new_notnull |= colDef->is_not_null;
7572 }
7573 }
7574
7575 /*
7576 * Add needed dependency entries for the new column.
7577 */
7578 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7579 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7580
7581 /*
7582 * Propagate to children as appropriate. Unlike most other ALTER
7583 * routines, we have to do this one level of recursion at a time; we can't
7584 * use find_all_inheritors to do it in one pass.
7585 */
7586 children =
7588
7589 /*
7590 * If we are told not to recurse, there had better not be any child
7591 * tables; else the addition would put them out of step.
7592 */
7593 if (children && !recurse)
7594 ereport(ERROR,
7595 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7596 errmsg("column must be added to child tables too")));
7597
7598 /* Children should see column as singly inherited */
7599 if (!recursing)
7600 {
7601 childcmd = copyObject(*cmd);
7602 colDef = castNode(ColumnDef, childcmd->def);
7603 colDef->inhcount = 1;
7604 colDef->is_local = false;
7605 }
7606 else
7607 childcmd = *cmd; /* no need to copy again */
7608
7609 foreach(child, children)
7610 {
7611 Oid childrelid = lfirst_oid(child);
7612 Relation childrel;
7613 AlteredTableInfo *childtab;
7614
7615 /* find_inheritance_children already got lock */
7616 childrel = table_open(childrelid, NoLock);
7617 CheckAlterTableIsSafe(childrel);
7618
7619 /* Find or create work queue entry for this table */
7620 childtab = ATGetQueueEntry(wqueue, childrel);
7621
7622 /* Recurse to child; return value is ignored */
7623 ATExecAddColumn(wqueue, childtab, childrel,
7624 &childcmd, recurse, true,
7625 lockmode, cur_pass, context);
7626
7627 table_close(childrel, NoLock);
7628 }
7629
7630 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7631 return address;
7632}
7633
7634/*
7635 * If a new or renamed column will collide with the name of an existing
7636 * column and if_not_exists is false then error out, else do nothing.
7637 */
7638static bool
7640 bool if_not_exists)
7641{
7642 HeapTuple attTuple;
7643 int attnum;
7644
7645 /*
7646 * this test is deliberately not attisdropped-aware, since if one tries to
7647 * add a column matching a dropped column name, it's gonna fail anyway.
7648 */
7649 attTuple = SearchSysCache2(ATTNAME,
7651 PointerGetDatum(colname));
7652 if (!HeapTupleIsValid(attTuple))
7653 return true;
7654
7655 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7656 ReleaseSysCache(attTuple);
7657
7658 /*
7659 * We throw a different error message for conflicts with system column
7660 * names, since they are normally not shown and the user might otherwise
7661 * be confused about the reason for the conflict.
7662 */
7663 if (attnum <= 0)
7664 ereport(ERROR,
7665 (errcode(ERRCODE_DUPLICATE_COLUMN),
7666 errmsg("column name \"%s\" conflicts with a system column name",
7667 colname)));
7668 else
7669 {
7670 if (if_not_exists)
7671 {
7673 (errcode(ERRCODE_DUPLICATE_COLUMN),
7674 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7675 colname, RelationGetRelationName(rel))));
7676 return false;
7677 }
7678
7679 ereport(ERROR,
7680 (errcode(ERRCODE_DUPLICATE_COLUMN),
7681 errmsg("column \"%s\" of relation \"%s\" already exists",
7682 colname, RelationGetRelationName(rel))));
7683 }
7684
7685 return true;
7686}
7687
7688/*
7689 * Install a column's dependency on its datatype.
7690 */
7691static void
7693{
7694 ObjectAddress myself,
7695 referenced;
7696
7697 myself.classId = RelationRelationId;
7698 myself.objectId = relid;
7699 myself.objectSubId = attnum;
7700 referenced.classId = TypeRelationId;
7701 referenced.objectId = typid;
7702 referenced.objectSubId = 0;
7703 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7704}
7705
7706/*
7707 * Install a column's dependency on its collation.
7708 */
7709static void
7711{
7712 ObjectAddress myself,
7713 referenced;
7714
7715 /* We know the default collation is pinned, so don't bother recording it */
7716 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7717 {
7718 myself.classId = RelationRelationId;
7719 myself.objectId = relid;
7720 myself.objectSubId = attnum;
7721 referenced.classId = CollationRelationId;
7722 referenced.objectId = collid;
7723 referenced.objectSubId = 0;
7724 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7725 }
7726}
7727
7728/*
7729 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7730 *
7731 * Return the address of the modified column. If the column was already
7732 * nullable, InvalidObjectAddress is returned.
7733 */
7734static ObjectAddress
7735ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7736 LOCKMODE lockmode)
7737{
7738 HeapTuple tuple;
7739 HeapTuple conTup;
7740 Form_pg_attribute attTup;
7742 Relation attr_rel;
7743 ObjectAddress address;
7744
7745 /*
7746 * lookup the attribute
7747 */
7748 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7749
7750 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7751 if (!HeapTupleIsValid(tuple))
7752 ereport(ERROR,
7753 (errcode(ERRCODE_UNDEFINED_COLUMN),
7754 errmsg("column \"%s\" of relation \"%s\" does not exist",
7755 colName, RelationGetRelationName(rel))));
7756 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7757 attnum = attTup->attnum;
7758 ObjectAddressSubSet(address, RelationRelationId,
7759 RelationGetRelid(rel), attnum);
7760
7761 /* If the column is already nullable there's nothing to do. */
7762 if (!attTup->attnotnull)
7763 {
7764 table_close(attr_rel, RowExclusiveLock);
7765 return InvalidObjectAddress;
7766 }
7767
7768 /* Prevent them from altering a system attribute */
7769 if (attnum <= 0)
7770 ereport(ERROR,
7771 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7772 errmsg("cannot alter system column \"%s\"",
7773 colName)));
7774
7775 if (attTup->attidentity)
7776 ereport(ERROR,
7777 (errcode(ERRCODE_SYNTAX_ERROR),
7778 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7779 colName, RelationGetRelationName(rel))));
7780
7781 /*
7782 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7783 */
7784 if (rel->rd_rel->relispartition)
7785 {
7786 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7787 Relation parent = table_open(parentId, AccessShareLock);
7788 TupleDesc tupDesc = RelationGetDescr(parent);
7789 AttrNumber parent_attnum;
7790
7791 parent_attnum = get_attnum(parentId, colName);
7792 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7793 ereport(ERROR,
7794 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7795 errmsg("column \"%s\" is marked NOT NULL in parent table",
7796 colName)));
7798 }
7799
7800 /*
7801 * Find the constraint that makes this column NOT NULL, and drop it.
7802 * dropconstraint_internal() resets attnotnull.
7803 */
7805 if (conTup == NULL)
7806 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7807 colName, RelationGetRelationName(rel));
7808
7809 /* The normal case: we have a pg_constraint row, remove it */
7810 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7811 false, lockmode);
7812 heap_freetuple(conTup);
7813
7814 InvokeObjectPostAlterHook(RelationRelationId,
7815 RelationGetRelid(rel), attnum);
7816
7817 table_close(attr_rel, RowExclusiveLock);
7818
7819 return address;
7820}
7821
7822/*
7823 * set_attnotnull
7824 * Helper to update/validate the pg_attribute status of a not-null
7825 * constraint
7826 *
7827 * pg_attribute.attnotnull is set true, if it isn't already.
7828 * If queue_validation is true, also set up wqueue to validate the constraint.
7829 * wqueue may be given as NULL when validation is not needed (e.g., on table
7830 * creation).
7831 */
7832static void
7834 bool is_valid, bool queue_validation)
7835{
7836 Form_pg_attribute attr;
7837 CompactAttribute *thisatt;
7838
7839 Assert(!queue_validation || wqueue);
7840
7842
7843 /*
7844 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7845 * attribute.
7846 */
7847 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7848 if (attr->attisdropped)
7849 return;
7850
7851 if (!attr->attnotnull)
7852 {
7853 Relation attr_rel;
7854 HeapTuple tuple;
7855
7856 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7857
7859 if (!HeapTupleIsValid(tuple))
7860 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7861 attnum, RelationGetRelid(rel));
7862
7863 thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7865
7866 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7867
7868 attr->attnotnull = true;
7869 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7870
7871 /*
7872 * If the nullness isn't already proven by validated constraints, have
7873 * ALTER TABLE phase 3 test for it.
7874 */
7875 if (queue_validation && wqueue &&
7877 {
7878 AlteredTableInfo *tab;
7879
7880 tab = ATGetQueueEntry(wqueue, rel);
7881 tab->verify_new_notnull = true;
7882 }
7883
7885
7886 table_close(attr_rel, RowExclusiveLock);
7887 heap_freetuple(tuple);
7888 }
7889 else
7890 {
7892 }
7893}
7894
7895/*
7896 * ALTER TABLE ALTER COLUMN SET NOT NULL
7897 *
7898 * Add a not-null constraint to a single table and its children. Returns
7899 * the address of the constraint added to the parent relation, if one gets
7900 * added, or InvalidObjectAddress otherwise.
7901 *
7902 * We must recurse to child tables during execution, rather than using
7903 * ALTER TABLE's normal prep-time recursion.
7904 */
7905static ObjectAddress
7906ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7907 bool recurse, bool recursing, LOCKMODE lockmode)
7908{
7909 HeapTuple tuple;
7911 ObjectAddress address;
7912 Constraint *constraint;
7913 CookedConstraint *ccon;
7914 List *cooked;
7915 bool is_no_inherit = false;
7916
7917 /* Guard against stack overflow due to overly deep inheritance tree. */
7919
7920 /* At top level, permission check was done in ATPrepCmd, else do it */
7921 if (recursing)
7922 {
7925 Assert(conName != NULL);
7926 }
7927
7928 attnum = get_attnum(RelationGetRelid(rel), colName);
7930 ereport(ERROR,
7931 (errcode(ERRCODE_UNDEFINED_COLUMN),
7932 errmsg("column \"%s\" of relation \"%s\" does not exist",
7933 colName, RelationGetRelationName(rel))));
7934
7935 /* Prevent them from altering a system attribute */
7936 if (attnum <= 0)
7937 ereport(ERROR,
7938 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7939 errmsg("cannot alter system column \"%s\"",
7940 colName)));
7941
7942 /* See if there's already a constraint */
7944 if (HeapTupleIsValid(tuple))
7945 {
7947 bool changed = false;
7948
7949 /*
7950 * Don't let a NO INHERIT constraint be changed into inherit.
7951 */
7952 if (conForm->connoinherit && recurse)
7953 ereport(ERROR,
7954 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7955 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7956 NameStr(conForm->conname),
7958
7959 /*
7960 * If we find an appropriate constraint, we're almost done, but just
7961 * need to change some properties on it: if we're recursing, increment
7962 * coninhcount; if not, set conislocal if not already set.
7963 */
7964 if (recursing)
7965 {
7966 if (pg_add_s16_overflow(conForm->coninhcount, 1,
7967 &conForm->coninhcount))
7968 ereport(ERROR,
7969 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7970 errmsg("too many inheritance parents"));
7971 changed = true;
7972 }
7973 else if (!conForm->conislocal)
7974 {
7975 conForm->conislocal = true;
7976 changed = true;
7977 }
7978 else if (!conForm->convalidated)
7979 {
7980 /*
7981 * Flip attnotnull and convalidated, and also validate the
7982 * constraint.
7983 */
7984 return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
7985 recurse, recursing, lockmode);
7986 }
7987
7988 if (changed)
7989 {
7990 Relation constr_rel;
7991
7992 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7993
7994 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7995 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7996 table_close(constr_rel, RowExclusiveLock);
7997 }
7998
7999 if (changed)
8000 return address;
8001 else
8002 return InvalidObjectAddress;
8003 }
8004
8005 /*
8006 * If we're asked not to recurse, and children exist, raise an error for
8007 * partitioned tables. For inheritance, we act as if NO INHERIT had been
8008 * specified.
8009 */
8010 if (!recurse &&
8012 NoLock) != NIL)
8013 {
8014 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8015 ereport(ERROR,
8016 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8017 errmsg("constraint must be added to child tables too"),
8018 errhint("Do not specify the ONLY keyword."));
8019 else
8020 is_no_inherit = true;
8021 }
8022
8023 /*
8024 * No constraint exists; we must add one. First determine a name to use,
8025 * if we haven't already.
8026 */
8027 if (!recursing)
8028 {
8029 Assert(conName == NULL);
8031 colName, "not_null",
8033 NIL);
8034 }
8035
8036 constraint = makeNotNullConstraint(makeString(colName));
8037 constraint->is_no_inherit = is_no_inherit;
8038 constraint->conname = conName;
8039
8040 /* and do it */
8041 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8042 false, !recursing, false, NULL);
8043 ccon = linitial(cooked);
8044 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8045
8046 InvokeObjectPostAlterHook(RelationRelationId,
8047 RelationGetRelid(rel), attnum);
8048
8049 /* Mark pg_attribute.attnotnull for the column and queue validation */
8050 set_attnotnull(wqueue, rel, attnum, true, true);
8051
8052 /*
8053 * Recurse to propagate the constraint to children that don't have one.
8054 */
8055 if (recurse)
8056 {
8057 List *children;
8058
8060 lockmode);
8061
8062 foreach_oid(childoid, children)
8063 {
8064 Relation childrel = table_open(childoid, NoLock);
8065
8067
8068 ATExecSetNotNull(wqueue, childrel, conName, colName,
8069 recurse, true, lockmode);
8070 table_close(childrel, NoLock);
8071 }
8072 }
8073
8074 return address;
8075}
8076
8077/*
8078 * NotNullImpliedByRelConstraints
8079 * Does rel's existing constraints imply NOT NULL for the given attribute?
8080 */
8081static bool
8083{
8084 NullTest *nnulltest = makeNode(NullTest);
8085
8086 nnulltest->arg = (Expr *) makeVar(1,
8087 attr->attnum,
8088 attr->atttypid,
8089 attr->atttypmod,
8090 attr->attcollation,
8091 0);
8092 nnulltest->nulltesttype = IS_NOT_NULL;
8093
8094 /*
8095 * argisrow = false is correct even for a composite column, because
8096 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8097 * case, just IS DISTINCT FROM NULL.
8098 */
8099 nnulltest->argisrow = false;
8100 nnulltest->location = -1;
8101
8102 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8103 {
8105 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8106 RelationGetRelationName(rel), NameStr(attr->attname))));
8107 return true;
8108 }
8109
8110 return false;
8111}
8112
8113/*
8114 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8115 *
8116 * Return the address of the affected column.
8117 */
8118static ObjectAddress
8119ATExecColumnDefault(Relation rel, const char *colName,
8120 Node *newDefault, LOCKMODE lockmode)
8121{
8122 TupleDesc tupdesc = RelationGetDescr(rel);
8124 ObjectAddress address;
8125
8126 /*
8127 * get the number of the attribute
8128 */
8129 attnum = get_attnum(RelationGetRelid(rel), colName);
8131 ereport(ERROR,
8132 (errcode(ERRCODE_UNDEFINED_COLUMN),
8133 errmsg("column \"%s\" of relation \"%s\" does not exist",
8134 colName, RelationGetRelationName(rel))));
8135
8136 /* Prevent them from altering a system attribute */
8137 if (attnum <= 0)
8138 ereport(ERROR,
8139 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8140 errmsg("cannot alter system column \"%s\"",
8141 colName)));
8142
8143 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8144 ereport(ERROR,
8145 (errcode(ERRCODE_SYNTAX_ERROR),
8146 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8147 colName, RelationGetRelationName(rel)),
8148 /* translator: %s is an SQL ALTER command */
8149 newDefault ? 0 : errhint("Use %s instead.",
8150 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8151
8152 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8153 ereport(ERROR,
8154 (errcode(ERRCODE_SYNTAX_ERROR),
8155 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8156 colName, RelationGetRelationName(rel)),
8157 newDefault ?
8158 /* translator: %s is an SQL ALTER command */
8159 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8160 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8161 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8162
8163 /*
8164 * Remove any old default for the column. We use RESTRICT here for
8165 * safety, but at present we do not expect anything to depend on the
8166 * default.
8167 *
8168 * We treat removing the existing default as an internal operation when it
8169 * is preparatory to adding a new default, but as a user-initiated
8170 * operation when the user asked for a drop.
8171 */
8173 newDefault != NULL);
8174
8175 if (newDefault)
8176 {
8177 /* SET DEFAULT */
8178 RawColumnDefault *rawEnt;
8179
8180 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8181 rawEnt->attnum = attnum;
8182 rawEnt->raw_default = newDefault;
8183 rawEnt->generated = '\0';
8184
8185 /*
8186 * This function is intended for CREATE TABLE, so it processes a
8187 * _list_ of defaults, but we just do one.
8188 */
8190 false, true, false, NULL);
8191 }
8192
8193 ObjectAddressSubSet(address, RelationRelationId,
8194 RelationGetRelid(rel), attnum);
8195 return address;
8196}
8197
8198/*
8199 * Add a pre-cooked default expression.
8200 *
8201 * Return the address of the affected column.
8202 */
8203static ObjectAddress
8205 Node *newDefault)
8206{
8207 ObjectAddress address;
8208
8209 /* We assume no checking is required */
8210
8211 /*
8212 * Remove any old default for the column. We use RESTRICT here for
8213 * safety, but at present we do not expect anything to depend on the
8214 * default. (In ordinary cases, there could not be a default in place
8215 * anyway, but it's possible when combining LIKE with inheritance.)
8216 */
8218 true);
8219
8220 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8221
8222 ObjectAddressSubSet(address, RelationRelationId,
8223 RelationGetRelid(rel), attnum);
8224 return address;
8225}
8226
8227/*
8228 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8229 *
8230 * Return the address of the affected column.
8231 */
8232static ObjectAddress
8233ATExecAddIdentity(Relation rel, const char *colName,
8234 Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8235{
8236 Relation attrelation;
8237 HeapTuple tuple;
8238 Form_pg_attribute attTup;
8240 ObjectAddress address;
8241 ColumnDef *cdef = castNode(ColumnDef, def);
8242 bool ispartitioned;
8243
8244 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8245 if (ispartitioned && !recurse)
8246 ereport(ERROR,
8247 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8248 errmsg("cannot add identity to a column of only the partitioned table"),
8249 errhint("Do not specify the ONLY keyword.")));
8250
8251 if (rel->rd_rel->relispartition && !recursing)
8252 ereport(ERROR,
8253 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8254 errmsg("cannot add identity to a column of a partition"));
8255
8256 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8257
8258 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8259 if (!HeapTupleIsValid(tuple))
8260 ereport(ERROR,
8261 (errcode(ERRCODE_UNDEFINED_COLUMN),
8262 errmsg("column \"%s\" of relation \"%s\" does not exist",
8263 colName, RelationGetRelationName(rel))));
8264 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8265 attnum = attTup->attnum;
8266
8267 /* Can't alter a system attribute */
8268 if (attnum <= 0)
8269 ereport(ERROR,
8270 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8271 errmsg("cannot alter system column \"%s\"",
8272 colName)));
8273
8274 /*
8275 * Creating a column as identity implies NOT NULL, so adding the identity
8276 * to an existing column that is not NOT NULL would create a state that
8277 * cannot be reproduced without contortions.
8278 */
8279 if (!attTup->attnotnull)
8280 ereport(ERROR,
8281 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8282 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8283 colName, RelationGetRelationName(rel))));
8284
8285 if (attTup->attidentity)
8286 ereport(ERROR,
8287 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8288 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8289 colName, RelationGetRelationName(rel))));
8290
8291 if (attTup->atthasdef)
8292 ereport(ERROR,
8293 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8294 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8295 colName, RelationGetRelationName(rel))));
8296
8297 attTup->attidentity = cdef->identity;
8298 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8299
8300 InvokeObjectPostAlterHook(RelationRelationId,
8301 RelationGetRelid(rel),
8302 attTup->attnum);
8303 ObjectAddressSubSet(address, RelationRelationId,
8304 RelationGetRelid(rel), attnum);
8305 heap_freetuple(tuple);
8306
8307 table_close(attrelation, RowExclusiveLock);
8308
8309 /*
8310 * Recurse to propagate the identity column to partitions. Identity is
8311 * not inherited in regular inheritance children.
8312 */
8313 if (recurse && ispartitioned)
8314 {
8315 List *children;
8316 ListCell *lc;
8317
8318 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8319
8320 foreach(lc, children)
8321 {
8322 Relation childrel;
8323
8324 childrel = table_open(lfirst_oid(lc), NoLock);
8325 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8326 table_close(childrel, NoLock);
8327 }
8328 }
8329
8330 return address;
8331}
8332
8333/*
8334 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8335 *
8336 * Return the address of the affected column.
8337 */
8338static ObjectAddress
8339ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8340 LOCKMODE lockmode, bool recurse, bool recursing)
8341{
8343 DefElem *generatedEl = NULL;
8344 HeapTuple tuple;
8345 Form_pg_attribute attTup;
8347 Relation attrelation;
8348 ObjectAddress address;
8349 bool ispartitioned;
8350
8351 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8352 if (ispartitioned && !recurse)
8353 ereport(ERROR,
8354 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8355 errmsg("cannot change identity column of only the partitioned table"),
8356 errhint("Do not specify the ONLY keyword.")));
8357
8358 if (rel->rd_rel->relispartition && !recursing)
8359 ereport(ERROR,
8360 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8361 errmsg("cannot change identity column of a partition"));
8362
8363 foreach(option, castNode(List, def))
8364 {
8365 DefElem *defel = lfirst_node(DefElem, option);
8366
8367 if (strcmp(defel->defname, "generated") == 0)
8368 {
8369 if (generatedEl)
8370 ereport(ERROR,
8371 (errcode(ERRCODE_SYNTAX_ERROR),
8372 errmsg("conflicting or redundant options")));
8373 generatedEl = defel;
8374 }
8375 else
8376 elog(ERROR, "option \"%s\" not recognized",
8377 defel->defname);
8378 }
8379
8380 /*
8381 * Even if there is nothing to change here, we run all the checks. There
8382 * will be a subsequent ALTER SEQUENCE that relies on everything being
8383 * there.
8384 */
8385
8386 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8387 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8388 if (!HeapTupleIsValid(tuple))
8389 ereport(ERROR,
8390 (errcode(ERRCODE_UNDEFINED_COLUMN),
8391 errmsg("column \"%s\" of relation \"%s\" does not exist",
8392 colName, RelationGetRelationName(rel))));
8393
8394 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8395 attnum = attTup->attnum;
8396
8397 if (attnum <= 0)
8398 ereport(ERROR,
8399 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8400 errmsg("cannot alter system column \"%s\"",
8401 colName)));
8402
8403 if (!attTup->attidentity)
8404 ereport(ERROR,
8405 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8406 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8407 colName, RelationGetRelationName(rel))));
8408
8409 if (generatedEl)
8410 {
8411 attTup->attidentity = defGetInt32(generatedEl);
8412 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8413
8414 InvokeObjectPostAlterHook(RelationRelationId,
8415 RelationGetRelid(rel),
8416 attTup->attnum);
8417 ObjectAddressSubSet(address, RelationRelationId,
8418 RelationGetRelid(rel), attnum);
8419 }
8420 else
8421 address = InvalidObjectAddress;
8422
8423 heap_freetuple(tuple);
8424 table_close(attrelation, RowExclusiveLock);
8425
8426 /*
8427 * Recurse to propagate the identity change to partitions. Identity is not
8428 * inherited in regular inheritance children.
8429 */
8430 if (generatedEl && recurse && ispartitioned)
8431 {
8432 List *children;
8433 ListCell *lc;
8434
8435 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8436
8437 foreach(lc, children)
8438 {
8439 Relation childrel;
8440
8441 childrel = table_open(lfirst_oid(lc), NoLock);
8442 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8443 table_close(childrel, NoLock);
8444 }
8445 }
8446
8447 return address;
8448}
8449
8450/*
8451 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8452 *
8453 * Return the address of the affected column.
8454 */
8455static ObjectAddress
8456ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8457 bool recurse, bool recursing)
8458{
8459 HeapTuple tuple;
8460 Form_pg_attribute attTup;
8462 Relation attrelation;
8463 ObjectAddress address;
8464 Oid seqid;
8465 ObjectAddress seqaddress;
8466 bool ispartitioned;
8467
8468 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8469 if (ispartitioned && !recurse)
8470 ereport(ERROR,
8471 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8472 errmsg("cannot drop identity from a column of only the partitioned table"),
8473 errhint("Do not specify the ONLY keyword.")));
8474
8475 if (rel->rd_rel->relispartition && !recursing)
8476 ereport(ERROR,
8477 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8478 errmsg("cannot drop identity from a column of a partition"));
8479
8480 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8481 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8482 if (!HeapTupleIsValid(tuple))
8483 ereport(ERROR,
8484 (errcode(ERRCODE_UNDEFINED_COLUMN),
8485 errmsg("column \"%s\" of relation \"%s\" does not exist",
8486 colName, RelationGetRelationName(rel))));
8487
8488 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8489 attnum = attTup->attnum;
8490
8491 if (attnum <= 0)
8492 ereport(ERROR,
8493 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8494 errmsg("cannot alter system column \"%s\"",
8495 colName)));
8496
8497 if (!attTup->attidentity)
8498 {
8499 if (!missing_ok)
8500 ereport(ERROR,
8501 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8502 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8503 colName, RelationGetRelationName(rel))));
8504 else
8505 {
8507 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8508 colName, RelationGetRelationName(rel))));
8509 heap_freetuple(tuple);
8510 table_close(attrelation, RowExclusiveLock);
8511 return InvalidObjectAddress;
8512 }
8513 }
8514
8515 attTup->attidentity = '\0';
8516 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8517
8518 InvokeObjectPostAlterHook(RelationRelationId,
8519 RelationGetRelid(rel),
8520 attTup->attnum);
8521 ObjectAddressSubSet(address, RelationRelationId,
8522 RelationGetRelid(rel), attnum);
8523 heap_freetuple(tuple);
8524
8525 table_close(attrelation, RowExclusiveLock);
8526
8527 /*
8528 * Recurse to drop the identity from column in partitions. Identity is
8529 * not inherited in regular inheritance children so ignore them.
8530 */
8531 if (recurse && ispartitioned)
8532 {
8533 List *children;
8534 ListCell *lc;
8535
8536 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8537
8538 foreach(lc, children)
8539 {
8540 Relation childrel;
8541
8542 childrel = table_open(lfirst_oid(lc), NoLock);
8543 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8544 table_close(childrel, NoLock);
8545 }
8546 }
8547
8548 if (!recursing)
8549 {
8550 /* drop the internal sequence */
8551 seqid = getIdentitySequence(rel, attnum, false);
8552 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8553 RelationRelationId, DEPENDENCY_INTERNAL);
8555 seqaddress.classId = RelationRelationId;
8556 seqaddress.objectId = seqid;
8557 seqaddress.objectSubId = 0;
8559 }
8560
8561 return address;
8562}
8563
8564/*
8565 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8566 *
8567 * Return the address of the affected column.
8568 */
8569static ObjectAddress
8570ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8571 Node *newExpr, LOCKMODE lockmode)
8572{
8573 HeapTuple tuple;
8574 Form_pg_attribute attTup;
8576 char attgenerated;
8577 bool rewrite;
8578 Oid attrdefoid;
8579 ObjectAddress address;
8580 Expr *defval;
8582 RawColumnDefault *rawEnt;
8583
8584 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8585 if (!HeapTupleIsValid(tuple))
8586 ereport(ERROR,
8587 (errcode(ERRCODE_UNDEFINED_COLUMN),
8588 errmsg("column \"%s\" of relation \"%s\" does not exist",
8589 colName, RelationGetRelationName(rel))));
8590
8591 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8592
8593 attnum = attTup->attnum;
8594 if (attnum <= 0)
8595 ereport(ERROR,
8596 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8597 errmsg("cannot alter system column \"%s\"",
8598 colName)));
8599
8600 attgenerated = attTup->attgenerated;
8601 if (!attgenerated)
8602 ereport(ERROR,
8603 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8604 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8605 colName, RelationGetRelationName(rel))));
8606
8607 /*
8608 * TODO: This could be done, just need to recheck any constraints
8609 * afterwards.
8610 */
8611 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8612 rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8613 ereport(ERROR,
8614 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8615 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
8616 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8617 colName, RelationGetRelationName(rel))));
8618
8619 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8620 tab->verify_new_notnull = true;
8621
8622 /*
8623 * We need to prevent this because a change of expression could affect a
8624 * row filter and inject expressions that are not permitted in a row
8625 * filter. XXX We could try to have a more precise check to catch only
8626 * publications with row filters, or even re-verify the row filter
8627 * expressions.
8628 */
8629 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8631 ereport(ERROR,
8632 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8633 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8634 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8635 colName, RelationGetRelationName(rel))));
8636
8637 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8638
8639 ReleaseSysCache(tuple);
8640
8641 if (rewrite)
8642 {
8643 /*
8644 * Clear all the missing values if we're rewriting the table, since
8645 * this renders them pointless.
8646 */
8648
8649 /* make sure we don't conflict with later attribute modifications */
8651
8652 /*
8653 * Find everything that depends on the column (constraints, indexes,
8654 * etc), and record enough information to let us recreate the objects
8655 * after rewrite.
8656 */
8658 }
8659
8660 /*
8661 * Drop the dependency records of the GENERATED expression, in particular
8662 * its INTERNAL dependency on the column, which would otherwise cause
8663 * dependency.c to refuse to perform the deletion.
8664 */
8665 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8666 if (!OidIsValid(attrdefoid))
8667 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8668 RelationGetRelid(rel), attnum);
8669 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8670
8671 /* Make above changes visible */
8673
8674 /*
8675 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8676 * safety, but at present we do not expect anything to depend on the
8677 * expression.
8678 */
8680 false, false);
8681
8682 /* Prepare to store the new expression, in the catalogs */
8683 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8684 rawEnt->attnum = attnum;
8685 rawEnt->raw_default = newExpr;
8686 rawEnt->generated = attgenerated;
8687
8688 /* Store the generated expression */
8690 false, true, false, NULL);
8691
8692 /* Make above new expression visible */
8694
8695 if (rewrite)
8696 {
8697 /* Prepare for table rewrite */
8698 defval = (Expr *) build_column_default(rel, attnum);
8699
8701 newval->attnum = attnum;
8702 newval->expr = expression_planner(defval);
8703 newval->is_generated = true;
8704
8705 tab->newvals = lappend(tab->newvals, newval);
8707 }
8708
8709 /* Drop any pg_statistic entry for the column */
8711
8712 InvokeObjectPostAlterHook(RelationRelationId,
8713 RelationGetRelid(rel), attnum);
8714
8715 ObjectAddressSubSet(address, RelationRelationId,
8716 RelationGetRelid(rel), attnum);
8717 return address;
8718}
8719
8720/*
8721 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8722 */
8723static void
8724ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8725{
8726 /*
8727 * Reject ONLY if there are child tables. We could implement this, but it
8728 * is a bit complicated. GENERATED clauses must be attached to the column
8729 * definition and cannot be added later like DEFAULT, so if a child table
8730 * has a generation expression that the parent does not have, the child
8731 * column will necessarily be an attislocal column. So to implement ONLY
8732 * here, we'd need extra code to update attislocal of the direct child
8733 * tables, somewhat similar to how DROP COLUMN does it, so that the
8734 * resulting state can be properly dumped and restored.
8735 */
8736 if (!recurse &&
8738 ereport(ERROR,
8739 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8740 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8741
8742 /*
8743 * Cannot drop generation expression from inherited columns.
8744 */
8745 if (!recursing)
8746 {
8747 HeapTuple tuple;
8748 Form_pg_attribute attTup;
8749
8751 if (!HeapTupleIsValid(tuple))
8752 ereport(ERROR,
8753 (errcode(ERRCODE_UNDEFINED_COLUMN),
8754 errmsg("column \"%s\" of relation \"%s\" does not exist",
8755 cmd->name, RelationGetRelationName(rel))));
8756
8757 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8758
8759 if (attTup->attinhcount > 0)
8760 ereport(ERROR,
8761 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8762 errmsg("cannot drop generation expression from inherited column")));
8763 }
8764}
8765
8766/*
8767 * Return the address of the affected column.
8768 */
8769static ObjectAddress
8770ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8771{
8772 HeapTuple tuple;
8773 Form_pg_attribute attTup;
8775 Relation attrelation;
8776 Oid attrdefoid;
8777 ObjectAddress address;
8778
8779 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8780 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8781 if (!HeapTupleIsValid(tuple))
8782 ereport(ERROR,
8783 (errcode(ERRCODE_UNDEFINED_COLUMN),
8784 errmsg("column \"%s\" of relation \"%s\" does not exist",
8785 colName, RelationGetRelationName(rel))));
8786
8787 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8788 attnum = attTup->attnum;
8789
8790 if (attnum <= 0)
8791 ereport(ERROR,
8792 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8793 errmsg("cannot alter system column \"%s\"",
8794 colName)));
8795
8796 /*
8797 * TODO: This could be done, but it would need a table rewrite to
8798 * materialize the generated values. Note that for the time being, we
8799 * still error with missing_ok, so that we don't silently leave the column
8800 * as generated.
8801 */
8802 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8803 ereport(ERROR,
8804 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8805 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8806 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8807 colName, RelationGetRelationName(rel))));
8808
8809 if (!attTup->attgenerated)
8810 {
8811 if (!missing_ok)
8812 ereport(ERROR,
8813 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8814 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8815 colName, RelationGetRelationName(rel))));
8816 else
8817 {
8819 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8820 colName, RelationGetRelationName(rel))));
8821 heap_freetuple(tuple);
8822 table_close(attrelation, RowExclusiveLock);
8823 return InvalidObjectAddress;
8824 }
8825 }
8826
8827 /*
8828 * Mark the column as no longer generated. (The atthasdef flag needs to
8829 * get cleared too, but RemoveAttrDefault will handle that.)
8830 */
8831 attTup->attgenerated = '\0';
8832 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8833
8834 InvokeObjectPostAlterHook(RelationRelationId,
8835 RelationGetRelid(rel),
8836 attnum);
8837 heap_freetuple(tuple);
8838
8839 table_close(attrelation, RowExclusiveLock);
8840
8841 /*
8842 * Drop the dependency records of the GENERATED expression, in particular
8843 * its INTERNAL dependency on the column, which would otherwise cause
8844 * dependency.c to refuse to perform the deletion.
8845 */
8846 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8847 if (!OidIsValid(attrdefoid))
8848 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8849 RelationGetRelid(rel), attnum);
8850 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8851
8852 /* Make above changes visible */
8854
8855 /*
8856 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8857 * safety, but at present we do not expect anything to depend on the
8858 * default.
8859 */
8861 false, false);
8862
8863 ObjectAddressSubSet(address, RelationRelationId,
8864 RelationGetRelid(rel), attnum);
8865 return address;
8866}
8867
8868/*
8869 * ALTER TABLE ALTER COLUMN SET STATISTICS
8870 *
8871 * Return value is the address of the modified column
8872 */
8873static ObjectAddress
8874ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8875{
8876 int newtarget = 0;
8877 bool newtarget_default;
8878 Relation attrelation;
8879 HeapTuple tuple,
8880 newtuple;
8881 Form_pg_attribute attrtuple;
8883 ObjectAddress address;
8884 Datum repl_val[Natts_pg_attribute];
8885 bool repl_null[Natts_pg_attribute];
8886 bool repl_repl[Natts_pg_attribute];
8887
8888 /*
8889 * We allow referencing columns by numbers only for indexes, since table
8890 * column numbers could contain gaps if columns are later dropped.
8891 */
8892 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8893 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8894 !colName)
8895 ereport(ERROR,
8896 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8897 errmsg("cannot refer to non-index column by number")));
8898
8899 /* -1 was used in previous versions for the default setting */
8900 if (newValue && intVal(newValue) != -1)
8901 {
8902 newtarget = intVal(newValue);
8903 newtarget_default = false;
8904 }
8905 else
8906 newtarget_default = true;
8907
8908 if (!newtarget_default)
8909 {
8910 /*
8911 * Limit target to a sane range
8912 */
8913 if (newtarget < 0)
8914 {
8915 ereport(ERROR,
8916 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8917 errmsg("statistics target %d is too low",
8918 newtarget)));
8919 }
8920 else if (newtarget > MAX_STATISTICS_TARGET)
8921 {
8922 newtarget = MAX_STATISTICS_TARGET;
8924 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8925 errmsg("lowering statistics target to %d",
8926 newtarget)));
8927 }
8928 }
8929
8930 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8931
8932 if (colName)
8933 {
8934 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8935
8936 if (!HeapTupleIsValid(tuple))
8937 ereport(ERROR,
8938 (errcode(ERRCODE_UNDEFINED_COLUMN),
8939 errmsg("column \"%s\" of relation \"%s\" does not exist",
8940 colName, RelationGetRelationName(rel))));
8941 }
8942 else
8943 {
8944 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8945
8946 if (!HeapTupleIsValid(tuple))
8947 ereport(ERROR,
8948 (errcode(ERRCODE_UNDEFINED_COLUMN),
8949 errmsg("column number %d of relation \"%s\" does not exist",
8950 colNum, RelationGetRelationName(rel))));
8951 }
8952
8953 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8954
8955 attnum = attrtuple->attnum;
8956 if (attnum <= 0)
8957 ereport(ERROR,
8958 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8959 errmsg("cannot alter system column \"%s\"",
8960 colName)));
8961
8962 /*
8963 * Prevent this as long as the ANALYZE code skips virtual generated
8964 * columns.
8965 */
8966 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8967 ereport(ERROR,
8968 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8969 errmsg("cannot alter statistics on virtual generated column \"%s\"",
8970 colName)));
8971
8972 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8973 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8974 {
8975 if (attnum > rel->rd_index->indnkeyatts)
8976 ereport(ERROR,
8977 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8978 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8979 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8980 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8981 ereport(ERROR,
8982 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8983 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8984 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8985 errhint("Alter statistics on table column instead.")));
8986 }
8987
8988 /* Build new tuple. */
8989 memset(repl_null, false, sizeof(repl_null));
8990 memset(repl_repl, false, sizeof(repl_repl));
8991 if (!newtarget_default)
8992 repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
8993 else
8994 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8995 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8996 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8997 repl_val, repl_null, repl_repl);
8998 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8999
9000 InvokeObjectPostAlterHook(RelationRelationId,
9001 RelationGetRelid(rel),
9002 attrtuple->attnum);
9003 ObjectAddressSubSet(address, RelationRelationId,
9004 RelationGetRelid(rel), attnum);
9005
9006 heap_freetuple(newtuple);
9007
9008 ReleaseSysCache(tuple);
9009
9010 table_close(attrelation, RowExclusiveLock);
9011
9012 return address;
9013}
9014
9015/*
9016 * Return value is the address of the modified column
9017 */
9018static ObjectAddress
9019ATExecSetOptions(Relation rel, const char *colName, Node *options,
9020 bool isReset, LOCKMODE lockmode)
9021{
9022 Relation attrelation;
9023 HeapTuple tuple,
9024 newtuple;
9025 Form_pg_attribute attrtuple;
9027 Datum datum,
9028 newOptions;
9029 bool isnull;
9030 ObjectAddress address;
9031 Datum repl_val[Natts_pg_attribute];
9032 bool repl_null[Natts_pg_attribute];
9033 bool repl_repl[Natts_pg_attribute];
9034
9035 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9036
9037 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9038
9039 if (!HeapTupleIsValid(tuple))
9040 ereport(ERROR,
9041 (errcode(ERRCODE_UNDEFINED_COLUMN),
9042 errmsg("column \"%s\" of relation \"%s\" does not exist",
9043 colName, RelationGetRelationName(rel))));
9044 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9045
9046 attnum = attrtuple->attnum;
9047 if (attnum <= 0)
9048 ereport(ERROR,
9049 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9050 errmsg("cannot alter system column \"%s\"",
9051 colName)));
9052
9053 /* Generate new proposed attoptions (text array) */
9054 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9055 &isnull);
9056 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9057 castNode(List, options), NULL, NULL,
9058 false, isReset);
9059 /* Validate new options */
9060 (void) attribute_reloptions(newOptions, true);
9061
9062 /* Build new tuple. */
9063 memset(repl_null, false, sizeof(repl_null));
9064 memset(repl_repl, false, sizeof(repl_repl));
9065 if (newOptions != (Datum) 0)
9066 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9067 else
9068 repl_null[Anum_pg_attribute_attoptions - 1] = true;
9069 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9070 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9071 repl_val, repl_null, repl_repl);
9072
9073 /* Update system catalog. */
9074 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9075
9076 InvokeObjectPostAlterHook(RelationRelationId,
9077 RelationGetRelid(rel),
9078 attrtuple->attnum);
9079 ObjectAddressSubSet(address, RelationRelationId,
9080 RelationGetRelid(rel), attnum);
9081
9082 heap_freetuple(newtuple);
9083
9084 ReleaseSysCache(tuple);
9085
9086 table_close(attrelation, RowExclusiveLock);
9087
9088 return address;
9089}
9090
9091/*
9092 * Helper function for ATExecSetStorage and ATExecSetCompression
9093 *
9094 * Set the attstorage and/or attcompression fields for index columns
9095 * associated with the specified table column.
9096 */
9097static void
9100 bool setstorage, char newstorage,
9101 bool setcompression, char newcompression,
9102 LOCKMODE lockmode)
9103{
9104 ListCell *lc;
9105
9106 foreach(lc, RelationGetIndexList(rel))
9107 {
9108 Oid indexoid = lfirst_oid(lc);
9109 Relation indrel;
9110 AttrNumber indattnum = 0;
9111 HeapTuple tuple;
9112
9113 indrel = index_open(indexoid, lockmode);
9114
9115 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9116 {
9117 if (indrel->rd_index->indkey.values[i] == attnum)
9118 {
9119 indattnum = i + 1;
9120 break;
9121 }
9122 }
9123
9124 if (indattnum == 0)
9125 {
9126 index_close(indrel, lockmode);
9127 continue;
9128 }
9129
9130 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9131
9132 if (HeapTupleIsValid(tuple))
9133 {
9134 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9135
9136 if (setstorage)
9137 attrtuple->attstorage = newstorage;
9138
9139 if (setcompression)
9140 attrtuple->attcompression = newcompression;
9141
9142 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9143
9144 InvokeObjectPostAlterHook(RelationRelationId,
9145 RelationGetRelid(rel),
9146 attrtuple->attnum);
9147
9148 heap_freetuple(tuple);
9149 }
9150
9151 index_close(indrel, lockmode);
9152 }
9153}
9154
9155/*
9156 * ALTER TABLE ALTER COLUMN SET STORAGE
9157 *
9158 * Return value is the address of the modified column
9159 */
9160static ObjectAddress
9161ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9162{
9163 Relation attrelation;
9164 HeapTuple tuple;
9165 Form_pg_attribute attrtuple;
9167 ObjectAddress address;
9168
9169 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9170
9171 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9172
9173 if (!HeapTupleIsValid(tuple))
9174 ereport(ERROR,
9175 (errcode(ERRCODE_UNDEFINED_COLUMN),
9176 errmsg("column \"%s\" of relation \"%s\" does not exist",
9177 colName, RelationGetRelationName(rel))));
9178 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9179
9180 attnum = attrtuple->attnum;
9181 if (attnum <= 0)
9182 ereport(ERROR,
9183 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9184 errmsg("cannot alter system column \"%s\"",
9185 colName)));
9186
9187 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9188
9189 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9190
9191 InvokeObjectPostAlterHook(RelationRelationId,
9192 RelationGetRelid(rel),
9193 attrtuple->attnum);
9194
9195 /*
9196 * Apply the change to indexes as well (only for simple index columns,
9197 * matching behavior of index.c ConstructTupleDescriptor()).
9198 */
9199 SetIndexStorageProperties(rel, attrelation, attnum,
9200 true, attrtuple->attstorage,
9201 false, 0,
9202 lockmode);
9203
9204 heap_freetuple(tuple);
9205
9206 table_close(attrelation, RowExclusiveLock);
9207
9208 ObjectAddressSubSet(address, RelationRelationId,
9209 RelationGetRelid(rel), attnum);
9210 return address;
9211}
9212
9213
9214/*
9215 * ALTER TABLE DROP COLUMN
9216 *
9217 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9218 * because we have to decide at runtime whether to recurse or not depending
9219 * on whether attinhcount goes to zero or not. (We can't check this in a
9220 * static pre-pass because it won't handle multiple inheritance situations
9221 * correctly.)
9222 */
9223static void
9224ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9225 AlterTableCmd *cmd, LOCKMODE lockmode,
9226 AlterTableUtilityContext *context)
9227{
9228 if (rel->rd_rel->reloftype && !recursing)
9229 ereport(ERROR,
9230 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9231 errmsg("cannot drop column from typed table")));
9232
9233 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9234 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9235
9236 if (recurse)
9237 cmd->recurse = true;
9238}
9239
9240/*
9241 * Drops column 'colName' from relation 'rel' and returns the address of the
9242 * dropped column. The column is also dropped (or marked as no longer
9243 * inherited from relation) from the relation's inheritance children, if any.
9244 *
9245 * In the recursive invocations for inheritance child relations, instead of
9246 * dropping the column directly (if to be dropped at all), its object address
9247 * is added to 'addrs', which must be non-NULL in such invocations. All
9248 * columns are dropped at the same time after all the children have been
9249 * checked recursively.
9250 */
9251static ObjectAddress
9252ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9253 DropBehavior behavior,
9254 bool recurse, bool recursing,
9255 bool missing_ok, LOCKMODE lockmode,
9256 ObjectAddresses *addrs)
9257{
9258 HeapTuple tuple;
9259 Form_pg_attribute targetatt;
9261 List *children;
9262 ObjectAddress object;
9263 bool is_expr;
9264
9265 /* At top level, permission check was done in ATPrepCmd, else do it */
9266 if (recursing)
9269
9270 /* Initialize addrs on the first invocation */
9271 Assert(!recursing || addrs != NULL);
9272
9273 /* since this function recurses, it could be driven to stack overflow */
9275
9276 if (!recursing)
9277 addrs = new_object_addresses();
9278
9279 /*
9280 * get the number of the attribute
9281 */
9282 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9283 if (!HeapTupleIsValid(tuple))
9284 {
9285 if (!missing_ok)
9286 {
9287 ereport(ERROR,
9288 (errcode(ERRCODE_UNDEFINED_COLUMN),
9289 errmsg("column \"%s\" of relation \"%s\" does not exist",
9290 colName, RelationGetRelationName(rel))));
9291 }
9292 else
9293 {
9295 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9296 colName, RelationGetRelationName(rel))));
9297 return InvalidObjectAddress;
9298 }
9299 }
9300 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9301
9302 attnum = targetatt->attnum;
9303
9304 /* Can't drop a system attribute */
9305 if (attnum <= 0)
9306 ereport(ERROR,
9307 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9308 errmsg("cannot drop system column \"%s\"",
9309 colName)));
9310
9311 /*
9312 * Don't drop inherited columns, unless recursing (presumably from a drop
9313 * of the parent column)
9314 */
9315 if (targetatt->attinhcount > 0 && !recursing)
9316 ereport(ERROR,
9317 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9318 errmsg("cannot drop inherited column \"%s\"",
9319 colName)));
9320
9321 /*
9322 * Don't drop columns used in the partition key, either. (If we let this
9323 * go through, the key column's dependencies would cause a cascaded drop
9324 * of the whole table, which is surely not what the user expected.)
9325 */
9326 if (has_partition_attrs(rel,
9328 &is_expr))
9329 ereport(ERROR,
9330 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9331 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9332 colName, RelationGetRelationName(rel))));
9333
9334 ReleaseSysCache(tuple);
9335
9336 /*
9337 * Propagate to children as appropriate. Unlike most other ALTER
9338 * routines, we have to do this one level of recursion at a time; we can't
9339 * use find_all_inheritors to do it in one pass.
9340 */
9341 children =
9343
9344 if (children)
9345 {
9346 Relation attr_rel;
9347 ListCell *child;
9348
9349 /*
9350 * In case of a partitioned table, the column must be dropped from the
9351 * partitions as well.
9352 */
9353 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9354 ereport(ERROR,
9355 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9356 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9357 errhint("Do not specify the ONLY keyword.")));
9358
9359 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9360 foreach(child, children)
9361 {
9362 Oid childrelid = lfirst_oid(child);
9363 Relation childrel;
9364 Form_pg_attribute childatt;
9365
9366 /* find_inheritance_children already got lock */
9367 childrel = table_open(childrelid, NoLock);
9368 CheckAlterTableIsSafe(childrel);
9369
9370 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9371 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9372 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9373 colName, childrelid);
9374 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9375
9376 if (childatt->attinhcount <= 0) /* shouldn't happen */
9377 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9378 childrelid, colName);
9379
9380 if (recurse)
9381 {
9382 /*
9383 * If the child column has other definition sources, just
9384 * decrement its inheritance count; if not, recurse to delete
9385 * it.
9386 */
9387 if (childatt->attinhcount == 1 && !childatt->attislocal)
9388 {
9389 /* Time to delete this child column, too */
9390 ATExecDropColumn(wqueue, childrel, colName,
9391 behavior, true, true,
9392 false, lockmode, addrs);
9393 }
9394 else
9395 {
9396 /* Child column must survive my deletion */
9397 childatt->attinhcount--;
9398
9399 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9400
9401 /* Make update visible */
9403 }
9404 }
9405 else
9406 {
9407 /*
9408 * If we were told to drop ONLY in this table (no recursion),
9409 * we need to mark the inheritors' attributes as locally
9410 * defined rather than inherited.
9411 */
9412 childatt->attinhcount--;
9413 childatt->attislocal = true;
9414
9415 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9416
9417 /* Make update visible */
9419 }
9420
9421 heap_freetuple(tuple);
9422
9423 table_close(childrel, NoLock);
9424 }
9425 table_close(attr_rel, RowExclusiveLock);
9426 }
9427
9428 /* Add object to delete */
9429 object.classId = RelationRelationId;
9430 object.objectId = RelationGetRelid(rel);
9431 object.objectSubId = attnum;
9432 add_exact_object_address(&object, addrs);
9433
9434 if (!recursing)
9435 {
9436 /* Recursion has ended, drop everything that was collected */
9437 performMultipleDeletions(addrs, behavior, 0);
9438 free_object_addresses(addrs);
9439 }
9440
9441 return object;
9442}
9443
9444/*
9445 * Prepare to add a primary key on a table, by adding not-null constraints
9446 * on all columns.
9447 *
9448 * The not-null constraints for a primary key must cover the whole inheritance
9449 * hierarchy (failing to ensure that leads to funny corner cases). For the
9450 * normal case where we're asked to recurse, this routine checks if the
9451 * not-null constraints exist already, and if not queues a requirement for
9452 * them to be created by phase 2.
9453 *
9454 * For the case where we're asked not to recurse, we verify that a not-null
9455 * constraint exists on each column of each (direct) child table, throwing an
9456 * error if not. Not throwing an error would also work, because a not-null
9457 * constraint would be created anyway, but it'd cause a silent scan of the
9458 * child table to verify absence of nulls. We prefer to let the user know so
9459 * that they can add the constraint manually without having to hold
9460 * AccessExclusiveLock while at it.
9461 *
9462 * However, it's also important that we do not acquire locks on children if
9463 * the not-null constraints already exist on the parent, to avoid risking
9464 * deadlocks during parallel pg_restore of PKs on partitioned tables.
9465 */
9466static void
9468 bool recurse, LOCKMODE lockmode,
9469 AlterTableUtilityContext *context)
9470{
9471 Constraint *pkconstr;
9472 List *children = NIL;
9473 bool got_children = false;
9474
9475 pkconstr = castNode(Constraint, cmd->def);
9476 if (pkconstr->contype != CONSTR_PRIMARY)
9477 return;
9478
9479 /* Verify that columns are not-null, or request that they be made so */
9480 foreach_node(String, column, pkconstr->keys)
9481 {
9482 AlterTableCmd *newcmd;
9483 Constraint *nnconstr;
9484 HeapTuple tuple;
9485
9486 /*
9487 * First check if a suitable constraint exists. If it does, we don't
9488 * need to request another one. We do need to bail out if it's not
9489 * valid, though.
9490 */
9491 tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9492 if (tuple != NULL)
9493 {
9494 verifyNotNullPKCompatible(tuple, strVal(column));
9495
9496 /* All good with this one; don't request another */
9497 heap_freetuple(tuple);
9498 continue;
9499 }
9500 else if (!recurse)
9501 {
9502 /*
9503 * No constraint on this column. Asked not to recurse, we won't
9504 * create one here, but verify that all children have one.
9505 */
9506 if (!got_children)
9507 {
9509 lockmode);
9510 /* only search for children on the first time through */
9511 got_children = true;
9512 }
9513
9514 foreach_oid(childrelid, children)
9515 {
9516 HeapTuple tup;
9517
9518 tup = findNotNullConstraint(childrelid, strVal(column));
9519 if (!tup)
9520 ereport(ERROR,
9521 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9522 strVal(column), get_rel_name(childrelid)));
9523 /* verify it's good enough */
9524 verifyNotNullPKCompatible(tup, strVal(column));
9525 }
9526 }
9527
9528 /* This column is not already not-null, so add it to the queue */
9529 nnconstr = makeNotNullConstraint(column);
9530
9531 newcmd = makeNode(AlterTableCmd);
9532 newcmd->subtype = AT_AddConstraint;
9533 /* note we force recurse=true here; see above */
9534 newcmd->recurse = true;
9535 newcmd->def = (Node *) nnconstr;
9536
9537 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9538 }
9539}
9540
9541/*
9542 * Verify whether the given not-null constraint is compatible with a
9543 * primary key. If not, an error is thrown.
9544 */
9545static void
9546verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9547{
9549
9550 if (conForm->contype != CONSTRAINT_NOTNULL)
9551 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9552
9553 /* a NO INHERIT constraint is no good */
9554 if (conForm->connoinherit)
9555 ereport(ERROR,
9556 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9557 errmsg("cannot create primary key on column \"%s\"", colname),
9558 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9559 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9560 NameStr(conForm->conname), colname,
9561 get_rel_name(conForm->conrelid), "NO INHERIT"),
9562 errhint("You might need to make the existing constraint inheritable using %s.",
9563 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9564
9565 /* an unvalidated constraint is no good */
9566 if (!conForm->convalidated)
9567 ereport(ERROR,
9568 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9569 errmsg("cannot create primary key on column \"%s\"", colname),
9570 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9571 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9572 NameStr(conForm->conname), colname,
9573 get_rel_name(conForm->conrelid), "NOT VALID"),
9574 errhint("You might need to validate it using %s.",
9575 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9576}
9577
9578/*
9579 * ALTER TABLE ADD INDEX
9580 *
9581 * There is no such command in the grammar, but parse_utilcmd.c converts
9582 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9583 * us schedule creation of the index at the appropriate time during ALTER.
9584 *
9585 * Return value is the address of the new index.
9586 */
9587static ObjectAddress
9589 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9590{
9591 bool check_rights;
9592 bool skip_build;
9593 bool quiet;
9594 ObjectAddress address;
9595
9597 Assert(!stmt->concurrent);
9598
9599 /* The IndexStmt has already been through transformIndexStmt */
9600 Assert(stmt->transformed);
9601
9602 /* suppress schema rights check when rebuilding existing index */
9603 check_rights = !is_rebuild;
9604 /* skip index build if phase 3 will do it or we're reusing an old one */
9605 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9606 /* suppress notices when rebuilding existing index */
9607 quiet = is_rebuild;
9608
9609 address = DefineIndex(RelationGetRelid(rel),
9610 stmt,
9611 InvalidOid, /* no predefined OID */
9612 InvalidOid, /* no parent index */
9613 InvalidOid, /* no parent constraint */
9614 -1, /* total_parts unknown */
9615 true, /* is_alter_table */
9616 check_rights,
9617 false, /* check_not_in_use - we did it already */
9618 skip_build,
9619 quiet);
9620
9621 /*
9622 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9623 * new index instead of building from scratch. Restore associated fields.
9624 * This may store InvalidSubTransactionId in both fields, in which case
9625 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9626 * this after the CCI that made catalog rows visible to any rebuild. The
9627 * DROP of the old edition of this index will have scheduled the storage
9628 * for deletion at commit, so cancel that pending deletion.
9629 */
9630 if (RelFileNumberIsValid(stmt->oldNumber))
9631 {
9632 Relation irel = index_open(address.objectId, NoLock);
9633
9634 irel->rd_createSubid = stmt->oldCreateSubid;
9635 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9637 index_close(irel, NoLock);
9638 }
9639
9640 return address;
9641}
9642
9643/*
9644 * ALTER TABLE ADD STATISTICS
9645 *
9646 * This is no such command in the grammar, but we use this internally to add
9647 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9648 * column type change.
9649 */
9650static ObjectAddress
9652 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9653{
9654 ObjectAddress address;
9655
9657
9658 /* The CreateStatsStmt has already been through transformStatsStmt */
9659 Assert(stmt->transformed);
9660
9661 address = CreateStatistics(stmt);
9662
9663 return address;
9664}
9665
9666/*
9667 * ALTER TABLE ADD CONSTRAINT USING INDEX
9668 *
9669 * Returns the address of the new constraint.
9670 */
9671static ObjectAddress
9673 IndexStmt *stmt, LOCKMODE lockmode)
9674{
9675 Oid index_oid = stmt->indexOid;
9676 Relation indexRel;
9677 char *indexName;
9678 IndexInfo *indexInfo;
9679 char *constraintName;
9680 char constraintType;
9681 ObjectAddress address;
9682 bits16 flags;
9683
9685 Assert(OidIsValid(index_oid));
9686 Assert(stmt->isconstraint);
9687
9688 /*
9689 * Doing this on partitioned tables is not a simple feature to implement,
9690 * so let's punt for now.
9691 */
9692 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9693 ereport(ERROR,
9694 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9695 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9696
9697 indexRel = index_open(index_oid, AccessShareLock);
9698
9699 indexName = pstrdup(RelationGetRelationName(indexRel));
9700
9701 indexInfo = BuildIndexInfo(indexRel);
9702
9703 /* this should have been checked at parse time */
9704 if (!indexInfo->ii_Unique)
9705 elog(ERROR, "index \"%s\" is not unique", indexName);
9706
9707 /*
9708 * Determine name to assign to constraint. We require a constraint to
9709 * have the same name as the underlying index; therefore, use the index's
9710 * existing name as the default constraint name, and if the user
9711 * explicitly gives some other name for the constraint, rename the index
9712 * to match.
9713 */
9714 constraintName = stmt->idxname;
9715 if (constraintName == NULL)
9716 constraintName = indexName;
9717 else if (strcmp(constraintName, indexName) != 0)
9718 {
9720 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9721 indexName, constraintName)));
9722 RenameRelationInternal(index_oid, constraintName, false, true);
9723 }
9724
9725 /* Extra checks needed if making primary key */
9726 if (stmt->primary)
9727 index_check_primary_key(rel, indexInfo, true, stmt);
9728
9729 /* Note we currently don't support EXCLUSION constraints here */
9730 if (stmt->primary)
9731 constraintType = CONSTRAINT_PRIMARY;
9732 else
9733 constraintType = CONSTRAINT_UNIQUE;
9734
9735 /* Create the catalog entries for the constraint */
9738 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9739 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9741
9742 address = index_constraint_create(rel,
9743 index_oid,
9744 InvalidOid,
9745 indexInfo,
9746 constraintName,
9747 constraintType,
9748 flags,
9750 false); /* is_internal */
9751
9752 index_close(indexRel, NoLock);
9753
9754 return address;
9755}
9756
9757/*
9758 * ALTER TABLE ADD CONSTRAINT
9759 *
9760 * Return value is the address of the new constraint; if no constraint was
9761 * added, InvalidObjectAddress is returned.
9762 */
9763static ObjectAddress
9765 Constraint *newConstraint, bool recurse, bool is_readd,
9766 LOCKMODE lockmode)
9767{
9769
9770 Assert(IsA(newConstraint, Constraint));
9771
9772 /*
9773 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9774 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9775 * parse_utilcmd.c).
9776 */
9777 switch (newConstraint->contype)
9778 {
9779 case CONSTR_CHECK:
9780 case CONSTR_NOTNULL:
9781 address =
9782 ATAddCheckNNConstraint(wqueue, tab, rel,
9783 newConstraint, recurse, false, is_readd,
9784 lockmode);
9785 break;
9786
9787 case CONSTR_FOREIGN:
9788
9789 /*
9790 * Assign or validate constraint name
9791 */
9792 if (newConstraint->conname)
9793 {
9795 RelationGetRelid(rel),
9796 newConstraint->conname))
9797 ereport(ERROR,
9799 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9800 newConstraint->conname,
9802 }
9803 else
9804 newConstraint->conname =
9807 "fkey",
9809 NIL);
9810
9811 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9812 newConstraint,
9813 recurse, false,
9814 lockmode);
9815 break;
9816
9817 default:
9818 elog(ERROR, "unrecognized constraint type: %d",
9819 (int) newConstraint->contype);
9820 }
9821
9822 return address;
9823}
9824
9825/*
9826 * Generate the column-name portion of the constraint name for a new foreign
9827 * key given the list of column names that reference the referenced
9828 * table. This will be passed to ChooseConstraintName along with the parent
9829 * table name and the "fkey" suffix.
9830 *
9831 * We know that less than NAMEDATALEN characters will actually be used, so we
9832 * can truncate the result once we've generated that many.
9833 *
9834 * XXX see also ChooseExtendedStatisticNameAddition and
9835 * ChooseIndexNameAddition.
9836 */
9837static char *
9839{
9840 char buf[NAMEDATALEN * 2];
9841 int buflen = 0;
9842 ListCell *lc;
9843
9844 buf[0] = '\0';
9845 foreach(lc, colnames)
9846 {
9847 const char *name = strVal(lfirst(lc));
9848
9849 if (buflen > 0)
9850 buf[buflen++] = '_'; /* insert _ between names */
9851
9852 /*
9853 * At this point we have buflen <= NAMEDATALEN. name should be less
9854 * than NAMEDATALEN already, but use strlcpy for paranoia.
9855 */
9856 strlcpy(buf + buflen, name, NAMEDATALEN);
9857 buflen += strlen(buf + buflen);
9858 if (buflen >= NAMEDATALEN)
9859 break;
9860 }
9861 return pstrdup(buf);
9862}
9863
9864/*
9865 * Add a check or not-null constraint to a single table and its children.
9866 * Returns the address of the constraint added to the parent relation,
9867 * if one gets added, or InvalidObjectAddress otherwise.
9868 *
9869 * Subroutine for ATExecAddConstraint.
9870 *
9871 * We must recurse to child tables during execution, rather than using
9872 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9873 * constraints *must* be given the same name, else they won't be seen as
9874 * related later. If the user didn't explicitly specify a name, then
9875 * AddRelationNewConstraints would normally assign different names to the
9876 * child constraints. To fix that, we must capture the name assigned at
9877 * the parent table and pass that down.
9878 */
9879static ObjectAddress
9881 Constraint *constr, bool recurse, bool recursing,
9882 bool is_readd, LOCKMODE lockmode)
9883{
9884 List *newcons;
9885 ListCell *lcon;
9886 List *children;
9887 ListCell *child;
9889
9890 /* Guard against stack overflow due to overly deep inheritance tree. */
9892
9893 /* At top level, permission check was done in ATPrepCmd, else do it */
9894 if (recursing)
9897
9898 /*
9899 * Call AddRelationNewConstraints to do the work, making sure it works on
9900 * a copy of the Constraint so transformExpr can't modify the original. It
9901 * returns a list of cooked constraints.
9902 *
9903 * If the constraint ends up getting merged with a pre-existing one, it's
9904 * omitted from the returned list, which is what we want: we do not need
9905 * to do any validation work. That can only happen at child tables,
9906 * though, since we disallow merging at the top level.
9907 */
9908 newcons = AddRelationNewConstraints(rel, NIL,
9909 list_make1(copyObject(constr)),
9910 recursing || is_readd, /* allow_merge */
9911 !recursing, /* is_local */
9912 is_readd, /* is_internal */
9913 NULL); /* queryString not available
9914 * here */
9915
9916 /* we don't expect more than one constraint here */
9917 Assert(list_length(newcons) <= 1);
9918
9919 /* Add each to-be-validated constraint to Phase 3's queue */
9920 foreach(lcon, newcons)
9921 {
9922 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9923
9924 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9925 {
9926 NewConstraint *newcon;
9927
9928 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9929 newcon->name = ccon->name;
9930 newcon->contype = ccon->contype;
9931 newcon->qual = ccon->expr;
9932
9933 tab->constraints = lappend(tab->constraints, newcon);
9934 }
9935
9936 /* Save the actually assigned name if it was defaulted */
9937 if (constr->conname == NULL)
9938 constr->conname = ccon->name;
9939
9940 /*
9941 * If adding a valid not-null constraint, set the pg_attribute flag
9942 * and tell phase 3 to verify existing rows, if needed. For an
9943 * invalid constraint, just set attnotnull, without queueing
9944 * verification.
9945 */
9946 if (constr->contype == CONSTR_NOTNULL)
9947 set_attnotnull(wqueue, rel, ccon->attnum,
9948 !constr->skip_validation,
9949 !constr->skip_validation);
9950
9951 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9952 }
9953
9954 /* At this point we must have a locked-down name to use */
9955 Assert(newcons == NIL || constr->conname != NULL);
9956
9957 /* Advance command counter in case same table is visited multiple times */
9959
9960 /*
9961 * If the constraint got merged with an existing constraint, we're done.
9962 * We mustn't recurse to child tables in this case, because they've
9963 * already got the constraint, and visiting them again would lead to an
9964 * incorrect value for coninhcount.
9965 */
9966 if (newcons == NIL)
9967 return address;
9968
9969 /*
9970 * If adding a NO INHERIT constraint, no need to find our children.
9971 */
9972 if (constr->is_no_inherit)
9973 return address;
9974
9975 /*
9976 * Propagate to children as appropriate. Unlike most other ALTER
9977 * routines, we have to do this one level of recursion at a time; we can't
9978 * use find_all_inheritors to do it in one pass.
9979 */
9980 children =
9982
9983 /*
9984 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9985 * constraint creation only if there are no children currently. Error out
9986 * otherwise.
9987 */
9988 if (!recurse && children != NIL)
9989 ereport(ERROR,
9990 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9991 errmsg("constraint must be added to child tables too")));
9992
9993 /*
9994 * Recurse to create the constraint on each child.
9995 */
9996 foreach(child, children)
9997 {
9998 Oid childrelid = lfirst_oid(child);
9999 Relation childrel;
10000 AlteredTableInfo *childtab;
10001
10002 /* find_inheritance_children already got lock */
10003 childrel = table_open(childrelid, NoLock);
10004 CheckAlterTableIsSafe(childrel);
10005
10006 /* Find or create work queue entry for this table */
10007 childtab = ATGetQueueEntry(wqueue, childrel);
10008
10009 /* Recurse to this child */
10010 ATAddCheckNNConstraint(wqueue, childtab, childrel,
10011 constr, recurse, true, is_readd, lockmode);
10012
10013 table_close(childrel, NoLock);
10014 }
10015
10016 return address;
10017}
10018
10019/*
10020 * Add a foreign-key constraint to a single table; return the new constraint's
10021 * address.
10022 *
10023 * Subroutine for ATExecAddConstraint. Must already hold exclusive
10024 * lock on the rel, and have done appropriate validity checks for it.
10025 * We do permissions checks here, however.
10026 *
10027 * When the referenced or referencing tables (or both) are partitioned,
10028 * multiple pg_constraint rows are required -- one for each partitioned table
10029 * and each partition on each side (fortunately, not one for every combination
10030 * thereof). We also need action triggers on each leaf partition on the
10031 * referenced side, and check triggers on each leaf partition on the
10032 * referencing side.
10033 */
10034static ObjectAddress
10036 Constraint *fkconstraint,
10037 bool recurse, bool recursing, LOCKMODE lockmode)
10038{
10039 Relation pkrel;
10040 int16 pkattnum[INDEX_MAX_KEYS] = {0};
10041 int16 fkattnum[INDEX_MAX_KEYS] = {0};
10042 Oid pktypoid[INDEX_MAX_KEYS] = {0};
10043 Oid fktypoid[INDEX_MAX_KEYS] = {0};
10044 Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10045 Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10046 Oid opclasses[INDEX_MAX_KEYS] = {0};
10047 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10048 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10049 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10050 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10051 bool with_period;
10052 bool pk_has_without_overlaps;
10053 int i;
10054 int numfks,
10055 numpks,
10056 numfkdelsetcols;
10057 Oid indexOid;
10058 bool old_check_ok;
10059 ObjectAddress address;
10060 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10061
10062 /*
10063 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10064 * delete rows out from under us.
10065 */
10066 if (OidIsValid(fkconstraint->old_pktable_oid))
10067 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10068 else
10069 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10070
10071 /*
10072 * Validity checks (permission checks wait till we have the column
10073 * numbers)
10074 */
10075 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10076 ereport(ERROR,
10077 errcode(ERRCODE_WRONG_OBJECT_TYPE),
10078 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10080 RelationGetRelationName(pkrel)));
10081
10082 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10083 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10084 ereport(ERROR,
10085 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10086 errmsg("referenced relation \"%s\" is not a table",
10087 RelationGetRelationName(pkrel))));
10088
10090 ereport(ERROR,
10091 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10092 errmsg("permission denied: \"%s\" is a system catalog",
10093 RelationGetRelationName(pkrel))));
10094
10095 /*
10096 * References from permanent or unlogged tables to temp tables, and from
10097 * permanent tables to unlogged tables, are disallowed because the
10098 * referenced data can vanish out from under us. References from temp
10099 * tables to any other table type are also disallowed, because other
10100 * backends might need to run the RI triggers on the perm table, but they
10101 * can't reliably see tuples in the local buffers of other backends.
10102 */
10103 switch (rel->rd_rel->relpersistence)
10104 {
10105 case RELPERSISTENCE_PERMANENT:
10106 if (!RelationIsPermanent(pkrel))
10107 ereport(ERROR,
10108 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10109 errmsg("constraints on permanent tables may reference only permanent tables")));
10110 break;
10111 case RELPERSISTENCE_UNLOGGED:
10112 if (!RelationIsPermanent(pkrel)
10113 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10114 ereport(ERROR,
10115 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10116 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10117 break;
10118 case RELPERSISTENCE_TEMP:
10119 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10120 ereport(ERROR,
10121 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10122 errmsg("constraints on temporary tables may reference only temporary tables")));
10123 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10124 ereport(ERROR,
10125 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10126 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10127 break;
10128 }
10129
10130 /*
10131 * Look up the referencing attributes to make sure they exist, and record
10132 * their attnums and type and collation OIDs.
10133 */
10135 fkconstraint->fk_attrs,
10136 fkattnum, fktypoid, fkcolloid);
10137 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10138 if (with_period && !fkconstraint->fk_with_period)
10139 ereport(ERROR,
10140 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10141 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10142
10143 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10144 fkconstraint->fk_del_set_cols,
10145 fkdelsetcols, NULL, NULL);
10146 numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10147 numfkdelsetcols,
10148 fkdelsetcols,
10149 fkconstraint->fk_del_set_cols);
10150
10151 /*
10152 * If the attribute list for the referenced table was omitted, lookup the
10153 * definition of the primary key and use it. Otherwise, validate the
10154 * supplied attribute list. In either case, discover the index OID and
10155 * index opclasses, and the attnums and type and collation OIDs of the
10156 * attributes.
10157 */
10158 if (fkconstraint->pk_attrs == NIL)
10159 {
10160 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10161 &fkconstraint->pk_attrs,
10162 pkattnum, pktypoid, pkcolloid,
10163 opclasses, &pk_has_without_overlaps);
10164
10165 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10166 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10167 ereport(ERROR,
10168 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10169 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10170 }
10171 else
10172 {
10174 fkconstraint->pk_attrs,
10175 pkattnum, pktypoid, pkcolloid);
10176
10177 /* Since we got pk_attrs, one should be a period. */
10178 if (with_period && !fkconstraint->pk_with_period)
10179 ereport(ERROR,
10180 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10181 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10182
10183 /* Look for an index matching the column list */
10184 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10185 with_period, opclasses, &pk_has_without_overlaps);
10186 }
10187
10188 /*
10189 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10190 * must use PERIOD.
10191 */
10192 if (pk_has_without_overlaps && !with_period)
10193 ereport(ERROR,
10194 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10195 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10196
10197 /*
10198 * Now we can check permissions.
10199 */
10200 checkFkeyPermissions(pkrel, pkattnum, numpks);
10201
10202 /*
10203 * Check some things for generated columns.
10204 */
10205 for (i = 0; i < numfks; i++)
10206 {
10207 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10208
10209 if (attgenerated)
10210 {
10211 /*
10212 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10213 */
10214 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10215 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10216 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10217 ereport(ERROR,
10218 (errcode(ERRCODE_SYNTAX_ERROR),
10219 errmsg("invalid %s action for foreign key constraint containing generated column",
10220 "ON UPDATE")));
10221 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10223 ereport(ERROR,
10224 (errcode(ERRCODE_SYNTAX_ERROR),
10225 errmsg("invalid %s action for foreign key constraint containing generated column",
10226 "ON DELETE")));
10227 }
10228
10229 /*
10230 * FKs on virtual columns are not supported. This would require
10231 * various additional support in ri_triggers.c, including special
10232 * handling in ri_NullCheck(), ri_KeysEqual(),
10233 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10234 * as NULL there). Also not really practical as long as you can't
10235 * index virtual columns.
10236 */
10237 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10238 ereport(ERROR,
10239 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10240 errmsg("foreign key constraints on virtual generated columns are not supported")));
10241 }
10242
10243 /*
10244 * Some actions are currently unsupported for foreign keys using PERIOD.
10245 */
10246 if (fkconstraint->fk_with_period)
10247 {
10248 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10249 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10250 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10252 ereport(ERROR,
10253 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10254 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10255 "ON UPDATE"));
10256
10257 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10258 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10259 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10261 ereport(ERROR,
10262 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10263 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10264 "ON DELETE"));
10265 }
10266
10267 /*
10268 * Look up the equality operators to use in the constraint.
10269 *
10270 * Note that we have to be careful about the difference between the actual
10271 * PK column type and the opclass' declared input type, which might be
10272 * only binary-compatible with it. The declared opcintype is the right
10273 * thing to probe pg_amop with.
10274 */
10275 if (numfks != numpks)
10276 ereport(ERROR,
10277 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10278 errmsg("number of referencing and referenced columns for foreign key disagree")));
10279
10280 /*
10281 * On the strength of a previous constraint, we might avoid scanning
10282 * tables to validate this one. See below.
10283 */
10284 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10285 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10286
10287 for (i = 0; i < numpks; i++)
10288 {
10289 Oid pktype = pktypoid[i];
10290 Oid fktype = fktypoid[i];
10291 Oid fktyped;
10292 Oid pkcoll = pkcolloid[i];
10293 Oid fkcoll = fkcolloid[i];
10294 HeapTuple cla_ht;
10295 Form_pg_opclass cla_tup;
10296 Oid amid;
10297 Oid opfamily;
10298 Oid opcintype;
10299 bool for_overlaps;
10300 CompareType cmptype;
10301 Oid pfeqop;
10302 Oid ppeqop;
10303 Oid ffeqop;
10304 int16 eqstrategy;
10305 Oid pfeqop_right;
10306
10307 /* We need several fields out of the pg_opclass entry */
10308 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10309 if (!HeapTupleIsValid(cla_ht))
10310 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10311 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10312 amid = cla_tup->opcmethod;
10313 opfamily = cla_tup->opcfamily;
10314 opcintype = cla_tup->opcintype;
10315 ReleaseSysCache(cla_ht);
10316
10317 /*
10318 * Get strategy number from index AM.
10319 *
10320 * For a normal foreign-key constraint, this should not fail, since we
10321 * already checked that the index is unique and should therefore have
10322 * appropriate equal operators. For a period foreign key, this could
10323 * fail if we selected a non-matching exclusion constraint earlier.
10324 * (XXX Maybe we should do these lookups earlier so we don't end up
10325 * doing that.)
10326 */
10327 for_overlaps = with_period && i == numpks - 1;
10328 cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10329 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10330 if (eqstrategy == InvalidStrategy)
10331 ereport(ERROR,
10332 errcode(ERRCODE_UNDEFINED_OBJECT),
10333 for_overlaps
10334 ? errmsg("could not identify an overlaps operator for foreign key")
10335 : errmsg("could not identify an equality operator for foreign key"),
10336 errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10337 cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10338
10339 /*
10340 * There had better be a primary equality operator for the index.
10341 * We'll use it for PK = PK comparisons.
10342 */
10343 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10344 eqstrategy);
10345
10346 if (!OidIsValid(ppeqop))
10347 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10348 eqstrategy, opcintype, opcintype, opfamily);
10349
10350 /*
10351 * Are there equality operators that take exactly the FK type? Assume
10352 * we should look through any domain here.
10353 */
10354 fktyped = getBaseType(fktype);
10355
10356 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10357 eqstrategy);
10358 if (OidIsValid(pfeqop))
10359 {
10360 pfeqop_right = fktyped;
10361 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10362 eqstrategy);
10363 }
10364 else
10365 {
10366 /* keep compiler quiet */
10367 pfeqop_right = InvalidOid;
10368 ffeqop = InvalidOid;
10369 }
10370
10371 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10372 {
10373 /*
10374 * Otherwise, look for an implicit cast from the FK type to the
10375 * opcintype, and if found, use the primary equality operator.
10376 * This is a bit tricky because opcintype might be a polymorphic
10377 * type such as ANYARRAY or ANYENUM; so what we have to test is
10378 * whether the two actual column types can be concurrently cast to
10379 * that type. (Otherwise, we'd fail to reject combinations such
10380 * as int[] and point[].)
10381 */
10382 Oid input_typeids[2];
10383 Oid target_typeids[2];
10384
10385 input_typeids[0] = pktype;
10386 input_typeids[1] = fktype;
10387 target_typeids[0] = opcintype;
10388 target_typeids[1] = opcintype;
10389 if (can_coerce_type(2, input_typeids, target_typeids,
10391 {
10392 pfeqop = ffeqop = ppeqop;
10393 pfeqop_right = opcintype;
10394 }
10395 }
10396
10397 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10398 ereport(ERROR,
10399 (errcode(ERRCODE_DATATYPE_MISMATCH),
10400 errmsg("foreign key constraint \"%s\" cannot be implemented",
10401 fkconstraint->conname),
10402 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10403 "are of incompatible types: %s and %s.",
10404 strVal(list_nth(fkconstraint->fk_attrs, i)),
10405 strVal(list_nth(fkconstraint->pk_attrs, i)),
10406 format_type_be(fktype),
10407 format_type_be(pktype))));
10408
10409 /*
10410 * This shouldn't be possible, but better check to make sure we have a
10411 * consistent state for the check below.
10412 */
10413 if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10414 elog(ERROR, "key columns are not both collatable");
10415
10416 if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10417 {
10418 bool pkcolldet;
10419 bool fkcolldet;
10420
10421 pkcolldet = get_collation_isdeterministic(pkcoll);
10422 fkcolldet = get_collation_isdeterministic(fkcoll);
10423
10424 /*
10425 * SQL requires that both collations are the same. This is
10426 * because we need a consistent notion of equality on both
10427 * columns. We relax this by allowing different collations if
10428 * they are both deterministic. (This is also for backward
10429 * compatibility, because PostgreSQL has always allowed this.)
10430 */
10431 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10432 ereport(ERROR,
10433 (errcode(ERRCODE_COLLATION_MISMATCH),
10434 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10435 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10436 "have incompatible collations: \"%s\" and \"%s\". "
10437 "If either collation is nondeterministic, then both collations have to be the same.",
10438 strVal(list_nth(fkconstraint->fk_attrs, i)),
10439 strVal(list_nth(fkconstraint->pk_attrs, i)),
10440 get_collation_name(fkcoll),
10441 get_collation_name(pkcoll))));
10442 }
10443
10444 if (old_check_ok)
10445 {
10446 /*
10447 * When a pfeqop changes, revalidate the constraint. We could
10448 * permit intra-opfamily changes, but that adds subtle complexity
10449 * without any concrete benefit for core types. We need not
10450 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10451 */
10452 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10453 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10454 old_pfeqop_item);
10455 }
10456 if (old_check_ok)
10457 {
10458 Oid old_fktype;
10459 Oid new_fktype;
10460 CoercionPathType old_pathtype;
10461 CoercionPathType new_pathtype;
10462 Oid old_castfunc;
10463 Oid new_castfunc;
10464 Oid old_fkcoll;
10465 Oid new_fkcoll;
10467 fkattnum[i] - 1);
10468
10469 /*
10470 * Identify coercion pathways from each of the old and new FK-side
10471 * column types to the right (foreign) operand type of the pfeqop.
10472 * We may assume that pg_constraint.conkey is not changing.
10473 */
10474 old_fktype = attr->atttypid;
10475 new_fktype = fktype;
10476 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10477 &old_castfunc);
10478 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10479 &new_castfunc);
10480
10481 old_fkcoll = attr->attcollation;
10482 new_fkcoll = fkcoll;
10483
10484 /*
10485 * Upon a change to the cast from the FK column to its pfeqop
10486 * operand, revalidate the constraint. For this evaluation, a
10487 * binary coercion cast is equivalent to no cast at all. While
10488 * type implementors should design implicit casts with an eye
10489 * toward consistency of operations like equality, we cannot
10490 * assume here that they have done so.
10491 *
10492 * A function with a polymorphic argument could change behavior
10493 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10494 * when the cast destination is polymorphic, we only avoid
10495 * revalidation if the input type has not changed at all. Given
10496 * just the core data types and operator classes, this requirement
10497 * prevents no would-be optimizations.
10498 *
10499 * If the cast converts from a base type to a domain thereon, then
10500 * that domain type must be the opcintype of the unique index.
10501 * Necessarily, the primary key column must then be of the domain
10502 * type. Since the constraint was previously valid, all values on
10503 * the foreign side necessarily exist on the primary side and in
10504 * turn conform to the domain. Consequently, we need not treat
10505 * domains specially here.
10506 *
10507 * If the collation changes, revalidation is required, unless both
10508 * collations are deterministic, because those share the same
10509 * notion of equality (because texteq reduces to bitwise
10510 * equality).
10511 *
10512 * We need not directly consider the PK type. It's necessarily
10513 * binary coercible to the opcintype of the unique index column,
10514 * and ri_triggers.c will only deal with PK datums in terms of
10515 * that opcintype. Changing the opcintype also changes pfeqop.
10516 */
10517 old_check_ok = (new_pathtype == old_pathtype &&
10518 new_castfunc == old_castfunc &&
10519 (!IsPolymorphicType(pfeqop_right) ||
10520 new_fktype == old_fktype) &&
10521 (new_fkcoll == old_fkcoll ||
10522 (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10523 }
10524
10525 pfeqoperators[i] = pfeqop;
10526 ppeqoperators[i] = ppeqop;
10527 ffeqoperators[i] = ffeqop;
10528 }
10529
10530 /*
10531 * For FKs with PERIOD we need additional operators to check whether the
10532 * referencing row's range is contained by the aggregated ranges of the
10533 * referenced row(s). For rangetypes and multirangetypes this is
10534 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10535 * support for now. FKs will look these up at "runtime", but we should
10536 * make sure the lookup works here, even if we don't use the values.
10537 */
10538 if (with_period)
10539 {
10540 Oid periodoperoid;
10541 Oid aggedperiodoperoid;
10542 Oid intersectoperoid;
10543
10544 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10545 &intersectoperoid);
10546 }
10547
10548 /* First, create the constraint catalog entry itself. */
10550 fkconstraint->conname, fkconstraint, rel, pkrel,
10551 indexOid,
10552 InvalidOid, /* no parent constraint */
10553 numfks,
10554 pkattnum,
10555 fkattnum,
10556 pfeqoperators,
10557 ppeqoperators,
10558 ffeqoperators,
10559 numfkdelsetcols,
10560 fkdelsetcols,
10561 false,
10562 with_period);
10563
10564 /* Next process the action triggers at the referenced side and recurse */
10565 addFkRecurseReferenced(fkconstraint, rel, pkrel,
10566 indexOid,
10567 address.objectId,
10568 numfks,
10569 pkattnum,
10570 fkattnum,
10571 pfeqoperators,
10572 ppeqoperators,
10573 ffeqoperators,
10574 numfkdelsetcols,
10575 fkdelsetcols,
10576 old_check_ok,
10578 with_period);
10579
10580 /* Lastly create the check triggers at the referencing side and recurse */
10581 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10582 indexOid,
10583 address.objectId,
10584 numfks,
10585 pkattnum,
10586 fkattnum,
10587 pfeqoperators,
10588 ppeqoperators,
10589 ffeqoperators,
10590 numfkdelsetcols,
10591 fkdelsetcols,
10592 old_check_ok,
10593 lockmode,
10595 with_period);
10596
10597 /*
10598 * Done. Close pk table, but keep lock until we've committed.
10599 */
10600 table_close(pkrel, NoLock);
10601
10602 return address;
10603}
10604
10605/*
10606 * validateFkOnDeleteSetColumns
10607 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10608 * column lists are valid.
10609 *
10610 * If there are duplicates in the fksetcolsattnums[] array, this silently
10611 * removes the dups. The new count of numfksetcols is returned.
10612 */
10613static int
10614validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10615 int numfksetcols, int16 *fksetcolsattnums,
10616 List *fksetcols)
10617{
10618 int numcolsout = 0;
10619
10620 for (int i = 0; i < numfksetcols; i++)
10621 {
10622 int16 setcol_attnum = fksetcolsattnums[i];
10623 bool seen = false;
10624
10625 /* Make sure it's in fkattnums[] */
10626 for (int j = 0; j < numfks; j++)
10627 {
10628 if (fkattnums[j] == setcol_attnum)
10629 {
10630 seen = true;
10631 break;
10632 }
10633 }
10634
10635 if (!seen)
10636 {
10637 char *col = strVal(list_nth(fksetcols, i));
10638
10639 ereport(ERROR,
10640 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10641 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10642 }
10643
10644 /* Now check for dups */
10645 seen = false;
10646 for (int j = 0; j < numcolsout; j++)
10647 {
10648 if (fksetcolsattnums[j] == setcol_attnum)
10649 {
10650 seen = true;
10651 break;
10652 }
10653 }
10654 if (!seen)
10655 fksetcolsattnums[numcolsout++] = setcol_attnum;
10656 }
10657 return numcolsout;
10658}
10659
10660/*
10661 * addFkConstraint
10662 * Install pg_constraint entries to implement a foreign key constraint.
10663 * Caller must separately invoke addFkRecurseReferenced and
10664 * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10665 * and (for partitioned tables) recurse to partitions.
10666 *
10667 * fkside: the side of the FK (or both) to create. Caller should
10668 * call addFkRecurseReferenced if this is addFkReferencedSide,
10669 * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10670 * addFkBothSides.
10671 * constraintname: the base name for the constraint being added,
10672 * copied to fkconstraint->conname if the latter is not set
10673 * fkconstraint: the constraint being added
10674 * rel: the root referencing relation
10675 * pkrel: the referenced relation; might be a partition, if recursing
10676 * indexOid: the OID of the index (on pkrel) implementing this constraint
10677 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10678 * top-level constraint
10679 * numfks: the number of columns in the foreign key
10680 * pkattnum: the attnum array of referenced attributes
10681 * fkattnum: the attnum array of referencing attributes
10682 * pf/pp/ffeqoperators: OID array of operators between columns
10683 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10684 * (...) clause
10685 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10686 * NULL/DEFAULT clause
10687 * with_period: true if this is a temporal FK
10688 */
10689static ObjectAddress
10691 char *constraintname, Constraint *fkconstraint,
10692 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10693 int numfks, int16 *pkattnum,
10694 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10695 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10696 bool is_internal, bool with_period)
10697{
10698 ObjectAddress address;
10699 Oid constrOid;
10700 char *conname;
10701 bool conislocal;
10702 int16 coninhcount;
10703 bool connoinherit;
10704
10705 /*
10706 * Verify relkind for each referenced partition. At the top level, this
10707 * is redundant with a previous check, but we need it when recursing.
10708 */
10709 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10710 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10711 ereport(ERROR,
10712 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10713 errmsg("referenced relation \"%s\" is not a table",
10714 RelationGetRelationName(pkrel))));
10715
10716 /*
10717 * Caller supplies us with a constraint name; however, it may be used in
10718 * this partition, so come up with a different one in that case. Unless
10719 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10720 * supplied name with an underscore and digit(s) appended.
10721 */
10723 RelationGetRelid(rel),
10724 constraintname))
10725 conname = ChooseConstraintName(constraintname,
10726 NULL,
10727 "",
10729 else
10730 conname = constraintname;
10731
10732 if (fkconstraint->conname == NULL)
10733 fkconstraint->conname = pstrdup(conname);
10734
10735 if (OidIsValid(parentConstr))
10736 {
10737 conislocal = false;
10738 coninhcount = 1;
10739 connoinherit = false;
10740 }
10741 else
10742 {
10743 conislocal = true;
10744 coninhcount = 0;
10745
10746 /*
10747 * always inherit for partitioned tables, never for legacy inheritance
10748 */
10749 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10750 }
10751
10752 /*
10753 * Record the FK constraint in pg_constraint.
10754 */
10755 constrOid = CreateConstraintEntry(conname,
10757 CONSTRAINT_FOREIGN,
10758 fkconstraint->deferrable,
10759 fkconstraint->initdeferred,
10760 fkconstraint->is_enforced,
10761 fkconstraint->initially_valid,
10762 parentConstr,
10763 RelationGetRelid(rel),
10764 fkattnum,
10765 numfks,
10766 numfks,
10767 InvalidOid, /* not a domain constraint */
10768 indexOid,
10769 RelationGetRelid(pkrel),
10770 pkattnum,
10771 pfeqoperators,
10772 ppeqoperators,
10773 ffeqoperators,
10774 numfks,
10775 fkconstraint->fk_upd_action,
10776 fkconstraint->fk_del_action,
10777 fkdelsetcols,
10778 numfkdelsetcols,
10779 fkconstraint->fk_matchtype,
10780 NULL, /* no exclusion constraint */
10781 NULL, /* no check constraint */
10782 NULL,
10783 conislocal, /* islocal */
10784 coninhcount, /* inhcount */
10785 connoinherit, /* conNoInherit */
10786 with_period, /* conPeriod */
10787 is_internal); /* is_internal */
10788
10789 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10790
10791 /*
10792 * In partitioning cases, create the dependency entries for this
10793 * constraint. (For non-partitioned cases, relevant entries were created
10794 * by CreateConstraintEntry.)
10795 *
10796 * On the referenced side, we need the constraint to have an internal
10797 * dependency on its parent constraint; this means that this constraint
10798 * cannot be dropped on its own -- only through the parent constraint. It
10799 * also means the containing partition cannot be dropped on its own, but
10800 * it can be detached, at which point this dependency is removed (after
10801 * verifying that no rows are referenced via this FK.)
10802 *
10803 * When processing the referencing side, we link the constraint via the
10804 * special partitioning dependencies: the parent constraint is the primary
10805 * dependent, and the partition on which the foreign key exists is the
10806 * secondary dependency. That way, this constraint is dropped if either
10807 * of these objects is.
10808 *
10809 * Note that this is only necessary for the subsidiary pg_constraint rows
10810 * in partitions; the topmost row doesn't need any of this.
10811 */
10812 if (OidIsValid(parentConstr))
10813 {
10814 ObjectAddress referenced;
10815
10816 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10817
10818 Assert(fkside != addFkBothSides);
10819 if (fkside == addFkReferencedSide)
10820 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10821 else
10822 {
10823 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10824 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10825 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10826 }
10827 }
10828
10829 /* make new constraint visible, in case we add more */
10831
10832 return address;
10833}
10834
10835/*
10836 * addFkRecurseReferenced
10837 * Recursive helper for the referenced side of foreign key creation,
10838 * which creates the action triggers and recurses
10839 *
10840 * If the referenced relation is a plain relation, create the necessary action
10841 * triggers that implement the constraint. If the referenced relation is a
10842 * partitioned table, then we create a pg_constraint row referencing the parent
10843 * of the referencing side for it and recurse on this routine for each
10844 * partition.
10845 *
10846 * fkconstraint: the constraint being added
10847 * rel: the root referencing relation
10848 * pkrel: the referenced relation; might be a partition, if recursing
10849 * indexOid: the OID of the index (on pkrel) implementing this constraint
10850 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10851 * top-level constraint
10852 * numfks: the number of columns in the foreign key
10853 * pkattnum: the attnum array of referenced attributes
10854 * fkattnum: the attnum array of referencing attributes
10855 * numfkdelsetcols: the number of columns in the ON DELETE SET
10856 * NULL/DEFAULT (...) clause
10857 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10858 * NULL/DEFAULT clause
10859 * pf/pp/ffeqoperators: OID array of operators between columns
10860 * old_check_ok: true if this constraint replaces an existing one that
10861 * was already validated (thus this one doesn't need validation)
10862 * parentDelTrigger and parentUpdTrigger: when recursively called on a
10863 * partition, the OIDs of the parent action triggers for DELETE and
10864 * UPDATE respectively.
10865 * with_period: true if this is a temporal FK
10866 */
10867static void
10869 Relation pkrel, Oid indexOid, Oid parentConstr,
10870 int numfks,
10871 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10872 Oid *ppeqoperators, Oid *ffeqoperators,
10873 int numfkdelsetcols, int16 *fkdelsetcols,
10874 bool old_check_ok,
10875 Oid parentDelTrigger, Oid parentUpdTrigger,
10876 bool with_period)
10877{
10878 Oid deleteTriggerOid = InvalidOid,
10879 updateTriggerOid = InvalidOid;
10880
10883
10884 /*
10885 * Create action triggers to enforce the constraint, or skip them if the
10886 * constraint is NOT ENFORCED.
10887 */
10888 if (fkconstraint->is_enforced)
10890 RelationGetRelid(pkrel),
10891 fkconstraint,
10892 parentConstr, indexOid,
10893 parentDelTrigger, parentUpdTrigger,
10894 &deleteTriggerOid, &updateTriggerOid);
10895
10896 /*
10897 * If the referenced table is partitioned, recurse on ourselves to handle
10898 * each partition. We need one pg_constraint row created for each
10899 * partition in addition to the pg_constraint row for the parent table.
10900 */
10901 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10902 {
10903 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10904
10905 for (int i = 0; i < pd->nparts; i++)
10906 {
10907 Relation partRel;
10908 AttrMap *map;
10909 AttrNumber *mapped_pkattnum;
10910 Oid partIndexId;
10911 ObjectAddress address;
10912
10913 /* XXX would it be better to acquire these locks beforehand? */
10914 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10915
10916 /*
10917 * Map the attribute numbers in the referenced side of the FK
10918 * definition to match the partition's column layout.
10919 */
10921 RelationGetDescr(pkrel),
10922 false);
10923 if (map)
10924 {
10925 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10926 for (int j = 0; j < numfks; j++)
10927 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10928 }
10929 else
10930 mapped_pkattnum = pkattnum;
10931
10932 /* Determine the index to use at this level */
10933 partIndexId = index_get_partition(partRel, indexOid);
10934 if (!OidIsValid(partIndexId))
10935 elog(ERROR, "index for %u not found in partition %s",
10936 indexOid, RelationGetRelationName(partRel));
10937
10938 /* Create entry at this level ... */
10940 fkconstraint->conname, fkconstraint, rel,
10941 partRel, partIndexId, parentConstr,
10942 numfks, mapped_pkattnum,
10943 fkattnum, pfeqoperators, ppeqoperators,
10944 ffeqoperators, numfkdelsetcols,
10945 fkdelsetcols, true, with_period);
10946 /* ... and recurse to our children */
10947 addFkRecurseReferenced(fkconstraint, rel, partRel,
10948 partIndexId, address.objectId, numfks,
10949 mapped_pkattnum, fkattnum,
10950 pfeqoperators, ppeqoperators, ffeqoperators,
10951 numfkdelsetcols, fkdelsetcols,
10952 old_check_ok,
10953 deleteTriggerOid, updateTriggerOid,
10954 with_period);
10955
10956 /* Done -- clean up (but keep the lock) */
10957 table_close(partRel, NoLock);
10958 if (map)
10959 {
10960 pfree(mapped_pkattnum);
10961 free_attrmap(map);
10962 }
10963 }
10964 }
10965}
10966
10967/*
10968 * addFkRecurseReferencing
10969 * Recursive helper for the referencing side of foreign key creation,
10970 * which creates the check triggers and recurses
10971 *
10972 * If the referencing relation is a plain relation, create the necessary check
10973 * triggers that implement the constraint, and set up for Phase 3 constraint
10974 * verification. If the referencing relation is a partitioned table, then
10975 * we create a pg_constraint row for it and recurse on this routine for each
10976 * partition.
10977 *
10978 * We assume that the referenced relation is locked against concurrent
10979 * deletions. If it's a partitioned relation, every partition must be so
10980 * locked.
10981 *
10982 * wqueue: the ALTER TABLE work queue; NULL when not running as part
10983 * of an ALTER TABLE sequence.
10984 * fkconstraint: the constraint being added
10985 * rel: the referencing relation; might be a partition, if recursing
10986 * pkrel: the root referenced relation
10987 * indexOid: the OID of the index (on pkrel) implementing this constraint
10988 * parentConstr: the OID of the parent constraint (there is always one)
10989 * numfks: the number of columns in the foreign key
10990 * pkattnum: the attnum array of referenced attributes
10991 * fkattnum: the attnum array of referencing attributes
10992 * pf/pp/ffeqoperators: OID array of operators between columns
10993 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10994 * (...) clause
10995 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10996 * NULL/DEFAULT clause
10997 * old_check_ok: true if this constraint replaces an existing one that
10998 * was already validated (thus this one doesn't need validation)
10999 * lockmode: the lockmode to acquire on partitions when recursing
11000 * parentInsTrigger and parentUpdTrigger: when being recursively called on
11001 * a partition, the OIDs of the parent check triggers for INSERT and
11002 * UPDATE respectively.
11003 * with_period: true if this is a temporal FK
11004 */
11005static void
11007 Relation pkrel, Oid indexOid, Oid parentConstr,
11008 int numfks, int16 *pkattnum, int16 *fkattnum,
11009 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11010 int numfkdelsetcols, int16 *fkdelsetcols,
11011 bool old_check_ok, LOCKMODE lockmode,
11012 Oid parentInsTrigger, Oid parentUpdTrigger,
11013 bool with_period)
11014{
11015 Oid insertTriggerOid = InvalidOid,
11016 updateTriggerOid = InvalidOid;
11017
11018 Assert(OidIsValid(parentConstr));
11021
11022 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11023 ereport(ERROR,
11024 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11025 errmsg("foreign key constraints are not supported on foreign tables")));
11026
11027 /*
11028 * Add check triggers if the constraint is ENFORCED, and if needed,
11029 * schedule them to be checked in Phase 3.
11030 *
11031 * If the relation is partitioned, drill down to do it to its partitions.
11032 */
11033 if (fkconstraint->is_enforced)
11035 RelationGetRelid(pkrel),
11036 fkconstraint,
11037 parentConstr,
11038 indexOid,
11039 parentInsTrigger, parentUpdTrigger,
11040 &insertTriggerOid, &updateTriggerOid);
11041
11042 if (rel->rd_rel->relkind == RELKIND_RELATION)
11043 {
11044 /*
11045 * Tell Phase 3 to check that the constraint is satisfied by existing
11046 * rows. We can skip this during table creation, when constraint is
11047 * specified as NOT ENFORCED, or when requested explicitly by
11048 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11049 * recreating a constraint following a SET DATA TYPE operation that
11050 * did not impugn its validity.
11051 */
11052 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11053 fkconstraint->is_enforced)
11054 {
11055 NewConstraint *newcon;
11056 AlteredTableInfo *tab;
11057
11058 tab = ATGetQueueEntry(wqueue, rel);
11059
11060 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11061 newcon->name = get_constraint_name(parentConstr);
11062 newcon->contype = CONSTR_FOREIGN;
11063 newcon->refrelid = RelationGetRelid(pkrel);
11064 newcon->refindid = indexOid;
11065 newcon->conid = parentConstr;
11066 newcon->conwithperiod = fkconstraint->fk_with_period;
11067 newcon->qual = (Node *) fkconstraint;
11068
11069 tab->constraints = lappend(tab->constraints, newcon);
11070 }
11071 }
11072 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11073 {
11075 Relation trigrel;
11076
11077 /*
11078 * Triggers of the foreign keys will be manipulated a bunch of times
11079 * in the loop below. To avoid repeatedly opening/closing the trigger
11080 * catalog relation, we open it here and pass it to the subroutines
11081 * called below.
11082 */
11083 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11084
11085 /*
11086 * Recurse to take appropriate action on each partition; either we
11087 * find an existing constraint to reparent to ours, or we create a new
11088 * one.
11089 */
11090 for (int i = 0; i < pd->nparts; i++)
11091 {
11092 Relation partition = table_open(pd->oids[i], lockmode);
11093 List *partFKs;
11094 AttrMap *attmap;
11095 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11096 bool attached;
11097 ObjectAddress address;
11098
11099 CheckAlterTableIsSafe(partition);
11100
11101 attmap = build_attrmap_by_name(RelationGetDescr(partition),
11102 RelationGetDescr(rel),
11103 false);
11104 for (int j = 0; j < numfks; j++)
11105 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11106
11107 /* Check whether an existing constraint can be repurposed */
11108 partFKs = copyObject(RelationGetFKeyList(partition));
11109 attached = false;
11110 foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11111 {
11113 fk,
11114 partition,
11115 parentConstr,
11116 numfks,
11117 mapped_fkattnum,
11118 pkattnum,
11119 pfeqoperators,
11120 insertTriggerOid,
11121 updateTriggerOid,
11122 trigrel))
11123 {
11124 attached = true;
11125 break;
11126 }
11127 }
11128 if (attached)
11129 {
11130 table_close(partition, NoLock);
11131 continue;
11132 }
11133
11134 /*
11135 * No luck finding a good constraint to reuse; create our own.
11136 */
11138 fkconstraint->conname, fkconstraint,
11139 partition, pkrel, indexOid, parentConstr,
11140 numfks, pkattnum,
11141 mapped_fkattnum, pfeqoperators,
11142 ppeqoperators, ffeqoperators,
11143 numfkdelsetcols, fkdelsetcols, true,
11144 with_period);
11145
11146 /* call ourselves to finalize the creation and we're done */
11147 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11148 indexOid,
11149 address.objectId,
11150 numfks,
11151 pkattnum,
11152 mapped_fkattnum,
11153 pfeqoperators,
11154 ppeqoperators,
11155 ffeqoperators,
11156 numfkdelsetcols,
11157 fkdelsetcols,
11158 old_check_ok,
11159 lockmode,
11160 insertTriggerOid,
11161 updateTriggerOid,
11162 with_period);
11163
11164 table_close(partition, NoLock);
11165 }
11166
11167 table_close(trigrel, RowExclusiveLock);
11168 }
11169}
11170
11171/*
11172 * CloneForeignKeyConstraints
11173 * Clone foreign keys from a partitioned table to a newly acquired
11174 * partition.
11175 *
11176 * partitionRel is a partition of parentRel, so we can be certain that it has
11177 * the same columns with the same datatypes. The columns may be in different
11178 * order, though.
11179 *
11180 * wqueue must be passed to set up phase 3 constraint checking, unless the
11181 * referencing-side partition is known to be empty (such as in CREATE TABLE /
11182 * PARTITION OF).
11183 */
11184static void
11186 Relation partitionRel)
11187{
11188 /* This only works for declarative partitioning */
11189 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11190
11191 /*
11192 * First, clone constraints where the parent is on the referencing side.
11193 */
11194 CloneFkReferencing(wqueue, parentRel, partitionRel);
11195
11196 /*
11197 * Clone constraints for which the parent is on the referenced side.
11198 */
11199 CloneFkReferenced(parentRel, partitionRel);
11200}
11201
11202/*
11203 * CloneFkReferenced
11204 * Subroutine for CloneForeignKeyConstraints
11205 *
11206 * Find all the FKs that have the parent relation on the referenced side;
11207 * clone those constraints to the given partition. This is to be called
11208 * when the partition is being created or attached.
11209 *
11210 * This recurses to partitions, if the relation being attached is partitioned.
11211 * Recursion is done by calling addFkRecurseReferenced.
11212 */
11213static void
11214CloneFkReferenced(Relation parentRel, Relation partitionRel)
11215{
11216 Relation pg_constraint;
11217 AttrMap *attmap;
11218 ListCell *cell;
11219 SysScanDesc scan;
11220 ScanKeyData key[2];
11221 HeapTuple tuple;
11222 List *clone = NIL;
11223 Relation trigrel;
11224
11225 /*
11226 * Search for any constraints where this partition's parent is in the
11227 * referenced side. However, we must not clone any constraint whose
11228 * parent constraint is also going to be cloned, to avoid duplicates. So
11229 * do it in two steps: first construct the list of constraints to clone,
11230 * then go over that list cloning those whose parents are not in the list.
11231 * (We must not rely on the parent being seen first, since the catalog
11232 * scan could return children first.)
11233 */
11234 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11235 ScanKeyInit(&key[0],
11236 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11237 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11238 ScanKeyInit(&key[1],
11239 Anum_pg_constraint_contype, BTEqualStrategyNumber,
11240 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11241 /* This is a seqscan, as we don't have a usable index ... */
11242 scan = systable_beginscan(pg_constraint, InvalidOid, true,
11243 NULL, 2, key);
11244 while ((tuple = systable_getnext(scan)) != NULL)
11245 {
11246 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11247
11248 clone = lappend_oid(clone, constrForm->oid);
11249 }
11250 systable_endscan(scan);
11251 table_close(pg_constraint, RowShareLock);
11252
11253 /*
11254 * Triggers of the foreign keys will be manipulated a bunch of times in
11255 * the loop below. To avoid repeatedly opening/closing the trigger
11256 * catalog relation, we open it here and pass it to the subroutines called
11257 * below.
11258 */
11259 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11260
11261 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11262 RelationGetDescr(parentRel),
11263 false);
11264 foreach(cell, clone)
11265 {
11266 Oid constrOid = lfirst_oid(cell);
11267 Form_pg_constraint constrForm;
11268 Relation fkRel;
11269 Oid indexOid;
11270 Oid partIndexId;
11271 int numfks;
11272 AttrNumber conkey[INDEX_MAX_KEYS];
11273 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11274 AttrNumber confkey[INDEX_MAX_KEYS];
11275 Oid conpfeqop[INDEX_MAX_KEYS];
11276 Oid conppeqop[INDEX_MAX_KEYS];
11277 Oid conffeqop[INDEX_MAX_KEYS];
11278 int numfkdelsetcols;
11279 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11280 Constraint *fkconstraint;
11281 ObjectAddress address;
11282 Oid deleteTriggerOid = InvalidOid,
11283 updateTriggerOid = InvalidOid;
11284
11285 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11286 if (!HeapTupleIsValid(tuple))
11287 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11288 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11289
11290 /*
11291 * As explained above: don't try to clone a constraint for which we're
11292 * going to clone the parent.
11293 */
11294 if (list_member_oid(clone, constrForm->conparentid))
11295 {
11296 ReleaseSysCache(tuple);
11297 continue;
11298 }
11299
11300 /* We need the same lock level that CreateTrigger will acquire */
11301 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11302
11303 indexOid = constrForm->conindid;
11305 &numfks,
11306 conkey,
11307 confkey,
11308 conpfeqop,
11309 conppeqop,
11310 conffeqop,
11311 &numfkdelsetcols,
11312 confdelsetcols);
11313
11314 for (int i = 0; i < numfks; i++)
11315 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11316
11317 fkconstraint = makeNode(Constraint);
11318 fkconstraint->contype = CONSTRAINT_FOREIGN;
11319 fkconstraint->conname = NameStr(constrForm->conname);
11320 fkconstraint->deferrable = constrForm->condeferrable;
11321 fkconstraint->initdeferred = constrForm->condeferred;
11322 fkconstraint->location = -1;
11323 fkconstraint->pktable = NULL;
11324 /* ->fk_attrs determined below */
11325 fkconstraint->pk_attrs = NIL;
11326 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11327 fkconstraint->fk_upd_action = constrForm->confupdtype;
11328 fkconstraint->fk_del_action = constrForm->confdeltype;
11329 fkconstraint->fk_del_set_cols = NIL;
11330 fkconstraint->old_conpfeqop = NIL;
11331 fkconstraint->old_pktable_oid = InvalidOid;
11332 fkconstraint->is_enforced = constrForm->conenforced;
11333 fkconstraint->skip_validation = false;
11334 fkconstraint->initially_valid = constrForm->convalidated;
11335
11336 /* set up colnames that are used to generate the constraint name */
11337 for (int i = 0; i < numfks; i++)
11338 {
11340
11341 att = TupleDescAttr(RelationGetDescr(fkRel),
11342 conkey[i] - 1);
11343 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11344 makeString(NameStr(att->attname)));
11345 }
11346
11347 /*
11348 * Add the new foreign key constraint pointing to the new partition.
11349 * Because this new partition appears in the referenced side of the
11350 * constraint, we don't need to set up for Phase 3 check.
11351 */
11352 partIndexId = index_get_partition(partitionRel, indexOid);
11353 if (!OidIsValid(partIndexId))
11354 elog(ERROR, "index for %u not found in partition %s",
11355 indexOid, RelationGetRelationName(partitionRel));
11356
11357 /*
11358 * Get the "action" triggers belonging to the constraint to pass as
11359 * parent OIDs for similar triggers that will be created on the
11360 * partition in addFkRecurseReferenced().
11361 */
11362 if (constrForm->conenforced)
11363 GetForeignKeyActionTriggers(trigrel, constrOid,
11364 constrForm->confrelid, constrForm->conrelid,
11365 &deleteTriggerOid, &updateTriggerOid);
11366
11367 /* Add this constraint ... */
11369 fkconstraint->conname, fkconstraint, fkRel,
11370 partitionRel, partIndexId, constrOid,
11371 numfks, mapped_confkey,
11372 conkey, conpfeqop, conppeqop, conffeqop,
11373 numfkdelsetcols, confdelsetcols, false,
11374 constrForm->conperiod);
11375 /* ... and recurse */
11376 addFkRecurseReferenced(fkconstraint,
11377 fkRel,
11378 partitionRel,
11379 partIndexId,
11380 address.objectId,
11381 numfks,
11382 mapped_confkey,
11383 conkey,
11384 conpfeqop,
11385 conppeqop,
11386 conffeqop,
11387 numfkdelsetcols,
11388 confdelsetcols,
11389 true,
11390 deleteTriggerOid,
11391 updateTriggerOid,
11392 constrForm->conperiod);
11393
11394 table_close(fkRel, NoLock);
11395 ReleaseSysCache(tuple);
11396 }
11397
11398 table_close(trigrel, RowExclusiveLock);
11399}
11400
11401/*
11402 * CloneFkReferencing
11403 * Subroutine for CloneForeignKeyConstraints
11404 *
11405 * For each FK constraint of the parent relation in the given list, find an
11406 * equivalent constraint in its partition relation that can be reparented;
11407 * if one cannot be found, create a new constraint in the partition as its
11408 * child.
11409 *
11410 * If wqueue is given, it is used to set up phase-3 verification for each
11411 * cloned constraint; omit it if such verification is not needed
11412 * (example: the partition is being created anew).
11413 */
11414static void
11415CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11416{
11417 AttrMap *attmap;
11418 List *partFKs;
11419 List *clone = NIL;
11420 ListCell *cell;
11421 Relation trigrel;
11422
11423 /* obtain a list of constraints that we need to clone */
11424 foreach(cell, RelationGetFKeyList(parentRel))
11425 {
11426 ForeignKeyCacheInfo *fk = lfirst(cell);
11427
11428 /*
11429 * Refuse to attach a table as partition that this partitioned table
11430 * already has a foreign key to. This isn't useful schema, which is
11431 * proven by the fact that there have been no user complaints that
11432 * it's already impossible to achieve this in the opposite direction,
11433 * i.e., creating a foreign key that references a partition. This
11434 * restriction allows us to dodge some complexities around
11435 * pg_constraint and pg_trigger row creations that would be needed
11436 * during ATTACH/DETACH for this kind of relationship.
11437 */
11438 if (fk->confrelid == RelationGetRelid(partRel))
11439 ereport(ERROR,
11440 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11441 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11442 RelationGetRelationName(partRel),
11444
11445 clone = lappend_oid(clone, fk->conoid);
11446 }
11447
11448 /*
11449 * Silently do nothing if there's nothing to do. In particular, this
11450 * avoids throwing a spurious error for foreign tables.
11451 */
11452 if (clone == NIL)
11453 return;
11454
11455 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11456 ereport(ERROR,
11457 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11458 errmsg("foreign key constraints are not supported on foreign tables")));
11459
11460 /*
11461 * Triggers of the foreign keys will be manipulated a bunch of times in
11462 * the loop below. To avoid repeatedly opening/closing the trigger
11463 * catalog relation, we open it here and pass it to the subroutines called
11464 * below.
11465 */
11466 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11467
11468 /*
11469 * The constraint key may differ, if the columns in the partition are
11470 * different. This map is used to convert them.
11471 */
11472 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11473 RelationGetDescr(parentRel),
11474 false);
11475
11476 partFKs = copyObject(RelationGetFKeyList(partRel));
11477
11478 foreach(cell, clone)
11479 {
11480 Oid parentConstrOid = lfirst_oid(cell);
11481 Form_pg_constraint constrForm;
11482 Relation pkrel;
11483 HeapTuple tuple;
11484 int numfks;
11485 AttrNumber conkey[INDEX_MAX_KEYS];
11486 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11487 AttrNumber confkey[INDEX_MAX_KEYS];
11488 Oid conpfeqop[INDEX_MAX_KEYS];
11489 Oid conppeqop[INDEX_MAX_KEYS];
11490 Oid conffeqop[INDEX_MAX_KEYS];
11491 int numfkdelsetcols;
11492 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11493 Constraint *fkconstraint;
11494 bool attached;
11495 Oid indexOid;
11496 ObjectAddress address;
11497 ListCell *lc;
11498 Oid insertTriggerOid = InvalidOid,
11499 updateTriggerOid = InvalidOid;
11500 bool with_period;
11501
11502 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11503 if (!HeapTupleIsValid(tuple))
11504 elog(ERROR, "cache lookup failed for constraint %u",
11505 parentConstrOid);
11506 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11507
11508 /* Don't clone constraints whose parents are being cloned */
11509 if (list_member_oid(clone, constrForm->conparentid))
11510 {
11511 ReleaseSysCache(tuple);
11512 continue;
11513 }
11514
11515 /*
11516 * Need to prevent concurrent deletions. If pkrel is a partitioned
11517 * relation, that means to lock all partitions.
11518 */
11519 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11520 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11522 ShareRowExclusiveLock, NULL);
11523
11524 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11525 conpfeqop, conppeqop, conffeqop,
11526 &numfkdelsetcols, confdelsetcols);
11527 for (int i = 0; i < numfks; i++)
11528 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11529
11530 /*
11531 * Get the "check" triggers belonging to the constraint, if it is
11532 * ENFORCED, to pass as parent OIDs for similar triggers that will be
11533 * created on the partition in addFkRecurseReferencing(). They are
11534 * also passed to tryAttachPartitionForeignKey() below to simply
11535 * assign as parents to the partition's existing "check" triggers,
11536 * that is, if the corresponding constraints is deemed attachable to
11537 * the parent constraint.
11538 */
11539 if (constrForm->conenforced)
11540 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11541 constrForm->confrelid, constrForm->conrelid,
11542 &insertTriggerOid, &updateTriggerOid);
11543
11544 /*
11545 * Before creating a new constraint, see whether any existing FKs are
11546 * fit for the purpose. If one is, attach the parent constraint to
11547 * it, and don't clone anything. This way we avoid the expensive
11548 * verification step and don't end up with a duplicate FK, and we
11549 * don't need to recurse to partitions for this constraint.
11550 */
11551 attached = false;
11552 foreach(lc, partFKs)
11553 {
11555
11557 fk,
11558 partRel,
11559 parentConstrOid,
11560 numfks,
11561 mapped_conkey,
11562 confkey,
11563 conpfeqop,
11564 insertTriggerOid,
11565 updateTriggerOid,
11566 trigrel))
11567 {
11568 attached = true;
11569 table_close(pkrel, NoLock);
11570 break;
11571 }
11572 }
11573 if (attached)
11574 {
11575 ReleaseSysCache(tuple);
11576 continue;
11577 }
11578
11579 /* No dice. Set up to create our own constraint */
11580 fkconstraint = makeNode(Constraint);
11581 fkconstraint->contype = CONSTRAINT_FOREIGN;
11582 /* ->conname determined below */
11583 fkconstraint->deferrable = constrForm->condeferrable;
11584 fkconstraint->initdeferred = constrForm->condeferred;
11585 fkconstraint->location = -1;
11586 fkconstraint->pktable = NULL;
11587 /* ->fk_attrs determined below */
11588 fkconstraint->pk_attrs = NIL;
11589 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11590 fkconstraint->fk_upd_action = constrForm->confupdtype;
11591 fkconstraint->fk_del_action = constrForm->confdeltype;
11592 fkconstraint->fk_del_set_cols = NIL;
11593 fkconstraint->old_conpfeqop = NIL;
11594 fkconstraint->old_pktable_oid = InvalidOid;
11595 fkconstraint->is_enforced = constrForm->conenforced;
11596 fkconstraint->skip_validation = false;
11597 fkconstraint->initially_valid = constrForm->convalidated;
11598 for (int i = 0; i < numfks; i++)
11599 {
11601
11602 att = TupleDescAttr(RelationGetDescr(partRel),
11603 mapped_conkey[i] - 1);
11604 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11605 makeString(NameStr(att->attname)));
11606 }
11607
11608 indexOid = constrForm->conindid;
11609 with_period = constrForm->conperiod;
11610
11611 /* Create the pg_constraint entry at this level */
11613 NameStr(constrForm->conname), fkconstraint,
11614 partRel, pkrel, indexOid, parentConstrOid,
11615 numfks, confkey,
11616 mapped_conkey, conpfeqop,
11617 conppeqop, conffeqop,
11618 numfkdelsetcols, confdelsetcols,
11619 false, with_period);
11620
11621 /* Done with the cloned constraint's tuple */
11622 ReleaseSysCache(tuple);
11623
11624 /* Create the check triggers, and recurse to partitions, if any */
11626 fkconstraint,
11627 partRel,
11628 pkrel,
11629 indexOid,
11630 address.objectId,
11631 numfks,
11632 confkey,
11633 mapped_conkey,
11634 conpfeqop,
11635 conppeqop,
11636 conffeqop,
11637 numfkdelsetcols,
11638 confdelsetcols,
11639 false, /* no old check exists */
11641 insertTriggerOid,
11642 updateTriggerOid,
11643 with_period);
11644 table_close(pkrel, NoLock);
11645 }
11646
11647 table_close(trigrel, RowExclusiveLock);
11648}
11649
11650/*
11651 * When the parent of a partition receives [the referencing side of] a foreign
11652 * key, we must propagate that foreign key to the partition. However, the
11653 * partition might already have an equivalent foreign key; this routine
11654 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11655 * by the other parameters. If they are equivalent, create the link between
11656 * the two constraints and return true.
11657 *
11658 * If the given FK does not match the one defined by rest of the params,
11659 * return false.
11660 */
11661static bool
11664 Relation partition,
11665 Oid parentConstrOid,
11666 int numfks,
11667 AttrNumber *mapped_conkey,
11668 AttrNumber *confkey,
11669 Oid *conpfeqop,
11670 Oid parentInsTrigger,
11671 Oid parentUpdTrigger,
11672 Relation trigrel)
11673{
11674 HeapTuple parentConstrTup;
11675 Form_pg_constraint parentConstr;
11676 HeapTuple partcontup;
11677 Form_pg_constraint partConstr;
11678
11679 parentConstrTup = SearchSysCache1(CONSTROID,
11680 ObjectIdGetDatum(parentConstrOid));
11681 if (!HeapTupleIsValid(parentConstrTup))
11682 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11683 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11684
11685 /*
11686 * Do some quick & easy initial checks. If any of these fail, we cannot
11687 * use this constraint.
11688 */
11689 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11690 {
11691 ReleaseSysCache(parentConstrTup);
11692 return false;
11693 }
11694 for (int i = 0; i < numfks; i++)
11695 {
11696 if (fk->conkey[i] != mapped_conkey[i] ||
11697 fk->confkey[i] != confkey[i] ||
11698 fk->conpfeqop[i] != conpfeqop[i])
11699 {
11700 ReleaseSysCache(parentConstrTup);
11701 return false;
11702 }
11703 }
11704
11705 /* Looks good so far; perform more extensive checks. */
11706 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11707 if (!HeapTupleIsValid(partcontup))
11708 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11709 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11710
11711 /*
11712 * An error should be raised if the constraint enforceability is
11713 * different. Returning false without raising an error, as we do for other
11714 * attributes, could lead to a duplicate constraint with the same
11715 * enforceability as the parent. While this may be acceptable, it may not
11716 * be ideal. Therefore, it's better to raise an error and allow the user
11717 * to correct the enforceability before proceeding.
11718 */
11719 if (partConstr->conenforced != parentConstr->conenforced)
11720 ereport(ERROR,
11721 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11722 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11723 NameStr(parentConstr->conname),
11724 NameStr(partConstr->conname),
11725 RelationGetRelationName(partition))));
11726
11727 if (OidIsValid(partConstr->conparentid) ||
11728 partConstr->condeferrable != parentConstr->condeferrable ||
11729 partConstr->condeferred != parentConstr->condeferred ||
11730 partConstr->confupdtype != parentConstr->confupdtype ||
11731 partConstr->confdeltype != parentConstr->confdeltype ||
11732 partConstr->confmatchtype != parentConstr->confmatchtype)
11733 {
11734 ReleaseSysCache(parentConstrTup);
11735 ReleaseSysCache(partcontup);
11736 return false;
11737 }
11738
11739 ReleaseSysCache(parentConstrTup);
11740 ReleaseSysCache(partcontup);
11741
11742 /* Looks good! Attach this constraint. */
11743 AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11744 parentConstrOid, parentInsTrigger,
11745 parentUpdTrigger, trigrel);
11746
11747 return true;
11748}
11749
11750/*
11751 * AttachPartitionForeignKey
11752 *
11753 * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11754 * attaching the constraint, removing redundant triggers and entries from
11755 * pg_constraint, and setting the constraint's parent.
11756 */
11757static void
11759 Relation partition,
11760 Oid partConstrOid,
11761 Oid parentConstrOid,
11762 Oid parentInsTrigger,
11763 Oid parentUpdTrigger,
11764 Relation trigrel)
11765{
11766 HeapTuple parentConstrTup;
11767 Form_pg_constraint parentConstr;
11768 HeapTuple partcontup;
11769 Form_pg_constraint partConstr;
11770 bool queueValidation;
11771 Oid partConstrFrelid;
11772 Oid partConstrRelid;
11773 bool parentConstrIsEnforced;
11774
11775 /* Fetch the parent constraint tuple */
11776 parentConstrTup = SearchSysCache1(CONSTROID,
11777 ObjectIdGetDatum(parentConstrOid));
11778 if (!HeapTupleIsValid(parentConstrTup))
11779 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11780 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11781 parentConstrIsEnforced = parentConstr->conenforced;
11782
11783 /* Fetch the child constraint tuple */
11784 partcontup = SearchSysCache1(CONSTROID,
11785 ObjectIdGetDatum(partConstrOid));
11786 if (!HeapTupleIsValid(partcontup))
11787 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11788 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11789 partConstrFrelid = partConstr->confrelid;
11790 partConstrRelid = partConstr->conrelid;
11791
11792 /*
11793 * If the referenced table is partitioned, then the partition we're
11794 * attaching now has extra pg_constraint rows and action triggers that are
11795 * no longer needed. Remove those.
11796 */
11797 if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11798 {
11799 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11800
11801 RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11802 partConstrRelid);
11803
11804 table_close(pg_constraint, RowShareLock);
11805 }
11806
11807 /*
11808 * Will we need to validate this constraint? A valid parent constraint
11809 * implies that all child constraints have been validated, so if this one
11810 * isn't, we must trigger phase 3 validation.
11811 */
11812 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11813
11814 ReleaseSysCache(partcontup);
11815 ReleaseSysCache(parentConstrTup);
11816
11817 /*
11818 * The action triggers in the new partition become redundant -- the parent
11819 * table already has equivalent ones, and those will be able to reach the
11820 * partition. Remove the ones in the partition. We identify them because
11821 * they have our constraint OID, as well as being on the referenced rel.
11822 */
11823 DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11824 partConstrRelid);
11825
11826 ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11827 RelationGetRelid(partition));
11828
11829 /*
11830 * Like the constraint, attach partition's "check" triggers to the
11831 * corresponding parent triggers if the constraint is ENFORCED. NOT
11832 * ENFORCED constraints do not have these triggers.
11833 */
11834 if (parentConstrIsEnforced)
11835 {
11836 Oid insertTriggerOid,
11837 updateTriggerOid;
11838
11840 partConstrOid, partConstrFrelid, partConstrRelid,
11841 &insertTriggerOid, &updateTriggerOid);
11842 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11843 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11844 RelationGetRelid(partition));
11845 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11846 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11847 RelationGetRelid(partition));
11848 }
11849
11850 /*
11851 * We updated this pg_constraint row above to set its parent; validating
11852 * it will cause its convalidated flag to change, so we need CCI here. In
11853 * addition, we need it unconditionally for the rare case where the parent
11854 * table has *two* identical constraints; when reaching this function for
11855 * the second one, we must have made our changes visible, otherwise we
11856 * would try to attach both to this one.
11857 */
11859
11860 /* If validation is needed, put it in the queue now. */
11861 if (queueValidation)
11862 {
11863 Relation conrel;
11864 Oid confrelid;
11865
11866 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11867
11868 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11869 if (!HeapTupleIsValid(partcontup))
11870 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11871
11872 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11873
11874 /* Use the same lock as for AT_ValidateConstraint */
11875 QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11876 partcontup, ShareUpdateExclusiveLock);
11877 ReleaseSysCache(partcontup);
11879 }
11880}
11881
11882/*
11883 * RemoveInheritedConstraint
11884 *
11885 * Removes the constraint and its associated trigger from the specified
11886 * relation, which inherited the given constraint.
11887 */
11888static void
11890 Oid conrelid)
11891{
11892 ObjectAddresses *objs;
11893 HeapTuple consttup;
11895 SysScanDesc scan;
11896 HeapTuple trigtup;
11897
11899 Anum_pg_constraint_conrelid,
11900 BTEqualStrategyNumber, F_OIDEQ,
11901 ObjectIdGetDatum(conrelid));
11902
11903 scan = systable_beginscan(conrel,
11904 ConstraintRelidTypidNameIndexId,
11905 true, NULL, 1, &key);
11906 objs = new_object_addresses();
11907 while ((consttup = systable_getnext(scan)) != NULL)
11908 {
11909 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11910
11911 if (conform->conparentid != conoid)
11912 continue;
11913 else
11914 {
11915 ObjectAddress addr;
11916 SysScanDesc scan2;
11917 ScanKeyData key2;
11919
11920 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11921 add_exact_object_address(&addr, objs);
11922
11923 /*
11924 * First we must delete the dependency record that binds the
11925 * constraint records together.
11926 */
11927 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11928 conform->oid,
11930 ConstraintRelationId,
11931 conoid);
11932 Assert(n == 1); /* actually only one is expected */
11933
11934 /*
11935 * Now search for the triggers for this constraint and set them up
11936 * for deletion too
11937 */
11938 ScanKeyInit(&key2,
11939 Anum_pg_trigger_tgconstraint,
11940 BTEqualStrategyNumber, F_OIDEQ,
11941 ObjectIdGetDatum(conform->oid));
11942 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11943 true, NULL, 1, &key2);
11944 while ((trigtup = systable_getnext(scan2)) != NULL)
11945 {
11946 ObjectAddressSet(addr, TriggerRelationId,
11947 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11948 add_exact_object_address(&addr, objs);
11949 }
11950 systable_endscan(scan2);
11951 }
11952 }
11953 /* make the dependency deletions visible */
11957 systable_endscan(scan);
11958}
11959
11960/*
11961 * DropForeignKeyConstraintTriggers
11962 *
11963 * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11964 * action triggers for the foreign key constraint.
11965 *
11966 * If valid confrelid and conrelid values are not provided, the respective
11967 * trigger check will be skipped, and the trigger will be considered for
11968 * removal.
11969 */
11970static void
11972 Oid conrelid)
11973{
11975 SysScanDesc scan;
11976 HeapTuple trigtup;
11977
11979 Anum_pg_trigger_tgconstraint,
11980 BTEqualStrategyNumber, F_OIDEQ,
11981 ObjectIdGetDatum(conoid));
11982 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11983 NULL, 1, &key);
11984 while ((trigtup = systable_getnext(scan)) != NULL)
11985 {
11986 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11987 ObjectAddress trigger;
11988
11989 /* Invalid if trigger is not for a referential integrity constraint */
11990 if (!OidIsValid(trgform->tgconstrrelid))
11991 continue;
11992 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
11993 continue;
11994 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
11995 continue;
11996
11997 /* We should be dropping trigger related to foreign key constraint */
11998 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
11999 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12000 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12001 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12002 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12003 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12004 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12005 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12006 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12007 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12008 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12009 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12010
12011 /*
12012 * The constraint is originally set up to contain this trigger as an
12013 * implementation object, so there's a dependency record that links
12014 * the two; however, since the trigger is no longer needed, we remove
12015 * the dependency link in order to be able to drop the trigger while
12016 * keeping the constraint intact.
12017 */
12018 deleteDependencyRecordsFor(TriggerRelationId,
12019 trgform->oid,
12020 false);
12021 /* make dependency deletion visible to performDeletion */
12023 ObjectAddressSet(trigger, TriggerRelationId,
12024 trgform->oid);
12025 performDeletion(&trigger, DROP_RESTRICT, 0);
12026 /* make trigger drop visible, in case the loop iterates */
12028 }
12029
12030 systable_endscan(scan);
12031}
12032
12033/*
12034 * GetForeignKeyActionTriggers
12035 * Returns delete and update "action" triggers of the given relation
12036 * belonging to the given constraint
12037 */
12038static void
12040 Oid conoid, Oid confrelid, Oid conrelid,
12041 Oid *deleteTriggerOid,
12042 Oid *updateTriggerOid)
12043{
12045 SysScanDesc scan;
12046 HeapTuple trigtup;
12047
12048 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12050 Anum_pg_trigger_tgconstraint,
12051 BTEqualStrategyNumber, F_OIDEQ,
12052 ObjectIdGetDatum(conoid));
12053
12054 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12055 NULL, 1, &key);
12056 while ((trigtup = systable_getnext(scan)) != NULL)
12057 {
12058 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12059
12060 if (trgform->tgconstrrelid != conrelid)
12061 continue;
12062 if (trgform->tgrelid != confrelid)
12063 continue;
12064 /* Only ever look at "action" triggers on the PK side. */
12065 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12066 continue;
12067 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12068 {
12069 Assert(*deleteTriggerOid == InvalidOid);
12070 *deleteTriggerOid = trgform->oid;
12071 }
12072 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12073 {
12074 Assert(*updateTriggerOid == InvalidOid);
12075 *updateTriggerOid = trgform->oid;
12076 }
12077#ifndef USE_ASSERT_CHECKING
12078 /* In an assert-enabled build, continue looking to find duplicates */
12079 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12080 break;
12081#endif
12082 }
12083
12084 if (!OidIsValid(*deleteTriggerOid))
12085 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12086 conoid);
12087 if (!OidIsValid(*updateTriggerOid))
12088 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12089 conoid);
12090
12091 systable_endscan(scan);
12092}
12093
12094/*
12095 * GetForeignKeyCheckTriggers
12096 * Returns insert and update "check" triggers of the given relation
12097 * belonging to the given constraint
12098 */
12099static void
12101 Oid conoid, Oid confrelid, Oid conrelid,
12102 Oid *insertTriggerOid,
12103 Oid *updateTriggerOid)
12104{
12106 SysScanDesc scan;
12107 HeapTuple trigtup;
12108
12109 *insertTriggerOid = *updateTriggerOid = InvalidOid;
12111 Anum_pg_trigger_tgconstraint,
12112 BTEqualStrategyNumber, F_OIDEQ,
12113 ObjectIdGetDatum(conoid));
12114
12115 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12116 NULL, 1, &key);
12117 while ((trigtup = systable_getnext(scan)) != NULL)
12118 {
12119 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12120
12121 if (trgform->tgconstrrelid != confrelid)
12122 continue;
12123 if (trgform->tgrelid != conrelid)
12124 continue;
12125 /* Only ever look at "check" triggers on the FK side. */
12126 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12127 continue;
12128 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12129 {
12130 Assert(*insertTriggerOid == InvalidOid);
12131 *insertTriggerOid = trgform->oid;
12132 }
12133 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12134 {
12135 Assert(*updateTriggerOid == InvalidOid);
12136 *updateTriggerOid = trgform->oid;
12137 }
12138#ifndef USE_ASSERT_CHECKING
12139 /* In an assert-enabled build, continue looking to find duplicates. */
12140 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12141 break;
12142#endif
12143 }
12144
12145 if (!OidIsValid(*insertTriggerOid))
12146 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12147 conoid);
12148 if (!OidIsValid(*updateTriggerOid))
12149 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12150 conoid);
12151
12152 systable_endscan(scan);
12153}
12154
12155/*
12156 * ALTER TABLE ALTER CONSTRAINT
12157 *
12158 * Update the attributes of a constraint.
12159 *
12160 * Currently only works for Foreign Key and not null constraints.
12161 *
12162 * If the constraint is modified, returns its address; otherwise, return
12163 * InvalidObjectAddress.
12164 */
12165static ObjectAddress
12167 bool recurse, LOCKMODE lockmode)
12168{
12169 Relation conrel;
12170 Relation tgrel;
12171 SysScanDesc scan;
12172 ScanKeyData skey[3];
12173 HeapTuple contuple;
12174 Form_pg_constraint currcon;
12175 ObjectAddress address;
12176
12177 /*
12178 * Disallow altering ONLY a partitioned table, as it would make no sense.
12179 * This is okay for legacy inheritance.
12180 */
12181 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12182 ereport(ERROR,
12183 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12184 errmsg("constraint must be altered in child tables too"),
12185 errhint("Do not specify the ONLY keyword."));
12186
12187
12188 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12189 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12190
12191 /*
12192 * Find and check the target constraint
12193 */
12194 ScanKeyInit(&skey[0],
12195 Anum_pg_constraint_conrelid,
12196 BTEqualStrategyNumber, F_OIDEQ,
12198 ScanKeyInit(&skey[1],
12199 Anum_pg_constraint_contypid,
12200 BTEqualStrategyNumber, F_OIDEQ,
12202 ScanKeyInit(&skey[2],
12203 Anum_pg_constraint_conname,
12204 BTEqualStrategyNumber, F_NAMEEQ,
12205 CStringGetDatum(cmdcon->conname));
12206 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12207 true, NULL, 3, skey);
12208
12209 /* There can be at most one matching row */
12210 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12211 ereport(ERROR,
12212 (errcode(ERRCODE_UNDEFINED_OBJECT),
12213 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12214 cmdcon->conname, RelationGetRelationName(rel))));
12215
12216 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12217 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12218 ereport(ERROR,
12219 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12220 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12221 cmdcon->conname, RelationGetRelationName(rel))));
12222 if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12223 ereport(ERROR,
12224 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12225 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12226 cmdcon->conname, RelationGetRelationName(rel))));
12227 if (cmdcon->alterInheritability &&
12228 currcon->contype != CONSTRAINT_NOTNULL)
12229 ereport(ERROR,
12230 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12231 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12232 cmdcon->conname, RelationGetRelationName(rel)));
12233
12234 /* Refuse to modify inheritability of inherited constraints */
12235 if (cmdcon->alterInheritability &&
12236 cmdcon->noinherit && currcon->coninhcount > 0)
12237 ereport(ERROR,
12238 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12239 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12240 NameStr(currcon->conname),
12242
12243 /*
12244 * If it's not the topmost constraint, raise an error.
12245 *
12246 * Altering a non-topmost constraint leaves some triggers untouched, since
12247 * they are not directly connected to this constraint; also, pg_dump would
12248 * ignore the deferrability status of the individual constraint, since it
12249 * only dumps topmost constraints. Avoid these problems by refusing this
12250 * operation and telling the user to alter the parent constraint instead.
12251 */
12252 if (OidIsValid(currcon->conparentid))
12253 {
12254 HeapTuple tp;
12255 Oid parent = currcon->conparentid;
12256 char *ancestorname = NULL;
12257 char *ancestortable = NULL;
12258
12259 /* Loop to find the topmost constraint */
12260 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12261 {
12263
12264 /* If no parent, this is the constraint we want */
12265 if (!OidIsValid(contup->conparentid))
12266 {
12267 ancestorname = pstrdup(NameStr(contup->conname));
12268 ancestortable = get_rel_name(contup->conrelid);
12269 ReleaseSysCache(tp);
12270 break;
12271 }
12272
12273 parent = contup->conparentid;
12274 ReleaseSysCache(tp);
12275 }
12276
12277 ereport(ERROR,
12278 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12279 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12280 cmdcon->conname, RelationGetRelationName(rel)),
12281 ancestorname && ancestortable ?
12282 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12283 cmdcon->conname, ancestorname, ancestortable) : 0,
12284 errhint("You may alter the constraint it derives from instead.")));
12285 }
12286
12287 address = InvalidObjectAddress;
12288
12289 /*
12290 * Do the actual catalog work, and recurse if necessary.
12291 */
12292 if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12293 contuple, recurse, lockmode))
12294 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12295
12296 systable_endscan(scan);
12297
12300
12301 return address;
12302}
12303
12304/*
12305 * A subroutine of ATExecAlterConstraint that calls the respective routines for
12306 * altering constraint's enforceability, deferrability or inheritability.
12307 */
12308static bool
12310 Relation conrel, Relation tgrel, Relation rel,
12311 HeapTuple contuple, bool recurse,
12312 LOCKMODE lockmode)
12313{
12314 Form_pg_constraint currcon;
12315 bool changed = false;
12316 List *otherrelids = NIL;
12317
12318 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12319
12320 /*
12321 * Do the catalog work for the enforceability or deferrability change,
12322 * recurse if necessary.
12323 *
12324 * Note that even if deferrability is requested to be altered along with
12325 * enforceability, we don't need to explicitly update multiple entries in
12326 * pg_trigger related to deferrability.
12327 *
12328 * Modifying enforceability involves either creating or dropping the
12329 * trigger, during which the deferrability setting will be adjusted
12330 * automatically.
12331 */
12332 if (cmdcon->alterEnforceability &&
12333 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12334 currcon->conrelid, currcon->confrelid,
12335 contuple, lockmode, InvalidOid,
12337 changed = true;
12338
12339 else if (cmdcon->alterDeferrability &&
12340 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12341 contuple, recurse, &otherrelids,
12342 lockmode))
12343 {
12344 /*
12345 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12346 * the relations having the constraint itself; here we also invalidate
12347 * for relations that have any triggers that are part of the
12348 * constraint.
12349 */
12350 foreach_oid(relid, otherrelids)
12352
12353 changed = true;
12354 }
12355
12356 /*
12357 * Do the catalog work for the inheritability change.
12358 */
12359 if (cmdcon->alterInheritability &&
12360 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12361 lockmode))
12362 changed = true;
12363
12364 return changed;
12365}
12366
12367/*
12368 * Returns true if the constraint's enforceability is altered.
12369 *
12370 * Depending on whether the constraint is being set to ENFORCED or NOT
12371 * ENFORCED, it creates or drops the trigger accordingly.
12372 *
12373 * Note that we must recurse even when trying to change a constraint to not
12374 * enforced if it is already not enforced, in case descendant constraints
12375 * might be enforced and need to be changed to not enforced. Conversely, we
12376 * should do nothing if a constraint is being set to enforced and is already
12377 * enforced, as descendant constraints cannot be different in that case.
12378 */
12379static bool
12381 Relation conrel, Relation tgrel,
12382 Oid fkrelid, Oid pkrelid,
12383 HeapTuple contuple, LOCKMODE lockmode,
12384 Oid ReferencedParentDelTrigger,
12385 Oid ReferencedParentUpdTrigger,
12386 Oid ReferencingParentInsTrigger,
12387 Oid ReferencingParentUpdTrigger)
12388{
12389 Form_pg_constraint currcon;
12390 Oid conoid;
12391 Relation rel;
12392 bool changed = false;
12393
12394 /* Since this function recurses, it could be driven to stack overflow */
12396
12397 Assert(cmdcon->alterEnforceability);
12398
12399 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12400 conoid = currcon->oid;
12401
12402 /* Should be foreign key constraint */
12403 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12404
12405 rel = table_open(currcon->conrelid, lockmode);
12406
12407 if (currcon->conenforced != cmdcon->is_enforced)
12408 {
12409 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12410 changed = true;
12411 }
12412
12413 /* Drop triggers */
12414 if (!cmdcon->is_enforced)
12415 {
12416 /*
12417 * When setting a constraint to NOT ENFORCED, the constraint triggers
12418 * need to be dropped. Therefore, we must process the child relations
12419 * first, followed by the parent, to account for dependencies.
12420 */
12421 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12422 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12423 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12424 fkrelid, pkrelid, contuple,
12425 lockmode, InvalidOid, InvalidOid,
12427
12428 /* Drop all the triggers */
12430 }
12431 else if (changed) /* Create triggers */
12432 {
12433 Oid ReferencedDelTriggerOid = InvalidOid,
12434 ReferencedUpdTriggerOid = InvalidOid,
12435 ReferencingInsTriggerOid = InvalidOid,
12436 ReferencingUpdTriggerOid = InvalidOid;
12437
12438 /* Prepare the minimal information required for trigger creation. */
12439 Constraint *fkconstraint = makeNode(Constraint);
12440
12441 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12442 fkconstraint->fk_matchtype = currcon->confmatchtype;
12443 fkconstraint->fk_upd_action = currcon->confupdtype;
12444 fkconstraint->fk_del_action = currcon->confdeltype;
12445
12446 /* Create referenced triggers */
12447 if (currcon->conrelid == fkrelid)
12448 createForeignKeyActionTriggers(currcon->conrelid,
12449 currcon->confrelid,
12450 fkconstraint,
12451 conoid,
12452 currcon->conindid,
12453 ReferencedParentDelTrigger,
12454 ReferencedParentUpdTrigger,
12455 &ReferencedDelTriggerOid,
12456 &ReferencedUpdTriggerOid);
12457
12458 /* Create referencing triggers */
12459 if (currcon->confrelid == pkrelid)
12460 createForeignKeyCheckTriggers(currcon->conrelid,
12461 pkrelid,
12462 fkconstraint,
12463 conoid,
12464 currcon->conindid,
12465 ReferencingParentInsTrigger,
12466 ReferencingParentUpdTrigger,
12467 &ReferencingInsTriggerOid,
12468 &ReferencingUpdTriggerOid);
12469
12470 /*
12471 * Tell Phase 3 to check that the constraint is satisfied by existing
12472 * rows. Only applies to leaf partitions, and (for constraints that
12473 * reference a partitioned table) only if this is not one of the
12474 * pg_constraint rows that exist solely to support action triggers.
12475 */
12476 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12477 currcon->confrelid == pkrelid)
12478 {
12479 AlteredTableInfo *tab;
12480 NewConstraint *newcon;
12481
12482 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12483 newcon->name = fkconstraint->conname;
12484 newcon->contype = CONSTR_FOREIGN;
12485 newcon->refrelid = currcon->confrelid;
12486 newcon->refindid = currcon->conindid;
12487 newcon->conid = currcon->oid;
12488 newcon->qual = (Node *) fkconstraint;
12489
12490 /* Find or create work queue entry for this table */
12491 tab = ATGetQueueEntry(wqueue, rel);
12492 tab->constraints = lappend(tab->constraints, newcon);
12493 }
12494
12495 /*
12496 * If the table at either end of the constraint is partitioned, we
12497 * need to recurse and create triggers for each constraint that is a
12498 * child of this one.
12499 */
12500 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12501 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12502 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12503 fkrelid, pkrelid, contuple,
12504 lockmode, ReferencedDelTriggerOid,
12505 ReferencedUpdTriggerOid,
12506 ReferencingInsTriggerOid,
12507 ReferencingUpdTriggerOid);
12508 }
12509
12510 table_close(rel, NoLock);
12511
12512 return changed;
12513}
12514
12515/*
12516 * Returns true if the constraint's deferrability is altered.
12517 *
12518 * *otherrelids is appended OIDs of relations containing affected triggers.
12519 *
12520 * Note that we must recurse even when the values are correct, in case
12521 * indirect descendants have had their constraints altered locally.
12522 * (This could be avoided if we forbade altering constraints in partitions
12523 * but existing releases don't do that.)
12524 */
12525static bool
12527 Relation conrel, Relation tgrel, Relation rel,
12528 HeapTuple contuple, bool recurse,
12529 List **otherrelids, LOCKMODE lockmode)
12530{
12531 Form_pg_constraint currcon;
12532 Oid refrelid;
12533 bool changed = false;
12534
12535 /* since this function recurses, it could be driven to stack overflow */
12537
12538 Assert(cmdcon->alterDeferrability);
12539
12540 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12541 refrelid = currcon->confrelid;
12542
12543 /* Should be foreign key constraint */
12544 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12545
12546 /*
12547 * If called to modify a constraint that's already in the desired state,
12548 * silently do nothing.
12549 */
12550 if (currcon->condeferrable != cmdcon->deferrable ||
12551 currcon->condeferred != cmdcon->initdeferred)
12552 {
12553 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12554 changed = true;
12555
12556 /*
12557 * Now we need to update the multiple entries in pg_trigger that
12558 * implement the constraint.
12559 */
12560 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12561 cmdcon->deferrable,
12562 cmdcon->initdeferred, otherrelids);
12563 }
12564
12565 /*
12566 * If the table at either end of the constraint is partitioned, we need to
12567 * handle every constraint that is a child of this one.
12568 */
12569 if (recurse && changed &&
12570 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12571 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12572 AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12573 contuple, recurse, otherrelids,
12574 lockmode);
12575
12576 return changed;
12577}
12578
12579/*
12580 * Returns true if the constraint's inheritability is altered.
12581 */
12582static bool
12584 Relation conrel, Relation rel,
12585 HeapTuple contuple, LOCKMODE lockmode)
12586{
12587 Form_pg_constraint currcon;
12588 AttrNumber colNum;
12589 char *colName;
12590 List *children;
12591
12592 Assert(cmdcon->alterInheritability);
12593
12594 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12595
12596 /* The current implementation only works for NOT NULL constraints */
12597 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12598
12599 /*
12600 * If called to modify a constraint that's already in the desired state,
12601 * silently do nothing.
12602 */
12603 if (cmdcon->noinherit == currcon->connoinherit)
12604 return false;
12605
12606 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12608
12609 /* Fetch the column number and name */
12610 colNum = extractNotNullColumn(contuple);
12611 colName = get_attname(currcon->conrelid, colNum, false);
12612
12613 /*
12614 * Propagate the change to children. For this subcommand type we don't
12615 * recursively affect children, just the immediate level.
12616 */
12618 lockmode);
12619 foreach_oid(childoid, children)
12620 {
12621 ObjectAddress addr;
12622
12623 if (cmdcon->noinherit)
12624 {
12625 HeapTuple childtup;
12626 Form_pg_constraint childcon;
12627
12628 childtup = findNotNullConstraint(childoid, colName);
12629 if (!childtup)
12630 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12631 colName, childoid);
12632 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12633 Assert(childcon->coninhcount > 0);
12634 childcon->coninhcount--;
12635 childcon->conislocal = true;
12636 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12637 heap_freetuple(childtup);
12638 }
12639 else
12640 {
12641 Relation childrel = table_open(childoid, NoLock);
12642
12643 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12644 colName, true, true, lockmode);
12645 if (OidIsValid(addr.objectId))
12647 table_close(childrel, NoLock);
12648 }
12649 }
12650
12651 return true;
12652}
12653
12654/*
12655 * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12656 * trigger's deferrability.
12657 *
12658 * The arguments to this function have the same meaning as the arguments to
12659 * ATExecAlterConstrDeferrability.
12660 */
12661static void
12663 bool deferrable, bool initdeferred,
12664 List **otherrelids)
12665{
12666 HeapTuple tgtuple;
12667 ScanKeyData tgkey;
12668 SysScanDesc tgscan;
12669
12670 ScanKeyInit(&tgkey,
12671 Anum_pg_trigger_tgconstraint,
12672 BTEqualStrategyNumber, F_OIDEQ,
12673 ObjectIdGetDatum(conoid));
12674 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12675 NULL, 1, &tgkey);
12676 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12677 {
12678 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12679 Form_pg_trigger copy_tg;
12680 HeapTuple tgCopyTuple;
12681
12682 /*
12683 * Remember OIDs of other relation(s) involved in FK constraint.
12684 * (Note: it's likely that we could skip forcing a relcache inval for
12685 * other rels that don't have a trigger whose properties change, but
12686 * let's be conservative.)
12687 */
12688 if (tgform->tgrelid != RelationGetRelid(rel))
12689 *otherrelids = list_append_unique_oid(*otherrelids,
12690 tgform->tgrelid);
12691
12692 /*
12693 * Update enable status and deferrability of RI_FKey_noaction_del,
12694 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12695 * triggers, but not others; see createForeignKeyActionTriggers and
12696 * CreateFKCheckTrigger.
12697 */
12698 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12699 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12700 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12701 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12702 continue;
12703
12704 tgCopyTuple = heap_copytuple(tgtuple);
12705 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12706
12707 copy_tg->tgdeferrable = deferrable;
12708 copy_tg->tginitdeferred = initdeferred;
12709 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12710
12711 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12712
12713 heap_freetuple(tgCopyTuple);
12714 }
12715
12716 systable_endscan(tgscan);
12717}
12718
12719/*
12720 * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12721 * the specified constraint.
12722 *
12723 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12724 * list of child relations and recursing; instead it uses the conparentid
12725 * relationships. This may need to be reconsidered.
12726 *
12727 * The arguments to this function have the same meaning as the arguments to
12728 * ATExecAlterConstrEnforceability.
12729 */
12730static void
12732 Relation conrel, Relation tgrel,
12733 Oid fkrelid, Oid pkrelid,
12734 HeapTuple contuple, LOCKMODE lockmode,
12735 Oid ReferencedParentDelTrigger,
12736 Oid ReferencedParentUpdTrigger,
12737 Oid ReferencingParentInsTrigger,
12738 Oid ReferencingParentUpdTrigger)
12739{
12740 Form_pg_constraint currcon;
12741 Oid conoid;
12742 ScanKeyData pkey;
12743 SysScanDesc pscan;
12744 HeapTuple childtup;
12745
12746 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12747 conoid = currcon->oid;
12748
12749 ScanKeyInit(&pkey,
12750 Anum_pg_constraint_conparentid,
12751 BTEqualStrategyNumber, F_OIDEQ,
12752 ObjectIdGetDatum(conoid));
12753
12754 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12755 true, NULL, 1, &pkey);
12756
12757 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12758 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12759 pkrelid, childtup, lockmode,
12760 ReferencedParentDelTrigger,
12761 ReferencedParentUpdTrigger,
12762 ReferencingParentInsTrigger,
12763 ReferencingParentUpdTrigger);
12764
12765 systable_endscan(pscan);
12766}
12767
12768/*
12769 * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12770 * the specified constraint.
12771 *
12772 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12773 * list of child relations and recursing; instead it uses the conparentid
12774 * relationships. This may need to be reconsidered.
12775 *
12776 * The arguments to this function have the same meaning as the arguments to
12777 * ATExecAlterConstrDeferrability.
12778 */
12779static void
12781 Relation conrel, Relation tgrel, Relation rel,
12782 HeapTuple contuple, bool recurse,
12783 List **otherrelids, LOCKMODE lockmode)
12784{
12785 Form_pg_constraint currcon;
12786 Oid conoid;
12787 ScanKeyData pkey;
12788 SysScanDesc pscan;
12789 HeapTuple childtup;
12790
12791 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12792 conoid = currcon->oid;
12793
12794 ScanKeyInit(&pkey,
12795 Anum_pg_constraint_conparentid,
12796 BTEqualStrategyNumber, F_OIDEQ,
12797 ObjectIdGetDatum(conoid));
12798
12799 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12800 true, NULL, 1, &pkey);
12801
12802 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12803 {
12804 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12805 Relation childrel;
12806
12807 childrel = table_open(childcon->conrelid, lockmode);
12808
12809 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12810 childtup, recurse, otherrelids, lockmode);
12811 table_close(childrel, NoLock);
12812 }
12813
12814 systable_endscan(pscan);
12815}
12816
12817/*
12818 * Update the constraint entry for the given ATAlterConstraint command, and
12819 * invoke the appropriate hooks.
12820 */
12821static void
12823 HeapTuple contuple)
12824{
12825 HeapTuple copyTuple;
12826 Form_pg_constraint copy_con;
12827
12828 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12829 cmdcon->alterInheritability);
12830
12831 copyTuple = heap_copytuple(contuple);
12832 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12833
12834 if (cmdcon->alterEnforceability)
12835 {
12836 copy_con->conenforced = cmdcon->is_enforced;
12837
12838 /*
12839 * NB: The convalidated status is irrelevant when the constraint is
12840 * set to NOT ENFORCED, but for consistency, it should still be set
12841 * appropriately. Similarly, if the constraint is later changed to
12842 * ENFORCED, validation will be performed during phase 3, so it makes
12843 * sense to mark it as valid in that case.
12844 */
12845 copy_con->convalidated = cmdcon->is_enforced;
12846 }
12847 if (cmdcon->alterDeferrability)
12848 {
12849 copy_con->condeferrable = cmdcon->deferrable;
12850 copy_con->condeferred = cmdcon->initdeferred;
12851 }
12852 if (cmdcon->alterInheritability)
12853 copy_con->connoinherit = cmdcon->noinherit;
12854
12855 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12856 InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12857
12858 /* Make new constraint flags visible to others */
12859 CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12860
12861 heap_freetuple(copyTuple);
12862}
12863
12864/*
12865 * ALTER TABLE VALIDATE CONSTRAINT
12866 *
12867 * XXX The reason we handle recursion here rather than at Phase 1 is because
12868 * there's no good way to skip recursing when handling foreign keys: there is
12869 * no need to lock children in that case, yet we wouldn't be able to avoid
12870 * doing so at that level.
12871 *
12872 * Return value is the address of the validated constraint. If the constraint
12873 * was already validated, InvalidObjectAddress is returned.
12874 */
12875static ObjectAddress
12876ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12877 bool recurse, bool recursing, LOCKMODE lockmode)
12878{
12879 Relation conrel;
12880 SysScanDesc scan;
12881 ScanKeyData skey[3];
12882 HeapTuple tuple;
12884 ObjectAddress address;
12885
12886 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12887
12888 /*
12889 * Find and check the target constraint
12890 */
12891 ScanKeyInit(&skey[0],
12892 Anum_pg_constraint_conrelid,
12893 BTEqualStrategyNumber, F_OIDEQ,
12895 ScanKeyInit(&skey[1],
12896 Anum_pg_constraint_contypid,
12897 BTEqualStrategyNumber, F_OIDEQ,
12899 ScanKeyInit(&skey[2],
12900 Anum_pg_constraint_conname,
12901 BTEqualStrategyNumber, F_NAMEEQ,
12902 CStringGetDatum(constrName));
12903 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12904 true, NULL, 3, skey);
12905
12906 /* There can be at most one matching row */
12907 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12908 ereport(ERROR,
12909 (errcode(ERRCODE_UNDEFINED_OBJECT),
12910 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12911 constrName, RelationGetRelationName(rel))));
12912
12913 con = (Form_pg_constraint) GETSTRUCT(tuple);
12914 if (con->contype != CONSTRAINT_FOREIGN &&
12915 con->contype != CONSTRAINT_CHECK &&
12916 con->contype != CONSTRAINT_NOTNULL)
12917 ereport(ERROR,
12918 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12919 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12920 constrName, RelationGetRelationName(rel)),
12921 errdetail("This operation is not supported for this type of constraint."));
12922
12923 if (!con->conenforced)
12924 ereport(ERROR,
12925 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12926 errmsg("cannot validate NOT ENFORCED constraint")));
12927
12928 if (!con->convalidated)
12929 {
12930 if (con->contype == CONSTRAINT_FOREIGN)
12931 {
12932 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12933 tuple, lockmode);
12934 }
12935 else if (con->contype == CONSTRAINT_CHECK)
12936 {
12937 QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12938 tuple, recurse, recursing, lockmode);
12939 }
12940 else if (con->contype == CONSTRAINT_NOTNULL)
12941 {
12942 QueueNNConstraintValidation(wqueue, conrel, rel,
12943 tuple, recurse, recursing, lockmode);
12944 }
12945
12946 ObjectAddressSet(address, ConstraintRelationId, con->oid);
12947 }
12948 else
12949 address = InvalidObjectAddress; /* already validated */
12950
12951 systable_endscan(scan);
12952
12954
12955 return address;
12956}
12957
12958/*
12959 * QueueFKConstraintValidation
12960 *
12961 * Add an entry to the wqueue to validate the given foreign key constraint in
12962 * Phase 3 and update the convalidated field in the pg_constraint catalog
12963 * for the specified relation and all its children.
12964 */
12965static void
12967 Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
12968{
12970 AlteredTableInfo *tab;
12971 HeapTuple copyTuple;
12972 Form_pg_constraint copy_con;
12973
12974 con = (Form_pg_constraint) GETSTRUCT(contuple);
12975 Assert(con->contype == CONSTRAINT_FOREIGN);
12976 Assert(!con->convalidated);
12977
12978 /*
12979 * Add the validation to phase 3's queue; not needed for partitioned
12980 * tables themselves, only for their partitions.
12981 *
12982 * When the referenced table (pkrelid) is partitioned, the referencing
12983 * table (fkrel) has one pg_constraint row pointing to each partition
12984 * thereof. These rows are there only to support action triggers and no
12985 * table scan is needed, therefore skip this for them as well.
12986 */
12987 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
12988 con->confrelid == pkrelid)
12989 {
12990 NewConstraint *newcon;
12991 Constraint *fkconstraint;
12992
12993 /* Queue validation for phase 3 */
12994 fkconstraint = makeNode(Constraint);
12995 /* for now this is all we need */
12996 fkconstraint->conname = pstrdup(NameStr(con->conname));
12997
12998 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12999 newcon->name = fkconstraint->conname;
13000 newcon->contype = CONSTR_FOREIGN;
13001 newcon->refrelid = con->confrelid;
13002 newcon->refindid = con->conindid;
13003 newcon->conid = con->oid;
13004 newcon->qual = (Node *) fkconstraint;
13005
13006 /* Find or create work queue entry for this table */
13007 tab = ATGetQueueEntry(wqueue, fkrel);
13008 tab->constraints = lappend(tab->constraints, newcon);
13009 }
13010
13011 /*
13012 * If the table at either end of the constraint is partitioned, we need to
13013 * recurse and handle every unvalidate constraint that is a child of this
13014 * constraint.
13015 */
13016 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13017 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13018 {
13019 ScanKeyData pkey;
13020 SysScanDesc pscan;
13021 HeapTuple childtup;
13022
13023 ScanKeyInit(&pkey,
13024 Anum_pg_constraint_conparentid,
13025 BTEqualStrategyNumber, F_OIDEQ,
13026 ObjectIdGetDatum(con->oid));
13027
13028 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13029 true, NULL, 1, &pkey);
13030
13031 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13032 {
13033 Form_pg_constraint childcon;
13034 Relation childrel;
13035
13036 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13037
13038 /*
13039 * If the child constraint has already been validated, no further
13040 * action is required for it or its descendants, as they are all
13041 * valid.
13042 */
13043 if (childcon->convalidated)
13044 continue;
13045
13046 childrel = table_open(childcon->conrelid, lockmode);
13047
13048 /*
13049 * NB: Note that pkrelid should be passed as-is during recursion,
13050 * as it is required to identify the root referenced table.
13051 */
13052 QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13053 childtup, lockmode);
13054 table_close(childrel, NoLock);
13055 }
13056
13057 systable_endscan(pscan);
13058 }
13059
13060 /*
13061 * Now mark the pg_constraint row as validated (even if we didn't check,
13062 * notably the ones for partitions on the referenced side).
13063 *
13064 * We rely on transaction abort to roll back this change if phase 3
13065 * ultimately finds violating rows. This is a bit ugly.
13066 */
13067 copyTuple = heap_copytuple(contuple);
13068 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13069 copy_con->convalidated = true;
13070 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13071
13072 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13073
13074 heap_freetuple(copyTuple);
13075}
13076
13077/*
13078 * QueueCheckConstraintValidation
13079 *
13080 * Add an entry to the wqueue to validate the given check constraint in Phase 3
13081 * and update the convalidated field in the pg_constraint catalog for the
13082 * specified relation and all its inheriting children.
13083 */
13084static void
13086 char *constrName, HeapTuple contuple,
13087 bool recurse, bool recursing, LOCKMODE lockmode)
13088{
13090 AlteredTableInfo *tab;
13091 HeapTuple copyTuple;
13092 Form_pg_constraint copy_con;
13093
13094 List *children = NIL;
13095 ListCell *child;
13096 NewConstraint *newcon;
13097 Datum val;
13098 char *conbin;
13099
13100 con = (Form_pg_constraint) GETSTRUCT(contuple);
13101 Assert(con->contype == CONSTRAINT_CHECK);
13102
13103 /*
13104 * If we're recursing, the parent has already done this, so skip it. Also,
13105 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13106 * for it in the children.
13107 */
13108 if (!recursing && !con->connoinherit)
13109 children = find_all_inheritors(RelationGetRelid(rel),
13110 lockmode, NULL);
13111
13112 /*
13113 * For CHECK constraints, we must ensure that we only mark the constraint
13114 * as validated on the parent if it's already validated on the children.
13115 *
13116 * We recurse before validating on the parent, to reduce risk of
13117 * deadlocks.
13118 */
13119 foreach(child, children)
13120 {
13121 Oid childoid = lfirst_oid(child);
13122 Relation childrel;
13123
13124 if (childoid == RelationGetRelid(rel))
13125 continue;
13126
13127 /*
13128 * If we are told not to recurse, there had better not be any child
13129 * tables, because we can't mark the constraint on the parent valid
13130 * unless it is valid for all child tables.
13131 */
13132 if (!recurse)
13133 ereport(ERROR,
13134 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13135 errmsg("constraint must be validated on child tables too")));
13136
13137 /* find_all_inheritors already got lock */
13138 childrel = table_open(childoid, NoLock);
13139
13140 ATExecValidateConstraint(wqueue, childrel, constrName, false,
13141 true, lockmode);
13142 table_close(childrel, NoLock);
13143 }
13144
13145 /* Queue validation for phase 3 */
13146 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13147 newcon->name = constrName;
13148 newcon->contype = CONSTR_CHECK;
13149 newcon->refrelid = InvalidOid;
13150 newcon->refindid = InvalidOid;
13151 newcon->conid = con->oid;
13152
13153 val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13154 Anum_pg_constraint_conbin);
13155 conbin = TextDatumGetCString(val);
13156 newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13157
13158 /* Find or create work queue entry for this table */
13159 tab = ATGetQueueEntry(wqueue, rel);
13160 tab->constraints = lappend(tab->constraints, newcon);
13161
13162 /*
13163 * Invalidate relcache so that others see the new validated constraint.
13164 */
13166
13167 /*
13168 * Now update the catalog, while we have the door open.
13169 */
13170 copyTuple = heap_copytuple(contuple);
13171 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13172 copy_con->convalidated = true;
13173 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13174
13175 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13176
13177 heap_freetuple(copyTuple);
13178}
13179
13180/*
13181 * QueueNNConstraintValidation
13182 *
13183 * Add an entry to the wqueue to validate the given not-null constraint in
13184 * Phase 3 and update the convalidated field in the pg_constraint catalog for
13185 * the specified relation and all its inheriting children.
13186 */
13187static void
13189 HeapTuple contuple, bool recurse, bool recursing,
13190 LOCKMODE lockmode)
13191{
13193 AlteredTableInfo *tab;
13194 HeapTuple copyTuple;
13195 Form_pg_constraint copy_con;
13196 List *children = NIL;
13198 char *colname;
13199
13200 con = (Form_pg_constraint) GETSTRUCT(contuple);
13201 Assert(con->contype == CONSTRAINT_NOTNULL);
13202
13203 attnum = extractNotNullColumn(contuple);
13204
13205 /*
13206 * If we're recursing, we've already done this for parent, so skip it.
13207 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13208 * look for it in the children.
13209 *
13210 * We recurse before validating on the parent, to reduce risk of
13211 * deadlocks.
13212 */
13213 if (!recursing && !con->connoinherit)
13214 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13215
13216 colname = get_attname(RelationGetRelid(rel), attnum, false);
13217 foreach_oid(childoid, children)
13218 {
13219 Relation childrel;
13220 HeapTuple contup;
13221 Form_pg_constraint childcon;
13222 char *conname;
13223
13224 if (childoid == RelationGetRelid(rel))
13225 continue;
13226
13227 /*
13228 * If we are told not to recurse, there had better not be any child
13229 * tables, because we can't mark the constraint on the parent valid
13230 * unless it is valid for all child tables.
13231 */
13232 if (!recurse)
13233 ereport(ERROR,
13234 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13235 errmsg("constraint must be validated on child tables too"));
13236
13237 /*
13238 * The column on child might have a different attnum, so search by
13239 * column name.
13240 */
13241 contup = findNotNullConstraint(childoid, colname);
13242 if (!contup)
13243 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13244 colname, get_rel_name(childoid));
13245 childcon = (Form_pg_constraint) GETSTRUCT(contup);
13246 if (childcon->convalidated)
13247 continue;
13248
13249 /* find_all_inheritors already got lock */
13250 childrel = table_open(childoid, NoLock);
13251 conname = pstrdup(NameStr(childcon->conname));
13252
13253 /* XXX improve ATExecValidateConstraint API to avoid double search */
13254 ATExecValidateConstraint(wqueue, childrel, conname,
13255 false, true, lockmode);
13256 table_close(childrel, NoLock);
13257 }
13258
13259 /* Set attnotnull appropriately without queueing another validation */
13260 set_attnotnull(NULL, rel, attnum, true, false);
13261
13262 tab = ATGetQueueEntry(wqueue, rel);
13263 tab->verify_new_notnull = true;
13264
13265 /*
13266 * Invalidate relcache so that others see the new validated constraint.
13267 */
13269
13270 /*
13271 * Now update the catalogs, while we have the door open.
13272 */
13273 copyTuple = heap_copytuple(contuple);
13274 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13275 copy_con->convalidated = true;
13276 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13277
13278 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13279
13280 heap_freetuple(copyTuple);
13281}
13282
13283/*
13284 * transformColumnNameList - transform list of column names
13285 *
13286 * Lookup each name and return its attnum and, optionally, type and collation
13287 * OIDs
13288 *
13289 * Note: the name of this function suggests that it's general-purpose,
13290 * but actually it's only used to look up names appearing in foreign-key
13291 * clauses. The error messages would need work to use it in other cases,
13292 * and perhaps the validity checks as well.
13293 */
13294static int
13296 int16 *attnums, Oid *atttypids, Oid *attcollids)
13297{
13298 ListCell *l;
13299 int attnum;
13300
13301 attnum = 0;
13302 foreach(l, colList)
13303 {
13304 char *attname = strVal(lfirst(l));
13305 HeapTuple atttuple;
13306 Form_pg_attribute attform;
13307
13308 atttuple = SearchSysCacheAttName(relId, attname);
13309 if (!HeapTupleIsValid(atttuple))
13310 ereport(ERROR,
13311 (errcode(ERRCODE_UNDEFINED_COLUMN),
13312 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13313 attname)));
13314 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13315 if (attform->attnum < 0)
13316 ereport(ERROR,
13317 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13318 errmsg("system columns cannot be used in foreign keys")));
13319 if (attnum >= INDEX_MAX_KEYS)
13320 ereport(ERROR,
13321 (errcode(ERRCODE_TOO_MANY_COLUMNS),
13322 errmsg("cannot have more than %d keys in a foreign key",
13323 INDEX_MAX_KEYS)));
13324 attnums[attnum] = attform->attnum;
13325 if (atttypids != NULL)
13326 atttypids[attnum] = attform->atttypid;
13327 if (attcollids != NULL)
13328 attcollids[attnum] = attform->attcollation;
13329 ReleaseSysCache(atttuple);
13330 attnum++;
13331 }
13332
13333 return attnum;
13334}
13335
13336/*
13337 * transformFkeyGetPrimaryKey -
13338 *
13339 * Look up the names, attnums, types, and collations of the primary key attributes
13340 * for the pkrel. Also return the index OID and index opclasses of the
13341 * index supporting the primary key. Also return whether the index has
13342 * WITHOUT OVERLAPS.
13343 *
13344 * All parameters except pkrel are output parameters. Also, the function
13345 * return value is the number of attributes in the primary key.
13346 *
13347 * Used when the column list in the REFERENCES specification is omitted.
13348 */
13349static int
13351 List **attnamelist,
13352 int16 *attnums, Oid *atttypids, Oid *attcollids,
13353 Oid *opclasses, bool *pk_has_without_overlaps)
13354{
13355 List *indexoidlist;
13356 ListCell *indexoidscan;
13357 HeapTuple indexTuple = NULL;
13358 Form_pg_index indexStruct = NULL;
13359 Datum indclassDatum;
13360 oidvector *indclass;
13361 int i;
13362
13363 /*
13364 * Get the list of index OIDs for the table from the relcache, and look up
13365 * each one in the pg_index syscache until we find one marked primary key
13366 * (hopefully there isn't more than one such). Insist it's valid, too.
13367 */
13368 *indexOid = InvalidOid;
13369
13370 indexoidlist = RelationGetIndexList(pkrel);
13371
13372 foreach(indexoidscan, indexoidlist)
13373 {
13374 Oid indexoid = lfirst_oid(indexoidscan);
13375
13376 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13377 if (!HeapTupleIsValid(indexTuple))
13378 elog(ERROR, "cache lookup failed for index %u", indexoid);
13379 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13380 if (indexStruct->indisprimary && indexStruct->indisvalid)
13381 {
13382 /*
13383 * Refuse to use a deferrable primary key. This is per SQL spec,
13384 * and there would be a lot of interesting semantic problems if we
13385 * tried to allow it.
13386 */
13387 if (!indexStruct->indimmediate)
13388 ereport(ERROR,
13389 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13390 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13391 RelationGetRelationName(pkrel))));
13392
13393 *indexOid = indexoid;
13394 break;
13395 }
13396 ReleaseSysCache(indexTuple);
13397 }
13398
13399 list_free(indexoidlist);
13400
13401 /*
13402 * Check that we found it
13403 */
13404 if (!OidIsValid(*indexOid))
13405 ereport(ERROR,
13406 (errcode(ERRCODE_UNDEFINED_OBJECT),
13407 errmsg("there is no primary key for referenced table \"%s\"",
13408 RelationGetRelationName(pkrel))));
13409
13410 /* Must get indclass the hard way */
13411 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13412 Anum_pg_index_indclass);
13413 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13414
13415 /*
13416 * Now build the list of PK attributes from the indkey definition (we
13417 * assume a primary key cannot have expressional elements)
13418 */
13419 *attnamelist = NIL;
13420 for (i = 0; i < indexStruct->indnkeyatts; i++)
13421 {
13422 int pkattno = indexStruct->indkey.values[i];
13423
13424 attnums[i] = pkattno;
13425 atttypids[i] = attnumTypeId(pkrel, pkattno);
13426 attcollids[i] = attnumCollationId(pkrel, pkattno);
13427 opclasses[i] = indclass->values[i];
13428 *attnamelist = lappend(*attnamelist,
13429 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13430 }
13431
13432 *pk_has_without_overlaps = indexStruct->indisexclusion;
13433
13434 ReleaseSysCache(indexTuple);
13435
13436 return i;
13437}
13438
13439/*
13440 * transformFkeyCheckAttrs -
13441 *
13442 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13443 * reference as part of a foreign key constraint.
13444 *
13445 * Returns the OID of the unique index supporting the constraint and
13446 * populates the caller-provided 'opclasses' array with the opclasses
13447 * associated with the index columns. Also sets whether the index
13448 * uses WITHOUT OVERLAPS.
13449 *
13450 * Raises an ERROR on validation failure.
13451 */
13452static Oid
13454 int numattrs, int16 *attnums,
13455 bool with_period, Oid *opclasses,
13456 bool *pk_has_without_overlaps)
13457{
13458 Oid indexoid = InvalidOid;
13459 bool found = false;
13460 bool found_deferrable = false;
13461 List *indexoidlist;
13462 ListCell *indexoidscan;
13463 int i,
13464 j;
13465
13466 /*
13467 * Reject duplicate appearances of columns in the referenced-columns list.
13468 * Such a case is forbidden by the SQL standard, and even if we thought it
13469 * useful to allow it, there would be ambiguity about how to match the
13470 * list to unique indexes (in particular, it'd be unclear which index
13471 * opclass goes with which FK column).
13472 */
13473 for (i = 0; i < numattrs; i++)
13474 {
13475 for (j = i + 1; j < numattrs; j++)
13476 {
13477 if (attnums[i] == attnums[j])
13478 ereport(ERROR,
13479 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13480 errmsg("foreign key referenced-columns list must not contain duplicates")));
13481 }
13482 }
13483
13484 /*
13485 * Get the list of index OIDs for the table from the relcache, and look up
13486 * each one in the pg_index syscache, and match unique indexes to the list
13487 * of attnums we are given.
13488 */
13489 indexoidlist = RelationGetIndexList(pkrel);
13490
13491 foreach(indexoidscan, indexoidlist)
13492 {
13493 HeapTuple indexTuple;
13494 Form_pg_index indexStruct;
13495
13496 indexoid = lfirst_oid(indexoidscan);
13497 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13498 if (!HeapTupleIsValid(indexTuple))
13499 elog(ERROR, "cache lookup failed for index %u", indexoid);
13500 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13501
13502 /*
13503 * Must have the right number of columns; must be unique (or if
13504 * temporal then exclusion instead) and not a partial index; forget it
13505 * if there are any expressions, too. Invalid indexes are out as well.
13506 */
13507 if (indexStruct->indnkeyatts == numattrs &&
13508 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13509 indexStruct->indisvalid &&
13510 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13511 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13512 {
13513 Datum indclassDatum;
13514 oidvector *indclass;
13515
13516 /* Must get indclass the hard way */
13517 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13518 Anum_pg_index_indclass);
13519 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13520
13521 /*
13522 * The given attnum list may match the index columns in any order.
13523 * Check for a match, and extract the appropriate opclasses while
13524 * we're at it.
13525 *
13526 * We know that attnums[] is duplicate-free per the test at the
13527 * start of this function, and we checked above that the number of
13528 * index columns agrees, so if we find a match for each attnums[]
13529 * entry then we must have a one-to-one match in some order.
13530 */
13531 for (i = 0; i < numattrs; i++)
13532 {
13533 found = false;
13534 for (j = 0; j < numattrs; j++)
13535 {
13536 if (attnums[i] == indexStruct->indkey.values[j])
13537 {
13538 opclasses[i] = indclass->values[j];
13539 found = true;
13540 break;
13541 }
13542 }
13543 if (!found)
13544 break;
13545 }
13546 /* The last attribute in the index must be the PERIOD FK part */
13547 if (found && with_period)
13548 {
13549 int16 periodattnum = attnums[numattrs - 1];
13550
13551 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13552 }
13553
13554 /*
13555 * Refuse to use a deferrable unique/primary key. This is per SQL
13556 * spec, and there would be a lot of interesting semantic problems
13557 * if we tried to allow it.
13558 */
13559 if (found && !indexStruct->indimmediate)
13560 {
13561 /*
13562 * Remember that we found an otherwise matching index, so that
13563 * we can generate a more appropriate error message.
13564 */
13565 found_deferrable = true;
13566 found = false;
13567 }
13568
13569 /* We need to know whether the index has WITHOUT OVERLAPS */
13570 if (found)
13571 *pk_has_without_overlaps = indexStruct->indisexclusion;
13572 }
13573 ReleaseSysCache(indexTuple);
13574 if (found)
13575 break;
13576 }
13577
13578 if (!found)
13579 {
13580 if (found_deferrable)
13581 ereport(ERROR,
13582 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13583 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13584 RelationGetRelationName(pkrel))));
13585 else
13586 ereport(ERROR,
13587 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13588 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13589 RelationGetRelationName(pkrel))));
13590 }
13591
13592 list_free(indexoidlist);
13593
13594 return indexoid;
13595}
13596
13597/*
13598 * findFkeyCast -
13599 *
13600 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13601 * Caller has equal regard for binary coercibility and for an exact match.
13602*/
13603static CoercionPathType
13604findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13605{
13606 CoercionPathType ret;
13607
13608 if (targetTypeId == sourceTypeId)
13609 {
13611 *funcid = InvalidOid;
13612 }
13613 else
13614 {
13615 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13616 COERCION_IMPLICIT, funcid);
13617 if (ret == COERCION_PATH_NONE)
13618 /* A previously-relied-upon cast is now gone. */
13619 elog(ERROR, "could not find cast from %u to %u",
13620 sourceTypeId, targetTypeId);
13621 }
13622
13623 return ret;
13624}
13625
13626/*
13627 * Permissions checks on the referenced table for ADD FOREIGN KEY
13628 *
13629 * Note: we have already checked that the user owns the referencing table,
13630 * else we'd have failed much earlier; no additional checks are needed for it.
13631 */
13632static void
13633checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13634{
13635 Oid roleid = GetUserId();
13636 AclResult aclresult;
13637 int i;
13638
13639 /* Okay if we have relation-level REFERENCES permission */
13640 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13642 if (aclresult == ACLCHECK_OK)
13643 return;
13644 /* Else we must have REFERENCES on each column */
13645 for (i = 0; i < natts; i++)
13646 {
13647 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13648 roleid, ACL_REFERENCES);
13649 if (aclresult != ACLCHECK_OK)
13650 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13652 }
13653}
13654
13655/*
13656 * Scan the existing rows in a table to verify they meet a proposed FK
13657 * constraint.
13658 *
13659 * Caller must have opened and locked both relations appropriately.
13660 */
13661static void
13663 Relation rel,
13664 Relation pkrel,
13665 Oid pkindOid,
13666 Oid constraintOid,
13667 bool hasperiod)
13668{
13669 TupleTableSlot *slot;
13670 TableScanDesc scan;
13671 Trigger trig = {0};
13672 Snapshot snapshot;
13673 MemoryContext oldcxt;
13674 MemoryContext perTupCxt;
13675
13677 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13678
13679 /*
13680 * Build a trigger call structure; we'll need it either way.
13681 */
13682 trig.tgoid = InvalidOid;
13683 trig.tgname = conname;
13685 trig.tgisinternal = true;
13686 trig.tgconstrrelid = RelationGetRelid(pkrel);
13687 trig.tgconstrindid = pkindOid;
13688 trig.tgconstraint = constraintOid;
13689 trig.tgdeferrable = false;
13690 trig.tginitdeferred = false;
13691 /* we needn't fill in remaining fields */
13692
13693 /*
13694 * See if we can do it with a single LEFT JOIN query. A false result
13695 * indicates we must proceed with the fire-the-trigger method. We can't do
13696 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13697 * left joins.
13698 */
13699 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13700 return;
13701
13702 /*
13703 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13704 * if that tuple had just been inserted. If any of those fail, it should
13705 * ereport(ERROR) and that's that.
13706 */
13707 snapshot = RegisterSnapshot(GetLatestSnapshot());
13708 slot = table_slot_create(rel, NULL);
13709 scan = table_beginscan(rel, snapshot, 0, NULL);
13710
13712 "validateForeignKeyConstraint",
13714 oldcxt = MemoryContextSwitchTo(perTupCxt);
13715
13716 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13717 {
13718 LOCAL_FCINFO(fcinfo, 0);
13719 TriggerData trigdata = {0};
13720
13722
13723 /*
13724 * Make a call to the trigger function
13725 *
13726 * No parameters are passed, but we do set a context
13727 */
13728 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13729
13730 /*
13731 * We assume RI_FKey_check_ins won't look at flinfo...
13732 */
13733 trigdata.type = T_TriggerData;
13735 trigdata.tg_relation = rel;
13736 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13737 trigdata.tg_trigslot = slot;
13738 trigdata.tg_trigger = &trig;
13739
13740 fcinfo->context = (Node *) &trigdata;
13741
13742 RI_FKey_check_ins(fcinfo);
13743
13744 MemoryContextReset(perTupCxt);
13745 }
13746
13747 MemoryContextSwitchTo(oldcxt);
13748 MemoryContextDelete(perTupCxt);
13749 table_endscan(scan);
13750 UnregisterSnapshot(snapshot);
13752}
13753
13754/*
13755 * CreateFKCheckTrigger
13756 * Creates the insert (on_insert=true) or update "check" trigger that
13757 * implements a given foreign key
13758 *
13759 * Returns the OID of the so created trigger.
13760 */
13761static Oid
13762CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13763 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13764 bool on_insert)
13765{
13766 ObjectAddress trigAddress;
13767 CreateTrigStmt *fk_trigger;
13768
13769 /*
13770 * Note: for a self-referential FK (referencing and referenced tables are
13771 * the same), it is important that the ON UPDATE action fires before the
13772 * CHECK action, since both triggers will fire on the same row during an
13773 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13774 * state of the row. Triggers fire in name order, so we ensure this by
13775 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13776 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13777 */
13778 fk_trigger = makeNode(CreateTrigStmt);
13779 fk_trigger->replace = false;
13780 fk_trigger->isconstraint = true;
13781 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13782 fk_trigger->relation = NULL;
13783
13784 /* Either ON INSERT or ON UPDATE */
13785 if (on_insert)
13786 {
13787 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13788 fk_trigger->events = TRIGGER_TYPE_INSERT;
13789 }
13790 else
13791 {
13792 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13793 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13794 }
13795
13796 fk_trigger->args = NIL;
13797 fk_trigger->row = true;
13798 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13799 fk_trigger->columns = NIL;
13800 fk_trigger->whenClause = NULL;
13801 fk_trigger->transitionRels = NIL;
13802 fk_trigger->deferrable = fkconstraint->deferrable;
13803 fk_trigger->initdeferred = fkconstraint->initdeferred;
13804 fk_trigger->constrrel = NULL;
13805
13806 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13807 constraintOid, indexOid, InvalidOid,
13808 parentTrigOid, NULL, true, false);
13809
13810 /* Make changes-so-far visible */
13812
13813 return trigAddress.objectId;
13814}
13815
13816/*
13817 * createForeignKeyActionTriggers
13818 * Create the referenced-side "action" triggers that implement a foreign
13819 * key.
13820 *
13821 * Returns the OIDs of the so created triggers in *deleteTrigOid and
13822 * *updateTrigOid.
13823 */
13824static void
13825createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13826 Oid constraintOid, Oid indexOid,
13827 Oid parentDelTrigger, Oid parentUpdTrigger,
13828 Oid *deleteTrigOid, Oid *updateTrigOid)
13829{
13830 CreateTrigStmt *fk_trigger;
13831 ObjectAddress trigAddress;
13832
13833 /*
13834 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13835 * DELETE action on the referenced table.
13836 */
13837 fk_trigger = makeNode(CreateTrigStmt);
13838 fk_trigger->replace = false;
13839 fk_trigger->isconstraint = true;
13840 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13841 fk_trigger->relation = NULL;
13842 fk_trigger->args = NIL;
13843 fk_trigger->row = true;
13844 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13845 fk_trigger->events = TRIGGER_TYPE_DELETE;
13846 fk_trigger->columns = NIL;
13847 fk_trigger->whenClause = NULL;
13848 fk_trigger->transitionRels = NIL;
13849 fk_trigger->constrrel = NULL;
13850
13851 switch (fkconstraint->fk_del_action)
13852 {
13854 fk_trigger->deferrable = fkconstraint->deferrable;
13855 fk_trigger->initdeferred = fkconstraint->initdeferred;
13856 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13857 break;
13859 fk_trigger->deferrable = false;
13860 fk_trigger->initdeferred = false;
13861 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13862 break;
13864 fk_trigger->deferrable = false;
13865 fk_trigger->initdeferred = false;
13866 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13867 break;
13869 fk_trigger->deferrable = false;
13870 fk_trigger->initdeferred = false;
13871 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13872 break;
13874 fk_trigger->deferrable = false;
13875 fk_trigger->initdeferred = false;
13876 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13877 break;
13878 default:
13879 elog(ERROR, "unrecognized FK action type: %d",
13880 (int) fkconstraint->fk_del_action);
13881 break;
13882 }
13883
13884 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13885 constraintOid, indexOid, InvalidOid,
13886 parentDelTrigger, NULL, true, false);
13887 if (deleteTrigOid)
13888 *deleteTrigOid = trigAddress.objectId;
13889
13890 /* Make changes-so-far visible */
13892
13893 /*
13894 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13895 * UPDATE action on the referenced table.
13896 */
13897 fk_trigger = makeNode(CreateTrigStmt);
13898 fk_trigger->replace = false;
13899 fk_trigger->isconstraint = true;
13900 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13901 fk_trigger->relation = NULL;
13902 fk_trigger->args = NIL;
13903 fk_trigger->row = true;
13904 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13905 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13906 fk_trigger->columns = NIL;
13907 fk_trigger->whenClause = NULL;
13908 fk_trigger->transitionRels = NIL;
13909 fk_trigger->constrrel = NULL;
13910
13911 switch (fkconstraint->fk_upd_action)
13912 {
13914 fk_trigger->deferrable = fkconstraint->deferrable;
13915 fk_trigger->initdeferred = fkconstraint->initdeferred;
13916 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13917 break;
13919 fk_trigger->deferrable = false;
13920 fk_trigger->initdeferred = false;
13921 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13922 break;
13924 fk_trigger->deferrable = false;
13925 fk_trigger->initdeferred = false;
13926 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13927 break;
13929 fk_trigger->deferrable = false;
13930 fk_trigger->initdeferred = false;
13931 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13932 break;
13934 fk_trigger->deferrable = false;
13935 fk_trigger->initdeferred = false;
13936 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13937 break;
13938 default:
13939 elog(ERROR, "unrecognized FK action type: %d",
13940 (int) fkconstraint->fk_upd_action);
13941 break;
13942 }
13943
13944 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13945 constraintOid, indexOid, InvalidOid,
13946 parentUpdTrigger, NULL, true, false);
13947 if (updateTrigOid)
13948 *updateTrigOid = trigAddress.objectId;
13949}
13950
13951/*
13952 * createForeignKeyCheckTriggers
13953 * Create the referencing-side "check" triggers that implement a foreign
13954 * key.
13955 *
13956 * Returns the OIDs of the so created triggers in *insertTrigOid and
13957 * *updateTrigOid.
13958 */
13959static void
13961 Constraint *fkconstraint, Oid constraintOid,
13962 Oid indexOid,
13963 Oid parentInsTrigger, Oid parentUpdTrigger,
13964 Oid *insertTrigOid, Oid *updateTrigOid)
13965{
13966 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13967 constraintOid, indexOid,
13968 parentInsTrigger, true);
13969 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13970 constraintOid, indexOid,
13971 parentUpdTrigger, false);
13972}
13973
13974/*
13975 * ALTER TABLE DROP CONSTRAINT
13976 *
13977 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
13978 */
13979static void
13980ATExecDropConstraint(Relation rel, const char *constrName,
13981 DropBehavior behavior, bool recurse,
13982 bool missing_ok, LOCKMODE lockmode)
13983{
13984 Relation conrel;
13985 SysScanDesc scan;
13986 ScanKeyData skey[3];
13987 HeapTuple tuple;
13988 bool found = false;
13989
13990 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13991
13992 /*
13993 * Find and drop the target constraint
13994 */
13995 ScanKeyInit(&skey[0],
13996 Anum_pg_constraint_conrelid,
13997 BTEqualStrategyNumber, F_OIDEQ,
13999 ScanKeyInit(&skey[1],
14000 Anum_pg_constraint_contypid,
14001 BTEqualStrategyNumber, F_OIDEQ,
14003 ScanKeyInit(&skey[2],
14004 Anum_pg_constraint_conname,
14005 BTEqualStrategyNumber, F_NAMEEQ,
14006 CStringGetDatum(constrName));
14007 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14008 true, NULL, 3, skey);
14009
14010 /* There can be at most one matching row */
14011 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14012 {
14013 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14014 missing_ok, lockmode);
14015 found = true;
14016 }
14017
14018 systable_endscan(scan);
14019
14020 if (!found)
14021 {
14022 if (!missing_ok)
14023 ereport(ERROR,
14024 errcode(ERRCODE_UNDEFINED_OBJECT),
14025 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14026 constrName, RelationGetRelationName(rel)));
14027 else
14029 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14030 constrName, RelationGetRelationName(rel)));
14031 }
14032
14034}
14035
14036/*
14037 * Remove a constraint, using its pg_constraint tuple
14038 *
14039 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14040 * DROP NOT NULL.
14041 *
14042 * Returns the address of the constraint being removed.
14043 */
14044static ObjectAddress
14046 bool recurse, bool recursing, bool missing_ok,
14047 LOCKMODE lockmode)
14048{
14049 Relation conrel;
14051 ObjectAddress conobj;
14052 List *children;
14053 bool is_no_inherit_constraint = false;
14054 char *constrName;
14055 char *colname = NULL;
14056
14057 /* Guard against stack overflow due to overly deep inheritance tree. */
14059
14060 /* At top level, permission check was done in ATPrepCmd, else do it */
14061 if (recursing)
14064
14065 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14066
14067 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14068 constrName = NameStr(con->conname);
14069
14070 /* Don't allow drop of inherited constraints */
14071 if (con->coninhcount > 0 && !recursing)
14072 ereport(ERROR,
14073 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14074 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14075 constrName, RelationGetRelationName(rel))));
14076
14077 /*
14078 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14079 *
14080 * While doing that, we're in a good position to disallow dropping a not-
14081 * null constraint underneath a primary key, a replica identity index, or
14082 * a generated identity column.
14083 */
14084 if (con->contype == CONSTRAINT_NOTNULL)
14085 {
14086 Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14087 AttrNumber attnum = extractNotNullColumn(constraintTup);
14088 Bitmapset *pkattrs;
14089 Bitmapset *irattrs;
14090 HeapTuple atttup;
14091 Form_pg_attribute attForm;
14092
14093 /* save column name for recursion step */
14094 colname = get_attname(RelationGetRelid(rel), attnum, false);
14095
14096 /*
14097 * Disallow if it's in the primary key. For partitioned tables we
14098 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14099 * return NULL if the primary key is invalid; but we still need to
14100 * protect not-null constraints under such a constraint, so check the
14101 * slow way.
14102 */
14104
14105 if (pkattrs == NULL &&
14106 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14107 {
14108 Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14109
14110 if (OidIsValid(pkindex))
14111 {
14112 Relation pk = relation_open(pkindex, AccessShareLock);
14113
14114 pkattrs = NULL;
14115 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14116 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14117
14119 }
14120 }
14121
14122 if (pkattrs &&
14124 ereport(ERROR,
14125 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14126 errmsg("column \"%s\" is in a primary key",
14127 get_attname(RelationGetRelid(rel), attnum, false)));
14128
14129 /* Disallow if it's in the replica identity */
14132 ereport(ERROR,
14133 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14134 errmsg("column \"%s\" is in index used as replica identity",
14135 get_attname(RelationGetRelid(rel), attnum, false)));
14136
14137 /* Disallow if it's a GENERATED AS IDENTITY column */
14139 if (!HeapTupleIsValid(atttup))
14140 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14141 attnum, RelationGetRelid(rel));
14142 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14143 if (attForm->attidentity != '\0')
14144 ereport(ERROR,
14145 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14146 errmsg("column \"%s\" of relation \"%s\" is an identity column",
14148 false),
14150
14151 /* All good -- reset attnotnull if needed */
14152 if (attForm->attnotnull)
14153 {
14154 attForm->attnotnull = false;
14155 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14156 }
14157
14159 }
14160
14161 is_no_inherit_constraint = con->connoinherit;
14162
14163 /*
14164 * If it's a foreign-key constraint, we'd better lock the referenced table
14165 * and check that that's not in use, just as we've already done for the
14166 * constrained table (else we might, eg, be dropping a trigger that has
14167 * unfired events). But we can/must skip that in the self-referential
14168 * case.
14169 */
14170 if (con->contype == CONSTRAINT_FOREIGN &&
14171 con->confrelid != RelationGetRelid(rel))
14172 {
14173 Relation frel;
14174
14175 /* Must match lock taken by RemoveTriggerById: */
14176 frel = table_open(con->confrelid, AccessExclusiveLock);
14178 table_close(frel, NoLock);
14179 }
14180
14181 /*
14182 * Perform the actual constraint deletion
14183 */
14184 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14185 performDeletion(&conobj, behavior, 0);
14186
14187 /*
14188 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14189 * are dropped via the dependency mechanism, so we're done here.
14190 */
14191 if (con->contype != CONSTRAINT_CHECK &&
14192 con->contype != CONSTRAINT_NOTNULL &&
14193 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14194 {
14196 return conobj;
14197 }
14198
14199 /*
14200 * Propagate to children as appropriate. Unlike most other ALTER
14201 * routines, we have to do this one level of recursion at a time; we can't
14202 * use find_all_inheritors to do it in one pass.
14203 */
14204 if (!is_no_inherit_constraint)
14205 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14206 else
14207 children = NIL;
14208
14209 foreach_oid(childrelid, children)
14210 {
14211 Relation childrel;
14212 HeapTuple tuple;
14213 Form_pg_constraint childcon;
14214
14215 /* find_inheritance_children already got lock */
14216 childrel = table_open(childrelid, NoLock);
14217 CheckAlterTableIsSafe(childrel);
14218
14219 /*
14220 * We search for not-null constraints by column name, and others by
14221 * constraint name.
14222 */
14223 if (con->contype == CONSTRAINT_NOTNULL)
14224 {
14225 tuple = findNotNullConstraint(childrelid, colname);
14226 if (!HeapTupleIsValid(tuple))
14227 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14228 colname, RelationGetRelid(childrel));
14229 }
14230 else
14231 {
14232 SysScanDesc scan;
14233 ScanKeyData skey[3];
14234
14235 ScanKeyInit(&skey[0],
14236 Anum_pg_constraint_conrelid,
14237 BTEqualStrategyNumber, F_OIDEQ,
14238 ObjectIdGetDatum(childrelid));
14239 ScanKeyInit(&skey[1],
14240 Anum_pg_constraint_contypid,
14241 BTEqualStrategyNumber, F_OIDEQ,
14243 ScanKeyInit(&skey[2],
14244 Anum_pg_constraint_conname,
14245 BTEqualStrategyNumber, F_NAMEEQ,
14246 CStringGetDatum(constrName));
14247 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14248 true, NULL, 3, skey);
14249 /* There can only be one, so no need to loop */
14250 tuple = systable_getnext(scan);
14251 if (!HeapTupleIsValid(tuple))
14252 ereport(ERROR,
14253 (errcode(ERRCODE_UNDEFINED_OBJECT),
14254 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14255 constrName,
14256 RelationGetRelationName(childrel))));
14257 tuple = heap_copytuple(tuple);
14258 systable_endscan(scan);
14259 }
14260
14261 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14262
14263 /* Right now only CHECK and not-null constraints can be inherited */
14264 if (childcon->contype != CONSTRAINT_CHECK &&
14265 childcon->contype != CONSTRAINT_NOTNULL)
14266 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14267
14268 if (childcon->coninhcount <= 0) /* shouldn't happen */
14269 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14270 childrelid, NameStr(childcon->conname));
14271
14272 if (recurse)
14273 {
14274 /*
14275 * If the child constraint has other definition sources, just
14276 * decrement its inheritance count; if not, recurse to delete it.
14277 */
14278 if (childcon->coninhcount == 1 && !childcon->conislocal)
14279 {
14280 /* Time to delete this child constraint, too */
14281 dropconstraint_internal(childrel, tuple, behavior,
14282 recurse, true, missing_ok,
14283 lockmode);
14284 }
14285 else
14286 {
14287 /* Child constraint must survive my deletion */
14288 childcon->coninhcount--;
14289 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14290
14291 /* Make update visible */
14293 }
14294 }
14295 else
14296 {
14297 /*
14298 * If we were told to drop ONLY in this table (no recursion) and
14299 * there are no further parents for this constraint, we need to
14300 * mark the inheritors' constraints as locally defined rather than
14301 * inherited.
14302 */
14303 childcon->coninhcount--;
14304 if (childcon->coninhcount == 0)
14305 childcon->conislocal = true;
14306
14307 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14308
14309 /* Make update visible */
14311 }
14312
14313 heap_freetuple(tuple);
14314
14315 table_close(childrel, NoLock);
14316 }
14317
14319
14320 return conobj;
14321}
14322
14323/*
14324 * ALTER COLUMN TYPE
14325 *
14326 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14327 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14328 * transformed (and must be, because we rely on some transformed fields).
14329 *
14330 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14331 * table will be done "in parallel" during phase 3, so all the USING
14332 * expressions should be parsed assuming the original column types. Also,
14333 * this allows a USING expression to refer to a field that will be dropped.
14334 *
14335 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14336 * the first two execution steps in phase 2; they must not see the effects
14337 * of any other subcommand types, since the USING expressions are parsed
14338 * against the unmodified table's state.
14339 */
14340static void
14342 AlteredTableInfo *tab, Relation rel,
14343 bool recurse, bool recursing,
14344 AlterTableCmd *cmd, LOCKMODE lockmode,
14345 AlterTableUtilityContext *context)
14346{
14347 char *colName = cmd->name;
14348 ColumnDef *def = (ColumnDef *) cmd->def;
14349 TypeName *typeName = def->typeName;
14350 Node *transform = def->cooked_default;
14351 HeapTuple tuple;
14352 Form_pg_attribute attTup;
14354 Oid targettype;
14355 int32 targettypmod;
14356 Oid targetcollid;
14358 ParseState *pstate = make_parsestate(NULL);
14359 AclResult aclresult;
14360 bool is_expr;
14361
14362 pstate->p_sourcetext = context->queryString;
14363
14364 if (rel->rd_rel->reloftype && !recursing)
14365 ereport(ERROR,
14366 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14367 errmsg("cannot alter column type of typed table"),
14368 parser_errposition(pstate, def->location)));
14369
14370 /* lookup the attribute so we can check inheritance status */
14371 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14372 if (!HeapTupleIsValid(tuple))
14373 ereport(ERROR,
14374 (errcode(ERRCODE_UNDEFINED_COLUMN),
14375 errmsg("column \"%s\" of relation \"%s\" does not exist",
14376 colName, RelationGetRelationName(rel)),
14377 parser_errposition(pstate, def->location)));
14378 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14379 attnum = attTup->attnum;
14380
14381 /* Can't alter a system attribute */
14382 if (attnum <= 0)
14383 ereport(ERROR,
14384 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14385 errmsg("cannot alter system column \"%s\"", colName),
14386 parser_errposition(pstate, def->location)));
14387
14388 /*
14389 * Cannot specify USING when altering type of a generated column, because
14390 * that would violate the generation expression.
14391 */
14392 if (attTup->attgenerated && def->cooked_default)
14393 ereport(ERROR,
14394 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14395 errmsg("cannot specify USING when altering type of generated column"),
14396 errdetail("Column \"%s\" is a generated column.", colName),
14397 parser_errposition(pstate, def->location)));
14398
14399 /*
14400 * Don't alter inherited columns. At outer level, there had better not be
14401 * any inherited definition; when recursing, we assume this was checked at
14402 * the parent level (see below).
14403 */
14404 if (attTup->attinhcount > 0 && !recursing)
14405 ereport(ERROR,
14406 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14407 errmsg("cannot alter inherited column \"%s\"", colName),
14408 parser_errposition(pstate, def->location)));
14409
14410 /* Don't alter columns used in the partition key */
14411 if (has_partition_attrs(rel,
14413 &is_expr))
14414 ereport(ERROR,
14415 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14416 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14417 colName, RelationGetRelationName(rel)),
14418 parser_errposition(pstate, def->location)));
14419
14420 /* Look up the target type */
14421 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14422
14423 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14424 if (aclresult != ACLCHECK_OK)
14425 aclcheck_error_type(aclresult, targettype);
14426
14427 /* And the collation */
14428 targetcollid = GetColumnDefCollation(pstate, def, targettype);
14429
14430 /* make sure datatype is legal for a column */
14431 CheckAttributeType(colName, targettype, targetcollid,
14432 list_make1_oid(rel->rd_rel->reltype),
14433 (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14434
14435 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14436 {
14437 /* do nothing */
14438 }
14439 else if (tab->relkind == RELKIND_RELATION ||
14440 tab->relkind == RELKIND_PARTITIONED_TABLE)
14441 {
14442 /*
14443 * Set up an expression to transform the old data value to the new
14444 * type. If a USING option was given, use the expression as
14445 * transformed by transformAlterTableStmt, else just take the old
14446 * value and try to coerce it. We do this first so that type
14447 * incompatibility can be detected before we waste effort, and because
14448 * we need the expression to be parsed against the original table row
14449 * type.
14450 */
14451 if (!transform)
14452 {
14453 transform = (Node *) makeVar(1, attnum,
14454 attTup->atttypid, attTup->atttypmod,
14455 attTup->attcollation,
14456 0);
14457 }
14458
14459 transform = coerce_to_target_type(pstate,
14460 transform, exprType(transform),
14461 targettype, targettypmod,
14464 -1);
14465 if (transform == NULL)
14466 {
14467 /* error text depends on whether USING was specified or not */
14468 if (def->cooked_default != NULL)
14469 ereport(ERROR,
14470 (errcode(ERRCODE_DATATYPE_MISMATCH),
14471 errmsg("result of USING clause for column \"%s\""
14472 " cannot be cast automatically to type %s",
14473 colName, format_type_be(targettype)),
14474 errhint("You might need to add an explicit cast.")));
14475 else
14476 ereport(ERROR,
14477 (errcode(ERRCODE_DATATYPE_MISMATCH),
14478 errmsg("column \"%s\" cannot be cast automatically to type %s",
14479 colName, format_type_be(targettype)),
14480 !attTup->attgenerated ?
14481 /* translator: USING is SQL, don't translate it */
14482 errhint("You might need to specify \"USING %s::%s\".",
14483 quote_identifier(colName),
14484 format_type_with_typemod(targettype,
14485 targettypmod)) : 0));
14486 }
14487
14488 /* Fix collations after all else */
14489 assign_expr_collations(pstate, transform);
14490
14491 /* Expand virtual generated columns in the expr. */
14492 transform = expand_generated_columns_in_expr(transform, rel, 1);
14493
14494 /* Plan the expr now so we can accurately assess the need to rewrite. */
14495 transform = (Node *) expression_planner((Expr *) transform);
14496
14497 /*
14498 * Add a work queue item to make ATRewriteTable update the column
14499 * contents.
14500 */
14502 newval->attnum = attnum;
14503 newval->expr = (Expr *) transform;
14504 newval->is_generated = false;
14505
14506 tab->newvals = lappend(tab->newvals, newval);
14507 if (ATColumnChangeRequiresRewrite(transform, attnum))
14509 }
14510 else if (transform)
14511 ereport(ERROR,
14512 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14513 errmsg("\"%s\" is not a table",
14515
14516 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14517 {
14518 /*
14519 * For relations or columns without storage, do this check now.
14520 * Regular tables will check it later when the table is being
14521 * rewritten.
14522 */
14523 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14524 }
14525
14526 ReleaseSysCache(tuple);
14527
14528 /*
14529 * Recurse manually by queueing a new command for each child, if
14530 * necessary. We cannot apply ATSimpleRecursion here because we need to
14531 * remap attribute numbers in the USING expression, if any.
14532 *
14533 * If we are told not to recurse, there had better not be any child
14534 * tables; else the alter would put them out of step.
14535 */
14536 if (recurse)
14537 {
14538 Oid relid = RelationGetRelid(rel);
14539 List *child_oids,
14540 *child_numparents;
14541 ListCell *lo,
14542 *li;
14543
14544 child_oids = find_all_inheritors(relid, lockmode,
14545 &child_numparents);
14546
14547 /*
14548 * find_all_inheritors does the recursive search of the inheritance
14549 * hierarchy, so all we have to do is process all of the relids in the
14550 * list that it returns.
14551 */
14552 forboth(lo, child_oids, li, child_numparents)
14553 {
14554 Oid childrelid = lfirst_oid(lo);
14555 int numparents = lfirst_int(li);
14556 Relation childrel;
14557 HeapTuple childtuple;
14558 Form_pg_attribute childattTup;
14559
14560 if (childrelid == relid)
14561 continue;
14562
14563 /* find_all_inheritors already got lock */
14564 childrel = relation_open(childrelid, NoLock);
14565 CheckAlterTableIsSafe(childrel);
14566
14567 /*
14568 * Verify that the child doesn't have any inherited definitions of
14569 * this column that came from outside this inheritance hierarchy.
14570 * (renameatt makes a similar test, though in a different way
14571 * because of its different recursion mechanism.)
14572 */
14573 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14574 colName);
14575 if (!HeapTupleIsValid(childtuple))
14576 ereport(ERROR,
14577 (errcode(ERRCODE_UNDEFINED_COLUMN),
14578 errmsg("column \"%s\" of relation \"%s\" does not exist",
14579 colName, RelationGetRelationName(childrel))));
14580 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14581
14582 if (childattTup->attinhcount > numparents)
14583 ereport(ERROR,
14584 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14585 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14586 colName, RelationGetRelationName(childrel))));
14587
14588 ReleaseSysCache(childtuple);
14589
14590 /*
14591 * Remap the attribute numbers. If no USING expression was
14592 * specified, there is no need for this step.
14593 */
14594 if (def->cooked_default)
14595 {
14596 AttrMap *attmap;
14597 bool found_whole_row;
14598
14599 /* create a copy to scribble on */
14600 cmd = copyObject(cmd);
14601
14602 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14603 RelationGetDescr(rel),
14604 false);
14605 ((ColumnDef *) cmd->def)->cooked_default =
14607 1, 0,
14608 attmap,
14609 InvalidOid, &found_whole_row);
14610 if (found_whole_row)
14611 ereport(ERROR,
14612 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14613 errmsg("cannot convert whole-row table reference"),
14614 errdetail("USING expression contains a whole-row table reference.")));
14615 pfree(attmap);
14616 }
14617 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14618 relation_close(childrel, NoLock);
14619 }
14620 }
14621 else if (!recursing &&
14623 ereport(ERROR,
14624 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14625 errmsg("type of inherited column \"%s\" must be changed in child tables too",
14626 colName)));
14627
14628 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14629 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14630}
14631
14632/*
14633 * When the data type of a column is changed, a rewrite might not be required
14634 * if the new type is sufficiently identical to the old one, and the USING
14635 * clause isn't trying to insert some other value. It's safe to skip the
14636 * rewrite in these cases:
14637 *
14638 * - the old type is binary coercible to the new type
14639 * - the new type is an unconstrained domain over the old type
14640 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14641 *
14642 * In the case of a constrained domain, we could get by with scanning the
14643 * table and checking the constraint rather than actually rewriting it, but we
14644 * don't currently try to do that.
14645 */
14646static bool
14648{
14649 Assert(expr != NULL);
14650
14651 for (;;)
14652 {
14653 /* only one varno, so no need to check that */
14654 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14655 return false;
14656 else if (IsA(expr, RelabelType))
14657 expr = (Node *) ((RelabelType *) expr)->arg;
14658 else if (IsA(expr, CoerceToDomain))
14659 {
14660 CoerceToDomain *d = (CoerceToDomain *) expr;
14661
14663 return true;
14664 expr = (Node *) d->arg;
14665 }
14666 else if (IsA(expr, FuncExpr))
14667 {
14668 FuncExpr *f = (FuncExpr *) expr;
14669
14670 switch (f->funcid)
14671 {
14672 case F_TIMESTAMPTZ_TIMESTAMP:
14673 case F_TIMESTAMP_TIMESTAMPTZ:
14675 return true;
14676 else
14677 expr = linitial(f->args);
14678 break;
14679 default:
14680 return true;
14681 }
14682 }
14683 else
14684 return true;
14685 }
14686}
14687
14688/*
14689 * ALTER COLUMN .. SET DATA TYPE
14690 *
14691 * Return the address of the modified column.
14692 */
14693static ObjectAddress
14695 AlterTableCmd *cmd, LOCKMODE lockmode)
14696{
14697 char *colName = cmd->name;
14698 ColumnDef *def = (ColumnDef *) cmd->def;
14699 TypeName *typeName = def->typeName;
14700 HeapTuple heapTup;
14701 Form_pg_attribute attTup,
14702 attOldTup;
14704 HeapTuple typeTuple;
14705 Form_pg_type tform;
14706 Oid targettype;
14707 int32 targettypmod;
14708 Oid targetcollid;
14709 Node *defaultexpr;
14710 Relation attrelation;
14711 Relation depRel;
14712 ScanKeyData key[3];
14713 SysScanDesc scan;
14714 HeapTuple depTup;
14715 ObjectAddress address;
14716
14717 /*
14718 * Clear all the missing values if we're rewriting the table, since this
14719 * renders them pointless.
14720 */
14721 if (tab->rewrite)
14722 {
14723 Relation newrel;
14724
14725 newrel = table_open(RelationGetRelid(rel), NoLock);
14726 RelationClearMissing(newrel);
14727 relation_close(newrel, NoLock);
14728 /* make sure we don't conflict with later attribute modifications */
14730 }
14731
14732 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14733
14734 /* Look up the target column */
14735 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14736 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14737 ereport(ERROR,
14738 (errcode(ERRCODE_UNDEFINED_COLUMN),
14739 errmsg("column \"%s\" of relation \"%s\" does not exist",
14740 colName, RelationGetRelationName(rel))));
14741 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14742 attnum = attTup->attnum;
14743 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14744
14745 /* Check for multiple ALTER TYPE on same column --- can't cope */
14746 if (attTup->atttypid != attOldTup->atttypid ||
14747 attTup->atttypmod != attOldTup->atttypmod)
14748 ereport(ERROR,
14749 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14750 errmsg("cannot alter type of column \"%s\" twice",
14751 colName)));
14752
14753 /* Look up the target type (should not fail, since prep found it) */
14754 typeTuple = typenameType(NULL, typeName, &targettypmod);
14755 tform = (Form_pg_type) GETSTRUCT(typeTuple);
14756 targettype = tform->oid;
14757 /* And the collation */
14758 targetcollid = GetColumnDefCollation(NULL, def, targettype);
14759
14760 /*
14761 * If there is a default expression for the column, get it and ensure we
14762 * can coerce it to the new datatype. (We must do this before changing
14763 * the column type, because build_column_default itself will try to
14764 * coerce, and will not issue the error message we want if it fails.)
14765 *
14766 * We remove any implicit coercion steps at the top level of the old
14767 * default expression; this has been agreed to satisfy the principle of
14768 * least surprise. (The conversion to the new column type should act like
14769 * it started from what the user sees as the stored expression, and the
14770 * implicit coercions aren't going to be shown.)
14771 */
14772 if (attTup->atthasdef)
14773 {
14774 defaultexpr = build_column_default(rel, attnum);
14775 Assert(defaultexpr);
14776 defaultexpr = strip_implicit_coercions(defaultexpr);
14777 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14778 defaultexpr, exprType(defaultexpr),
14779 targettype, targettypmod,
14782 -1);
14783 if (defaultexpr == NULL)
14784 {
14785 if (attTup->attgenerated)
14786 ereport(ERROR,
14787 (errcode(ERRCODE_DATATYPE_MISMATCH),
14788 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14789 colName, format_type_be(targettype))));
14790 else
14791 ereport(ERROR,
14792 (errcode(ERRCODE_DATATYPE_MISMATCH),
14793 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14794 colName, format_type_be(targettype))));
14795 }
14796 }
14797 else
14798 defaultexpr = NULL;
14799
14800 /*
14801 * Find everything that depends on the column (constraints, indexes, etc),
14802 * and record enough information to let us recreate the objects.
14803 *
14804 * The actual recreation does not happen here, but only after we have
14805 * performed all the individual ALTER TYPE operations. We have to save
14806 * the info before executing ALTER TYPE, though, else the deparser will
14807 * get confused.
14808 */
14810
14811 /*
14812 * Now scan for dependencies of this column on other things. The only
14813 * things we should find are the dependency on the column datatype and
14814 * possibly a collation dependency. Those can be removed.
14815 */
14816 depRel = table_open(DependRelationId, RowExclusiveLock);
14817
14818 ScanKeyInit(&key[0],
14819 Anum_pg_depend_classid,
14820 BTEqualStrategyNumber, F_OIDEQ,
14821 ObjectIdGetDatum(RelationRelationId));
14822 ScanKeyInit(&key[1],
14823 Anum_pg_depend_objid,
14824 BTEqualStrategyNumber, F_OIDEQ,
14826 ScanKeyInit(&key[2],
14827 Anum_pg_depend_objsubid,
14828 BTEqualStrategyNumber, F_INT4EQ,
14830
14831 scan = systable_beginscan(depRel, DependDependerIndexId, true,
14832 NULL, 3, key);
14833
14834 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14835 {
14836 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14837 ObjectAddress foundObject;
14838
14839 foundObject.classId = foundDep->refclassid;
14840 foundObject.objectId = foundDep->refobjid;
14841 foundObject.objectSubId = foundDep->refobjsubid;
14842
14843 if (foundDep->deptype != DEPENDENCY_NORMAL)
14844 elog(ERROR, "found unexpected dependency type '%c'",
14845 foundDep->deptype);
14846 if (!(foundDep->refclassid == TypeRelationId &&
14847 foundDep->refobjid == attTup->atttypid) &&
14848 !(foundDep->refclassid == CollationRelationId &&
14849 foundDep->refobjid == attTup->attcollation))
14850 elog(ERROR, "found unexpected dependency for column: %s",
14851 getObjectDescription(&foundObject, false));
14852
14853 CatalogTupleDelete(depRel, &depTup->t_self);
14854 }
14855
14856 systable_endscan(scan);
14857
14859
14860 /*
14861 * Here we go --- change the recorded column type and collation. (Note
14862 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14863 * fix up the missing value if any.
14864 */
14865 if (attTup->atthasmissing)
14866 {
14867 Datum missingval;
14868 bool missingNull;
14869
14870 /* if rewrite is true the missing value should already be cleared */
14871 Assert(tab->rewrite == 0);
14872
14873 /* Get the missing value datum */
14874 missingval = heap_getattr(heapTup,
14875 Anum_pg_attribute_attmissingval,
14876 attrelation->rd_att,
14877 &missingNull);
14878
14879 /* if it's a null array there is nothing to do */
14880
14881 if (!missingNull)
14882 {
14883 /*
14884 * Get the datum out of the array and repack it in a new array
14885 * built with the new type data. We assume that since the table
14886 * doesn't need rewriting, the actual Datum doesn't need to be
14887 * changed, only the array metadata.
14888 */
14889
14890 int one = 1;
14891 bool isNull;
14892 Datum valuesAtt[Natts_pg_attribute] = {0};
14893 bool nullsAtt[Natts_pg_attribute] = {0};
14894 bool replacesAtt[Natts_pg_attribute] = {0};
14895 HeapTuple newTup;
14896
14897 missingval = array_get_element(missingval,
14898 1,
14899 &one,
14900 0,
14901 attTup->attlen,
14902 attTup->attbyval,
14903 attTup->attalign,
14904 &isNull);
14905 missingval = PointerGetDatum(construct_array(&missingval,
14906 1,
14907 targettype,
14908 tform->typlen,
14909 tform->typbyval,
14910 tform->typalign));
14911
14912 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14913 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14914 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14915
14916 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14917 valuesAtt, nullsAtt, replacesAtt);
14918 heap_freetuple(heapTup);
14919 heapTup = newTup;
14920 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14921 }
14922 }
14923
14924 attTup->atttypid = targettype;
14925 attTup->atttypmod = targettypmod;
14926 attTup->attcollation = targetcollid;
14927 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14928 ereport(ERROR,
14929 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14930 errmsg("too many array dimensions"));
14931 attTup->attndims = list_length(typeName->arrayBounds);
14932 attTup->attlen = tform->typlen;
14933 attTup->attbyval = tform->typbyval;
14934 attTup->attalign = tform->typalign;
14935 attTup->attstorage = tform->typstorage;
14936 attTup->attcompression = InvalidCompressionMethod;
14937
14938 ReleaseSysCache(typeTuple);
14939
14940 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14941
14942 table_close(attrelation, RowExclusiveLock);
14943
14944 /* Install dependencies on new datatype and collation */
14947
14948 /*
14949 * Drop any pg_statistic entry for the column, since it's now wrong type
14950 */
14952
14953 InvokeObjectPostAlterHook(RelationRelationId,
14954 RelationGetRelid(rel), attnum);
14955
14956 /*
14957 * Update the default, if present, by brute force --- remove and re-add
14958 * the default. Probably unsafe to take shortcuts, since the new version
14959 * may well have additional dependencies. (It's okay to do this now,
14960 * rather than after other ALTER TYPE commands, since the default won't
14961 * depend on other column types.)
14962 */
14963 if (defaultexpr)
14964 {
14965 /*
14966 * If it's a GENERATED default, drop its dependency records, in
14967 * particular its INTERNAL dependency on the column, which would
14968 * otherwise cause dependency.c to refuse to perform the deletion.
14969 */
14970 if (attTup->attgenerated)
14971 {
14972 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14973
14974 if (!OidIsValid(attrdefoid))
14975 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14976 RelationGetRelid(rel), attnum);
14977 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14978 }
14979
14980 /*
14981 * Make updates-so-far visible, particularly the new pg_attribute row
14982 * which will be updated again.
14983 */
14985
14986 /*
14987 * We use RESTRICT here for safety, but at present we do not expect
14988 * anything to depend on the default.
14989 */
14991 true);
14992
14993 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
14994 }
14995
14996 ObjectAddressSubSet(address, RelationRelationId,
14997 RelationGetRelid(rel), attnum);
14998
14999 /* Cleanup */
15000 heap_freetuple(heapTup);
15001
15002 return address;
15003}
15004
15005/*
15006 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
15007 * that depends on the column (constraints, indexes, etc), and record enough
15008 * information to let us recreate the objects.
15009 */
15010static void
15012 Relation rel, AttrNumber attnum, const char *colName)
15013{
15014 Relation depRel;
15015 ScanKeyData key[3];
15016 SysScanDesc scan;
15017 HeapTuple depTup;
15018
15019 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15020
15021 depRel = table_open(DependRelationId, RowExclusiveLock);
15022
15023 ScanKeyInit(&key[0],
15024 Anum_pg_depend_refclassid,
15025 BTEqualStrategyNumber, F_OIDEQ,
15026 ObjectIdGetDatum(RelationRelationId));
15027 ScanKeyInit(&key[1],
15028 Anum_pg_depend_refobjid,
15029 BTEqualStrategyNumber, F_OIDEQ,
15031 ScanKeyInit(&key[2],
15032 Anum_pg_depend_refobjsubid,
15033 BTEqualStrategyNumber, F_INT4EQ,
15035
15036 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15037 NULL, 3, key);
15038
15039 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15040 {
15041 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15042 ObjectAddress foundObject;
15043
15044 foundObject.classId = foundDep->classid;
15045 foundObject.objectId = foundDep->objid;
15046 foundObject.objectSubId = foundDep->objsubid;
15047
15048 switch (foundObject.classId)
15049 {
15050 case RelationRelationId:
15051 {
15052 char relKind = get_rel_relkind(foundObject.objectId);
15053
15054 if (relKind == RELKIND_INDEX ||
15055 relKind == RELKIND_PARTITIONED_INDEX)
15056 {
15057 Assert(foundObject.objectSubId == 0);
15058 RememberIndexForRebuilding(foundObject.objectId, tab);
15059 }
15060 else if (relKind == RELKIND_SEQUENCE)
15061 {
15062 /*
15063 * This must be a SERIAL column's sequence. We need
15064 * not do anything to it.
15065 */
15066 Assert(foundObject.objectSubId == 0);
15067 }
15068 else
15069 {
15070 /* Not expecting any other direct dependencies... */
15071 elog(ERROR, "unexpected object depending on column: %s",
15072 getObjectDescription(&foundObject, false));
15073 }
15074 break;
15075 }
15076
15077 case ConstraintRelationId:
15078 Assert(foundObject.objectSubId == 0);
15079 RememberConstraintForRebuilding(foundObject.objectId, tab);
15080 break;
15081
15082 case ProcedureRelationId:
15083
15084 /*
15085 * A new-style SQL function can depend on a column, if that
15086 * column is referenced in the parsed function body. Ideally
15087 * we'd automatically update the function by deparsing and
15088 * reparsing it, but that's risky and might well fail anyhow.
15089 * FIXME someday.
15090 *
15091 * This is only a problem for AT_AlterColumnType, not
15092 * AT_SetExpression.
15093 */
15094 if (subtype == AT_AlterColumnType)
15095 ereport(ERROR,
15096 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15097 errmsg("cannot alter type of a column used by a function or procedure"),
15098 errdetail("%s depends on column \"%s\"",
15099 getObjectDescription(&foundObject, false),
15100 colName)));
15101 break;
15102
15103 case RewriteRelationId:
15104
15105 /*
15106 * View/rule bodies have pretty much the same issues as
15107 * function bodies. FIXME someday.
15108 */
15109 if (subtype == AT_AlterColumnType)
15110 ereport(ERROR,
15111 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15112 errmsg("cannot alter type of a column used by a view or rule"),
15113 errdetail("%s depends on column \"%s\"",
15114 getObjectDescription(&foundObject, false),
15115 colName)));
15116 break;
15117
15118 case TriggerRelationId:
15119
15120 /*
15121 * A trigger can depend on a column because the column is
15122 * specified as an update target, or because the column is
15123 * used in the trigger's WHEN condition. The first case would
15124 * not require any extra work, but the second case would
15125 * require updating the WHEN expression, which has the same
15126 * issues as above. Since we can't easily tell which case
15127 * applies, we punt for both. FIXME someday.
15128 */
15129 if (subtype == AT_AlterColumnType)
15130 ereport(ERROR,
15131 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15132 errmsg("cannot alter type of a column used in a trigger definition"),
15133 errdetail("%s depends on column \"%s\"",
15134 getObjectDescription(&foundObject, false),
15135 colName)));
15136 break;
15137
15138 case PolicyRelationId:
15139
15140 /*
15141 * A policy can depend on a column because the column is
15142 * specified in the policy's USING or WITH CHECK qual
15143 * expressions. It might be possible to rewrite and recheck
15144 * the policy expression, but punt for now. It's certainly
15145 * easy enough to remove and recreate the policy; still, FIXME
15146 * someday.
15147 */
15148 if (subtype == AT_AlterColumnType)
15149 ereport(ERROR,
15150 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15151 errmsg("cannot alter type of a column used in a policy definition"),
15152 errdetail("%s depends on column \"%s\"",
15153 getObjectDescription(&foundObject, false),
15154 colName)));
15155 break;
15156
15157 case AttrDefaultRelationId:
15158 {
15160
15161 if (col.objectId == RelationGetRelid(rel) &&
15162 col.objectSubId == attnum)
15163 {
15164 /*
15165 * Ignore the column's own default expression. The
15166 * caller deals with it.
15167 */
15168 }
15169 else
15170 {
15171 /*
15172 * This must be a reference from the expression of a
15173 * generated column elsewhere in the same table.
15174 * Changing the type/generated expression of a column
15175 * that is used by a generated column is not allowed
15176 * by SQL standard, so just punt for now. It might be
15177 * doable with some thinking and effort.
15178 */
15179 if (subtype == AT_AlterColumnType)
15180 ereport(ERROR,
15181 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15182 errmsg("cannot alter type of a column used by a generated column"),
15183 errdetail("Column \"%s\" is used by generated column \"%s\".",
15184 colName,
15186 col.objectSubId,
15187 false))));
15188 }
15189 break;
15190 }
15191
15192 case StatisticExtRelationId:
15193
15194 /*
15195 * Give the extended-stats machinery a chance to fix anything
15196 * that this column type change would break.
15197 */
15198 RememberStatisticsForRebuilding(foundObject.objectId, tab);
15199 break;
15200
15201 case PublicationRelRelationId:
15202
15203 /*
15204 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15205 * clause. Same issues as above. FIXME someday.
15206 */
15207 if (subtype == AT_AlterColumnType)
15208 ereport(ERROR,
15209 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15210 errmsg("cannot alter type of a column used by a publication WHERE clause"),
15211 errdetail("%s depends on column \"%s\"",
15212 getObjectDescription(&foundObject, false),
15213 colName)));
15214 break;
15215
15216 default:
15217
15218 /*
15219 * We don't expect any other sorts of objects to depend on a
15220 * column.
15221 */
15222 elog(ERROR, "unexpected object depending on column: %s",
15223 getObjectDescription(&foundObject, false));
15224 break;
15225 }
15226 }
15227
15228 systable_endscan(scan);
15229 table_close(depRel, NoLock);
15230}
15231
15232/*
15233 * Subroutine for ATExecAlterColumnType: remember that a replica identity
15234 * needs to be reset.
15235 */
15236static void
15238{
15239 if (!get_index_isreplident(indoid))
15240 return;
15241
15242 if (tab->replicaIdentityIndex)
15243 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15244
15245 tab->replicaIdentityIndex = get_rel_name(indoid);
15246}
15247
15248/*
15249 * Subroutine for ATExecAlterColumnType: remember any clustered index.
15250 */
15251static void
15253{
15254 if (!get_index_isclustered(indoid))
15255 return;
15256
15257 if (tab->clusterOnIndex)
15258 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15259
15260 tab->clusterOnIndex = get_rel_name(indoid);
15261}
15262
15263/*
15264 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15265 * to be rebuilt (which we might already know).
15266 */
15267static void
15269{
15270 /*
15271 * This de-duplication check is critical for two independent reasons: we
15272 * mustn't try to recreate the same constraint twice, and if a constraint
15273 * depends on more than one column whose type is to be altered, we must
15274 * capture its definition string before applying any of the column type
15275 * changes. ruleutils.c will get confused if we ask again later.
15276 */
15277 if (!list_member_oid(tab->changedConstraintOids, conoid))
15278 {
15279 /* OK, capture the constraint's existing definition string */
15280 char *defstring = pg_get_constraintdef_command(conoid);
15281 Oid indoid;
15282
15283 /*
15284 * It is critical to create not-null constraints ahead of primary key
15285 * indexes; otherwise, the not-null constraint would be created by the
15286 * primary key, and the constraint name would be wrong.
15287 */
15288 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15289 {
15290 tab->changedConstraintOids = lcons_oid(conoid,
15292 tab->changedConstraintDefs = lcons(defstring,
15294 }
15295 else
15296 {
15297
15299 conoid);
15301 defstring);
15302 }
15303
15304 /*
15305 * For the index of a constraint, if any, remember if it is used for
15306 * the table's replica identity or if it is a clustered index, so that
15307 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15308 * those properties.
15309 */
15310 indoid = get_constraint_index(conoid);
15311 if (OidIsValid(indoid))
15312 {
15314 RememberClusterOnForRebuilding(indoid, tab);
15315 }
15316 }
15317}
15318
15319/*
15320 * Subroutine for ATExecAlterColumnType: remember that an index needs
15321 * to be rebuilt (which we might already know).
15322 */
15323static void
15325{
15326 /*
15327 * This de-duplication check is critical for two independent reasons: we
15328 * mustn't try to recreate the same index twice, and if an index depends
15329 * on more than one column whose type is to be altered, we must capture
15330 * its definition string before applying any of the column type changes.
15331 * ruleutils.c will get confused if we ask again later.
15332 */
15333 if (!list_member_oid(tab->changedIndexOids, indoid))
15334 {
15335 /*
15336 * Before adding it as an index-to-rebuild, we'd better see if it
15337 * belongs to a constraint, and if so rebuild the constraint instead.
15338 * Typically this check fails, because constraint indexes normally
15339 * have only dependencies on their constraint. But it's possible for
15340 * such an index to also have direct dependencies on table columns,
15341 * for example with a partial exclusion constraint.
15342 */
15343 Oid conoid = get_index_constraint(indoid);
15344
15345 if (OidIsValid(conoid))
15346 {
15348 }
15349 else
15350 {
15351 /* OK, capture the index's existing definition string */
15352 char *defstring = pg_get_indexdef_string(indoid);
15353
15355 indoid);
15357 defstring);
15358
15359 /*
15360 * Remember if this index is used for the table's replica identity
15361 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15362 * can queue up commands necessary to restore those properties.
15363 */
15365 RememberClusterOnForRebuilding(indoid, tab);
15366 }
15367 }
15368}
15369
15370/*
15371 * Subroutine for ATExecAlterColumnType: remember that a statistics object
15372 * needs to be rebuilt (which we might already know).
15373 */
15374static void
15376{
15377 /*
15378 * This de-duplication check is critical for two independent reasons: we
15379 * mustn't try to recreate the same statistics object twice, and if the
15380 * statistics object depends on more than one column whose type is to be
15381 * altered, we must capture its definition string before applying any of
15382 * the type changes. ruleutils.c will get confused if we ask again later.
15383 */
15384 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15385 {
15386 /* OK, capture the statistics object's existing definition string */
15387 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15388
15390 stxoid);
15392 defstring);
15393 }
15394}
15395
15396/*
15397 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15398 * operations for a particular relation. We have to drop and recreate all the
15399 * indexes and constraints that depend on the altered columns. We do the
15400 * actual dropping here, but re-creation is managed by adding work queue
15401 * entries to do those steps later.
15402 */
15403static void
15405{
15406 ObjectAddress obj;
15407 ObjectAddresses *objects;
15408 ListCell *def_item;
15409 ListCell *oid_item;
15410
15411 /*
15412 * Collect all the constraints and indexes to drop so we can process them
15413 * in a single call. That way we don't have to worry about dependencies
15414 * among them.
15415 */
15416 objects = new_object_addresses();
15417
15418 /*
15419 * Re-parse the index and constraint definitions, and attach them to the
15420 * appropriate work queue entries. We do this before dropping because in
15421 * the case of a constraint on another table, we might not yet have
15422 * exclusive lock on the table the constraint is attached to, and we need
15423 * to get that before reparsing/dropping. (That's possible at least for
15424 * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15425 * requires a dependency on the target table's composite type in the other
15426 * table's constraint expressions.)
15427 *
15428 * We can't rely on the output of deparsing to tell us which relation to
15429 * operate on, because concurrent activity might have made the name
15430 * resolve differently. Instead, we've got to use the OID of the
15431 * constraint or index we're processing to figure out which relation to
15432 * operate on.
15433 */
15434 forboth(oid_item, tab->changedConstraintOids,
15435 def_item, tab->changedConstraintDefs)
15436 {
15437 Oid oldId = lfirst_oid(oid_item);
15438 HeapTuple tup;
15440 Oid relid;
15441 Oid confrelid;
15442 bool conislocal;
15443
15444 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15445 if (!HeapTupleIsValid(tup)) /* should not happen */
15446 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15447 con = (Form_pg_constraint) GETSTRUCT(tup);
15448 if (OidIsValid(con->conrelid))
15449 relid = con->conrelid;
15450 else
15451 {
15452 /* must be a domain constraint */
15453 relid = get_typ_typrelid(getBaseType(con->contypid));
15454 if (!OidIsValid(relid))
15455 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15456 }
15457 confrelid = con->confrelid;
15458 conislocal = con->conislocal;
15459 ReleaseSysCache(tup);
15460
15461 ObjectAddressSet(obj, ConstraintRelationId, oldId);
15462 add_exact_object_address(&obj, objects);
15463
15464 /*
15465 * If the constraint is inherited (only), we don't want to inject a
15466 * new definition here; it'll get recreated when
15467 * ATAddCheckNNConstraint recurses from adding the parent table's
15468 * constraint. But we had to carry the info this far so that we can
15469 * drop the constraint below.
15470 */
15471 if (!conislocal)
15472 continue;
15473
15474 /*
15475 * When rebuilding another table's constraint that references the
15476 * table we're modifying, we might not yet have any lock on the other
15477 * table, so get one now. We'll need AccessExclusiveLock for the DROP
15478 * CONSTRAINT step, so there's no value in asking for anything weaker.
15479 */
15480 if (relid != tab->relid)
15482
15483 ATPostAlterTypeParse(oldId, relid, confrelid,
15484 (char *) lfirst(def_item),
15485 wqueue, lockmode, tab->rewrite);
15486 }
15487 forboth(oid_item, tab->changedIndexOids,
15488 def_item, tab->changedIndexDefs)
15489 {
15490 Oid oldId = lfirst_oid(oid_item);
15491 Oid relid;
15492
15493 relid = IndexGetRelation(oldId, false);
15494
15495 /*
15496 * As above, make sure we have lock on the index's table if it's not
15497 * the same table.
15498 */
15499 if (relid != tab->relid)
15501
15502 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15503 (char *) lfirst(def_item),
15504 wqueue, lockmode, tab->rewrite);
15505
15506 ObjectAddressSet(obj, RelationRelationId, oldId);
15507 add_exact_object_address(&obj, objects);
15508 }
15509
15510 /* add dependencies for new statistics */
15511 forboth(oid_item, tab->changedStatisticsOids,
15512 def_item, tab->changedStatisticsDefs)
15513 {
15514 Oid oldId = lfirst_oid(oid_item);
15515 Oid relid;
15516
15517 relid = StatisticsGetRelation(oldId, false);
15518
15519 /*
15520 * As above, make sure we have lock on the statistics object's table
15521 * if it's not the same table. However, we take
15522 * ShareUpdateExclusiveLock here, aligning with the lock level used in
15523 * CreateStatistics and RemoveStatisticsById.
15524 *
15525 * CAUTION: this should be done after all cases that grab
15526 * AccessExclusiveLock, else we risk causing deadlock due to needing
15527 * to promote our table lock.
15528 */
15529 if (relid != tab->relid)
15531
15532 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15533 (char *) lfirst(def_item),
15534 wqueue, lockmode, tab->rewrite);
15535
15536 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15537 add_exact_object_address(&obj, objects);
15538 }
15539
15540 /*
15541 * Queue up command to restore replica identity index marking
15542 */
15543 if (tab->replicaIdentityIndex)
15544 {
15547
15548 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15549 subcmd->name = tab->replicaIdentityIndex;
15551 cmd->def = (Node *) subcmd;
15552
15553 /* do it after indexes and constraints */
15554 tab->subcmds[AT_PASS_OLD_CONSTR] =
15555 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15556 }
15557
15558 /*
15559 * Queue up command to restore marking of index used for cluster.
15560 */
15561 if (tab->clusterOnIndex)
15562 {
15564
15565 cmd->subtype = AT_ClusterOn;
15566 cmd->name = tab->clusterOnIndex;
15567
15568 /* do it after indexes and constraints */
15569 tab->subcmds[AT_PASS_OLD_CONSTR] =
15570 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15571 }
15572
15573 /*
15574 * It should be okay to use DROP_RESTRICT here, since nothing else should
15575 * be depending on these objects.
15576 */
15578
15579 free_object_addresses(objects);
15580
15581 /*
15582 * The objects will get recreated during subsequent passes over the work
15583 * queue.
15584 */
15585}
15586
15587/*
15588 * Parse the previously-saved definition string for a constraint, index or
15589 * statistics object against the newly-established column data type(s), and
15590 * queue up the resulting command parsetrees for execution.
15591 *
15592 * This might fail if, for example, you have a WHERE clause that uses an
15593 * operator that's not available for the new column type.
15594 */
15595static void
15596ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15597 List **wqueue, LOCKMODE lockmode, bool rewrite)
15598{
15599 List *raw_parsetree_list;
15600 List *querytree_list;
15601 ListCell *list_item;
15602 Relation rel;
15603
15604 /*
15605 * We expect that we will get only ALTER TABLE and CREATE INDEX
15606 * statements. Hence, there is no need to pass them through
15607 * parse_analyze_*() or the rewriter, but instead we need to pass them
15608 * through parse_utilcmd.c to make them ready for execution.
15609 */
15610 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15611 querytree_list = NIL;
15612 foreach(list_item, raw_parsetree_list)
15613 {
15614 RawStmt *rs = lfirst_node(RawStmt, list_item);
15615 Node *stmt = rs->stmt;
15616
15617 if (IsA(stmt, IndexStmt))
15618 querytree_list = lappend(querytree_list,
15619 transformIndexStmt(oldRelId,
15620 (IndexStmt *) stmt,
15621 cmd));
15622 else if (IsA(stmt, AlterTableStmt))
15623 {
15624 List *beforeStmts;
15625 List *afterStmts;
15626
15627 stmt = (Node *) transformAlterTableStmt(oldRelId,
15628 (AlterTableStmt *) stmt,
15629 cmd,
15630 &beforeStmts,
15631 &afterStmts);
15632 querytree_list = list_concat(querytree_list, beforeStmts);
15633 querytree_list = lappend(querytree_list, stmt);
15634 querytree_list = list_concat(querytree_list, afterStmts);
15635 }
15636 else if (IsA(stmt, CreateStatsStmt))
15637 querytree_list = lappend(querytree_list,
15638 transformStatsStmt(oldRelId,
15640 cmd));
15641 else
15642 querytree_list = lappend(querytree_list, stmt);
15643 }
15644
15645 /* Caller should already have acquired whatever lock we need. */
15646 rel = relation_open(oldRelId, NoLock);
15647
15648 /*
15649 * Attach each generated command to the proper place in the work queue.
15650 * Note this could result in creation of entirely new work-queue entries.
15651 *
15652 * Also note that we have to tweak the command subtypes, because it turns
15653 * out that re-creation of indexes and constraints has to act a bit
15654 * differently from initial creation.
15655 */
15656 foreach(list_item, querytree_list)
15657 {
15658 Node *stm = (Node *) lfirst(list_item);
15659 AlteredTableInfo *tab;
15660
15661 tab = ATGetQueueEntry(wqueue, rel);
15662
15663 if (IsA(stm, IndexStmt))
15664 {
15665 IndexStmt *stmt = (IndexStmt *) stm;
15666 AlterTableCmd *newcmd;
15667
15668 if (!rewrite)
15669 TryReuseIndex(oldId, stmt);
15670 stmt->reset_default_tblspc = true;
15671 /* keep the index's comment */
15672 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15673
15674 newcmd = makeNode(AlterTableCmd);
15675 newcmd->subtype = AT_ReAddIndex;
15676 newcmd->def = (Node *) stmt;
15678 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15679 }
15680 else if (IsA(stm, AlterTableStmt))
15681 {
15683 ListCell *lcmd;
15684
15685 foreach(lcmd, stmt->cmds)
15686 {
15688
15689 if (cmd->subtype == AT_AddIndex)
15690 {
15691 IndexStmt *indstmt;
15692 Oid indoid;
15693
15694 indstmt = castNode(IndexStmt, cmd->def);
15695 indoid = get_constraint_index(oldId);
15696
15697 if (!rewrite)
15698 TryReuseIndex(indoid, indstmt);
15699 /* keep any comment on the index */
15700 indstmt->idxcomment = GetComment(indoid,
15701 RelationRelationId, 0);
15702 indstmt->reset_default_tblspc = true;
15703
15704 cmd->subtype = AT_ReAddIndex;
15706 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15707
15708 /* recreate any comment on the constraint */
15711 oldId,
15712 rel,
15713 NIL,
15714 indstmt->idxname);
15715 }
15716 else if (cmd->subtype == AT_AddConstraint)
15717 {
15718 Constraint *con = castNode(Constraint, cmd->def);
15719
15720 con->old_pktable_oid = refRelId;
15721 /* rewriting neither side of a FK */
15722 if (con->contype == CONSTR_FOREIGN &&
15723 !rewrite && tab->rewrite == 0)
15724 TryReuseForeignKey(oldId, con);
15725 con->reset_default_tblspc = true;
15729
15730 /*
15731 * Recreate any comment on the constraint. If we have
15732 * recreated a primary key, then transformTableConstraint
15733 * has added an unnamed not-null constraint here; skip
15734 * this in that case.
15735 */
15736 if (con->conname)
15739 oldId,
15740 rel,
15741 NIL,
15742 con->conname);
15743 else
15744 Assert(con->contype == CONSTR_NOTNULL);
15745 }
15746 else
15747 elog(ERROR, "unexpected statement subtype: %d",
15748 (int) cmd->subtype);
15749 }
15750 }
15751 else if (IsA(stm, AlterDomainStmt))
15752 {
15754
15755 if (stmt->subtype == AD_AddConstraint)
15756 {
15757 Constraint *con = castNode(Constraint, stmt->def);
15759
15761 cmd->def = (Node *) stmt;
15764
15765 /* recreate any comment on the constraint */
15768 oldId,
15769 NULL,
15770 stmt->typeName,
15771 con->conname);
15772 }
15773 else
15774 elog(ERROR, "unexpected statement subtype: %d",
15775 (int) stmt->subtype);
15776 }
15777 else if (IsA(stm, CreateStatsStmt))
15778 {
15780 AlterTableCmd *newcmd;
15781
15782 /* keep the statistics object's comment */
15783 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15784
15785 newcmd = makeNode(AlterTableCmd);
15786 newcmd->subtype = AT_ReAddStatistics;
15787 newcmd->def = (Node *) stmt;
15788 tab->subcmds[AT_PASS_MISC] =
15789 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15790 }
15791 else
15792 elog(ERROR, "unexpected statement type: %d",
15793 (int) nodeTag(stm));
15794 }
15795
15796 relation_close(rel, NoLock);
15797}
15798
15799/*
15800 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15801 * for a table or domain constraint that is being rebuilt.
15802 *
15803 * objid is the OID of the constraint.
15804 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15805 * as a string list) for a domain constraint.
15806 * (We could dig that info, as well as the conname, out of the pg_constraint
15807 * entry; but callers already have them so might as well pass them.)
15808 */
15809static void
15811 Relation rel, List *domname,
15812 const char *conname)
15813{
15814 CommentStmt *cmd;
15815 char *comment_str;
15816 AlterTableCmd *newcmd;
15817
15818 /* Look for comment for object wanted, and leave if none */
15819 comment_str = GetComment(objid, ConstraintRelationId, 0);
15820 if (comment_str == NULL)
15821 return;
15822
15823 /* Build CommentStmt node, copying all input data for safety */
15824 cmd = makeNode(CommentStmt);
15825 if (rel)
15826 {
15828 cmd->object = (Node *)
15831 makeString(pstrdup(conname)));
15832 }
15833 else
15834 {
15836 cmd->object = (Node *)
15838 makeString(pstrdup(conname)));
15839 }
15840 cmd->comment = comment_str;
15841
15842 /* Append it to list of commands */
15843 newcmd = makeNode(AlterTableCmd);
15844 newcmd->subtype = AT_ReAddComment;
15845 newcmd->def = (Node *) cmd;
15846 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15847}
15848
15849/*
15850 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15851 * for the real analysis, then mutates the IndexStmt based on that verdict.
15852 */
15853static void
15855{
15856 if (CheckIndexCompatible(oldId,
15857 stmt->accessMethod,
15858 stmt->indexParams,
15859 stmt->excludeOpNames,
15860 stmt->iswithoutoverlaps))
15861 {
15862 Relation irel = index_open(oldId, NoLock);
15863
15864 /* If it's a partitioned index, there is no storage to share. */
15865 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15866 {
15867 stmt->oldNumber = irel->rd_locator.relNumber;
15868 stmt->oldCreateSubid = irel->rd_createSubid;
15869 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15870 }
15871 index_close(irel, NoLock);
15872 }
15873}
15874
15875/*
15876 * Subroutine for ATPostAlterTypeParse().
15877 *
15878 * Stash the old P-F equality operator into the Constraint node, for possible
15879 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15880 * this constraint can be skipped.
15881 */
15882static void
15884{
15885 HeapTuple tup;
15886 Datum adatum;
15887 ArrayType *arr;
15888 Oid *rawarr;
15889 int numkeys;
15890 int i;
15891
15892 Assert(con->contype == CONSTR_FOREIGN);
15893 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15894
15895 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15896 if (!HeapTupleIsValid(tup)) /* should not happen */
15897 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15898
15899 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15900 Anum_pg_constraint_conpfeqop);
15901 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15902 numkeys = ARR_DIMS(arr)[0];
15903 /* test follows the one in ri_FetchConstraintInfo() */
15904 if (ARR_NDIM(arr) != 1 ||
15905 ARR_HASNULL(arr) ||
15906 ARR_ELEMTYPE(arr) != OIDOID)
15907 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15908 rawarr = (Oid *) ARR_DATA_PTR(arr);
15909
15910 /* stash a List of the operator Oids in our Constraint node */
15911 for (i = 0; i < numkeys; i++)
15912 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15913
15914 ReleaseSysCache(tup);
15915}
15916
15917/*
15918 * ALTER COLUMN .. OPTIONS ( ... )
15919 *
15920 * Returns the address of the modified column
15921 */
15922static ObjectAddress
15924 const char *colName,
15925 List *options,
15926 LOCKMODE lockmode)
15927{
15928 Relation ftrel;
15929 Relation attrel;
15930 ForeignServer *server;
15931 ForeignDataWrapper *fdw;
15932 HeapTuple tuple;
15933 HeapTuple newtuple;
15934 bool isnull;
15935 Datum repl_val[Natts_pg_attribute];
15936 bool repl_null[Natts_pg_attribute];
15937 bool repl_repl[Natts_pg_attribute];
15938 Datum datum;
15939 Form_pg_foreign_table fttableform;
15940 Form_pg_attribute atttableform;
15942 ObjectAddress address;
15943
15944 if (options == NIL)
15945 return InvalidObjectAddress;
15946
15947 /* First, determine FDW validator associated to the foreign table. */
15948 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15949 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15950 if (!HeapTupleIsValid(tuple))
15951 ereport(ERROR,
15952 (errcode(ERRCODE_UNDEFINED_OBJECT),
15953 errmsg("foreign table \"%s\" does not exist",
15955 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15956 server = GetForeignServer(fttableform->ftserver);
15957 fdw = GetForeignDataWrapper(server->fdwid);
15958
15960 ReleaseSysCache(tuple);
15961
15962 attrel = table_open(AttributeRelationId, RowExclusiveLock);
15963 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15964 if (!HeapTupleIsValid(tuple))
15965 ereport(ERROR,
15966 (errcode(ERRCODE_UNDEFINED_COLUMN),
15967 errmsg("column \"%s\" of relation \"%s\" does not exist",
15968 colName, RelationGetRelationName(rel))));
15969
15970 /* Prevent them from altering a system attribute */
15971 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15972 attnum = atttableform->attnum;
15973 if (attnum <= 0)
15974 ereport(ERROR,
15975 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15976 errmsg("cannot alter system column \"%s\"", colName)));
15977
15978
15979 /* Initialize buffers for new tuple values */
15980 memset(repl_val, 0, sizeof(repl_val));
15981 memset(repl_null, false, sizeof(repl_null));
15982 memset(repl_repl, false, sizeof(repl_repl));
15983
15984 /* Extract the current options */
15985 datum = SysCacheGetAttr(ATTNAME,
15986 tuple,
15987 Anum_pg_attribute_attfdwoptions,
15988 &isnull);
15989 if (isnull)
15990 datum = PointerGetDatum(NULL);
15991
15992 /* Transform the options */
15993 datum = transformGenericOptions(AttributeRelationId,
15994 datum,
15995 options,
15996 fdw->fdwvalidator);
15997
15998 if (DatumGetPointer(datum) != NULL)
15999 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16000 else
16001 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16002
16003 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16004
16005 /* Everything looks good - update the tuple */
16006
16007 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16008 repl_val, repl_null, repl_repl);
16009
16010 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16011
16012 InvokeObjectPostAlterHook(RelationRelationId,
16013 RelationGetRelid(rel),
16014 atttableform->attnum);
16015 ObjectAddressSubSet(address, RelationRelationId,
16016 RelationGetRelid(rel), attnum);
16017
16018 ReleaseSysCache(tuple);
16019
16021
16022 heap_freetuple(newtuple);
16023
16024 return address;
16025}
16026
16027/*
16028 * ALTER TABLE OWNER
16029 *
16030 * recursing is true if we are recursing from a table to its indexes,
16031 * sequences, or toast table. We don't allow the ownership of those things to
16032 * be changed separately from the parent table. Also, we can skip permission
16033 * checks (this is necessary not just an optimization, else we'd fail to
16034 * handle toast tables properly).
16035 *
16036 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
16037 * free-standing composite type.
16038 */
16039void
16040ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
16041{
16042 Relation target_rel;
16043 Relation class_rel;
16044 HeapTuple tuple;
16045 Form_pg_class tuple_class;
16046
16047 /*
16048 * Get exclusive lock till end of transaction on the target table. Use
16049 * relation_open so that we can work on indexes and sequences.
16050 */
16051 target_rel = relation_open(relationOid, lockmode);
16052
16053 /* Get its pg_class tuple, too */
16054 class_rel = table_open(RelationRelationId, RowExclusiveLock);
16055
16056 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16057 if (!HeapTupleIsValid(tuple))
16058 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16059 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16060
16061 /* Can we change the ownership of this tuple? */
16062 switch (tuple_class->relkind)
16063 {
16064 case RELKIND_RELATION:
16065 case RELKIND_VIEW:
16066 case RELKIND_MATVIEW:
16067 case RELKIND_FOREIGN_TABLE:
16068 case RELKIND_PARTITIONED_TABLE:
16069 /* ok to change owner */
16070 break;
16071 case RELKIND_INDEX:
16072 if (!recursing)
16073 {
16074 /*
16075 * Because ALTER INDEX OWNER used to be allowed, and in fact
16076 * is generated by old versions of pg_dump, we give a warning
16077 * and do nothing rather than erroring out. Also, to avoid
16078 * unnecessary chatter while restoring those old dumps, say
16079 * nothing at all if the command would be a no-op anyway.
16080 */
16081 if (tuple_class->relowner != newOwnerId)
16083 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16084 errmsg("cannot change owner of index \"%s\"",
16085 NameStr(tuple_class->relname)),
16086 errhint("Change the ownership of the index's table instead.")));
16087 /* quick hack to exit via the no-op path */
16088 newOwnerId = tuple_class->relowner;
16089 }
16090 break;
16091 case RELKIND_PARTITIONED_INDEX:
16092 if (recursing)
16093 break;
16094 ereport(ERROR,
16095 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16096 errmsg("cannot change owner of index \"%s\"",
16097 NameStr(tuple_class->relname)),
16098 errhint("Change the ownership of the index's table instead.")));
16099 break;
16100 case RELKIND_SEQUENCE:
16101 if (!recursing &&
16102 tuple_class->relowner != newOwnerId)
16103 {
16104 /* if it's an owned sequence, disallow changing it by itself */
16105 Oid tableId;
16106 int32 colId;
16107
16108 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16109 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16110 ereport(ERROR,
16111 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16112 errmsg("cannot change owner of sequence \"%s\"",
16113 NameStr(tuple_class->relname)),
16114 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16115 NameStr(tuple_class->relname),
16116 get_rel_name(tableId))));
16117 }
16118 break;
16119 case RELKIND_COMPOSITE_TYPE:
16120 if (recursing)
16121 break;
16122 ereport(ERROR,
16123 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16124 errmsg("\"%s\" is a composite type",
16125 NameStr(tuple_class->relname)),
16126 /* translator: %s is an SQL ALTER command */
16127 errhint("Use %s instead.",
16128 "ALTER TYPE")));
16129 break;
16130 case RELKIND_TOASTVALUE:
16131 if (recursing)
16132 break;
16133 /* FALL THRU */
16134 default:
16135 ereport(ERROR,
16136 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16137 errmsg("cannot change owner of relation \"%s\"",
16138 NameStr(tuple_class->relname)),
16139 errdetail_relkind_not_supported(tuple_class->relkind)));
16140 }
16141
16142 /*
16143 * If the new owner is the same as the existing owner, consider the
16144 * command to have succeeded. This is for dump restoration purposes.
16145 */
16146 if (tuple_class->relowner != newOwnerId)
16147 {
16148 Datum repl_val[Natts_pg_class];
16149 bool repl_null[Natts_pg_class];
16150 bool repl_repl[Natts_pg_class];
16151 Acl *newAcl;
16152 Datum aclDatum;
16153 bool isNull;
16154 HeapTuple newtuple;
16155
16156 /* skip permission checks when recursing to index or toast table */
16157 if (!recursing)
16158 {
16159 /* Superusers can always do it */
16160 if (!superuser())
16161 {
16162 Oid namespaceOid = tuple_class->relnamespace;
16163 AclResult aclresult;
16164
16165 /* Otherwise, must be owner of the existing object */
16166 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16168 RelationGetRelationName(target_rel));
16169
16170 /* Must be able to become new owner */
16171 check_can_set_role(GetUserId(), newOwnerId);
16172
16173 /* New owner must have CREATE privilege on namespace */
16174 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16175 ACL_CREATE);
16176 if (aclresult != ACLCHECK_OK)
16177 aclcheck_error(aclresult, OBJECT_SCHEMA,
16178 get_namespace_name(namespaceOid));
16179 }
16180 }
16181
16182 memset(repl_null, false, sizeof(repl_null));
16183 memset(repl_repl, false, sizeof(repl_repl));
16184
16185 repl_repl[Anum_pg_class_relowner - 1] = true;
16186 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16187
16188 /*
16189 * Determine the modified ACL for the new owner. This is only
16190 * necessary when the ACL is non-null.
16191 */
16192 aclDatum = SysCacheGetAttr(RELOID, tuple,
16193 Anum_pg_class_relacl,
16194 &isNull);
16195 if (!isNull)
16196 {
16197 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16198 tuple_class->relowner, newOwnerId);
16199 repl_repl[Anum_pg_class_relacl - 1] = true;
16200 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16201 }
16202
16203 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16204
16205 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16206
16207 heap_freetuple(newtuple);
16208
16209 /*
16210 * We must similarly update any per-column ACLs to reflect the new
16211 * owner; for neatness reasons that's split out as a subroutine.
16212 */
16213 change_owner_fix_column_acls(relationOid,
16214 tuple_class->relowner,
16215 newOwnerId);
16216
16217 /*
16218 * Update owner dependency reference, if any. A composite type has
16219 * none, because it's tracked for the pg_type entry instead of here;
16220 * indexes and TOAST tables don't have their own entries either.
16221 */
16222 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16223 tuple_class->relkind != RELKIND_INDEX &&
16224 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16225 tuple_class->relkind != RELKIND_TOASTVALUE)
16226 changeDependencyOnOwner(RelationRelationId, relationOid,
16227 newOwnerId);
16228
16229 /*
16230 * Also change the ownership of the table's row type, if it has one
16231 */
16232 if (OidIsValid(tuple_class->reltype))
16233 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16234
16235 /*
16236 * If we are operating on a table or materialized view, also change
16237 * the ownership of any indexes and sequences that belong to the
16238 * relation, as well as its toast table (if it has one).
16239 */
16240 if (tuple_class->relkind == RELKIND_RELATION ||
16241 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16242 tuple_class->relkind == RELKIND_MATVIEW ||
16243 tuple_class->relkind == RELKIND_TOASTVALUE)
16244 {
16245 List *index_oid_list;
16246 ListCell *i;
16247
16248 /* Find all the indexes belonging to this relation */
16249 index_oid_list = RelationGetIndexList(target_rel);
16250
16251 /* For each index, recursively change its ownership */
16252 foreach(i, index_oid_list)
16253 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16254
16255 list_free(index_oid_list);
16256 }
16257
16258 /* If it has a toast table, recurse to change its ownership */
16259 if (tuple_class->reltoastrelid != InvalidOid)
16260 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16261 true, lockmode);
16262
16263 /* If it has dependent sequences, recurse to change them too */
16264 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16265 }
16266
16267 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16268
16269 ReleaseSysCache(tuple);
16270 table_close(class_rel, RowExclusiveLock);
16271 relation_close(target_rel, NoLock);
16272}
16273
16274/*
16275 * change_owner_fix_column_acls
16276 *
16277 * Helper function for ATExecChangeOwner. Scan the columns of the table
16278 * and fix any non-null column ACLs to reflect the new owner.
16279 */
16280static void
16281change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16282{
16283 Relation attRelation;
16284 SysScanDesc scan;
16285 ScanKeyData key[1];
16286 HeapTuple attributeTuple;
16287
16288 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16289 ScanKeyInit(&key[0],
16290 Anum_pg_attribute_attrelid,
16291 BTEqualStrategyNumber, F_OIDEQ,
16292 ObjectIdGetDatum(relationOid));
16293 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16294 true, NULL, 1, key);
16295 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16296 {
16297 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16298 Datum repl_val[Natts_pg_attribute];
16299 bool repl_null[Natts_pg_attribute];
16300 bool repl_repl[Natts_pg_attribute];
16301 Acl *newAcl;
16302 Datum aclDatum;
16303 bool isNull;
16304 HeapTuple newtuple;
16305
16306 /* Ignore dropped columns */
16307 if (att->attisdropped)
16308 continue;
16309
16310 aclDatum = heap_getattr(attributeTuple,
16311 Anum_pg_attribute_attacl,
16312 RelationGetDescr(attRelation),
16313 &isNull);
16314 /* Null ACLs do not require changes */
16315 if (isNull)
16316 continue;
16317
16318 memset(repl_null, false, sizeof(repl_null));
16319 memset(repl_repl, false, sizeof(repl_repl));
16320
16321 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16322 oldOwnerId, newOwnerId);
16323 repl_repl[Anum_pg_attribute_attacl - 1] = true;
16324 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16325
16326 newtuple = heap_modify_tuple(attributeTuple,
16327 RelationGetDescr(attRelation),
16328 repl_val, repl_null, repl_repl);
16329
16330 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16331
16332 heap_freetuple(newtuple);
16333 }
16334 systable_endscan(scan);
16335 table_close(attRelation, RowExclusiveLock);
16336}
16337
16338/*
16339 * change_owner_recurse_to_sequences
16340 *
16341 * Helper function for ATExecChangeOwner. Examines pg_depend searching
16342 * for sequences that are dependent on serial columns, and changes their
16343 * ownership.
16344 */
16345static void
16346change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16347{
16348 Relation depRel;
16349 SysScanDesc scan;
16350 ScanKeyData key[2];
16351 HeapTuple tup;
16352
16353 /*
16354 * SERIAL sequences are those having an auto dependency on one of the
16355 * table's columns (we don't care *which* column, exactly).
16356 */
16357 depRel = table_open(DependRelationId, AccessShareLock);
16358
16359 ScanKeyInit(&key[0],
16360 Anum_pg_depend_refclassid,
16361 BTEqualStrategyNumber, F_OIDEQ,
16362 ObjectIdGetDatum(RelationRelationId));
16363 ScanKeyInit(&key[1],
16364 Anum_pg_depend_refobjid,
16365 BTEqualStrategyNumber, F_OIDEQ,
16366 ObjectIdGetDatum(relationOid));
16367 /* we leave refobjsubid unspecified */
16368
16369 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16370 NULL, 2, key);
16371
16372 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16373 {
16374 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16375 Relation seqRel;
16376
16377 /* skip dependencies other than auto dependencies on columns */
16378 if (depForm->refobjsubid == 0 ||
16379 depForm->classid != RelationRelationId ||
16380 depForm->objsubid != 0 ||
16381 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16382 continue;
16383
16384 /* Use relation_open just in case it's an index */
16385 seqRel = relation_open(depForm->objid, lockmode);
16386
16387 /* skip non-sequence relations */
16388 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16389 {
16390 /* No need to keep the lock */
16391 relation_close(seqRel, lockmode);
16392 continue;
16393 }
16394
16395 /* We don't need to close the sequence while we alter it. */
16396 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16397
16398 /* Now we can close it. Keep the lock till end of transaction. */
16399 relation_close(seqRel, NoLock);
16400 }
16401
16402 systable_endscan(scan);
16403
16405}
16406
16407/*
16408 * ALTER TABLE CLUSTER ON
16409 *
16410 * The only thing we have to do is to change the indisclustered bits.
16411 *
16412 * Return the address of the new clustering index.
16413 */
16414static ObjectAddress
16415ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16416{
16417 Oid indexOid;
16418 ObjectAddress address;
16419
16420 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16421
16422 if (!OidIsValid(indexOid))
16423 ereport(ERROR,
16424 (errcode(ERRCODE_UNDEFINED_OBJECT),
16425 errmsg("index \"%s\" for table \"%s\" does not exist",
16426 indexName, RelationGetRelationName(rel))));
16427
16428 /* Check index is valid to cluster on */
16429 check_index_is_clusterable(rel, indexOid, lockmode);
16430
16431 /* And do the work */
16432 mark_index_clustered(rel, indexOid, false);
16433
16434 ObjectAddressSet(address,
16435 RelationRelationId, indexOid);
16436
16437 return address;
16438}
16439
16440/*
16441 * ALTER TABLE SET WITHOUT CLUSTER
16442 *
16443 * We have to find any indexes on the table that have indisclustered bit
16444 * set and turn it off.
16445 */
16446static void
16448{
16449 mark_index_clustered(rel, InvalidOid, false);
16450}
16451
16452/*
16453 * Preparation phase for SET ACCESS METHOD
16454 *
16455 * Check that the access method exists and determine whether a change is
16456 * actually needed.
16457 */
16458static void
16460{
16461 Oid amoid;
16462
16463 /*
16464 * Look up the access method name and check that it differs from the
16465 * table's current AM. If DEFAULT was specified for a partitioned table
16466 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16467 */
16468 if (amname != NULL)
16469 amoid = get_table_am_oid(amname, false);
16470 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16471 amoid = InvalidOid;
16472 else
16474
16475 /* if it's a match, phase 3 doesn't need to do anything */
16476 if (rel->rd_rel->relam == amoid)
16477 return;
16478
16479 /* Save info for Phase 3 to do the real work */
16481 tab->newAccessMethod = amoid;
16482 tab->chgAccessMethod = true;
16483}
16484
16485/*
16486 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16487 * storage that have an interest in preserving AM.
16488 *
16489 * Since these have no storage, setting the access method is a catalog only
16490 * operation.
16491 */
16492static void
16494{
16495 Relation pg_class;
16496 Oid oldAccessMethodId;
16497 HeapTuple tuple;
16498 Form_pg_class rd_rel;
16499 Oid reloid = RelationGetRelid(rel);
16500
16501 /*
16502 * Shouldn't be called on relations having storage; these are processed in
16503 * phase 3.
16504 */
16505 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16506
16507 /* Get a modifiable copy of the relation's pg_class row. */
16508 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16509
16510 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16511 if (!HeapTupleIsValid(tuple))
16512 elog(ERROR, "cache lookup failed for relation %u", reloid);
16513 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16514
16515 /* Update the pg_class row. */
16516 oldAccessMethodId = rd_rel->relam;
16517 rd_rel->relam = newAccessMethodId;
16518
16519 /* Leave if no update required */
16520 if (rd_rel->relam == oldAccessMethodId)
16521 {
16522 heap_freetuple(tuple);
16523 table_close(pg_class, RowExclusiveLock);
16524 return;
16525 }
16526
16527 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16528
16529 /*
16530 * Update the dependency on the new access method. No dependency is added
16531 * if the new access method is InvalidOid (default case). Be very careful
16532 * that this has to compare the previous value stored in pg_class with the
16533 * new one.
16534 */
16535 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16536 {
16537 ObjectAddress relobj,
16538 referenced;
16539
16540 /*
16541 * New access method is defined and there was no dependency
16542 * previously, so record a new one.
16543 */
16544 ObjectAddressSet(relobj, RelationRelationId, reloid);
16545 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16546 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16547 }
16548 else if (OidIsValid(oldAccessMethodId) &&
16549 !OidIsValid(rd_rel->relam))
16550 {
16551 /*
16552 * There was an access method defined, and no new one, so just remove
16553 * the existing dependency.
16554 */
16555 deleteDependencyRecordsForClass(RelationRelationId, reloid,
16556 AccessMethodRelationId,
16558 }
16559 else
16560 {
16561 Assert(OidIsValid(oldAccessMethodId) &&
16562 OidIsValid(rd_rel->relam));
16563
16564 /* Both are valid, so update the dependency */
16565 changeDependencyFor(RelationRelationId, reloid,
16566 AccessMethodRelationId,
16567 oldAccessMethodId, rd_rel->relam);
16568 }
16569
16570 /* make the relam and dependency changes visible */
16572
16573 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16574
16575 heap_freetuple(tuple);
16576 table_close(pg_class, RowExclusiveLock);
16577}
16578
16579/*
16580 * ALTER TABLE SET TABLESPACE
16581 */
16582static void
16583ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16584{
16585 Oid tablespaceId;
16586
16587 /* Check that the tablespace exists */
16588 tablespaceId = get_tablespace_oid(tablespacename, false);
16589
16590 /* Check permissions except when moving to database's default */
16591 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16592 {
16593 AclResult aclresult;
16594
16595 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16596 if (aclresult != ACLCHECK_OK)
16597 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16598 }
16599
16600 /* Save info for Phase 3 to do the real work */
16601 if (OidIsValid(tab->newTableSpace))
16602 ereport(ERROR,
16603 (errcode(ERRCODE_SYNTAX_ERROR),
16604 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16605
16606 tab->newTableSpace = tablespaceId;
16607}
16608
16609/*
16610 * Set, reset, or replace reloptions.
16611 */
16612static void
16614 LOCKMODE lockmode)
16615{
16616 Oid relid;
16617 Relation pgclass;
16618 HeapTuple tuple;
16619 HeapTuple newtuple;
16620 Datum datum;
16621 Datum newOptions;
16622 Datum repl_val[Natts_pg_class];
16623 bool repl_null[Natts_pg_class];
16624 bool repl_repl[Natts_pg_class];
16625 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16626
16627 if (defList == NIL && operation != AT_ReplaceRelOptions)
16628 return; /* nothing to do */
16629
16630 pgclass = table_open(RelationRelationId, RowExclusiveLock);
16631
16632 /* Fetch heap tuple */
16633 relid = RelationGetRelid(rel);
16634 tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16635 if (!HeapTupleIsValid(tuple))
16636 elog(ERROR, "cache lookup failed for relation %u", relid);
16637
16638 if (operation == AT_ReplaceRelOptions)
16639 {
16640 /*
16641 * If we're supposed to replace the reloptions list, we just pretend
16642 * there were none before.
16643 */
16644 datum = (Datum) 0;
16645 }
16646 else
16647 {
16648 bool isnull;
16649
16650 /* Get the old reloptions */
16651 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16652 &isnull);
16653 if (isnull)
16654 datum = (Datum) 0;
16655 }
16656
16657 /* Generate new proposed reloptions (text array) */
16658 newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16659 operation == AT_ResetRelOptions);
16660
16661 /* Validate */
16662 switch (rel->rd_rel->relkind)
16663 {
16664 case RELKIND_RELATION:
16665 case RELKIND_MATVIEW:
16666 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16667 break;
16668 case RELKIND_PARTITIONED_TABLE:
16669 (void) partitioned_table_reloptions(newOptions, true);
16670 break;
16671 case RELKIND_VIEW:
16672 (void) view_reloptions(newOptions, true);
16673 break;
16674 case RELKIND_INDEX:
16675 case RELKIND_PARTITIONED_INDEX:
16676 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16677 break;
16678 case RELKIND_TOASTVALUE:
16679 /* fall through to error -- shouldn't ever get here */
16680 default:
16681 ereport(ERROR,
16682 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16683 errmsg("cannot set options for relation \"%s\"",
16685 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16686 break;
16687 }
16688
16689 /* Special-case validation of view options */
16690 if (rel->rd_rel->relkind == RELKIND_VIEW)
16691 {
16692 Query *view_query = get_view_query(rel);
16693 List *view_options = untransformRelOptions(newOptions);
16694 ListCell *cell;
16695 bool check_option = false;
16696
16697 foreach(cell, view_options)
16698 {
16699 DefElem *defel = (DefElem *) lfirst(cell);
16700
16701 if (strcmp(defel->defname, "check_option") == 0)
16702 check_option = true;
16703 }
16704
16705 /*
16706 * If the check option is specified, look to see if the view is
16707 * actually auto-updatable or not.
16708 */
16709 if (check_option)
16710 {
16711 const char *view_updatable_error =
16712 view_query_is_auto_updatable(view_query, true);
16713
16714 if (view_updatable_error)
16715 ereport(ERROR,
16716 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16717 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16718 errhint("%s", _(view_updatable_error))));
16719 }
16720 }
16721
16722 /*
16723 * All we need do here is update the pg_class row; the new options will be
16724 * propagated into relcaches during post-commit cache inval.
16725 */
16726 memset(repl_val, 0, sizeof(repl_val));
16727 memset(repl_null, false, sizeof(repl_null));
16728 memset(repl_repl, false, sizeof(repl_repl));
16729
16730 if (newOptions != (Datum) 0)
16731 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16732 else
16733 repl_null[Anum_pg_class_reloptions - 1] = true;
16734
16735 repl_repl[Anum_pg_class_reloptions - 1] = true;
16736
16737 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16738 repl_val, repl_null, repl_repl);
16739
16740 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16741 UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16742
16743 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16744
16745 heap_freetuple(newtuple);
16746
16747 ReleaseSysCache(tuple);
16748
16749 /* repeat the whole exercise for the toast table, if there's one */
16750 if (OidIsValid(rel->rd_rel->reltoastrelid))
16751 {
16752 Relation toastrel;
16753 Oid toastid = rel->rd_rel->reltoastrelid;
16754
16755 toastrel = table_open(toastid, lockmode);
16756
16757 /* Fetch heap tuple */
16758 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16759 if (!HeapTupleIsValid(tuple))
16760 elog(ERROR, "cache lookup failed for relation %u", toastid);
16761
16762 if (operation == AT_ReplaceRelOptions)
16763 {
16764 /*
16765 * If we're supposed to replace the reloptions list, we just
16766 * pretend there were none before.
16767 */
16768 datum = (Datum) 0;
16769 }
16770 else
16771 {
16772 bool isnull;
16773
16774 /* Get the old reloptions */
16775 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16776 &isnull);
16777 if (isnull)
16778 datum = (Datum) 0;
16779 }
16780
16781 newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16782 false, operation == AT_ResetRelOptions);
16783
16784 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16785
16786 memset(repl_val, 0, sizeof(repl_val));
16787 memset(repl_null, false, sizeof(repl_null));
16788 memset(repl_repl, false, sizeof(repl_repl));
16789
16790 if (newOptions != (Datum) 0)
16791 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16792 else
16793 repl_null[Anum_pg_class_reloptions - 1] = true;
16794
16795 repl_repl[Anum_pg_class_reloptions - 1] = true;
16796
16797 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16798 repl_val, repl_null, repl_repl);
16799
16800 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16801
16802 InvokeObjectPostAlterHookArg(RelationRelationId,
16803 RelationGetRelid(toastrel), 0,
16804 InvalidOid, true);
16805
16806 heap_freetuple(newtuple);
16807
16808 ReleaseSysCache(tuple);
16809
16810 table_close(toastrel, NoLock);
16811 }
16812
16813 table_close(pgclass, RowExclusiveLock);
16814}
16815
16816/*
16817 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16818 * rewriting to be done, so we just want to copy the data as fast as possible.
16819 */
16820static void
16821ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16822{
16823 Relation rel;
16824 Oid reltoastrelid;
16825 RelFileNumber newrelfilenumber;
16826 RelFileLocator newrlocator;
16827 List *reltoastidxids = NIL;
16828 ListCell *lc;
16829
16830 /*
16831 * Need lock here in case we are recursing to toast table or index
16832 */
16833 rel = relation_open(tableOid, lockmode);
16834
16835 /* Check first if relation can be moved to new tablespace */
16836 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16837 {
16838 InvokeObjectPostAlterHook(RelationRelationId,
16839 RelationGetRelid(rel), 0);
16840 relation_close(rel, NoLock);
16841 return;
16842 }
16843
16844 reltoastrelid = rel->rd_rel->reltoastrelid;
16845 /* Fetch the list of indexes on toast relation if necessary */
16846 if (OidIsValid(reltoastrelid))
16847 {
16848 Relation toastRel = relation_open(reltoastrelid, lockmode);
16849
16850 reltoastidxids = RelationGetIndexList(toastRel);
16851 relation_close(toastRel, lockmode);
16852 }
16853
16854 /*
16855 * Relfilenumbers are not unique in databases across tablespaces, so we
16856 * need to allocate a new one in the new tablespace.
16857 */
16858 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16859 rel->rd_rel->relpersistence);
16860
16861 /* Open old and new relation */
16862 newrlocator = rel->rd_locator;
16863 newrlocator.relNumber = newrelfilenumber;
16864 newrlocator.spcOid = newTableSpace;
16865
16866 /* hand off to AM to actually create new rel storage and copy the data */
16867 if (rel->rd_rel->relkind == RELKIND_INDEX)
16868 {
16869 index_copy_data(rel, newrlocator);
16870 }
16871 else
16872 {
16873 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16874 table_relation_copy_data(rel, &newrlocator);
16875 }
16876
16877 /*
16878 * Update the pg_class row.
16879 *
16880 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16881 * executed on pg_class or its indexes (the above copy wouldn't contain
16882 * the updated pg_class entry), but that's forbidden with
16883 * CheckRelationTableSpaceMove().
16884 */
16885 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16886
16887 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16888
16890
16891 relation_close(rel, NoLock);
16892
16893 /* Make sure the reltablespace change is visible */
16895
16896 /* Move associated toast relation and/or indexes, too */
16897 if (OidIsValid(reltoastrelid))
16898 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16899 foreach(lc, reltoastidxids)
16900 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16901
16902 /* Clean up */
16903 list_free(reltoastidxids);
16904}
16905
16906/*
16907 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16908 * storage that have an interest in preserving tablespace.
16909 *
16910 * Since these have no storage the tablespace can be updated with a simple
16911 * metadata only operation to update the tablespace.
16912 */
16913static void
16915{
16916 /*
16917 * Shouldn't be called on relations having storage; these are processed in
16918 * phase 3.
16919 */
16920 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16921
16922 /* check if relation can be moved to its new tablespace */
16923 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16924 {
16925 InvokeObjectPostAlterHook(RelationRelationId,
16926 RelationGetRelid(rel),
16927 0);
16928 return;
16929 }
16930
16931 /* Update can be done, so change reltablespace */
16932 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16933
16934 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16935
16936 /* Make sure the reltablespace change is visible */
16938}
16939
16940/*
16941 * Alter Table ALL ... SET TABLESPACE
16942 *
16943 * Allows a user to move all objects of some type in a given tablespace in the
16944 * current database to another tablespace. Objects can be chosen based on the
16945 * owner of the object also, to allow users to move only their objects.
16946 * The user must have CREATE rights on the new tablespace, as usual. The main
16947 * permissions handling is done by the lower-level table move function.
16948 *
16949 * All to-be-moved objects are locked first. If NOWAIT is specified and the
16950 * lock can't be acquired then we ereport(ERROR).
16951 */
16952Oid
16954{
16955 List *relations = NIL;
16956 ListCell *l;
16957 ScanKeyData key[1];
16958 Relation rel;
16959 TableScanDesc scan;
16960 HeapTuple tuple;
16961 Oid orig_tablespaceoid;
16962 Oid new_tablespaceoid;
16963 List *role_oids = roleSpecsToIds(stmt->roles);
16964
16965 /* Ensure we were not asked to move something we can't */
16966 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16967 stmt->objtype != OBJECT_MATVIEW)
16968 ereport(ERROR,
16969 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16970 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16971
16972 /* Get the orig and new tablespace OIDs */
16973 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16974 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16975
16976 /* Can't move shared relations in to or out of pg_global */
16977 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16978 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16979 new_tablespaceoid == GLOBALTABLESPACE_OID)
16980 ereport(ERROR,
16981 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16982 errmsg("cannot move relations in to or out of pg_global tablespace")));
16983
16984 /*
16985 * Must have CREATE rights on the new tablespace, unless it is the
16986 * database default tablespace (which all users implicitly have CREATE
16987 * rights on).
16988 */
16989 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16990 {
16991 AclResult aclresult;
16992
16993 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16994 ACL_CREATE);
16995 if (aclresult != ACLCHECK_OK)
16997 get_tablespace_name(new_tablespaceoid));
16998 }
16999
17000 /*
17001 * Now that the checks are done, check if we should set either to
17002 * InvalidOid because it is our database's default tablespace.
17003 */
17004 if (orig_tablespaceoid == MyDatabaseTableSpace)
17005 orig_tablespaceoid = InvalidOid;
17006
17007 if (new_tablespaceoid == MyDatabaseTableSpace)
17008 new_tablespaceoid = InvalidOid;
17009
17010 /* no-op */
17011 if (orig_tablespaceoid == new_tablespaceoid)
17012 return new_tablespaceoid;
17013
17014 /*
17015 * Walk the list of objects in the tablespace and move them. This will
17016 * only find objects in our database, of course.
17017 */
17018 ScanKeyInit(&key[0],
17019 Anum_pg_class_reltablespace,
17020 BTEqualStrategyNumber, F_OIDEQ,
17021 ObjectIdGetDatum(orig_tablespaceoid));
17022
17023 rel = table_open(RelationRelationId, AccessShareLock);
17024 scan = table_beginscan_catalog(rel, 1, key);
17025 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17026 {
17027 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17028 Oid relOid = relForm->oid;
17029
17030 /*
17031 * Do not move objects in pg_catalog as part of this, if an admin
17032 * really wishes to do so, they can issue the individual ALTER
17033 * commands directly.
17034 *
17035 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17036 * (TOAST will be moved with the main table).
17037 */
17038 if (IsCatalogNamespace(relForm->relnamespace) ||
17039 relForm->relisshared ||
17040 isAnyTempNamespace(relForm->relnamespace) ||
17041 IsToastNamespace(relForm->relnamespace))
17042 continue;
17043
17044 /* Only move the object type requested */
17045 if ((stmt->objtype == OBJECT_TABLE &&
17046 relForm->relkind != RELKIND_RELATION &&
17047 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17048 (stmt->objtype == OBJECT_INDEX &&
17049 relForm->relkind != RELKIND_INDEX &&
17050 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17051 (stmt->objtype == OBJECT_MATVIEW &&
17052 relForm->relkind != RELKIND_MATVIEW))
17053 continue;
17054
17055 /* Check if we are only moving objects owned by certain roles */
17056 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17057 continue;
17058
17059 /*
17060 * Handle permissions-checking here since we are locking the tables
17061 * and also to avoid doing a bunch of work only to fail part-way. Note
17062 * that permissions will also be checked by AlterTableInternal().
17063 *
17064 * Caller must be considered an owner on the table to move it.
17065 */
17066 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17068 NameStr(relForm->relname));
17069
17070 if (stmt->nowait &&
17072 ereport(ERROR,
17073 (errcode(ERRCODE_OBJECT_IN_USE),
17074 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17075 get_namespace_name(relForm->relnamespace),
17076 NameStr(relForm->relname))));
17077 else
17079
17080 /* Add to our list of objects to move */
17081 relations = lappend_oid(relations, relOid);
17082 }
17083
17084 table_endscan(scan);
17086
17087 if (relations == NIL)
17089 (errcode(ERRCODE_NO_DATA_FOUND),
17090 errmsg("no matching relations in tablespace \"%s\" found",
17091 orig_tablespaceoid == InvalidOid ? "(database default)" :
17092 get_tablespace_name(orig_tablespaceoid))));
17093
17094 /* Everything is locked, loop through and move all of the relations. */
17095 foreach(l, relations)
17096 {
17097 List *cmds = NIL;
17099
17101 cmd->name = stmt->new_tablespacename;
17102
17103 cmds = lappend(cmds, cmd);
17104
17106 /* OID is set by AlterTableInternal */
17107 AlterTableInternal(lfirst_oid(l), cmds, false);
17109 }
17110
17111 return new_tablespaceoid;
17112}
17113
17114static void
17116{
17117 SMgrRelation dstrel;
17118
17119 /*
17120 * Since we copy the file directly without looking at the shared buffers,
17121 * we'd better first flush out any pages of the source relation that are
17122 * in shared buffers. We assume no new changes will be made while we are
17123 * holding exclusive lock on the rel.
17124 */
17126
17127 /*
17128 * Create and copy all forks of the relation, and schedule unlinking of
17129 * old physical files.
17130 *
17131 * NOTE: any conflict in relfilenumber value will be caught in
17132 * RelationCreateStorage().
17133 */
17134 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17135
17136 /* copy main fork */
17138 rel->rd_rel->relpersistence);
17139
17140 /* copy those extra forks that exist */
17141 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17142 forkNum <= MAX_FORKNUM; forkNum++)
17143 {
17144 if (smgrexists(RelationGetSmgr(rel), forkNum))
17145 {
17146 smgrcreate(dstrel, forkNum, false);
17147
17148 /*
17149 * WAL log creation if the relation is persistent, or this is the
17150 * init fork of an unlogged relation.
17151 */
17152 if (RelationIsPermanent(rel) ||
17153 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17154 forkNum == INIT_FORKNUM))
17155 log_smgrcreate(&newrlocator, forkNum);
17156 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17157 rel->rd_rel->relpersistence);
17158 }
17159 }
17160
17161 /* drop old relation, and close new one */
17163 smgrclose(dstrel);
17164}
17165
17166/*
17167 * ALTER TABLE ENABLE/DISABLE TRIGGER
17168 *
17169 * We just pass this off to trigger.c.
17170 */
17171static void
17172ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17173 char fires_when, bool skip_system, bool recurse,
17174 LOCKMODE lockmode)
17175{
17176 EnableDisableTrigger(rel, trigname, InvalidOid,
17177 fires_when, skip_system, recurse,
17178 lockmode);
17179
17180 InvokeObjectPostAlterHook(RelationRelationId,
17181 RelationGetRelid(rel), 0);
17182}
17183
17184/*
17185 * ALTER TABLE ENABLE/DISABLE RULE
17186 *
17187 * We just pass this off to rewriteDefine.c.
17188 */
17189static void
17190ATExecEnableDisableRule(Relation rel, const char *rulename,
17191 char fires_when, LOCKMODE lockmode)
17192{
17193 EnableDisableRule(rel, rulename, fires_when);
17194
17195 InvokeObjectPostAlterHook(RelationRelationId,
17196 RelationGetRelid(rel), 0);
17197}
17198
17199/*
17200 * ALTER TABLE INHERIT
17201 *
17202 * Add a parent to the child's parents. This verifies that all the columns and
17203 * check constraints of the parent appear in the child and that they have the
17204 * same data types and expressions.
17205 */
17206static void
17208{
17209 if (child_rel->rd_rel->reloftype)
17210 ereport(ERROR,
17211 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17212 errmsg("cannot change inheritance of typed table")));
17213
17214 if (child_rel->rd_rel->relispartition)
17215 ereport(ERROR,
17216 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17217 errmsg("cannot change inheritance of a partition")));
17218
17219 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17220 ereport(ERROR,
17221 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17222 errmsg("cannot change inheritance of partitioned table")));
17223}
17224
17225/*
17226 * Return the address of the new parent relation.
17227 */
17228static ObjectAddress
17229ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17230{
17231 Relation parent_rel;
17232 List *children;
17233 ObjectAddress address;
17234 const char *trigger_name;
17235
17236 /*
17237 * A self-exclusive lock is needed here. See the similar case in
17238 * MergeAttributes() for a full explanation.
17239 */
17240 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17241
17242 /*
17243 * Must be owner of both parent and child -- child was checked by
17244 * ATSimplePermissions call in ATPrepCmd
17245 */
17248
17249 /* Permanent rels cannot inherit from temporary ones */
17250 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17251 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17252 ereport(ERROR,
17253 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17254 errmsg("cannot inherit from temporary relation \"%s\"",
17255 RelationGetRelationName(parent_rel))));
17256
17257 /* If parent rel is temp, it must belong to this session */
17258 if (RELATION_IS_OTHER_TEMP(parent_rel))
17259 ereport(ERROR,
17260 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17261 errmsg("cannot inherit from temporary relation of another session")));
17262
17263 /* Ditto for the child */
17264 if (RELATION_IS_OTHER_TEMP(child_rel))
17265 ereport(ERROR,
17266 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17267 errmsg("cannot inherit to temporary relation of another session")));
17268
17269 /* Prevent partitioned tables from becoming inheritance parents */
17270 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17271 ereport(ERROR,
17272 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17273 errmsg("cannot inherit from partitioned table \"%s\"",
17274 parent->relname)));
17275
17276 /* Likewise for partitions */
17277 if (parent_rel->rd_rel->relispartition)
17278 ereport(ERROR,
17279 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17280 errmsg("cannot inherit from a partition")));
17281
17282 /*
17283 * Prevent circularity by seeing if proposed parent inherits from child.
17284 * (In particular, this disallows making a rel inherit from itself.)
17285 *
17286 * This is not completely bulletproof because of race conditions: in
17287 * multi-level inheritance trees, someone else could concurrently be
17288 * making another inheritance link that closes the loop but does not join
17289 * either of the rels we have locked. Preventing that seems to require
17290 * exclusive locks on the entire inheritance tree, which is a cure worse
17291 * than the disease. find_all_inheritors() will cope with circularity
17292 * anyway, so don't sweat it too much.
17293 *
17294 * We use weakest lock we can on child's children, namely AccessShareLock.
17295 */
17296 children = find_all_inheritors(RelationGetRelid(child_rel),
17297 AccessShareLock, NULL);
17298
17299 if (list_member_oid(children, RelationGetRelid(parent_rel)))
17300 ereport(ERROR,
17301 (errcode(ERRCODE_DUPLICATE_TABLE),
17302 errmsg("circular inheritance not allowed"),
17303 errdetail("\"%s\" is already a child of \"%s\".",
17304 parent->relname,
17305 RelationGetRelationName(child_rel))));
17306
17307 /*
17308 * If child_rel has row-level triggers with transition tables, we
17309 * currently don't allow it to become an inheritance child. See also
17310 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17311 */
17312 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17313 if (trigger_name != NULL)
17314 ereport(ERROR,
17315 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17316 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17317 trigger_name, RelationGetRelationName(child_rel)),
17318 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17319
17320 /* OK to create inheritance */
17321 CreateInheritance(child_rel, parent_rel, false);
17322
17323 ObjectAddressSet(address, RelationRelationId,
17324 RelationGetRelid(parent_rel));
17325
17326 /* keep our lock on the parent relation until commit */
17327 table_close(parent_rel, NoLock);
17328
17329 return address;
17330}
17331
17332/*
17333 * CreateInheritance
17334 * Catalog manipulation portion of creating inheritance between a child
17335 * table and a parent table.
17336 *
17337 * Common to ATExecAddInherit() and ATExecAttachPartition().
17338 */
17339static void
17340CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17341{
17342 Relation catalogRelation;
17343 SysScanDesc scan;
17345 HeapTuple inheritsTuple;
17346 int32 inhseqno;
17347
17348 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17349 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17350
17351 /*
17352 * Check for duplicates in the list of parents, and determine the highest
17353 * inhseqno already present; we'll use the next one for the new parent.
17354 * Also, if proposed child is a partition, it cannot already be
17355 * inheriting.
17356 *
17357 * Note: we do not reject the case where the child already inherits from
17358 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17359 */
17361 Anum_pg_inherits_inhrelid,
17362 BTEqualStrategyNumber, F_OIDEQ,
17364 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17365 true, NULL, 1, &key);
17366
17367 /* inhseqno sequences start at 1 */
17368 inhseqno = 0;
17369 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17370 {
17371 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17372
17373 if (inh->inhparent == RelationGetRelid(parent_rel))
17374 ereport(ERROR,
17375 (errcode(ERRCODE_DUPLICATE_TABLE),
17376 errmsg("relation \"%s\" would be inherited from more than once",
17377 RelationGetRelationName(parent_rel))));
17378
17379 if (inh->inhseqno > inhseqno)
17380 inhseqno = inh->inhseqno;
17381 }
17382 systable_endscan(scan);
17383
17384 /* Match up the columns and bump attinhcount as needed */
17385 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17386
17387 /* Match up the constraints and bump coninhcount as needed */
17388 MergeConstraintsIntoExisting(child_rel, parent_rel);
17389
17390 /*
17391 * OK, it looks valid. Make the catalog entries that show inheritance.
17392 */
17394 RelationGetRelid(parent_rel),
17395 inhseqno + 1,
17396 catalogRelation,
17397 parent_rel->rd_rel->relkind ==
17398 RELKIND_PARTITIONED_TABLE);
17399
17400 /* Now we're done with pg_inherits */
17401 table_close(catalogRelation, RowExclusiveLock);
17402}
17403
17404/*
17405 * Obtain the source-text form of the constraint expression for a check
17406 * constraint, given its pg_constraint tuple
17407 */
17408static char *
17410{
17412 bool isnull;
17413 Datum attr;
17414 Datum expr;
17415
17416 con = (Form_pg_constraint) GETSTRUCT(contup);
17417 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17418 if (isnull)
17419 elog(ERROR, "null conbin for constraint %u", con->oid);
17420
17421 expr = DirectFunctionCall2(pg_get_expr, attr,
17422 ObjectIdGetDatum(con->conrelid));
17423 return TextDatumGetCString(expr);
17424}
17425
17426/*
17427 * Determine whether two check constraints are functionally equivalent
17428 *
17429 * The test we apply is to see whether they reverse-compile to the same
17430 * source string. This insulates us from issues like whether attributes
17431 * have the same physical column numbers in parent and child relations.
17432 *
17433 * Note that we ignore enforceability as there are cases where constraints
17434 * with differing enforceability are allowed.
17435 */
17436static bool
17438{
17441
17442 if (acon->condeferrable != bcon->condeferrable ||
17443 acon->condeferred != bcon->condeferred ||
17444 strcmp(decompile_conbin(a, tupleDesc),
17445 decompile_conbin(b, tupleDesc)) != 0)
17446 return false;
17447 else
17448 return true;
17449}
17450
17451/*
17452 * Check columns in child table match up with columns in parent, and increment
17453 * their attinhcount.
17454 *
17455 * Called by CreateInheritance
17456 *
17457 * Currently all parent columns must be found in child. Missing columns are an
17458 * error. One day we might consider creating new columns like CREATE TABLE
17459 * does. However, that is widely unpopular --- in the common use case of
17460 * partitioned tables it's a foot-gun.
17461 *
17462 * The data type must match exactly. If the parent column is NOT NULL then
17463 * the child must be as well. Defaults are not compared, however.
17464 */
17465static void
17466MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17467{
17468 Relation attrrel;
17469 TupleDesc parent_desc;
17470
17471 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17472 parent_desc = RelationGetDescr(parent_rel);
17473
17474 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17475 {
17476 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17477 char *parent_attname = NameStr(parent_att->attname);
17478 HeapTuple tuple;
17479
17480 /* Ignore dropped columns in the parent. */
17481 if (parent_att->attisdropped)
17482 continue;
17483
17484 /* Find same column in child (matching on column name). */
17485 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17486 if (HeapTupleIsValid(tuple))
17487 {
17488 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17489
17490 if (parent_att->atttypid != child_att->atttypid ||
17491 parent_att->atttypmod != child_att->atttypmod)
17492 ereport(ERROR,
17493 (errcode(ERRCODE_DATATYPE_MISMATCH),
17494 errmsg("child table \"%s\" has different type for column \"%s\"",
17495 RelationGetRelationName(child_rel), parent_attname)));
17496
17497 if (parent_att->attcollation != child_att->attcollation)
17498 ereport(ERROR,
17499 (errcode(ERRCODE_COLLATION_MISMATCH),
17500 errmsg("child table \"%s\" has different collation for column \"%s\"",
17501 RelationGetRelationName(child_rel), parent_attname)));
17502
17503 /*
17504 * If the parent has a not-null constraint that's not NO INHERIT,
17505 * make sure the child has one too.
17506 *
17507 * Other constraints are checked elsewhere.
17508 */
17509 if (parent_att->attnotnull && !child_att->attnotnull)
17510 {
17511 HeapTuple contup;
17512
17513 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17514 parent_att->attnum);
17515 if (HeapTupleIsValid(contup) &&
17516 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17517 ereport(ERROR,
17518 errcode(ERRCODE_DATATYPE_MISMATCH),
17519 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17520 parent_attname, RelationGetRelationName(child_rel)));
17521 }
17522
17523 /*
17524 * Child column must be generated if and only if parent column is.
17525 */
17526 if (parent_att->attgenerated && !child_att->attgenerated)
17527 ereport(ERROR,
17528 (errcode(ERRCODE_DATATYPE_MISMATCH),
17529 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17530 if (child_att->attgenerated && !parent_att->attgenerated)
17531 ereport(ERROR,
17532 (errcode(ERRCODE_DATATYPE_MISMATCH),
17533 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17534
17535 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17536 ereport(ERROR,
17537 (errcode(ERRCODE_DATATYPE_MISMATCH),
17538 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17539 errdetail("Parent column is %s, child column is %s.",
17540 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17541 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17542
17543 /*
17544 * Regular inheritance children are independent enough not to
17545 * inherit identity columns. But partitions are integral part of
17546 * a partitioned table and inherit identity column.
17547 */
17548 if (ispartition)
17549 child_att->attidentity = parent_att->attidentity;
17550
17551 /*
17552 * OK, bump the child column's inheritance count. (If we fail
17553 * later on, this change will just roll back.)
17554 */
17555 if (pg_add_s16_overflow(child_att->attinhcount, 1,
17556 &child_att->attinhcount))
17557 ereport(ERROR,
17558 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17559 errmsg("too many inheritance parents"));
17560
17561 /*
17562 * In case of partitions, we must enforce that value of attislocal
17563 * is same in all partitions. (Note: there are only inherited
17564 * attributes in partitions)
17565 */
17566 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17567 {
17568 Assert(child_att->attinhcount == 1);
17569 child_att->attislocal = false;
17570 }
17571
17572 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17573 heap_freetuple(tuple);
17574 }
17575 else
17576 {
17577 ereport(ERROR,
17578 (errcode(ERRCODE_DATATYPE_MISMATCH),
17579 errmsg("child table is missing column \"%s\"", parent_attname)));
17580 }
17581 }
17582
17583 table_close(attrrel, RowExclusiveLock);
17584}
17585
17586/*
17587 * Check constraints in child table match up with constraints in parent,
17588 * and increment their coninhcount.
17589 *
17590 * Constraints that are marked ONLY in the parent are ignored.
17591 *
17592 * Called by CreateInheritance
17593 *
17594 * Currently all constraints in parent must be present in the child. One day we
17595 * may consider adding new constraints like CREATE TABLE does.
17596 *
17597 * XXX This is O(N^2) which may be an issue with tables with hundreds of
17598 * constraints. As long as tables have more like 10 constraints it shouldn't be
17599 * a problem though. Even 100 constraints ought not be the end of the world.
17600 *
17601 * XXX See MergeWithExistingConstraint too if you change this code.
17602 */
17603static void
17605{
17606 Relation constraintrel;
17607 SysScanDesc parent_scan;
17608 ScanKeyData parent_key;
17609 HeapTuple parent_tuple;
17610 Oid parent_relid = RelationGetRelid(parent_rel);
17611 AttrMap *attmap;
17612
17613 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17614
17615 /* Outer loop scans through the parent's constraint definitions */
17616 ScanKeyInit(&parent_key,
17617 Anum_pg_constraint_conrelid,
17618 BTEqualStrategyNumber, F_OIDEQ,
17619 ObjectIdGetDatum(parent_relid));
17620 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17621 true, NULL, 1, &parent_key);
17622
17623 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17624 RelationGetDescr(child_rel),
17625 true);
17626
17627 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17628 {
17629 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17630 SysScanDesc child_scan;
17631 ScanKeyData child_key;
17632 HeapTuple child_tuple;
17633 AttrNumber parent_attno;
17634 bool found = false;
17635
17636 if (parent_con->contype != CONSTRAINT_CHECK &&
17637 parent_con->contype != CONSTRAINT_NOTNULL)
17638 continue;
17639
17640 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17641 if (parent_con->connoinherit)
17642 continue;
17643
17644 if (parent_con->contype == CONSTRAINT_NOTNULL)
17645 parent_attno = extractNotNullColumn(parent_tuple);
17646 else
17647 parent_attno = InvalidAttrNumber;
17648
17649 /* Search for a child constraint matching this one */
17650 ScanKeyInit(&child_key,
17651 Anum_pg_constraint_conrelid,
17652 BTEqualStrategyNumber, F_OIDEQ,
17654 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17655 true, NULL, 1, &child_key);
17656
17657 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17658 {
17659 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17660 HeapTuple child_copy;
17661
17662 if (child_con->contype != parent_con->contype)
17663 continue;
17664
17665 /*
17666 * CHECK constraint are matched by constraint name, NOT NULL ones
17667 * by attribute number.
17668 */
17669 if (child_con->contype == CONSTRAINT_CHECK)
17670 {
17671 if (strcmp(NameStr(parent_con->conname),
17672 NameStr(child_con->conname)) != 0)
17673 continue;
17674 }
17675 else if (child_con->contype == CONSTRAINT_NOTNULL)
17676 {
17677 Form_pg_attribute parent_attr;
17678 Form_pg_attribute child_attr;
17679 AttrNumber child_attno;
17680
17681 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17682 child_attno = extractNotNullColumn(child_tuple);
17683 if (parent_attno != attmap->attnums[child_attno - 1])
17684 continue;
17685
17686 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17687 /* there shouldn't be constraints on dropped columns */
17688 if (parent_attr->attisdropped || child_attr->attisdropped)
17689 elog(ERROR, "found not-null constraint on dropped columns");
17690 }
17691
17692 if (child_con->contype == CONSTRAINT_CHECK &&
17693 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17694 ereport(ERROR,
17695 (errcode(ERRCODE_DATATYPE_MISMATCH),
17696 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17697 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17698
17699 /*
17700 * If the child constraint is "no inherit" then cannot merge
17701 */
17702 if (child_con->connoinherit)
17703 ereport(ERROR,
17704 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17705 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17706 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17707
17708 /*
17709 * If the child constraint is "not valid" then cannot merge with a
17710 * valid parent constraint
17711 */
17712 if (parent_con->convalidated && child_con->conenforced &&
17713 !child_con->convalidated)
17714 ereport(ERROR,
17715 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17716 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17717 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17718
17719 /*
17720 * A NOT ENFORCED child constraint cannot be merged with an
17721 * ENFORCED parent constraint. However, the reverse is allowed,
17722 * where the child constraint is ENFORCED.
17723 */
17724 if (parent_con->conenforced && !child_con->conenforced)
17725 ereport(ERROR,
17726 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17727 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17728 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17729
17730 /*
17731 * OK, bump the child constraint's inheritance count. (If we fail
17732 * later on, this change will just roll back.)
17733 */
17734 child_copy = heap_copytuple(child_tuple);
17735 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17736
17737 if (pg_add_s16_overflow(child_con->coninhcount, 1,
17738 &child_con->coninhcount))
17739 ereport(ERROR,
17740 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17741 errmsg("too many inheritance parents"));
17742
17743 /*
17744 * In case of partitions, an inherited constraint must be
17745 * inherited only once since it cannot have multiple parents and
17746 * it is never considered local.
17747 */
17748 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17749 {
17750 Assert(child_con->coninhcount == 1);
17751 child_con->conislocal = false;
17752 }
17753
17754 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17755 heap_freetuple(child_copy);
17756
17757 found = true;
17758 break;
17759 }
17760
17761 systable_endscan(child_scan);
17762
17763 if (!found)
17764 {
17765 if (parent_con->contype == CONSTRAINT_NOTNULL)
17766 ereport(ERROR,
17767 errcode(ERRCODE_DATATYPE_MISMATCH),
17768 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17769 get_attname(parent_relid,
17770 extractNotNullColumn(parent_tuple),
17771 false),
17772 RelationGetRelationName(child_rel)));
17773
17774 ereport(ERROR,
17775 (errcode(ERRCODE_DATATYPE_MISMATCH),
17776 errmsg("child table is missing constraint \"%s\"",
17777 NameStr(parent_con->conname))));
17778 }
17779 }
17780
17781 systable_endscan(parent_scan);
17782 table_close(constraintrel, RowExclusiveLock);
17783}
17784
17785/*
17786 * ALTER TABLE NO INHERIT
17787 *
17788 * Return value is the address of the relation that is no longer parent.
17789 */
17790static ObjectAddress
17792{
17793 ObjectAddress address;
17794 Relation parent_rel;
17795
17796 if (rel->rd_rel->relispartition)
17797 ereport(ERROR,
17798 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17799 errmsg("cannot change inheritance of a partition")));
17800
17801 /*
17802 * AccessShareLock on the parent is probably enough, seeing that DROP
17803 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17804 * be inspecting the parent's schema.
17805 */
17806 parent_rel = table_openrv(parent, AccessShareLock);
17807
17808 /*
17809 * We don't bother to check ownership of the parent table --- ownership of
17810 * the child is presumed enough rights.
17811 */
17812
17813 /* Off to RemoveInheritance() where most of the work happens */
17814 RemoveInheritance(rel, parent_rel, false);
17815
17816 ObjectAddressSet(address, RelationRelationId,
17817 RelationGetRelid(parent_rel));
17818
17819 /* keep our lock on the parent relation until commit */
17820 table_close(parent_rel, NoLock);
17821
17822 return address;
17823}
17824
17825/*
17826 * MarkInheritDetached
17827 *
17828 * Set inhdetachpending for a partition, for ATExecDetachPartition
17829 * in concurrent mode. While at it, verify that no other partition is
17830 * already pending detach.
17831 */
17832static void
17834{
17835 Relation catalogRelation;
17836 SysScanDesc scan;
17838 HeapTuple inheritsTuple;
17839 bool found = false;
17840
17841 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17842
17843 /*
17844 * Find pg_inherits entries by inhparent. (We need to scan them all in
17845 * order to verify that no other partition is pending detach.)
17846 */
17847 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17849 Anum_pg_inherits_inhparent,
17850 BTEqualStrategyNumber, F_OIDEQ,
17851 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17852 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17853 true, NULL, 1, &key);
17854
17855 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17856 {
17857 Form_pg_inherits inhForm;
17858
17859 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17860 if (inhForm->inhdetachpending)
17861 ereport(ERROR,
17862 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17863 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17864 get_rel_name(inhForm->inhrelid),
17865 get_namespace_name(parent_rel->rd_rel->relnamespace),
17866 RelationGetRelationName(parent_rel)),
17867 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17868
17869 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17870 {
17871 HeapTuple newtup;
17872
17873 newtup = heap_copytuple(inheritsTuple);
17874 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17875
17876 CatalogTupleUpdate(catalogRelation,
17877 &inheritsTuple->t_self,
17878 newtup);
17879 found = true;
17880 heap_freetuple(newtup);
17881 /* keep looking, to ensure we catch others pending detach */
17882 }
17883 }
17884
17885 /* Done */
17886 systable_endscan(scan);
17887 table_close(catalogRelation, RowExclusiveLock);
17888
17889 if (!found)
17890 ereport(ERROR,
17892 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17893 RelationGetRelationName(child_rel),
17894 RelationGetRelationName(parent_rel))));
17895}
17896
17897/*
17898 * RemoveInheritance
17899 *
17900 * Drop a parent from the child's parents. This just adjusts the attinhcount
17901 * and attislocal of the columns and removes the pg_inherit and pg_depend
17902 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17903 *
17904 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17905 * up attislocal stays true, which means if a child is ever removed from a
17906 * parent then its columns will never be automatically dropped which may
17907 * surprise. But at least we'll never surprise by dropping columns someone
17908 * isn't expecting to be dropped which would actually mean data loss.
17909 *
17910 * coninhcount and conislocal for inherited constraints are adjusted in
17911 * exactly the same way.
17912 *
17913 * Common to ATExecDropInherit() and ATExecDetachPartition().
17914 */
17915static void
17916RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17917{
17918 Relation catalogRelation;
17919 SysScanDesc scan;
17920 ScanKeyData key[3];
17921 HeapTuple attributeTuple,
17922 constraintTuple;
17923 AttrMap *attmap;
17924 List *connames;
17925 List *nncolumns;
17926 bool found;
17927 bool is_partitioning;
17928
17929 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17930
17931 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17932 RelationGetRelid(parent_rel),
17933 expect_detached,
17934 RelationGetRelationName(child_rel));
17935 if (!found)
17936 {
17937 if (is_partitioning)
17938 ereport(ERROR,
17940 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17941 RelationGetRelationName(child_rel),
17942 RelationGetRelationName(parent_rel))));
17943 else
17944 ereport(ERROR,
17946 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17947 RelationGetRelationName(parent_rel),
17948 RelationGetRelationName(child_rel))));
17949 }
17950
17951 /*
17952 * Search through child columns looking for ones matching parent rel
17953 */
17954 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17955 ScanKeyInit(&key[0],
17956 Anum_pg_attribute_attrelid,
17957 BTEqualStrategyNumber, F_OIDEQ,
17959 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17960 true, NULL, 1, key);
17961 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17962 {
17963 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17964
17965 /* Ignore if dropped or not inherited */
17966 if (att->attisdropped)
17967 continue;
17968 if (att->attinhcount <= 0)
17969 continue;
17970
17972 NameStr(att->attname)))
17973 {
17974 /* Decrement inhcount and possibly set islocal to true */
17975 HeapTuple copyTuple = heap_copytuple(attributeTuple);
17976 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17977
17978 copy_att->attinhcount--;
17979 if (copy_att->attinhcount == 0)
17980 copy_att->attislocal = true;
17981
17982 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
17983 heap_freetuple(copyTuple);
17984 }
17985 }
17986 systable_endscan(scan);
17987 table_close(catalogRelation, RowExclusiveLock);
17988
17989 /*
17990 * Likewise, find inherited check and not-null constraints and disinherit
17991 * them. To do this, we first need a list of the names of the parent's
17992 * check constraints. (We cheat a bit by only checking for name matches,
17993 * assuming that the expressions will match.)
17994 *
17995 * For NOT NULL columns, we store column numbers to match, mapping them in
17996 * to the child rel's attribute numbers.
17997 */
17998 attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17999 RelationGetDescr(parent_rel),
18000 false);
18001
18002 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
18003 ScanKeyInit(&key[0],
18004 Anum_pg_constraint_conrelid,
18005 BTEqualStrategyNumber, F_OIDEQ,
18006 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18007 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18008 true, NULL, 1, key);
18009
18010 connames = NIL;
18011 nncolumns = NIL;
18012
18013 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18014 {
18015 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18016
18017 if (con->connoinherit)
18018 continue;
18019
18020 if (con->contype == CONSTRAINT_CHECK)
18021 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18022 if (con->contype == CONSTRAINT_NOTNULL)
18023 {
18024 AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
18025
18026 nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18027 }
18028 }
18029
18030 systable_endscan(scan);
18031
18032 /* Now scan the child's constraints to find matches */
18033 ScanKeyInit(&key[0],
18034 Anum_pg_constraint_conrelid,
18035 BTEqualStrategyNumber, F_OIDEQ,
18037 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18038 true, NULL, 1, key);
18039
18040 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18041 {
18042 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18043 bool match = false;
18044
18045 /*
18046 * Match CHECK constraints by name, not-null constraints by column
18047 * number, and ignore all others.
18048 */
18049 if (con->contype == CONSTRAINT_CHECK)
18050 {
18051 foreach_ptr(char, chkname, connames)
18052 {
18053 if (con->contype == CONSTRAINT_CHECK &&
18054 strcmp(NameStr(con->conname), chkname) == 0)
18055 {
18056 match = true;
18057 connames = foreach_delete_current(connames, chkname);
18058 break;
18059 }
18060 }
18061 }
18062 else if (con->contype == CONSTRAINT_NOTNULL)
18063 {
18064 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18065
18066 foreach_int(prevattno, nncolumns)
18067 {
18068 if (prevattno == child_attno)
18069 {
18070 match = true;
18071 nncolumns = foreach_delete_current(nncolumns, prevattno);
18072 break;
18073 }
18074 }
18075 }
18076 else
18077 continue;
18078
18079 if (match)
18080 {
18081 /* Decrement inhcount and possibly set islocal to true */
18082 HeapTuple copyTuple = heap_copytuple(constraintTuple);
18083 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18084
18085 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18086 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18087 RelationGetRelid(child_rel), NameStr(copy_con->conname));
18088
18089 copy_con->coninhcount--;
18090 if (copy_con->coninhcount == 0)
18091 copy_con->conislocal = true;
18092
18093 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18094 heap_freetuple(copyTuple);
18095 }
18096 }
18097
18098 /* We should have matched all constraints */
18099 if (connames != NIL || nncolumns != NIL)
18100 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18101 list_length(connames) + list_length(nncolumns),
18102 RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18103
18104 systable_endscan(scan);
18105 table_close(catalogRelation, RowExclusiveLock);
18106
18108 RelationRelationId,
18109 RelationGetRelid(parent_rel),
18110 child_dependency_type(is_partitioning));
18111
18112 /*
18113 * Post alter hook of this inherits. Since object_access_hook doesn't take
18114 * multiple object identifiers, we relay oid of parent relation using
18115 * auxiliary_id argument.
18116 */
18117 InvokeObjectPostAlterHookArg(InheritsRelationId,
18118 RelationGetRelid(child_rel), 0,
18119 RelationGetRelid(parent_rel), false);
18120}
18121
18122/*
18123 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18124 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18125 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18126 * be TypeRelationId). There's no convenient way to do this, so go trawling
18127 * through pg_depend.
18128 */
18129static void
18130drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18131 DependencyType deptype)
18132{
18133 Relation catalogRelation;
18134 SysScanDesc scan;
18135 ScanKeyData key[3];
18136 HeapTuple depTuple;
18137
18138 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18139
18140 ScanKeyInit(&key[0],
18141 Anum_pg_depend_classid,
18142 BTEqualStrategyNumber, F_OIDEQ,
18143 ObjectIdGetDatum(RelationRelationId));
18144 ScanKeyInit(&key[1],
18145 Anum_pg_depend_objid,
18146 BTEqualStrategyNumber, F_OIDEQ,
18147 ObjectIdGetDatum(relid));
18148 ScanKeyInit(&key[2],
18149 Anum_pg_depend_objsubid,
18150 BTEqualStrategyNumber, F_INT4EQ,
18151 Int32GetDatum(0));
18152
18153 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18154 NULL, 3, key);
18155
18156 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18157 {
18158 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18159
18160 if (dep->refclassid == refclassid &&
18161 dep->refobjid == refobjid &&
18162 dep->refobjsubid == 0 &&
18163 dep->deptype == deptype)
18164 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18165 }
18166
18167 systable_endscan(scan);
18168 table_close(catalogRelation, RowExclusiveLock);
18169}
18170
18171/*
18172 * ALTER TABLE OF
18173 *
18174 * Attach a table to a composite type, as though it had been created with CREATE
18175 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18176 * subject table must not have inheritance parents. These restrictions ensure
18177 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18178 *
18179 * The address of the type is returned.
18180 */
18181static ObjectAddress
18182ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18183{
18184 Oid relid = RelationGetRelid(rel);
18185 Type typetuple;
18186 Form_pg_type typeform;
18187 Oid typeid;
18188 Relation inheritsRelation,
18189 relationRelation;
18190 SysScanDesc scan;
18192 AttrNumber table_attno,
18193 type_attno;
18194 TupleDesc typeTupleDesc,
18195 tableTupleDesc;
18196 ObjectAddress tableobj,
18197 typeobj;
18198 HeapTuple classtuple;
18199
18200 /* Validate the type. */
18201 typetuple = typenameType(NULL, ofTypename, NULL);
18202 check_of_type(typetuple);
18203 typeform = (Form_pg_type) GETSTRUCT(typetuple);
18204 typeid = typeform->oid;
18205
18206 /* Fail if the table has any inheritance parents. */
18207 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18209 Anum_pg_inherits_inhrelid,
18210 BTEqualStrategyNumber, F_OIDEQ,
18211 ObjectIdGetDatum(relid));
18212 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18213 true, NULL, 1, &key);
18215 ereport(ERROR,
18216 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18217 errmsg("typed tables cannot inherit")));
18218 systable_endscan(scan);
18219 table_close(inheritsRelation, AccessShareLock);
18220
18221 /*
18222 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18223 * require that the order also match. However, attnotnull need not match.
18224 */
18225 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18226 tableTupleDesc = RelationGetDescr(rel);
18227 table_attno = 1;
18228 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18229 {
18230 Form_pg_attribute type_attr,
18231 table_attr;
18232 const char *type_attname,
18233 *table_attname;
18234
18235 /* Get the next non-dropped type attribute. */
18236 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18237 if (type_attr->attisdropped)
18238 continue;
18239 type_attname = NameStr(type_attr->attname);
18240
18241 /* Get the next non-dropped table attribute. */
18242 do
18243 {
18244 if (table_attno > tableTupleDesc->natts)
18245 ereport(ERROR,
18246 (errcode(ERRCODE_DATATYPE_MISMATCH),
18247 errmsg("table is missing column \"%s\"",
18248 type_attname)));
18249 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18250 table_attno++;
18251 } while (table_attr->attisdropped);
18252 table_attname = NameStr(table_attr->attname);
18253
18254 /* Compare name. */
18255 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18256 ereport(ERROR,
18257 (errcode(ERRCODE_DATATYPE_MISMATCH),
18258 errmsg("table has column \"%s\" where type requires \"%s\"",
18259 table_attname, type_attname)));
18260
18261 /* Compare type. */
18262 if (table_attr->atttypid != type_attr->atttypid ||
18263 table_attr->atttypmod != type_attr->atttypmod ||
18264 table_attr->attcollation != type_attr->attcollation)
18265 ereport(ERROR,
18266 (errcode(ERRCODE_DATATYPE_MISMATCH),
18267 errmsg("table \"%s\" has different type for column \"%s\"",
18268 RelationGetRelationName(rel), type_attname)));
18269 }
18270 ReleaseTupleDesc(typeTupleDesc);
18271
18272 /* Any remaining columns at the end of the table had better be dropped. */
18273 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18274 {
18275 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18276 table_attno - 1);
18277
18278 if (!table_attr->attisdropped)
18279 ereport(ERROR,
18280 (errcode(ERRCODE_DATATYPE_MISMATCH),
18281 errmsg("table has extra column \"%s\"",
18282 NameStr(table_attr->attname))));
18283 }
18284
18285 /* If the table was already typed, drop the existing dependency. */
18286 if (rel->rd_rel->reloftype)
18287 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18289
18290 /* Record a dependency on the new type. */
18291 tableobj.classId = RelationRelationId;
18292 tableobj.objectId = relid;
18293 tableobj.objectSubId = 0;
18294 typeobj.classId = TypeRelationId;
18295 typeobj.objectId = typeid;
18296 typeobj.objectSubId = 0;
18297 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18298
18299 /* Update pg_class.reloftype */
18300 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18301 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18302 if (!HeapTupleIsValid(classtuple))
18303 elog(ERROR, "cache lookup failed for relation %u", relid);
18304 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18305 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18306
18307 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18308
18309 heap_freetuple(classtuple);
18310 table_close(relationRelation, RowExclusiveLock);
18311
18312 ReleaseSysCache(typetuple);
18313
18314 return typeobj;
18315}
18316
18317/*
18318 * ALTER TABLE NOT OF
18319 *
18320 * Detach a typed table from its originating type. Just clear reloftype and
18321 * remove the dependency.
18322 */
18323static void
18325{
18326 Oid relid = RelationGetRelid(rel);
18327 Relation relationRelation;
18328 HeapTuple tuple;
18329
18330 if (!OidIsValid(rel->rd_rel->reloftype))
18331 ereport(ERROR,
18332 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18333 errmsg("\"%s\" is not a typed table",
18335
18336 /*
18337 * We don't bother to check ownership of the type --- ownership of the
18338 * table is presumed enough rights. No lock required on the type, either.
18339 */
18340
18341 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18343
18344 /* Clear pg_class.reloftype */
18345 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18346 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18347 if (!HeapTupleIsValid(tuple))
18348 elog(ERROR, "cache lookup failed for relation %u", relid);
18349 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18350 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18351
18352 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18353
18354 heap_freetuple(tuple);
18355 table_close(relationRelation, RowExclusiveLock);
18356}
18357
18358/*
18359 * relation_mark_replica_identity: Update a table's replica identity
18360 *
18361 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18362 * index. Otherwise, it must be InvalidOid.
18363 *
18364 * Caller had better hold an exclusive lock on the relation, as the results
18365 * of running two of these concurrently wouldn't be pretty.
18366 */
18367static void
18368relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18369 bool is_internal)
18370{
18371 Relation pg_index;
18372 Relation pg_class;
18373 HeapTuple pg_class_tuple;
18374 HeapTuple pg_index_tuple;
18375 Form_pg_class pg_class_form;
18376 Form_pg_index pg_index_form;
18377 ListCell *index;
18378
18379 /*
18380 * Check whether relreplident has changed, and update it if so.
18381 */
18382 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18383 pg_class_tuple = SearchSysCacheCopy1(RELOID,
18385 if (!HeapTupleIsValid(pg_class_tuple))
18386 elog(ERROR, "cache lookup failed for relation \"%s\"",
18388 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18389 if (pg_class_form->relreplident != ri_type)
18390 {
18391 pg_class_form->relreplident = ri_type;
18392 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18393 }
18394 table_close(pg_class, RowExclusiveLock);
18395 heap_freetuple(pg_class_tuple);
18396
18397 /*
18398 * Update the per-index indisreplident flags correctly.
18399 */
18400 pg_index = table_open(IndexRelationId, RowExclusiveLock);
18401 foreach(index, RelationGetIndexList(rel))
18402 {
18403 Oid thisIndexOid = lfirst_oid(index);
18404 bool dirty = false;
18405
18406 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18407 ObjectIdGetDatum(thisIndexOid));
18408 if (!HeapTupleIsValid(pg_index_tuple))
18409 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18410 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18411
18412 if (thisIndexOid == indexOid)
18413 {
18414 /* Set the bit if not already set. */
18415 if (!pg_index_form->indisreplident)
18416 {
18417 dirty = true;
18418 pg_index_form->indisreplident = true;
18419 }
18420 }
18421 else
18422 {
18423 /* Unset the bit if set. */
18424 if (pg_index_form->indisreplident)
18425 {
18426 dirty = true;
18427 pg_index_form->indisreplident = false;
18428 }
18429 }
18430
18431 if (dirty)
18432 {
18433 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18434 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18435 InvalidOid, is_internal);
18436
18437 /*
18438 * Invalidate the relcache for the table, so that after we commit
18439 * all sessions will refresh the table's replica identity index
18440 * before attempting any UPDATE or DELETE on the table. (If we
18441 * changed the table's pg_class row above, then a relcache inval
18442 * is already queued due to that; but we might not have.)
18443 */
18445 }
18446 heap_freetuple(pg_index_tuple);
18447 }
18448
18449 table_close(pg_index, RowExclusiveLock);
18450}
18451
18452/*
18453 * ALTER TABLE <name> REPLICA IDENTITY ...
18454 */
18455static void
18457{
18458 Oid indexOid;
18459 Relation indexRel;
18460 int key;
18461
18462 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18463 {
18464 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18465 return;
18466 }
18467 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18468 {
18469 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18470 return;
18471 }
18472 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18473 {
18474 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18475 return;
18476 }
18477 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18478 {
18479 /* fallthrough */ ;
18480 }
18481 else
18482 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18483
18484 /* Check that the index exists */
18485 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18486 if (!OidIsValid(indexOid))
18487 ereport(ERROR,
18488 (errcode(ERRCODE_UNDEFINED_OBJECT),
18489 errmsg("index \"%s\" for table \"%s\" does not exist",
18490 stmt->name, RelationGetRelationName(rel))));
18491
18492 indexRel = index_open(indexOid, ShareLock);
18493
18494 /* Check that the index is on the relation we're altering. */
18495 if (indexRel->rd_index == NULL ||
18496 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18497 ereport(ERROR,
18498 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18499 errmsg("\"%s\" is not an index for table \"%s\"",
18500 RelationGetRelationName(indexRel),
18502
18503 /*
18504 * The AM must support uniqueness, and the index must in fact be unique.
18505 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18506 * exclusion), we can use that too.
18507 */
18508 if ((!indexRel->rd_indam->amcanunique ||
18509 !indexRel->rd_index->indisunique) &&
18510 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18511 ereport(ERROR,
18512 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18513 errmsg("cannot use non-unique index \"%s\" as replica identity",
18514 RelationGetRelationName(indexRel))));
18515 /* Deferred indexes are not guaranteed to be always unique. */
18516 if (!indexRel->rd_index->indimmediate)
18517 ereport(ERROR,
18518 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18519 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18520 RelationGetRelationName(indexRel))));
18521 /* Expression indexes aren't supported. */
18522 if (RelationGetIndexExpressions(indexRel) != NIL)
18523 ereport(ERROR,
18524 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18525 errmsg("cannot use expression index \"%s\" as replica identity",
18526 RelationGetRelationName(indexRel))));
18527 /* Predicate indexes aren't supported. */
18528 if (RelationGetIndexPredicate(indexRel) != NIL)
18529 ereport(ERROR,
18530 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18531 errmsg("cannot use partial index \"%s\" as replica identity",
18532 RelationGetRelationName(indexRel))));
18533
18534 /* Check index for nullable columns. */
18535 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18536 {
18537 int16 attno = indexRel->rd_index->indkey.values[key];
18538 Form_pg_attribute attr;
18539
18540 /*
18541 * Reject any other system columns. (Going forward, we'll disallow
18542 * indexes containing such columns in the first place, but they might
18543 * exist in older branches.)
18544 */
18545 if (attno <= 0)
18546 ereport(ERROR,
18547 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18548 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18549 RelationGetRelationName(indexRel), attno)));
18550
18551 attr = TupleDescAttr(rel->rd_att, attno - 1);
18552 if (!attr->attnotnull)
18553 ereport(ERROR,
18554 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18555 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18556 RelationGetRelationName(indexRel),
18557 NameStr(attr->attname))));
18558 }
18559
18560 /* This index is suitable for use as a replica identity. Mark it. */
18561 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18562
18563 index_close(indexRel, NoLock);
18564}
18565
18566/*
18567 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18568 */
18569static void
18571{
18572 Relation pg_class;
18573 Oid relid;
18574 HeapTuple tuple;
18575
18576 relid = RelationGetRelid(rel);
18577
18578 /* Pull the record for this relation and update it */
18579 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18580
18581 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18582
18583 if (!HeapTupleIsValid(tuple))
18584 elog(ERROR, "cache lookup failed for relation %u", relid);
18585
18586 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18587 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18588
18589 InvokeObjectPostAlterHook(RelationRelationId,
18590 RelationGetRelid(rel), 0);
18591
18592 table_close(pg_class, RowExclusiveLock);
18593 heap_freetuple(tuple);
18594}
18595
18596/*
18597 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18598 */
18599static void
18601{
18602 Relation pg_class;
18603 Oid relid;
18604 HeapTuple tuple;
18605
18606 relid = RelationGetRelid(rel);
18607
18608 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18609
18610 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18611
18612 if (!HeapTupleIsValid(tuple))
18613 elog(ERROR, "cache lookup failed for relation %u", relid);
18614
18615 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18616 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18617
18618 InvokeObjectPostAlterHook(RelationRelationId,
18619 RelationGetRelid(rel), 0);
18620
18621 table_close(pg_class, RowExclusiveLock);
18622 heap_freetuple(tuple);
18623}
18624
18625/*
18626 * ALTER FOREIGN TABLE <name> OPTIONS (...)
18627 */
18628static void
18630{
18631 Relation ftrel;
18632 ForeignServer *server;
18633 ForeignDataWrapper *fdw;
18634 HeapTuple tuple;
18635 bool isnull;
18636 Datum repl_val[Natts_pg_foreign_table];
18637 bool repl_null[Natts_pg_foreign_table];
18638 bool repl_repl[Natts_pg_foreign_table];
18639 Datum datum;
18640 Form_pg_foreign_table tableform;
18641
18642 if (options == NIL)
18643 return;
18644
18645 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18646
18647 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18648 ObjectIdGetDatum(rel->rd_id));
18649 if (!HeapTupleIsValid(tuple))
18650 ereport(ERROR,
18651 (errcode(ERRCODE_UNDEFINED_OBJECT),
18652 errmsg("foreign table \"%s\" does not exist",
18654 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18655 server = GetForeignServer(tableform->ftserver);
18656 fdw = GetForeignDataWrapper(server->fdwid);
18657
18658 memset(repl_val, 0, sizeof(repl_val));
18659 memset(repl_null, false, sizeof(repl_null));
18660 memset(repl_repl, false, sizeof(repl_repl));
18661
18662 /* Extract the current options */
18663 datum = SysCacheGetAttr(FOREIGNTABLEREL,
18664 tuple,
18665 Anum_pg_foreign_table_ftoptions,
18666 &isnull);
18667 if (isnull)
18668 datum = PointerGetDatum(NULL);
18669
18670 /* Transform the options */
18671 datum = transformGenericOptions(ForeignTableRelationId,
18672 datum,
18673 options,
18674 fdw->fdwvalidator);
18675
18676 if (DatumGetPointer(datum) != NULL)
18677 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18678 else
18679 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18680
18681 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18682
18683 /* Everything looks good - update the tuple */
18684
18685 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18686 repl_val, repl_null, repl_repl);
18687
18688 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18689
18690 /*
18691 * Invalidate relcache so that all sessions will refresh any cached plans
18692 * that might depend on the old options.
18693 */
18695
18696 InvokeObjectPostAlterHook(ForeignTableRelationId,
18697 RelationGetRelid(rel), 0);
18698
18700
18701 heap_freetuple(tuple);
18702}
18703
18704/*
18705 * ALTER TABLE ALTER COLUMN SET COMPRESSION
18706 *
18707 * Return value is the address of the modified column
18708 */
18709static ObjectAddress
18711 const char *column,
18712 Node *newValue,
18713 LOCKMODE lockmode)
18714{
18715 Relation attrel;
18716 HeapTuple tuple;
18717 Form_pg_attribute atttableform;
18719 char *compression;
18720 char cmethod;
18721 ObjectAddress address;
18722
18723 compression = strVal(newValue);
18724
18725 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18726
18727 /* copy the cache entry so we can scribble on it below */
18728 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18729 if (!HeapTupleIsValid(tuple))
18730 ereport(ERROR,
18731 (errcode(ERRCODE_UNDEFINED_COLUMN),
18732 errmsg("column \"%s\" of relation \"%s\" does not exist",
18733 column, RelationGetRelationName(rel))));
18734
18735 /* prevent them from altering a system attribute */
18736 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18737 attnum = atttableform->attnum;
18738 if (attnum <= 0)
18739 ereport(ERROR,
18740 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18741 errmsg("cannot alter system column \"%s\"", column)));
18742
18743 /*
18744 * Check that column type is compressible, then get the attribute
18745 * compression method code
18746 */
18747 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18748
18749 /* update pg_attribute entry */
18750 atttableform->attcompression = cmethod;
18751 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18752
18753 InvokeObjectPostAlterHook(RelationRelationId,
18754 RelationGetRelid(rel),
18755 attnum);
18756
18757 /*
18758 * Apply the change to indexes as well (only for simple index columns,
18759 * matching behavior of index.c ConstructTupleDescriptor()).
18760 */
18761 SetIndexStorageProperties(rel, attrel, attnum,
18762 false, 0,
18763 true, cmethod,
18764 lockmode);
18765
18766 heap_freetuple(tuple);
18767
18769
18770 /* make changes visible */
18772
18773 ObjectAddressSubSet(address, RelationRelationId,
18774 RelationGetRelid(rel), attnum);
18775 return address;
18776}
18777
18778
18779/*
18780 * Preparation phase for SET LOGGED/UNLOGGED
18781 *
18782 * This verifies that we're not trying to change a temp table. Also,
18783 * existing foreign key constraints are checked to avoid ending up with
18784 * permanent tables referencing unlogged tables.
18785 */
18786static void
18788{
18789 Relation pg_constraint;
18790 HeapTuple tuple;
18791 SysScanDesc scan;
18792 ScanKeyData skey[1];
18793
18794 /*
18795 * Disallow changing status for a temp table. Also verify whether we can
18796 * get away with doing nothing; in such cases we don't need to run the
18797 * checks below, either.
18798 */
18799 switch (rel->rd_rel->relpersistence)
18800 {
18801 case RELPERSISTENCE_TEMP:
18802 ereport(ERROR,
18803 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18804 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18806 errtable(rel)));
18807 break;
18808 case RELPERSISTENCE_PERMANENT:
18809 if (toLogged)
18810 /* nothing to do */
18811 return;
18812 break;
18813 case RELPERSISTENCE_UNLOGGED:
18814 if (!toLogged)
18815 /* nothing to do */
18816 return;
18817 break;
18818 }
18819
18820 /*
18821 * Check that the table is not part of any publication when changing to
18822 * UNLOGGED, as UNLOGGED tables can't be published.
18823 */
18824 if (!toLogged &&
18826 ereport(ERROR,
18827 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18828 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18830 errdetail("Unlogged relations cannot be replicated.")));
18831
18832 /*
18833 * Check existing foreign key constraints to preserve the invariant that
18834 * permanent tables cannot reference unlogged ones. Self-referencing
18835 * foreign keys can safely be ignored.
18836 */
18837 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18838
18839 /*
18840 * Scan conrelid if changing to permanent, else confrelid. This also
18841 * determines whether a useful index exists.
18842 */
18843 ScanKeyInit(&skey[0],
18844 toLogged ? Anum_pg_constraint_conrelid :
18845 Anum_pg_constraint_confrelid,
18846 BTEqualStrategyNumber, F_OIDEQ,
18848 scan = systable_beginscan(pg_constraint,
18849 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18850 true, NULL, 1, skey);
18851
18852 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18853 {
18855
18856 if (con->contype == CONSTRAINT_FOREIGN)
18857 {
18858 Oid foreignrelid;
18859 Relation foreignrel;
18860
18861 /* the opposite end of what we used as scankey */
18862 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18863
18864 /* ignore if self-referencing */
18865 if (RelationGetRelid(rel) == foreignrelid)
18866 continue;
18867
18868 foreignrel = relation_open(foreignrelid, AccessShareLock);
18869
18870 if (toLogged)
18871 {
18872 if (!RelationIsPermanent(foreignrel))
18873 ereport(ERROR,
18874 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18875 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18877 RelationGetRelationName(foreignrel)),
18878 errtableconstraint(rel, NameStr(con->conname))));
18879 }
18880 else
18881 {
18882 if (RelationIsPermanent(foreignrel))
18883 ereport(ERROR,
18884 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18885 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18887 RelationGetRelationName(foreignrel)),
18888 errtableconstraint(rel, NameStr(con->conname))));
18889 }
18890
18891 relation_close(foreignrel, AccessShareLock);
18892 }
18893 }
18894
18895 systable_endscan(scan);
18896
18897 table_close(pg_constraint, AccessShareLock);
18898
18899 /* force rewrite if necessary; see comment in ATRewriteTables */
18901 if (toLogged)
18902 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18903 else
18904 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18905 tab->chgPersistence = true;
18906}
18907
18908/*
18909 * Execute ALTER TABLE SET SCHEMA
18910 */
18913{
18914 Relation rel;
18915 Oid relid;
18916 Oid oldNspOid;
18917 Oid nspOid;
18918 RangeVar *newrv;
18919 ObjectAddresses *objsMoved;
18920 ObjectAddress myself;
18921
18923 stmt->missing_ok ? RVR_MISSING_OK : 0,
18925 stmt);
18926
18927 if (!OidIsValid(relid))
18928 {
18930 (errmsg("relation \"%s\" does not exist, skipping",
18931 stmt->relation->relname)));
18932 return InvalidObjectAddress;
18933 }
18934
18935 rel = relation_open(relid, NoLock);
18936
18937 oldNspOid = RelationGetNamespace(rel);
18938
18939 /* If it's an owned sequence, disallow moving it by itself. */
18940 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18941 {
18942 Oid tableId;
18943 int32 colId;
18944
18945 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18946 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18947 ereport(ERROR,
18948 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18949 errmsg("cannot move an owned sequence into another schema"),
18950 errdetail("Sequence \"%s\" is linked to table \"%s\".",
18952 get_rel_name(tableId))));
18953 }
18954
18955 /* Get and lock schema OID and check its permissions. */
18956 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18957 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18958
18959 /* common checks on switching namespaces */
18960 CheckSetNamespace(oldNspOid, nspOid);
18961
18962 objsMoved = new_object_addresses();
18963 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18964 free_object_addresses(objsMoved);
18965
18966 ObjectAddressSet(myself, RelationRelationId, relid);
18967
18968 if (oldschema)
18969 *oldschema = oldNspOid;
18970
18971 /* close rel, but keep lock until commit */
18972 relation_close(rel, NoLock);
18973
18974 return myself;
18975}
18976
18977/*
18978 * The guts of relocating a table or materialized view to another namespace:
18979 * besides moving the relation itself, its dependent objects are relocated to
18980 * the new schema.
18981 */
18982void
18984 ObjectAddresses *objsMoved)
18985{
18986 Relation classRel;
18987
18988 Assert(objsMoved != NULL);
18989
18990 /* OK, modify the pg_class row and pg_depend entry */
18991 classRel = table_open(RelationRelationId, RowExclusiveLock);
18992
18993 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18994 nspOid, true, objsMoved);
18995
18996 /* Fix the table's row type too, if it has one */
18997 if (OidIsValid(rel->rd_rel->reltype))
18998 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18999 false, /* isImplicitArray */
19000 false, /* ignoreDependent */
19001 false, /* errorOnTableType */
19002 objsMoved);
19003
19004 /* Fix other dependent stuff */
19005 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19006 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19007 objsMoved, AccessExclusiveLock);
19008 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19009 false, objsMoved);
19010
19011 table_close(classRel, RowExclusiveLock);
19012}
19013
19014/*
19015 * The guts of relocating a relation to another namespace: fix the pg_class
19016 * entry, and the pg_depend entry if any. Caller must already have
19017 * opened and write-locked pg_class.
19018 */
19019void
19021 Oid oldNspOid, Oid newNspOid,
19022 bool hasDependEntry,
19023 ObjectAddresses *objsMoved)
19024{
19025 HeapTuple classTup;
19026 Form_pg_class classForm;
19027 ObjectAddress thisobj;
19028 bool already_done = false;
19029
19030 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19031 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19032 if (!HeapTupleIsValid(classTup))
19033 elog(ERROR, "cache lookup failed for relation %u", relOid);
19034 classForm = (Form_pg_class) GETSTRUCT(classTup);
19035
19036 Assert(classForm->relnamespace == oldNspOid);
19037
19038 thisobj.classId = RelationRelationId;
19039 thisobj.objectId = relOid;
19040 thisobj.objectSubId = 0;
19041
19042 /*
19043 * If the object has already been moved, don't move it again. If it's
19044 * already in the right place, don't move it, but still fire the object
19045 * access hook.
19046 */
19047 already_done = object_address_present(&thisobj, objsMoved);
19048 if (!already_done && oldNspOid != newNspOid)
19049 {
19050 ItemPointerData otid = classTup->t_self;
19051
19052 /* check for duplicate name (more friendly than unique-index failure) */
19053 if (get_relname_relid(NameStr(classForm->relname),
19054 newNspOid) != InvalidOid)
19055 ereport(ERROR,
19056 (errcode(ERRCODE_DUPLICATE_TABLE),
19057 errmsg("relation \"%s\" already exists in schema \"%s\"",
19058 NameStr(classForm->relname),
19059 get_namespace_name(newNspOid))));
19060
19061 /* classTup is a copy, so OK to scribble on */
19062 classForm->relnamespace = newNspOid;
19063
19064 CatalogTupleUpdate(classRel, &otid, classTup);
19065 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19066
19067
19068 /* Update dependency on schema if caller said so */
19069 if (hasDependEntry &&
19070 changeDependencyFor(RelationRelationId,
19071 relOid,
19072 NamespaceRelationId,
19073 oldNspOid,
19074 newNspOid) != 1)
19075 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19076 NameStr(classForm->relname));
19077 }
19078 else
19079 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19080 if (!already_done)
19081 {
19082 add_exact_object_address(&thisobj, objsMoved);
19083
19084 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19085 }
19086
19087 heap_freetuple(classTup);
19088}
19089
19090/*
19091 * Move all indexes for the specified relation to another namespace.
19092 *
19093 * Note: we assume adequate permission checking was done by the caller,
19094 * and that the caller has a suitable lock on the owning relation.
19095 */
19096static void
19098 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19099{
19100 List *indexList;
19101 ListCell *l;
19102
19103 indexList = RelationGetIndexList(rel);
19104
19105 foreach(l, indexList)
19106 {
19107 Oid indexOid = lfirst_oid(l);
19108 ObjectAddress thisobj;
19109
19110 thisobj.classId = RelationRelationId;
19111 thisobj.objectId = indexOid;
19112 thisobj.objectSubId = 0;
19113
19114 /*
19115 * Note: currently, the index will not have its own dependency on the
19116 * namespace, so we don't need to do changeDependencyFor(). There's no
19117 * row type in pg_type, either.
19118 *
19119 * XXX this objsMoved test may be pointless -- surely we have a single
19120 * dependency link from a relation to each index?
19121 */
19122 if (!object_address_present(&thisobj, objsMoved))
19123 {
19124 AlterRelationNamespaceInternal(classRel, indexOid,
19125 oldNspOid, newNspOid,
19126 false, objsMoved);
19127 add_exact_object_address(&thisobj, objsMoved);
19128 }
19129 }
19130
19131 list_free(indexList);
19132}
19133
19134/*
19135 * Move all identity and SERIAL-column sequences of the specified relation to another
19136 * namespace.
19137 *
19138 * Note: we assume adequate permission checking was done by the caller,
19139 * and that the caller has a suitable lock on the owning relation.
19140 */
19141static void
19143 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19144 LOCKMODE lockmode)
19145{
19146 Relation depRel;
19147 SysScanDesc scan;
19148 ScanKeyData key[2];
19149 HeapTuple tup;
19150
19151 /*
19152 * SERIAL sequences are those having an auto dependency on one of the
19153 * table's columns (we don't care *which* column, exactly).
19154 */
19155 depRel = table_open(DependRelationId, AccessShareLock);
19156
19157 ScanKeyInit(&key[0],
19158 Anum_pg_depend_refclassid,
19159 BTEqualStrategyNumber, F_OIDEQ,
19160 ObjectIdGetDatum(RelationRelationId));
19161 ScanKeyInit(&key[1],
19162 Anum_pg_depend_refobjid,
19163 BTEqualStrategyNumber, F_OIDEQ,
19165 /* we leave refobjsubid unspecified */
19166
19167 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19168 NULL, 2, key);
19169
19170 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19171 {
19172 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19173 Relation seqRel;
19174
19175 /* skip dependencies other than auto dependencies on columns */
19176 if (depForm->refobjsubid == 0 ||
19177 depForm->classid != RelationRelationId ||
19178 depForm->objsubid != 0 ||
19179 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19180 continue;
19181
19182 /* Use relation_open just in case it's an index */
19183 seqRel = relation_open(depForm->objid, lockmode);
19184
19185 /* skip non-sequence relations */
19186 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19187 {
19188 /* No need to keep the lock */
19189 relation_close(seqRel, lockmode);
19190 continue;
19191 }
19192
19193 /* Fix the pg_class and pg_depend entries */
19194 AlterRelationNamespaceInternal(classRel, depForm->objid,
19195 oldNspOid, newNspOid,
19196 true, objsMoved);
19197
19198 /*
19199 * Sequences used to have entries in pg_type, but no longer do. If we
19200 * ever re-instate that, we'll need to move the pg_type entry to the
19201 * new namespace, too (using AlterTypeNamespaceInternal).
19202 */
19203 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19204
19205 /* Now we can close it. Keep the lock till end of transaction. */
19206 relation_close(seqRel, NoLock);
19207 }
19208
19209 systable_endscan(scan);
19210
19212}
19213
19214
19215/*
19216 * This code supports
19217 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19218 *
19219 * Because we only support this for TEMP tables, it's sufficient to remember
19220 * the state in a backend-local data structure.
19221 */
19222
19223/*
19224 * Register a newly-created relation's ON COMMIT action.
19225 */
19226void
19228{
19229 OnCommitItem *oc;
19230 MemoryContext oldcxt;
19231
19232 /*
19233 * We needn't bother registering the relation unless there is an ON COMMIT
19234 * action we need to take.
19235 */
19237 return;
19238
19240
19241 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19242 oc->relid = relid;
19243 oc->oncommit = action;
19246
19247 /*
19248 * We use lcons() here so that ON COMMIT actions are processed in reverse
19249 * order of registration. That might not be essential but it seems
19250 * reasonable.
19251 */
19253
19254 MemoryContextSwitchTo(oldcxt);
19255}
19256
19257/*
19258 * Unregister any ON COMMIT action when a relation is deleted.
19259 *
19260 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19261 */
19262void
19264{
19265 ListCell *l;
19266
19267 foreach(l, on_commits)
19268 {
19269 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19270
19271 if (oc->relid == relid)
19272 {
19274 break;
19275 }
19276 }
19277}
19278
19279/*
19280 * Perform ON COMMIT actions.
19281 *
19282 * This is invoked just before actually committing, since it's possible
19283 * to encounter errors.
19284 */
19285void
19287{
19288 ListCell *l;
19289 List *oids_to_truncate = NIL;
19290 List *oids_to_drop = NIL;
19291
19292 foreach(l, on_commits)
19293 {
19294 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19295
19296 /* Ignore entry if already dropped in this xact */
19298 continue;
19299
19300 switch (oc->oncommit)
19301 {
19302 case ONCOMMIT_NOOP:
19304 /* Do nothing (there shouldn't be such entries, actually) */
19305 break;
19307
19308 /*
19309 * If this transaction hasn't accessed any temporary
19310 * relations, we can skip truncating ON COMMIT DELETE ROWS
19311 * tables, as they must still be empty.
19312 */
19314 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19315 break;
19316 case ONCOMMIT_DROP:
19317 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19318 break;
19319 }
19320 }
19321
19322 /*
19323 * Truncate relations before dropping so that all dependencies between
19324 * relations are removed after they are worked on. Doing it like this
19325 * might be a waste as it is possible that a relation being truncated will
19326 * be dropped anyway due to its parent being dropped, but this makes the
19327 * code more robust because of not having to re-check that the relation
19328 * exists at truncation time.
19329 */
19330 if (oids_to_truncate != NIL)
19331 heap_truncate(oids_to_truncate);
19332
19333 if (oids_to_drop != NIL)
19334 {
19335 ObjectAddresses *targetObjects = new_object_addresses();
19336
19337 foreach(l, oids_to_drop)
19338 {
19339 ObjectAddress object;
19340
19341 object.classId = RelationRelationId;
19342 object.objectId = lfirst_oid(l);
19343 object.objectSubId = 0;
19344
19345 Assert(!object_address_present(&object, targetObjects));
19346
19347 add_exact_object_address(&object, targetObjects);
19348 }
19349
19350 /*
19351 * Object deletion might involve toast table access (to clean up
19352 * toasted catalog entries), so ensure we have a valid snapshot.
19353 */
19355
19356 /*
19357 * Since this is an automatic drop, rather than one directly initiated
19358 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19359 */
19362
19364
19365#ifdef USE_ASSERT_CHECKING
19366
19367 /*
19368 * Note that table deletion will call remove_on_commit_action, so the
19369 * entry should get marked as deleted.
19370 */
19371 foreach(l, on_commits)
19372 {
19373 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19374
19375 if (oc->oncommit != ONCOMMIT_DROP)
19376 continue;
19377
19379 }
19380#endif
19381 }
19382}
19383
19384/*
19385 * Post-commit or post-abort cleanup for ON COMMIT management.
19386 *
19387 * All we do here is remove no-longer-needed OnCommitItem entries.
19388 *
19389 * During commit, remove entries that were deleted during this transaction;
19390 * during abort, remove those created during this transaction.
19391 */
19392void
19394{
19395 ListCell *cur_item;
19396
19397 foreach(cur_item, on_commits)
19398 {
19399 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19400
19401 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19403 {
19404 /* cur_item must be removed */
19406 pfree(oc);
19407 }
19408 else
19409 {
19410 /* cur_item must be preserved */
19413 }
19414 }
19415}
19416
19417/*
19418 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19419 *
19420 * During subabort, we can immediately remove entries created during this
19421 * subtransaction. During subcommit, just relabel entries marked during
19422 * this subtransaction as being the parent's responsibility.
19423 */
19424void
19426 SubTransactionId parentSubid)
19427{
19428 ListCell *cur_item;
19429
19430 foreach(cur_item, on_commits)
19431 {
19432 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19433
19434 if (!isCommit && oc->creating_subid == mySubid)
19435 {
19436 /* cur_item must be removed */
19438 pfree(oc);
19439 }
19440 else
19441 {
19442 /* cur_item must be preserved */
19443 if (oc->creating_subid == mySubid)
19444 oc->creating_subid = parentSubid;
19445 if (oc->deleting_subid == mySubid)
19446 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19447 }
19448 }
19449}
19450
19451/*
19452 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19453 * the relation to be locked only if (1) it's a plain or partitioned table,
19454 * materialized view, or TOAST table and (2) the current user is the owner (or
19455 * the superuser) or has been granted MAINTAIN. This meets the
19456 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19457 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19458 */
19459void
19461 Oid relId, Oid oldRelId, void *arg)
19462{
19463 char relkind;
19464 AclResult aclresult;
19465
19466 /* Nothing to do if the relation was not found. */
19467 if (!OidIsValid(relId))
19468 return;
19469
19470 /*
19471 * If the relation does exist, check whether it's an index. But note that
19472 * the relation might have been dropped between the time we did the name
19473 * lookup and now. In that case, there's nothing to do.
19474 */
19475 relkind = get_rel_relkind(relId);
19476 if (!relkind)
19477 return;
19478 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19479 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19480 ereport(ERROR,
19481 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19482 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19483
19484 /* Check permissions */
19485 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19486 if (aclresult != ACLCHECK_OK)
19487 aclcheck_error(aclresult,
19489 relation->relname);
19490}
19491
19492/*
19493 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19494 */
19495static void
19497 Oid relId, Oid oldRelId, void *arg)
19498{
19499 HeapTuple tuple;
19500
19501 /* Nothing to do if the relation was not found. */
19502 if (!OidIsValid(relId))
19503 return;
19504
19505 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19506 if (!HeapTupleIsValid(tuple)) /* should not happen */
19507 elog(ERROR, "cache lookup failed for relation %u", relId);
19508
19511
19512 ReleaseSysCache(tuple);
19513}
19514
19515/*
19516 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19517 * the owner of the relation, or superuser.
19518 */
19519void
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
19533 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19535 relation->relname);
19536
19537 if (!allowSystemTableMods &&
19538 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19539 ereport(ERROR,
19540 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19541 errmsg("permission denied: \"%s\" is a system catalog",
19542 relation->relname)));
19543
19544 ReleaseSysCache(tuple);
19545}
19546
19547/*
19548 * Common RangeVarGetRelid callback for rename, set schema, and alter table
19549 * processing.
19550 */
19551static void
19553 void *arg)
19554{
19555 Node *stmt = (Node *) arg;
19556 ObjectType reltype;
19557 HeapTuple tuple;
19558 Form_pg_class classform;
19559 AclResult aclresult;
19560 char relkind;
19561
19562 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19563 if (!HeapTupleIsValid(tuple))
19564 return; /* concurrently dropped */
19565 classform = (Form_pg_class) GETSTRUCT(tuple);
19566 relkind = classform->relkind;
19567
19568 /* Must own relation. */
19569 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19571
19572 /* No system table modifications unless explicitly allowed. */
19573 if (!allowSystemTableMods && IsSystemClass(relid, classform))
19574 ereport(ERROR,
19575 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19576 errmsg("permission denied: \"%s\" is a system catalog",
19577 rv->relname)));
19578
19579 /*
19580 * Extract the specified relation type from the statement parse tree.
19581 *
19582 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19583 * have CREATE rights on the containing namespace.
19584 */
19585 if (IsA(stmt, RenameStmt))
19586 {
19587 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19589 if (aclresult != ACLCHECK_OK)
19590 aclcheck_error(aclresult, OBJECT_SCHEMA,
19591 get_namespace_name(classform->relnamespace));
19592 reltype = ((RenameStmt *) stmt)->renameType;
19593 }
19594 else if (IsA(stmt, AlterObjectSchemaStmt))
19595 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19596
19597 else if (IsA(stmt, AlterTableStmt))
19598 reltype = ((AlterTableStmt *) stmt)->objtype;
19599 else
19600 {
19601 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19602 reltype = OBJECT_TABLE; /* placate compiler */
19603 }
19604
19605 /*
19606 * For compatibility with prior releases, we allow ALTER TABLE to be used
19607 * with most other types of relations (but not composite types). We allow
19608 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19609 * otherwise. Otherwise, the user must select the correct form of the
19610 * command for the relation at issue.
19611 */
19612 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19613 ereport(ERROR,
19614 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19615 errmsg("\"%s\" is not a sequence", rv->relname)));
19616
19617 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19618 ereport(ERROR,
19619 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19620 errmsg("\"%s\" is not a view", rv->relname)));
19621
19622 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19623 ereport(ERROR,
19624 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19625 errmsg("\"%s\" is not a materialized view", rv->relname)));
19626
19627 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19628 ereport(ERROR,
19629 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19630 errmsg("\"%s\" is not a foreign table", rv->relname)));
19631
19632 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19633 ereport(ERROR,
19634 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19635 errmsg("\"%s\" is not a composite type", rv->relname)));
19636
19637 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19638 relkind != RELKIND_PARTITIONED_INDEX
19639 && !IsA(stmt, RenameStmt))
19640 ereport(ERROR,
19641 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19642 errmsg("\"%s\" is not an index", rv->relname)));
19643
19644 /*
19645 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19646 * TYPE for that.
19647 */
19648 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19649 ereport(ERROR,
19650 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19651 errmsg("\"%s\" is a composite type", rv->relname),
19652 /* translator: %s is an SQL ALTER command */
19653 errhint("Use %s instead.",
19654 "ALTER TYPE")));
19655
19656 /*
19657 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19658 * to a different schema, such as indexes and TOAST tables.
19659 */
19661 {
19662 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19663 ereport(ERROR,
19664 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19665 errmsg("cannot change schema of index \"%s\"",
19666 rv->relname),
19667 errhint("Change the schema of the table instead.")));
19668 else if (relkind == RELKIND_COMPOSITE_TYPE)
19669 ereport(ERROR,
19670 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19671 errmsg("cannot change schema of composite type \"%s\"",
19672 rv->relname),
19673 /* translator: %s is an SQL ALTER command */
19674 errhint("Use %s instead.",
19675 "ALTER TYPE")));
19676 else if (relkind == RELKIND_TOASTVALUE)
19677 ereport(ERROR,
19678 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19679 errmsg("cannot change schema of TOAST table \"%s\"",
19680 rv->relname),
19681 errhint("Change the schema of the table instead.")));
19682 }
19683
19684 ReleaseSysCache(tuple);
19685}
19686
19687/*
19688 * Transform any expressions present in the partition key
19689 *
19690 * Returns a transformed PartitionSpec.
19691 */
19692static PartitionSpec *
19694{
19695 PartitionSpec *newspec;
19696 ParseState *pstate;
19697 ParseNamespaceItem *nsitem;
19698 ListCell *l;
19699
19700 newspec = makeNode(PartitionSpec);
19701
19702 newspec->strategy = partspec->strategy;
19703 newspec->partParams = NIL;
19704 newspec->location = partspec->location;
19705
19706 /* Check valid number of columns for strategy */
19707 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19708 list_length(partspec->partParams) != 1)
19709 ereport(ERROR,
19710 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19711 errmsg("cannot use \"list\" partition strategy with more than one column")));
19712
19713 /*
19714 * Create a dummy ParseState and insert the target relation as its sole
19715 * rangetable entry. We need a ParseState for transformExpr.
19716 */
19717 pstate = make_parsestate(NULL);
19718 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19719 NULL, false, true);
19720 addNSItemToQuery(pstate, nsitem, true, true, true);
19721
19722 /* take care of any partition expressions */
19723 foreach(l, partspec->partParams)
19724 {
19726
19727 if (pelem->expr)
19728 {
19729 /* Copy, to avoid scribbling on the input */
19730 pelem = copyObject(pelem);
19731
19732 /* Now do parse transformation of the expression */
19733 pelem->expr = transformExpr(pstate, pelem->expr,
19735
19736 /* we have to fix its collations too */
19737 assign_expr_collations(pstate, pelem->expr);
19738 }
19739
19740 newspec->partParams = lappend(newspec->partParams, pelem);
19741 }
19742
19743 return newspec;
19744}
19745
19746/*
19747 * Compute per-partition-column information from a list of PartitionElems.
19748 * Expressions in the PartitionElems must be parse-analyzed already.
19749 */
19750static void
19751ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19752 List **partexprs, Oid *partopclass, Oid *partcollation,
19753 PartitionStrategy strategy)
19754{
19755 int attn;
19756 ListCell *lc;
19757 Oid am_oid;
19758
19759 attn = 0;
19760 foreach(lc, partParams)
19761 {
19763 Oid atttype;
19764 Oid attcollation;
19765
19766 if (pelem->name != NULL)
19767 {
19768 /* Simple attribute reference */
19769 HeapTuple atttuple;
19770 Form_pg_attribute attform;
19771
19773 pelem->name);
19774 if (!HeapTupleIsValid(atttuple))
19775 ereport(ERROR,
19776 (errcode(ERRCODE_UNDEFINED_COLUMN),
19777 errmsg("column \"%s\" named in partition key does not exist",
19778 pelem->name),
19779 parser_errposition(pstate, pelem->location)));
19780 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19781
19782 if (attform->attnum <= 0)
19783 ereport(ERROR,
19784 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19785 errmsg("cannot use system column \"%s\" in partition key",
19786 pelem->name),
19787 parser_errposition(pstate, pelem->location)));
19788
19789 /*
19790 * Stored generated columns cannot work: They are computed after
19791 * BEFORE triggers, but partition routing is done before all
19792 * triggers. Maybe virtual generated columns could be made to
19793 * work, but then they would need to be handled as an expression
19794 * below.
19795 */
19796 if (attform->attgenerated)
19797 ereport(ERROR,
19798 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19799 errmsg("cannot use generated column in partition key"),
19800 errdetail("Column \"%s\" is a generated column.",
19801 pelem->name),
19802 parser_errposition(pstate, pelem->location)));
19803
19804 partattrs[attn] = attform->attnum;
19805 atttype = attform->atttypid;
19806 attcollation = attform->attcollation;
19807 ReleaseSysCache(atttuple);
19808 }
19809 else
19810 {
19811 /* Expression */
19812 Node *expr = pelem->expr;
19813 char partattname[16];
19814
19815 Assert(expr != NULL);
19816 atttype = exprType(expr);
19817 attcollation = exprCollation(expr);
19818
19819 /*
19820 * The expression must be of a storable type (e.g., not RECORD).
19821 * The test is the same as for whether a table column is of a safe
19822 * type (which is why we needn't check for the non-expression
19823 * case).
19824 */
19825 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19826 CheckAttributeType(partattname,
19827 atttype, attcollation,
19829
19830 /*
19831 * Strip any top-level COLLATE clause. This ensures that we treat
19832 * "x COLLATE y" and "(x COLLATE y)" alike.
19833 */
19834 while (IsA(expr, CollateExpr))
19835 expr = (Node *) ((CollateExpr *) expr)->arg;
19836
19837 if (IsA(expr, Var) &&
19838 ((Var *) expr)->varattno > 0)
19839 {
19840 /*
19841 * User wrote "(column)" or "(column COLLATE something)".
19842 * Treat it like simple attribute anyway.
19843 */
19844 partattrs[attn] = ((Var *) expr)->varattno;
19845 }
19846 else
19847 {
19848 Bitmapset *expr_attrs = NULL;
19849 int i;
19850
19851 partattrs[attn] = 0; /* marks the column as expression */
19852 *partexprs = lappend(*partexprs, expr);
19853
19854 /*
19855 * transformPartitionSpec() should have already rejected
19856 * subqueries, aggregates, window functions, and SRFs, based
19857 * on the EXPR_KIND_ for partition expressions.
19858 */
19859
19860 /*
19861 * Cannot allow system column references, since that would
19862 * make partition routing impossible: their values won't be
19863 * known yet when we need to do that.
19864 */
19865 pull_varattnos(expr, 1, &expr_attrs);
19866 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
19867 {
19869 expr_attrs))
19870 ereport(ERROR,
19871 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19872 errmsg("partition key expressions cannot contain system column references")));
19873 }
19874
19875 /*
19876 * Stored generated columns cannot work: They are computed
19877 * after BEFORE triggers, but partition routing is done before
19878 * all triggers. Virtual generated columns could probably
19879 * work, but it would require more work elsewhere (for example
19880 * SET EXPRESSION would need to check whether the column is
19881 * used in partition keys). Seems safer to prohibit for now.
19882 */
19883 i = -1;
19884 while ((i = bms_next_member(expr_attrs, i)) >= 0)
19885 {
19887
19888 if (attno > 0 &&
19889 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19890 ereport(ERROR,
19891 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19892 errmsg("cannot use generated column in partition key"),
19893 errdetail("Column \"%s\" is a generated column.",
19894 get_attname(RelationGetRelid(rel), attno, false)),
19895 parser_errposition(pstate, pelem->location)));
19896 }
19897
19898 /*
19899 * Preprocess the expression before checking for mutability.
19900 * This is essential for the reasons described in
19901 * contain_mutable_functions_after_planning. However, we call
19902 * expression_planner for ourselves rather than using that
19903 * function, because if constant-folding reduces the
19904 * expression to a constant, we'd like to know that so we can
19905 * complain below.
19906 *
19907 * Like contain_mutable_functions_after_planning, assume that
19908 * expression_planner won't scribble on its input, so this
19909 * won't affect the partexprs entry we saved above.
19910 */
19911 expr = (Node *) expression_planner((Expr *) expr);
19912
19913 /*
19914 * Partition expressions cannot contain mutable functions,
19915 * because a given row must always map to the same partition
19916 * as long as there is no change in the partition boundary
19917 * structure.
19918 */
19919 if (contain_mutable_functions(expr))
19920 ereport(ERROR,
19921 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19922 errmsg("functions in partition key expression must be marked IMMUTABLE")));
19923
19924 /*
19925 * While it is not exactly *wrong* for a partition expression
19926 * to be a constant, it seems better to reject such keys.
19927 */
19928 if (IsA(expr, Const))
19929 ereport(ERROR,
19930 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19931 errmsg("cannot use constant expression as partition key")));
19932 }
19933 }
19934
19935 /*
19936 * Apply collation override if any
19937 */
19938 if (pelem->collation)
19939 attcollation = get_collation_oid(pelem->collation, false);
19940
19941 /*
19942 * Check we have a collation iff it's a collatable type. The only
19943 * expected failures here are (1) COLLATE applied to a noncollatable
19944 * type, or (2) partition expression had an unresolved collation. But
19945 * we might as well code this to be a complete consistency check.
19946 */
19947 if (type_is_collatable(atttype))
19948 {
19949 if (!OidIsValid(attcollation))
19950 ereport(ERROR,
19951 (errcode(ERRCODE_INDETERMINATE_COLLATION),
19952 errmsg("could not determine which collation to use for partition expression"),
19953 errhint("Use the COLLATE clause to set the collation explicitly.")));
19954 }
19955 else
19956 {
19957 if (OidIsValid(attcollation))
19958 ereport(ERROR,
19959 (errcode(ERRCODE_DATATYPE_MISMATCH),
19960 errmsg("collations are not supported by type %s",
19961 format_type_be(atttype))));
19962 }
19963
19964 partcollation[attn] = attcollation;
19965
19966 /*
19967 * Identify the appropriate operator class. For list and range
19968 * partitioning, we use a btree operator class; hash partitioning uses
19969 * a hash operator class.
19970 */
19971 if (strategy == PARTITION_STRATEGY_HASH)
19972 am_oid = HASH_AM_OID;
19973 else
19974 am_oid = BTREE_AM_OID;
19975
19976 if (!pelem->opclass)
19977 {
19978 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19979
19980 if (!OidIsValid(partopclass[attn]))
19981 {
19982 if (strategy == PARTITION_STRATEGY_HASH)
19983 ereport(ERROR,
19984 (errcode(ERRCODE_UNDEFINED_OBJECT),
19985 errmsg("data type %s has no default operator class for access method \"%s\"",
19986 format_type_be(atttype), "hash"),
19987 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19988 else
19989 ereport(ERROR,
19990 (errcode(ERRCODE_UNDEFINED_OBJECT),
19991 errmsg("data type %s has no default operator class for access method \"%s\"",
19992 format_type_be(atttype), "btree"),
19993 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19994 }
19995 }
19996 else
19997 partopclass[attn] = ResolveOpClass(pelem->opclass,
19998 atttype,
19999 am_oid == HASH_AM_OID ? "hash" : "btree",
20000 am_oid);
20001
20002 attn++;
20003 }
20004}
20005
20006/*
20007 * PartConstraintImpliedByRelConstraint
20008 * Do scanrel's existing constraints imply the partition constraint?
20009 *
20010 * "Existing constraints" include its check constraints and column-level
20011 * not-null constraints. partConstraint describes the partition constraint,
20012 * in implicit-AND form.
20013 */
20014bool
20016 List *partConstraint)
20017{
20018 List *existConstraint = NIL;
20019 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20020 int i;
20021
20022 if (constr && constr->has_not_null)
20023 {
20024 int natts = scanrel->rd_att->natts;
20025
20026 for (i = 1; i <= natts; i++)
20027 {
20028 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20029
20030 /* invalid not-null constraint must be ignored here */
20031 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20032 {
20033 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20034 NullTest *ntest = makeNode(NullTest);
20035
20036 ntest->arg = (Expr *) makeVar(1,
20037 i,
20038 wholeatt->atttypid,
20039 wholeatt->atttypmod,
20040 wholeatt->attcollation,
20041 0);
20042 ntest->nulltesttype = IS_NOT_NULL;
20043
20044 /*
20045 * argisrow=false is correct even for a composite column,
20046 * because attnotnull does not represent a SQL-spec IS NOT
20047 * NULL test in such a case, just IS DISTINCT FROM NULL.
20048 */
20049 ntest->argisrow = false;
20050 ntest->location = -1;
20051 existConstraint = lappend(existConstraint, ntest);
20052 }
20053 }
20054 }
20055
20056 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20057}
20058
20059/*
20060 * ConstraintImpliedByRelConstraint
20061 * Do scanrel's existing constraints imply the given constraint?
20062 *
20063 * testConstraint is the constraint to validate. provenConstraint is a
20064 * caller-provided list of conditions which this function may assume
20065 * to be true. Both provenConstraint and testConstraint must be in
20066 * implicit-AND form, must only contain immutable clauses, and must
20067 * contain only Vars with varno = 1.
20068 */
20069bool
20070ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20071{
20072 List *existConstraint = list_copy(provenConstraint);
20073 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20074 int num_check,
20075 i;
20076
20077 num_check = (constr != NULL) ? constr->num_check : 0;
20078 for (i = 0; i < num_check; i++)
20079 {
20080 Node *cexpr;
20081
20082 /*
20083 * If this constraint hasn't been fully validated yet, we must ignore
20084 * it here.
20085 */
20086 if (!constr->check[i].ccvalid)
20087 continue;
20088
20089 /*
20090 * NOT ENFORCED constraints are always marked as invalid, which should
20091 * have been ignored.
20092 */
20093 Assert(constr->check[i].ccenforced);
20094
20095 cexpr = stringToNode(constr->check[i].ccbin);
20096
20097 /*
20098 * Run each expression through const-simplification and
20099 * canonicalization. It is necessary, because we will be comparing it
20100 * to similarly-processed partition constraint expressions, and may
20101 * fail to detect valid matches without this.
20102 */
20103 cexpr = eval_const_expressions(NULL, cexpr);
20104 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20105
20106 existConstraint = list_concat(existConstraint,
20107 make_ands_implicit((Expr *) cexpr));
20108 }
20109
20110 /*
20111 * Try to make the proof. Since we are comparing CHECK constraints, we
20112 * need to use weak implication, i.e., we assume existConstraint is
20113 * not-false and try to prove the same for testConstraint.
20114 *
20115 * Note that predicate_implied_by assumes its first argument is known
20116 * immutable. That should always be true for both NOT NULL and partition
20117 * constraints, so we don't test it here.
20118 */
20119 return predicate_implied_by(testConstraint, existConstraint, true);
20120}
20121
20122/*
20123 * QueuePartitionConstraintValidation
20124 *
20125 * Add an entry to wqueue to have the given partition constraint validated by
20126 * Phase 3, for the given relation, and all its children.
20127 *
20128 * We first verify whether the given constraint is implied by pre-existing
20129 * relation constraints; if it is, there's no need to scan the table to
20130 * validate, so don't queue in that case.
20131 */
20132static void
20134 List *partConstraint,
20135 bool validate_default)
20136{
20137 /*
20138 * Based on the table's existing constraints, determine whether or not we
20139 * may skip scanning the table.
20140 */
20141 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20142 {
20143 if (!validate_default)
20145 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20146 RelationGetRelationName(scanrel))));
20147 else
20149 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20150 RelationGetRelationName(scanrel))));
20151 return;
20152 }
20153
20154 /*
20155 * Constraints proved insufficient. For plain relations, queue a
20156 * validation item now; for partitioned tables, recurse to process each
20157 * partition.
20158 */
20159 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20160 {
20161 AlteredTableInfo *tab;
20162
20163 /* Grab a work queue entry. */
20164 tab = ATGetQueueEntry(wqueue, scanrel);
20165 Assert(tab->partition_constraint == NULL);
20166 tab->partition_constraint = (Expr *) linitial(partConstraint);
20167 tab->validate_default = validate_default;
20168 }
20169 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20170 {
20171 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20172 int i;
20173
20174 for (i = 0; i < partdesc->nparts; i++)
20175 {
20176 Relation part_rel;
20177 List *thisPartConstraint;
20178
20179 /*
20180 * This is the minimum lock we need to prevent deadlocks.
20181 */
20182 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20183
20184 /*
20185 * Adjust the constraint for scanrel so that it matches this
20186 * partition's attribute numbers.
20187 */
20188 thisPartConstraint =
20189 map_partition_varattnos(partConstraint, 1,
20190 part_rel, scanrel);
20191
20192 QueuePartitionConstraintValidation(wqueue, part_rel,
20193 thisPartConstraint,
20194 validate_default);
20195 table_close(part_rel, NoLock); /* keep lock till commit */
20196 }
20197 }
20198}
20199
20200/*
20201 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20202 *
20203 * Return the address of the newly attached partition.
20204 */
20205static ObjectAddress
20207 AlterTableUtilityContext *context)
20208{
20209 Relation attachrel,
20210 catalog;
20211 List *attachrel_children;
20212 List *partConstraint;
20213 SysScanDesc scan;
20214 ScanKeyData skey;
20215 AttrNumber attno;
20216 int natts;
20217 TupleDesc tupleDesc;
20218 ObjectAddress address;
20219 const char *trigger_name;
20220 Oid defaultPartOid;
20221 List *partBoundConstraint;
20222 ParseState *pstate = make_parsestate(NULL);
20223
20224 pstate->p_sourcetext = context->queryString;
20225
20226 /*
20227 * We must lock the default partition if one exists, because attaching a
20228 * new partition will change its partition constraint.
20229 */
20230 defaultPartOid =
20232 if (OidIsValid(defaultPartOid))
20233 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20234
20235 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20236
20237 /*
20238 * XXX I think it'd be a good idea to grab locks on all tables referenced
20239 * by FKs at this point also.
20240 */
20241
20242 /*
20243 * Must be owner of both parent and source table -- parent was checked by
20244 * ATSimplePermissions call in ATPrepCmd
20245 */
20248
20249 /* A partition can only have one parent */
20250 if (attachrel->rd_rel->relispartition)
20251 ereport(ERROR,
20252 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20253 errmsg("\"%s\" is already a partition",
20254 RelationGetRelationName(attachrel))));
20255
20256 if (OidIsValid(attachrel->rd_rel->reloftype))
20257 ereport(ERROR,
20258 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20259 errmsg("cannot attach a typed table as partition")));
20260
20261 /*
20262 * Table being attached should not already be part of inheritance; either
20263 * as a child table...
20264 */
20265 catalog = table_open(InheritsRelationId, AccessShareLock);
20266 ScanKeyInit(&skey,
20267 Anum_pg_inherits_inhrelid,
20268 BTEqualStrategyNumber, F_OIDEQ,
20270 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20271 NULL, 1, &skey);
20273 ereport(ERROR,
20274 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20275 errmsg("cannot attach inheritance child as partition")));
20276 systable_endscan(scan);
20277
20278 /* ...or as a parent table (except the case when it is partitioned) */
20279 ScanKeyInit(&skey,
20280 Anum_pg_inherits_inhparent,
20281 BTEqualStrategyNumber, F_OIDEQ,
20283 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20284 1, &skey);
20286 attachrel->rd_rel->relkind == RELKIND_RELATION)
20287 ereport(ERROR,
20288 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20289 errmsg("cannot attach inheritance parent as partition")));
20290 systable_endscan(scan);
20291 table_close(catalog, AccessShareLock);
20292
20293 /*
20294 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20295 * particular, this disallows making a rel a partition of itself.)
20296 *
20297 * We do that by checking if rel is a member of the list of attachrel's
20298 * partitions provided the latter is partitioned at all. We want to avoid
20299 * having to construct this list again, so we request the strongest lock
20300 * on all partitions. We need the strongest lock, because we may decide
20301 * to scan them if we find out that the table being attached (or its leaf
20302 * partitions) may contain rows that violate the partition constraint. If
20303 * the table has a constraint that would prevent such rows, which by
20304 * definition is present in all the partitions, we need not scan the
20305 * table, nor its partitions. But we cannot risk a deadlock by taking a
20306 * weaker lock now and the stronger one only when needed.
20307 */
20308 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20309 AccessExclusiveLock, NULL);
20310 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20311 ereport(ERROR,
20312 (errcode(ERRCODE_DUPLICATE_TABLE),
20313 errmsg("circular inheritance not allowed"),
20314 errdetail("\"%s\" is already a child of \"%s\".",
20316 RelationGetRelationName(attachrel))));
20317
20318 /* If the parent is permanent, so must be all of its partitions. */
20319 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20320 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20321 ereport(ERROR,
20322 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20323 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20325
20326 /* Temp parent cannot have a partition that is itself not a temp */
20327 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20328 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20329 ereport(ERROR,
20330 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20331 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20333
20334 /* If the parent is temp, it must belong to this session */
20335 if (RELATION_IS_OTHER_TEMP(rel))
20336 ereport(ERROR,
20337 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20338 errmsg("cannot attach as partition of temporary relation of another session")));
20339
20340 /* Ditto for the partition */
20341 if (RELATION_IS_OTHER_TEMP(attachrel))
20342 ereport(ERROR,
20343 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20344 errmsg("cannot attach temporary relation of another session as partition")));
20345
20346 /*
20347 * Check if attachrel has any identity columns or any columns that aren't
20348 * in the parent.
20349 */
20350 tupleDesc = RelationGetDescr(attachrel);
20351 natts = tupleDesc->natts;
20352 for (attno = 1; attno <= natts; attno++)
20353 {
20354 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20355 char *attributeName = NameStr(attribute->attname);
20356
20357 /* Ignore dropped */
20358 if (attribute->attisdropped)
20359 continue;
20360
20361 if (attribute->attidentity)
20362 ereport(ERROR,
20363 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20364 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20365 RelationGetRelationName(attachrel), attributeName),
20366 errdetail("The new partition may not contain an identity column."));
20367
20368 /* Try to find the column in parent (matching on column name) */
20369 if (!SearchSysCacheExists2(ATTNAME,
20371 CStringGetDatum(attributeName)))
20372 ereport(ERROR,
20373 (errcode(ERRCODE_DATATYPE_MISMATCH),
20374 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20375 RelationGetRelationName(attachrel), attributeName,
20377 errdetail("The new partition may contain only the columns present in parent.")));
20378 }
20379
20380 /*
20381 * If child_rel has row-level triggers with transition tables, we
20382 * currently don't allow it to become a partition. See also prohibitions
20383 * in ATExecAddInherit() and CreateTrigger().
20384 */
20385 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20386 if (trigger_name != NULL)
20387 ereport(ERROR,
20388 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20389 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20390 trigger_name, RelationGetRelationName(attachrel)),
20391 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20392
20393 /*
20394 * Check that the new partition's bound is valid and does not overlap any
20395 * of existing partitions of the parent - note that it does not return on
20396 * error.
20397 */
20399 cmd->bound, pstate);
20400
20401 /* OK to create inheritance. Rest of the checks performed there */
20402 CreateInheritance(attachrel, rel, true);
20403
20404 /* Update the pg_class entry. */
20405 StorePartitionBound(attachrel, rel, cmd->bound);
20406
20407 /* Ensure there exists a correct set of indexes in the partition. */
20408 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20409
20410 /* and triggers */
20411 CloneRowTriggersToPartition(rel, attachrel);
20412
20413 /*
20414 * Clone foreign key constraints. Callee is responsible for setting up
20415 * for phase 3 constraint verification.
20416 */
20417 CloneForeignKeyConstraints(wqueue, rel, attachrel);
20418
20419 /*
20420 * Generate partition constraint from the partition bound specification.
20421 * If the parent itself is a partition, make sure to include its
20422 * constraint as well.
20423 */
20424 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20425
20426 /*
20427 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20428 * since it's needed later to construct the constraint expression for
20429 * validating against the default partition, if any.
20430 */
20431 partConstraint = list_concat_copy(partBoundConstraint,
20433
20434 /* Skip validation if there are no constraints to validate. */
20435 if (partConstraint)
20436 {
20437 /*
20438 * Run the partition quals through const-simplification similar to
20439 * check constraints. We skip canonicalize_qual, though, because
20440 * partition quals should be in canonical form already.
20441 */
20442 partConstraint =
20444 (Node *) partConstraint);
20445
20446 /* XXX this sure looks wrong */
20447 partConstraint = list_make1(make_ands_explicit(partConstraint));
20448
20449 /*
20450 * Adjust the generated constraint to match this partition's attribute
20451 * numbers.
20452 */
20453 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20454 rel);
20455
20456 /* Validate partition constraints against the table being attached. */
20457 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20458 false);
20459 }
20460
20461 /*
20462 * If we're attaching a partition other than the default partition and a
20463 * default one exists, then that partition's partition constraint changes,
20464 * so add an entry to the work queue to validate it, too. (We must not do
20465 * this when the partition being attached is the default one; we already
20466 * did it above!)
20467 */
20468 if (OidIsValid(defaultPartOid))
20469 {
20470 Relation defaultrel;
20471 List *defPartConstraint;
20472
20473 Assert(!cmd->bound->is_default);
20474
20475 /* we already hold a lock on the default partition */
20476 defaultrel = table_open(defaultPartOid, NoLock);
20477 defPartConstraint =
20478 get_proposed_default_constraint(partBoundConstraint);
20479
20480 /*
20481 * Map the Vars in the constraint expression from rel's attnos to
20482 * defaultrel's.
20483 */
20484 defPartConstraint =
20485 map_partition_varattnos(defPartConstraint,
20486 1, defaultrel, rel);
20487 QueuePartitionConstraintValidation(wqueue, defaultrel,
20488 defPartConstraint, true);
20489
20490 /* keep our lock until commit. */
20491 table_close(defaultrel, NoLock);
20492 }
20493
20494 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20495
20496 /*
20497 * If the partition we just attached is partitioned itself, invalidate
20498 * relcache for all descendent partitions too to ensure that their
20499 * rd_partcheck expression trees are rebuilt; partitions already locked at
20500 * the beginning of this function.
20501 */
20502 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20503 {
20504 ListCell *l;
20505
20506 foreach(l, attachrel_children)
20507 {
20509 }
20510 }
20511
20512 /* keep our lock until commit */
20513 table_close(attachrel, NoLock);
20514
20515 return address;
20516}
20517
20518/*
20519 * AttachPartitionEnsureIndexes
20520 * subroutine for ATExecAttachPartition to create/match indexes
20521 *
20522 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20523 * PARTITION: every partition must have an index attached to each index on the
20524 * partitioned table.
20525 */
20526static void
20528{
20529 List *idxes;
20530 List *attachRelIdxs;
20531 Relation *attachrelIdxRels;
20532 IndexInfo **attachInfos;
20533 ListCell *cell;
20534 MemoryContext cxt;
20535 MemoryContext oldcxt;
20536
20538 "AttachPartitionEnsureIndexes",
20540 oldcxt = MemoryContextSwitchTo(cxt);
20541
20542 idxes = RelationGetIndexList(rel);
20543 attachRelIdxs = RelationGetIndexList(attachrel);
20544 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20545 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20546
20547 /* Build arrays of all existing indexes and their IndexInfos */
20548 foreach_oid(cldIdxId, attachRelIdxs)
20549 {
20550 int i = foreach_current_index(cldIdxId);
20551
20552 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20553 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20554 }
20555
20556 /*
20557 * If we're attaching a foreign table, we must fail if any of the indexes
20558 * is a constraint index; otherwise, there's nothing to do here. Do this
20559 * before starting work, to avoid wasting the effort of building a few
20560 * non-unique indexes before coming across a unique one.
20561 */
20562 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20563 {
20564 foreach(cell, idxes)
20565 {
20566 Oid idx = lfirst_oid(cell);
20568
20569 if (idxRel->rd_index->indisunique ||
20570 idxRel->rd_index->indisprimary)
20571 ereport(ERROR,
20572 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20573 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20574 RelationGetRelationName(attachrel),
20576 errdetail("Partitioned table \"%s\" contains unique indexes.",
20579 }
20580
20581 goto out;
20582 }
20583
20584 /*
20585 * For each index on the partitioned table, find a matching one in the
20586 * partition-to-be; if one is not found, create one.
20587 */
20588 foreach(cell, idxes)
20589 {
20590 Oid idx = lfirst_oid(cell);
20592 IndexInfo *info;
20593 AttrMap *attmap;
20594 bool found = false;
20595 Oid constraintOid;
20596
20597 /*
20598 * Ignore indexes in the partitioned table other than partitioned
20599 * indexes.
20600 */
20601 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20602 {
20604 continue;
20605 }
20606
20607 /* construct an indexinfo to compare existing indexes against */
20608 info = BuildIndexInfo(idxRel);
20609 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20610 RelationGetDescr(rel),
20611 false);
20613
20614 /*
20615 * Scan the list of existing indexes in the partition-to-be, and mark
20616 * the first matching, valid, unattached one we find, if any, as
20617 * partition of the parent index. If we find one, we're done.
20618 */
20619 for (int i = 0; i < list_length(attachRelIdxs); i++)
20620 {
20621 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20622 Oid cldConstrOid = InvalidOid;
20623
20624 /* does this index have a parent? if so, can't use it */
20625 if (attachrelIdxRels[i]->rd_rel->relispartition)
20626 continue;
20627
20628 /* If this index is invalid, can't use it */
20629 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20630 continue;
20631
20632 if (CompareIndexInfo(attachInfos[i], info,
20633 attachrelIdxRels[i]->rd_indcollation,
20634 idxRel->rd_indcollation,
20635 attachrelIdxRels[i]->rd_opfamily,
20636 idxRel->rd_opfamily,
20637 attmap))
20638 {
20639 /*
20640 * If this index is being created in the parent because of a
20641 * constraint, then the child needs to have a constraint also,
20642 * so look for one. If there is no such constraint, this
20643 * index is no good, so keep looking.
20644 */
20645 if (OidIsValid(constraintOid))
20646 {
20647 cldConstrOid =
20649 cldIdxId);
20650 /* no dice */
20651 if (!OidIsValid(cldConstrOid))
20652 continue;
20653
20654 /* Ensure they're both the same type of constraint */
20655 if (get_constraint_type(constraintOid) !=
20656 get_constraint_type(cldConstrOid))
20657 continue;
20658 }
20659
20660 /* bingo. */
20661 IndexSetParentIndex(attachrelIdxRels[i], idx);
20662 if (OidIsValid(constraintOid))
20663 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20664 RelationGetRelid(attachrel));
20665 found = true;
20666
20668 break;
20669 }
20670 }
20671
20672 /*
20673 * If no suitable index was found in the partition-to-be, create one
20674 * now. Note that if this is a PK, not-null constraints must already
20675 * exist.
20676 */
20677 if (!found)
20678 {
20679 IndexStmt *stmt;
20680 Oid conOid;
20681
20683 idxRel, attmap,
20684 &conOid);
20686 RelationGetRelid(idxRel),
20687 conOid,
20688 -1,
20689 true, false, false, false, false);
20690 }
20691
20693 }
20694
20695out:
20696 /* Clean up. */
20697 for (int i = 0; i < list_length(attachRelIdxs); i++)
20698 index_close(attachrelIdxRels[i], AccessShareLock);
20699 MemoryContextSwitchTo(oldcxt);
20701}
20702
20703/*
20704 * CloneRowTriggersToPartition
20705 * subroutine for ATExecAttachPartition/DefineRelation to create row
20706 * triggers on partitions
20707 */
20708static void
20710{
20711 Relation pg_trigger;
20713 SysScanDesc scan;
20714 HeapTuple tuple;
20715 MemoryContext perTupCxt;
20716
20717 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20718 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20719 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20720 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20721 true, NULL, 1, &key);
20722
20724 "clone trig", ALLOCSET_SMALL_SIZES);
20725
20726 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20727 {
20728 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20729 CreateTrigStmt *trigStmt;
20730 Node *qual = NULL;
20731 Datum value;
20732 bool isnull;
20733 List *cols = NIL;
20734 List *trigargs = NIL;
20735 MemoryContext oldcxt;
20736
20737 /*
20738 * Ignore statement-level triggers; those are not cloned.
20739 */
20740 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20741 continue;
20742
20743 /*
20744 * Don't clone internal triggers, because the constraint cloning code
20745 * will.
20746 */
20747 if (trigForm->tgisinternal)
20748 continue;
20749
20750 /*
20751 * Complain if we find an unexpected trigger type.
20752 */
20753 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20754 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20755 elog(ERROR, "unexpected trigger \"%s\" found",
20756 NameStr(trigForm->tgname));
20757
20758 /* Use short-lived context for CREATE TRIGGER */
20759 oldcxt = MemoryContextSwitchTo(perTupCxt);
20760
20761 /*
20762 * If there is a WHEN clause, generate a 'cooked' version of it that's
20763 * appropriate for the partition.
20764 */
20765 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20766 RelationGetDescr(pg_trigger), &isnull);
20767 if (!isnull)
20768 {
20770 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20771 partition, parent);
20772 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20773 partition, parent);
20774 }
20775
20776 /*
20777 * If there is a column list, transform it to a list of column names.
20778 * Note we don't need to map this list in any way ...
20779 */
20780 if (trigForm->tgattr.dim1 > 0)
20781 {
20782 int i;
20783
20784 for (i = 0; i < trigForm->tgattr.dim1; i++)
20785 {
20787
20788 col = TupleDescAttr(parent->rd_att,
20789 trigForm->tgattr.values[i] - 1);
20790 cols = lappend(cols,
20791 makeString(pstrdup(NameStr(col->attname))));
20792 }
20793 }
20794
20795 /* Reconstruct trigger arguments list. */
20796 if (trigForm->tgnargs > 0)
20797 {
20798 char *p;
20799
20800 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20801 RelationGetDescr(pg_trigger), &isnull);
20802 if (isnull)
20803 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20804 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20805
20806 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20807
20808 for (int i = 0; i < trigForm->tgnargs; i++)
20809 {
20810 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20811 p += strlen(p) + 1;
20812 }
20813 }
20814
20815 trigStmt = makeNode(CreateTrigStmt);
20816 trigStmt->replace = false;
20817 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20818 trigStmt->trigname = NameStr(trigForm->tgname);
20819 trigStmt->relation = NULL;
20820 trigStmt->funcname = NULL; /* passed separately */
20821 trigStmt->args = trigargs;
20822 trigStmt->row = true;
20823 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20824 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20825 trigStmt->columns = cols;
20826 trigStmt->whenClause = NULL; /* passed separately */
20827 trigStmt->transitionRels = NIL; /* not supported at present */
20828 trigStmt->deferrable = trigForm->tgdeferrable;
20829 trigStmt->initdeferred = trigForm->tginitdeferred;
20830 trigStmt->constrrel = NULL; /* passed separately */
20831
20832 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20833 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20834 trigForm->tgfoid, trigForm->oid, qual,
20835 false, true, trigForm->tgenabled);
20836
20837 MemoryContextSwitchTo(oldcxt);
20838 MemoryContextReset(perTupCxt);
20839 }
20840
20841 MemoryContextDelete(perTupCxt);
20842
20843 systable_endscan(scan);
20844 table_close(pg_trigger, RowExclusiveLock);
20845}
20846
20847/*
20848 * ALTER TABLE DETACH PARTITION
20849 *
20850 * Return the address of the relation that is no longer a partition of rel.
20851 *
20852 * If concurrent mode is requested, we run in two transactions. A side-
20853 * effect is that this command cannot run in a multi-part ALTER TABLE.
20854 * Currently, that's enforced by the grammar.
20855 *
20856 * The strategy for concurrency is to first modify the partition's
20857 * pg_inherit catalog row to make it visible to everyone that the
20858 * partition is detached, lock the partition against writes, and commit
20859 * the transaction; anyone who requests the partition descriptor from
20860 * that point onwards has to ignore such a partition. In a second
20861 * transaction, we wait until all transactions that could have seen the
20862 * partition as attached are gone, then we remove the rest of partition
20863 * metadata (pg_inherits and pg_class.relpartbounds).
20864 */
20865static ObjectAddress
20867 RangeVar *name, bool concurrent)
20868{
20869 Relation partRel;
20870 ObjectAddress address;
20871 Oid defaultPartOid;
20872
20873 /*
20874 * We must lock the default partition, because detaching this partition
20875 * will change its partition constraint.
20876 */
20877 defaultPartOid =
20879 if (OidIsValid(defaultPartOid))
20880 {
20881 /*
20882 * Concurrent detaching when a default partition exists is not
20883 * supported. The main problem is that the default partition
20884 * constraint would change. And there's a definitional problem: what
20885 * should happen to the tuples that are being inserted that belong to
20886 * the partition being detached? Putting them on the partition being
20887 * detached would be wrong, since they'd become "lost" after the
20888 * detaching completes but we cannot put them in the default partition
20889 * either until we alter its partition constraint.
20890 *
20891 * I think we could solve this problem if we effected the constraint
20892 * change before committing the first transaction. But the lock would
20893 * have to remain AEL and it would cause concurrent query planning to
20894 * be blocked, so changing it that way would be even worse.
20895 */
20896 if (concurrent)
20897 ereport(ERROR,
20898 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20899 errmsg("cannot detach partitions concurrently when a default partition exists")));
20900 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20901 }
20902
20903 /*
20904 * In concurrent mode, the partition is locked with share-update-exclusive
20905 * in the first transaction. This allows concurrent transactions to be
20906 * doing DML to the partition.
20907 */
20908 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20910
20911 /*
20912 * Check inheritance conditions and either delete the pg_inherits row (in
20913 * non-concurrent mode) or just set the inhdetachpending flag.
20914 */
20915 if (!concurrent)
20916 RemoveInheritance(partRel, rel, false);
20917 else
20918 MarkInheritDetached(partRel, rel);
20919
20920 /*
20921 * Ensure that foreign keys still hold after this detach. This keeps
20922 * locks on the referencing tables, which prevents concurrent transactions
20923 * from adding rows that we wouldn't see. For this to work in concurrent
20924 * mode, it is critical that the partition appears as no longer attached
20925 * for the RI queries as soon as the first transaction commits.
20926 */
20928
20929 /*
20930 * Concurrent mode has to work harder; first we add a new constraint to
20931 * the partition that matches the partition constraint. Then we close our
20932 * existing transaction, and in a new one wait for all processes to catch
20933 * up on the catalog updates we've done so far; at that point we can
20934 * complete the operation.
20935 */
20936 if (concurrent)
20937 {
20938 Oid partrelid,
20939 parentrelid;
20940 LOCKTAG tag;
20941 char *parentrelname;
20942 char *partrelname;
20943
20944 /*
20945 * Add a new constraint to the partition being detached, which
20946 * supplants the partition constraint (unless there is one already).
20947 */
20948 DetachAddConstraintIfNeeded(wqueue, partRel);
20949
20950 /*
20951 * We're almost done now; the only traces that remain are the
20952 * pg_inherits tuple and the partition's relpartbounds. Before we can
20953 * remove those, we need to wait until all transactions that know that
20954 * this is a partition are gone.
20955 */
20956
20957 /*
20958 * Remember relation OIDs to re-acquire them later; and relation names
20959 * too, for error messages if something is dropped in between.
20960 */
20961 partrelid = RelationGetRelid(partRel);
20962 parentrelid = RelationGetRelid(rel);
20963 parentrelname = MemoryContextStrdup(PortalContext,
20965 partrelname = MemoryContextStrdup(PortalContext,
20966 RelationGetRelationName(partRel));
20967
20968 /* Invalidate relcache entries for the parent -- must be before close */
20970
20971 table_close(partRel, NoLock);
20972 table_close(rel, NoLock);
20973 tab->rel = NULL;
20974
20975 /* Make updated catalog entry visible */
20978
20980
20981 /*
20982 * Now wait. This ensures that all queries that were planned
20983 * including the partition are finished before we remove the rest of
20984 * catalog entries. We don't need or indeed want to acquire this
20985 * lock, though -- that would block later queries.
20986 *
20987 * We don't need to concern ourselves with waiting for a lock on the
20988 * partition itself, since we will acquire AccessExclusiveLock below.
20989 */
20990 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20992
20993 /*
20994 * Now acquire locks in both relations again. Note they may have been
20995 * removed in the meantime, so care is required.
20996 */
20997 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20998 partRel = try_relation_open(partrelid, AccessExclusiveLock);
20999
21000 /* If the relations aren't there, something bad happened; bail out */
21001 if (rel == NULL)
21002 {
21003 if (partRel != NULL) /* shouldn't happen */
21004 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21005 partrelname);
21006 ereport(ERROR,
21007 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21008 errmsg("partitioned table \"%s\" was removed concurrently",
21009 parentrelname)));
21010 }
21011 if (partRel == NULL)
21012 ereport(ERROR,
21013 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21014 errmsg("partition \"%s\" was removed concurrently", partrelname)));
21015
21016 tab->rel = rel;
21017 }
21018
21019 /*
21020 * Detaching the partition might involve TOAST table access, so ensure we
21021 * have a valid snapshot.
21022 */
21024
21025 /* Do the final part of detaching */
21026 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21027
21029
21030 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21031
21032 /* keep our lock until commit */
21033 table_close(partRel, NoLock);
21034
21035 return address;
21036}
21037
21038/*
21039 * Second part of ALTER TABLE .. DETACH.
21040 *
21041 * This is separate so that it can be run independently when the second
21042 * transaction of the concurrent algorithm fails (crash or abort).
21043 */
21044static void
21045DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
21046 Oid defaultPartOid)
21047{
21048 Relation classRel;
21049 List *fks;
21050 ListCell *cell;
21051 List *indexes;
21052 Datum new_val[Natts_pg_class];
21053 bool new_null[Natts_pg_class],
21054 new_repl[Natts_pg_class];
21055 HeapTuple tuple,
21056 newtuple;
21057 Relation trigrel = NULL;
21058 List *fkoids = NIL;
21059
21060 if (concurrent)
21061 {
21062 /*
21063 * We can remove the pg_inherits row now. (In the non-concurrent case,
21064 * this was already done).
21065 */
21066 RemoveInheritance(partRel, rel, true);
21067 }
21068
21069 /* Drop any triggers that were cloned on creation/attach. */
21071
21072 /*
21073 * Detach any foreign keys that are inherited. This includes creating
21074 * additional action triggers.
21075 */
21076 fks = copyObject(RelationGetFKeyList(partRel));
21077 if (fks != NIL)
21078 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21079
21080 /*
21081 * It's possible that the partition being detached has a foreign key that
21082 * references a partitioned table. When that happens, there are multiple
21083 * pg_constraint rows for the partition: one points to the partitioned
21084 * table itself, while the others point to each of its partitions. Only
21085 * the topmost one is to be considered here; the child constraints must be
21086 * left alone, because conceptually those aren't coming from our parent
21087 * partitioned table, but from this partition itself.
21088 *
21089 * We implement this by collecting all the constraint OIDs in a first scan
21090 * of the FK array, and skipping in the loop below those constraints whose
21091 * parents are listed here.
21092 */
21094 fkoids = lappend_oid(fkoids, fk->conoid);
21095
21096 foreach(cell, fks)
21097 {
21098 ForeignKeyCacheInfo *fk = lfirst(cell);
21099 HeapTuple contup;
21100 Form_pg_constraint conform;
21101
21102 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21103 if (!HeapTupleIsValid(contup))
21104 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21105 conform = (Form_pg_constraint) GETSTRUCT(contup);
21106
21107 /*
21108 * Consider only inherited foreign keys, and only if their parents
21109 * aren't in the list.
21110 */
21111 if (conform->contype != CONSTRAINT_FOREIGN ||
21112 !OidIsValid(conform->conparentid) ||
21113 list_member_oid(fkoids, conform->conparentid))
21114 {
21115 ReleaseSysCache(contup);
21116 continue;
21117 }
21118
21119 /*
21120 * The constraint on this table must be marked no longer a child of
21121 * the parent's constraint, as do its check triggers.
21122 */
21124
21125 /*
21126 * Also, look up the partition's "check" triggers corresponding to the
21127 * ENFORCED constraint being detached and detach them from the parent
21128 * triggers. NOT ENFORCED constraints do not have these triggers;
21129 * therefore, this step is not needed.
21130 */
21131 if (fk->conenforced)
21132 {
21133 Oid insertTriggerOid,
21134 updateTriggerOid;
21135
21137 fk->conoid, fk->confrelid, fk->conrelid,
21138 &insertTriggerOid, &updateTriggerOid);
21139 Assert(OidIsValid(insertTriggerOid));
21140 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21141 RelationGetRelid(partRel));
21142 Assert(OidIsValid(updateTriggerOid));
21143 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21144 RelationGetRelid(partRel));
21145 }
21146
21147 /*
21148 * Lastly, create the action triggers on the referenced table, using
21149 * addFkRecurseReferenced, which requires some elaborate setup (so put
21150 * it in a separate block). While at it, if the table is partitioned,
21151 * that function will recurse to create the pg_constraint rows and
21152 * action triggers for each partition.
21153 *
21154 * Note there's no need to do addFkConstraint() here, because the
21155 * pg_constraint row already exists.
21156 */
21157 {
21158 Constraint *fkconstraint;
21159 int numfks;
21160 AttrNumber conkey[INDEX_MAX_KEYS];
21161 AttrNumber confkey[INDEX_MAX_KEYS];
21162 Oid conpfeqop[INDEX_MAX_KEYS];
21163 Oid conppeqop[INDEX_MAX_KEYS];
21164 Oid conffeqop[INDEX_MAX_KEYS];
21165 int numfkdelsetcols;
21166 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21167 Relation refdRel;
21168
21170 &numfks,
21171 conkey,
21172 confkey,
21173 conpfeqop,
21174 conppeqop,
21175 conffeqop,
21176 &numfkdelsetcols,
21177 confdelsetcols);
21178
21179 /* Create a synthetic node we'll use throughout */
21180 fkconstraint = makeNode(Constraint);
21181 fkconstraint->contype = CONSTRAINT_FOREIGN;
21182 fkconstraint->conname = pstrdup(NameStr(conform->conname));
21183 fkconstraint->deferrable = conform->condeferrable;
21184 fkconstraint->initdeferred = conform->condeferred;
21185 fkconstraint->is_enforced = conform->conenforced;
21186 fkconstraint->skip_validation = true;
21187 fkconstraint->initially_valid = conform->convalidated;
21188 /* a few irrelevant fields omitted here */
21189 fkconstraint->pktable = NULL;
21190 fkconstraint->fk_attrs = NIL;
21191 fkconstraint->pk_attrs = NIL;
21192 fkconstraint->fk_matchtype = conform->confmatchtype;
21193 fkconstraint->fk_upd_action = conform->confupdtype;
21194 fkconstraint->fk_del_action = conform->confdeltype;
21195 fkconstraint->fk_del_set_cols = NIL;
21196 fkconstraint->old_conpfeqop = NIL;
21197 fkconstraint->old_pktable_oid = InvalidOid;
21198 fkconstraint->location = -1;
21199
21200 /* set up colnames, used to generate the constraint name */
21201 for (int i = 0; i < numfks; i++)
21202 {
21204
21205 att = TupleDescAttr(RelationGetDescr(partRel),
21206 conkey[i] - 1);
21207
21208 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21209 makeString(NameStr(att->attname)));
21210 }
21211
21213
21214 addFkRecurseReferenced(fkconstraint, partRel,
21215 refdRel,
21216 conform->conindid,
21217 fk->conoid,
21218 numfks,
21219 confkey,
21220 conkey,
21221 conpfeqop,
21222 conppeqop,
21223 conffeqop,
21224 numfkdelsetcols,
21225 confdelsetcols,
21226 true,
21228 conform->conperiod);
21229 table_close(refdRel, NoLock); /* keep lock till end of xact */
21230 }
21231
21232 ReleaseSysCache(contup);
21233 }
21234 list_free_deep(fks);
21235 if (trigrel)
21236 table_close(trigrel, RowExclusiveLock);
21237
21238 /*
21239 * Any sub-constraints that are in the referenced-side of a larger
21240 * constraint have to be removed. This partition is no longer part of the
21241 * key space of the constraint.
21242 */
21243 foreach(cell, GetParentedForeignKeyRefs(partRel))
21244 {
21245 Oid constrOid = lfirst_oid(cell);
21246 ObjectAddress constraint;
21247
21249 deleteDependencyRecordsForClass(ConstraintRelationId,
21250 constrOid,
21251 ConstraintRelationId,
21254
21255 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21256 performDeletion(&constraint, DROP_RESTRICT, 0);
21257 }
21258
21259 /* Now we can detach indexes */
21260 indexes = RelationGetIndexList(partRel);
21261 foreach(cell, indexes)
21262 {
21263 Oid idxid = lfirst_oid(cell);
21264 Oid parentidx;
21265 Relation idx;
21266 Oid constrOid;
21267 Oid parentConstrOid;
21268
21269 if (!has_superclass(idxid))
21270 continue;
21271
21272 parentidx = get_partition_parent(idxid, false);
21273 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21274
21277
21278 /*
21279 * If there's a constraint associated with the index, detach it too.
21280 * Careful: it is possible for a constraint index in a partition to be
21281 * the child of a non-constraint index, so verify whether the parent
21282 * index does actually have a constraint.
21283 */
21285 idxid);
21287 parentidx);
21288 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21290
21292 }
21293
21294 /* Update pg_class tuple */
21295 classRel = table_open(RelationRelationId, RowExclusiveLock);
21296 tuple = SearchSysCacheCopy1(RELOID,
21298 if (!HeapTupleIsValid(tuple))
21299 elog(ERROR, "cache lookup failed for relation %u",
21300 RelationGetRelid(partRel));
21301 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21302
21303 /* Clear relpartbound and reset relispartition */
21304 memset(new_val, 0, sizeof(new_val));
21305 memset(new_null, false, sizeof(new_null));
21306 memset(new_repl, false, sizeof(new_repl));
21307 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21308 new_null[Anum_pg_class_relpartbound - 1] = true;
21309 new_repl[Anum_pg_class_relpartbound - 1] = true;
21310 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21311 new_val, new_null, new_repl);
21312
21313 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21314 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21315 heap_freetuple(newtuple);
21316 table_close(classRel, RowExclusiveLock);
21317
21318 /*
21319 * Drop identity property from all identity columns of partition.
21320 */
21321 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21322 {
21323 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21324
21325 if (!attr->attisdropped && attr->attidentity)
21326 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21327 AccessExclusiveLock, true, true);
21328 }
21329
21330 if (OidIsValid(defaultPartOid))
21331 {
21332 /*
21333 * If the relation being detached is the default partition itself,
21334 * remove it from the parent's pg_partitioned_table entry.
21335 *
21336 * If not, we must invalidate default partition's relcache entry, as
21337 * in StorePartitionBound: its partition constraint depends on every
21338 * other partition's partition constraint.
21339 */
21340 if (RelationGetRelid(partRel) == defaultPartOid)
21342 else
21343 CacheInvalidateRelcacheByRelid(defaultPartOid);
21344 }
21345
21346 /*
21347 * Invalidate the parent's relcache so that the partition is no longer
21348 * included in its partition descriptor.
21349 */
21351
21352 /*
21353 * If the partition we just detached is partitioned itself, invalidate
21354 * relcache for all descendent partitions too to ensure that their
21355 * rd_partcheck expression trees are rebuilt; must lock partitions before
21356 * doing so, using the same lockmode as what partRel has been locked with
21357 * by the caller.
21358 */
21359 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21360 {
21361 List *children;
21362
21363 children = find_all_inheritors(RelationGetRelid(partRel),
21364 AccessExclusiveLock, NULL);
21365 foreach(cell, children)
21366 {
21368 }
21369 }
21370}
21371
21372/*
21373 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21374 *
21375 * To use when a DETACH PARTITION command previously did not run to
21376 * completion; this completes the detaching process.
21377 */
21378static ObjectAddress
21380{
21381 Relation partRel;
21382 ObjectAddress address;
21383 Snapshot snap = GetActiveSnapshot();
21384
21386
21387 /*
21388 * Wait until existing snapshots are gone. This is important if the
21389 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21390 * user could immediately run DETACH FINALIZE without actually waiting for
21391 * existing transactions. We must not complete the detach action until
21392 * all such queries are complete (otherwise we would present them with an
21393 * inconsistent view of catalogs).
21394 */
21395 WaitForOlderSnapshots(snap->xmin, false);
21396
21397 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21398
21399 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21400
21401 table_close(partRel, NoLock);
21402
21403 return address;
21404}
21405
21406/*
21407 * DetachAddConstraintIfNeeded
21408 * Subroutine for ATExecDetachPartition. Create a constraint that
21409 * takes the place of the partition constraint, but avoid creating
21410 * a dupe if a constraint already exists which implies the needed
21411 * constraint.
21412 */
21413static void
21415{
21416 List *constraintExpr;
21417
21418 constraintExpr = RelationGetPartitionQual(partRel);
21419 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
21420
21421 /*
21422 * Avoid adding a new constraint if the needed constraint is implied by an
21423 * existing constraint
21424 */
21425 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
21426 {
21427 AlteredTableInfo *tab;
21428 Constraint *n;
21429
21430 tab = ATGetQueueEntry(wqueue, partRel);
21431
21432 /* Add constraint on partition, equivalent to the partition constraint */
21433 n = makeNode(Constraint);
21434 n->contype = CONSTR_CHECK;
21435 n->conname = NULL;
21436 n->location = -1;
21437 n->is_no_inherit = false;
21438 n->raw_expr = NULL;
21439 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
21440 n->is_enforced = true;
21441 n->initially_valid = true;
21442 n->skip_validation = true;
21443 /* It's a re-add, since it nominally already exists */
21444 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
21445 true, false, true, ShareUpdateExclusiveLock);
21446 }
21447}
21448
21449/*
21450 * DropClonedTriggersFromPartition
21451 * subroutine for ATExecDetachPartition to remove any triggers that were
21452 * cloned to the partition when it was created-as-partition or attached.
21453 * This undoes what CloneRowTriggersToPartition did.
21454 */
21455static void
21457{
21458 ScanKeyData skey;
21459 SysScanDesc scan;
21460 HeapTuple trigtup;
21461 Relation tgrel;
21462 ObjectAddresses *objects;
21463
21464 objects = new_object_addresses();
21465
21466 /*
21467 * Scan pg_trigger to search for all triggers on this rel.
21468 */
21469 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21470 F_OIDEQ, ObjectIdGetDatum(partitionId));
21471 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21472 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21473 true, NULL, 1, &skey);
21474 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21475 {
21476 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21477 ObjectAddress trig;
21478
21479 /* Ignore triggers that weren't cloned */
21480 if (!OidIsValid(pg_trigger->tgparentid))
21481 continue;
21482
21483 /*
21484 * Ignore internal triggers that are implementation objects of foreign
21485 * keys, because these will be detached when the foreign keys
21486 * themselves are.
21487 */
21488 if (OidIsValid(pg_trigger->tgconstrrelid))
21489 continue;
21490
21491 /*
21492 * This is ugly, but necessary: remove the dependency markings on the
21493 * trigger so that it can be removed.
21494 */
21495 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21496 TriggerRelationId,
21498 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21499 RelationRelationId,
21501
21502 /* remember this trigger to remove it below */
21503 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21504 add_exact_object_address(&trig, objects);
21505 }
21506
21507 /* make the dependency removal visible to the deletion below */
21510
21511 /* done */
21512 free_object_addresses(objects);
21513 systable_endscan(scan);
21515}
21516
21517/*
21518 * Before acquiring lock on an index, acquire the same lock on the owning
21519 * table.
21520 */
21522{
21526};
21527
21528static void
21529RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21530 void *arg)
21531{
21533 Form_pg_class classform;
21534 HeapTuple tuple;
21535
21536 state = (struct AttachIndexCallbackState *) arg;
21537
21538 if (!state->lockedParentTbl)
21539 {
21540 LockRelationOid(state->parentTblOid, AccessShareLock);
21541 state->lockedParentTbl = true;
21542 }
21543
21544 /*
21545 * If we previously locked some other heap, and the name we're looking up
21546 * no longer refers to an index on that relation, release the now-useless
21547 * lock. XXX maybe we should do *after* we verify whether the index does
21548 * not actually belong to the same relation ...
21549 */
21550 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21551 {
21552 UnlockRelationOid(state->partitionOid, AccessShareLock);
21553 state->partitionOid = InvalidOid;
21554 }
21555
21556 /* Didn't find a relation, so no need for locking or permission checks. */
21557 if (!OidIsValid(relOid))
21558 return;
21559
21560 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21561 if (!HeapTupleIsValid(tuple))
21562 return; /* concurrently dropped, so nothing to do */
21563 classform = (Form_pg_class) GETSTRUCT(tuple);
21564 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21565 classform->relkind != RELKIND_INDEX)
21566 ereport(ERROR,
21567 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21568 errmsg("\"%s\" is not an index", rv->relname)));
21569 ReleaseSysCache(tuple);
21570
21571 /*
21572 * Since we need only examine the heap's tupledesc, an access share lock
21573 * on it (preventing any DDL) is sufficient.
21574 */
21575 state->partitionOid = IndexGetRelation(relOid, false);
21576 LockRelationOid(state->partitionOid, AccessShareLock);
21577}
21578
21579/*
21580 * ALTER INDEX i1 ATTACH PARTITION i2
21581 */
21582static ObjectAddress
21584{
21585 Relation partIdx;
21586 Relation partTbl;
21587 Relation parentTbl;
21588 ObjectAddress address;
21589 Oid partIdxId;
21590 Oid currParent;
21592
21593 /*
21594 * We need to obtain lock on the index 'name' to modify it, but we also
21595 * need to read its owning table's tuple descriptor -- so we need to lock
21596 * both. To avoid deadlocks, obtain lock on the table before doing so on
21597 * the index. Furthermore, we need to examine the parent table of the
21598 * partition, so lock that one too.
21599 */
21600 state.partitionOid = InvalidOid;
21601 state.parentTblOid = parentIdx->rd_index->indrelid;
21602 state.lockedParentTbl = false;
21603 partIdxId =
21606 &state);
21607 /* Not there? */
21608 if (!OidIsValid(partIdxId))
21609 ereport(ERROR,
21610 (errcode(ERRCODE_UNDEFINED_OBJECT),
21611 errmsg("index \"%s\" does not exist", name->relname)));
21612
21613 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21614 partIdx = relation_open(partIdxId, AccessExclusiveLock);
21615
21616 /* we already hold locks on both tables, so this is safe: */
21617 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21618 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21619
21620 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21621
21622 /* Silently do nothing if already in the right state */
21623 currParent = partIdx->rd_rel->relispartition ?
21624 get_partition_parent(partIdxId, false) : InvalidOid;
21625 if (currParent != RelationGetRelid(parentIdx))
21626 {
21627 IndexInfo *childInfo;
21628 IndexInfo *parentInfo;
21629 AttrMap *attmap;
21630 bool found;
21631 int i;
21632 PartitionDesc partDesc;
21633 Oid constraintOid,
21634 cldConstrId = InvalidOid;
21635
21636 /*
21637 * If this partition already has an index attached, refuse the
21638 * operation.
21639 */
21640 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21641
21642 if (OidIsValid(currParent))
21643 ereport(ERROR,
21644 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21645 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21646 RelationGetRelationName(partIdx),
21647 RelationGetRelationName(parentIdx)),
21648 errdetail("Index \"%s\" is already attached to another index.",
21649 RelationGetRelationName(partIdx))));
21650
21651 /* Make sure it indexes a partition of the other index's table */
21652 partDesc = RelationGetPartitionDesc(parentTbl, true);
21653 found = false;
21654 for (i = 0; i < partDesc->nparts; i++)
21655 {
21656 if (partDesc->oids[i] == state.partitionOid)
21657 {
21658 found = true;
21659 break;
21660 }
21661 }
21662 if (!found)
21663 ereport(ERROR,
21664 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21665 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21666 RelationGetRelationName(partIdx),
21667 RelationGetRelationName(parentIdx)),
21668 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21669 RelationGetRelationName(partIdx),
21670 RelationGetRelationName(parentTbl))));
21671
21672 /* Ensure the indexes are compatible */
21673 childInfo = BuildIndexInfo(partIdx);
21674 parentInfo = BuildIndexInfo(parentIdx);
21675 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21676 RelationGetDescr(parentTbl),
21677 false);
21678 if (!CompareIndexInfo(childInfo, parentInfo,
21679 partIdx->rd_indcollation,
21680 parentIdx->rd_indcollation,
21681 partIdx->rd_opfamily,
21682 parentIdx->rd_opfamily,
21683 attmap))
21684 ereport(ERROR,
21685 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21686 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21687 RelationGetRelationName(partIdx),
21688 RelationGetRelationName(parentIdx)),
21689 errdetail("The index definitions do not match.")));
21690
21691 /*
21692 * If there is a constraint in the parent, make sure there is one in
21693 * the child too.
21694 */
21695 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21696 RelationGetRelid(parentIdx));
21697
21698 if (OidIsValid(constraintOid))
21699 {
21701 partIdxId);
21702 if (!OidIsValid(cldConstrId))
21703 ereport(ERROR,
21704 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21705 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21706 RelationGetRelationName(partIdx),
21707 RelationGetRelationName(parentIdx)),
21708 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21709 RelationGetRelationName(parentIdx),
21710 RelationGetRelationName(parentTbl),
21711 RelationGetRelationName(partIdx))));
21712 }
21713
21714 /*
21715 * If it's a primary key, make sure the columns in the partition are
21716 * NOT NULL.
21717 */
21718 if (parentIdx->rd_index->indisprimary)
21719 verifyPartitionIndexNotNull(childInfo, partTbl);
21720
21721 /* All good -- do it */
21722 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21723 if (OidIsValid(constraintOid))
21724 ConstraintSetParentConstraint(cldConstrId, constraintOid,
21725 RelationGetRelid(partTbl));
21726
21727 free_attrmap(attmap);
21728
21729 validatePartitionedIndex(parentIdx, parentTbl);
21730 }
21731
21732 relation_close(parentTbl, AccessShareLock);
21733 /* keep these locks till commit */
21734 relation_close(partTbl, NoLock);
21735 relation_close(partIdx, NoLock);
21736
21737 return address;
21738}
21739
21740/*
21741 * Verify whether the given partition already contains an index attached
21742 * to the given partitioned index. If so, raise an error.
21743 */
21744static void
21745refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21746{
21747 Oid existingIdx;
21748
21749 existingIdx = index_get_partition(partitionTbl,
21750 RelationGetRelid(parentIdx));
21751 if (OidIsValid(existingIdx))
21752 ereport(ERROR,
21753 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21754 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21755 RelationGetRelationName(partIdx),
21756 RelationGetRelationName(parentIdx)),
21757 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21758 get_rel_name(existingIdx),
21759 RelationGetRelationName(partitionTbl))));
21760}
21761
21762/*
21763 * Verify whether the set of attached partition indexes to a parent index on
21764 * a partitioned table is complete. If it is, mark the parent index valid.
21765 *
21766 * This should be called each time a partition index is attached.
21767 */
21768static void
21770{
21771 Relation inheritsRel;
21772 SysScanDesc scan;
21774 int tuples = 0;
21775 HeapTuple inhTup;
21776 bool updated = false;
21777
21778 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21779
21780 /*
21781 * Scan pg_inherits for this parent index. Count each valid index we find
21782 * (verifying the pg_index entry for each), and if we reach the total
21783 * amount we expect, we can mark this parent index as valid.
21784 */
21785 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21786 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21787 BTEqualStrategyNumber, F_OIDEQ,
21789 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21790 NULL, 1, &key);
21791 while ((inhTup = systable_getnext(scan)) != NULL)
21792 {
21793 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21794 HeapTuple indTup;
21795 Form_pg_index indexForm;
21796
21797 indTup = SearchSysCache1(INDEXRELID,
21798 ObjectIdGetDatum(inhForm->inhrelid));
21799 if (!HeapTupleIsValid(indTup))
21800 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21801 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21802 if (indexForm->indisvalid)
21803 tuples += 1;
21804 ReleaseSysCache(indTup);
21805 }
21806
21807 /* Done with pg_inherits */
21808 systable_endscan(scan);
21809 table_close(inheritsRel, AccessShareLock);
21810
21811 /*
21812 * If we found as many inherited indexes as the partitioned table has
21813 * partitions, we're good; update pg_index to set indisvalid.
21814 */
21815 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21816 {
21817 Relation idxRel;
21818 HeapTuple indTup;
21819 Form_pg_index indexForm;
21820
21821 idxRel = table_open(IndexRelationId, RowExclusiveLock);
21822 indTup = SearchSysCacheCopy1(INDEXRELID,
21824 if (!HeapTupleIsValid(indTup))
21825 elog(ERROR, "cache lookup failed for index %u",
21826 RelationGetRelid(partedIdx));
21827 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21828
21829 indexForm->indisvalid = true;
21830 updated = true;
21831
21832 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21833
21835 heap_freetuple(indTup);
21836 }
21837
21838 /*
21839 * If this index is in turn a partition of a larger index, validating it
21840 * might cause the parent to become valid also. Try that.
21841 */
21842 if (updated && partedIdx->rd_rel->relispartition)
21843 {
21844 Oid parentIdxId,
21845 parentTblId;
21846 Relation parentIdx,
21847 parentTbl;
21848
21849 /* make sure we see the validation we just did */
21851
21852 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21853 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21854 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21855 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21856 Assert(!parentIdx->rd_index->indisvalid);
21857
21858 validatePartitionedIndex(parentIdx, parentTbl);
21859
21862 }
21863}
21864
21865/*
21866 * When attaching an index as a partition of a partitioned index which is a
21867 * primary key, verify that all the columns in the partition are marked NOT
21868 * NULL.
21869 */
21870static void
21872{
21873 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21874 {
21876 iinfo->ii_IndexAttrNumbers[i] - 1);
21877
21878 if (!att->attnotnull)
21879 ereport(ERROR,
21880 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21881 errmsg("invalid primary key definition"),
21882 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21883 NameStr(att->attname),
21884 RelationGetRelationName(partition)));
21885 }
21886}
21887
21888/*
21889 * Return an OID list of constraints that reference the given relation
21890 * that are marked as having a parent constraints.
21891 */
21892static List *
21894{
21895 Relation pg_constraint;
21896 HeapTuple tuple;
21897 SysScanDesc scan;
21898 ScanKeyData key[2];
21899 List *constraints = NIL;
21900
21901 /*
21902 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21903 * scan.
21904 */
21905 if (RelationGetIndexList(partition) == NIL ||
21908 return NIL;
21909
21910 /* Search for constraints referencing this table */
21911 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21912 ScanKeyInit(&key[0],
21913 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21914 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21915 ScanKeyInit(&key[1],
21916 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21917 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21918
21919 /* XXX This is a seqscan, as we don't have a usable index */
21920 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21921 while ((tuple = systable_getnext(scan)) != NULL)
21922 {
21923 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21924
21925 /*
21926 * We only need to process constraints that are part of larger ones.
21927 */
21928 if (!OidIsValid(constrForm->conparentid))
21929 continue;
21930
21931 constraints = lappend_oid(constraints, constrForm->oid);
21932 }
21933
21934 systable_endscan(scan);
21935 table_close(pg_constraint, AccessShareLock);
21936
21937 return constraints;
21938}
21939
21940/*
21941 * During DETACH PARTITION, verify that any foreign keys pointing to the
21942 * partitioned table would not become invalid. An error is raised if any
21943 * referenced values exist.
21944 */
21945static void
21947{
21948 List *constraints;
21949 ListCell *cell;
21950
21951 constraints = GetParentedForeignKeyRefs(partition);
21952
21953 foreach(cell, constraints)
21954 {
21955 Oid constrOid = lfirst_oid(cell);
21956 HeapTuple tuple;
21957 Form_pg_constraint constrForm;
21958 Relation rel;
21959 Trigger trig = {0};
21960
21961 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21962 if (!HeapTupleIsValid(tuple))
21963 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21964 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21965
21966 Assert(OidIsValid(constrForm->conparentid));
21967 Assert(constrForm->confrelid == RelationGetRelid(partition));
21968
21969 /* prevent data changes into the referencing table until commit */
21970 rel = table_open(constrForm->conrelid, ShareLock);
21971
21972 trig.tgoid = InvalidOid;
21973 trig.tgname = NameStr(constrForm->conname);
21975 trig.tgisinternal = true;
21976 trig.tgconstrrelid = RelationGetRelid(partition);
21977 trig.tgconstrindid = constrForm->conindid;
21978 trig.tgconstraint = constrForm->oid;
21979 trig.tgdeferrable = false;
21980 trig.tginitdeferred = false;
21981 /* we needn't fill in remaining fields */
21982
21983 RI_PartitionRemove_Check(&trig, rel, partition);
21984
21985 ReleaseSysCache(tuple);
21986
21987 table_close(rel, NoLock);
21988 }
21989}
21990
21991/*
21992 * resolve column compression specification to compression method.
21993 */
21994static char
21995GetAttributeCompression(Oid atttypid, const char *compression)
21996{
21997 char cmethod;
21998
21999 if (compression == NULL || strcmp(compression, "default") == 0)
22001
22002 /*
22003 * To specify a nondefault method, the column data type must be toastable.
22004 * Note this says nothing about whether the column's attstorage setting
22005 * permits compression; we intentionally allow attstorage and
22006 * attcompression to be independent. But with a non-toastable type,
22007 * attstorage could not be set to a value that would permit compression.
22008 *
22009 * We don't actually need to enforce this, since nothing bad would happen
22010 * if attcompression were non-default; it would never be consulted. But
22011 * it seems more user-friendly to complain about a certainly-useless
22012 * attempt to set the property.
22013 */
22014 if (!TypeIsToastable(atttypid))
22015 ereport(ERROR,
22016 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22017 errmsg("column data type %s does not support compression",
22018 format_type_be(atttypid))));
22019
22020 cmethod = CompressionNameToMethod(compression);
22021 if (!CompressionMethodIsValid(cmethod))
22022 ereport(ERROR,
22023 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22024 errmsg("invalid compression method \"%s\"", compression)));
22025
22026 return cmethod;
22027}
22028
22029/*
22030 * resolve column storage specification
22031 */
22032static char
22033GetAttributeStorage(Oid atttypid, const char *storagemode)
22034{
22035 char cstorage = 0;
22036
22037 if (pg_strcasecmp(storagemode, "plain") == 0)
22038 cstorage = TYPSTORAGE_PLAIN;
22039 else if (pg_strcasecmp(storagemode, "external") == 0)
22040 cstorage = TYPSTORAGE_EXTERNAL;
22041 else if (pg_strcasecmp(storagemode, "extended") == 0)
22042 cstorage = TYPSTORAGE_EXTENDED;
22043 else if (pg_strcasecmp(storagemode, "main") == 0)
22044 cstorage = TYPSTORAGE_MAIN;
22045 else if (pg_strcasecmp(storagemode, "default") == 0)
22046 cstorage = get_typstorage(atttypid);
22047 else
22048 ereport(ERROR,
22049 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22050 errmsg("invalid storage type \"%s\"",
22051 storagemode)));
22052
22053 /*
22054 * safety check: do not allow toasted storage modes unless column datatype
22055 * is TOAST-aware.
22056 */
22057 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22058 ereport(ERROR,
22059 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22060 errmsg("column data type %s can only have storage PLAIN",
22061 format_type_be(atttypid))));
22062
22063 return cstorage;
22064}
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:3361
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1820
void free_attrmap(AttrMap *map)
Definition: attmap.c:56
AttrMap * make_attrmap(int maplen)
Definition: attmap.c:40
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:175
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:261
int16 AttrNumber
Definition: attnum.h:21
#define InvalidAttrNumber
Definition: attnum.h:23
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1472
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
Definition: tablespace.c:1426
Oid GetDefaultTablespace(char relpersistence, bool partitioned)
Definition: tablespace.c:1143
List * raw_parser(const char *str, RawParseMode mode)
Definition: parser.c:42
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6414
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
#define bms_is_empty(a)
Definition: bitmapset.h:118
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4908
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define NameStr(name)
Definition: c.h:752
uint16 bits16
Definition: c.h:547
uint32 SubTransactionId
Definition: c.h:662
#define gettext_noop(x)
Definition: c.h:1196
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:223
#define InvalidSubTransactionId
Definition: c.h:664
int16_t int16
Definition: c.h:534
int32_t int32
Definition: c.h:535
#define MemSet(start, val, len)
Definition: c.h:1020
uint32 CommandId
Definition: c.h:672
#define PG_INT16_MAX
Definition: c.h:592
#define OidIsValid(objectId)
Definition: c.h:775
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:554
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:1161
int errdetail(const char *fmt,...)
Definition: elog.c:1207
int errhint(const char *fmt,...)
Definition: elog.c:1321
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define _(x)
Definition: elog.c:91
#define PG_TRY(...)
Definition: elog.h:372
#define WARNING
Definition: elog.h:36
#define PG_END_TRY(...)
Definition: elog.h:397
#define DEBUG1
Definition: elog.h:30
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define NOTICE
Definition: elog.h:35
#define PG_FINALLY(...)
Definition: elog.h:389
#define ereport(elevel,...)
Definition: elog.h: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:2084
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:653
#define ResetExprContext(econtext)
Definition: executor.h:647
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:658
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:390
#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
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, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
long val
Definition: informix.c:689
static struct @166 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:122
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:3754
Oid get_collation_oid(List *collname, bool missing_ok)
Definition: namespace.c:4038
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition: namespace.c:3526
RangeVar * makeRangeVarFromNameList(const List *names)
Definition: namespace.c:3621
Oid LookupNamespaceNoError(const char *nspname)
Definition: namespace.c:3422
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
char * nodeToString(const void *obj)
Definition: outfuncs.c:805
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid)
bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, CoercionContext ccontext)
Definition: parse_coerce.c:557
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
CoercionPathType
Definition: parse_coerce.h:25
@ COERCION_PATH_NONE
Definition: parse_coerce.h:26
@ COERCION_PATH_RELABELTYPE
Definition: parse_coerce.h:28
void assign_expr_collations(ParseState *pstate, Node *expr)
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:117
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:2544
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2817
#define ACL_MAINTAIN
Definition: parsenodes.h:90
#define ACL_USAGE
Definition: parsenodes.h:84
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2820
PartitionStrategy
Definition: parsenodes.h:897
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:900
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:898
ConstrType
Definition: parsenodes.h:2795
@ CONSTR_FOREIGN
Definition: parsenodes.h:2806
@ CONSTR_UNIQUE
Definition: parsenodes.h:2804
@ CONSTR_DEFAULT
Definition: parsenodes.h:2799
@ CONSTR_NOTNULL
Definition: parsenodes.h:2798
@ CONSTR_CHECK
Definition: parsenodes.h:2802
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2805
@ CONSTR_PRIMARY
Definition: parsenodes.h:2803
DropBehavior
Definition: parsenodes.h:2394
@ DROP_CASCADE
Definition: parsenodes.h:2396
@ DROP_RESTRICT
Definition: parsenodes.h:2395
ObjectType
Definition: parsenodes.h:2321
@ OBJECT_MATVIEW
Definition: parsenodes.h:2345
@ OBJECT_SCHEMA
Definition: parsenodes.h:2358
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2340
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2364
@ OBJECT_INDEX
Definition: parsenodes.h:2342
@ OBJECT_SEQUENCE
Definition: parsenodes.h:2359
@ OBJECT_TABLE
Definition: parsenodes.h:2363
@ OBJECT_VIEW
Definition: parsenodes.h:2373
@ OBJECT_TYPE
Definition: parsenodes.h:2371
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2362
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2335
AlterTableType
Definition: parsenodes.h:2413
@ AT_AddIndexConstraint
Definition: parsenodes.h:2435
@ AT_DropOf
Definition: parsenodes.h:2466
@ AT_SetOptions
Definition: parsenodes.h:2423
@ AT_DropIdentity
Definition: parsenodes.h:2478
@ AT_DisableTrigUser
Definition: parsenodes.h:2458
@ AT_DropNotNull
Definition: parsenodes.h:2418
@ AT_AddOf
Definition: parsenodes.h:2465
@ AT_ResetOptions
Definition: parsenodes.h:2424
@ AT_ReplicaIdentity
Definition: parsenodes.h:2467
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2450
@ AT_EnableRowSecurity
Definition: parsenodes.h:2468
@ AT_AddColumnToView
Definition: parsenodes.h:2415
@ AT_ResetRelOptions
Definition: parsenodes.h:2449
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2453
@ AT_DropOids
Definition: parsenodes.h:2445
@ AT_SetIdentity
Definition: parsenodes.h:2477
@ AT_ReAddStatistics
Definition: parsenodes.h:2479
@ AT_SetUnLogged
Definition: parsenodes.h:2444
@ AT_DisableTrig
Definition: parsenodes.h:2454
@ AT_SetCompression
Definition: parsenodes.h:2426
@ AT_DropExpression
Definition: parsenodes.h:2421
@ AT_AddIndex
Definition: parsenodes.h:2428
@ AT_EnableReplicaRule
Definition: parsenodes.h:2461
@ AT_ReAddIndex
Definition: parsenodes.h:2429
@ AT_DropConstraint
Definition: parsenodes.h:2436
@ AT_SetNotNull
Definition: parsenodes.h:2419
@ AT_ClusterOn
Definition: parsenodes.h:2441
@ AT_AddIdentity
Definition: parsenodes.h:2476
@ AT_ForceRowSecurity
Definition: parsenodes.h:2470
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2460
@ AT_SetAccessMethod
Definition: parsenodes.h:2446
@ AT_AlterColumnType
Definition: parsenodes.h:2438
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2475
@ AT_AddInherit
Definition: parsenodes.h:2463
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2432
@ AT_EnableTrig
Definition: parsenodes.h:2451
@ AT_DropColumn
Definition: parsenodes.h:2427
@ AT_ReAddComment
Definition: parsenodes.h:2437
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2439
@ AT_DisableTrigAll
Definition: parsenodes.h:2456
@ AT_EnableRule
Definition: parsenodes.h:2459
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2471
@ AT_DetachPartition
Definition: parsenodes.h:2474
@ AT_SetStatistics
Definition: parsenodes.h:2422
@ AT_AttachPartition
Definition: parsenodes.h:2473
@ AT_AddConstraint
Definition: parsenodes.h:2430
@ AT_DropInherit
Definition: parsenodes.h:2464
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2452
@ AT_SetLogged
Definition: parsenodes.h:2443
@ AT_SetStorage
Definition: parsenodes.h:2425
@ AT_DisableRule
Definition: parsenodes.h:2462
@ AT_DisableRowSecurity
Definition: parsenodes.h:2469
@ AT_SetRelOptions
Definition: parsenodes.h:2448
@ AT_ChangeOwner
Definition: parsenodes.h:2440
@ AT_EnableTrigUser
Definition: parsenodes.h:2457
@ AT_SetExpression
Definition: parsenodes.h:2420
@ AT_ReAddConstraint
Definition: parsenodes.h:2431
@ AT_SetTableSpace
Definition: parsenodes.h:2447
@ AT_GenericOptions
Definition: parsenodes.h:2472
@ AT_ColumnDefault
Definition: parsenodes.h:2416
@ AT_CookedColumnDefault
Definition: parsenodes.h:2417
@ AT_AlterConstraint
Definition: parsenodes.h:2433
@ AT_EnableTrigAll
Definition: parsenodes.h:2455
@ AT_DropCluster
Definition: parsenodes.h:2442
@ AT_ValidateConstraint
Definition: parsenodes.h:2434
@ AT_AddColumn
Definition: parsenodes.h:2414
#define ACL_REFERENCES
Definition: parsenodes.h:81
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2818
#define ACL_TRUNCATE
Definition: parsenodes.h:80
#define ACL_CREATE
Definition: parsenodes.h:85
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2819
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2816
@ 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:35
Oid GetAttrDefaultOid(Oid relid, AttrNumber attnum)
Definition: pg_attrdef.c:278
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid)
Definition: pg_attrdef.c:320
void RemoveAttrDefault(Oid relid, AttrNumber attnum, DropBehavior behavior, bool complain, bool internal)
Definition: pg_attrdef.c:152
NameData attname
Definition: pg_attribute.h:41
int16 attnum
Definition: pg_attribute.h:74
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
bool attnotnull
Definition: pg_attribute.h:123
void * arg
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
NameData relname
Definition: pg_class.h:38
FormData_pg_class * Form_pg_class
Definition: pg_class.h:156
#define PARTITION_MAX_KEYS
#define INDEX_MAX_KEYS
#define NAMEDATALEN
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isEnforced, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int16 conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
Definition: pg_constraint.c:51
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, const char *conname)
void FindFKPeriodOpers(Oid opclass, Oid *containedbyoperoid, Oid *aggedcontainedbyoperoid, Oid *intersectoperoid)
void RenameConstraintById(Oid conId, const char *newname)
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
void DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, AttrNumber *conkey, AttrNumber *confkey, Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs, int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)
void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType, ObjectAddresses *objsMoved)
List * RelationGetNotNullConstraints(Oid relid, bool cooked, bool include_noinh)
char * ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others)
Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
AttrNumber extractNotNullColumn(HeapTuple constrTup)
Oid get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
FormData_pg_constraint * Form_pg_constraint
@ CONSTRAINT_RELATION
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:45
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition: pg_depend.c:457
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:351
List * getOwnedSequences(Oid relid)
Definition: pg_depend.c:936
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:301
long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype, Oid refclassId, Oid refobjectId)
Definition: pg_depend.c:398
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:945
Oid get_index_constraint(Oid indexId)
Definition: pg_depend.c:988
bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
Definition: pg_depend.c:828
FormData_pg_depend * Form_pg_depend
Definition: pg_depend.h:72
FormData_pg_foreign_table * Form_pg_foreign_table
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
Definition: pg_inherits.c:552
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition: pg_inherits.c:58
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
Definition: pg_inherits.c:508
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:377
bool PartitionHasPendingDetach(Oid partoid)
Definition: pg_inherits.c:620
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h:45
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
#define lfirst_int(lc)
Definition: pg_list.h:173
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
#define list_make1_oid(x1)
Definition: pg_list.h:242
#define list_make1(x1)
Definition: pg_list.h:212
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469
#define for_each_from(cell, lst, N)
Definition: pg_list.h:414
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define linitial(l)
Definition: pg_list.h:178
#define list_make3(x1, x2, x3)
Definition: pg_list.h:216
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
#define foreach_oid(var, lst)
Definition: pg_list.h:471
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define linitial_oid(l)
Definition: pg_list.h:180
#define lfirst_oid(lc)
Definition: pg_list.h:174
#define list_make2(x1, x2)
Definition: pg_list.h:214
#define foreach_int(var, lst)
Definition: pg_list.h:470
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
List * GetRelationPublications(Oid relid)
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
Definition: pg_shdepend.c:316
void changeDependencyOnTablespace(Oid classId, Oid objectId, Oid newTablespaceId)
Definition: pg_shdepend.c:391
static char * buf
Definition: pg_test_fsync.c:72
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
void RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
Definition: pg_type.c: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:6719
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:755
@ IS_NOT_NULL
Definition: primnodes.h:1963
@ COERCION_ASSIGNMENT
Definition: primnodes.h:734
@ COERCION_IMPLICIT
Definition: primnodes.h:733
void * stringToNode(const char *str)
Definition: read.c:90
#define RelationGetForm(relation)
Definition: rel.h:508
#define RelationGetRelid(relation)
Definition: rel.h:514
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:710
#define RelationIsUsedAsCatalogTable(relation)
Definition: rel.h:397
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:576
#define RelationGetDescr(relation)
Definition: rel.h:540
#define RelationIsMapped(relation)
Definition: rel.h:563
#define RelationGetNumberOfAttributes(relation)
Definition: rel.h:520
#define RelationGetRelationName(relation)
Definition: rel.h:548
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:667
#define RelationGetNamespace(relation)
Definition: rel.h:555
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:533
#define RelationIsPermanent(relation)
Definition: rel.h:626
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
struct RelationData * Relation
Definition: relcache.h:27
@ 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:1351
bytea * view_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2034
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2090
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2020
Datum transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1167
LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList)
Definition: reloptions.c:2144
bytea * attribute_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2105
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
Definition: reloptions.c:2055
#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:13028
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)
Definition: statscmds.c:62
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:916
void RelationPreserveStorage(RelFileLocator rlocator, bool atCommit)
Definition: storage.c:252
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition: storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition: storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition: storage.c:187
void RelationDropStorage(Relation rel)
Definition: storage.c:207
#define InvalidStrategy
Definition: stratnum.h:24
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
RoleSpec * newowner
Definition: parsenodes.h:2490
DropBehavior behavior
Definition: parsenodes.h:2493
AlterTableType subtype
Definition: parsenodes.h:2485
RangeVar * relation
Definition: parsenodes.h:2406
ObjectType objtype
Definition: parsenodes.h:2408
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:757
char identity
Definition: parsenodes.h:763
RangeVar * identitySequence
Definition: parsenodes.h:764
List * constraints
Definition: parsenodes.h:769
Node * cooked_default
Definition: parsenodes.h:762
char * storage_name
Definition: parsenodes.h:760
char * colname
Definition: parsenodes.h:752
TypeName * typeName
Definition: parsenodes.h:753
char generated
Definition: parsenodes.h:766
bool is_from_type
Definition: parsenodes.h:758
Node * raw_default
Definition: parsenodes.h:761
char storage
Definition: parsenodes.h:759
bool is_local
Definition: parsenodes.h:756
int16 inhcount
Definition: parsenodes.h:755
char * compression
Definition: parsenodes.h:754
ParseLoc location
Definition: parsenodes.h:771
char * comment
Definition: parsenodes.h:3359
ObjectType objtype
Definition: parsenodes.h:3357
Node * object
Definition: parsenodes.h:3358
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:2833
ParseLoc location
Definition: parsenodes.h:2874
bool reset_default_tblspc
Definition: parsenodes.h:2855
List * keys
Definition: parsenodes.h:2845
List * pk_attrs
Definition: parsenodes.h:2863
List * fk_del_set_cols
Definition: parsenodes.h:2869
bool fk_with_period
Definition: parsenodes.h:2864
ConstrType contype
Definition: parsenodes.h:2830
Oid old_pktable_oid
Definition: parsenodes.h:2871
bool is_no_inherit
Definition: parsenodes.h:2837
char fk_upd_action
Definition: parsenodes.h:2867
List * old_conpfeqop
Definition: parsenodes.h:2870
bool is_enforced
Definition: parsenodes.h:2834
char fk_matchtype
Definition: parsenodes.h:2866
bool pk_with_period
Definition: parsenodes.h:2865
char * cooked_expr
Definition: parsenodes.h:2840
bool initially_valid
Definition: parsenodes.h:2836
bool skip_validation
Definition: parsenodes.h:2835
bool deferrable
Definition: parsenodes.h:2832
Node * raw_expr
Definition: parsenodes.h:2838
char * conname
Definition: parsenodes.h:2831
RangeVar * pktable
Definition: parsenodes.h:2861
char fk_del_action
Definition: parsenodes.h:2868
List * fk_attrs
Definition: parsenodes.h:2862
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:3118
List * transitionRels
Definition: parsenodes.h:3120
RangeVar * constrrel
Definition: parsenodes.h:3124
RangeVar * relation
Definition: parsenodes.h:3109
char * defname
Definition: parsenodes.h:841
bool missing_ok
Definition: parsenodes.h:3334
List * objects
Definition: parsenodes.h:3331
ObjectType removeType
Definition: parsenodes.h:3332
bool concurrent
Definition: parsenodes.h:3335
DropBehavior behavior
Definition: parsenodes.h:3333
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:769
List * args
Definition: primnodes.h:787
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:302
bool amcanunique
Definition: amapi.h:256
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:3508
char * idxname
Definition: parsenodes.h:3482
char * idxcomment
Definition: parsenodes.h:3492
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:1970
ParseLoc location
Definition: primnodes.h:1973
Expr * arg
Definition: primnodes.h:1969
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:973
RangeVar * name
Definition: parsenodes.h:972
List * collation
Definition: parsenodes.h:891
ParseLoc location
Definition: parsenodes.h:893
List * opclass
Definition: parsenodes.h:892
List * partParams
Definition: parsenodes.h:912
ParseLoc location
Definition: parsenodes.h:913
PartitionStrategy strategy
Definition: parsenodes.h:911
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:2086
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:130
AttrNumber tts_nvalid
Definition: tuptable.h:120
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
bool setof
Definition: parsenodes.h:286
List * arrayBounds
Definition: parsenodes.h:290
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:732
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:739
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:12966
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition: tablecmds.c:3837
void ResetRelRewrite(Oid myrelid)
Definition: tablecmds.c:4356
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
Definition: tablecmds.c:10614
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17466
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17833
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode)
Definition: tablecmds.c:15923
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5295
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:12731
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17229
ObjectAddress RenameRelation(RenameStmt *stmt)
Definition: tablecmds.c:4199
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6555
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition: tablecmds.c:9546
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:18324
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12166
static ColumnDef * MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3409
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:11006
#define ATT_TABLE
Definition: tablecmds.c:329
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
Definition: tablecmds.c:20070
static const char * storage_name(char c)
Definition: tablecmds.c:2455
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8770
void AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
Definition: tablecmds.c:19425
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition: tablecmds.c:7692
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8119
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
Definition: tablecmds.c:15596
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16821
static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
Definition: tablecmds.c:18130
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
Definition: tablecmds.c:11971
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17916
#define AT_NUM_PASSES
Definition: tablecmds.c:167
void PreCommit_on_commit_actions(void)
Definition: tablecmds.c:19286
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14045
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
Definition: tablecmds.c:11889
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8456
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:21995
static int findAttrByName(const char *attributeName, const List *columns)
Definition: tablecmds.c:3603
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15324
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
Definition: tablecmds.c:14647
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9838
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12876
void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4527
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15375
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
Definition: tablecmds.c:13604
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition: tablecmds.c:7710
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7735
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13453
#define ATT_SEQUENCE
Definition: tablecmds.c:336
void RemoveRelations(DropStmt *drop)
Definition: tablecmds.c:1529
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
Definition: tablecmds.c:16583
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6884
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15237
static List * GetParentedForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21893
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19020
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9764
ObjectAddress renameatt(RenameStmt *stmt)
Definition: tablecmds.c:4002
void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:16040
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:19552
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4442
static void DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
Definition: tablecmds.c:1502
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4601
struct ForeignTruncateInfo ForeignTruncateInfo
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:20866
static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
Definition: tablecmds.c:19142
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:3982
TupleDesc BuildDescForRelation(const List *columns)
Definition: tablecmds.c:1371
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:18600
void AtEOXact_on_commit_actions(bool isCommit)
Definition: tablecmds.c:19393
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:13633
struct OnCommitItem OnCommitItem
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4409
static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12583
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13825
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:17172
static void AttachPartitionForeignKey(List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11758
static List * MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
Definition: tablecmds.c:3160
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition: tablecmds.c:3788
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:21529
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:6854
static void truncate_check_activity(Relation rel)
Definition: tablecmds.c:2432
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:21769
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
Definition: tablecmds.c:12822
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:10035
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15854
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12100
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:10690
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15252
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:9019
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:12380
static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13188
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:9252
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11415
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9098
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4863
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3686
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:18570
void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName)
Definition: tablecmds.c:6929
static void truncate_check_perms(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2414
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2363
#define ATT_INDEX
Definition: tablecmds.c:332
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:16346
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3558
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
Definition: tablecmds.c:15011
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:8724
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12309
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
Definition: tablecmds.c:21871
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8204
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:4040
static void DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
Definition: tablecmds.c:1454
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
Definition: tablecmds.c:15404
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5704
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18983
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9880
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11214
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17604
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9224
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3640
static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3239
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6732
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6589
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:16613
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8339
ObjectAddress AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
Definition: tablecmds.c:18912
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15268
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:1852
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)
Definition: tablecmds.c:18368
#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:7186
static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, PartitionStrategy strategy)
Definition: tablecmds.c:19751
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:20709
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:20527
static void StoreCatalogInheritance(Oid relationId, List *supers, bool child_is_partition)
Definition: tablecmds.c:3514
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:18629
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15883
struct AlteredTableInfo AlteredTableInfo
Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:4468
static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12780
struct NewConstraint NewConstraint
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8874
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6809
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:21045
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15810
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11185
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:10868
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition: tablecmds.c:7639
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:7210
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:14694
static void validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, bool hasperiod)
Definition: tablecmds.c:13662
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17409
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13085
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3743
void remove_on_commit_action(Oid relid)
Definition: tablecmds.c:19263
static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
Definition: tablecmds.c:21414
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16447
#define ATT_PARTITIONED_INDEX
Definition: tablecmds.c:335
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17340
static const struct dropmsgstrings dropmsgstringarray[]
Definition: tablecmds.c:256
static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
Definition: tablecmds.c:18787
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8570
ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, ObjectAddress *typaddress, const char *queryString)
Definition: tablecmds.c:765
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13762
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
Definition: tablecmds.c:12662
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9467
static void DropClonedTriggersFromPartition(Oid partitionId)
Definition: tablecmds.c:21456
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:20133
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:18182
static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:1693
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4556
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12039
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7136
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17791
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21946
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9672
#define ATT_VIEW
Definition: tablecmds.c:330
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19097
#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:5369
static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12526
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:18456
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13960
static void ATPrepAddInherit(Relation child_rel)
Definition: tablecmds.c:17207
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:21379
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9161
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20206
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:7087
Oid AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
Definition: tablecmds.c:16953
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:13350
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8233
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:16493
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:13980
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9588
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:18710
static PartitionSpec * transformPartitionSpec(Relation rel, PartitionSpec *partspec)
Definition: tablecmds.c:19693
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4898
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:17115
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:7906
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4263
static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
Definition: tablecmds.c:7833
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:11662
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16914
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:14341
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
Definition: tablecmds.c:13295
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:16281
#define ATT_COMPOSITE_TYPE
Definition: tablecmds.c:333
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:21583
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:20015
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19460
static List * on_commits
Definition: tablecmds.c:132
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:17437
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9651
ObjectAddress RenameConstraint(RenameStmt *stmt)
Definition: tablecmds.c:4149
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5831
void register_on_commit_action(Oid relid, OnCommitAction action)
Definition: tablecmds.c:19227
static void RangeVarCallbackForTruncate(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19496
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:16415
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:22033
void RangeVarCallbackOwnsRelation(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19520
#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:21745
void ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
Definition: tablecmds.c:1976
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
Definition: tablecmds.c:6119
static List * MergeAttributes(List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
Definition: tablecmds.c:2540
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:17190
struct NewColumnValue NewColumnValue
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
Definition: tablecmds.c:16459
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:8082
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:458
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:372
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition: tuptable.h:385
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1921
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1488
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition: typecmds.c:3996
ObjectAddress AlterDomainAddConstraint(List *names, Node *newConstraint, ObjectAddress *constrAddr)
Definition: typecmds.c:2935
void checkDomainOwner(HeapTuple tup)
Definition: typecmds.c:3495
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4165
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:1950
#define MAX_STATISTICS_TARGET
Definition: vacuum.h:324
String * makeString(char *str)
Definition: value.c:63
#define intVal(v)
Definition: value.h:79
#define strVal(v)
Definition: value.h:82
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:296
static char * VARDATA_ANY(const void *PTR)
Definition: varatt.h:486
const char * name
SubTransactionId GetCurrentSubTransactionId(void)
Definition: xact.c:791
void CommandCounterIncrement(void)
Definition: xact.c:1100
void StartTransactionCommand(void)
Definition: xact.c:3071
void CommitTransactionCommand(void)
Definition: xact.c:3169
int MyXactFlags
Definition: xact.c:136
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:829
#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:474
void XLogRegisterData(const void *data, uint32 len)
Definition: xloginsert.c:364
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:456
void XLogBeginInsert(void)
Definition: xloginsert.c:149