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-2026, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/commands/tablecmds.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include "access/attmap.h"
18#include "access/genam.h"
19#include "access/gist.h"
20#include "access/heapam.h"
21#include "access/heapam_xlog.h"
22#include "access/multixact.h"
23#include "access/reloptions.h"
24#include "access/relscan.h"
25#include "access/sysattr.h"
26#include "access/tableam.h"
28#include "access/xact.h"
29#include "access/xlog.h"
30#include "access/xloginsert.h"
31#include "catalog/catalog.h"
32#include "catalog/heap.h"
33#include "catalog/index.h"
34#include "catalog/namespace.h"
36#include "catalog/partition.h"
37#include "catalog/pg_am.h"
38#include "catalog/pg_attrdef.h"
41#include "catalog/pg_depend.h"
43#include "catalog/pg_inherits.h"
47#include "catalog/pg_opclass.h"
48#include "catalog/pg_policy.h"
49#include "catalog/pg_proc.h"
51#include "catalog/pg_rewrite.h"
54#include "catalog/pg_trigger.h"
55#include "catalog/pg_type.h"
56#include "catalog/storage.h"
58#include "catalog/toasting.h"
59#include "commands/cluster.h"
60#include "commands/comment.h"
61#include "commands/defrem.h"
63#include "commands/sequence.h"
64#include "commands/tablecmds.h"
65#include "commands/tablespace.h"
66#include "commands/trigger.h"
67#include "commands/typecmds.h"
68#include "commands/user.h"
69#include "commands/vacuum.h"
70#include "common/int.h"
71#include "executor/executor.h"
72#include "foreign/fdwapi.h"
73#include "foreign/foreign.h"
74#include "miscadmin.h"
75#include "nodes/makefuncs.h"
76#include "nodes/nodeFuncs.h"
77#include "nodes/parsenodes.h"
78#include "optimizer/optimizer.h"
79#include "parser/parse_coerce.h"
81#include "parser/parse_expr.h"
83#include "parser/parse_type.h"
85#include "parser/parser.h"
88#include "pgstat.h"
92#include "storage/bufmgr.h"
93#include "storage/lmgr.h"
94#include "storage/lock.h"
95#include "storage/predicate.h"
96#include "storage/smgr.h"
97#include "tcop/utility.h"
98#include "utils/acl.h"
99#include "utils/builtins.h"
100#include "utils/fmgroids.h"
101#include "utils/inval.h"
102#include "utils/lsyscache.h"
103#include "utils/memutils.h"
104#include "utils/partcache.h"
105#include "utils/relcache.h"
106#include "utils/ruleutils.h"
107#include "utils/snapmgr.h"
108#include "utils/syscache.h"
109#include "utils/timestamp.h"
110#include "utils/typcache.h"
111#include "utils/usercontext.h"
112
113/*
114 * ON COMMIT action list
115 */
116typedef struct OnCommitItem
117{
118 Oid relid; /* relid of relation */
119 OnCommitAction oncommit; /* what to do at end of xact */
120
121 /*
122 * If this entry was created during the current transaction,
123 * creating_subid is the ID of the creating subxact; if created in a prior
124 * transaction, creating_subid is zero. If deleted during the current
125 * transaction, deleting_subid is the ID of the deleting subxact; if no
126 * deletion request is pending, deleting_subid is zero.
127 */
131
133
134
135/*
136 * State information for ALTER TABLE
137 *
138 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
139 * structs, one for each table modified by the operation (the named table
140 * plus any child tables that are affected). We save lists of subcommands
141 * to apply to this table (possibly modified by parse transformation steps);
142 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
143 * necessary information is stored in the constraints and newvals lists.
144 *
145 * Phase 2 is divided into multiple passes; subcommands are executed in
146 * a pass determined by subcommand type.
147 */
148
149typedef enum AlterTablePass
150{
151 AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
152 AT_PASS_DROP, /* DROP (all flavors) */
153 AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
154 AT_PASS_ADD_COL, /* ADD COLUMN */
155 AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
156 AT_PASS_OLD_INDEX, /* re-add existing indexes */
157 AT_PASS_OLD_CONSTR, /* re-add existing constraints */
158 /* We could support a RENAME COLUMN pass here, but not currently used */
159 AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
160 AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
161 AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
162 AT_PASS_ADD_INDEX, /* ADD indexes */
163 AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
164 AT_PASS_MISC, /* other stuff */
166
167#define AT_NUM_PASSES (AT_PASS_MISC + 1)
168
169typedef struct AlteredTableInfo
170{
171 /* Information saved before any work commences: */
172 Oid relid; /* Relation to work on */
173 char relkind; /* Its relkind */
174 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
175
176 /*
177 * Transiently set during Phase 2, normally set to NULL.
178 *
179 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
180 * returns control. This can be exploited by ATExecCmd subroutines to
181 * close/reopen across transaction boundaries.
182 */
184
185 /* Information saved by Phase 1 for Phase 2: */
186 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
187 /* Information saved by Phases 1/2 for Phase 3: */
188 List *constraints; /* List of NewConstraint */
189 List *newvals; /* List of NewColumnValue */
190 List *afterStmts; /* List of utility command parsetrees */
191 bool verify_new_notnull; /* T if we should recheck NOT NULL */
192 int rewrite; /* Reason for forced rewrite, if any */
193 bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
194 Oid newAccessMethod; /* new access method; 0 means no change,
195 * if above is true */
196 Oid newTableSpace; /* new tablespace; 0 means no change */
197 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
198 char newrelpersistence; /* if above is true */
199 Expr *partition_constraint; /* for attach partition validation */
200 /* true, if validating default due to some other attach/detach */
202 /* Objects to rebuild after completing ALTER TYPE operations */
203 List *changedConstraintOids; /* OIDs of constraints to rebuild */
204 List *changedConstraintDefs; /* string definitions of same */
205 List *changedIndexOids; /* OIDs of indexes to rebuild */
206 List *changedIndexDefs; /* string definitions of same */
207 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
208 char *clusterOnIndex; /* index to use for CLUSTER */
209 List *changedStatisticsOids; /* OIDs of statistics to rebuild */
210 List *changedStatisticsDefs; /* string definitions of same */
212
213/* Struct describing one new constraint to check in Phase 3 scan */
214/* Note: new not-null constraints are handled elsewhere */
215typedef struct NewConstraint
216{
217 char *name; /* Constraint name, or NULL if none */
218 ConstrType contype; /* CHECK or FOREIGN */
219 Oid refrelid; /* PK rel, if FOREIGN */
220 Oid refindid; /* OID of PK's index, if FOREIGN */
221 bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
222 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
223 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
224 ExprState *qualstate; /* Execution state for CHECK expr */
226
227/*
228 * Struct describing one new column value that needs to be computed during
229 * Phase 3 copy (this could be either a new column with a non-null default, or
230 * a column that we're changing the type of). Columns without such an entry
231 * are just copied from the old table during ATRewriteTable. Note that the
232 * expr is an expression over *old* table values, except when is_generated
233 * is true; then it is an expression over columns of the *new* tuple.
234 */
235typedef struct NewColumnValue
236{
237 AttrNumber attnum; /* which column */
238 Expr *expr; /* expression to compute */
239 ExprState *exprstate; /* execution state */
240 bool is_generated; /* is it a GENERATED expression? */
242
243/*
244 * Error-reporting support for RemoveRelations
245 */
247{
248 char kind;
250 const char *nonexistent_msg;
251 const char *skipping_msg;
252 const char *nota_msg;
253 const char *drophint_msg;
254};
255
256static const struct dropmsgstrings dropmsgstringarray[] = {
257 {RELKIND_RELATION,
259 gettext_noop("table \"%s\" does not exist"),
260 gettext_noop("table \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a table"),
262 gettext_noop("Use DROP TABLE to remove a table.")},
263 {RELKIND_SEQUENCE,
265 gettext_noop("sequence \"%s\" does not exist"),
266 gettext_noop("sequence \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not a sequence"),
268 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
269 {RELKIND_VIEW,
271 gettext_noop("view \"%s\" does not exist"),
272 gettext_noop("view \"%s\" does not exist, skipping"),
273 gettext_noop("\"%s\" is not a view"),
274 gettext_noop("Use DROP VIEW to remove a view.")},
275 {RELKIND_MATVIEW,
277 gettext_noop("materialized view \"%s\" does not exist"),
278 gettext_noop("materialized view \"%s\" does not exist, skipping"),
279 gettext_noop("\"%s\" is not a materialized view"),
280 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
281 {RELKIND_INDEX,
282 ERRCODE_UNDEFINED_OBJECT,
283 gettext_noop("index \"%s\" does not exist"),
284 gettext_noop("index \"%s\" does not exist, skipping"),
285 gettext_noop("\"%s\" is not an index"),
286 gettext_noop("Use DROP INDEX to remove an index.")},
287 {RELKIND_COMPOSITE_TYPE,
288 ERRCODE_UNDEFINED_OBJECT,
289 gettext_noop("type \"%s\" does not exist"),
290 gettext_noop("type \"%s\" does not exist, skipping"),
291 gettext_noop("\"%s\" is not a type"),
292 gettext_noop("Use DROP TYPE to remove a type.")},
293 {RELKIND_FOREIGN_TABLE,
294 ERRCODE_UNDEFINED_OBJECT,
295 gettext_noop("foreign table \"%s\" does not exist"),
296 gettext_noop("foreign table \"%s\" does not exist, skipping"),
297 gettext_noop("\"%s\" is not a foreign table"),
298 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
299 {RELKIND_PARTITIONED_TABLE,
301 gettext_noop("table \"%s\" does not exist"),
302 gettext_noop("table \"%s\" does not exist, skipping"),
303 gettext_noop("\"%s\" is not a table"),
304 gettext_noop("Use DROP TABLE to remove a table.")},
305 {RELKIND_PARTITIONED_INDEX,
306 ERRCODE_UNDEFINED_OBJECT,
307 gettext_noop("index \"%s\" does not exist"),
308 gettext_noop("index \"%s\" does not exist, skipping"),
309 gettext_noop("\"%s\" is not an index"),
310 gettext_noop("Use DROP INDEX to remove an index.")},
311 {'\0', 0, NULL, NULL, NULL, NULL}
312};
313
314/* communication between RemoveRelations and RangeVarCallbackForDropRelation */
316{
317 /* These fields are set by RemoveRelations: */
320 /* These fields are state to track which subsidiary locks are held: */
323 /* These fields are passed back by RangeVarCallbackForDropRelation: */
326};
327
328/* Alter table target-type flags for ATSimplePermissions */
329#define ATT_TABLE 0x0001
330#define ATT_VIEW 0x0002
331#define ATT_MATVIEW 0x0004
332#define ATT_INDEX 0x0008
333#define ATT_COMPOSITE_TYPE 0x0010
334#define ATT_FOREIGN_TABLE 0x0020
335#define ATT_PARTITIONED_INDEX 0x0040
336#define ATT_SEQUENCE 0x0080
337#define ATT_PARTITIONED_TABLE 0x0100
338
339/*
340 * ForeignTruncateInfo
341 *
342 * Information related to truncation of foreign tables. This is used for
343 * the elements in a hash table. It uses the server OID as lookup key,
344 * and includes a per-server list of all foreign tables involved in the
345 * truncation.
346 */
348{
352
353/* Partial or complete FK creation in addFkConstraint() */
355{
360
361/*
362 * Partition tables are expected to be dropped when the parent partitioned
363 * table gets dropped. Hence for partitioning we use AUTO dependency.
364 * Otherwise, for regular inheritance use NORMAL dependency.
365 */
366#define child_dependency_type(child_is_partition) \
367 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
368
369static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
370static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
371static void truncate_check_activity(Relation rel);
372static void RangeVarCallbackForTruncate(const RangeVar *relation,
373 Oid relId, Oid oldRelId, void *arg);
374static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
375 bool is_partition, List **supconstr,
376 List **supnotnulls);
377static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
378static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
379static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
380static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
381static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
382static void StoreCatalogInheritance(Oid relationId, List *supers,
383 bool child_is_partition);
384static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
385 int32 seqNumber, Relation inhRelation,
386 bool child_is_partition);
387static int findAttrByName(const char *attributeName, const List *columns);
388static void AlterIndexNamespaces(Relation classRel, Relation rel,
389 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
390static void AlterSeqNamespaces(Relation classRel, Relation rel,
391 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
392 LOCKMODE lockmode);
394 ATAlterConstraint *cmdcon,
395 bool recurse, LOCKMODE lockmode);
396static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
397 Relation tgrel, Relation rel, HeapTuple contuple,
398 bool recurse, LOCKMODE lockmode);
399static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
400 Relation conrel, Relation tgrel,
401 Oid fkrelid, Oid pkrelid,
402 HeapTuple contuple, LOCKMODE lockmode,
403 Oid ReferencedParentDelTrigger,
404 Oid ReferencedParentUpdTrigger,
405 Oid ReferencingParentInsTrigger,
406 Oid ReferencingParentUpdTrigger);
407static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
408 Relation conrel, Relation tgrel, Relation rel,
409 HeapTuple contuple, bool recurse,
410 List **otherrelids, LOCKMODE lockmode);
411static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
412 Relation conrel, Relation rel,
413 HeapTuple contuple, LOCKMODE lockmode);
414static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
415 bool deferrable, bool initdeferred,
416 List **otherrelids);
417static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
418 Relation conrel, Relation tgrel,
419 Oid fkrelid, Oid pkrelid,
420 HeapTuple contuple, LOCKMODE lockmode,
421 Oid ReferencedParentDelTrigger,
422 Oid ReferencedParentUpdTrigger,
423 Oid ReferencingParentInsTrigger,
424 Oid ReferencingParentUpdTrigger);
425static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
426 Relation conrel, Relation tgrel, Relation rel,
427 HeapTuple contuple, bool recurse,
428 List **otherrelids, LOCKMODE lockmode);
430 HeapTuple contuple);
432 Relation rel, char *constrName,
433 bool recurse, bool recursing, LOCKMODE lockmode);
434static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
435 Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
436static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
437 char *constrName, HeapTuple contuple,
438 bool recurse, bool recursing, LOCKMODE lockmode);
439static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
440 HeapTuple contuple, bool recurse, bool recursing,
441 LOCKMODE lockmode);
442static int transformColumnNameList(Oid relId, List *colList,
443 int16 *attnums, Oid *atttypids, Oid *attcollids);
444static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
445 List **attnamelist,
446 int16 *attnums, Oid *atttypids, Oid *attcollids,
447 Oid *opclasses, bool *pk_has_without_overlaps);
449 int numattrs, int16 *attnums,
450 bool with_period, Oid *opclasses,
451 bool *pk_has_without_overlaps);
452static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
453static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
454 Oid *funcid);
455static void validateForeignKeyConstraint(char *conname,
456 Relation rel, Relation pkrel,
457 Oid pkindOid, Oid constraintOid, bool hasperiod);
458static void CheckAlterTableIsSafe(Relation rel);
459static void ATController(AlterTableStmt *parsetree,
460 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
461 AlterTableUtilityContext *context);
462static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
463 bool recurse, bool recursing, LOCKMODE lockmode,
464 AlterTableUtilityContext *context);
465static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
466 AlterTableUtilityContext *context);
467static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
468 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
469 AlterTableUtilityContext *context);
471 Relation rel, AlterTableCmd *cmd,
472 bool recurse, LOCKMODE lockmode,
473 AlterTablePass cur_pass,
474 AlterTableUtilityContext *context);
475static void ATRewriteTables(AlterTableStmt *parsetree,
476 List **wqueue, LOCKMODE lockmode,
477 AlterTableUtilityContext *context);
478static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
479static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
480static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
481static void ATSimpleRecursion(List **wqueue, Relation rel,
482 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
483 AlterTableUtilityContext *context);
484static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
485static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
486 LOCKMODE lockmode,
487 AlterTableUtilityContext *context);
488static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
489 DropBehavior behavior);
490static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
491 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
492 AlterTableUtilityContext *context);
494 Relation rel, AlterTableCmd **cmd,
495 bool recurse, bool recursing,
496 LOCKMODE lockmode, AlterTablePass cur_pass,
497 AlterTableUtilityContext *context);
498static bool check_for_column_name_collision(Relation rel, const char *colname,
499 bool if_not_exists);
500static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
502static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
503 LOCKMODE lockmode);
504static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
505 bool is_valid, bool queue_validation);
506static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
507 char *conName, char *colName,
508 bool recurse, bool recursing,
509 LOCKMODE lockmode);
512 List *testConstraint, List *provenConstraint);
513static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
514 Node *newDefault, LOCKMODE lockmode);
516 Node *newDefault);
517static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
518 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
519static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
520 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
521static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
522 bool recurse, bool recursing);
523static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
524 Node *newExpr, LOCKMODE lockmode);
525static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
526static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
527static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
528 Node *newValue, LOCKMODE lockmode);
529static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
530 Node *options, bool isReset, LOCKMODE lockmode);
531static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
532 Node *newValue, LOCKMODE lockmode);
533static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
534 AlterTableCmd *cmd, LOCKMODE lockmode,
535 AlterTableUtilityContext *context);
536static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
537 DropBehavior behavior,
538 bool recurse, bool recursing,
539 bool missing_ok, LOCKMODE lockmode,
540 ObjectAddresses *addrs);
541static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
542 bool recurse, LOCKMODE lockmode,
543 AlterTableUtilityContext *context);
544static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
546 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
548 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
550 AlteredTableInfo *tab, Relation rel,
551 Constraint *newConstraint, bool recurse, bool is_readd,
552 LOCKMODE lockmode);
553static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
555 IndexStmt *stmt, LOCKMODE lockmode);
557 AlteredTableInfo *tab, Relation rel,
558 Constraint *constr,
559 bool recurse, bool recursing, bool is_readd,
560 LOCKMODE lockmode);
562 Relation rel, Constraint *fkconstraint,
563 bool recurse, bool recursing,
564 LOCKMODE lockmode);
565static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
566 int numfksetcols, int16 *fksetcolsattnums,
567 List *fksetcols);
569 char *constraintname,
570 Constraint *fkconstraint, Relation rel,
571 Relation pkrel, Oid indexOid,
572 Oid parentConstr,
573 int numfks, int16 *pkattnum, int16 *fkattnum,
574 Oid *pfeqoperators, Oid *ppeqoperators,
575 Oid *ffeqoperators, int numfkdelsetcols,
576 int16 *fkdelsetcols, bool is_internal,
577 bool with_period);
578static void addFkRecurseReferenced(Constraint *fkconstraint,
579 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
580 int numfks, int16 *pkattnum, int16 *fkattnum,
581 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
582 int numfkdelsetcols, int16 *fkdelsetcols,
583 bool old_check_ok,
584 Oid parentDelTrigger, Oid parentUpdTrigger,
585 bool with_period);
586static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
587 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
588 int numfks, int16 *pkattnum, int16 *fkattnum,
589 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
590 int numfkdelsetcols, int16 *fkdelsetcols,
591 bool old_check_ok, LOCKMODE lockmode,
592 Oid parentInsTrigger, Oid parentUpdTrigger,
593 bool with_period);
594static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
595 Relation partitionRel);
596static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
597static void CloneFkReferencing(List **wqueue, Relation parentRel,
598 Relation partRel);
599static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
600 Constraint *fkconstraint, Oid constraintOid,
601 Oid indexOid,
602 Oid parentInsTrigger, Oid parentUpdTrigger,
603 Oid *insertTrigOid, Oid *updateTrigOid);
604static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
605 Constraint *fkconstraint, Oid constraintOid,
606 Oid indexOid,
607 Oid parentDelTrigger, Oid parentUpdTrigger,
608 Oid *deleteTrigOid, Oid *updateTrigOid);
609static bool tryAttachPartitionForeignKey(List **wqueue,
611 Relation partition,
612 Oid parentConstrOid, int numfks,
613 AttrNumber *mapped_conkey, AttrNumber *confkey,
614 Oid *conpfeqop,
615 Oid parentInsTrigger,
616 Oid parentUpdTrigger,
617 Relation trigrel);
618static void AttachPartitionForeignKey(List **wqueue, Relation partition,
619 Oid partConstrOid, Oid parentConstrOid,
620 Oid parentInsTrigger, Oid parentUpdTrigger,
621 Relation trigrel);
622static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
623 Oid conoid, Oid conrelid);
624static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
625 Oid confrelid, Oid conrelid);
626static void GetForeignKeyActionTriggers(Relation trigrel,
627 Oid conoid, Oid confrelid, Oid conrelid,
628 Oid *deleteTriggerOid,
629 Oid *updateTriggerOid);
630static void GetForeignKeyCheckTriggers(Relation trigrel,
631 Oid conoid, Oid confrelid, Oid conrelid,
632 Oid *insertTriggerOid,
633 Oid *updateTriggerOid);
634static void ATExecDropConstraint(Relation rel, const char *constrName,
635 DropBehavior behavior, bool recurse,
636 bool missing_ok, LOCKMODE lockmode);
638 HeapTuple constraintTup, DropBehavior behavior,
639 bool recurse, bool recursing,
640 bool missing_ok, LOCKMODE lockmode);
641static void ATPrepAlterColumnType(List **wqueue,
642 AlteredTableInfo *tab, Relation rel,
643 bool recurse, bool recursing,
644 AlterTableCmd *cmd, LOCKMODE lockmode,
645 AlterTableUtilityContext *context);
646static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
648 AlterTableCmd *cmd, LOCKMODE lockmode);
650 Relation rel, AttrNumber attnum, const char *colName);
652static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
654static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
655 LOCKMODE lockmode);
656static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
657 char *cmd, List **wqueue, LOCKMODE lockmode,
658 bool rewrite);
660 Oid objid, Relation rel, List *domname,
661 const char *conname);
662static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
663static void TryReuseForeignKey(Oid oldId, Constraint *con);
664static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
665 List *options, LOCKMODE lockmode);
666static void change_owner_fix_column_acls(Oid relationOid,
667 Oid oldOwnerId, Oid newOwnerId);
668static void change_owner_recurse_to_sequences(Oid relationOid,
669 Oid newOwnerId, LOCKMODE lockmode);
670static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
671 LOCKMODE lockmode);
672static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
673static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
674static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
676 bool toLogged);
677static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
678 const char *tablespacename, LOCKMODE lockmode);
679static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
680static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
681static void ATExecSetRelOptions(Relation rel, List *defList,
682 AlterTableType operation,
683 LOCKMODE lockmode);
684static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
685 char fires_when, bool skip_system, bool recurse,
686 LOCKMODE lockmode);
687static void ATExecEnableDisableRule(Relation rel, const char *rulename,
688 char fires_when, LOCKMODE lockmode);
689static void ATPrepAddInherit(Relation child_rel);
690static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
691static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
692static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
693 DependencyType deptype);
694static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
695static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
697static void ATExecGenericOptions(Relation rel, List *options);
698static void ATExecSetRowSecurity(Relation rel, bool rls);
699static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
701 const char *column, Node *newValue, LOCKMODE lockmode);
702
703static void index_copy_data(Relation rel, RelFileLocator newrlocator);
704static const char *storage_name(char c);
705
706static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
707 Oid oldRelOid, void *arg);
708static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
709 Oid oldrelid, void *arg);
711static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
712 List **partexprs, Oid *partopclass, Oid *partcollation,
713 PartitionStrategy strategy);
714static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
715static void RemoveInheritance(Relation child_rel, Relation parent_rel,
716 bool expect_detached);
718 PartitionCmd *cmd,
719 AlterTableUtilityContext *context);
720static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
721static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
722 List *partConstraint,
723 bool validate_default);
724static void CloneRowTriggersToPartition(Relation parent, Relation partition);
725static void DropClonedTriggersFromPartition(Oid partitionId);
727 Relation rel, RangeVar *name,
728 bool concurrent);
729static void DetachPartitionFinalize(Relation rel, Relation partRel,
730 bool concurrent, Oid defaultPartOid);
732static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
733 RangeVar *name);
734static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
735static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
736 Relation partitionTbl);
737static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
738static List *GetParentedForeignKeyRefs(Relation partition);
739static void ATDetachCheckNoForeignKeyRefs(Relation partition);
740static char GetAttributeCompression(Oid atttypid, const char *compression);
741static char GetAttributeStorage(Oid atttypid, const char *storagemode);
742
743static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
745static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
746 Relation rel, PartitionCmd *cmd,
747 AlterTableUtilityContext *context);
748
749/* ----------------------------------------------------------------
750 * DefineRelation
751 * Creates a new relation.
752 *
753 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
754 * The other arguments are used to extend the behavior for other cases:
755 * relkind: relkind to assign to the new relation
756 * ownerId: if not InvalidOid, use this as the new relation's owner.
757 * typaddress: if not null, it's set to the pg_type entry's address.
758 * queryString: for error reporting
759 *
760 * Note that permissions checks are done against current user regardless of
761 * ownerId. A nonzero ownerId is used when someone is creating a relation
762 * "on behalf of" someone else, so we still want to see that the current user
763 * has permissions to do it.
764 *
765 * If successful, returns the address of the new relation.
766 * ----------------------------------------------------------------
767 */
769DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
770 ObjectAddress *typaddress, const char *queryString)
771{
772 char relname[NAMEDATALEN];
773 Oid namespaceId;
774 Oid relationId;
775 Oid tablespaceId;
776 Relation rel;
778 List *inheritOids;
779 List *old_constraints;
780 List *old_notnulls;
781 List *rawDefaults;
782 List *cookedDefaults;
783 List *nncols;
784 Datum reloptions;
785 ListCell *listptr;
787 bool partitioned;
788 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
789 Oid ofTypeId;
790 ObjectAddress address;
791 LOCKMODE parentLockmode;
792 Oid accessMethodId = InvalidOid;
793
794 /*
795 * Truncate relname to appropriate length (probably a waste of time, as
796 * parser should have done this already).
797 */
798 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
799
800 /*
801 * Check consistency of arguments
802 */
803 if (stmt->oncommit != ONCOMMIT_NOOP
804 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
806 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
807 errmsg("ON COMMIT can only be used on temporary tables")));
808
809 if (stmt->partspec != NULL)
810 {
811 if (relkind != RELKIND_RELATION)
812 elog(ERROR, "unexpected relkind: %d", (int) relkind);
813
814 relkind = RELKIND_PARTITIONED_TABLE;
815 partitioned = true;
816 }
817 else
818 partitioned = false;
819
820 if (relkind == RELKIND_PARTITIONED_TABLE &&
821 stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
823 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
824 errmsg("partitioned tables cannot be unlogged")));
825
826 /*
827 * Look up the namespace in which we are supposed to create the relation,
828 * check we have permission to create there, lock it against concurrent
829 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
830 * namespace is selected.
831 */
832 namespaceId =
834
835 /*
836 * Security check: disallow creating temp tables from security-restricted
837 * code. This is needed because calling code might not expect untrusted
838 * tables to appear in pg_temp at the front of its search path.
839 */
840 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
843 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
844 errmsg("cannot create temporary table within security-restricted operation")));
845
846 /*
847 * Determine the lockmode to use when scanning parents. A self-exclusive
848 * lock is needed here.
849 *
850 * For regular inheritance, if two backends attempt to add children to the
851 * same parent simultaneously, and that parent has no pre-existing
852 * children, then both will attempt to update the parent's relhassubclass
853 * field, leading to a "tuple concurrently updated" error. Also, this
854 * interlocks against a concurrent ANALYZE on the parent table, which
855 * might otherwise be attempting to clear the parent's relhassubclass
856 * field, if its previous children were recently dropped.
857 *
858 * If the child table is a partition, then we instead grab an exclusive
859 * lock on the parent because its partition descriptor will be changed by
860 * addition of the new partition.
861 */
862 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
864
865 /* Determine the list of OIDs of the parents. */
866 inheritOids = NIL;
867 foreach(listptr, stmt->inhRelations)
868 {
869 RangeVar *rv = (RangeVar *) lfirst(listptr);
870 Oid parentOid;
871
872 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
873
874 /*
875 * Reject duplications in the list of parents.
876 */
877 if (list_member_oid(inheritOids, parentOid))
879 (errcode(ERRCODE_DUPLICATE_TABLE),
880 errmsg("relation \"%s\" would be inherited from more than once",
881 get_rel_name(parentOid))));
882
883 inheritOids = lappend_oid(inheritOids, parentOid);
884 }
885
886 /*
887 * Select tablespace to use: an explicitly indicated one, or (in the case
888 * of a partitioned table) the parent's, if it has one.
889 */
890 if (stmt->tablespacename)
891 {
892 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
893
894 if (partitioned && tablespaceId == MyDatabaseTableSpace)
896 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
897 errmsg("cannot specify default tablespace for partitioned relations")));
898 }
899 else if (stmt->partbound)
900 {
901 Assert(list_length(inheritOids) == 1);
902 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
903 }
904 else
905 tablespaceId = InvalidOid;
906
907 /* still nothing? use the default */
908 if (!OidIsValid(tablespaceId))
909 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
910 partitioned);
911
912 /* Check permissions except when using database's default */
913 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
914 {
915 AclResult aclresult;
916
917 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
918 ACL_CREATE);
919 if (aclresult != ACLCHECK_OK)
921 get_tablespace_name(tablespaceId));
922 }
923
924 /* In all cases disallow placing user relations in pg_global */
925 if (tablespaceId == GLOBALTABLESPACE_OID)
927 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
928 errmsg("only shared relations can be placed in pg_global tablespace")));
929
930 /* Identify user ID that will own the table */
931 if (!OidIsValid(ownerId))
932 ownerId = GetUserId();
933
934 /*
935 * Parse and validate reloptions, if any.
936 */
937 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
938 true, false);
939
940 switch (relkind)
941 {
942 case RELKIND_VIEW:
943 (void) view_reloptions(reloptions, true);
944 break;
945 case RELKIND_PARTITIONED_TABLE:
946 (void) partitioned_table_reloptions(reloptions, true);
947 break;
948 default:
949 (void) heap_reloptions(relkind, reloptions, true);
950 }
951
952 if (stmt->ofTypename)
953 {
954 AclResult aclresult;
955
956 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
957
958 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
959 if (aclresult != ACLCHECK_OK)
960 aclcheck_error_type(aclresult, ofTypeId);
961 }
962 else
963 ofTypeId = InvalidOid;
964
965 /*
966 * Look up inheritance ancestors and generate relation schema, including
967 * inherited attributes. (Note that stmt->tableElts is destructively
968 * modified by MergeAttributes.)
969 */
970 stmt->tableElts =
971 MergeAttributes(stmt->tableElts, inheritOids,
972 stmt->relation->relpersistence,
973 stmt->partbound != NULL,
974 &old_constraints, &old_notnulls);
975
976 /*
977 * Create a tuple descriptor from the relation schema. Note that this
978 * deals with column names, types, and in-descriptor NOT NULL flags, but
979 * not default values, NOT NULL or CHECK constraints; we handle those
980 * below.
981 */
983
984 /*
985 * Find columns with default values and prepare for insertion of the
986 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
987 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
988 * while raw defaults go into a list of RawColumnDefault structs that will
989 * be processed by AddRelationNewConstraints. (We can't deal with raw
990 * expressions until we can do transformExpr.)
991 */
992 rawDefaults = NIL;
993 cookedDefaults = NIL;
994 attnum = 0;
995
996 foreach(listptr, stmt->tableElts)
997 {
998 ColumnDef *colDef = lfirst(listptr);
999
1000 attnum++;
1001 if (colDef->raw_default != NULL)
1002 {
1003 RawColumnDefault *rawEnt;
1004
1005 Assert(colDef->cooked_default == NULL);
1006
1008 rawEnt->attnum = attnum;
1009 rawEnt->raw_default = colDef->raw_default;
1010 rawEnt->generated = colDef->generated;
1011 rawDefaults = lappend(rawDefaults, rawEnt);
1012 }
1013 else if (colDef->cooked_default != NULL)
1014 {
1015 CookedConstraint *cooked;
1016
1018 cooked->contype = CONSTR_DEFAULT;
1019 cooked->conoid = InvalidOid; /* until created */
1020 cooked->name = NULL;
1021 cooked->attnum = attnum;
1022 cooked->expr = colDef->cooked_default;
1023 cooked->is_enforced = true;
1024 cooked->skip_validation = false;
1025 cooked->is_local = true; /* not used for defaults */
1026 cooked->inhcount = 0; /* ditto */
1027 cooked->is_no_inherit = false;
1028 cookedDefaults = lappend(cookedDefaults, cooked);
1029 }
1030 }
1031
1032 /*
1033 * For relations with table AM and partitioned tables, select access
1034 * method to use: an explicitly indicated one, or (in the case of a
1035 * partitioned table) the parent's, if it has one.
1036 */
1037 if (stmt->accessMethod != NULL)
1038 {
1039 Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1040 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1041 }
1042 else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1043 {
1044 if (stmt->partbound)
1045 {
1046 Assert(list_length(inheritOids) == 1);
1047 accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1048 }
1049
1050 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1051 accessMethodId = get_table_am_oid(default_table_access_method, false);
1052 }
1053
1054 /*
1055 * Create the relation. Inherited defaults and CHECK constraints are
1056 * passed in for immediate handling --- since they don't need parsing,
1057 * they can be stored immediately.
1058 */
1059 relationId = heap_create_with_catalog(relname,
1060 namespaceId,
1061 tablespaceId,
1062 InvalidOid,
1063 InvalidOid,
1064 ofTypeId,
1065 ownerId,
1066 accessMethodId,
1067 descriptor,
1068 list_concat(cookedDefaults,
1069 old_constraints),
1070 relkind,
1071 stmt->relation->relpersistence,
1072 false,
1073 false,
1074 stmt->oncommit,
1075 reloptions,
1076 true,
1078 false,
1079 InvalidOid,
1080 typaddress);
1081
1082 /*
1083 * We must bump the command counter to make the newly-created relation
1084 * tuple visible for opening.
1085 */
1087
1088 /*
1089 * Open the new relation and acquire exclusive lock on it. This isn't
1090 * really necessary for locking out other backends (since they can't see
1091 * the new rel anyway until we commit), but it keeps the lock manager from
1092 * complaining about deadlock risks.
1093 */
1094 rel = relation_open(relationId, AccessExclusiveLock);
1095
1096 /*
1097 * Now add any newly specified column default and generation expressions
1098 * to the new relation. These are passed to us in the form of raw
1099 * parsetrees; we need to transform them to executable expression trees
1100 * before they can be added. The most convenient way to do that is to
1101 * apply the parser's transformExpr routine, but transformExpr doesn't
1102 * work unless we have a pre-existing relation. So, the transformation has
1103 * to be postponed to this final step of CREATE TABLE.
1104 *
1105 * This needs to be before processing the partitioning clauses because
1106 * those could refer to generated columns.
1107 */
1108 if (rawDefaults)
1109 AddRelationNewConstraints(rel, rawDefaults, NIL,
1110 true, true, false, queryString);
1111
1112 /*
1113 * Make column generation expressions visible for use by partitioning.
1114 */
1116
1117 /* Process and store partition bound, if any. */
1118 if (stmt->partbound)
1119 {
1120 PartitionBoundSpec *bound;
1121 ParseState *pstate;
1122 Oid parentId = linitial_oid(inheritOids),
1123 defaultPartOid;
1124 Relation parent,
1125 defaultRel = NULL;
1126 ParseNamespaceItem *nsitem;
1127
1128 /* Already have strong enough lock on the parent */
1129 parent = table_open(parentId, NoLock);
1130
1131 /*
1132 * We are going to try to validate the partition bound specification
1133 * against the partition key of parentRel, so it better have one.
1134 */
1135 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1136 ereport(ERROR,
1137 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1138 errmsg("\"%s\" is not partitioned",
1139 RelationGetRelationName(parent))));
1140
1141 /*
1142 * The partition constraint of the default partition depends on the
1143 * partition bounds of every other partition. It is possible that
1144 * another backend might be about to execute a query on the default
1145 * partition table, and that the query relies on previously cached
1146 * default partition constraints. We must therefore take a table lock
1147 * strong enough to prevent all queries on the default partition from
1148 * proceeding until we commit and send out a shared-cache-inval notice
1149 * that will make them update their index lists.
1150 *
1151 * Order of locking: The relation being added won't be visible to
1152 * other backends until it is committed, hence here in
1153 * DefineRelation() the order of locking the default partition and the
1154 * relation being added does not matter. But at all other places we
1155 * need to lock the default relation before we lock the relation being
1156 * added or removed i.e. we should take the lock in same order at all
1157 * the places such that lock parent, lock default partition and then
1158 * lock the partition so as to avoid a deadlock.
1159 */
1160 defaultPartOid =
1162 true));
1163 if (OidIsValid(defaultPartOid))
1164 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1165
1166 /* Transform the bound values */
1167 pstate = make_parsestate(NULL);
1168 pstate->p_sourcetext = queryString;
1169
1170 /*
1171 * Add an nsitem containing this relation, so that transformExpr
1172 * called on partition bound expressions is able to report errors
1173 * using a proper context.
1174 */
1175 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1176 NULL, false, false);
1177 addNSItemToQuery(pstate, nsitem, false, true, true);
1178
1179 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1180
1181 /*
1182 * Check first that the new partition's bound is valid and does not
1183 * overlap with any of existing partitions of the parent.
1184 */
1185 check_new_partition_bound(relname, parent, bound, pstate);
1186
1187 /*
1188 * If the default partition exists, its partition constraints will
1189 * change after the addition of this new partition such that it won't
1190 * allow any row that qualifies for this new partition. So, check that
1191 * the existing data in the default partition satisfies the constraint
1192 * as it will exist after adding this partition.
1193 */
1194 if (OidIsValid(defaultPartOid))
1195 {
1196 check_default_partition_contents(parent, defaultRel, bound);
1197 /* Keep the lock until commit. */
1198 table_close(defaultRel, NoLock);
1199 }
1200
1201 /* Update the pg_class entry. */
1202 StorePartitionBound(rel, parent, bound);
1203
1204 table_close(parent, NoLock);
1205 }
1206
1207 /* Store inheritance information for new rel. */
1208 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1209
1210 /*
1211 * Process the partitioning specification (if any) and store the partition
1212 * key information into the catalog.
1213 */
1214 if (partitioned)
1215 {
1216 ParseState *pstate;
1217 int partnatts;
1218 AttrNumber partattrs[PARTITION_MAX_KEYS];
1219 Oid partopclass[PARTITION_MAX_KEYS];
1220 Oid partcollation[PARTITION_MAX_KEYS];
1221 List *partexprs = NIL;
1222
1223 pstate = make_parsestate(NULL);
1224 pstate->p_sourcetext = queryString;
1225
1226 partnatts = list_length(stmt->partspec->partParams);
1227
1228 /* Protect fixed-size arrays here and in executor */
1229 if (partnatts > PARTITION_MAX_KEYS)
1230 ereport(ERROR,
1231 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1232 errmsg("cannot partition using more than %d columns",
1234
1235 /*
1236 * We need to transform the raw parsetrees corresponding to partition
1237 * expressions into executable expression trees. Like column defaults
1238 * and CHECK constraints, we could not have done the transformation
1239 * earlier.
1240 */
1241 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1242
1243 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1244 partattrs, &partexprs, partopclass,
1245 partcollation, stmt->partspec->strategy);
1246
1247 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1248 partexprs,
1249 partopclass, partcollation);
1250
1251 /* make it all visible */
1253 }
1254
1255 /*
1256 * If we're creating a partition, create now all the indexes, triggers,
1257 * FKs defined in the parent.
1258 *
1259 * We can't do it earlier, because DefineIndex wants to know the partition
1260 * key which we just stored.
1261 */
1262 if (stmt->partbound)
1263 {
1264 Oid parentId = linitial_oid(inheritOids);
1265 Relation parent;
1266 List *idxlist;
1267 ListCell *cell;
1268
1269 /* Already have strong enough lock on the parent */
1270 parent = table_open(parentId, NoLock);
1271 idxlist = RelationGetIndexList(parent);
1272
1273 /*
1274 * For each index in the parent table, create one in the partition
1275 */
1276 foreach(cell, idxlist)
1277 {
1279 AttrMap *attmap;
1280 IndexStmt *idxstmt;
1281 Oid constraintOid;
1282
1283 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1284 {
1285 if (idxRel->rd_index->indisunique)
1286 ereport(ERROR,
1287 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1288 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1289 RelationGetRelationName(parent)),
1290 errdetail("Table \"%s\" contains indexes that are unique.",
1291 RelationGetRelationName(parent))));
1292 else
1293 {
1295 continue;
1296 }
1297 }
1298
1300 RelationGetDescr(parent),
1301 false);
1302 idxstmt =
1303 generateClonedIndexStmt(NULL, idxRel,
1304 attmap, &constraintOid);
1305 DefineIndex(NULL,
1306 RelationGetRelid(rel),
1307 idxstmt,
1308 InvalidOid,
1309 RelationGetRelid(idxRel),
1310 constraintOid,
1311 -1,
1312 false, false, false, false, false);
1313
1315 }
1316
1317 list_free(idxlist);
1318
1319 /*
1320 * If there are any row-level triggers, clone them to the new
1321 * partition.
1322 */
1323 if (parent->trigdesc != NULL)
1324 CloneRowTriggersToPartition(parent, rel);
1325
1326 /*
1327 * And foreign keys too. Note that because we're freshly creating the
1328 * table, there is no need to verify these new constraints.
1329 */
1330 CloneForeignKeyConstraints(NULL, parent, rel);
1331
1332 table_close(parent, NoLock);
1333 }
1334
1335 /*
1336 * Now add any newly specified CHECK constraints to the new relation. Same
1337 * as for defaults above, but these need to come after partitioning is set
1338 * up.
1339 */
1340 if (stmt->constraints)
1341 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1342 true, true, false, queryString);
1343
1344 /*
1345 * Finally, merge the not-null constraints that are declared directly with
1346 * those that come from parent relations (making sure to count inheritance
1347 * appropriately for each), create them, and set the attnotnull flag on
1348 * columns that don't yet have it.
1349 */
1350 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1351 old_notnulls);
1352 foreach_int(attrnum, nncols)
1353 set_attnotnull(NULL, rel, attrnum, true, false);
1354
1355 ObjectAddressSet(address, RelationRelationId, relationId);
1356
1357 /*
1358 * Clean up. We keep lock on new relation (although it shouldn't be
1359 * visible to anyone else anyway, until commit).
1360 */
1361 relation_close(rel, NoLock);
1362
1363 return address;
1364}
1365
1366/*
1367 * BuildDescForRelation
1368 *
1369 * Given a list of ColumnDef nodes, build a TupleDesc.
1370 *
1371 * Note: This is only for the limited purpose of table and view creation. Not
1372 * everything is filled in. A real tuple descriptor should be obtained from
1373 * the relcache.
1374 */
1377{
1378 int natts;
1380 ListCell *l;
1381 TupleDesc desc;
1382 char *attname;
1383 Oid atttypid;
1384 int32 atttypmod;
1385 Oid attcollation;
1386 int attdim;
1387
1388 /*
1389 * allocate a new tuple descriptor
1390 */
1391 natts = list_length(columns);
1392 desc = CreateTemplateTupleDesc(natts);
1393
1394 attnum = 0;
1395
1396 foreach(l, columns)
1397 {
1398 ColumnDef *entry = lfirst(l);
1399 AclResult aclresult;
1401
1402 /*
1403 * for each entry in the list, get the name and type information from
1404 * the list and have TupleDescInitEntry fill in the attribute
1405 * information we need.
1406 */
1407 attnum++;
1408
1409 attname = entry->colname;
1410 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1411
1412 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1413 if (aclresult != ACLCHECK_OK)
1414 aclcheck_error_type(aclresult, atttypid);
1415
1416 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1417 attdim = list_length(entry->typeName->arrayBounds);
1418 if (attdim > PG_INT16_MAX)
1419 ereport(ERROR,
1420 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1421 errmsg("too many array dimensions"));
1422
1423 if (entry->typeName->setof)
1424 ereport(ERROR,
1425 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1426 errmsg("column \"%s\" cannot be declared SETOF",
1427 attname)));
1428
1430 atttypid, atttypmod, attdim);
1431 att = TupleDescAttr(desc, attnum - 1);
1432
1433 /* Override TupleDescInitEntry's settings as requested */
1434 TupleDescInitEntryCollation(desc, attnum, attcollation);
1435
1436 /* Fill in additional stuff not handled by TupleDescInitEntry */
1437 att->attnotnull = entry->is_not_null;
1438 att->attislocal = entry->is_local;
1439 att->attinhcount = entry->inhcount;
1440 att->attidentity = entry->identity;
1441 att->attgenerated = entry->generated;
1442 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1443 if (entry->storage)
1444 att->attstorage = entry->storage;
1445 else if (entry->storage_name)
1446 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1447
1449 }
1450
1451 return desc;
1452}
1453
1454/*
1455 * Emit the right error or warning message for a "DROP" command issued on a
1456 * non-existent relation
1457 */
1458static void
1459DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1460{
1461 const struct dropmsgstrings *rentry;
1462
1463 if (rel->schemaname != NULL &&
1465 {
1466 if (!missing_ok)
1467 {
1468 ereport(ERROR,
1469 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1470 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1471 }
1472 else
1473 {
1475 (errmsg("schema \"%s\" does not exist, skipping",
1476 rel->schemaname)));
1477 }
1478 return;
1479 }
1480
1481 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1482 {
1483 if (rentry->kind == rightkind)
1484 {
1485 if (!missing_ok)
1486 {
1487 ereport(ERROR,
1488 (errcode(rentry->nonexistent_code),
1489 errmsg(rentry->nonexistent_msg, rel->relname)));
1490 }
1491 else
1492 {
1493 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1494 break;
1495 }
1496 }
1497 }
1498
1499 Assert(rentry->kind != '\0'); /* Should be impossible */
1500}
1501
1502/*
1503 * Emit the right error message for a "DROP" command issued on a
1504 * relation of the wrong type
1505 */
1506static void
1507DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1508{
1509 const struct dropmsgstrings *rentry;
1510 const struct dropmsgstrings *wentry;
1511
1512 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1513 if (rentry->kind == rightkind)
1514 break;
1515 Assert(rentry->kind != '\0');
1516
1517 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1518 if (wentry->kind == wrongkind)
1519 break;
1520 /* wrongkind could be something we don't have in our table... */
1521
1522 ereport(ERROR,
1523 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1524 errmsg(rentry->nota_msg, relname),
1525 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1526}
1527
1528/*
1529 * RemoveRelations
1530 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1531 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1532 */
1533void
1535{
1536 ObjectAddresses *objects;
1537 char relkind;
1538 ListCell *cell;
1539 int flags = 0;
1540 LOCKMODE lockmode = AccessExclusiveLock;
1541
1542 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1543 if (drop->concurrent)
1544 {
1545 /*
1546 * Note that for temporary relations this lock may get upgraded later
1547 * on, but as no other session can access a temporary relation, this
1548 * is actually fine.
1549 */
1550 lockmode = ShareUpdateExclusiveLock;
1551 Assert(drop->removeType == OBJECT_INDEX);
1552 if (list_length(drop->objects) != 1)
1553 ereport(ERROR,
1554 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1555 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1556 if (drop->behavior == DROP_CASCADE)
1557 ereport(ERROR,
1558 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1559 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1560 }
1561
1562 /*
1563 * First we identify all the relations, then we delete them in a single
1564 * performMultipleDeletions() call. This is to avoid unwanted DROP
1565 * RESTRICT errors if one of the relations depends on another.
1566 */
1567
1568 /* Determine required relkind */
1569 switch (drop->removeType)
1570 {
1571 case OBJECT_TABLE:
1572 relkind = RELKIND_RELATION;
1573 break;
1574
1575 case OBJECT_INDEX:
1576 relkind = RELKIND_INDEX;
1577 break;
1578
1579 case OBJECT_SEQUENCE:
1580 relkind = RELKIND_SEQUENCE;
1581 break;
1582
1583 case OBJECT_VIEW:
1584 relkind = RELKIND_VIEW;
1585 break;
1586
1587 case OBJECT_MATVIEW:
1588 relkind = RELKIND_MATVIEW;
1589 break;
1590
1592 relkind = RELKIND_FOREIGN_TABLE;
1593 break;
1594
1595 default:
1596 elog(ERROR, "unrecognized drop object type: %d",
1597 (int) drop->removeType);
1598 relkind = 0; /* keep compiler quiet */
1599 break;
1600 }
1601
1602 /* Lock and validate each relation; build a list of object addresses */
1603 objects = new_object_addresses();
1604
1605 foreach(cell, drop->objects)
1606 {
1607 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1608 Oid relOid;
1609 ObjectAddress obj;
1611
1612 /*
1613 * These next few steps are a great deal like relation_openrv, but we
1614 * don't bother building a relcache entry since we don't need it.
1615 *
1616 * Check for shared-cache-inval messages before trying to access the
1617 * relation. This is needed to cover the case where the name
1618 * identifies a rel that has been dropped and recreated since the
1619 * start of our transaction: if we don't flush the old syscache entry,
1620 * then we'll latch onto that entry and suffer an error later.
1621 */
1623
1624 /* Look up the appropriate relation using namespace search. */
1625 state.expected_relkind = relkind;
1626 state.heap_lockmode = drop->concurrent ?
1628 /* We must initialize these fields to show that no locks are held: */
1629 state.heapOid = InvalidOid;
1630 state.partParentOid = InvalidOid;
1631
1632 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1634 &state);
1635
1636 /* Not there? */
1637 if (!OidIsValid(relOid))
1638 {
1639 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1640 continue;
1641 }
1642
1643 /*
1644 * Decide if concurrent mode needs to be used here or not. The
1645 * callback retrieved the rel's persistence for us.
1646 */
1647 if (drop->concurrent &&
1648 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1649 {
1650 Assert(list_length(drop->objects) == 1 &&
1651 drop->removeType == OBJECT_INDEX);
1653 }
1654
1655 /*
1656 * Concurrent index drop cannot be used with partitioned indexes,
1657 * either.
1658 */
1659 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1660 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1661 ereport(ERROR,
1662 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1663 errmsg("cannot drop partitioned index \"%s\" concurrently",
1664 rel->relname)));
1665
1666 /*
1667 * If we're told to drop a partitioned index, we must acquire lock on
1668 * all the children of its parent partitioned table before proceeding.
1669 * Otherwise we'd try to lock the child index partitions before their
1670 * tables, leading to potential deadlock against other sessions that
1671 * will lock those objects in the other order.
1672 */
1673 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1674 (void) find_all_inheritors(state.heapOid,
1675 state.heap_lockmode,
1676 NULL);
1677
1678 /* OK, we're ready to delete this one */
1679 obj.classId = RelationRelationId;
1680 obj.objectId = relOid;
1681 obj.objectSubId = 0;
1682
1683 add_exact_object_address(&obj, objects);
1684 }
1685
1686 performMultipleDeletions(objects, drop->behavior, flags);
1687
1688 free_object_addresses(objects);
1689}
1690
1691/*
1692 * Before acquiring a table lock, check whether we have sufficient rights.
1693 * In the case of DROP INDEX, also try to lock the table before the index.
1694 * Also, if the table to be dropped is a partition, we try to lock the parent
1695 * first.
1696 */
1697static void
1698RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1699 void *arg)
1700{
1701 HeapTuple tuple;
1703 char expected_relkind;
1704 bool is_partition;
1705 Form_pg_class classform;
1707 bool invalid_system_index = false;
1708
1709 state = (struct DropRelationCallbackState *) arg;
1710 heap_lockmode = state->heap_lockmode;
1711
1712 /*
1713 * If we previously locked some other index's heap, and the name we're
1714 * looking up no longer refers to that relation, release the now-useless
1715 * lock.
1716 */
1717 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1718 {
1720 state->heapOid = InvalidOid;
1721 }
1722
1723 /*
1724 * Similarly, if we previously locked some other partition's heap, and the
1725 * name we're looking up no longer refers to that relation, release the
1726 * now-useless lock.
1727 */
1728 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1729 {
1731 state->partParentOid = InvalidOid;
1732 }
1733
1734 /* Didn't find a relation, so no need for locking or permission checks. */
1735 if (!OidIsValid(relOid))
1736 return;
1737
1738 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1739 if (!HeapTupleIsValid(tuple))
1740 return; /* concurrently dropped, so nothing to do */
1741 classform = (Form_pg_class) GETSTRUCT(tuple);
1742 is_partition = classform->relispartition;
1743
1744 /* Pass back some data to save lookups in RemoveRelations */
1745 state->actual_relkind = classform->relkind;
1746 state->actual_relpersistence = classform->relpersistence;
1747
1748 /*
1749 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1750 * but RemoveRelations() can only pass one relkind for a given relation.
1751 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1752 * That means we must be careful before giving the wrong type error when
1753 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1754 * exists with indexes.
1755 */
1756 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1757 expected_relkind = RELKIND_RELATION;
1758 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1759 expected_relkind = RELKIND_INDEX;
1760 else
1761 expected_relkind = classform->relkind;
1762
1763 if (state->expected_relkind != expected_relkind)
1764 DropErrorMsgWrongType(rel->relname, classform->relkind,
1765 state->expected_relkind);
1766
1767 /* Allow DROP to either table owner or schema owner */
1768 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1769 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1771 get_relkind_objtype(classform->relkind),
1772 rel->relname);
1773
1774 /*
1775 * Check the case of a system index that might have been invalidated by a
1776 * failed concurrent process and allow its drop. For the time being, this
1777 * only concerns indexes of toast relations that became invalid during a
1778 * REINDEX CONCURRENTLY process.
1779 */
1780 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1781 {
1782 HeapTuple locTuple;
1783 Form_pg_index indexform;
1784 bool indisvalid;
1785
1786 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1787 if (!HeapTupleIsValid(locTuple))
1788 {
1789 ReleaseSysCache(tuple);
1790 return;
1791 }
1792
1793 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1794 indisvalid = indexform->indisvalid;
1795 ReleaseSysCache(locTuple);
1796
1797 /* Mark object as being an invalid index of system catalogs */
1798 if (!indisvalid)
1799 invalid_system_index = true;
1800 }
1801
1802 /* In the case of an invalid index, it is fine to bypass this check */
1803 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1804 ereport(ERROR,
1805 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1806 errmsg("permission denied: \"%s\" is a system catalog",
1807 rel->relname)));
1808
1809 ReleaseSysCache(tuple);
1810
1811 /*
1812 * In DROP INDEX, attempt to acquire lock on the parent table before
1813 * locking the index. index_drop() will need this anyway, and since
1814 * regular queries lock tables before their indexes, we risk deadlock if
1815 * we do it the other way around. No error if we don't find a pg_index
1816 * entry, though --- the relation may have been dropped. Note that this
1817 * code will execute for either plain or partitioned indexes.
1818 */
1819 if (expected_relkind == RELKIND_INDEX &&
1820 relOid != oldRelOid)
1821 {
1822 state->heapOid = IndexGetRelation(relOid, true);
1823 if (OidIsValid(state->heapOid))
1825 }
1826
1827 /*
1828 * Similarly, if the relation is a partition, we must acquire lock on its
1829 * parent before locking the partition. That's because queries lock the
1830 * parent before its partitions, so we risk deadlock if we do it the other
1831 * way around.
1832 */
1833 if (is_partition && relOid != oldRelOid)
1834 {
1835 state->partParentOid = get_partition_parent(relOid, true);
1836 if (OidIsValid(state->partParentOid))
1837 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1838 }
1839}
1840
1841/*
1842 * ExecuteTruncate
1843 * Executes a TRUNCATE command.
1844 *
1845 * This is a multi-relation truncate. We first open and grab exclusive
1846 * lock on all relations involved, checking permissions and otherwise
1847 * verifying that the relation is OK for truncation. Note that if relations
1848 * are foreign tables, at this stage, we have not yet checked that their
1849 * foreign data in external data sources are OK for truncation. These are
1850 * checked when foreign data are actually truncated later. In CASCADE mode,
1851 * relations having FK references to the targeted relations are automatically
1852 * added to the group; in RESTRICT mode, we check that all FK references are
1853 * internal to the group that's being truncated. Finally all the relations
1854 * are truncated and reindexed.
1855 */
1856void
1858{
1859 List *rels = NIL;
1860 List *relids = NIL;
1861 List *relids_logged = NIL;
1862 ListCell *cell;
1863
1864 /*
1865 * Open, exclusive-lock, and check all the explicitly-specified relations
1866 */
1867 foreach(cell, stmt->relations)
1868 {
1869 RangeVar *rv = lfirst(cell);
1870 Relation rel;
1871 bool recurse = rv->inh;
1872 Oid myrelid;
1873 LOCKMODE lockmode = AccessExclusiveLock;
1874
1875 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1877 NULL);
1878
1879 /* don't throw error for "TRUNCATE foo, foo" */
1880 if (list_member_oid(relids, myrelid))
1881 continue;
1882
1883 /* open the relation, we already hold a lock on it */
1884 rel = table_open(myrelid, NoLock);
1885
1886 /*
1887 * RangeVarGetRelidExtended() has done most checks with its callback,
1888 * but other checks with the now-opened Relation remain.
1889 */
1891
1892 rels = lappend(rels, rel);
1893 relids = lappend_oid(relids, myrelid);
1894
1895 /* Log this relation only if needed for logical decoding */
1897 relids_logged = lappend_oid(relids_logged, myrelid);
1898
1899 if (recurse)
1900 {
1901 ListCell *child;
1902 List *children;
1903
1904 children = find_all_inheritors(myrelid, lockmode, NULL);
1905
1906 foreach(child, children)
1907 {
1908 Oid childrelid = lfirst_oid(child);
1909
1910 if (list_member_oid(relids, childrelid))
1911 continue;
1912
1913 /* find_all_inheritors already got lock */
1914 rel = table_open(childrelid, NoLock);
1915
1916 /*
1917 * It is possible that the parent table has children that are
1918 * temp tables of other backends. We cannot safely access
1919 * such tables (because of buffering issues), and the best
1920 * thing to do is to silently ignore them. Note that this
1921 * check is the same as one of the checks done in
1922 * truncate_check_activity() called below, still it is kept
1923 * here for simplicity.
1924 */
1925 if (RELATION_IS_OTHER_TEMP(rel))
1926 {
1927 table_close(rel, lockmode);
1928 continue;
1929 }
1930
1931 /*
1932 * Inherited TRUNCATE commands perform access permission
1933 * checks on the parent table only. So we skip checking the
1934 * children's permissions and don't call
1935 * truncate_check_perms() here.
1936 */
1939
1940 rels = lappend(rels, rel);
1941 relids = lappend_oid(relids, childrelid);
1942
1943 /* Log this relation only if needed for logical decoding */
1945 relids_logged = lappend_oid(relids_logged, childrelid);
1946 }
1947 }
1948 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1949 ereport(ERROR,
1950 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1951 errmsg("cannot truncate only a partitioned table"),
1952 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1953 }
1954
1955 ExecuteTruncateGuts(rels, relids, relids_logged,
1956 stmt->behavior, stmt->restart_seqs, false);
1957
1958 /* And close the rels */
1959 foreach(cell, rels)
1960 {
1961 Relation rel = (Relation) lfirst(cell);
1962
1963 table_close(rel, NoLock);
1964 }
1965}
1966
1967/*
1968 * ExecuteTruncateGuts
1969 *
1970 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1971 * command (see above) as well as replication subscribers that execute a
1972 * replicated TRUNCATE action.
1973 *
1974 * explicit_rels is the list of Relations to truncate that the command
1975 * specified. relids is the list of Oids corresponding to explicit_rels.
1976 * relids_logged is the list of Oids (a subset of relids) that require
1977 * WAL-logging. This is all a bit redundant, but the existing callers have
1978 * this information handy in this form.
1979 */
1980void
1982 List *relids,
1983 List *relids_logged,
1984 DropBehavior behavior, bool restart_seqs,
1985 bool run_as_table_owner)
1986{
1987 List *rels;
1988 List *seq_relids = NIL;
1989 HTAB *ft_htab = NULL;
1990 EState *estate;
1991 ResultRelInfo *resultRelInfos;
1992 ResultRelInfo *resultRelInfo;
1993 SubTransactionId mySubid;
1994 ListCell *cell;
1995 Oid *logrelids;
1996
1997 /*
1998 * Check the explicitly-specified relations.
1999 *
2000 * In CASCADE mode, suck in all referencing relations as well. This
2001 * requires multiple iterations to find indirectly-dependent relations. At
2002 * each phase, we need to exclusive-lock new rels before looking for their
2003 * dependencies, else we might miss something. Also, we check each rel as
2004 * soon as we open it, to avoid a faux pas such as holding lock for a long
2005 * time on a rel we have no permissions for.
2006 */
2007 rels = list_copy(explicit_rels);
2008 if (behavior == DROP_CASCADE)
2009 {
2010 for (;;)
2011 {
2012 List *newrelids;
2013
2014 newrelids = heap_truncate_find_FKs(relids);
2015 if (newrelids == NIL)
2016 break; /* nothing else to add */
2017
2018 foreach(cell, newrelids)
2019 {
2020 Oid relid = lfirst_oid(cell);
2021 Relation rel;
2022
2023 rel = table_open(relid, AccessExclusiveLock);
2025 (errmsg("truncate cascades to table \"%s\"",
2027 truncate_check_rel(relid, rel->rd_rel);
2028 truncate_check_perms(relid, rel->rd_rel);
2030 rels = lappend(rels, rel);
2031 relids = lappend_oid(relids, relid);
2032
2033 /* Log this relation only if needed for logical decoding */
2035 relids_logged = lappend_oid(relids_logged, relid);
2036 }
2037 }
2038 }
2039
2040 /*
2041 * Check foreign key references. In CASCADE mode, this should be
2042 * unnecessary since we just pulled in all the references; but as a
2043 * cross-check, do it anyway if in an Assert-enabled build.
2044 */
2045#ifdef USE_ASSERT_CHECKING
2046 heap_truncate_check_FKs(rels, false);
2047#else
2048 if (behavior == DROP_RESTRICT)
2049 heap_truncate_check_FKs(rels, false);
2050#endif
2051
2052 /*
2053 * If we are asked to restart sequences, find all the sequences, lock them
2054 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2055 * We want to do this early since it's pointless to do all the truncation
2056 * work only to fail on sequence permissions.
2057 */
2058 if (restart_seqs)
2059 {
2060 foreach(cell, rels)
2061 {
2062 Relation rel = (Relation) lfirst(cell);
2063 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2064 ListCell *seqcell;
2065
2066 foreach(seqcell, seqlist)
2067 {
2068 Oid seq_relid = lfirst_oid(seqcell);
2069 Relation seq_rel;
2070
2071 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2072
2073 /* This check must match AlterSequence! */
2074 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2076 RelationGetRelationName(seq_rel));
2077
2078 seq_relids = lappend_oid(seq_relids, seq_relid);
2079
2080 relation_close(seq_rel, NoLock);
2081 }
2082 }
2083 }
2084
2085 /* Prepare to catch AFTER triggers. */
2087
2088 /*
2089 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2090 * each relation. We don't need to call ExecOpenIndices, though.
2091 *
2092 * We put the ResultRelInfos in the es_opened_result_relations list, even
2093 * though we don't have a range table and don't populate the
2094 * es_result_relations array. That's a bit bogus, but it's enough to make
2095 * ExecGetTriggerResultRel() find them.
2096 */
2097 estate = CreateExecutorState();
2098 resultRelInfos = (ResultRelInfo *)
2099 palloc(list_length(rels) * sizeof(ResultRelInfo));
2100 resultRelInfo = resultRelInfos;
2101 foreach(cell, rels)
2102 {
2103 Relation rel = (Relation) lfirst(cell);
2104
2105 InitResultRelInfo(resultRelInfo,
2106 rel,
2107 0, /* dummy rangetable index */
2108 NULL,
2109 0);
2111 lappend(estate->es_opened_result_relations, resultRelInfo);
2112 resultRelInfo++;
2113 }
2114
2115 /*
2116 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2117 * truncating (this is because one of them might throw an error). Also, if
2118 * we were to allow them to prevent statement execution, that would need
2119 * to be handled here.
2120 */
2121 resultRelInfo = resultRelInfos;
2122 foreach(cell, rels)
2123 {
2124 UserContext ucxt;
2125
2126 if (run_as_table_owner)
2127 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2128 &ucxt);
2129 ExecBSTruncateTriggers(estate, resultRelInfo);
2130 if (run_as_table_owner)
2131 RestoreUserContext(&ucxt);
2132 resultRelInfo++;
2133 }
2134
2135 /*
2136 * OK, truncate each table.
2137 */
2138 mySubid = GetCurrentSubTransactionId();
2139
2140 foreach(cell, rels)
2141 {
2142 Relation rel = (Relation) lfirst(cell);
2143
2144 /* Skip partitioned tables as there is nothing to do */
2145 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2146 continue;
2147
2148 /*
2149 * Build the lists of foreign tables belonging to each foreign server
2150 * and pass each list to the foreign data wrapper's callback function,
2151 * so that each server can truncate its all foreign tables in bulk.
2152 * Each list is saved as a single entry in a hash table that uses the
2153 * server OID as lookup key.
2154 */
2155 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2156 {
2158 bool found;
2159 ForeignTruncateInfo *ft_info;
2160
2161 /* First time through, initialize hashtable for foreign tables */
2162 if (!ft_htab)
2163 {
2164 HASHCTL hctl;
2165
2166 memset(&hctl, 0, sizeof(HASHCTL));
2167 hctl.keysize = sizeof(Oid);
2168 hctl.entrysize = sizeof(ForeignTruncateInfo);
2170
2171 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2172 32, /* start small and extend */
2173 &hctl,
2175 }
2176
2177 /* Find or create cached entry for the foreign table */
2178 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2179 if (!found)
2180 ft_info->rels = NIL;
2181
2182 /*
2183 * Save the foreign table in the entry of the server that the
2184 * foreign table belongs to.
2185 */
2186 ft_info->rels = lappend(ft_info->rels, rel);
2187 continue;
2188 }
2189
2190 /*
2191 * Normally, we need a transaction-safe truncation here. However, if
2192 * the table was either created in the current (sub)transaction or has
2193 * a new relfilenumber in the current (sub)transaction, then we can
2194 * just truncate it in-place, because a rollback would cause the whole
2195 * table or the current physical file to be thrown away anyway.
2196 */
2197 if (rel->rd_createSubid == mySubid ||
2198 rel->rd_newRelfilelocatorSubid == mySubid)
2199 {
2200 /* Immediate, non-rollbackable truncation is OK */
2202 }
2203 else
2204 {
2205 Oid heap_relid;
2206 Oid toast_relid;
2207 ReindexParams reindex_params = {0};
2208
2209 /*
2210 * This effectively deletes all rows in the table, and may be done
2211 * in a serializable transaction. In that case we must record a
2212 * rw-conflict in to this transaction from each transaction
2213 * holding a predicate lock on the table.
2214 */
2216
2217 /*
2218 * Need the full transaction-safe pushups.
2219 *
2220 * Create a new empty storage file for the relation, and assign it
2221 * as the relfilenumber value. The old storage file is scheduled
2222 * for deletion at commit.
2223 */
2224 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2225
2226 heap_relid = RelationGetRelid(rel);
2227
2228 /*
2229 * The same for the toast table, if any.
2230 */
2231 toast_relid = rel->rd_rel->reltoastrelid;
2232 if (OidIsValid(toast_relid))
2233 {
2234 Relation toastrel = relation_open(toast_relid,
2236
2238 toastrel->rd_rel->relpersistence);
2239 table_close(toastrel, NoLock);
2240 }
2241
2242 /*
2243 * Reconstruct the indexes to match, and we're done.
2244 */
2246 &reindex_params);
2247 }
2248
2250 }
2251
2252 /* Now go through the hash table, and truncate foreign tables */
2253 if (ft_htab)
2254 {
2255 ForeignTruncateInfo *ft_info;
2256 HASH_SEQ_STATUS seq;
2257
2258 hash_seq_init(&seq, ft_htab);
2259
2260 PG_TRY();
2261 {
2262 while ((ft_info = hash_seq_search(&seq)) != NULL)
2263 {
2264 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2265
2266 /* truncate_check_rel() has checked that already */
2267 Assert(routine->ExecForeignTruncate != NULL);
2268
2269 routine->ExecForeignTruncate(ft_info->rels,
2270 behavior,
2271 restart_seqs);
2272 }
2273 }
2274 PG_FINALLY();
2275 {
2276 hash_destroy(ft_htab);
2277 }
2278 PG_END_TRY();
2279 }
2280
2281 /*
2282 * Restart owned sequences if we were asked to.
2283 */
2284 foreach(cell, seq_relids)
2285 {
2286 Oid seq_relid = lfirst_oid(cell);
2287
2288 ResetSequence(seq_relid);
2289 }
2290
2291 /*
2292 * Write a WAL record to allow this set of actions to be logically
2293 * decoded.
2294 *
2295 * Assemble an array of relids so we can write a single WAL record for the
2296 * whole action.
2297 */
2298 if (relids_logged != NIL)
2299 {
2300 xl_heap_truncate xlrec;
2301 int i = 0;
2302
2303 /* should only get here if effective_wal_level is 'logical' */
2305
2306 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2307 foreach(cell, relids_logged)
2308 logrelids[i++] = lfirst_oid(cell);
2309
2310 xlrec.dbId = MyDatabaseId;
2311 xlrec.nrelids = list_length(relids_logged);
2312 xlrec.flags = 0;
2313 if (behavior == DROP_CASCADE)
2314 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2315 if (restart_seqs)
2317
2320 XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2321
2323
2324 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2325 }
2326
2327 /*
2328 * Process all AFTER STATEMENT TRUNCATE triggers.
2329 */
2330 resultRelInfo = resultRelInfos;
2331 foreach(cell, rels)
2332 {
2333 UserContext ucxt;
2334
2335 if (run_as_table_owner)
2336 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2337 &ucxt);
2338 ExecASTruncateTriggers(estate, resultRelInfo);
2339 if (run_as_table_owner)
2340 RestoreUserContext(&ucxt);
2341 resultRelInfo++;
2342 }
2343
2344 /* Handle queued AFTER triggers */
2345 AfterTriggerEndQuery(estate);
2346
2347 /* We can clean up the EState now */
2348 FreeExecutorState(estate);
2349
2350 /*
2351 * Close any rels opened by CASCADE (can't do this while EState still
2352 * holds refs)
2353 */
2354 rels = list_difference_ptr(rels, explicit_rels);
2355 foreach(cell, rels)
2356 {
2357 Relation rel = (Relation) lfirst(cell);
2358
2359 table_close(rel, NoLock);
2360 }
2361}
2362
2363/*
2364 * Check that a given relation is safe to truncate. Subroutine for
2365 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2366 */
2367static void
2369{
2370 char *relname = NameStr(reltuple->relname);
2371
2372 /*
2373 * Only allow truncate on regular tables, foreign tables using foreign
2374 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2375 * latter are only being included here for the following checks; no
2376 * physical truncation will occur in their case.).
2377 */
2378 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2379 {
2380 Oid serverid = GetForeignServerIdByRelId(relid);
2381 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2382
2383 if (!fdwroutine->ExecForeignTruncate)
2384 ereport(ERROR,
2385 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2386 errmsg("cannot truncate foreign table \"%s\"",
2387 relname)));
2388 }
2389 else if (reltuple->relkind != RELKIND_RELATION &&
2390 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2391 ereport(ERROR,
2392 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2393 errmsg("\"%s\" is not a table", relname)));
2394
2395 /*
2396 * Most system catalogs can't be truncated at all, or at least not unless
2397 * allow_system_table_mods=on. As an exception, however, we allow
2398 * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2399 * pg_upgrade, because we need to change its relfilenode to match the old
2400 * cluster, and allowing a TRUNCATE command to be executed is the easiest
2401 * way of doing that.
2402 */
2403 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2404 && (!IsBinaryUpgrade ||
2405 (relid != LargeObjectRelationId &&
2406 relid != LargeObjectMetadataRelationId)))
2407 ereport(ERROR,
2408 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2409 errmsg("permission denied: \"%s\" is a system catalog",
2410 relname)));
2411
2413}
2414
2415/*
2416 * Check that current user has the permission to truncate given relation.
2417 */
2418static void
2420{
2421 char *relname = NameStr(reltuple->relname);
2422 AclResult aclresult;
2423
2424 /* Permissions checks */
2425 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2426 if (aclresult != ACLCHECK_OK)
2427 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2428 relname);
2429}
2430
2431/*
2432 * Set of extra sanity checks to check if a given relation is safe to
2433 * truncate. This is split with truncate_check_rel() as
2434 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2435 */
2436static void
2438{
2439 /*
2440 * Don't allow truncate on temp tables of other backends ... their local
2441 * buffer manager is not going to cope.
2442 */
2443 if (RELATION_IS_OTHER_TEMP(rel))
2444 ereport(ERROR,
2445 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2446 errmsg("cannot truncate temporary tables of other sessions")));
2447
2448 /*
2449 * Also check for active uses of the relation in the current transaction,
2450 * including open scans and pending AFTER trigger events.
2451 */
2452 CheckTableNotInUse(rel, "TRUNCATE");
2453}
2454
2455/*
2456 * storage_name
2457 * returns the name corresponding to a typstorage/attstorage enum value
2458 */
2459static const char *
2461{
2462 switch (c)
2463 {
2464 case TYPSTORAGE_PLAIN:
2465 return "PLAIN";
2466 case TYPSTORAGE_EXTERNAL:
2467 return "EXTERNAL";
2468 case TYPSTORAGE_EXTENDED:
2469 return "EXTENDED";
2470 case TYPSTORAGE_MAIN:
2471 return "MAIN";
2472 default:
2473 return "???";
2474 }
2475}
2476
2477/*----------
2478 * MergeAttributes
2479 * Returns new schema given initial schema and superclasses.
2480 *
2481 * Input arguments:
2482 * 'columns' is the column/attribute definition for the table. (It's a list
2483 * of ColumnDef's.) It is destructively changed.
2484 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2485 * 'relpersistence' is the persistence type of the table.
2486 * 'is_partition' tells if the table is a partition.
2487 *
2488 * Output arguments:
2489 * 'supconstr' receives a list of CookedConstraint representing
2490 * CHECK constraints belonging to parent relations, updated as
2491 * necessary to be valid for the child.
2492 * 'supnotnulls' receives a list of CookedConstraint representing
2493 * not-null constraints based on those from parent relations.
2494 *
2495 * Return value:
2496 * Completed schema list.
2497 *
2498 * Notes:
2499 * The order in which the attributes are inherited is very important.
2500 * Intuitively, the inherited attributes should come first. If a table
2501 * inherits from multiple parents, the order of those attributes are
2502 * according to the order of the parents specified in CREATE TABLE.
2503 *
2504 * Here's an example:
2505 *
2506 * create table person (name text, age int4, location point);
2507 * create table emp (salary int4, manager text) inherits(person);
2508 * create table student (gpa float8) inherits (person);
2509 * create table stud_emp (percent int4) inherits (emp, student);
2510 *
2511 * The order of the attributes of stud_emp is:
2512 *
2513 * person {1:name, 2:age, 3:location}
2514 * / \
2515 * {6:gpa} student emp {4:salary, 5:manager}
2516 * \ /
2517 * stud_emp {7:percent}
2518 *
2519 * If the same attribute name appears multiple times, then it appears
2520 * in the result table in the proper location for its first appearance.
2521 *
2522 * Constraints (including not-null constraints) for the child table
2523 * are the union of all relevant constraints, from both the child schema
2524 * and parent tables. In addition, in legacy inheritance, each column that
2525 * appears in a primary key in any of the parents also gets a NOT NULL
2526 * constraint (partitioning doesn't need this, because the PK itself gets
2527 * inherited.)
2528 *
2529 * The default value for a child column is defined as:
2530 * (1) If the child schema specifies a default, that value is used.
2531 * (2) If neither the child nor any parent specifies a default, then
2532 * the column will not have a default.
2533 * (3) If conflicting defaults are inherited from different parents
2534 * (and not overridden by the child), an error is raised.
2535 * (4) Otherwise the inherited default is used.
2536 *
2537 * Note that the default-value infrastructure is used for generated
2538 * columns' expressions too, so most of the preceding paragraph applies
2539 * to generation expressions too. We insist that a child column be
2540 * generated if and only if its parent(s) are, but it need not have
2541 * the same generation expression.
2542 *----------
2543 */
2544static List *
2545MergeAttributes(List *columns, const List *supers, char relpersistence,
2546 bool is_partition, List **supconstr, List **supnotnulls)
2547{
2548 List *inh_columns = NIL;
2549 List *constraints = NIL;
2550 List *nnconstraints = NIL;
2551 bool have_bogus_defaults = false;
2552 int child_attno;
2553 static Node bogus_marker = {0}; /* marks conflicting defaults */
2554 List *saved_columns = NIL;
2555 ListCell *lc;
2556
2557 /*
2558 * Check for and reject tables with too many columns. We perform this
2559 * check relatively early for two reasons: (a) we don't run the risk of
2560 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2561 * okay if we're processing <= 1600 columns, but could take minutes to
2562 * execute if the user attempts to create a table with hundreds of
2563 * thousands of columns.
2564 *
2565 * Note that we also need to check that we do not exceed this figure after
2566 * including columns from inherited relations.
2567 */
2568 if (list_length(columns) > MaxHeapAttributeNumber)
2569 ereport(ERROR,
2570 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2571 errmsg("tables can have at most %d columns",
2573
2574 /*
2575 * Check for duplicate names in the explicit list of attributes.
2576 *
2577 * Although we might consider merging such entries in the same way that we
2578 * handle name conflicts for inherited attributes, it seems to make more
2579 * sense to assume such conflicts are errors.
2580 *
2581 * We don't use foreach() here because we have two nested loops over the
2582 * columns list, with possible element deletions in the inner one. If we
2583 * used foreach_delete_current() it could only fix up the state of one of
2584 * the loops, so it seems cleaner to use looping over list indexes for
2585 * both loops. Note that any deletion will happen beyond where the outer
2586 * loop is, so its index never needs adjustment.
2587 */
2588 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2589 {
2590 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2591
2592 if (!is_partition && coldef->typeName == NULL)
2593 {
2594 /*
2595 * Typed table column option that does not belong to a column from
2596 * the type. This works because the columns from the type come
2597 * first in the list. (We omit this check for partition column
2598 * lists; those are processed separately below.)
2599 */
2600 ereport(ERROR,
2601 (errcode(ERRCODE_UNDEFINED_COLUMN),
2602 errmsg("column \"%s\" does not exist",
2603 coldef->colname)));
2604 }
2605
2606 /* restpos scans all entries beyond coldef; incr is in loop body */
2607 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2608 {
2609 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2610
2611 if (strcmp(coldef->colname, restdef->colname) == 0)
2612 {
2613 if (coldef->is_from_type)
2614 {
2615 /*
2616 * merge the column options into the column from the type
2617 */
2618 coldef->is_not_null = restdef->is_not_null;
2619 coldef->raw_default = restdef->raw_default;
2620 coldef->cooked_default = restdef->cooked_default;
2621 coldef->constraints = restdef->constraints;
2622 coldef->is_from_type = false;
2623 columns = list_delete_nth_cell(columns, restpos);
2624 }
2625 else
2626 ereport(ERROR,
2627 (errcode(ERRCODE_DUPLICATE_COLUMN),
2628 errmsg("column \"%s\" specified more than once",
2629 coldef->colname)));
2630 }
2631 else
2632 restpos++;
2633 }
2634 }
2635
2636 /*
2637 * In case of a partition, there are no new column definitions, only dummy
2638 * ColumnDefs created for column constraints. Set them aside for now and
2639 * process them at the end.
2640 */
2641 if (is_partition)
2642 {
2643 saved_columns = columns;
2644 columns = NIL;
2645 }
2646
2647 /*
2648 * Scan the parents left-to-right, and merge their attributes to form a
2649 * list of inherited columns (inh_columns).
2650 */
2651 child_attno = 0;
2652 foreach(lc, supers)
2653 {
2654 Oid parent = lfirst_oid(lc);
2655 Relation relation;
2656 TupleDesc tupleDesc;
2657 TupleConstr *constr;
2658 AttrMap *newattmap;
2659 List *inherited_defaults;
2660 List *cols_with_defaults;
2661 List *nnconstrs;
2662 ListCell *lc1;
2663 ListCell *lc2;
2664 Bitmapset *nncols = NULL;
2665
2666 /* caller already got lock */
2667 relation = table_open(parent, NoLock);
2668
2669 /*
2670 * Check for active uses of the parent partitioned table in the
2671 * current transaction, such as being used in some manner by an
2672 * enclosing command.
2673 */
2674 if (is_partition)
2675 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2676
2677 /*
2678 * We do not allow partitioned tables and partitions to participate in
2679 * regular inheritance.
2680 */
2681 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2682 ereport(ERROR,
2683 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2684 errmsg("cannot inherit from partitioned table \"%s\"",
2685 RelationGetRelationName(relation))));
2686 if (relation->rd_rel->relispartition && !is_partition)
2687 ereport(ERROR,
2688 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2689 errmsg("cannot inherit from partition \"%s\"",
2690 RelationGetRelationName(relation))));
2691
2692 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2693 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2694 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2695 ereport(ERROR,
2696 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2697 errmsg("inherited relation \"%s\" is not a table or foreign table",
2698 RelationGetRelationName(relation))));
2699
2700 /*
2701 * If the parent is permanent, so must be all of its partitions. Note
2702 * that inheritance allows that case.
2703 */
2704 if (is_partition &&
2705 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2706 relpersistence == RELPERSISTENCE_TEMP)
2707 ereport(ERROR,
2708 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2709 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2710 RelationGetRelationName(relation))));
2711
2712 /* Permanent rels cannot inherit from temporary ones */
2713 if (relpersistence != RELPERSISTENCE_TEMP &&
2714 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2715 ereport(ERROR,
2716 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2717 errmsg(!is_partition
2718 ? "cannot inherit from temporary relation \"%s\""
2719 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2720 RelationGetRelationName(relation))));
2721
2722 /* If existing rel is temp, it must belong to this session */
2723 if (RELATION_IS_OTHER_TEMP(relation))
2724 ereport(ERROR,
2725 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2726 errmsg(!is_partition
2727 ? "cannot inherit from temporary relation of another session"
2728 : "cannot create as partition of temporary relation of another session")));
2729
2730 /*
2731 * We should have an UNDER permission flag for this, but for now,
2732 * demand that creator of a child table own the parent.
2733 */
2734 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2736 RelationGetRelationName(relation));
2737
2738 tupleDesc = RelationGetDescr(relation);
2739 constr = tupleDesc->constr;
2740
2741 /*
2742 * newattmap->attnums[] will contain the child-table attribute numbers
2743 * for the attributes of this parent table. (They are not the same
2744 * for parents after the first one, nor if we have dropped columns.)
2745 */
2746 newattmap = make_attrmap(tupleDesc->natts);
2747
2748 /* We can't process inherited defaults until newattmap is complete. */
2749 inherited_defaults = cols_with_defaults = NIL;
2750
2751 /*
2752 * Request attnotnull on columns that have a not-null constraint
2753 * that's not marked NO INHERIT (even if not valid).
2754 */
2755 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2756 true, false);
2757 foreach_ptr(CookedConstraint, cc, nnconstrs)
2758 nncols = bms_add_member(nncols, cc->attnum);
2759
2760 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2761 parent_attno++)
2762 {
2763 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2764 parent_attno - 1);
2765 char *attributeName = NameStr(attribute->attname);
2766 int exist_attno;
2767 ColumnDef *newdef;
2768 ColumnDef *mergeddef;
2769
2770 /*
2771 * Ignore dropped columns in the parent.
2772 */
2773 if (attribute->attisdropped)
2774 continue; /* leave newattmap->attnums entry as zero */
2775
2776 /*
2777 * Create new column definition
2778 */
2779 newdef = makeColumnDef(attributeName, attribute->atttypid,
2780 attribute->atttypmod, attribute->attcollation);
2781 newdef->storage = attribute->attstorage;
2782 newdef->generated = attribute->attgenerated;
2783 if (CompressionMethodIsValid(attribute->attcompression))
2784 newdef->compression =
2785 pstrdup(GetCompressionMethodName(attribute->attcompression));
2786
2787 /*
2788 * Regular inheritance children are independent enough not to
2789 * inherit identity columns. But partitions are integral part of
2790 * a partitioned table and inherit identity column.
2791 */
2792 if (is_partition)
2793 newdef->identity = attribute->attidentity;
2794
2795 /*
2796 * Does it match some previously considered column from another
2797 * parent?
2798 */
2799 exist_attno = findAttrByName(attributeName, inh_columns);
2800 if (exist_attno > 0)
2801 {
2802 /*
2803 * Yes, try to merge the two column definitions.
2804 */
2805 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2806
2807 newattmap->attnums[parent_attno - 1] = exist_attno;
2808
2809 /*
2810 * Partitions have only one parent, so conflict should never
2811 * occur.
2812 */
2813 Assert(!is_partition);
2814 }
2815 else
2816 {
2817 /*
2818 * No, create a new inherited column
2819 */
2820 newdef->inhcount = 1;
2821 newdef->is_local = false;
2822 inh_columns = lappend(inh_columns, newdef);
2823
2824 newattmap->attnums[parent_attno - 1] = ++child_attno;
2825 mergeddef = newdef;
2826 }
2827
2828 /*
2829 * mark attnotnull if parent has it
2830 */
2831 if (bms_is_member(parent_attno, nncols))
2832 mergeddef->is_not_null = true;
2833
2834 /*
2835 * Locate default/generation expression if any
2836 */
2837 if (attribute->atthasdef)
2838 {
2839 Node *this_default;
2840
2841 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2842 if (this_default == NULL)
2843 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2844 parent_attno, RelationGetRelationName(relation));
2845
2846 /*
2847 * If it's a GENERATED default, it might contain Vars that
2848 * need to be mapped to the inherited column(s)' new numbers.
2849 * We can't do that till newattmap is ready, so just remember
2850 * all the inherited default expressions for the moment.
2851 */
2852 inherited_defaults = lappend(inherited_defaults, this_default);
2853 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2854 }
2855 }
2856
2857 /*
2858 * Now process any inherited default expressions, adjusting attnos
2859 * using the completed newattmap map.
2860 */
2861 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2862 {
2863 Node *this_default = (Node *) lfirst(lc1);
2864 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2865 bool found_whole_row;
2866
2867 /* Adjust Vars to match new table's column numbering */
2868 this_default = map_variable_attnos(this_default,
2869 1, 0,
2870 newattmap,
2871 InvalidOid, &found_whole_row);
2872
2873 /*
2874 * For the moment we have to reject whole-row variables. We could
2875 * convert them, if we knew the new table's rowtype OID, but that
2876 * hasn't been assigned yet. (A variable could only appear in a
2877 * generation expression, so the error message is correct.)
2878 */
2879 if (found_whole_row)
2880 ereport(ERROR,
2881 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2882 errmsg("cannot convert whole-row table reference"),
2883 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2884 def->colname,
2885 RelationGetRelationName(relation))));
2886
2887 /*
2888 * If we already had a default from some prior parent, check to
2889 * see if they are the same. If so, no problem; if not, mark the
2890 * column as having a bogus default. Below, we will complain if
2891 * the bogus default isn't overridden by the child columns.
2892 */
2893 Assert(def->raw_default == NULL);
2894 if (def->cooked_default == NULL)
2895 def->cooked_default = this_default;
2896 else if (!equal(def->cooked_default, this_default))
2897 {
2898 def->cooked_default = &bogus_marker;
2899 have_bogus_defaults = true;
2900 }
2901 }
2902
2903 /*
2904 * Now copy the CHECK constraints of this parent, adjusting attnos
2905 * using the completed newattmap map. Identically named constraints
2906 * are merged if possible, else we throw error.
2907 */
2908 if (constr && constr->num_check > 0)
2909 {
2910 ConstrCheck *check = constr->check;
2911
2912 for (int i = 0; i < constr->num_check; i++)
2913 {
2914 char *name = check[i].ccname;
2915 Node *expr;
2916 bool found_whole_row;
2917
2918 /* ignore if the constraint is non-inheritable */
2919 if (check[i].ccnoinherit)
2920 continue;
2921
2922 /* Adjust Vars to match new table's column numbering */
2923 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2924 1, 0,
2925 newattmap,
2926 InvalidOid, &found_whole_row);
2927
2928 /*
2929 * For the moment we have to reject whole-row variables. We
2930 * could convert them, if we knew the new table's rowtype OID,
2931 * but that hasn't been assigned yet.
2932 */
2933 if (found_whole_row)
2934 ereport(ERROR,
2935 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2936 errmsg("cannot convert whole-row table reference"),
2937 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2938 name,
2939 RelationGetRelationName(relation))));
2940
2941 constraints = MergeCheckConstraint(constraints, name, expr,
2942 check[i].ccenforced);
2943 }
2944 }
2945
2946 /*
2947 * Also copy the not-null constraints from this parent. The
2948 * attnotnull markings were already installed above.
2949 */
2950 foreach_ptr(CookedConstraint, nn, nnconstrs)
2951 {
2952 Assert(nn->contype == CONSTR_NOTNULL);
2953
2954 nn->attnum = newattmap->attnums[nn->attnum - 1];
2955
2956 nnconstraints = lappend(nnconstraints, nn);
2957 }
2958
2959 free_attrmap(newattmap);
2960
2961 /*
2962 * Close the parent rel, but keep our lock on it until xact commit.
2963 * That will prevent someone else from deleting or ALTERing the parent
2964 * before the child is committed.
2965 */
2966 table_close(relation, NoLock);
2967 }
2968
2969 /*
2970 * If we had no inherited attributes, the result columns are just the
2971 * explicitly declared columns. Otherwise, we need to merge the declared
2972 * columns into the inherited column list. Although, we never have any
2973 * explicitly declared columns if the table is a partition.
2974 */
2975 if (inh_columns != NIL)
2976 {
2977 int newcol_attno = 0;
2978
2979 foreach(lc, columns)
2980 {
2981 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2982 char *attributeName = newdef->colname;
2983 int exist_attno;
2984
2985 /*
2986 * Partitions have only one parent and have no column definitions
2987 * of their own, so conflict should never occur.
2988 */
2989 Assert(!is_partition);
2990
2991 newcol_attno++;
2992
2993 /*
2994 * Does it match some inherited column?
2995 */
2996 exist_attno = findAttrByName(attributeName, inh_columns);
2997 if (exist_attno > 0)
2998 {
2999 /*
3000 * Yes, try to merge the two column definitions.
3001 */
3002 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
3003 }
3004 else
3005 {
3006 /*
3007 * No, attach new column unchanged to result columns.
3008 */
3009 inh_columns = lappend(inh_columns, newdef);
3010 }
3011 }
3012
3013 columns = inh_columns;
3014
3015 /*
3016 * Check that we haven't exceeded the legal # of columns after merging
3017 * in inherited columns.
3018 */
3019 if (list_length(columns) > MaxHeapAttributeNumber)
3020 ereport(ERROR,
3021 (errcode(ERRCODE_TOO_MANY_COLUMNS),
3022 errmsg("tables can have at most %d columns",
3024 }
3025
3026 /*
3027 * Now that we have the column definition list for a partition, we can
3028 * check whether the columns referenced in the column constraint specs
3029 * actually exist. Also, merge column defaults.
3030 */
3031 if (is_partition)
3032 {
3033 foreach(lc, saved_columns)
3034 {
3035 ColumnDef *restdef = lfirst(lc);
3036 bool found = false;
3037 ListCell *l;
3038
3039 foreach(l, columns)
3040 {
3041 ColumnDef *coldef = lfirst(l);
3042
3043 if (strcmp(coldef->colname, restdef->colname) == 0)
3044 {
3045 found = true;
3046
3047 /*
3048 * Check for conflicts related to generated columns.
3049 *
3050 * Same rules as above: generated-ness has to match the
3051 * parent, but the contents of the generation expression
3052 * can be different.
3053 */
3054 if (coldef->generated)
3055 {
3056 if (restdef->raw_default && !restdef->generated)
3057 ereport(ERROR,
3058 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3059 errmsg("column \"%s\" inherits from generated column but specifies default",
3060 restdef->colname)));
3061 if (restdef->identity)
3062 ereport(ERROR,
3063 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3064 errmsg("column \"%s\" inherits from generated column but specifies identity",
3065 restdef->colname)));
3066 }
3067 else
3068 {
3069 if (restdef->generated)
3070 ereport(ERROR,
3071 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3072 errmsg("child column \"%s\" specifies generation expression",
3073 restdef->colname),
3074 errhint("A child table column cannot be generated unless its parent column is.")));
3075 }
3076
3077 if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3078 ereport(ERROR,
3079 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3080 errmsg("column \"%s\" inherits from generated column of different kind",
3081 restdef->colname),
3082 errdetail("Parent column is %s, child column is %s.",
3083 coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3084 restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3085
3086 /*
3087 * Override the parent's default value for this column
3088 * (coldef->cooked_default) with the partition's local
3089 * definition (restdef->raw_default), if there's one. It
3090 * should be physically impossible to get a cooked default
3091 * in the local definition or a raw default in the
3092 * inherited definition, but make sure they're nulls, for
3093 * future-proofing.
3094 */
3095 Assert(restdef->cooked_default == NULL);
3096 Assert(coldef->raw_default == NULL);
3097 if (restdef->raw_default)
3098 {
3099 coldef->raw_default = restdef->raw_default;
3100 coldef->cooked_default = NULL;
3101 }
3102 }
3103 }
3104
3105 /* complain for constraints on columns not in parent */
3106 if (!found)
3107 ereport(ERROR,
3108 (errcode(ERRCODE_UNDEFINED_COLUMN),
3109 errmsg("column \"%s\" does not exist",
3110 restdef->colname)));
3111 }
3112 }
3113
3114 /*
3115 * If we found any conflicting parent default values, check to make sure
3116 * they were overridden by the child.
3117 */
3118 if (have_bogus_defaults)
3119 {
3120 foreach(lc, columns)
3121 {
3122 ColumnDef *def = lfirst(lc);
3123
3124 if (def->cooked_default == &bogus_marker)
3125 {
3126 if (def->generated)
3127 ereport(ERROR,
3128 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3129 errmsg("column \"%s\" inherits conflicting generation expressions",
3130 def->colname),
3131 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3132 else
3133 ereport(ERROR,
3134 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3135 errmsg("column \"%s\" inherits conflicting default values",
3136 def->colname),
3137 errhint("To resolve the conflict, specify a default explicitly.")));
3138 }
3139 }
3140 }
3141
3142 *supconstr = constraints;
3143 *supnotnulls = nnconstraints;
3144
3145 return columns;
3146}
3147
3148
3149/*
3150 * MergeCheckConstraint
3151 * Try to merge an inherited CHECK constraint with previous ones
3152 *
3153 * If we inherit identically-named constraints from multiple parents, we must
3154 * merge them, or throw an error if they don't have identical definitions.
3155 *
3156 * constraints is a list of CookedConstraint structs for previous constraints.
3157 *
3158 * If the new constraint matches an existing one, then the existing
3159 * constraint's inheritance count is updated. If there is a conflict (same
3160 * name but different expression), throw an error. If the constraint neither
3161 * matches nor conflicts with an existing one, a new constraint is appended to
3162 * the list.
3163 */
3164static List *
3165MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3166{
3167 ListCell *lc;
3168 CookedConstraint *newcon;
3169
3170 foreach(lc, constraints)
3171 {
3173
3174 Assert(ccon->contype == CONSTR_CHECK);
3175
3176 /* Non-matching names never conflict */
3177 if (strcmp(ccon->name, name) != 0)
3178 continue;
3179
3180 if (equal(expr, ccon->expr))
3181 {
3182 /* OK to merge constraint with existing */
3183 if (pg_add_s16_overflow(ccon->inhcount, 1,
3184 &ccon->inhcount))
3185 ereport(ERROR,
3186 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3187 errmsg("too many inheritance parents"));
3188
3189 /*
3190 * When enforceability differs, the merged constraint should be
3191 * marked as ENFORCED because one of the parents is ENFORCED.
3192 */
3193 if (!ccon->is_enforced && is_enforced)
3194 {
3195 ccon->is_enforced = true;
3196 ccon->skip_validation = false;
3197 }
3198
3199 return constraints;
3200 }
3201
3202 ereport(ERROR,
3204 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3205 name)));
3206 }
3207
3208 /*
3209 * Constraint couldn't be merged with an existing one and also didn't
3210 * conflict with an existing one, so add it as a new one to the list.
3211 */
3213 newcon->contype = CONSTR_CHECK;
3214 newcon->name = pstrdup(name);
3215 newcon->expr = expr;
3216 newcon->inhcount = 1;
3217 newcon->is_enforced = is_enforced;
3218 newcon->skip_validation = !is_enforced;
3219 return lappend(constraints, newcon);
3220}
3221
3222/*
3223 * MergeChildAttribute
3224 * Merge given child attribute definition into given inherited attribute.
3225 *
3226 * Input arguments:
3227 * 'inh_columns' is the list of inherited ColumnDefs.
3228 * 'exist_attno' is the number of the inherited attribute in inh_columns
3229 * 'newcol_attno' is the attribute number in child table's schema definition
3230 * 'newdef' is the column/attribute definition from the child table.
3231 *
3232 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3233 * ColumnDef remains unchanged.
3234 *
3235 * Notes:
3236 * - The attribute is merged according to the rules laid out in the prologue
3237 * of MergeAttributes().
3238 * - If matching inherited attribute exists but the child attribute can not be
3239 * merged into it, the function throws respective errors.
3240 * - A partition can not have its own column definitions. Hence this function
3241 * is applicable only to a regular inheritance child.
3242 */
3243static void
3244MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3245{
3246 char *attributeName = newdef->colname;
3247 ColumnDef *inhdef;
3248 Oid inhtypeid,
3249 newtypeid;
3250 int32 inhtypmod,
3251 newtypmod;
3252 Oid inhcollid,
3253 newcollid;
3254
3255 if (exist_attno == newcol_attno)
3257 (errmsg("merging column \"%s\" with inherited definition",
3258 attributeName)));
3259 else
3261 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3262 errdetail("User-specified column moved to the position of the inherited column.")));
3263
3264 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3265
3266 /*
3267 * Must have the same type and typmod
3268 */
3269 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3270 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3271 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3272 ereport(ERROR,
3273 (errcode(ERRCODE_DATATYPE_MISMATCH),
3274 errmsg("column \"%s\" has a type conflict",
3275 attributeName),
3276 errdetail("%s versus %s",
3277 format_type_with_typemod(inhtypeid, inhtypmod),
3278 format_type_with_typemod(newtypeid, newtypmod))));
3279
3280 /*
3281 * Must have the same collation
3282 */
3283 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3284 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3285 if (inhcollid != newcollid)
3286 ereport(ERROR,
3287 (errcode(ERRCODE_COLLATION_MISMATCH),
3288 errmsg("column \"%s\" has a collation conflict",
3289 attributeName),
3290 errdetail("\"%s\" versus \"%s\"",
3291 get_collation_name(inhcollid),
3292 get_collation_name(newcollid))));
3293
3294 /*
3295 * Identity is never inherited by a regular inheritance child. Pick
3296 * child's identity definition if there's one.
3297 */
3298 inhdef->identity = newdef->identity;
3299
3300 /*
3301 * Copy storage parameter
3302 */
3303 if (inhdef->storage == 0)
3304 inhdef->storage = newdef->storage;
3305 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3306 ereport(ERROR,
3307 (errcode(ERRCODE_DATATYPE_MISMATCH),
3308 errmsg("column \"%s\" has a storage parameter conflict",
3309 attributeName),
3310 errdetail("%s versus %s",
3311 storage_name(inhdef->storage),
3312 storage_name(newdef->storage))));
3313
3314 /*
3315 * Copy compression parameter
3316 */
3317 if (inhdef->compression == NULL)
3318 inhdef->compression = newdef->compression;
3319 else if (newdef->compression != NULL)
3320 {
3321 if (strcmp(inhdef->compression, newdef->compression) != 0)
3322 ereport(ERROR,
3323 (errcode(ERRCODE_DATATYPE_MISMATCH),
3324 errmsg("column \"%s\" has a compression method conflict",
3325 attributeName),
3326 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3327 }
3328
3329 /*
3330 * Merge of not-null constraints = OR 'em together
3331 */
3332 inhdef->is_not_null |= newdef->is_not_null;
3333
3334 /*
3335 * Check for conflicts related to generated columns.
3336 *
3337 * If the parent column is generated, the child column will be made a
3338 * generated column if it isn't already. If it is a generated column,
3339 * we'll take its generation expression in preference to the parent's. We
3340 * must check that the child column doesn't specify a default value or
3341 * identity, which matches the rules for a single column in
3342 * parse_utilcmd.c.
3343 *
3344 * Conversely, if the parent column is not generated, the child column
3345 * can't be either. (We used to allow that, but it results in being able
3346 * to override the generation expression via UPDATEs through the parent.)
3347 */
3348 if (inhdef->generated)
3349 {
3350 if (newdef->raw_default && !newdef->generated)
3351 ereport(ERROR,
3352 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3353 errmsg("column \"%s\" inherits from generated column but specifies default",
3354 inhdef->colname)));
3355 if (newdef->identity)
3356 ereport(ERROR,
3357 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3358 errmsg("column \"%s\" inherits from generated column but specifies identity",
3359 inhdef->colname)));
3360 }
3361 else
3362 {
3363 if (newdef->generated)
3364 ereport(ERROR,
3365 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3366 errmsg("child column \"%s\" specifies generation expression",
3367 inhdef->colname),
3368 errhint("A child table column cannot be generated unless its parent column is.")));
3369 }
3370
3371 if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3372 ereport(ERROR,
3373 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3374 errmsg("column \"%s\" inherits from generated column of different kind",
3375 inhdef->colname),
3376 errdetail("Parent column is %s, child column is %s.",
3377 inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3378 newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3379
3380 /*
3381 * If new def has a default, override previous default
3382 */
3383 if (newdef->raw_default != NULL)
3384 {
3385 inhdef->raw_default = newdef->raw_default;
3386 inhdef->cooked_default = newdef->cooked_default;
3387 }
3388
3389 /* Mark the column as locally defined */
3390 inhdef->is_local = true;
3391}
3392
3393/*
3394 * MergeInheritedAttribute
3395 * Merge given parent attribute definition into specified attribute
3396 * inherited from the previous parents.
3397 *
3398 * Input arguments:
3399 * 'inh_columns' is the list of previously inherited ColumnDefs.
3400 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3401 * 'newdef' is the new parent column/attribute definition to be merged.
3402 *
3403 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3404 *
3405 * Notes:
3406 * - The attribute is merged according to the rules laid out in the prologue
3407 * of MergeAttributes().
3408 * - If matching inherited attribute exists but the new attribute can not be
3409 * merged into it, the function throws respective errors.
3410 * - A partition inherits from only a single parent. Hence this function is
3411 * applicable only to a regular inheritance.
3412 */
3413static ColumnDef *
3415 int exist_attno,
3416 const ColumnDef *newdef)
3417{
3418 char *attributeName = newdef->colname;
3419 ColumnDef *prevdef;
3420 Oid prevtypeid,
3421 newtypeid;
3422 int32 prevtypmod,
3423 newtypmod;
3424 Oid prevcollid,
3425 newcollid;
3426
3428 (errmsg("merging multiple inherited definitions of column \"%s\"",
3429 attributeName)));
3430 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3431
3432 /*
3433 * Must have the same type and typmod
3434 */
3435 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3436 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3437 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3438 ereport(ERROR,
3439 (errcode(ERRCODE_DATATYPE_MISMATCH),
3440 errmsg("inherited column \"%s\" has a type conflict",
3441 attributeName),
3442 errdetail("%s versus %s",
3443 format_type_with_typemod(prevtypeid, prevtypmod),
3444 format_type_with_typemod(newtypeid, newtypmod))));
3445
3446 /*
3447 * Must have the same collation
3448 */
3449 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3450 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3451 if (prevcollid != newcollid)
3452 ereport(ERROR,
3453 (errcode(ERRCODE_COLLATION_MISMATCH),
3454 errmsg("inherited column \"%s\" has a collation conflict",
3455 attributeName),
3456 errdetail("\"%s\" versus \"%s\"",
3457 get_collation_name(prevcollid),
3458 get_collation_name(newcollid))));
3459
3460 /*
3461 * Copy/check storage parameter
3462 */
3463 if (prevdef->storage == 0)
3464 prevdef->storage = newdef->storage;
3465 else if (prevdef->storage != newdef->storage)
3466 ereport(ERROR,
3467 (errcode(ERRCODE_DATATYPE_MISMATCH),
3468 errmsg("inherited column \"%s\" has a storage parameter conflict",
3469 attributeName),
3470 errdetail("%s versus %s",
3471 storage_name(prevdef->storage),
3472 storage_name(newdef->storage))));
3473
3474 /*
3475 * Copy/check compression parameter
3476 */
3477 if (prevdef->compression == NULL)
3478 prevdef->compression = newdef->compression;
3479 else if (newdef->compression != NULL)
3480 {
3481 if (strcmp(prevdef->compression, newdef->compression) != 0)
3482 ereport(ERROR,
3483 (errcode(ERRCODE_DATATYPE_MISMATCH),
3484 errmsg("column \"%s\" has a compression method conflict",
3485 attributeName),
3486 errdetail("%s versus %s",
3487 prevdef->compression, newdef->compression)));
3488 }
3489
3490 /*
3491 * Check for GENERATED conflicts
3492 */
3493 if (prevdef->generated != newdef->generated)
3494 ereport(ERROR,
3495 (errcode(ERRCODE_DATATYPE_MISMATCH),
3496 errmsg("inherited column \"%s\" has a generation conflict",
3497 attributeName)));
3498
3499 /*
3500 * Default and other constraints are handled by the caller.
3501 */
3502
3503 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3504 &prevdef->inhcount))
3505 ereport(ERROR,
3506 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3507 errmsg("too many inheritance parents"));
3508
3509 return prevdef;
3510}
3511
3512/*
3513 * StoreCatalogInheritance
3514 * Updates the system catalogs with proper inheritance information.
3515 *
3516 * supers is a list of the OIDs of the new relation's direct ancestors.
3517 */
3518static void
3520 bool child_is_partition)
3521{
3522 Relation relation;
3523 int32 seqNumber;
3524 ListCell *entry;
3525
3526 /*
3527 * sanity checks
3528 */
3529 Assert(OidIsValid(relationId));
3530
3531 if (supers == NIL)
3532 return;
3533
3534 /*
3535 * Store INHERITS information in pg_inherits using direct ancestors only.
3536 * Also enter dependencies on the direct ancestors, and make sure they are
3537 * marked with relhassubclass = true.
3538 *
3539 * (Once upon a time, both direct and indirect ancestors were found here
3540 * and then entered into pg_ipl. Since that catalog doesn't exist
3541 * anymore, there's no need to look for indirect ancestors.)
3542 */
3543 relation = table_open(InheritsRelationId, RowExclusiveLock);
3544
3545 seqNumber = 1;
3546 foreach(entry, supers)
3547 {
3548 Oid parentOid = lfirst_oid(entry);
3549
3550 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3551 child_is_partition);
3552 seqNumber++;
3553 }
3554
3555 table_close(relation, RowExclusiveLock);
3556}
3557
3558/*
3559 * Make catalog entries showing relationId as being an inheritance child
3560 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3561 */
3562static void
3563StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3564 int32 seqNumber, Relation inhRelation,
3565 bool child_is_partition)
3566{
3567 ObjectAddress childobject,
3568 parentobject;
3569
3570 /* store the pg_inherits row */
3571 StoreSingleInheritance(relationId, parentOid, seqNumber);
3572
3573 /*
3574 * Store a dependency too
3575 */
3576 parentobject.classId = RelationRelationId;
3577 parentobject.objectId = parentOid;
3578 parentobject.objectSubId = 0;
3579 childobject.classId = RelationRelationId;
3580 childobject.objectId = relationId;
3581 childobject.objectSubId = 0;
3582
3583 recordDependencyOn(&childobject, &parentobject,
3584 child_dependency_type(child_is_partition));
3585
3586 /*
3587 * Post creation hook of this inheritance. Since object_access_hook
3588 * doesn't take multiple object identifiers, we relay oid of parent
3589 * relation using auxiliary_id argument.
3590 */
3591 InvokeObjectPostAlterHookArg(InheritsRelationId,
3592 relationId, 0,
3593 parentOid, false);
3594
3595 /*
3596 * Mark the parent as having subclasses.
3597 */
3598 SetRelationHasSubclass(parentOid, true);
3599}
3600
3601/*
3602 * Look for an existing column entry with the given name.
3603 *
3604 * Returns the index (starting with 1) if attribute already exists in columns,
3605 * 0 if it doesn't.
3606 */
3607static int
3608findAttrByName(const char *attributeName, const List *columns)
3609{
3610 ListCell *lc;
3611 int i = 1;
3612
3613 foreach(lc, columns)
3614 {
3615 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3616 return i;
3617
3618 i++;
3619 }
3620 return 0;
3621}
3622
3623
3624/*
3625 * SetRelationHasSubclass
3626 * Set the value of the relation's relhassubclass field in pg_class.
3627 *
3628 * It's always safe to set this field to true, because all SQL commands are
3629 * ready to see true and then find no children. On the other hand, commands
3630 * generally assume zero children if this is false.
3631 *
3632 * Caller must hold any self-exclusive lock until end of transaction. If the
3633 * new value is false, caller must have acquired that lock before reading the
3634 * evidence that justified the false value. That way, it properly waits if
3635 * another backend is simultaneously concluding no need to change the tuple
3636 * (new and old values are true).
3637 *
3638 * NOTE: an important side-effect of this operation is that an SI invalidation
3639 * message is sent out to all backends --- including me --- causing plans
3640 * referencing the relation to be rebuilt with the new list of children.
3641 * This must happen even if we find that no change is needed in the pg_class
3642 * row.
3643 */
3644void
3645SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3646{
3647 Relation relationRelation;
3648 HeapTuple tuple;
3649 Form_pg_class classtuple;
3650
3652 ShareUpdateExclusiveLock, false) ||
3653 CheckRelationOidLockedByMe(relationId,
3654 ShareRowExclusiveLock, true));
3655
3656 /*
3657 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3658 */
3659 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3660 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3661 if (!HeapTupleIsValid(tuple))
3662 elog(ERROR, "cache lookup failed for relation %u", relationId);
3663 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3664
3665 if (classtuple->relhassubclass != relhassubclass)
3666 {
3667 classtuple->relhassubclass = relhassubclass;
3668 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3669 }
3670 else
3671 {
3672 /* no need to change tuple, but force relcache rebuild anyway */
3674 }
3675
3676 heap_freetuple(tuple);
3677 table_close(relationRelation, RowExclusiveLock);
3678}
3679
3680/*
3681 * CheckRelationTableSpaceMove
3682 * Check if relation can be moved to new tablespace.
3683 *
3684 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3685 *
3686 * Returns true if the relation can be moved to the new tablespace; raises
3687 * an error if it is not possible to do the move; returns false if the move
3688 * would have no effect.
3689 */
3690bool
3692{
3693 Oid oldTableSpaceId;
3694
3695 /*
3696 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3697 * stored as 0.
3698 */
3699 oldTableSpaceId = rel->rd_rel->reltablespace;
3700 if (newTableSpaceId == oldTableSpaceId ||
3701 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3702 return false;
3703
3704 /*
3705 * We cannot support moving mapped relations into different tablespaces.
3706 * (In particular this eliminates all shared catalogs.)
3707 */
3708 if (RelationIsMapped(rel))
3709 ereport(ERROR,
3710 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3711 errmsg("cannot move system relation \"%s\"",
3713
3714 /* Cannot move a non-shared relation into pg_global */
3715 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3716 ereport(ERROR,
3717 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3718 errmsg("only shared relations can be placed in pg_global tablespace")));
3719
3720 /*
3721 * Do not allow moving temp tables of other backends ... their local
3722 * buffer manager is not going to cope.
3723 */
3724 if (RELATION_IS_OTHER_TEMP(rel))
3725 ereport(ERROR,
3726 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3727 errmsg("cannot move temporary tables of other sessions")));
3728
3729 return true;
3730}
3731
3732/*
3733 * SetRelationTableSpace
3734 * Set new reltablespace and relfilenumber in pg_class entry.
3735 *
3736 * newTableSpaceId is the new tablespace for the relation, and
3737 * newRelFilenumber its new filenumber. If newRelFilenumber is
3738 * InvalidRelFileNumber, this field is not updated.
3739 *
3740 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3741 *
3742 * The caller of this routine had better check if a relation can be
3743 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3744 * first, and is responsible for making the change visible with
3745 * CommandCounterIncrement().
3746 */
3747void
3749 Oid newTableSpaceId,
3750 RelFileNumber newRelFilenumber)
3751{
3752 Relation pg_class;
3753 HeapTuple tuple;
3754 ItemPointerData otid;
3755 Form_pg_class rd_rel;
3756 Oid reloid = RelationGetRelid(rel);
3757
3758 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3759
3760 /* Get a modifiable copy of the relation's pg_class row. */
3761 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3762
3763 tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3764 if (!HeapTupleIsValid(tuple))
3765 elog(ERROR, "cache lookup failed for relation %u", reloid);
3766 otid = tuple->t_self;
3767 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3768
3769 /* Update the pg_class row. */
3770 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3771 InvalidOid : newTableSpaceId;
3772 if (RelFileNumberIsValid(newRelFilenumber))
3773 rd_rel->relfilenode = newRelFilenumber;
3774 CatalogTupleUpdate(pg_class, &otid, tuple);
3775 UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3776
3777 /*
3778 * Record dependency on tablespace. This is only required for relations
3779 * that have no physical storage.
3780 */
3781 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3782 changeDependencyOnTablespace(RelationRelationId, reloid,
3783 rd_rel->reltablespace);
3784
3785 heap_freetuple(tuple);
3786 table_close(pg_class, RowExclusiveLock);
3787}
3788
3789/*
3790 * renameatt_check - basic sanity checks before attribute rename
3791 */
3792static void
3793renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3794{
3795 char relkind = classform->relkind;
3796
3797 if (classform->reloftype && !recursing)
3798 ereport(ERROR,
3799 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3800 errmsg("cannot rename column of typed table")));
3801
3802 /*
3803 * Renaming the columns of sequences or toast tables doesn't actually
3804 * break anything from the system's point of view, since internal
3805 * references are by attnum. But it doesn't seem right to allow users to
3806 * change names that are hardcoded into the system, hence the following
3807 * restriction.
3808 */
3809 if (relkind != RELKIND_RELATION &&
3810 relkind != RELKIND_VIEW &&
3811 relkind != RELKIND_MATVIEW &&
3812 relkind != RELKIND_COMPOSITE_TYPE &&
3813 relkind != RELKIND_INDEX &&
3814 relkind != RELKIND_PARTITIONED_INDEX &&
3815 relkind != RELKIND_FOREIGN_TABLE &&
3816 relkind != RELKIND_PARTITIONED_TABLE)
3817 ereport(ERROR,
3818 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3819 errmsg("cannot rename columns of relation \"%s\"",
3820 NameStr(classform->relname)),
3822
3823 /*
3824 * permissions checking. only the owner of a class can change its schema.
3825 */
3826 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3828 NameStr(classform->relname));
3829 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3830 ereport(ERROR,
3831 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3832 errmsg("permission denied: \"%s\" is a system catalog",
3833 NameStr(classform->relname))));
3834}
3835
3836/*
3837 * renameatt_internal - workhorse for renameatt
3838 *
3839 * Return value is the attribute number in the 'myrelid' relation.
3840 */
3841static AttrNumber
3843 const char *oldattname,
3844 const char *newattname,
3845 bool recurse,
3846 bool recursing,
3847 int expected_parents,
3848 DropBehavior behavior)
3849{
3850 Relation targetrelation;
3851 Relation attrelation;
3852 HeapTuple atttup;
3853 Form_pg_attribute attform;
3855
3856 /*
3857 * Grab an exclusive lock on the target table, which we will NOT release
3858 * until end of transaction.
3859 */
3860 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3861 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3862
3863 /*
3864 * if the 'recurse' flag is set then we are supposed to rename this
3865 * attribute in all classes that inherit from 'relname' (as well as in
3866 * 'relname').
3867 *
3868 * any permissions or problems with duplicate attributes will cause the
3869 * whole transaction to abort, which is what we want -- all or nothing.
3870 */
3871 if (recurse)
3872 {
3873 List *child_oids,
3874 *child_numparents;
3875 ListCell *lo,
3876 *li;
3877
3878 /*
3879 * we need the number of parents for each child so that the recursive
3880 * calls to renameatt() can determine whether there are any parents
3881 * outside the inheritance hierarchy being processed.
3882 */
3883 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3884 &child_numparents);
3885
3886 /*
3887 * find_all_inheritors does the recursive search of the inheritance
3888 * hierarchy, so all we have to do is process all of the relids in the
3889 * list that it returns.
3890 */
3891 forboth(lo, child_oids, li, child_numparents)
3892 {
3893 Oid childrelid = lfirst_oid(lo);
3894 int numparents = lfirst_int(li);
3895
3896 if (childrelid == myrelid)
3897 continue;
3898 /* note we need not recurse again */
3899 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3900 }
3901 }
3902 else
3903 {
3904 /*
3905 * If we are told not to recurse, there had better not be any child
3906 * tables; else the rename would put them out of step.
3907 *
3908 * expected_parents will only be 0 if we are not already recursing.
3909 */
3910 if (expected_parents == 0 &&
3912 ereport(ERROR,
3913 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3914 errmsg("inherited column \"%s\" must be renamed in child tables too",
3915 oldattname)));
3916 }
3917
3918 /* rename attributes in typed tables of composite type */
3919 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3920 {
3921 List *child_oids;
3922 ListCell *lo;
3923
3924 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3925 RelationGetRelationName(targetrelation),
3926 behavior);
3927
3928 foreach(lo, child_oids)
3929 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3930 }
3931
3932 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3933
3934 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3935 if (!HeapTupleIsValid(atttup))
3936 ereport(ERROR,
3937 (errcode(ERRCODE_UNDEFINED_COLUMN),
3938 errmsg("column \"%s\" does not exist",
3939 oldattname)));
3940 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3941
3942 attnum = attform->attnum;
3943 if (attnum <= 0)
3944 ereport(ERROR,
3945 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3946 errmsg("cannot rename system column \"%s\"",
3947 oldattname)));
3948
3949 /*
3950 * if the attribute is inherited, forbid the renaming. if this is a
3951 * top-level call to renameatt(), then expected_parents will be 0, so the
3952 * effect of this code will be to prohibit the renaming if the attribute
3953 * is inherited at all. if this is a recursive call to renameatt(),
3954 * expected_parents will be the number of parents the current relation has
3955 * within the inheritance hierarchy being processed, so we'll prohibit the
3956 * renaming only if there are additional parents from elsewhere.
3957 */
3958 if (attform->attinhcount > expected_parents)
3959 ereport(ERROR,
3960 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3961 errmsg("cannot rename inherited column \"%s\"",
3962 oldattname)));
3963
3964 /* new name should not already exist */
3965 (void) check_for_column_name_collision(targetrelation, newattname, false);
3966
3967 /* apply the update */
3968 namestrcpy(&(attform->attname), newattname);
3969
3970 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3971
3972 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3973
3974 heap_freetuple(atttup);
3975
3976 table_close(attrelation, RowExclusiveLock);
3977
3978 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3979
3980 return attnum;
3981}
3982
3983/*
3984 * Perform permissions and integrity checks before acquiring a relation lock.
3985 */
3986static void
3988 void *arg)
3989{
3990 HeapTuple tuple;
3991 Form_pg_class form;
3992
3993 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3994 if (!HeapTupleIsValid(tuple))
3995 return; /* concurrently dropped */
3996 form = (Form_pg_class) GETSTRUCT(tuple);
3997 renameatt_check(relid, form, false);
3998 ReleaseSysCache(tuple);
3999}
4000
4001/*
4002 * renameatt - changes the name of an attribute in a relation
4003 *
4004 * The returned ObjectAddress is that of the renamed column.
4005 */
4008{
4009 Oid relid;
4011 ObjectAddress address;
4012
4013 /* lock level taken here should match renameatt_internal */
4015 stmt->missing_ok ? RVR_MISSING_OK : 0,
4017 NULL);
4018
4019 if (!OidIsValid(relid))
4020 {
4022 (errmsg("relation \"%s\" does not exist, skipping",
4023 stmt->relation->relname)));
4024 return InvalidObjectAddress;
4025 }
4026
4027 attnum =
4028 renameatt_internal(relid,
4029 stmt->subname, /* old att name */
4030 stmt->newname, /* new att name */
4031 stmt->relation->inh, /* recursive? */
4032 false, /* recursing? */
4033 0, /* expected inhcount */
4034 stmt->behavior);
4035
4036 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4037
4038 return address;
4039}
4040
4041/*
4042 * same logic as renameatt_internal
4043 */
4044static ObjectAddress
4046 Oid mytypid,
4047 const char *oldconname,
4048 const char *newconname,
4049 bool recurse,
4050 bool recursing,
4051 int expected_parents)
4052{
4053 Relation targetrelation = NULL;
4054 Oid constraintOid;
4055 HeapTuple tuple;
4057 ObjectAddress address;
4058
4059 Assert(!myrelid || !mytypid);
4060
4061 if (mytypid)
4062 {
4063 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4064 }
4065 else
4066 {
4067 targetrelation = relation_open(myrelid, AccessExclusiveLock);
4068
4069 /*
4070 * don't tell it whether we're recursing; we allow changing typed
4071 * tables here
4072 */
4073 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4074
4075 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4076 }
4077
4078 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4079 if (!HeapTupleIsValid(tuple))
4080 elog(ERROR, "cache lookup failed for constraint %u",
4081 constraintOid);
4082 con = (Form_pg_constraint) GETSTRUCT(tuple);
4083
4084 if (myrelid &&
4085 (con->contype == CONSTRAINT_CHECK ||
4086 con->contype == CONSTRAINT_NOTNULL) &&
4087 !con->connoinherit)
4088 {
4089 if (recurse)
4090 {
4091 List *child_oids,
4092 *child_numparents;
4093 ListCell *lo,
4094 *li;
4095
4096 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4097 &child_numparents);
4098
4099 forboth(lo, child_oids, li, child_numparents)
4100 {
4101 Oid childrelid = lfirst_oid(lo);
4102 int numparents = lfirst_int(li);
4103
4104 if (childrelid == myrelid)
4105 continue;
4106
4107 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4108 }
4109 }
4110 else
4111 {
4112 if (expected_parents == 0 &&
4114 ereport(ERROR,
4115 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4116 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4117 oldconname)));
4118 }
4119
4120 if (con->coninhcount > expected_parents)
4121 ereport(ERROR,
4122 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4123 errmsg("cannot rename inherited constraint \"%s\"",
4124 oldconname)));
4125 }
4126
4127 if (con->conindid
4128 && (con->contype == CONSTRAINT_PRIMARY
4129 || con->contype == CONSTRAINT_UNIQUE
4130 || con->contype == CONSTRAINT_EXCLUSION))
4131 /* rename the index; this renames the constraint as well */
4132 RenameRelationInternal(con->conindid, newconname, false, true);
4133 else
4134 RenameConstraintById(constraintOid, newconname);
4135
4136 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4137
4138 ReleaseSysCache(tuple);
4139
4140 if (targetrelation)
4141 {
4142 /*
4143 * Invalidate relcache so as others can see the new constraint name.
4144 */
4145 CacheInvalidateRelcache(targetrelation);
4146
4147 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4148 }
4149
4150 return address;
4151}
4152
4155{
4156 Oid relid = InvalidOid;
4157 Oid typid = InvalidOid;
4158
4159 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4160 {
4161 Relation rel;
4162 HeapTuple tup;
4163
4164 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4165 rel = table_open(TypeRelationId, RowExclusiveLock);
4166 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4167 if (!HeapTupleIsValid(tup))
4168 elog(ERROR, "cache lookup failed for type %u", typid);
4169 checkDomainOwner(tup);
4170 ReleaseSysCache(tup);
4171 table_close(rel, NoLock);
4172 }
4173 else
4174 {
4175 /* lock level taken here should match rename_constraint_internal */
4177 stmt->missing_ok ? RVR_MISSING_OK : 0,
4179 NULL);
4180 if (!OidIsValid(relid))
4181 {
4183 (errmsg("relation \"%s\" does not exist, skipping",
4184 stmt->relation->relname)));
4185 return InvalidObjectAddress;
4186 }
4187 }
4188
4189 return
4190 rename_constraint_internal(relid, typid,
4191 stmt->subname,
4192 stmt->newname,
4193 (stmt->relation &&
4194 stmt->relation->inh), /* recursive? */
4195 false, /* recursing? */
4196 0 /* expected inhcount */ );
4197}
4198
4199/*
4200 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4201 * RENAME
4202 */
4205{
4206 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4207 Oid relid;
4208 ObjectAddress address;
4209
4210 /*
4211 * Grab an exclusive lock on the target table, index, sequence, view,
4212 * materialized view, or foreign table, which we will NOT release until
4213 * end of transaction.
4214 *
4215 * Lock level used here should match RenameRelationInternal, to avoid lock
4216 * escalation. However, because ALTER INDEX can be used with any relation
4217 * type, we mustn't believe without verification.
4218 */
4219 for (;;)
4220 {
4221 LOCKMODE lockmode;
4222 char relkind;
4223 bool obj_is_index;
4224
4225 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4226
4227 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4228 stmt->missing_ok ? RVR_MISSING_OK : 0,
4230 stmt);
4231
4232 if (!OidIsValid(relid))
4233 {
4235 (errmsg("relation \"%s\" does not exist, skipping",
4236 stmt->relation->relname)));
4237 return InvalidObjectAddress;
4238 }
4239
4240 /*
4241 * We allow mismatched statement and object types (e.g., ALTER INDEX
4242 * to rename a table), but we might've used the wrong lock level. If
4243 * that happens, retry with the correct lock level. We don't bother
4244 * if we already acquired AccessExclusiveLock with an index, however.
4245 */
4246 relkind = get_rel_relkind(relid);
4247 obj_is_index = (relkind == RELKIND_INDEX ||
4248 relkind == RELKIND_PARTITIONED_INDEX);
4249 if (obj_is_index || is_index_stmt == obj_is_index)
4250 break;
4251
4252 UnlockRelationOid(relid, lockmode);
4253 is_index_stmt = obj_is_index;
4254 }
4255
4256 /* Do the work */
4257 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4258
4259 ObjectAddressSet(address, RelationRelationId, relid);
4260
4261 return address;
4262}
4263
4264/*
4265 * RenameRelationInternal - change the name of a relation
4266 */
4267void
4268RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4269{
4270 Relation targetrelation;
4271 Relation relrelation; /* for RELATION relation */
4272 ItemPointerData otid;
4273 HeapTuple reltup;
4274 Form_pg_class relform;
4275 Oid namespaceId;
4276
4277 /*
4278 * Grab a lock on the target relation, which we will NOT release until end
4279 * of transaction. We need at least a self-exclusive lock so that
4280 * concurrent DDL doesn't overwrite the rename if they start updating
4281 * while still seeing the old version. The lock also guards against
4282 * triggering relcache reloads in concurrent sessions, which might not
4283 * handle this information changing under them. For indexes, we can use a
4284 * reduced lock level because RelationReloadIndexInfo() handles indexes
4285 * specially.
4286 */
4287 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4288 namespaceId = RelationGetNamespace(targetrelation);
4289
4290 /*
4291 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4292 */
4293 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4294
4295 reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4296 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4297 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4298 otid = reltup->t_self;
4299 relform = (Form_pg_class) GETSTRUCT(reltup);
4300
4301 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4302 ereport(ERROR,
4303 (errcode(ERRCODE_DUPLICATE_TABLE),
4304 errmsg("relation \"%s\" already exists",
4305 newrelname)));
4306
4307 /*
4308 * RenameRelation is careful not to believe the caller's idea of the
4309 * relation kind being handled. We don't have to worry about this, but
4310 * let's not be totally oblivious to it. We can process an index as
4311 * not-an-index, but not the other way around.
4312 */
4313 Assert(!is_index ||
4314 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4315 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4316
4317 /*
4318 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4319 * because it's a copy...)
4320 */
4321 namestrcpy(&(relform->relname), newrelname);
4322
4323 CatalogTupleUpdate(relrelation, &otid, reltup);
4324 UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4325
4326 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4327 InvalidOid, is_internal);
4328
4329 heap_freetuple(reltup);
4330 table_close(relrelation, RowExclusiveLock);
4331
4332 /*
4333 * Also rename the associated type, if any.
4334 */
4335 if (OidIsValid(targetrelation->rd_rel->reltype))
4336 RenameTypeInternal(targetrelation->rd_rel->reltype,
4337 newrelname, namespaceId);
4338
4339 /*
4340 * Also rename the associated constraint, if any.
4341 */
4342 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4343 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4344 {
4345 Oid constraintId = get_index_constraint(myrelid);
4346
4347 if (OidIsValid(constraintId))
4348 RenameConstraintById(constraintId, newrelname);
4349 }
4350
4351 /*
4352 * Close rel, but keep lock!
4353 */
4354 relation_close(targetrelation, NoLock);
4355}
4356
4357/*
4358 * ResetRelRewrite - reset relrewrite
4359 */
4360void
4362{
4363 Relation relrelation; /* for RELATION relation */
4364 HeapTuple reltup;
4365 Form_pg_class relform;
4366
4367 /*
4368 * Find relation's pg_class tuple.
4369 */
4370 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4371
4372 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4373 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4374 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4375 relform = (Form_pg_class) GETSTRUCT(reltup);
4376
4377 /*
4378 * Update pg_class tuple.
4379 */
4380 relform->relrewrite = InvalidOid;
4381
4382 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4383
4384 heap_freetuple(reltup);
4385 table_close(relrelation, RowExclusiveLock);
4386}
4387
4388/*
4389 * Disallow ALTER TABLE (and similar commands) when the current backend has
4390 * any open reference to the target table besides the one just acquired by
4391 * the calling command; this implies there's an open cursor or active plan.
4392 * We need this check because our lock doesn't protect us against stomping
4393 * on our own foot, only other people's feet!
4394 *
4395 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4396 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4397 * possibly be relaxed to only error out for certain types of alterations.
4398 * But the use-case for allowing any of these things is not obvious, so we
4399 * won't work hard at it for now.
4400 *
4401 * We also reject these commands if there are any pending AFTER trigger events
4402 * for the rel. This is certainly necessary for the rewriting variants of
4403 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4404 * events would try to fetch the wrong tuples. It might be overly cautious
4405 * in other cases, but again it seems better to err on the side of paranoia.
4406 *
4407 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4408 * we are worried about active indexscans on the index. The trigger-event
4409 * check can be skipped, since we are doing no damage to the parent table.
4410 *
4411 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4412 */
4413void
4415{
4416 int expected_refcnt;
4417
4418 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4419 if (rel->rd_refcnt != expected_refcnt)
4420 ereport(ERROR,
4421 (errcode(ERRCODE_OBJECT_IN_USE),
4422 /* translator: first %s is a SQL command, eg ALTER TABLE */
4423 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4425
4426 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4427 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4429 ereport(ERROR,
4430 (errcode(ERRCODE_OBJECT_IN_USE),
4431 /* translator: first %s is a SQL command, eg ALTER TABLE */
4432 errmsg("cannot %s \"%s\" because it has pending trigger events",
4434}
4435
4436/*
4437 * CheckAlterTableIsSafe
4438 * Verify that it's safe to allow ALTER TABLE on this relation.
4439 *
4440 * This consists of CheckTableNotInUse() plus a check that the relation
4441 * isn't another session's temp table. We must split out the temp-table
4442 * check because there are callers of CheckTableNotInUse() that don't want
4443 * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4444 * an orphaned temp schema.) Compare truncate_check_activity().
4445 */
4446static void
4448{
4449 /*
4450 * Don't allow ALTER on temp tables of other backends. Their local buffer
4451 * manager is not going to cope if we need to change the table's contents.
4452 * Even if we don't, there may be optimizations that assume temp tables
4453 * aren't subject to such interference.
4454 */
4455 if (RELATION_IS_OTHER_TEMP(rel))
4456 ereport(ERROR,
4457 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4458 errmsg("cannot alter temporary tables of other sessions")));
4459
4460 /*
4461 * Also check for active uses of the relation in the current transaction,
4462 * including open scans and pending AFTER trigger events.
4463 */
4464 CheckTableNotInUse(rel, "ALTER TABLE");
4465}
4466
4467/*
4468 * AlterTableLookupRelation
4469 * Look up, and lock, the OID for the relation named by an alter table
4470 * statement.
4471 */
4472Oid
4474{
4475 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4476 stmt->missing_ok ? RVR_MISSING_OK : 0,
4478 stmt);
4479}
4480
4481/*
4482 * AlterTable
4483 * Execute ALTER TABLE, which can be a list of subcommands
4484 *
4485 * ALTER TABLE is performed in three phases:
4486 * 1. Examine subcommands and perform pre-transformation checking.
4487 * 2. Validate and transform subcommands, and update system catalogs.
4488 * 3. Scan table(s) to check new constraints, and optionally recopy
4489 * the data into new table(s).
4490 * Phase 3 is not performed unless one or more of the subcommands requires
4491 * it. The intention of this design is to allow multiple independent
4492 * updates of the table schema to be performed with only one pass over the
4493 * data.
4494 *
4495 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4496 * each table to be affected (there may be multiple affected tables if the
4497 * commands traverse a table inheritance hierarchy). Also we do preliminary
4498 * validation of the subcommands. Because earlier subcommands may change
4499 * the catalog state seen by later commands, there are limits to what can
4500 * be done in this phase. Generally, this phase acquires table locks,
4501 * checks permissions and relkind, and recurses to find child tables.
4502 *
4503 * ATRewriteCatalogs performs phase 2 for each affected table.
4504 * Certain subcommands need to be performed before others to avoid
4505 * unnecessary conflicts; for example, DROP COLUMN should come before
4506 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4507 * lists, one for each logical "pass" of phase 2.
4508 *
4509 * ATRewriteTables performs phase 3 for those tables that need it.
4510 *
4511 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4512 * since phase 1 already does it. However, for certain subcommand types
4513 * it is only possible to determine how to recurse at phase 2 time; for
4514 * those cases, phase 1 sets the cmd->recurse flag.
4515 *
4516 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4517 * the whole operation; we don't have to do anything special to clean up.
4518 *
4519 * The caller must lock the relation, with an appropriate lock level
4520 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4521 * or higher. We pass the lock level down
4522 * so that we can apply it recursively to inherited tables. Note that the
4523 * lock level we want as we recurse might well be higher than required for
4524 * that specific subcommand. So we pass down the overall lock requirement,
4525 * rather than reassess it at lower levels.
4526 *
4527 * The caller also provides a "context" which is to be passed back to
4528 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4529 * Some of the fields therein, such as the relid, are used here as well.
4530 */
4531void
4533 AlterTableUtilityContext *context)
4534{
4535 Relation rel;
4536
4537 /* Caller is required to provide an adequate lock. */
4538 rel = relation_open(context->relid, NoLock);
4539
4541
4542 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4543}
4544
4545/*
4546 * AlterTableInternal
4547 *
4548 * ALTER TABLE with target specified by OID
4549 *
4550 * We do not reject if the relation is already open, because it's quite
4551 * likely that one or more layers of caller have it open. That means it
4552 * is unsafe to use this entry point for alterations that could break
4553 * existing query plans. On the assumption it's not used for such, we
4554 * don't have to reject pending AFTER triggers, either.
4555 *
4556 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4557 * used for any subcommand types that require parse transformation or
4558 * could generate subcommands that have to be passed to ProcessUtility.
4559 */
4560void
4561AlterTableInternal(Oid relid, List *cmds, bool recurse)
4562{
4563 Relation rel;
4564 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4565
4566 rel = relation_open(relid, lockmode);
4567
4569
4570 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4571}
4572
4573/*
4574 * AlterTableGetLockLevel
4575 *
4576 * Sets the overall lock level required for the supplied list of subcommands.
4577 * Policy for doing this set according to needs of AlterTable(), see
4578 * comments there for overall explanation.
4579 *
4580 * Function is called before and after parsing, so it must give same
4581 * answer each time it is called. Some subcommands are transformed
4582 * into other subcommand types, so the transform must never be made to a
4583 * lower lock level than previously assigned. All transforms are noted below.
4584 *
4585 * Since this is called before we lock the table we cannot use table metadata
4586 * to influence the type of lock we acquire.
4587 *
4588 * There should be no lockmodes hardcoded into the subcommand functions. All
4589 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4590 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4591 * and does not travel through this section of code and cannot be combined with
4592 * any of the subcommands given here.
4593 *
4594 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4595 * so any changes that might affect SELECTs running on standbys need to use
4596 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4597 * have a solution for that also.
4598 *
4599 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4600 * that takes a lock less than AccessExclusiveLock can change object definitions
4601 * while pg_dump is running. Be careful to check that the appropriate data is
4602 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4603 * otherwise we might end up with an inconsistent dump that can't restore.
4604 */
4607{
4608 /*
4609 * This only works if we read catalog tables using MVCC snapshots.
4610 */
4611 ListCell *lcmd;
4613
4614 foreach(lcmd, cmds)
4615 {
4616 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4617 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4618
4619 switch (cmd->subtype)
4620 {
4621 /*
4622 * These subcommands rewrite the heap, so require full locks.
4623 */
4624 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4625 * to SELECT */
4626 case AT_SetAccessMethod: /* must rewrite heap */
4627 case AT_SetTableSpace: /* must rewrite heap */
4628 case AT_AlterColumnType: /* must rewrite heap */
4629 cmd_lockmode = AccessExclusiveLock;
4630 break;
4631
4632 /*
4633 * These subcommands may require addition of toast tables. If
4634 * we add a toast table to a table currently being scanned, we
4635 * might miss data added to the new toast table by concurrent
4636 * insert transactions.
4637 */
4638 case AT_SetStorage: /* may add toast tables, see
4639 * ATRewriteCatalogs() */
4640 cmd_lockmode = AccessExclusiveLock;
4641 break;
4642
4643 /*
4644 * Removing constraints can affect SELECTs that have been
4645 * optimized assuming the constraint holds true. See also
4646 * CloneFkReferenced.
4647 */
4648 case AT_DropConstraint: /* as DROP INDEX */
4649 case AT_DropNotNull: /* may change some SQL plans */
4650 cmd_lockmode = AccessExclusiveLock;
4651 break;
4652
4653 /*
4654 * Subcommands that may be visible to concurrent SELECTs
4655 */
4656 case AT_DropColumn: /* change visible to SELECT */
4657 case AT_AddColumnToView: /* CREATE VIEW */
4658 case AT_DropOids: /* used to equiv to DropColumn */
4659 case AT_EnableAlwaysRule: /* may change SELECT rules */
4660 case AT_EnableReplicaRule: /* may change SELECT rules */
4661 case AT_EnableRule: /* may change SELECT rules */
4662 case AT_DisableRule: /* may change SELECT rules */
4663 cmd_lockmode = AccessExclusiveLock;
4664 break;
4665
4666 /*
4667 * Changing owner may remove implicit SELECT privileges
4668 */
4669 case AT_ChangeOwner: /* change visible to SELECT */
4670 cmd_lockmode = AccessExclusiveLock;
4671 break;
4672
4673 /*
4674 * Changing foreign table options may affect optimization.
4675 */
4676 case AT_GenericOptions:
4678 cmd_lockmode = AccessExclusiveLock;
4679 break;
4680
4681 /*
4682 * These subcommands affect write operations only.
4683 */
4684 case AT_EnableTrig:
4687 case AT_EnableTrigAll:
4688 case AT_EnableTrigUser:
4689 case AT_DisableTrig:
4690 case AT_DisableTrigAll:
4691 case AT_DisableTrigUser:
4692 cmd_lockmode = ShareRowExclusiveLock;
4693 break;
4694
4695 /*
4696 * These subcommands affect write operations only. XXX
4697 * Theoretically, these could be ShareRowExclusiveLock.
4698 */
4699 case AT_ColumnDefault:
4701 case AT_AlterConstraint:
4702 case AT_AddIndex: /* from ADD CONSTRAINT */
4704 case AT_ReplicaIdentity:
4705 case AT_SetNotNull:
4710 case AT_AddIdentity:
4711 case AT_DropIdentity:
4712 case AT_SetIdentity:
4713 case AT_SetExpression:
4714 case AT_DropExpression:
4715 case AT_SetCompression:
4716 cmd_lockmode = AccessExclusiveLock;
4717 break;
4718
4719 case AT_AddConstraint:
4720 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4721 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4722 if (IsA(cmd->def, Constraint))
4723 {
4724 Constraint *con = (Constraint *) cmd->def;
4725
4726 switch (con->contype)
4727 {
4728 case CONSTR_EXCLUSION:
4729 case CONSTR_PRIMARY:
4730 case CONSTR_UNIQUE:
4731
4732 /*
4733 * Cases essentially the same as CREATE INDEX. We
4734 * could reduce the lock strength to ShareLock if
4735 * we can work out how to allow concurrent catalog
4736 * updates. XXX Might be set down to
4737 * ShareRowExclusiveLock but requires further
4738 * analysis.
4739 */
4740 cmd_lockmode = AccessExclusiveLock;
4741 break;
4742 case CONSTR_FOREIGN:
4743
4744 /*
4745 * We add triggers to both tables when we add a
4746 * Foreign Key, so the lock level must be at least
4747 * as strong as CREATE TRIGGER.
4748 */
4749 cmd_lockmode = ShareRowExclusiveLock;
4750 break;
4751
4752 default:
4753 cmd_lockmode = AccessExclusiveLock;
4754 }
4755 }
4756 break;
4757
4758 /*
4759 * These subcommands affect inheritance behaviour. Queries
4760 * started before us will continue to see the old inheritance
4761 * behaviour, while queries started after we commit will see
4762 * new behaviour. No need to prevent reads or writes to the
4763 * subtable while we hook it up though. Changing the TupDesc
4764 * may be a problem, so keep highest lock.
4765 */
4766 case AT_AddInherit:
4767 case AT_DropInherit:
4768 cmd_lockmode = AccessExclusiveLock;
4769 break;
4770
4771 /*
4772 * These subcommands affect implicit row type conversion. They
4773 * have affects similar to CREATE/DROP CAST on queries. don't
4774 * provide for invalidating parse trees as a result of such
4775 * changes, so we keep these at AccessExclusiveLock.
4776 */
4777 case AT_AddOf:
4778 case AT_DropOf:
4779 cmd_lockmode = AccessExclusiveLock;
4780 break;
4781
4782 /*
4783 * Only used by CREATE OR REPLACE VIEW which must conflict
4784 * with an SELECTs currently using the view.
4785 */
4787 cmd_lockmode = AccessExclusiveLock;
4788 break;
4789
4790 /*
4791 * These subcommands affect general strategies for performance
4792 * and maintenance, though don't change the semantic results
4793 * from normal data reads and writes. Delaying an ALTER TABLE
4794 * behind currently active writes only delays the point where
4795 * the new strategy begins to take effect, so there is no
4796 * benefit in waiting. In this case the minimum restriction
4797 * applies: we don't currently allow concurrent catalog
4798 * updates.
4799 */
4800 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4801 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4802 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4803 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4804 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4805 cmd_lockmode = ShareUpdateExclusiveLock;
4806 break;
4807
4808 case AT_SetLogged:
4809 case AT_SetUnLogged:
4810 cmd_lockmode = AccessExclusiveLock;
4811 break;
4812
4813 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4814 cmd_lockmode = ShareUpdateExclusiveLock;
4815 break;
4816
4817 /*
4818 * Rel options are more complex than first appears. Options
4819 * are set here for tables, views and indexes; for historical
4820 * reasons these can all be used with ALTER TABLE, so we can't
4821 * decide between them using the basic grammar.
4822 */
4823 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4824 * getTables() */
4825 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4826 * getTables() */
4827 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4828 break;
4829
4830 case AT_AttachPartition:
4831 cmd_lockmode = ShareUpdateExclusiveLock;
4832 break;
4833
4834 case AT_DetachPartition:
4835 if (((PartitionCmd *) cmd->def)->concurrent)
4836 cmd_lockmode = ShareUpdateExclusiveLock;
4837 else
4838 cmd_lockmode = AccessExclusiveLock;
4839 break;
4840
4842 cmd_lockmode = ShareUpdateExclusiveLock;
4843 break;
4844
4845 case AT_MergePartitions:
4846 case AT_SplitPartition:
4847 cmd_lockmode = AccessExclusiveLock;
4848 break;
4849
4850 default: /* oops */
4851 elog(ERROR, "unrecognized alter table type: %d",
4852 (int) cmd->subtype);
4853 break;
4854 }
4855
4856 /*
4857 * Take the greatest lockmode from any subcommand
4858 */
4859 if (cmd_lockmode > lockmode)
4860 lockmode = cmd_lockmode;
4861 }
4862
4863 return lockmode;
4864}
4865
4866/*
4867 * ATController provides top level control over the phases.
4868 *
4869 * parsetree is passed in to allow it to be passed to event triggers
4870 * when requested.
4871 */
4872static void
4874 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4875 AlterTableUtilityContext *context)
4876{
4877 List *wqueue = NIL;
4878 ListCell *lcmd;
4879
4880 /* Phase 1: preliminary examination of commands, create work queue */
4881 foreach(lcmd, cmds)
4882 {
4883 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4884
4885 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4886 }
4887
4888 /* Close the relation, but keep lock until commit */
4889 relation_close(rel, NoLock);
4890
4891 /* Phase 2: update system catalogs */
4892 ATRewriteCatalogs(&wqueue, lockmode, context);
4893
4894 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4895 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4896}
4897
4898/*
4899 * ATPrepCmd
4900 *
4901 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4902 * recursion and permission checks.
4903 *
4904 * Caller must have acquired appropriate lock type on relation already.
4905 * This lock should be held until commit.
4906 */
4907static void
4909 bool recurse, bool recursing, LOCKMODE lockmode,
4910 AlterTableUtilityContext *context)
4911{
4912 AlteredTableInfo *tab;
4914
4915 /* Find or create work queue entry for this table */
4916 tab = ATGetQueueEntry(wqueue, rel);
4917
4918 /*
4919 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4920 * partitions that are pending detach.
4921 */
4922 if (rel->rd_rel->relispartition &&
4925 ereport(ERROR,
4926 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4927 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4929 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4930
4931 /*
4932 * Copy the original subcommand for each table, so we can scribble on it.
4933 * This avoids conflicts when different child tables need to make
4934 * different parse transformations (for example, the same column may have
4935 * different column numbers in different children).
4936 */
4937 cmd = copyObject(cmd);
4938
4939 /*
4940 * Do permissions and relkind checking, recursion to child tables if
4941 * needed, and any additional phase-1 processing needed. (But beware of
4942 * adding any processing that looks at table details that another
4943 * subcommand could change. In some cases we reject multiple subcommands
4944 * that could try to change the same state in contrary ways.)
4945 */
4946 switch (cmd->subtype)
4947 {
4948 case AT_AddColumn: /* ADD COLUMN */
4949 ATSimplePermissions(cmd->subtype, rel,
4952 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4953 lockmode, context);
4954 /* Recursion occurs during execution phase */
4955 pass = AT_PASS_ADD_COL;
4956 break;
4957 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4959 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4960 lockmode, context);
4961 /* Recursion occurs during execution phase */
4962 pass = AT_PASS_ADD_COL;
4963 break;
4964 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4965
4966 /*
4967 * We allow defaults on views so that INSERT into a view can have
4968 * default-ish behavior. This works because the rewriter
4969 * substitutes default values into INSERTs before it expands
4970 * rules.
4971 */
4972 ATSimplePermissions(cmd->subtype, rel,
4975 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4976 /* No command-specific prep needed */
4978 break;
4979 case AT_CookedColumnDefault: /* add a pre-cooked default */
4980 /* This is currently used only in CREATE TABLE */
4981 /* (so the permission check really isn't necessary) */
4982 ATSimplePermissions(cmd->subtype, rel,
4984 /* This command never recurses */
4986 break;
4987 case AT_AddIdentity:
4988 ATSimplePermissions(cmd->subtype, rel,
4991 /* Set up recursion for phase 2; no other prep needed */
4992 if (recurse)
4993 cmd->recurse = true;
4995 break;
4996 case AT_SetIdentity:
4997 ATSimplePermissions(cmd->subtype, rel,
5000 /* Set up recursion for phase 2; no other prep needed */
5001 if (recurse)
5002 cmd->recurse = true;
5003 /* This should run after AddIdentity, so do it in MISC pass */
5004 pass = AT_PASS_MISC;
5005 break;
5006 case AT_DropIdentity:
5007 ATSimplePermissions(cmd->subtype, rel,
5010 /* Set up recursion for phase 2; no other prep needed */
5011 if (recurse)
5012 cmd->recurse = true;
5013 pass = AT_PASS_DROP;
5014 break;
5015 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5016 ATSimplePermissions(cmd->subtype, rel,
5018 /* Set up recursion for phase 2; no other prep needed */
5019 if (recurse)
5020 cmd->recurse = true;
5021 pass = AT_PASS_DROP;
5022 break;
5023 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5024 ATSimplePermissions(cmd->subtype, rel,
5026 /* Set up recursion for phase 2; no other prep needed */
5027 if (recurse)
5028 cmd->recurse = true;
5029 pass = AT_PASS_COL_ATTRS;
5030 break;
5031 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5032 ATSimplePermissions(cmd->subtype, rel,
5034 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5036 break;
5037 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5038 ATSimplePermissions(cmd->subtype, rel,
5040 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5041 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5042 pass = AT_PASS_DROP;
5043 break;
5044 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5045 ATSimplePermissions(cmd->subtype, rel,
5048 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5049 /* No command-specific prep needed */
5050 pass = AT_PASS_MISC;
5051 break;
5052 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5053 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5054 ATSimplePermissions(cmd->subtype, rel,
5057 /* This command never recurses */
5058 pass = AT_PASS_MISC;
5059 break;
5060 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5061 ATSimplePermissions(cmd->subtype, rel,
5064 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5065 /* No command-specific prep needed */
5066 pass = AT_PASS_MISC;
5067 break;
5068 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5069 ATSimplePermissions(cmd->subtype, rel,
5071 /* This command never recurses */
5072 /* No command-specific prep needed */
5073 pass = AT_PASS_MISC;
5074 break;
5075 case AT_DropColumn: /* DROP COLUMN */
5076 ATSimplePermissions(cmd->subtype, rel,
5079 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5080 lockmode, context);
5081 /* Recursion occurs during execution phase */
5082 pass = AT_PASS_DROP;
5083 break;
5084 case AT_AddIndex: /* ADD INDEX */
5086 /* This command never recurses */
5087 /* No command-specific prep needed */
5088 pass = AT_PASS_ADD_INDEX;
5089 break;
5090 case AT_AddConstraint: /* ADD CONSTRAINT */
5091 ATSimplePermissions(cmd->subtype, rel,
5093 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5094 if (recurse)
5095 {
5096 /* recurses at exec time; lock descendants and set flag */
5097 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5098 cmd->recurse = true;
5099 }
5100 pass = AT_PASS_ADD_CONSTR;
5101 break;
5102 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5104 /* This command never recurses */
5105 /* No command-specific prep needed */
5107 break;
5108 case AT_DropConstraint: /* DROP CONSTRAINT */
5109 ATSimplePermissions(cmd->subtype, rel,
5111 ATCheckPartitionsNotInUse(rel, lockmode);
5112 /* Other recursion occurs during execution phase */
5113 /* No command-specific prep needed except saving recurse flag */
5114 if (recurse)
5115 cmd->recurse = true;
5116 pass = AT_PASS_DROP;
5117 break;
5118 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5119 ATSimplePermissions(cmd->subtype, rel,
5122 /* See comments for ATPrepAlterColumnType */
5123 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5124 AT_PASS_UNSET, context);
5125 Assert(cmd != NULL);
5126 /* Performs own recursion */
5127 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5128 lockmode, context);
5129 pass = AT_PASS_ALTER_TYPE;
5130 break;
5133 /* This command never recurses */
5134 /* No command-specific prep needed */
5135 pass = AT_PASS_MISC;
5136 break;
5137 case AT_ChangeOwner: /* ALTER OWNER */
5138 /* This command never recurses */
5139 /* No command-specific prep needed */
5140 pass = AT_PASS_MISC;
5141 break;
5142 case AT_ClusterOn: /* CLUSTER ON */
5143 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5144 ATSimplePermissions(cmd->subtype, rel,
5146 /* These commands never recurse */
5147 /* No command-specific prep needed */
5148 pass = AT_PASS_MISC;
5149 break;
5150 case AT_SetLogged: /* SET LOGGED */
5151 case AT_SetUnLogged: /* SET UNLOGGED */
5153 if (tab->chgPersistence)
5154 ereport(ERROR,
5155 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5156 errmsg("cannot change persistence setting twice")));
5157 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5158 pass = AT_PASS_MISC;
5159 break;
5160 case AT_DropOids: /* SET WITHOUT OIDS */
5161 ATSimplePermissions(cmd->subtype, rel,
5163 pass = AT_PASS_DROP;
5164 break;
5165 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5166 ATSimplePermissions(cmd->subtype, rel,
5168
5169 /* check if another access method change was already requested */
5170 if (tab->chgAccessMethod)
5171 ereport(ERROR,
5172 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5173 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5174
5175 ATPrepSetAccessMethod(tab, rel, cmd->name);
5176 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5177 break;
5178 case AT_SetTableSpace: /* SET TABLESPACE */
5181 /* This command never recurses */
5182 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5183 pass = AT_PASS_MISC; /* doesn't actually matter */
5184 break;
5185 case AT_SetRelOptions: /* SET (...) */
5186 case AT_ResetRelOptions: /* RESET (...) */
5187 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5188 ATSimplePermissions(cmd->subtype, rel,
5191 /* This command never recurses */
5192 /* No command-specific prep needed */
5193 pass = AT_PASS_MISC;
5194 break;
5195 case AT_AddInherit: /* INHERIT */
5196 ATSimplePermissions(cmd->subtype, rel,
5198 /* This command never recurses */
5199 ATPrepAddInherit(rel);
5200 pass = AT_PASS_MISC;
5201 break;
5202 case AT_DropInherit: /* NO INHERIT */
5203 ATSimplePermissions(cmd->subtype, rel,
5205 /* This command never recurses */
5206 /* No command-specific prep needed */
5207 pass = AT_PASS_MISC;
5208 break;
5209 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5210 ATSimplePermissions(cmd->subtype, rel,
5212 /* Recursion occurs during execution phase */
5213 if (recurse)
5214 cmd->recurse = true;
5215 pass = AT_PASS_MISC;
5216 break;
5217 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5218 ATSimplePermissions(cmd->subtype, rel,
5220 /* Recursion occurs during execution phase */
5221 /* No command-specific prep needed except saving recurse flag */
5222 if (recurse)
5223 cmd->recurse = true;
5224 pass = AT_PASS_MISC;
5225 break;
5226 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5227 ATSimplePermissions(cmd->subtype, rel,
5229 pass = AT_PASS_MISC;
5230 /* This command never recurses */
5231 /* No command-specific prep needed */
5232 break;
5233 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5236 case AT_EnableTrigAll:
5237 case AT_EnableTrigUser:
5238 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5239 case AT_DisableTrigAll:
5240 case AT_DisableTrigUser:
5241 ATSimplePermissions(cmd->subtype, rel,
5243 /* Set up recursion for phase 2; no other prep needed */
5244 if (recurse)
5245 cmd->recurse = true;
5246 pass = AT_PASS_MISC;
5247 break;
5248 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5251 case AT_DisableRule:
5252 case AT_AddOf: /* OF */
5253 case AT_DropOf: /* NOT OF */
5258 ATSimplePermissions(cmd->subtype, rel,
5260 /* These commands never recurse */
5261 /* No command-specific prep needed */
5262 pass = AT_PASS_MISC;
5263 break;
5264 case AT_GenericOptions:
5266 /* No command-specific prep needed */
5267 pass = AT_PASS_MISC;
5268 break;
5269 case AT_AttachPartition:
5270 ATSimplePermissions(cmd->subtype, rel,
5272 /* No command-specific prep needed */
5273 pass = AT_PASS_MISC;
5274 break;
5275 case AT_DetachPartition:
5277 /* No command-specific prep needed */
5278 pass = AT_PASS_MISC;
5279 break;
5282 /* No command-specific prep needed */
5283 pass = AT_PASS_MISC;
5284 break;
5285 case AT_MergePartitions:
5286 case AT_SplitPartition:
5288 /* No command-specific prep needed */
5289 pass = AT_PASS_MISC;
5290 break;
5291 default: /* oops */
5292 elog(ERROR, "unrecognized alter table type: %d",
5293 (int) cmd->subtype);
5294 pass = AT_PASS_UNSET; /* keep compiler quiet */
5295 break;
5296 }
5297 Assert(pass > AT_PASS_UNSET);
5298
5299 /* Add the subcommand to the appropriate list for phase 2 */
5300 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5301}
5302
5303/*
5304 * ATRewriteCatalogs
5305 *
5306 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5307 * dispatched in a "safe" execution order (designed to avoid unnecessary
5308 * conflicts).
5309 */
5310static void
5312 AlterTableUtilityContext *context)
5313{
5314 ListCell *ltab;
5315
5316 /*
5317 * We process all the tables "in parallel", one pass at a time. This is
5318 * needed because we may have to propagate work from one table to another
5319 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5320 * re-adding of the foreign key constraint to the other table). Work can
5321 * only be propagated into later passes, however.
5322 */
5323 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5324 {
5325 /* Go through each table that needs to be processed */
5326 foreach(ltab, *wqueue)
5327 {
5328 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5329 List *subcmds = tab->subcmds[pass];
5330 ListCell *lcmd;
5331
5332 if (subcmds == NIL)
5333 continue;
5334
5335 /*
5336 * Open the relation and store it in tab. This allows subroutines
5337 * close and reopen, if necessary. Appropriate lock was obtained
5338 * by phase 1, needn't get it again.
5339 */
5340 tab->rel = relation_open(tab->relid, NoLock);
5341
5342 foreach(lcmd, subcmds)
5343 ATExecCmd(wqueue, tab,
5345 lockmode, pass, context);
5346
5347 /*
5348 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5349 * (this is not done in ATExecAlterColumnType since it should be
5350 * done only once if multiple columns of a table are altered).
5351 */
5352 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5353 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5354
5355 if (tab->rel)
5356 {
5357 relation_close(tab->rel, NoLock);
5358 tab->rel = NULL;
5359 }
5360 }
5361 }
5362
5363 /* Check to see if a toast table must be added. */
5364 foreach(ltab, *wqueue)
5365 {
5366 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5367
5368 /*
5369 * If the table is source table of ATTACH PARTITION command, we did
5370 * not modify anything about it that will change its toasting
5371 * requirement, so no need to check.
5372 */
5373 if (((tab->relkind == RELKIND_RELATION ||
5374 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5375 tab->partition_constraint == NULL) ||
5376 tab->relkind == RELKIND_MATVIEW)
5377 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5378 }
5379}
5380
5381/*
5382 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5383 */
5384static void
5386 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5387 AlterTableUtilityContext *context)
5388{
5390 Relation rel = tab->rel;
5391
5392 switch (cmd->subtype)
5393 {
5394 case AT_AddColumn: /* ADD COLUMN */
5395 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5396 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5397 cmd->recurse, false,
5398 lockmode, cur_pass, context);
5399 break;
5400 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5401 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5402 break;
5403 case AT_CookedColumnDefault: /* add a pre-cooked default */
5404 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5405 break;
5406 case AT_AddIdentity:
5407 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5408 cur_pass, context);
5409 Assert(cmd != NULL);
5410 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5411 break;
5412 case AT_SetIdentity:
5413 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5414 cur_pass, context);
5415 Assert(cmd != NULL);
5416 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5417 break;
5418 case AT_DropIdentity:
5419 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5420 break;
5421 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5422 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5423 break;
5424 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5425 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5426 cmd->recurse, false, lockmode);
5427 break;
5428 case AT_SetExpression:
5429 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5430 break;
5431 case AT_DropExpression:
5432 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5433 break;
5434 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5435 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5436 break;
5437 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5438 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5439 break;
5440 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5441 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5442 break;
5443 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5444 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5445 break;
5446 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5447 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5448 lockmode);
5449 break;
5450 case AT_DropColumn: /* DROP COLUMN */
5451 address = ATExecDropColumn(wqueue, rel, cmd->name,
5452 cmd->behavior, cmd->recurse, false,
5453 cmd->missing_ok, lockmode,
5454 NULL);
5455 break;
5456 case AT_AddIndex: /* ADD INDEX */
5457 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5458 lockmode);
5459 break;
5460 case AT_ReAddIndex: /* ADD INDEX */
5461 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5462 lockmode);
5463 break;
5464 case AT_ReAddStatistics: /* ADD STATISTICS */
5465 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5466 true, lockmode);
5467 break;
5468 case AT_AddConstraint: /* ADD CONSTRAINT */
5469 /* Transform the command only during initial examination */
5470 if (cur_pass == AT_PASS_ADD_CONSTR)
5471 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5472 cmd->recurse, lockmode,
5473 cur_pass, context);
5474 /* Depending on constraint type, might be no more work to do now */
5475 if (cmd != NULL)
5476 address =
5477 ATExecAddConstraint(wqueue, tab, rel,
5478 (Constraint *) cmd->def,
5479 cmd->recurse, false, lockmode);
5480 break;
5481 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5482 address =
5483 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5484 true, true, lockmode);
5485 break;
5486 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5487 * constraint */
5488 address =
5489 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5490 ((AlterDomainStmt *) cmd->def)->def,
5491 NULL);
5492 break;
5493 case AT_ReAddComment: /* Re-add existing comment */
5494 address = CommentObject((CommentStmt *) cmd->def);
5495 break;
5496 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5497 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5498 lockmode);
5499 break;
5500 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5501 address = ATExecAlterConstraint(wqueue, rel,
5503 cmd->recurse, lockmode);
5504 break;
5505 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5506 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5507 false, lockmode);
5508 break;
5509 case AT_DropConstraint: /* DROP CONSTRAINT */
5510 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5511 cmd->recurse,
5512 cmd->missing_ok, lockmode);
5513 break;
5514 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5515 /* parse transformation was done earlier */
5516 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5517 break;
5518 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5519 address =
5521 (List *) cmd->def, lockmode);
5522 break;
5523 case AT_ChangeOwner: /* ALTER OWNER */
5525 get_rolespec_oid(cmd->newowner, false),
5526 false, lockmode);
5527 break;
5528 case AT_ClusterOn: /* CLUSTER ON */
5529 address = ATExecClusterOn(rel, cmd->name, lockmode);
5530 break;
5531 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5532 ATExecDropCluster(rel, lockmode);
5533 break;
5534 case AT_SetLogged: /* SET LOGGED */
5535 case AT_SetUnLogged: /* SET UNLOGGED */
5536 break;
5537 case AT_DropOids: /* SET WITHOUT OIDS */
5538 /* nothing to do here, oid columns don't exist anymore */
5539 break;
5540 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5541
5542 /*
5543 * Only do this for partitioned tables, for which this is just a
5544 * catalog change. Tables with storage are handled by Phase 3.
5545 */
5546 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5547 tab->chgAccessMethod)
5549 break;
5550 case AT_SetTableSpace: /* SET TABLESPACE */
5551
5552 /*
5553 * Only do this for partitioned tables and indexes, for which this
5554 * is just a catalog change. Other relation types which have
5555 * storage are handled by Phase 3.
5556 */
5557 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5558 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5560
5561 break;
5562 case AT_SetRelOptions: /* SET (...) */
5563 case AT_ResetRelOptions: /* RESET (...) */
5564 case AT_ReplaceRelOptions: /* replace entire option list */
5565 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5566 break;
5567 case AT_EnableTrig: /* ENABLE TRIGGER name */
5570 cmd->recurse,
5571 lockmode);
5572 break;
5573 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5575 TRIGGER_FIRES_ALWAYS, false,
5576 cmd->recurse,
5577 lockmode);
5578 break;
5579 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5582 cmd->recurse,
5583 lockmode);
5584 break;
5585 case AT_DisableTrig: /* DISABLE TRIGGER name */
5587 TRIGGER_DISABLED, false,
5588 cmd->recurse,
5589 lockmode);
5590 break;
5591 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5594 cmd->recurse,
5595 lockmode);
5596 break;
5597 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5599 TRIGGER_DISABLED, false,
5600 cmd->recurse,
5601 lockmode);
5602 break;
5603 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5606 cmd->recurse,
5607 lockmode);
5608 break;
5609 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5611 TRIGGER_DISABLED, true,
5612 cmd->recurse,
5613 lockmode);
5614 break;
5615
5616 case AT_EnableRule: /* ENABLE RULE name */
5617 ATExecEnableDisableRule(rel, cmd->name,
5618 RULE_FIRES_ON_ORIGIN, lockmode);
5619 break;
5620 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5621 ATExecEnableDisableRule(rel, cmd->name,
5622 RULE_FIRES_ALWAYS, lockmode);
5623 break;
5624 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5625 ATExecEnableDisableRule(rel, cmd->name,
5626 RULE_FIRES_ON_REPLICA, lockmode);
5627 break;
5628 case AT_DisableRule: /* DISABLE RULE name */
5629 ATExecEnableDisableRule(rel, cmd->name,
5630 RULE_DISABLED, lockmode);
5631 break;
5632
5633 case AT_AddInherit:
5634 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5635 break;
5636 case AT_DropInherit:
5637 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5638 break;
5639 case AT_AddOf:
5640 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5641 break;
5642 case AT_DropOf:
5643 ATExecDropOf(rel, lockmode);
5644 break;
5645 case AT_ReplicaIdentity:
5646 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5647 break;
5649 ATExecSetRowSecurity(rel, true);
5650 break;
5652 ATExecSetRowSecurity(rel, false);
5653 break;
5656 break;
5659 break;
5660 case AT_GenericOptions:
5661 ATExecGenericOptions(rel, (List *) cmd->def);
5662 break;
5663 case AT_AttachPartition:
5664 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5665 cur_pass, context);
5666 Assert(cmd != NULL);
5667 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5668 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5669 context);
5670 else
5671 address = ATExecAttachPartitionIdx(wqueue, rel,
5672 ((PartitionCmd *) cmd->def)->name);
5673 break;
5674 case AT_DetachPartition:
5675 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5676 cur_pass, context);
5677 Assert(cmd != NULL);
5678 /* ATPrepCmd ensures it must be a table */
5679 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5680 address = ATExecDetachPartition(wqueue, tab, rel,
5681 ((PartitionCmd *) cmd->def)->name,
5682 ((PartitionCmd *) cmd->def)->concurrent);
5683 break;
5685 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5686 break;
5687 case AT_MergePartitions:
5688 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5689 cur_pass, context);
5690 Assert(cmd != NULL);
5691 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5692 ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5693 context);
5694 break;
5695 case AT_SplitPartition:
5696 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5697 cur_pass, context);
5698 Assert(cmd != NULL);
5699 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5700 ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5701 context);
5702 break;
5703 default: /* oops */
5704 elog(ERROR, "unrecognized alter table type: %d",
5705 (int) cmd->subtype);
5706 break;
5707 }
5708
5709 /*
5710 * Report the subcommand to interested event triggers.
5711 */
5712 if (cmd)
5713 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5714
5715 /*
5716 * Bump the command counter to ensure the next subcommand in the sequence
5717 * can see the changes so far
5718 */
5720}
5721
5722/*
5723 * ATParseTransformCmd: perform parse transformation for one subcommand
5724 *
5725 * Returns the transformed subcommand tree, if there is one, else NULL.
5726 *
5727 * The parser may hand back additional AlterTableCmd(s) and/or other
5728 * utility statements, either before or after the original subcommand.
5729 * Other AlterTableCmds are scheduled into the appropriate slot of the
5730 * AlteredTableInfo (they had better be for later passes than the current one).
5731 * Utility statements that are supposed to happen before the AlterTableCmd
5732 * are executed immediately. Those that are supposed to happen afterwards
5733 * are added to the tab->afterStmts list to be done at the very end.
5734 */
5735static AlterTableCmd *
5737 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5738 AlterTablePass cur_pass, AlterTableUtilityContext *context)
5739{
5740 AlterTableCmd *newcmd = NULL;
5742 List *beforeStmts;
5743 List *afterStmts;
5744 ListCell *lc;
5745
5746 /* Gin up an AlterTableStmt with just this subcommand and this table */
5747 atstmt->relation =
5750 -1);
5751 atstmt->relation->inh = recurse;
5752 atstmt->cmds = list_make1(cmd);
5753 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5754 atstmt->missing_ok = false;
5755
5756 /* Transform the AlterTableStmt */
5758 atstmt,
5759 context->queryString,
5760 &beforeStmts,
5761 &afterStmts);
5762
5763 /* Execute any statements that should happen before these subcommand(s) */
5764 foreach(lc, beforeStmts)
5765 {
5766 Node *stmt = (Node *) lfirst(lc);
5767
5770 }
5771
5772 /* Examine the transformed subcommands and schedule them appropriately */
5773 foreach(lc, atstmt->cmds)
5774 {
5776 AlterTablePass pass;
5777
5778 /*
5779 * This switch need only cover the subcommand types that can be added
5780 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5781 * executing the subcommand immediately, as a substitute for the
5782 * original subcommand. (Note, however, that this does cause
5783 * AT_AddConstraint subcommands to be rescheduled into later passes,
5784 * which is important for index and foreign key constraints.)
5785 *
5786 * We assume we needn't do any phase-1 checks for added subcommands.
5787 */
5788 switch (cmd2->subtype)
5789 {
5790 case AT_AddIndex:
5791 pass = AT_PASS_ADD_INDEX;
5792 break;
5795 break;
5796 case AT_AddConstraint:
5797 /* Recursion occurs during execution phase */
5798 if (recurse)
5799 cmd2->recurse = true;
5800 switch (castNode(Constraint, cmd2->def)->contype)
5801 {
5802 case CONSTR_NOTNULL:
5803 pass = AT_PASS_COL_ATTRS;
5804 break;
5805 case CONSTR_PRIMARY:
5806 case CONSTR_UNIQUE:
5807 case CONSTR_EXCLUSION:
5809 break;
5810 default:
5812 break;
5813 }
5814 break;
5816 /* This command never recurses */
5817 /* No command-specific prep needed */
5818 pass = AT_PASS_MISC;
5819 break;
5820 default:
5821 pass = cur_pass;
5822 break;
5823 }
5824
5825 if (pass < cur_pass)
5826 {
5827 /* Cannot schedule into a pass we already finished */
5828 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5829 pass);
5830 }
5831 else if (pass > cur_pass)
5832 {
5833 /* OK, queue it up for later */
5834 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5835 }
5836 else
5837 {
5838 /*
5839 * We should see at most one subcommand for the current pass,
5840 * which is the transformed version of the original subcommand.
5841 */
5842 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5843 {
5844 /* Found the transformed version of our subcommand */
5845 newcmd = cmd2;
5846 }
5847 else
5848 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5849 pass);
5850 }
5851 }
5852
5853 /* Queue up any after-statements to happen at the end */
5854 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5855
5856 return newcmd;
5857}
5858
5859/*
5860 * ATRewriteTables: ALTER TABLE phase 3
5861 */
5862static void
5863ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5864 AlterTableUtilityContext *context)
5865{
5866 ListCell *ltab;
5867
5868 /* Go through each table that needs to be checked or rewritten */
5869 foreach(ltab, *wqueue)
5870 {
5871 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5872
5873 /* Relations without storage may be ignored here */
5874 if (!RELKIND_HAS_STORAGE(tab->relkind))
5875 continue;
5876
5877 /*
5878 * If we change column data types, the operation has to be propagated
5879 * to tables that use this table's rowtype as a column type.
5880 * tab->newvals will also be non-NULL in the case where we're adding a
5881 * column with a default. We choose to forbid that case as well,
5882 * since composite types might eventually support defaults.
5883 *
5884 * (Eventually we'll probably need to check for composite type
5885 * dependencies even when we're just scanning the table without a
5886 * rewrite, but at the moment a composite type does not enforce any
5887 * constraints, so it's not necessary/appropriate to enforce them just
5888 * during ALTER.)
5889 */
5890 if (tab->newvals != NIL || tab->rewrite > 0)
5891 {
5892 Relation rel;
5893
5894 rel = table_open(tab->relid, NoLock);
5895 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5896 table_close(rel, NoLock);
5897 }
5898
5899 /*
5900 * We only need to rewrite the table if at least one column needs to
5901 * be recomputed, or we are changing its persistence or access method.
5902 *
5903 * There are two reasons for requiring a rewrite when changing
5904 * persistence: on one hand, we need to ensure that the buffers
5905 * belonging to each of the two relations are marked with or without
5906 * BM_PERMANENT properly. On the other hand, since rewriting creates
5907 * and assigns a new relfilenumber, we automatically create or drop an
5908 * init fork for the relation as appropriate.
5909 */
5910 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5911 {
5912 /* Build a temporary relation and copy data */
5913 Relation OldHeap;
5914 Oid OIDNewHeap;
5915 Oid NewAccessMethod;
5916 Oid NewTableSpace;
5917 char persistence;
5918
5919 OldHeap = table_open(tab->relid, NoLock);
5920
5921 /*
5922 * We don't support rewriting of system catalogs; there are too
5923 * many corner cases and too little benefit. In particular this
5924 * is certainly not going to work for mapped catalogs.
5925 */
5926 if (IsSystemRelation(OldHeap))
5927 ereport(ERROR,
5928 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5929 errmsg("cannot rewrite system relation \"%s\"",
5930 RelationGetRelationName(OldHeap))));
5931
5932 if (RelationIsUsedAsCatalogTable(OldHeap))
5933 ereport(ERROR,
5934 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5935 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5936 RelationGetRelationName(OldHeap))));
5937
5938 /*
5939 * Don't allow rewrite on temp tables of other backends ... their
5940 * local buffer manager is not going to cope. (This is redundant
5941 * with the check in CheckAlterTableIsSafe, but for safety we'll
5942 * check here too.)
5943 */
5944 if (RELATION_IS_OTHER_TEMP(OldHeap))
5945 ereport(ERROR,
5946 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5947 errmsg("cannot rewrite temporary tables of other sessions")));
5948
5949 /*
5950 * Select destination tablespace (same as original unless user
5951 * requested a change)
5952 */
5953 if (tab->newTableSpace)
5954 NewTableSpace = tab->newTableSpace;
5955 else
5956 NewTableSpace = OldHeap->rd_rel->reltablespace;
5957
5958 /*
5959 * Select destination access method (same as original unless user
5960 * requested a change)
5961 */
5962 if (tab->chgAccessMethod)
5963 NewAccessMethod = tab->newAccessMethod;
5964 else
5965 NewAccessMethod = OldHeap->rd_rel->relam;
5966
5967 /*
5968 * Select persistence of transient table (same as original unless
5969 * user requested a change)
5970 */
5971 persistence = tab->chgPersistence ?
5972 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5973
5974 table_close(OldHeap, NoLock);
5975
5976 /*
5977 * Fire off an Event Trigger now, before actually rewriting the
5978 * table.
5979 *
5980 * We don't support Event Trigger for nested commands anywhere,
5981 * here included, and parsetree is given NULL when coming from
5982 * AlterTableInternal.
5983 *
5984 * And fire it only once.
5985 */
5986 if (parsetree)
5987 EventTriggerTableRewrite((Node *) parsetree,
5988 tab->relid,
5989 tab->rewrite);
5990
5991 /*
5992 * Create transient table that will receive the modified data.
5993 *
5994 * Ensure it is marked correctly as logged or unlogged. We have
5995 * to do this here so that buffers for the new relfilenumber will
5996 * have the right persistence set, and at the same time ensure
5997 * that the original filenumbers's buffers will get read in with
5998 * the correct setting (i.e. the original one). Otherwise a
5999 * rollback after the rewrite would possibly result with buffers
6000 * for the original filenumbers having the wrong persistence
6001 * setting.
6002 *
6003 * NB: This relies on swap_relation_files() also swapping the
6004 * persistence. That wouldn't work for pg_class, but that can't be
6005 * unlogged anyway.
6006 */
6007 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
6008 persistence, lockmode);
6009
6010 /*
6011 * Copy the heap data into the new table with the desired
6012 * modifications, and test the current data within the table
6013 * against new constraints generated by ALTER TABLE commands.
6014 */
6015 ATRewriteTable(tab, OIDNewHeap);
6016
6017 /*
6018 * Swap the physical files of the old and new heaps, then rebuild
6019 * indexes and discard the old heap. We can use RecentXmin for
6020 * the table's new relfrozenxid because we rewrote all the tuples
6021 * in ATRewriteTable, so no older Xid remains in the table. Also,
6022 * we never try to swap toast tables by content, since we have no
6023 * interest in letting this code work on system catalogs.
6024 */
6025 finish_heap_swap(tab->relid, OIDNewHeap,
6026 false, false, true,
6028 RecentXmin,
6030 persistence);
6031
6032 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
6033 }
6034 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6035 {
6036 if (tab->chgPersistence)
6038 }
6039 else
6040 {
6041 /*
6042 * If required, test the current data within the table against new
6043 * constraints generated by ALTER TABLE commands, but don't
6044 * rebuild data.
6045 */
6046 if (tab->constraints != NIL || tab->verify_new_notnull ||
6047 tab->partition_constraint != NULL)
6049
6050 /*
6051 * If we had SET TABLESPACE but no reason to reconstruct tuples,
6052 * just do a block-by-block copy.
6053 */
6054 if (tab->newTableSpace)
6055 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6056 }
6057
6058 /*
6059 * Also change persistence of owned sequences, so that it matches the
6060 * table persistence.
6061 */
6062 if (tab->chgPersistence)
6063 {
6064 List *seqlist = getOwnedSequences(tab->relid);
6065 ListCell *lc;
6066
6067 foreach(lc, seqlist)
6068 {
6069 Oid seq_relid = lfirst_oid(lc);
6070
6072 }
6073 }
6074 }
6075
6076 /*
6077 * Foreign key constraints are checked in a final pass, since (a) it's
6078 * generally best to examine each one separately, and (b) it's at least
6079 * theoretically possible that we have changed both relations of the
6080 * foreign key, and we'd better have finished both rewrites before we try
6081 * to read the tables.
6082 */
6083 foreach(ltab, *wqueue)
6084 {
6085 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6086 Relation rel = NULL;
6087 ListCell *lcon;
6088
6089 /* Relations without storage may be ignored here too */
6090 if (!RELKIND_HAS_STORAGE(tab->relkind))
6091 continue;
6092
6093 foreach(lcon, tab->constraints)
6094 {
6095 NewConstraint *con = lfirst(lcon);
6096
6097 if (con->contype == CONSTR_FOREIGN)
6098 {
6099 Constraint *fkconstraint = (Constraint *) con->qual;
6100 Relation refrel;
6101
6102 if (rel == NULL)
6103 {
6104 /* Long since locked, no need for another */
6105 rel = table_open(tab->relid, NoLock);
6106 }
6107
6108 refrel = table_open(con->refrelid, RowShareLock);
6109
6110 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6111 con->refindid,
6112 con->conid,
6113 con->conwithperiod);
6114
6115 /*
6116 * No need to mark the constraint row as validated, we did
6117 * that when we inserted the row earlier.
6118 */
6119
6120 table_close(refrel, NoLock);
6121 }
6122 }
6123
6124 if (rel)
6125 table_close(rel, NoLock);
6126 }
6127
6128 /* Finally, run any afterStmts that were queued up */
6129 foreach(ltab, *wqueue)
6130 {
6131 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6132 ListCell *lc;
6133
6134 foreach(lc, tab->afterStmts)
6135 {
6136 Node *stmt = (Node *) lfirst(lc);
6137
6140 }
6141 }
6142}
6143
6144/*
6145 * ATRewriteTable: scan or rewrite one table
6146 *
6147 * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6148 * must already hold AccessExclusiveLock on it.
6149 */
6150static void
6152{
6153 Relation oldrel;
6154 Relation newrel;
6155 TupleDesc oldTupDesc;
6156 TupleDesc newTupDesc;
6157 bool needscan = false;
6158 List *notnull_attrs;
6159 List *notnull_virtual_attrs;
6160 int i;
6161 ListCell *l;
6162 EState *estate;
6163 CommandId mycid;
6164 BulkInsertState bistate;
6165 int ti_options;
6166 ExprState *partqualstate = NULL;
6167
6168 /*
6169 * Open the relation(s). We have surely already locked the existing
6170 * table.
6171 */
6172 oldrel = table_open(tab->relid, NoLock);
6173 oldTupDesc = tab->oldDesc;
6174 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6175
6176 if (OidIsValid(OIDNewHeap))
6177 {
6179 false));
6180 newrel = table_open(OIDNewHeap, NoLock);
6181 }
6182 else
6183 newrel = NULL;
6184
6185 /*
6186 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6187 * is empty, so don't bother using it.
6188 */
6189 if (newrel)
6190 {
6191 mycid = GetCurrentCommandId(true);
6192 bistate = GetBulkInsertState();
6193 ti_options = TABLE_INSERT_SKIP_FSM;
6194 }
6195 else
6196 {
6197 /* keep compiler quiet about using these uninitialized */
6198 mycid = 0;
6199 bistate = NULL;
6200 ti_options = 0;
6201 }
6202
6203 /*
6204 * Generate the constraint and default execution states
6205 */
6206
6207 estate = CreateExecutorState();
6208
6209 /* Build the needed expression execution states */
6210 foreach(l, tab->constraints)
6211 {
6212 NewConstraint *con = lfirst(l);
6213
6214 switch (con->contype)
6215 {
6216 case CONSTR_CHECK:
6217 needscan = true;
6218 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6219 break;
6220 case CONSTR_FOREIGN:
6221 /* Nothing to do here */
6222 break;
6223 default:
6224 elog(ERROR, "unrecognized constraint type: %d",
6225 (int) con->contype);
6226 }
6227 }
6228
6229 /* Build expression execution states for partition check quals */
6230 if (tab->partition_constraint)
6231 {
6232 needscan = true;
6233 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6234 }
6235
6236 foreach(l, tab->newvals)
6237 {
6238 NewColumnValue *ex = lfirst(l);
6239
6240 /* expr already planned */
6241 ex->exprstate = ExecInitExpr(ex->expr, NULL);
6242 }
6243
6244 notnull_attrs = notnull_virtual_attrs = NIL;
6245 if (newrel || tab->verify_new_notnull)
6246 {
6247 /*
6248 * If we are rebuilding the tuples OR if we added any new but not
6249 * verified not-null constraints, check all *valid* not-null
6250 * constraints. This is a bit of overkill but it minimizes risk of
6251 * bugs.
6252 *
6253 * notnull_attrs does *not* collect attribute numbers for valid
6254 * not-null constraints over virtual generated columns; instead, they
6255 * are collected in notnull_virtual_attrs for verification elsewhere.
6256 */
6257 for (i = 0; i < newTupDesc->natts; i++)
6258 {
6259 CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6260
6261 if (attr->attnullability == ATTNULLABLE_VALID &&
6262 !attr->attisdropped)
6263 {
6264 Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6265
6266 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6267 notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6268 else
6269 notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6270 wholeatt->attnum);
6271 }
6272 }
6273 if (notnull_attrs || notnull_virtual_attrs)
6274 needscan = true;
6275 }
6276
6277 if (newrel || needscan)
6278 {
6279 ExprContext *econtext;
6280 TupleTableSlot *oldslot;
6281 TupleTableSlot *newslot;
6282 TableScanDesc scan;
6283 MemoryContext oldCxt;
6284 List *dropped_attrs = NIL;
6285 ListCell *lc;
6286 Snapshot snapshot;
6287 ResultRelInfo *rInfo = NULL;
6288
6289 /*
6290 * When adding or changing a virtual generated column with a not-null
6291 * constraint, we need to evaluate whether the generation expression
6292 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6293 * prepare a dummy ResultRelInfo.
6294 */
6295 if (notnull_virtual_attrs != NIL)
6296 {
6297 MemoryContext oldcontext;
6298
6299 Assert(newTupDesc->constr->has_generated_virtual);
6300 Assert(newTupDesc->constr->has_not_null);
6301 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6302 rInfo = makeNode(ResultRelInfo);
6303 InitResultRelInfo(rInfo,
6304 oldrel,
6305 0, /* dummy rangetable index */
6306 NULL,
6307 estate->es_instrument);
6308 MemoryContextSwitchTo(oldcontext);
6309 }
6310
6311 if (newrel)
6313 (errmsg_internal("rewriting table \"%s\"",
6314 RelationGetRelationName(oldrel))));
6315 else
6317 (errmsg_internal("verifying table \"%s\"",
6318 RelationGetRelationName(oldrel))));
6319
6320 if (newrel)
6321 {
6322 /*
6323 * All predicate locks on the tuples or pages are about to be made
6324 * invalid, because we move tuples around. Promote them to
6325 * relation locks.
6326 */
6328 }
6329
6330 econtext = GetPerTupleExprContext(estate);
6331
6332 /*
6333 * Create necessary tuple slots. When rewriting, two slots are needed,
6334 * otherwise one suffices. In the case where one slot suffices, we
6335 * need to use the new tuple descriptor, otherwise some constraints
6336 * can't be evaluated. Note that even when the tuple layout is the
6337 * same and no rewrite is required, the tupDescs might not be
6338 * (consider ADD COLUMN without a default).
6339 */
6340 if (tab->rewrite)
6341 {
6342 Assert(newrel != NULL);
6343 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6344 table_slot_callbacks(oldrel));
6345 newslot = MakeSingleTupleTableSlot(newTupDesc,
6346 table_slot_callbacks(newrel));
6347
6348 /*
6349 * Set all columns in the new slot to NULL initially, to ensure
6350 * columns added as part of the rewrite are initialized to NULL.
6351 * That is necessary as tab->newvals will not contain an
6352 * expression for columns with a NULL default, e.g. when adding a
6353 * column without a default together with a column with a default
6354 * requiring an actual rewrite.
6355 */
6356 ExecStoreAllNullTuple(newslot);
6357 }
6358 else
6359 {
6360 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6361 table_slot_callbacks(oldrel));
6362 newslot = NULL;
6363 }
6364
6365 /*
6366 * Any attributes that are dropped according to the new tuple
6367 * descriptor can be set to NULL. We precompute the list of dropped
6368 * attributes to avoid needing to do so in the per-tuple loop.
6369 */
6370 for (i = 0; i < newTupDesc->natts; i++)
6371 {
6372 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6373 dropped_attrs = lappend_int(dropped_attrs, i);
6374 }
6375
6376 /*
6377 * Scan through the rows, generating a new row if needed and then
6378 * checking all the constraints.
6379 */
6380 snapshot = RegisterSnapshot(GetLatestSnapshot());
6381 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6382
6383 /*
6384 * Switch to per-tuple memory context and reset it for each tuple
6385 * produced, so we don't leak memory.
6386 */
6388
6389 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6390 {
6391 TupleTableSlot *insertslot;
6392
6393 if (tab->rewrite > 0)
6394 {
6395 /* Extract data from old tuple */
6396 slot_getallattrs(oldslot);
6397 ExecClearTuple(newslot);
6398
6399 /* copy attributes */
6400 memcpy(newslot->tts_values, oldslot->tts_values,
6401 sizeof(Datum) * oldslot->tts_nvalid);
6402 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6403 sizeof(bool) * oldslot->tts_nvalid);
6404
6405 /* Set dropped attributes to null in new tuple */
6406 foreach(lc, dropped_attrs)
6407 newslot->tts_isnull[lfirst_int(lc)] = true;
6408
6409 /*
6410 * Constraints and GENERATED expressions might reference the
6411 * tableoid column, so fill tts_tableOid with the desired
6412 * value. (We must do this each time, because it gets
6413 * overwritten with newrel's OID during storing.)
6414 */
6415 newslot->tts_tableOid = RelationGetRelid(oldrel);
6416
6417 /*
6418 * Process supplied expressions to replace selected columns.
6419 *
6420 * First, evaluate expressions whose inputs come from the old
6421 * tuple.
6422 */
6423 econtext->ecxt_scantuple = oldslot;
6424
6425 foreach(l, tab->newvals)
6426 {
6427 NewColumnValue *ex = lfirst(l);
6428
6429 if (ex->is_generated)
6430 continue;
6431
6432 newslot->tts_values[ex->attnum - 1]
6433 = ExecEvalExpr(ex->exprstate,
6434 econtext,
6435 &newslot->tts_isnull[ex->attnum - 1]);
6436 }
6437
6438 ExecStoreVirtualTuple(newslot);
6439
6440 /*
6441 * Now, evaluate any expressions whose inputs come from the
6442 * new tuple. We assume these columns won't reference each
6443 * other, so that there's no ordering dependency.
6444 */
6445 econtext->ecxt_scantuple = newslot;
6446
6447 foreach(l, tab->newvals)
6448 {
6449 NewColumnValue *ex = lfirst(l);
6450
6451 if (!ex->is_generated)
6452 continue;
6453
6454 newslot->tts_values[ex->attnum - 1]
6455 = ExecEvalExpr(ex->exprstate,
6456 econtext,
6457 &newslot->tts_isnull[ex->attnum - 1]);
6458 }
6459
6460 insertslot = newslot;
6461 }
6462 else
6463 {
6464 /*
6465 * If there's no rewrite, old and new table are guaranteed to
6466 * have the same AM, so we can just use the old slot to verify
6467 * new constraints etc.
6468 */
6469 insertslot = oldslot;
6470 }
6471
6472 /* Now check any constraints on the possibly-changed tuple */
6473 econtext->ecxt_scantuple = insertslot;
6474
6475 foreach_int(attn, notnull_attrs)
6476 {
6477 if (slot_attisnull(insertslot, attn))
6478 {
6479 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6480
6481 ereport(ERROR,
6482 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6483 errmsg("column \"%s\" of relation \"%s\" contains null values",
6484 NameStr(attr->attname),
6485 RelationGetRelationName(oldrel)),
6486 errtablecol(oldrel, attn)));
6487 }
6488 }
6489
6490 if (notnull_virtual_attrs != NIL)
6491 {
6493
6494 attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6495 estate,
6496 notnull_virtual_attrs);
6498 {
6499 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6500
6501 ereport(ERROR,
6502 errcode(ERRCODE_NOT_NULL_VIOLATION),
6503 errmsg("column \"%s\" of relation \"%s\" contains null values",
6504 NameStr(attr->attname),
6505 RelationGetRelationName(oldrel)),
6506 errtablecol(oldrel, attnum));
6507 }
6508 }
6509
6510 foreach(l, tab->constraints)
6511 {
6512 NewConstraint *con = lfirst(l);
6513
6514 switch (con->contype)
6515 {
6516 case CONSTR_CHECK:
6517 if (!ExecCheck(con->qualstate, econtext))
6518 ereport(ERROR,
6519 (errcode(ERRCODE_CHECK_VIOLATION),
6520 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6521 con->name,
6522 RelationGetRelationName(oldrel)),
6523 errtableconstraint(oldrel, con->name)));
6524 break;
6525 case CONSTR_NOTNULL:
6526 case CONSTR_FOREIGN:
6527 /* Nothing to do here */
6528 break;
6529 default:
6530 elog(ERROR, "unrecognized constraint type: %d",
6531 (int) con->contype);
6532 }
6533 }
6534
6535 if (partqualstate && !ExecCheck(partqualstate, econtext))
6536 {
6537 if (tab->validate_default)
6538 ereport(ERROR,
6539 (errcode(ERRCODE_CHECK_VIOLATION),
6540 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6541 RelationGetRelationName(oldrel)),
6542 errtable(oldrel)));
6543 else
6544 ereport(ERROR,
6545 (errcode(ERRCODE_CHECK_VIOLATION),
6546 errmsg("partition constraint of relation \"%s\" is violated by some row",
6547 RelationGetRelationName(oldrel)),
6548 errtable(oldrel)));
6549 }
6550
6551 /* Write the tuple out to the new relation */
6552 if (newrel)
6553 table_tuple_insert(newrel, insertslot, mycid,
6554 ti_options, bistate);
6555
6556 ResetExprContext(econtext);
6557
6559 }
6560
6561 MemoryContextSwitchTo(oldCxt);
6562 table_endscan(scan);
6563 UnregisterSnapshot(snapshot);
6564
6566 if (newslot)
6568 }
6569
6570 FreeExecutorState(estate);
6571
6572 table_close(oldrel, NoLock);
6573 if (newrel)
6574 {
6575 FreeBulkInsertState(bistate);
6576
6577 table_finish_bulk_insert(newrel, ti_options);
6578
6579 table_close(newrel, NoLock);
6580 }
6581}
6582
6583/*
6584 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6585 */
6586static AlteredTableInfo *
6588{
6589 Oid relid = RelationGetRelid(rel);
6590 AlteredTableInfo *tab;
6591 ListCell *ltab;
6592
6593 foreach(ltab, *wqueue)
6594 {
6595 tab = (AlteredTableInfo *) lfirst(ltab);
6596 if (tab->relid == relid)
6597 return tab;
6598 }
6599
6600 /*
6601 * Not there, so add it. Note that we make a copy of the relation's
6602 * existing descriptor before anything interesting can happen to it.
6603 */
6605 tab->relid = relid;
6606 tab->rel = NULL; /* set later */
6607 tab->relkind = rel->rd_rel->relkind;
6610 tab->chgAccessMethod = false;
6612 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6613 tab->chgPersistence = false;
6614
6615 *wqueue = lappend(*wqueue, tab);
6616
6617 return tab;
6618}
6619
6620static const char *
6622{
6623 switch (cmdtype)
6624 {
6625 case AT_AddColumn:
6626 case AT_AddColumnToView:
6627 return "ADD COLUMN";
6628 case AT_ColumnDefault:
6630 return "ALTER COLUMN ... SET DEFAULT";
6631 case AT_DropNotNull:
6632 return "ALTER COLUMN ... DROP NOT NULL";
6633 case AT_SetNotNull:
6634 return "ALTER COLUMN ... SET NOT NULL";
6635 case AT_SetExpression:
6636 return "ALTER COLUMN ... SET EXPRESSION";
6637 case AT_DropExpression:
6638 return "ALTER COLUMN ... DROP EXPRESSION";
6639 case AT_SetStatistics:
6640 return "ALTER COLUMN ... SET STATISTICS";
6641 case AT_SetOptions:
6642 return "ALTER COLUMN ... SET";
6643 case AT_ResetOptions:
6644 return "ALTER COLUMN ... RESET";
6645 case AT_SetStorage:
6646 return "ALTER COLUMN ... SET STORAGE";
6647 case AT_SetCompression:
6648 return "ALTER COLUMN ... SET COMPRESSION";
6649 case AT_DropColumn:
6650 return "DROP COLUMN";
6651 case AT_AddIndex:
6652 case AT_ReAddIndex:
6653 return NULL; /* not real grammar */
6654 case AT_AddConstraint:
6655 case AT_ReAddConstraint:
6658 return "ADD CONSTRAINT";
6659 case AT_AlterConstraint:
6660 return "ALTER CONSTRAINT";
6662 return "VALIDATE CONSTRAINT";
6663 case AT_DropConstraint:
6664 return "DROP CONSTRAINT";
6665 case AT_ReAddComment:
6666 return NULL; /* not real grammar */
6667 case AT_AlterColumnType:
6668 return "ALTER COLUMN ... SET DATA TYPE";
6670 return "ALTER COLUMN ... OPTIONS";
6671 case AT_ChangeOwner:
6672 return "OWNER TO";
6673 case AT_ClusterOn:
6674 return "CLUSTER ON";
6675 case AT_DropCluster:
6676 return "SET WITHOUT CLUSTER";
6677 case AT_SetAccessMethod:
6678 return "SET ACCESS METHOD";
6679 case AT_SetLogged:
6680 return "SET LOGGED";
6681 case AT_SetUnLogged:
6682 return "SET UNLOGGED";
6683 case AT_DropOids:
6684 return "SET WITHOUT OIDS";
6685 case AT_SetTableSpace:
6686 return "SET TABLESPACE";
6687 case AT_SetRelOptions:
6688 return "SET";
6689 case AT_ResetRelOptions:
6690 return "RESET";
6692 return NULL; /* not real grammar */
6693 case AT_EnableTrig:
6694 return "ENABLE TRIGGER";
6696 return "ENABLE ALWAYS TRIGGER";
6698 return "ENABLE REPLICA TRIGGER";
6699 case AT_DisableTrig:
6700 return "DISABLE TRIGGER";
6701 case AT_EnableTrigAll:
6702 return "ENABLE TRIGGER ALL";
6703 case AT_DisableTrigAll:
6704 return "DISABLE TRIGGER ALL";
6705 case AT_EnableTrigUser:
6706 return "ENABLE TRIGGER USER";
6707 case AT_DisableTrigUser:
6708 return "DISABLE TRIGGER USER";
6709 case AT_EnableRule:
6710 return "ENABLE RULE";
6712 return "ENABLE ALWAYS RULE";
6714 return "ENABLE REPLICA RULE";
6715 case AT_DisableRule:
6716 return "DISABLE RULE";
6717 case AT_AddInherit:
6718 return "INHERIT";
6719 case AT_DropInherit:
6720 return "NO INHERIT";
6721 case AT_AddOf:
6722 return "OF";
6723 case AT_DropOf:
6724 return "NOT OF";
6725 case AT_ReplicaIdentity:
6726 return "REPLICA IDENTITY";
6728 return "ENABLE ROW SECURITY";
6730 return "DISABLE ROW SECURITY";
6732 return "FORCE ROW SECURITY";
6734 return "NO FORCE ROW SECURITY";
6735 case AT_GenericOptions:
6736 return "OPTIONS";
6737 case AT_AttachPartition:
6738 return "ATTACH PARTITION";
6739 case AT_DetachPartition:
6740 return "DETACH PARTITION";
6742 return "DETACH PARTITION ... FINALIZE";
6743 case AT_MergePartitions:
6744 return "MERGE PARTITIONS";
6745 case AT_SplitPartition:
6746 return "SPLIT PARTITION";
6747 case AT_AddIdentity:
6748 return "ALTER COLUMN ... ADD IDENTITY";
6749 case AT_SetIdentity:
6750 return "ALTER COLUMN ... SET";
6751 case AT_DropIdentity:
6752 return "ALTER COLUMN ... DROP IDENTITY";
6753 case AT_ReAddStatistics:
6754 return NULL; /* not real grammar */
6755 }
6756
6757 return NULL;
6758}
6759
6760/*
6761 * ATSimplePermissions
6762 *
6763 * - Ensure that it is a relation (or possibly a view)
6764 * - Ensure this user is the owner
6765 * - Ensure that it is not a system table
6766 */
6767static void
6768ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6769{
6770 int actual_target;
6771
6772 switch (rel->rd_rel->relkind)
6773 {
6774 case RELKIND_RELATION:
6775 actual_target = ATT_TABLE;
6776 break;
6777 case RELKIND_PARTITIONED_TABLE:
6778 actual_target = ATT_PARTITIONED_TABLE;
6779 break;
6780 case RELKIND_VIEW:
6781 actual_target = ATT_VIEW;
6782 break;
6783 case RELKIND_MATVIEW:
6784 actual_target = ATT_MATVIEW;
6785 break;
6786 case RELKIND_INDEX:
6787 actual_target = ATT_INDEX;
6788 break;
6789 case RELKIND_PARTITIONED_INDEX:
6790 actual_target = ATT_PARTITIONED_INDEX;
6791 break;
6792 case RELKIND_COMPOSITE_TYPE:
6793 actual_target = ATT_COMPOSITE_TYPE;
6794 break;
6795 case RELKIND_FOREIGN_TABLE:
6796 actual_target = ATT_FOREIGN_TABLE;
6797 break;
6798 case RELKIND_SEQUENCE:
6799 actual_target = ATT_SEQUENCE;
6800 break;
6801 default:
6802 actual_target = 0;
6803 break;
6804 }
6805
6806 /* Wrong target type? */
6807 if ((actual_target & allowed_targets) == 0)
6808 {
6809 const char *action_str = alter_table_type_to_string(cmdtype);
6810
6811 if (action_str)
6812 ereport(ERROR,
6813 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6814 /* translator: %s is a group of some SQL keywords */
6815 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6816 action_str, RelationGetRelationName(rel)),
6817 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6818 else
6819 /* internal error? */
6820 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6822 }
6823
6824 /* Permissions checks */
6825 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6828
6830 ereport(ERROR,
6831 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6832 errmsg("permission denied: \"%s\" is a system catalog",
6834}
6835
6836/*
6837 * ATSimpleRecursion
6838 *
6839 * Simple table recursion sufficient for most ALTER TABLE operations.
6840 * All direct and indirect children are processed in an unspecified order.
6841 * Note that if a child inherits from the original table via multiple
6842 * inheritance paths, it will be visited just once.
6843 */
6844static void
6846 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6847 AlterTableUtilityContext *context)
6848{
6849 /*
6850 * Propagate to children, if desired and if there are (or might be) any
6851 * children.
6852 */
6853 if (recurse && rel->rd_rel->relhassubclass)
6854 {
6855 Oid relid = RelationGetRelid(rel);
6856 ListCell *child;
6857 List *children;
6858
6859 children = find_all_inheritors(relid, lockmode, NULL);
6860
6861 /*
6862 * find_all_inheritors does the recursive search of the inheritance
6863 * hierarchy, so all we have to do is process all of the relids in the
6864 * list that it returns.
6865 */
6866 foreach(child, children)
6867 {
6868 Oid childrelid = lfirst_oid(child);
6869 Relation childrel;
6870
6871 if (childrelid == relid)
6872 continue;
6873 /* find_all_inheritors already got lock */
6874 childrel = relation_open(childrelid, NoLock);
6875 CheckAlterTableIsSafe(childrel);
6876 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6877 relation_close(childrel, NoLock);
6878 }
6879 }
6880}
6881
6882/*
6883 * Obtain list of partitions of the given table, locking them all at the given
6884 * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6885 *
6886 * This function is a no-op if the given relation is not a partitioned table;
6887 * in particular, nothing is done if it's a legacy inheritance parent.
6888 */
6889static void
6891{
6892 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6893 {
6894 List *inh;
6895 ListCell *cell;
6896
6897 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6898 /* first element is the parent rel; must ignore it */
6899 for_each_from(cell, inh, 1)
6900 {
6901 Relation childrel;
6902
6903 /* find_all_inheritors already got lock */
6904 childrel = table_open(lfirst_oid(cell), NoLock);
6905 CheckAlterTableIsSafe(childrel);
6906 table_close(childrel, NoLock);
6907 }
6908 list_free(inh);
6909 }
6910}
6911
6912/*
6913 * ATTypedTableRecursion
6914 *
6915 * Propagate ALTER TYPE operations to the typed tables of that type.
6916 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6917 * recursion to inheritance children of the typed tables.
6918 */
6919static void
6921 LOCKMODE lockmode, AlterTableUtilityContext *context)
6922{
6923 ListCell *child;
6924 List *children;
6925
6926 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6927
6928 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6930 cmd->behavior);
6931
6932 foreach(child, children)
6933 {
6934 Oid childrelid = lfirst_oid(child);
6935 Relation childrel;
6936
6937 childrel = relation_open(childrelid, lockmode);
6938 CheckAlterTableIsSafe(childrel);
6939 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6940 relation_close(childrel, NoLock);
6941 }
6942}
6943
6944
6945/*
6946 * find_composite_type_dependencies
6947 *
6948 * Check to see if the type "typeOid" is being used as a column in some table
6949 * (possibly nested several levels deep in composite types, arrays, etc!).
6950 * Eventually, we'd like to propagate the check or rewrite operation
6951 * into such tables, but for now, just error out if we find any.
6952 *
6953 * Caller should provide either the associated relation of a rowtype,
6954 * or a type name (not both) for use in the error message, if any.
6955 *
6956 * Note that "typeOid" is not necessarily a composite type; it could also be
6957 * another container type such as an array or range, or a domain over one of
6958 * these things. The name of this function is therefore somewhat historical,
6959 * but it's not worth changing.
6960 *
6961 * We assume that functions and views depending on the type are not reasons
6962 * to reject the ALTER. (How safe is this really?)
6963 */
6964void
6966 const char *origTypeName)
6967{
6968 Relation depRel;
6969 ScanKeyData key[2];
6970 SysScanDesc depScan;
6971 HeapTuple depTup;
6972
6973 /* since this function recurses, it could be driven to stack overflow */
6975
6976 /*
6977 * We scan pg_depend to find those things that depend on the given type.
6978 * (We assume we can ignore refobjsubid for a type.)
6979 */
6980 depRel = table_open(DependRelationId, AccessShareLock);
6981
6982 ScanKeyInit(&key[0],
6983 Anum_pg_depend_refclassid,
6984 BTEqualStrategyNumber, F_OIDEQ,
6985 ObjectIdGetDatum(TypeRelationId));
6986 ScanKeyInit(&key[1],
6987 Anum_pg_depend_refobjid,
6988 BTEqualStrategyNumber, F_OIDEQ,
6989 ObjectIdGetDatum(typeOid));
6990
6991 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6992 NULL, 2, key);
6993
6994 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6995 {
6996 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6997 Relation rel;
6998 TupleDesc tupleDesc;
7000
7001 /* Check for directly dependent types */
7002 if (pg_depend->classid == TypeRelationId)
7003 {
7004 /*
7005 * This must be an array, domain, or range containing the given
7006 * type, so recursively check for uses of this type. Note that
7007 * any error message will mention the original type not the
7008 * container; this is intentional.
7009 */
7010 find_composite_type_dependencies(pg_depend->objid,
7011 origRelation, origTypeName);
7012 continue;
7013 }
7014
7015 /* Else, ignore dependees that aren't relations */
7016 if (pg_depend->classid != RelationRelationId)
7017 continue;
7018
7019 rel = relation_open(pg_depend->objid, AccessShareLock);
7020 tupleDesc = RelationGetDescr(rel);
7021
7022 /*
7023 * If objsubid identifies a specific column, refer to that in error
7024 * messages. Otherwise, search to see if there's a user column of the
7025 * type. (We assume system columns are never of interesting types.)
7026 * The search is needed because an index containing an expression
7027 * column of the target type will just be recorded as a whole-relation
7028 * dependency. If we do not find a column of the type, the dependency
7029 * must indicate that the type is transiently referenced in an index
7030 * expression but not stored on disk, which we assume is OK, just as
7031 * we do for references in views. (It could also be that the target
7032 * type is embedded in some container type that is stored in an index
7033 * column, but the previous recursion should catch such cases.)
7034 */
7035 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
7036 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7037 else
7038 {
7039 att = NULL;
7040 for (int attno = 1; attno <= tupleDesc->natts; attno++)
7041 {
7042 att = TupleDescAttr(tupleDesc, attno - 1);
7043 if (att->atttypid == typeOid && !att->attisdropped)
7044 break;
7045 att = NULL;
7046 }
7047 if (att == NULL)
7048 {
7049 /* No such column, so assume OK */
7051 continue;
7052 }
7053 }
7054
7055 /*
7056 * We definitely should reject if the relation has storage. If it's
7057 * partitioned, then perhaps we don't have to reject: if there are
7058 * partitions then we'll fail when we find one, else there is no
7059 * stored data to worry about. However, it's possible that the type
7060 * change would affect conclusions about whether the type is sortable
7061 * or hashable and thus (if it's a partitioning column) break the
7062 * partitioning rule. For now, reject for partitioned rels too.
7063 */
7064 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7065 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7066 {
7067 if (origTypeName)
7068 ereport(ERROR,
7069 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7070 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7071 origTypeName,
7073 NameStr(att->attname))));
7074 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7075 ereport(ERROR,
7076 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7077 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7078 RelationGetRelationName(origRelation),
7080 NameStr(att->attname))));
7081 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7082 ereport(ERROR,
7083 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7084 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7085 RelationGetRelationName(origRelation),
7087 NameStr(att->attname))));
7088 else
7089 ereport(ERROR,
7090 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7091 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7092 RelationGetRelationName(origRelation),
7094 NameStr(att->attname))));
7095 }
7096 else if (OidIsValid(rel->rd_rel->reltype))
7097 {
7098 /*
7099 * A view or composite type itself isn't a problem, but we must
7100 * recursively check for indirect dependencies via its rowtype.
7101 */
7103 origRelation, origTypeName);
7104 }
7105
7107 }
7108
7109 systable_endscan(depScan);
7110
7112}
7113
7114
7115/*
7116 * find_typed_table_dependencies
7117 *
7118 * Check to see if a composite type is being used as the type of a
7119 * typed table. Abort if any are found and behavior is RESTRICT.
7120 * Else return the list of tables.
7121 */
7122static List *
7123find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7124{
7125 Relation classRel;
7126 ScanKeyData key[1];
7127 TableScanDesc scan;
7128 HeapTuple tuple;
7129 List *result = NIL;
7130
7131 classRel = table_open(RelationRelationId, AccessShareLock);
7132
7133 ScanKeyInit(&key[0],
7134 Anum_pg_class_reloftype,
7135 BTEqualStrategyNumber, F_OIDEQ,
7136 ObjectIdGetDatum(typeOid));
7137
7138 scan = table_beginscan_catalog(classRel, 1, key);
7139
7140 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7141 {
7142 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7143
7144 if (behavior == DROP_RESTRICT)
7145 ereport(ERROR,
7146 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7147 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7148 typeName),
7149 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7150 else
7151 result = lappend_oid(result, classform->oid);
7152 }
7153
7154 table_endscan(scan);
7155 table_close(classRel, AccessShareLock);
7156
7157 return result;
7158}
7159
7160
7161/*
7162 * check_of_type
7163 *
7164 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7165 * isn't suitable, throw an error. Currently, we require that the type
7166 * originated with CREATE TYPE AS. We could support any row type, but doing so
7167 * would require handling a number of extra corner cases in the DDL commands.
7168 * (Also, allowing domain-over-composite would open up a can of worms about
7169 * whether and how the domain's constraints should apply to derived tables.)
7170 */
7171void
7173{
7174 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7175 bool typeOk = false;
7176
7177 if (typ->typtype == TYPTYPE_COMPOSITE)
7178 {
7179 Relation typeRelation;
7180
7181 Assert(OidIsValid(typ->typrelid));
7182 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7183 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7184
7185 /*
7186 * Close the parent rel, but keep our AccessShareLock on it until xact
7187 * commit. That will prevent someone else from deleting or ALTERing
7188 * the type before the typed table creation/conversion commits.
7189 */
7190 relation_close(typeRelation, NoLock);
7191
7192 if (!typeOk)
7193 ereport(ERROR,
7194 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7195 errmsg("type %s is the row type of another table",
7196 format_type_be(typ->oid)),
7197 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7198 }
7199 else
7200 ereport(ERROR,
7201 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7202 errmsg("type %s is not a composite type",
7203 format_type_be(typ->oid))));
7204}
7205
7206
7207/*
7208 * ALTER TABLE ADD COLUMN
7209 *
7210 * Adds an additional attribute to a relation making the assumption that
7211 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7212 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7213 * AlterTableCmd's.
7214 *
7215 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7216 * have to decide at runtime whether to recurse or not depending on whether we
7217 * actually add a column or merely merge with an existing column. (We can't
7218 * check this in a static pre-pass because it won't handle multiple inheritance
7219 * situations correctly.)
7220 */
7221static void
7222ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7223 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7224 AlterTableUtilityContext *context)
7225{
7226 if (rel->rd_rel->reloftype && !recursing)
7227 ereport(ERROR,
7228 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7229 errmsg("cannot add column to typed table")));
7230
7231 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7232 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7233
7234 if (recurse && !is_view)
7235 cmd->recurse = true;
7236}
7237
7238/*
7239 * Add a column to a table. The return value is the address of the
7240 * new column in the parent relation.
7241 *
7242 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7243 * copy (but that happens only after we check for IF NOT EXISTS).
7244 */
7245static ObjectAddress
7247 AlterTableCmd **cmd, bool recurse, bool recursing,
7248 LOCKMODE lockmode, AlterTablePass cur_pass,
7249 AlterTableUtilityContext *context)
7250{
7251 Oid myrelid = RelationGetRelid(rel);
7252 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7253 bool if_not_exists = (*cmd)->missing_ok;
7254 Relation pgclass,
7255 attrdesc;
7256 HeapTuple reltup;
7257 Form_pg_class relform;
7258 Form_pg_attribute attribute;
7259 int newattnum;
7260 char relkind;
7261 Expr *defval;
7262 List *children;
7263 ListCell *child;
7264 AlterTableCmd *childcmd;
7265 ObjectAddress address;
7266 TupleDesc tupdesc;
7267
7268 /* since this function recurses, it could be driven to stack overflow */
7270
7271 /* At top level, permission check was done in ATPrepCmd, else do it */
7272 if (recursing)
7273 ATSimplePermissions((*cmd)->subtype, rel,
7275
7276 if (rel->rd_rel->relispartition && !recursing)
7277 ereport(ERROR,
7278 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7279 errmsg("cannot add column to a partition")));
7280
7281 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7282
7283 /*
7284 * Are we adding the column to a recursion child? If so, check whether to
7285 * merge with an existing definition for the column. If we do merge, we
7286 * must not recurse. Children will already have the column, and recursing
7287 * into them would mess up attinhcount.
7288 */
7289 if (colDef->inhcount > 0)
7290 {
7291 HeapTuple tuple;
7292
7293 /* Does child already have a column by this name? */
7294 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7295 if (HeapTupleIsValid(tuple))
7296 {
7297 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7298 Oid ctypeId;
7299 int32 ctypmod;
7300 Oid ccollid;
7301
7302 /* Child column must match on type, typmod, and collation */
7303 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7304 if (ctypeId != childatt->atttypid ||
7305 ctypmod != childatt->atttypmod)
7306 ereport(ERROR,
7307 (errcode(ERRCODE_DATATYPE_MISMATCH),
7308 errmsg("child table \"%s\" has different type for column \"%s\"",
7309 RelationGetRelationName(rel), colDef->colname)));
7310 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7311 if (ccollid != childatt->attcollation)
7312 ereport(ERROR,
7313 (errcode(ERRCODE_COLLATION_MISMATCH),
7314 errmsg("child table \"%s\" has different collation for column \"%s\"",
7315 RelationGetRelationName(rel), colDef->colname),
7316 errdetail("\"%s\" versus \"%s\"",
7317 get_collation_name(ccollid),
7318 get_collation_name(childatt->attcollation))));
7319
7320 /* Bump the existing child att's inhcount */
7321 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7322 &childatt->attinhcount))
7323 ereport(ERROR,
7324 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7325 errmsg("too many inheritance parents"));
7326 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7327
7328 heap_freetuple(tuple);
7329
7330 /* Inform the user about the merge */
7332 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7333 colDef->colname, RelationGetRelationName(rel))));
7334
7335 table_close(attrdesc, RowExclusiveLock);
7336
7337 /* Make the child column change visible */
7339
7340 return InvalidObjectAddress;
7341 }
7342 }
7343
7344 /* skip if the name already exists and if_not_exists is true */
7345 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7346 {
7347 table_close(attrdesc, RowExclusiveLock);
7348 return InvalidObjectAddress;
7349 }
7350
7351 /*
7352 * Okay, we need to add the column, so go ahead and do parse
7353 * transformation. This can result in queueing up, or even immediately
7354 * executing, subsidiary operations (such as creation of unique indexes);
7355 * so we mustn't do it until we have made the if_not_exists check.
7356 *
7357 * When recursing, the command was already transformed and we needn't do
7358 * so again. Also, if context isn't given we can't transform. (That
7359 * currently happens only for AT_AddColumnToView; we expect that view.c
7360 * passed us a ColumnDef that doesn't need work.)
7361 */
7362 if (context != NULL && !recursing)
7363 {
7364 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7365 cur_pass, context);
7366 Assert(*cmd != NULL);
7367 colDef = castNode(ColumnDef, (*cmd)->def);
7368 }
7369
7370 /*
7371 * Regular inheritance children are independent enough not to inherit the
7372 * identity column from parent hence cannot recursively add identity
7373 * column if the table has inheritance children.
7374 *
7375 * Partitions, on the other hand, are integral part of a partitioned table
7376 * and inherit identity column. Hence propagate identity column down the
7377 * partition hierarchy.
7378 */
7379 if (colDef->identity &&
7380 recurse &&
7381 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7383 ereport(ERROR,
7384 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7385 errmsg("cannot recursively add identity column to table that has child tables")));
7386
7387 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7388
7389 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7390 if (!HeapTupleIsValid(reltup))
7391 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7392 relform = (Form_pg_class) GETSTRUCT(reltup);
7393 relkind = relform->relkind;
7394
7395 /* Determine the new attribute's number */
7396 newattnum = relform->relnatts + 1;
7397 if (newattnum > MaxHeapAttributeNumber)
7398 ereport(ERROR,
7399 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7400 errmsg("tables can have at most %d columns",
7402
7403 /*
7404 * Construct new attribute's pg_attribute entry.
7405 */
7406 tupdesc = BuildDescForRelation(list_make1(colDef));
7407
7408 attribute = TupleDescAttr(tupdesc, 0);
7409
7410 /* Fix up attribute number */
7411 attribute->attnum = newattnum;
7412
7413 /* make sure datatype is legal for a column */
7414 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7415 list_make1_oid(rel->rd_rel->reltype),
7416 (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7417
7418 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7419
7420 table_close(attrdesc, RowExclusiveLock);
7421
7422 /*
7423 * Update pg_class tuple as appropriate
7424 */
7425 relform->relnatts = newattnum;
7426
7427 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7428
7429 heap_freetuple(reltup);
7430
7431 /* Post creation hook for new attribute */
7432 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7433
7434 table_close(pgclass, RowExclusiveLock);
7435
7436 /* Make the attribute's catalog entry visible */
7438
7439 /*
7440 * Store the DEFAULT, if any, in the catalogs
7441 */
7442 if (colDef->raw_default)
7443 {
7444 RawColumnDefault *rawEnt;
7445
7447 rawEnt->attnum = attribute->attnum;
7448 rawEnt->raw_default = copyObject(colDef->raw_default);
7449 rawEnt->generated = colDef->generated;
7450
7451 /*
7452 * This function is intended for CREATE TABLE, so it processes a
7453 * _list_ of defaults, but we just do one.
7454 */
7456 false, true, false, NULL);
7457
7458 /* Make the additional catalog changes visible */
7460 }
7461
7462 /*
7463 * Tell Phase 3 to fill in the default expression, if there is one.
7464 *
7465 * If there is no default, Phase 3 doesn't have to do anything, because
7466 * that effectively means that the default is NULL. The heap tuple access
7467 * routines always check for attnum > # of attributes in tuple, and return
7468 * NULL if so, so without any modification of the tuple data we will get
7469 * the effect of NULL values in the new column.
7470 *
7471 * An exception occurs when the new column is of a domain type: the domain
7472 * might have a not-null constraint, or a check constraint that indirectly
7473 * rejects nulls. If there are any domain constraints then we construct
7474 * an explicit NULL default value that will be passed through
7475 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7476 * rewriting the table which we really wouldn't have to do; but we do it
7477 * to preserve the historical behavior that such a failure will be raised
7478 * only if the table currently contains some rows.)
7479 *
7480 * Note: we use build_column_default, and not just the cooked default
7481 * returned by AddRelationNewConstraints, so that the right thing happens
7482 * when a datatype's default applies.
7483 *
7484 * Note: it might seem that this should happen at the end of Phase 2, so
7485 * that the effects of subsequent subcommands can be taken into account.
7486 * It's intentional that we do it now, though. The new column should be
7487 * filled according to what is said in the ADD COLUMN subcommand, so that
7488 * the effects are the same as if this subcommand had been run by itself
7489 * and the later subcommands had been issued in new ALTER TABLE commands.
7490 *
7491 * We can skip this entirely for relations without storage, since Phase 3
7492 * is certainly not going to touch them.
7493 */
7494 if (RELKIND_HAS_STORAGE(relkind))
7495 {
7496 bool has_domain_constraints;
7497 bool has_missing = false;
7498
7499 /*
7500 * For an identity column, we can't use build_column_default(),
7501 * because the sequence ownership isn't set yet. So do it manually.
7502 */
7503 if (colDef->identity)
7504 {
7506
7507 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7508 nve->typeId = attribute->atttypid;
7509
7510 defval = (Expr *) nve;
7511 }
7512 else
7513 defval = (Expr *) build_column_default(rel, attribute->attnum);
7514
7515 /* Build CoerceToDomain(NULL) expression if needed */
7516 has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7517 if (!defval && has_domain_constraints)
7518 {
7519 Oid baseTypeId;
7520 int32 baseTypeMod;
7521 Oid baseTypeColl;
7522
7523 baseTypeMod = attribute->atttypmod;
7524 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7525 baseTypeColl = get_typcollation(baseTypeId);
7526 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7527 defval = (Expr *) coerce_to_target_type(NULL,
7528 (Node *) defval,
7529 baseTypeId,
7530 attribute->atttypid,
7531 attribute->atttypmod,
7534 -1);
7535 if (defval == NULL) /* should not happen */
7536 elog(ERROR, "failed to coerce base type to domain");
7537 }
7538
7539 if (defval)
7540 {
7542
7543 /* Prepare defval for execution, either here or in Phase 3 */
7544 defval = expression_planner(defval);
7545
7546 /* Add the new default to the newvals list */
7548 newval->attnum = attribute->attnum;
7549 newval->expr = defval;
7550 newval->is_generated = (colDef->generated != '\0');
7551
7552 tab->newvals = lappend(tab->newvals, newval);
7553
7554 /*
7555 * Attempt to skip a complete table rewrite by storing the
7556 * specified DEFAULT value outside of the heap. This is only
7557 * allowed for plain relations and non-generated columns, and the
7558 * default expression can't be volatile (stable is OK). Note that
7559 * contain_volatile_functions deems CoerceToDomain immutable, but
7560 * here we consider that coercion to a domain with constraints is
7561 * volatile; else it might fail even when the table is empty.
7562 */
7563 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7564 !colDef->generated &&
7565 !has_domain_constraints &&
7566 !contain_volatile_functions((Node *) defval))
7567 {
7568 EState *estate;
7569 ExprState *exprState;
7570 Datum missingval;
7571 bool missingIsNull;
7572
7573 /* Evaluate the default expression */
7574 estate = CreateExecutorState();
7575 exprState = ExecPrepareExpr(defval, estate);
7576 missingval = ExecEvalExpr(exprState,
7577 GetPerTupleExprContext(estate),
7578 &missingIsNull);
7579 /* If it turns out NULL, nothing to do; else store it */
7580 if (!missingIsNull)
7581 {
7582 StoreAttrMissingVal(rel, attribute->attnum, missingval);
7583 /* Make the additional catalog change visible */
7585 has_missing = true;
7586 }
7587 FreeExecutorState(estate);
7588 }
7589 else
7590 {
7591 /*
7592 * Failed to use missing mode. We have to do a table rewrite
7593 * to install the value --- unless it's a virtual generated
7594 * column.
7595 */
7596 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7598 }
7599 }
7600
7601 if (!has_missing)
7602 {
7603 /*
7604 * If the new column is NOT NULL, and there is no missing value,
7605 * tell Phase 3 it needs to check for NULLs.
7606 */
7607 tab->verify_new_notnull |= colDef->is_not_null;
7608 }
7609 }
7610
7611 /*
7612 * Add needed dependency entries for the new column.
7613 */
7614 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7615 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7616
7617 /*
7618 * Propagate to children as appropriate. Unlike most other ALTER
7619 * routines, we have to do this one level of recursion at a time; we can't
7620 * use find_all_inheritors to do it in one pass.
7621 */
7622 children =
7624
7625 /*
7626 * If we are told not to recurse, there had better not be any child
7627 * tables; else the addition would put them out of step.
7628 */
7629 if (children && !recurse)
7630 ereport(ERROR,
7631 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7632 errmsg("column must be added to child tables too")));
7633
7634 /* Children should see column as singly inherited */
7635 if (!recursing)
7636 {
7637 childcmd = copyObject(*cmd);
7638 colDef = castNode(ColumnDef, childcmd->def);
7639 colDef->inhcount = 1;
7640 colDef->is_local = false;
7641 }
7642 else
7643 childcmd = *cmd; /* no need to copy again */
7644
7645 foreach(child, children)
7646 {
7647 Oid childrelid = lfirst_oid(child);
7648 Relation childrel;
7649 AlteredTableInfo *childtab;
7650
7651 /* find_inheritance_children already got lock */
7652 childrel = table_open(childrelid, NoLock);
7653 CheckAlterTableIsSafe(childrel);
7654
7655 /* Find or create work queue entry for this table */
7656 childtab = ATGetQueueEntry(wqueue, childrel);
7657
7658 /* Recurse to child; return value is ignored */
7659 ATExecAddColumn(wqueue, childtab, childrel,
7660 &childcmd, recurse, true,
7661 lockmode, cur_pass, context);
7662
7663 table_close(childrel, NoLock);
7664 }
7665
7666 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7667 return address;
7668}
7669
7670/*
7671 * If a new or renamed column will collide with the name of an existing
7672 * column and if_not_exists is false then error out, else do nothing.
7673 */
7674static bool
7676 bool if_not_exists)
7677{
7678 HeapTuple attTuple;
7679 int attnum;
7680
7681 /*
7682 * this test is deliberately not attisdropped-aware, since if one tries to
7683 * add a column matching a dropped column name, it's gonna fail anyway.
7684 */
7685 attTuple = SearchSysCache2(ATTNAME,
7687 PointerGetDatum(colname));
7688 if (!HeapTupleIsValid(attTuple))
7689 return true;
7690
7691 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7692 ReleaseSysCache(attTuple);
7693
7694 /*
7695 * We throw a different error message for conflicts with system column
7696 * names, since they are normally not shown and the user might otherwise
7697 * be confused about the reason for the conflict.
7698 */
7699 if (attnum <= 0)
7700 ereport(ERROR,
7701 (errcode(ERRCODE_DUPLICATE_COLUMN),
7702 errmsg("column name \"%s\" conflicts with a system column name",
7703 colname)));
7704 else
7705 {
7706 if (if_not_exists)
7707 {
7709 (errcode(ERRCODE_DUPLICATE_COLUMN),
7710 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7711 colname, RelationGetRelationName(rel))));
7712 return false;
7713 }
7714
7715 ereport(ERROR,
7716 (errcode(ERRCODE_DUPLICATE_COLUMN),
7717 errmsg("column \"%s\" of relation \"%s\" already exists",
7718 colname, RelationGetRelationName(rel))));
7719 }
7720
7721 return true;
7722}
7723
7724/*
7725 * Install a column's dependency on its datatype.
7726 */
7727static void
7729{
7730 ObjectAddress myself,
7731 referenced;
7732
7733 myself.classId = RelationRelationId;
7734 myself.objectId = relid;
7735 myself.objectSubId = attnum;
7736 referenced.classId = TypeRelationId;
7737 referenced.objectId = typid;
7738 referenced.objectSubId = 0;
7739 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7740}
7741
7742/*
7743 * Install a column's dependency on its collation.
7744 */
7745static void
7747{
7748 ObjectAddress myself,
7749 referenced;
7750
7751 /* We know the default collation is pinned, so don't bother recording it */
7752 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7753 {
7754 myself.classId = RelationRelationId;
7755 myself.objectId = relid;
7756 myself.objectSubId = attnum;
7757 referenced.classId = CollationRelationId;
7758 referenced.objectId = collid;
7759 referenced.objectSubId = 0;
7760 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7761 }
7762}
7763
7764/*
7765 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7766 *
7767 * Return the address of the modified column. If the column was already
7768 * nullable, InvalidObjectAddress is returned.
7769 */
7770static ObjectAddress
7771ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7772 LOCKMODE lockmode)
7773{
7774 HeapTuple tuple;
7775 HeapTuple conTup;
7776 Form_pg_attribute attTup;
7778 Relation attr_rel;
7779 ObjectAddress address;
7780
7781 /*
7782 * lookup the attribute
7783 */
7784 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7785
7786 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7787 if (!HeapTupleIsValid(tuple))
7788 ereport(ERROR,
7789 (errcode(ERRCODE_UNDEFINED_COLUMN),
7790 errmsg("column \"%s\" of relation \"%s\" does not exist",
7791 colName, RelationGetRelationName(rel))));
7792 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7793 attnum = attTup->attnum;
7794 ObjectAddressSubSet(address, RelationRelationId,
7795 RelationGetRelid(rel), attnum);
7796
7797 /* If the column is already nullable there's nothing to do. */
7798 if (!attTup->attnotnull)
7799 {
7800 table_close(attr_rel, RowExclusiveLock);
7801 return InvalidObjectAddress;
7802 }
7803
7804 /* Prevent them from altering a system attribute */
7805 if (attnum <= 0)
7806 ereport(ERROR,
7807 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7808 errmsg("cannot alter system column \"%s\"",
7809 colName)));
7810
7811 if (attTup->attidentity)
7812 ereport(ERROR,
7813 (errcode(ERRCODE_SYNTAX_ERROR),
7814 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7815 colName, RelationGetRelationName(rel))));
7816
7817 /*
7818 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7819 */
7820 if (rel->rd_rel->relispartition)
7821 {
7822 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7823 Relation parent = table_open(parentId, AccessShareLock);
7824 TupleDesc tupDesc = RelationGetDescr(parent);
7825 AttrNumber parent_attnum;
7826
7827 parent_attnum = get_attnum(parentId, colName);
7828 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7829 ereport(ERROR,
7830 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7831 errmsg("column \"%s\" is marked NOT NULL in parent table",
7832 colName)));
7834 }
7835
7836 /*
7837 * Find the constraint that makes this column NOT NULL, and drop it.
7838 * dropconstraint_internal() resets attnotnull.
7839 */
7841 if (conTup == NULL)
7842 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7843 colName, RelationGetRelationName(rel));
7844
7845 /* The normal case: we have a pg_constraint row, remove it */
7846 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7847 false, lockmode);
7848 heap_freetuple(conTup);
7849
7850 InvokeObjectPostAlterHook(RelationRelationId,
7851 RelationGetRelid(rel), attnum);
7852
7853 table_close(attr_rel, RowExclusiveLock);
7854
7855 return address;
7856}
7857
7858/*
7859 * set_attnotnull
7860 * Helper to update/validate the pg_attribute status of a not-null
7861 * constraint
7862 *
7863 * pg_attribute.attnotnull is set true, if it isn't already.
7864 * If queue_validation is true, also set up wqueue to validate the constraint.
7865 * wqueue may be given as NULL when validation is not needed (e.g., on table
7866 * creation).
7867 */
7868static void
7870 bool is_valid, bool queue_validation)
7871{
7872 Form_pg_attribute attr;
7873 CompactAttribute *thisatt;
7874
7875 Assert(!queue_validation || wqueue);
7876
7878
7879 /*
7880 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7881 * attribute.
7882 */
7883 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7884 if (attr->attisdropped)
7885 return;
7886
7887 if (!attr->attnotnull)
7888 {
7889 Relation attr_rel;
7890 HeapTuple tuple;
7891
7892 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7893
7895 if (!HeapTupleIsValid(tuple))
7896 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7897 attnum, RelationGetRelid(rel));
7898
7899 thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7901
7902 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7903
7904 attr->attnotnull = true;
7905 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7906
7907 /*
7908 * If the nullness isn't already proven by validated constraints, have
7909 * ALTER TABLE phase 3 test for it.
7910 */
7911 if (queue_validation && wqueue &&
7913 {
7914 AlteredTableInfo *tab;
7915
7916 tab = ATGetQueueEntry(wqueue, rel);
7917 tab->verify_new_notnull = true;
7918 }
7919
7921
7922 table_close(attr_rel, RowExclusiveLock);
7923 heap_freetuple(tuple);
7924 }
7925 else
7926 {
7928 }
7929}
7930
7931/*
7932 * ALTER TABLE ALTER COLUMN SET NOT NULL
7933 *
7934 * Add a not-null constraint to a single table and its children. Returns
7935 * the address of the constraint added to the parent relation, if one gets
7936 * added, or InvalidObjectAddress otherwise.
7937 *
7938 * We must recurse to child tables during execution, rather than using
7939 * ALTER TABLE's normal prep-time recursion.
7940 */
7941static ObjectAddress
7942ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7943 bool recurse, bool recursing, LOCKMODE lockmode)
7944{
7945 HeapTuple tuple;
7947 ObjectAddress address;
7948 Constraint *constraint;
7949 CookedConstraint *ccon;
7950 List *cooked;
7951 bool is_no_inherit = false;
7952
7953 /* Guard against stack overflow due to overly deep inheritance tree. */
7955
7956 /* At top level, permission check was done in ATPrepCmd, else do it */
7957 if (recursing)
7958 {
7961 Assert(conName != NULL);
7962 }
7963
7964 attnum = get_attnum(RelationGetRelid(rel), colName);
7966 ereport(ERROR,
7967 (errcode(ERRCODE_UNDEFINED_COLUMN),
7968 errmsg("column \"%s\" of relation \"%s\" does not exist",
7969 colName, RelationGetRelationName(rel))));
7970
7971 /* Prevent them from altering a system attribute */
7972 if (attnum <= 0)
7973 ereport(ERROR,
7974 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7975 errmsg("cannot alter system column \"%s\"",
7976 colName)));
7977
7978 /* See if there's already a constraint */
7980 if (HeapTupleIsValid(tuple))
7981 {
7983 bool changed = false;
7984
7985 /*
7986 * Don't let a NO INHERIT constraint be changed into inherit.
7987 */
7988 if (conForm->connoinherit && recurse)
7989 ereport(ERROR,
7990 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7991 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7992 NameStr(conForm->conname),
7994
7995 /*
7996 * If we find an appropriate constraint, we're almost done, but just
7997 * need to change some properties on it: if we're recursing, increment
7998 * coninhcount; if not, set conislocal if not already set.
7999 */
8000 if (recursing)
8001 {
8002 if (pg_add_s16_overflow(conForm->coninhcount, 1,
8003 &conForm->coninhcount))
8004 ereport(ERROR,
8005 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
8006 errmsg("too many inheritance parents"));
8007 changed = true;
8008 }
8009 else if (!conForm->conislocal)
8010 {
8011 conForm->conislocal = true;
8012 changed = true;
8013 }
8014 else if (!conForm->convalidated)
8015 {
8016 /*
8017 * Flip attnotnull and convalidated, and also validate the
8018 * constraint.
8019 */
8020 return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
8021 recurse, recursing, lockmode);
8022 }
8023
8024 if (changed)
8025 {
8026 Relation constr_rel;
8027
8028 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
8029
8030 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
8031 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
8032 table_close(constr_rel, RowExclusiveLock);
8033 }
8034
8035 if (changed)
8036 return address;
8037 else
8038 return InvalidObjectAddress;
8039 }
8040
8041 /*
8042 * If we're asked not to recurse, and children exist, raise an error for
8043 * partitioned tables. For inheritance, we act as if NO INHERIT had been
8044 * specified.
8045 */
8046 if (!recurse &&
8048 NoLock) != NIL)
8049 {
8050 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8051 ereport(ERROR,
8052 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8053 errmsg("constraint must be added to child tables too"),
8054 errhint("Do not specify the ONLY keyword."));
8055 else
8056 is_no_inherit = true;
8057 }
8058
8059 /*
8060 * No constraint exists; we must add one. First determine a name to use,
8061 * if we haven't already.
8062 */
8063 if (!recursing)
8064 {
8065 Assert(conName == NULL);
8067 colName, "not_null",
8069 NIL);
8070 }
8071
8072 constraint = makeNotNullConstraint(makeString(colName));
8073 constraint->is_no_inherit = is_no_inherit;
8074 constraint->conname = conName;
8075
8076 /* and do it */
8077 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8078 false, !recursing, false, NULL);
8079 ccon = linitial(cooked);
8080 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8081
8082 InvokeObjectPostAlterHook(RelationRelationId,
8083 RelationGetRelid(rel), attnum);
8084
8085 /* Mark pg_attribute.attnotnull for the column and queue validation */
8086 set_attnotnull(wqueue, rel, attnum, true, true);
8087
8088 /*
8089 * Recurse to propagate the constraint to children that don't have one.
8090 */
8091 if (recurse)
8092 {
8093 List *children;
8094
8096 lockmode);
8097
8098 foreach_oid(childoid, children)
8099 {
8100 Relation childrel = table_open(childoid, NoLock);
8101
8103
8104 ATExecSetNotNull(wqueue, childrel, conName, colName,
8105 recurse, true, lockmode);
8106 table_close(childrel, NoLock);
8107 }
8108 }
8109
8110 return address;
8111}
8112
8113/*
8114 * NotNullImpliedByRelConstraints
8115 * Does rel's existing constraints imply NOT NULL for the given attribute?
8116 */
8117static bool
8119{
8120 NullTest *nnulltest = makeNode(NullTest);
8121
8122 nnulltest->arg = (Expr *) makeVar(1,
8123 attr->attnum,
8124 attr->atttypid,
8125 attr->atttypmod,
8126 attr->attcollation,
8127 0);
8128 nnulltest->nulltesttype = IS_NOT_NULL;
8129
8130 /*
8131 * argisrow = false is correct even for a composite column, because
8132 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8133 * case, just IS DISTINCT FROM NULL.
8134 */
8135 nnulltest->argisrow = false;
8136 nnulltest->location = -1;
8137
8138 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8139 {
8141 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8142 RelationGetRelationName(rel), NameStr(attr->attname))));
8143 return true;
8144 }
8145
8146 return false;
8147}
8148
8149/*
8150 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8151 *
8152 * Return the address of the affected column.
8153 */
8154static ObjectAddress
8155ATExecColumnDefault(Relation rel, const char *colName,
8156 Node *newDefault, LOCKMODE lockmode)
8157{
8158 TupleDesc tupdesc = RelationGetDescr(rel);
8160 ObjectAddress address;
8161
8162 /*
8163 * get the number of the attribute
8164 */
8165 attnum = get_attnum(RelationGetRelid(rel), colName);
8167 ereport(ERROR,
8168 (errcode(ERRCODE_UNDEFINED_COLUMN),
8169 errmsg("column \"%s\" of relation \"%s\" does not exist",
8170 colName, RelationGetRelationName(rel))));
8171
8172 /* Prevent them from altering a system attribute */
8173 if (attnum <= 0)
8174 ereport(ERROR,
8175 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8176 errmsg("cannot alter system column \"%s\"",
8177 colName)));
8178
8179 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8180 ereport(ERROR,
8181 (errcode(ERRCODE_SYNTAX_ERROR),
8182 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8183 colName, RelationGetRelationName(rel)),
8184 /* translator: %s is an SQL ALTER command */
8185 newDefault ? 0 : errhint("Use %s instead.",
8186 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8187
8188 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8189 ereport(ERROR,
8190 (errcode(ERRCODE_SYNTAX_ERROR),
8191 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8192 colName, RelationGetRelationName(rel)),
8193 newDefault ?
8194 /* translator: %s is an SQL ALTER command */
8195 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8196 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8197 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8198
8199 /*
8200 * Remove any old default for the column. We use RESTRICT here for
8201 * safety, but at present we do not expect anything to depend on the
8202 * default.
8203 *
8204 * We treat removing the existing default as an internal operation when it
8205 * is preparatory to adding a new default, but as a user-initiated
8206 * operation when the user asked for a drop.
8207 */
8209 newDefault != NULL);
8210
8211 if (newDefault)
8212 {
8213 /* SET DEFAULT */
8214 RawColumnDefault *rawEnt;
8215
8217 rawEnt->attnum = attnum;
8218 rawEnt->raw_default = newDefault;
8219 rawEnt->generated = '\0';
8220
8221 /*
8222 * This function is intended for CREATE TABLE, so it processes a
8223 * _list_ of defaults, but we just do one.
8224 */
8226 false, true, false, NULL);
8227 }
8228
8229 ObjectAddressSubSet(address, RelationRelationId,
8230 RelationGetRelid(rel), attnum);
8231 return address;
8232}
8233
8234/*
8235 * Add a pre-cooked default expression.
8236 *
8237 * Return the address of the affected column.
8238 */
8239static ObjectAddress
8241 Node *newDefault)
8242{
8243 ObjectAddress address;
8244
8245 /* We assume no checking is required */
8246
8247 /*
8248 * Remove any old default for the column. We use RESTRICT here for
8249 * safety, but at present we do not expect anything to depend on the
8250 * default. (In ordinary cases, there could not be a default in place
8251 * anyway, but it's possible when combining LIKE with inheritance.)
8252 */
8254 true);
8255
8256 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8257
8258 ObjectAddressSubSet(address, RelationRelationId,
8259 RelationGetRelid(rel), attnum);
8260 return address;
8261}
8262
8263/*
8264 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8265 *
8266 * Return the address of the affected column.
8267 */
8268static ObjectAddress
8269ATExecAddIdentity(Relation rel, const char *colName,
8270 Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8271{
8272 Relation attrelation;
8273 HeapTuple tuple;
8274 Form_pg_attribute attTup;
8276 ObjectAddress address;
8277 ColumnDef *cdef = castNode(ColumnDef, def);
8278 bool ispartitioned;
8279
8280 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8281 if (ispartitioned && !recurse)
8282 ereport(ERROR,
8283 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8284 errmsg("cannot add identity to a column of only the partitioned table"),
8285 errhint("Do not specify the ONLY keyword.")));
8286
8287 if (rel->rd_rel->relispartition && !recursing)
8288 ereport(ERROR,
8289 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8290 errmsg("cannot add identity to a column of a partition"));
8291
8292 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8293
8294 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8295 if (!HeapTupleIsValid(tuple))
8296 ereport(ERROR,
8297 (errcode(ERRCODE_UNDEFINED_COLUMN),
8298 errmsg("column \"%s\" of relation \"%s\" does not exist",
8299 colName, RelationGetRelationName(rel))));
8300 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8301 attnum = attTup->attnum;
8302
8303 /* Can't alter a system attribute */
8304 if (attnum <= 0)
8305 ereport(ERROR,
8306 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8307 errmsg("cannot alter system column \"%s\"",
8308 colName)));
8309
8310 /*
8311 * Creating a column as identity implies NOT NULL, so adding the identity
8312 * to an existing column that is not NOT NULL would create a state that
8313 * cannot be reproduced without contortions.
8314 */
8315 if (!attTup->attnotnull)
8316 ereport(ERROR,
8317 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8318 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8319 colName, RelationGetRelationName(rel))));
8320
8321 /*
8322 * On the other hand, if a not-null constraint exists, then verify that
8323 * it's compatible.
8324 */
8325 if (attTup->attnotnull)
8326 {
8327 HeapTuple contup;
8328 Form_pg_constraint conForm;
8329
8331 attnum);
8332 if (!HeapTupleIsValid(contup))
8333 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8334 colName, RelationGetRelationName(rel));
8335
8336 conForm = (Form_pg_constraint) GETSTRUCT(contup);
8337 if (!conForm->convalidated)
8338 ereport(ERROR,
8339 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8340 errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8341 NameStr(conForm->conname), RelationGetRelationName(rel)),
8342 errhint("You might need to validate it using %s.",
8343 "ALTER TABLE ... VALIDATE CONSTRAINT"));
8344 }
8345
8346 if (attTup->attidentity)
8347 ereport(ERROR,
8348 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8349 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8350 colName, RelationGetRelationName(rel))));
8351
8352 if (attTup->atthasdef)
8353 ereport(ERROR,
8354 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8355 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8356 colName, RelationGetRelationName(rel))));
8357
8358 attTup->attidentity = cdef->identity;
8359 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8360
8361 InvokeObjectPostAlterHook(RelationRelationId,
8362 RelationGetRelid(rel),
8363 attTup->attnum);
8364 ObjectAddressSubSet(address, RelationRelationId,
8365 RelationGetRelid(rel), attnum);
8366 heap_freetuple(tuple);
8367
8368 table_close(attrelation, RowExclusiveLock);
8369
8370 /*
8371 * Recurse to propagate the identity column to partitions. Identity is
8372 * not inherited in regular inheritance children.
8373 */
8374 if (recurse && ispartitioned)
8375 {
8376 List *children;
8377 ListCell *lc;
8378
8379 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8380
8381 foreach(lc, children)
8382 {
8383 Relation childrel;
8384
8385 childrel = table_open(lfirst_oid(lc), NoLock);
8386 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8387 table_close(childrel, NoLock);
8388 }
8389 }
8390
8391 return address;
8392}
8393
8394/*
8395 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8396 *
8397 * Return the address of the affected column.
8398 */
8399static ObjectAddress
8400ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8401 LOCKMODE lockmode, bool recurse, bool recursing)
8402{
8404 DefElem *generatedEl = NULL;
8405 HeapTuple tuple;
8406 Form_pg_attribute attTup;
8408 Relation attrelation;
8409 ObjectAddress address;
8410 bool ispartitioned;
8411
8412 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8413 if (ispartitioned && !recurse)
8414 ereport(ERROR,
8415 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8416 errmsg("cannot change identity column of only the partitioned table"),
8417 errhint("Do not specify the ONLY keyword.")));
8418
8419 if (rel->rd_rel->relispartition && !recursing)
8420 ereport(ERROR,
8421 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8422 errmsg("cannot change identity column of a partition"));
8423
8424 foreach(option, castNode(List, def))
8425 {
8426 DefElem *defel = lfirst_node(DefElem, option);
8427
8428 if (strcmp(defel->defname, "generated") == 0)
8429 {
8430 if (generatedEl)
8431 ereport(ERROR,
8432 (errcode(ERRCODE_SYNTAX_ERROR),
8433 errmsg("conflicting or redundant options")));
8434 generatedEl = defel;
8435 }
8436 else
8437 elog(ERROR, "option \"%s\" not recognized",
8438 defel->defname);
8439 }
8440
8441 /*
8442 * Even if there is nothing to change here, we run all the checks. There
8443 * will be a subsequent ALTER SEQUENCE that relies on everything being
8444 * there.
8445 */
8446
8447 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8448 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8449 if (!HeapTupleIsValid(tuple))
8450 ereport(ERROR,
8451 (errcode(ERRCODE_UNDEFINED_COLUMN),
8452 errmsg("column \"%s\" of relation \"%s\" does not exist",
8453 colName, RelationGetRelationName(rel))));
8454
8455 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8456 attnum = attTup->attnum;
8457
8458 if (attnum <= 0)
8459 ereport(ERROR,
8460 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8461 errmsg("cannot alter system column \"%s\"",
8462 colName)));
8463
8464 if (!attTup->attidentity)
8465 ereport(ERROR,
8466 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8467 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8468 colName, RelationGetRelationName(rel))));
8469
8470 if (generatedEl)
8471 {
8472 attTup->attidentity = defGetInt32(generatedEl);
8473 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8474
8475 InvokeObjectPostAlterHook(RelationRelationId,
8476 RelationGetRelid(rel),
8477 attTup->attnum);
8478 ObjectAddressSubSet(address, RelationRelationId,
8479 RelationGetRelid(rel), attnum);
8480 }
8481 else
8482 address = InvalidObjectAddress;
8483
8484 heap_freetuple(tuple);
8485 table_close(attrelation, RowExclusiveLock);
8486
8487 /*
8488 * Recurse to propagate the identity change to partitions. Identity is not
8489 * inherited in regular inheritance children.
8490 */
8491 if (generatedEl && recurse && ispartitioned)
8492 {
8493 List *children;
8494 ListCell *lc;
8495
8496 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8497
8498 foreach(lc, children)
8499 {
8500 Relation childrel;
8501
8502 childrel = table_open(lfirst_oid(lc), NoLock);
8503 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8504 table_close(childrel, NoLock);
8505 }
8506 }
8507
8508 return address;
8509}
8510
8511/*
8512 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8513 *
8514 * Return the address of the affected column.
8515 */
8516static ObjectAddress
8517ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8518 bool recurse, bool recursing)
8519{
8520 HeapTuple tuple;
8521 Form_pg_attribute attTup;
8523 Relation attrelation;
8524 ObjectAddress address;
8525 Oid seqid;
8526 ObjectAddress seqaddress;
8527 bool ispartitioned;
8528
8529 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8530 if (ispartitioned && !recurse)
8531 ereport(ERROR,
8532 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8533 errmsg("cannot drop identity from a column of only the partitioned table"),
8534 errhint("Do not specify the ONLY keyword.")));
8535
8536 if (rel->rd_rel->relispartition && !recursing)
8537 ereport(ERROR,
8538 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8539 errmsg("cannot drop identity from a column of a partition"));
8540
8541 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8542 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8543 if (!HeapTupleIsValid(tuple))
8544 ereport(ERROR,
8545 (errcode(ERRCODE_UNDEFINED_COLUMN),
8546 errmsg("column \"%s\" of relation \"%s\" does not exist",
8547 colName, RelationGetRelationName(rel))));
8548
8549 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8550 attnum = attTup->attnum;
8551
8552 if (attnum <= 0)
8553 ereport(ERROR,
8554 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8555 errmsg("cannot alter system column \"%s\"",
8556 colName)));
8557
8558 if (!attTup->attidentity)
8559 {
8560 if (!missing_ok)
8561 ereport(ERROR,
8562 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8563 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8564 colName, RelationGetRelationName(rel))));
8565 else
8566 {
8568 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8569 colName, RelationGetRelationName(rel))));
8570 heap_freetuple(tuple);
8571 table_close(attrelation, RowExclusiveLock);
8572 return InvalidObjectAddress;
8573 }
8574 }
8575
8576 attTup->attidentity = '\0';
8577 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8578
8579 InvokeObjectPostAlterHook(RelationRelationId,
8580 RelationGetRelid(rel),
8581 attTup->attnum);
8582 ObjectAddressSubSet(address, RelationRelationId,
8583 RelationGetRelid(rel), attnum);
8584 heap_freetuple(tuple);
8585
8586 table_close(attrelation, RowExclusiveLock);
8587
8588 /*
8589 * Recurse to drop the identity from column in partitions. Identity is
8590 * not inherited in regular inheritance children so ignore them.
8591 */
8592 if (recurse && ispartitioned)
8593 {
8594 List *children;
8595 ListCell *lc;
8596
8597 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8598
8599 foreach(lc, children)
8600 {
8601 Relation childrel;
8602
8603 childrel = table_open(lfirst_oid(lc), NoLock);
8604 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8605 table_close(childrel, NoLock);
8606 }
8607 }
8608
8609 if (!recursing)
8610 {
8611 /* drop the internal sequence */
8612 seqid = getIdentitySequence(rel, attnum, false);
8613 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8614 RelationRelationId, DEPENDENCY_INTERNAL);
8616 seqaddress.classId = RelationRelationId;
8617 seqaddress.objectId = seqid;
8618 seqaddress.objectSubId = 0;
8620 }
8621
8622 return address;
8623}
8624
8625/*
8626 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8627 *
8628 * Return the address of the affected column.
8629 */
8630static ObjectAddress
8631ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8632 Node *newExpr, LOCKMODE lockmode)
8633{
8634 HeapTuple tuple;
8635 Form_pg_attribute attTup;
8637 char attgenerated;
8638 bool rewrite;
8639 Oid attrdefoid;
8640 ObjectAddress address;
8641 Expr *defval;
8643 RawColumnDefault *rawEnt;
8644
8645 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8646 if (!HeapTupleIsValid(tuple))
8647 ereport(ERROR,
8648 (errcode(ERRCODE_UNDEFINED_COLUMN),
8649 errmsg("column \"%s\" of relation \"%s\" does not exist",
8650 colName, RelationGetRelationName(rel))));
8651
8652 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8653
8654 attnum = attTup->attnum;
8655 if (attnum <= 0)
8656 ereport(ERROR,
8657 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8658 errmsg("cannot alter system column \"%s\"",
8659 colName)));
8660
8661 attgenerated = attTup->attgenerated;
8662 if (!attgenerated)
8663 ereport(ERROR,
8664 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8665 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8666 colName, RelationGetRelationName(rel))));
8667
8668 /*
8669 * TODO: This could be done, just need to recheck any constraints
8670 * afterwards.
8671 */
8672 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8673 rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8674 ereport(ERROR,
8675 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8676 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
8677 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8678 colName, RelationGetRelationName(rel))));
8679
8680 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8681 tab->verify_new_notnull = true;
8682
8683 /*
8684 * We need to prevent this because a change of expression could affect a
8685 * row filter and inject expressions that are not permitted in a row
8686 * filter. XXX We could try to have a more precise check to catch only
8687 * publications with row filters, or even re-verify the row filter
8688 * expressions.
8689 */
8690 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8692 ereport(ERROR,
8693 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8694 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8695 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8696 colName, RelationGetRelationName(rel))));
8697
8698 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8699
8700 ReleaseSysCache(tuple);
8701
8702 if (rewrite)
8703 {
8704 /*
8705 * Clear all the missing values if we're rewriting the table, since
8706 * this renders them pointless.
8707 */
8709
8710 /* make sure we don't conflict with later attribute modifications */
8712
8713 /*
8714 * Find everything that depends on the column (constraints, indexes,
8715 * etc), and record enough information to let us recreate the objects
8716 * after rewrite.
8717 */
8719 }
8720
8721 /*
8722 * Drop the dependency records of the GENERATED expression, in particular
8723 * its INTERNAL dependency on the column, which would otherwise cause
8724 * dependency.c to refuse to perform the deletion.
8725 */
8726 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8727 if (!OidIsValid(attrdefoid))
8728 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8729 RelationGetRelid(rel), attnum);
8730 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8731
8732 /* Make above changes visible */
8734
8735 /*
8736 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8737 * safety, but at present we do not expect anything to depend on the
8738 * expression.
8739 */
8741 false, false);
8742
8743 /* Prepare to store the new expression, in the catalogs */
8745 rawEnt->attnum = attnum;
8746 rawEnt->raw_default = newExpr;
8747 rawEnt->generated = attgenerated;
8748
8749 /* Store the generated expression */
8751 false, true, false, NULL);
8752
8753 /* Make above new expression visible */
8755
8756 if (rewrite)
8757 {
8758 /* Prepare for table rewrite */
8759 defval = (Expr *) build_column_default(rel, attnum);
8760
8762 newval->attnum = attnum;
8763 newval->expr = expression_planner(defval);
8764 newval->is_generated = true;
8765
8766 tab->newvals = lappend(tab->newvals, newval);
8768 }
8769
8770 /* Drop any pg_statistic entry for the column */
8772
8773 InvokeObjectPostAlterHook(RelationRelationId,
8774 RelationGetRelid(rel), attnum);
8775
8776 ObjectAddressSubSet(address, RelationRelationId,
8777 RelationGetRelid(rel), attnum);
8778 return address;
8779}
8780
8781/*
8782 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8783 */
8784static void
8785ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8786{
8787 /*
8788 * Reject ONLY if there are child tables. We could implement this, but it
8789 * is a bit complicated. GENERATED clauses must be attached to the column
8790 * definition and cannot be added later like DEFAULT, so if a child table
8791 * has a generation expression that the parent does not have, the child
8792 * column will necessarily be an attislocal column. So to implement ONLY
8793 * here, we'd need extra code to update attislocal of the direct child
8794 * tables, somewhat similar to how DROP COLUMN does it, so that the
8795 * resulting state can be properly dumped and restored.
8796 */
8797 if (!recurse &&
8799 ereport(ERROR,
8800 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8801 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8802
8803 /*
8804 * Cannot drop generation expression from inherited columns.
8805 */
8806 if (!recursing)
8807 {
8808 HeapTuple tuple;
8809 Form_pg_attribute attTup;
8810
8812 if (!HeapTupleIsValid(tuple))
8813 ereport(ERROR,
8814 (errcode(ERRCODE_UNDEFINED_COLUMN),
8815 errmsg("column \"%s\" of relation \"%s\" does not exist",
8816 cmd->name, RelationGetRelationName(rel))));
8817
8818 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8819
8820 if (attTup->attinhcount > 0)
8821 ereport(ERROR,
8822 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8823 errmsg("cannot drop generation expression from inherited column")));
8824 }
8825}
8826
8827/*
8828 * Return the address of the affected column.
8829 */
8830static ObjectAddress
8831ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8832{
8833 HeapTuple tuple;
8834 Form_pg_attribute attTup;
8836 Relation attrelation;
8837 Oid attrdefoid;
8838 ObjectAddress address;
8839
8840 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8841 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8842 if (!HeapTupleIsValid(tuple))
8843 ereport(ERROR,
8844 (errcode(ERRCODE_UNDEFINED_COLUMN),
8845 errmsg("column \"%s\" of relation \"%s\" does not exist",
8846 colName, RelationGetRelationName(rel))));
8847
8848 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8849 attnum = attTup->attnum;
8850
8851 if (attnum <= 0)
8852 ereport(ERROR,
8853 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8854 errmsg("cannot alter system column \"%s\"",
8855 colName)));
8856
8857 /*
8858 * TODO: This could be done, but it would need a table rewrite to
8859 * materialize the generated values. Note that for the time being, we
8860 * still error with missing_ok, so that we don't silently leave the column
8861 * as generated.
8862 */
8863 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8864 ereport(ERROR,
8865 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8866 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8867 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8868 colName, RelationGetRelationName(rel))));
8869
8870 if (!attTup->attgenerated)
8871 {
8872 if (!missing_ok)
8873 ereport(ERROR,
8874 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8875 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8876 colName, RelationGetRelationName(rel))));
8877 else
8878 {
8880 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8881 colName, RelationGetRelationName(rel))));
8882 heap_freetuple(tuple);
8883 table_close(attrelation, RowExclusiveLock);
8884 return InvalidObjectAddress;
8885 }
8886 }
8887
8888 /*
8889 * Mark the column as no longer generated. (The atthasdef flag needs to
8890 * get cleared too, but RemoveAttrDefault will handle that.)
8891 */
8892 attTup->attgenerated = '\0';
8893 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8894
8895 InvokeObjectPostAlterHook(RelationRelationId,
8896 RelationGetRelid(rel),
8897 attnum);
8898 heap_freetuple(tuple);
8899
8900 table_close(attrelation, RowExclusiveLock);
8901
8902 /*
8903 * Drop the dependency records of the GENERATED expression, in particular
8904 * its INTERNAL dependency on the column, which would otherwise cause
8905 * dependency.c to refuse to perform the deletion.
8906 */
8907 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8908 if (!OidIsValid(attrdefoid))
8909 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8910 RelationGetRelid(rel), attnum);
8911 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8912
8913 /* Make above changes visible */
8915
8916 /*
8917 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8918 * safety, but at present we do not expect anything to depend on the
8919 * default.
8920 */
8922 false, false);
8923
8924 ObjectAddressSubSet(address, RelationRelationId,
8925 RelationGetRelid(rel), attnum);
8926 return address;
8927}
8928
8929/*
8930 * ALTER TABLE ALTER COLUMN SET STATISTICS
8931 *
8932 * Return value is the address of the modified column
8933 */
8934static ObjectAddress
8935ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8936{
8937 int newtarget = 0;
8938 bool newtarget_default;
8939 Relation attrelation;
8940 HeapTuple tuple,
8941 newtuple;
8942 Form_pg_attribute attrtuple;
8944 ObjectAddress address;
8945 Datum repl_val[Natts_pg_attribute];
8946 bool repl_null[Natts_pg_attribute];
8947 bool repl_repl[Natts_pg_attribute];
8948
8949 /*
8950 * We allow referencing columns by numbers only for indexes, since table
8951 * column numbers could contain gaps if columns are later dropped.
8952 */
8953 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8954 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8955 !colName)
8956 ereport(ERROR,
8957 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8958 errmsg("cannot refer to non-index column by number")));
8959
8960 /* -1 was used in previous versions for the default setting */
8961 if (newValue && intVal(newValue) != -1)
8962 {
8963 newtarget = intVal(newValue);
8964 newtarget_default = false;
8965 }
8966 else
8967 newtarget_default = true;
8968
8969 if (!newtarget_default)
8970 {
8971 /*
8972 * Limit target to a sane range
8973 */
8974 if (newtarget < 0)
8975 {
8976 ereport(ERROR,
8977 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8978 errmsg("statistics target %d is too low",
8979 newtarget)));
8980 }
8981 else if (newtarget > MAX_STATISTICS_TARGET)
8982 {
8983 newtarget = MAX_STATISTICS_TARGET;
8985 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8986 errmsg("lowering statistics target to %d",
8987 newtarget)));
8988 }
8989 }
8990
8991 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8992
8993 if (colName)
8994 {
8995 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8996
8997 if (!HeapTupleIsValid(tuple))
8998 ereport(ERROR,
8999 (errcode(ERRCODE_UNDEFINED_COLUMN),
9000 errmsg("column \"%s\" of relation \"%s\" does not exist",
9001 colName, RelationGetRelationName(rel))));
9002 }
9003 else
9004 {
9005 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
9006
9007 if (!HeapTupleIsValid(tuple))
9008 ereport(ERROR,
9009 (errcode(ERRCODE_UNDEFINED_COLUMN),
9010 errmsg("column number %d of relation \"%s\" does not exist",
9011 colNum, RelationGetRelationName(rel))));
9012 }
9013
9014 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9015
9016 attnum = attrtuple->attnum;
9017 if (attnum <= 0)
9018 ereport(ERROR,
9019 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9020 errmsg("cannot alter system column \"%s\"",
9021 colName)));
9022
9023 /*
9024 * Prevent this as long as the ANALYZE code skips virtual generated
9025 * columns.
9026 */
9027 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9028 ereport(ERROR,
9029 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9030 errmsg("cannot alter statistics on virtual generated column \"%s\"",
9031 colName)));
9032
9033 if (rel->rd_rel->relkind == RELKIND_INDEX ||
9034 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9035 {
9036 if (attnum > rel->rd_index->indnkeyatts)
9037 ereport(ERROR,
9038 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9039 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9040 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9041 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9042 ereport(ERROR,
9043 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9044 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9045 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9046 errhint("Alter statistics on table column instead.")));
9047 }
9048
9049 /* Build new tuple. */
9050 memset(repl_null, false, sizeof(repl_null));
9051 memset(repl_repl, false, sizeof(repl_repl));
9052 if (!newtarget_default)
9053 repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
9054 else
9055 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
9056 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
9057 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9058 repl_val, repl_null, repl_repl);
9059 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9060
9061 InvokeObjectPostAlterHook(RelationRelationId,
9062 RelationGetRelid(rel),
9063 attrtuple->attnum);
9064 ObjectAddressSubSet(address, RelationRelationId,
9065 RelationGetRelid(rel), attnum);
9066
9067 heap_freetuple(newtuple);
9068
9069 ReleaseSysCache(tuple);
9070
9071 table_close(attrelation, RowExclusiveLock);
9072
9073 return address;
9074}
9075
9076/*
9077 * Return value is the address of the modified column
9078 */
9079static ObjectAddress
9080ATExecSetOptions(Relation rel, const char *colName, Node *options,
9081 bool isReset, LOCKMODE lockmode)
9082{
9083 Relation attrelation;
9084 HeapTuple tuple,
9085 newtuple;
9086 Form_pg_attribute attrtuple;
9088 Datum datum,
9089 newOptions;
9090 bool isnull;
9091 ObjectAddress address;
9092 Datum repl_val[Natts_pg_attribute];
9093 bool repl_null[Natts_pg_attribute];
9094 bool repl_repl[Natts_pg_attribute];
9095
9096 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9097
9098 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9099
9100 if (!HeapTupleIsValid(tuple))
9101 ereport(ERROR,
9102 (errcode(ERRCODE_UNDEFINED_COLUMN),
9103 errmsg("column \"%s\" of relation \"%s\" does not exist",
9104 colName, RelationGetRelationName(rel))));
9105 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9106
9107 attnum = attrtuple->attnum;
9108 if (attnum <= 0)
9109 ereport(ERROR,
9110 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9111 errmsg("cannot alter system column \"%s\"",
9112 colName)));
9113
9114 /* Generate new proposed attoptions (text array) */
9115 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9116 &isnull);
9117 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9118 castNode(List, options), NULL, NULL,
9119 false, isReset);
9120 /* Validate new options */
9121 (void) attribute_reloptions(newOptions, true);
9122
9123 /* Build new tuple. */
9124 memset(repl_null, false, sizeof(repl_null));
9125 memset(repl_repl, false, sizeof(repl_repl));
9126 if (newOptions != (Datum) 0)
9127 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9128 else
9129 repl_null[Anum_pg_attribute_attoptions - 1] = true;
9130 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9131 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9132 repl_val, repl_null, repl_repl);
9133
9134 /* Update system catalog. */
9135 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9136
9137 InvokeObjectPostAlterHook(RelationRelationId,
9138 RelationGetRelid(rel),
9139 attrtuple->attnum);
9140 ObjectAddressSubSet(address, RelationRelationId,
9141 RelationGetRelid(rel), attnum);
9142
9143 heap_freetuple(newtuple);
9144
9145 ReleaseSysCache(tuple);
9146
9147 table_close(attrelation, RowExclusiveLock);
9148
9149 return address;
9150}
9151
9152/*
9153 * Helper function for ATExecSetStorage and ATExecSetCompression
9154 *
9155 * Set the attstorage and/or attcompression fields for index columns
9156 * associated with the specified table column.
9157 */
9158static void
9161 bool setstorage, char newstorage,
9162 bool setcompression, char newcompression,
9163 LOCKMODE lockmode)
9164{
9165 ListCell *lc;
9166
9167 foreach(lc, RelationGetIndexList(rel))
9168 {
9169 Oid indexoid = lfirst_oid(lc);
9170 Relation indrel;
9171 AttrNumber indattnum = 0;
9172 HeapTuple tuple;
9173
9174 indrel = index_open(indexoid, lockmode);
9175
9176 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9177 {
9178 if (indrel->rd_index->indkey.values[i] == attnum)
9179 {
9180 indattnum = i + 1;
9181 break;
9182 }
9183 }
9184
9185 if (indattnum == 0)
9186 {
9187 index_close(indrel, lockmode);
9188 continue;
9189 }
9190
9191 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9192
9193 if (HeapTupleIsValid(tuple))
9194 {
9195 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9196
9197 if (setstorage)
9198 attrtuple->attstorage = newstorage;
9199
9200 if (setcompression)
9201 attrtuple->attcompression = newcompression;
9202
9203 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9204
9205 InvokeObjectPostAlterHook(RelationRelationId,
9206 RelationGetRelid(rel),
9207 attrtuple->attnum);
9208
9209 heap_freetuple(tuple);
9210 }
9211
9212 index_close(indrel, lockmode);
9213 }
9214}
9215
9216/*
9217 * ALTER TABLE ALTER COLUMN SET STORAGE
9218 *
9219 * Return value is the address of the modified column
9220 */
9221static ObjectAddress
9222ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9223{
9224 Relation attrelation;
9225 HeapTuple tuple;
9226 Form_pg_attribute attrtuple;
9228 ObjectAddress address;
9229
9230 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9231
9232 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9233
9234 if (!HeapTupleIsValid(tuple))
9235 ereport(ERROR,
9236 (errcode(ERRCODE_UNDEFINED_COLUMN),
9237 errmsg("column \"%s\" of relation \"%s\" does not exist",
9238 colName, RelationGetRelationName(rel))));
9239 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9240
9241 attnum = attrtuple->attnum;
9242 if (attnum <= 0)
9243 ereport(ERROR,
9244 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9245 errmsg("cannot alter system column \"%s\"",
9246 colName)));
9247
9248 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9249
9250 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9251
9252 InvokeObjectPostAlterHook(RelationRelationId,
9253 RelationGetRelid(rel),
9254 attrtuple->attnum);
9255
9256 /*
9257 * Apply the change to indexes as well (only for simple index columns,
9258 * matching behavior of index.c ConstructTupleDescriptor()).
9259 */
9260 SetIndexStorageProperties(rel, attrelation, attnum,
9261 true, attrtuple->attstorage,
9262 false, 0,
9263 lockmode);
9264
9265 heap_freetuple(tuple);
9266
9267 table_close(attrelation, RowExclusiveLock);
9268
9269 ObjectAddressSubSet(address, RelationRelationId,
9270 RelationGetRelid(rel), attnum);
9271 return address;
9272}
9273
9274
9275/*
9276 * ALTER TABLE DROP COLUMN
9277 *
9278 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9279 * because we have to decide at runtime whether to recurse or not depending
9280 * on whether attinhcount goes to zero or not. (We can't check this in a
9281 * static pre-pass because it won't handle multiple inheritance situations
9282 * correctly.)
9283 */
9284static void
9285ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9286 AlterTableCmd *cmd, LOCKMODE lockmode,
9287 AlterTableUtilityContext *context)
9288{
9289 if (rel->rd_rel->reloftype && !recursing)
9290 ereport(ERROR,
9291 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9292 errmsg("cannot drop column from typed table")));
9293
9294 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9295 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9296
9297 if (recurse)
9298 cmd->recurse = true;
9299}
9300
9301/*
9302 * Drops column 'colName' from relation 'rel' and returns the address of the
9303 * dropped column. The column is also dropped (or marked as no longer
9304 * inherited from relation) from the relation's inheritance children, if any.
9305 *
9306 * In the recursive invocations for inheritance child relations, instead of
9307 * dropping the column directly (if to be dropped at all), its object address
9308 * is added to 'addrs', which must be non-NULL in such invocations. All
9309 * columns are dropped at the same time after all the children have been
9310 * checked recursively.
9311 */
9312static ObjectAddress
9313ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9314 DropBehavior behavior,
9315 bool recurse, bool recursing,
9316 bool missing_ok, LOCKMODE lockmode,
9317 ObjectAddresses *addrs)
9318{
9319 HeapTuple tuple;
9320 Form_pg_attribute targetatt;
9322 List *children;
9323 ObjectAddress object;
9324 bool is_expr;
9325
9326 /* At top level, permission check was done in ATPrepCmd, else do it */
9327 if (recursing)
9330
9331 /* Initialize addrs on the first invocation */
9332 Assert(!recursing || addrs != NULL);
9333
9334 /* since this function recurses, it could be driven to stack overflow */
9336
9337 if (!recursing)
9338 addrs = new_object_addresses();
9339
9340 /*
9341 * get the number of the attribute
9342 */
9343 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9344 if (!HeapTupleIsValid(tuple))
9345 {
9346 if (!missing_ok)
9347 {
9348 ereport(ERROR,
9349 (errcode(ERRCODE_UNDEFINED_COLUMN),
9350 errmsg("column \"%s\" of relation \"%s\" does not exist",
9351 colName, RelationGetRelationName(rel))));
9352 }
9353 else
9354 {
9356 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9357 colName, RelationGetRelationName(rel))));
9358 return InvalidObjectAddress;
9359 }
9360 }
9361 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9362
9363 attnum = targetatt->attnum;
9364
9365 /* Can't drop a system attribute */
9366 if (attnum <= 0)
9367 ereport(ERROR,
9368 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9369 errmsg("cannot drop system column \"%s\"",
9370 colName)));
9371
9372 /*
9373 * Don't drop inherited columns, unless recursing (presumably from a drop
9374 * of the parent column)
9375 */
9376 if (targetatt->attinhcount > 0 && !recursing)
9377 ereport(ERROR,
9378 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9379 errmsg("cannot drop inherited column \"%s\"",
9380 colName)));
9381
9382 /*
9383 * Don't drop columns used in the partition key, either. (If we let this
9384 * go through, the key column's dependencies would cause a cascaded drop
9385 * of the whole table, which is surely not what the user expected.)
9386 */
9387 if (has_partition_attrs(rel,
9389 &is_expr))
9390 ereport(ERROR,
9391 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9392 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9393 colName, RelationGetRelationName(rel))));
9394
9395 ReleaseSysCache(tuple);
9396
9397 /*
9398 * Propagate to children as appropriate. Unlike most other ALTER
9399 * routines, we have to do this one level of recursion at a time; we can't
9400 * use find_all_inheritors to do it in one pass.
9401 */
9402 children =
9404
9405 if (children)
9406 {
9407 Relation attr_rel;
9408 ListCell *child;
9409
9410 /*
9411 * In case of a partitioned table, the column must be dropped from the
9412 * partitions as well.
9413 */
9414 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9415 ereport(ERROR,
9416 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9417 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9418 errhint("Do not specify the ONLY keyword.")));
9419
9420 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9421 foreach(child, children)
9422 {
9423 Oid childrelid = lfirst_oid(child);
9424 Relation childrel;
9425 Form_pg_attribute childatt;
9426
9427 /* find_inheritance_children already got lock */
9428 childrel = table_open(childrelid, NoLock);
9429 CheckAlterTableIsSafe(childrel);
9430
9431 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9432 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9433 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9434 colName, childrelid);
9435 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9436
9437 if (childatt->attinhcount <= 0) /* shouldn't happen */
9438 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9439 childrelid, colName);
9440
9441 if (recurse)
9442 {
9443 /*
9444 * If the child column has other definition sources, just
9445 * decrement its inheritance count; if not, recurse to delete
9446 * it.
9447 */
9448 if (childatt->attinhcount == 1 && !childatt->attislocal)
9449 {
9450 /* Time to delete this child column, too */
9451 ATExecDropColumn(wqueue, childrel, colName,
9452 behavior, true, true,
9453 false, lockmode, addrs);
9454 }
9455 else
9456 {
9457 /* Child column must survive my deletion */
9458 childatt->attinhcount--;
9459
9460 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9461
9462 /* Make update visible */
9464 }
9465 }
9466 else
9467 {
9468 /*
9469 * If we were told to drop ONLY in this table (no recursion),
9470 * we need to mark the inheritors' attributes as locally
9471 * defined rather than inherited.
9472 */
9473 childatt->attinhcount--;
9474 childatt->attislocal = true;
9475
9476 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9477
9478 /* Make update visible */
9480 }
9481
9482 heap_freetuple(tuple);
9483
9484 table_close(childrel, NoLock);
9485 }
9486 table_close(attr_rel, RowExclusiveLock);
9487 }
9488
9489 /* Add object to delete */
9490 object.classId = RelationRelationId;
9491 object.objectId = RelationGetRelid(rel);
9492 object.objectSubId = attnum;
9493 add_exact_object_address(&object, addrs);
9494
9495 if (!recursing)
9496 {
9497 /* Recursion has ended, drop everything that was collected */
9498 performMultipleDeletions(addrs, behavior, 0);
9499 free_object_addresses(addrs);
9500 }
9501
9502 return object;
9503}
9504
9505/*
9506 * Prepare to add a primary key on a table, by adding not-null constraints
9507 * on all columns.
9508 *
9509 * The not-null constraints for a primary key must cover the whole inheritance
9510 * hierarchy (failing to ensure that leads to funny corner cases). For the
9511 * normal case where we're asked to recurse, this routine checks if the
9512 * not-null constraints exist already, and if not queues a requirement for
9513 * them to be created by phase 2.
9514 *
9515 * For the case where we're asked not to recurse, we verify that a not-null
9516 * constraint exists on each column of each (direct) child table, throwing an
9517 * error if not. Not throwing an error would also work, because a not-null
9518 * constraint would be created anyway, but it'd cause a silent scan of the
9519 * child table to verify absence of nulls. We prefer to let the user know so
9520 * that they can add the constraint manually without having to hold
9521 * AccessExclusiveLock while at it.
9522 *
9523 * However, it's also important that we do not acquire locks on children if
9524 * the not-null constraints already exist on the parent, to avoid risking
9525 * deadlocks during parallel pg_restore of PKs on partitioned tables.
9526 */
9527static void
9529 bool recurse, LOCKMODE lockmode,
9530 AlterTableUtilityContext *context)
9531{
9532 Constraint *pkconstr;
9533 List *children = NIL;
9534 bool got_children = false;
9535
9536 pkconstr = castNode(Constraint, cmd->def);
9537 if (pkconstr->contype != CONSTR_PRIMARY)
9538 return;
9539
9540 /* Verify that columns are not-null, or request that they be made so */
9541 foreach_node(String, column, pkconstr->keys)
9542 {
9543 AlterTableCmd *newcmd;
9544 Constraint *nnconstr;
9545 HeapTuple tuple;
9546
9547 /*
9548 * First check if a suitable constraint exists. If it does, we don't
9549 * need to request another one. We do need to bail out if it's not
9550 * valid, though.
9551 */
9552 tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9553 if (tuple != NULL)
9554 {
9555 verifyNotNullPKCompatible(tuple, strVal(column));
9556
9557 /* All good with this one; don't request another */
9558 heap_freetuple(tuple);
9559 continue;
9560 }
9561 else if (!recurse)
9562 {
9563 /*
9564 * No constraint on this column. Asked not to recurse, we won't
9565 * create one here, but verify that all children have one.
9566 */
9567 if (!got_children)
9568 {
9570 lockmode);
9571 /* only search for children on the first time through */
9572 got_children = true;
9573 }
9574
9575 foreach_oid(childrelid, children)
9576 {
9577 HeapTuple tup;
9578
9579 tup = findNotNullConstraint(childrelid, strVal(column));
9580 if (!tup)
9581 ereport(ERROR,
9582 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9583 strVal(column), get_rel_name(childrelid)));
9584 /* verify it's good enough */
9585 verifyNotNullPKCompatible(tup, strVal(column));
9586 }
9587 }
9588
9589 /* This column is not already not-null, so add it to the queue */
9590 nnconstr = makeNotNullConstraint(column);
9591
9592 newcmd = makeNode(AlterTableCmd);
9593 newcmd->subtype = AT_AddConstraint;
9594 /* note we force recurse=true here; see above */
9595 newcmd->recurse = true;
9596 newcmd->def = (Node *) nnconstr;
9597
9598 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9599 }
9600}
9601
9602/*
9603 * Verify whether the given not-null constraint is compatible with a
9604 * primary key. If not, an error is thrown.
9605 */
9606static void
9607verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9608{
9610
9611 if (conForm->contype != CONSTRAINT_NOTNULL)
9612 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9613
9614 /* a NO INHERIT constraint is no good */
9615 if (conForm->connoinherit)
9616 ereport(ERROR,
9617 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9618 errmsg("cannot create primary key on column \"%s\"", colname),
9619 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9620 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9621 NameStr(conForm->conname), colname,
9622 get_rel_name(conForm->conrelid), "NO INHERIT"),
9623 errhint("You might need to make the existing constraint inheritable using %s.",
9624 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9625
9626 /* an unvalidated constraint is no good */
9627 if (!conForm->convalidated)
9628 ereport(ERROR,
9629 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9630 errmsg("cannot create primary key on column \"%s\"", colname),
9631 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9632 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9633 NameStr(conForm->conname), colname,
9634 get_rel_name(conForm->conrelid), "NOT VALID"),
9635 errhint("You might need to validate it using %s.",
9636 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9637}
9638
9639/*
9640 * ALTER TABLE ADD INDEX
9641 *
9642 * There is no such command in the grammar, but parse_utilcmd.c converts
9643 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9644 * us schedule creation of the index at the appropriate time during ALTER.
9645 *
9646 * Return value is the address of the new index.
9647 */
9648static ObjectAddress
9650 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9651{
9652 bool check_rights;
9653 bool skip_build;
9654 bool quiet;
9655 ObjectAddress address;
9656
9658 Assert(!stmt->concurrent);
9659
9660 /* The IndexStmt has already been through transformIndexStmt */
9661 Assert(stmt->transformed);
9662
9663 /* suppress schema rights check when rebuilding existing index */
9664 check_rights = !is_rebuild;
9665 /* skip index build if phase 3 will do it or we're reusing an old one */
9666 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9667 /* suppress notices when rebuilding existing index */
9668 quiet = is_rebuild;
9669
9670 address = DefineIndex(NULL,
9671 RelationGetRelid(rel),
9672 stmt,
9673 InvalidOid, /* no predefined OID */
9674 InvalidOid, /* no parent index */
9675 InvalidOid, /* no parent constraint */
9676 -1, /* total_parts unknown */
9677 true, /* is_alter_table */
9678 check_rights,
9679 false, /* check_not_in_use - we did it already */
9680 skip_build,
9681 quiet);
9682
9683 /*
9684 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9685 * new index instead of building from scratch. Restore associated fields.
9686 * This may store InvalidSubTransactionId in both fields, in which case
9687 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9688 * this after the CCI that made catalog rows visible to any rebuild. The
9689 * DROP of the old edition of this index will have scheduled the storage
9690 * for deletion at commit, so cancel that pending deletion.
9691 */
9692 if (RelFileNumberIsValid(stmt->oldNumber))
9693 {
9694 Relation irel = index_open(address.objectId, NoLock);
9695
9696 irel->rd_createSubid = stmt->oldCreateSubid;
9697 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9699 index_close(irel, NoLock);
9700 }
9701
9702 return address;
9703}
9704
9705/*
9706 * ALTER TABLE ADD STATISTICS
9707 *
9708 * This is no such command in the grammar, but we use this internally to add
9709 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9710 * column type change.
9711 */
9712static ObjectAddress
9714 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9715{
9716 ObjectAddress address;
9717
9719
9720 /* The CreateStatsStmt has already been through transformStatsStmt */
9721 Assert(stmt->transformed);
9722
9723 address = CreateStatistics(stmt, !is_rebuild);
9724
9725 return address;
9726}
9727
9728/*
9729 * ALTER TABLE ADD CONSTRAINT USING INDEX
9730 *
9731 * Returns the address of the new constraint.
9732 */
9733static ObjectAddress
9735 IndexStmt *stmt, LOCKMODE lockmode)
9736{
9737 Oid index_oid = stmt->indexOid;
9738 Relation indexRel;
9739 char *indexName;
9740 IndexInfo *indexInfo;
9741 char *constraintName;
9742 char constraintType;
9743 ObjectAddress address;
9744 bits16 flags;
9745
9747 Assert(OidIsValid(index_oid));
9748 Assert(stmt->isconstraint);
9749
9750 /*
9751 * Doing this on partitioned tables is not a simple feature to implement,
9752 * so let's punt for now.
9753 */
9754 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9755 ereport(ERROR,
9756 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9757 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9758
9759 indexRel = index_open(index_oid, AccessShareLock);
9760
9761 indexName = pstrdup(RelationGetRelationName(indexRel));
9762
9763 indexInfo = BuildIndexInfo(indexRel);
9764
9765 /* this should have been checked at parse time */
9766 if (!indexInfo->ii_Unique)
9767 elog(ERROR, "index \"%s\" is not unique", indexName);
9768
9769 /*
9770 * Determine name to assign to constraint. We require a constraint to
9771 * have the same name as the underlying index; therefore, use the index's
9772 * existing name as the default constraint name, and if the user
9773 * explicitly gives some other name for the constraint, rename the index
9774 * to match.
9775 */
9776 constraintName = stmt->idxname;
9777 if (constraintName == NULL)
9778 constraintName = indexName;
9779 else if (strcmp(constraintName, indexName) != 0)
9780 {
9782 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9783 indexName, constraintName)));
9784 RenameRelationInternal(index_oid, constraintName, false, true);
9785 }
9786
9787 /* Extra checks needed if making primary key */
9788 if (stmt->primary)
9789 index_check_primary_key(rel, indexInfo, true, stmt);
9790
9791 /* Note we currently don't support EXCLUSION constraints here */
9792 if (stmt->primary)
9793 constraintType = CONSTRAINT_PRIMARY;
9794 else
9795 constraintType = CONSTRAINT_UNIQUE;
9796
9797 /* Create the catalog entries for the constraint */
9800 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9801 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9803
9804 address = index_constraint_create(rel,
9805 index_oid,
9806 InvalidOid,
9807 indexInfo,
9808 constraintName,
9809 constraintType,
9810 flags,
9812 false); /* is_internal */
9813
9814 index_close(indexRel, NoLock);
9815
9816 return address;
9817}
9818
9819/*
9820 * ALTER TABLE ADD CONSTRAINT
9821 *
9822 * Return value is the address of the new constraint; if no constraint was
9823 * added, InvalidObjectAddress is returned.
9824 */
9825static ObjectAddress
9827 Constraint *newConstraint, bool recurse, bool is_readd,
9828 LOCKMODE lockmode)
9829{
9831
9832 Assert(IsA(newConstraint, Constraint));
9833
9834 /*
9835 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9836 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9837 * parse_utilcmd.c).
9838 */
9839 switch (newConstraint->contype)
9840 {
9841 case CONSTR_CHECK:
9842 case CONSTR_NOTNULL:
9843 address =
9844 ATAddCheckNNConstraint(wqueue, tab, rel,
9845 newConstraint, recurse, false, is_readd,
9846 lockmode);
9847 break;
9848
9849 case CONSTR_FOREIGN:
9850
9851 /*
9852 * Assign or validate constraint name
9853 */
9854 if (newConstraint->conname)
9855 {
9857 RelationGetRelid(rel),
9858 newConstraint->conname))
9859 ereport(ERROR,
9861 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9862 newConstraint->conname,
9864 }
9865 else
9866 newConstraint->conname =
9869 "fkey",
9871 NIL);
9872
9873 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9874 newConstraint,
9875 recurse, false,
9876 lockmode);
9877 break;
9878
9879 default:
9880 elog(ERROR, "unrecognized constraint type: %d",
9881 (int) newConstraint->contype);
9882 }
9883
9884 return address;
9885}
9886
9887/*
9888 * Generate the column-name portion of the constraint name for a new foreign
9889 * key given the list of column names that reference the referenced
9890 * table. This will be passed to ChooseConstraintName along with the parent
9891 * table name and the "fkey" suffix.
9892 *
9893 * We know that less than NAMEDATALEN characters will actually be used, so we
9894 * can truncate the result once we've generated that many.
9895 *
9896 * XXX see also ChooseExtendedStatisticNameAddition and
9897 * ChooseIndexNameAddition.
9898 */
9899static char *
9901{
9902 char buf[NAMEDATALEN * 2];
9903 int buflen = 0;
9904 ListCell *lc;
9905
9906 buf[0] = '\0';
9907 foreach(lc, colnames)
9908 {
9909 const char *name = strVal(lfirst(lc));
9910
9911 if (buflen > 0)
9912 buf[buflen++] = '_'; /* insert _ between names */
9913
9914 /*
9915 * At this point we have buflen <= NAMEDATALEN. name should be less
9916 * than NAMEDATALEN already, but use strlcpy for paranoia.
9917 */
9918 strlcpy(buf + buflen, name, NAMEDATALEN);
9919 buflen += strlen(buf + buflen);
9920 if (buflen >= NAMEDATALEN)
9921 break;
9922 }
9923 return pstrdup(buf);
9924}
9925
9926/*
9927 * Add a check or not-null constraint to a single table and its children.
9928 * Returns the address of the constraint added to the parent relation,
9929 * if one gets added, or InvalidObjectAddress otherwise.
9930 *
9931 * Subroutine for ATExecAddConstraint.
9932 *
9933 * We must recurse to child tables during execution, rather than using
9934 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9935 * constraints *must* be given the same name, else they won't be seen as
9936 * related later. If the user didn't explicitly specify a name, then
9937 * AddRelationNewConstraints would normally assign different names to the
9938 * child constraints. To fix that, we must capture the name assigned at
9939 * the parent table and pass that down.
9940 */
9941static ObjectAddress
9943 Constraint *constr, bool recurse, bool recursing,
9944 bool is_readd, LOCKMODE lockmode)
9945{
9946 List *newcons;
9947 ListCell *lcon;
9948 List *children;
9949 ListCell *child;
9951
9952 /* Guard against stack overflow due to overly deep inheritance tree. */
9954
9955 /* At top level, permission check was done in ATPrepCmd, else do it */
9956 if (recursing)
9959
9960 /*
9961 * Call AddRelationNewConstraints to do the work, making sure it works on
9962 * a copy of the Constraint so transformExpr can't modify the original. It
9963 * returns a list of cooked constraints.
9964 *
9965 * If the constraint ends up getting merged with a pre-existing one, it's
9966 * omitted from the returned list, which is what we want: we do not need
9967 * to do any validation work. That can only happen at child tables,
9968 * though, since we disallow merging at the top level.
9969 */
9970 newcons = AddRelationNewConstraints(rel, NIL,
9971 list_make1(copyObject(constr)),
9972 recursing || is_readd, /* allow_merge */
9973 !recursing, /* is_local */
9974 is_readd, /* is_internal */
9975 NULL); /* queryString not available
9976 * here */
9977
9978 /* we don't expect more than one constraint here */
9979 Assert(list_length(newcons) <= 1);
9980
9981 /* Add each to-be-validated constraint to Phase 3's queue */
9982 foreach(lcon, newcons)
9983 {
9984 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9985
9986 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9987 {
9988 NewConstraint *newcon;
9989
9990 newcon = palloc0_object(NewConstraint);
9991 newcon->name = ccon->name;
9992 newcon->contype = ccon->contype;
9993 newcon->qual = ccon->expr;
9994
9995 tab->constraints = lappend(tab->constraints, newcon);
9996 }
9997
9998 /* Save the actually assigned name if it was defaulted */
9999 if (constr->conname == NULL)
10000 constr->conname = ccon->name;
10001
10002 /*
10003 * If adding a valid not-null constraint, set the pg_attribute flag
10004 * and tell phase 3 to verify existing rows, if needed. For an
10005 * invalid constraint, just set attnotnull, without queueing
10006 * verification.
10007 */
10008 if (constr->contype == CONSTR_NOTNULL)
10009 set_attnotnull(wqueue, rel, ccon->attnum,
10010 !constr->skip_validation,
10011 !constr->skip_validation);
10012
10013 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
10014 }
10015
10016 /* At this point we must have a locked-down name to use */
10017 Assert(newcons == NIL || constr->conname != NULL);
10018
10019 /* Advance command counter in case same table is visited multiple times */
10021
10022 /*
10023 * If the constraint got merged with an existing constraint, we're done.
10024 * We mustn't recurse to child tables in this case, because they've
10025 * already got the constraint, and visiting them again would lead to an
10026 * incorrect value for coninhcount.
10027 */
10028 if (newcons == NIL)
10029 return address;
10030
10031 /*
10032 * If adding a NO INHERIT constraint, no need to find our children.
10033 */
10034 if (constr->is_no_inherit)
10035 return address;
10036
10037 /*
10038 * Propagate to children as appropriate. Unlike most other ALTER
10039 * routines, we have to do this one level of recursion at a time; we can't
10040 * use find_all_inheritors to do it in one pass.
10041 */
10042 children =
10044
10045 /*
10046 * Check if ONLY was specified with ALTER TABLE. If so, allow the
10047 * constraint creation only if there are no children currently. Error out
10048 * otherwise.
10049 */
10050 if (!recurse && children != NIL)
10051 ereport(ERROR,
10052 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10053 errmsg("constraint must be added to child tables too")));
10054
10055 /*
10056 * Recurse to create the constraint on each child.
10057 */
10058 foreach(child, children)
10059 {
10060 Oid childrelid = lfirst_oid(child);
10061 Relation childrel;
10062 AlteredTableInfo *childtab;
10063
10064 /* find_inheritance_children already got lock */
10065 childrel = table_open(childrelid, NoLock);
10066 CheckAlterTableIsSafe(childrel);
10067
10068 /* Find or create work queue entry for this table */
10069 childtab = ATGetQueueEntry(wqueue, childrel);
10070
10071 /* Recurse to this child */
10072 ATAddCheckNNConstraint(wqueue, childtab, childrel,
10073 constr, recurse, true, is_readd, lockmode);
10074
10075 table_close(childrel, NoLock);
10076 }
10077
10078 return address;
10079}
10080
10081/*
10082 * Add a foreign-key constraint to a single table; return the new constraint's
10083 * address.
10084 *
10085 * Subroutine for ATExecAddConstraint. Must already hold exclusive
10086 * lock on the rel, and have done appropriate validity checks for it.
10087 * We do permissions checks here, however.
10088 *
10089 * When the referenced or referencing tables (or both) are partitioned,
10090 * multiple pg_constraint rows are required -- one for each partitioned table
10091 * and each partition on each side (fortunately, not one for every combination
10092 * thereof). We also need action triggers on each leaf partition on the
10093 * referenced side, and check triggers on each leaf partition on the
10094 * referencing side.
10095 */
10096static ObjectAddress
10098 Constraint *fkconstraint,
10099 bool recurse, bool recursing, LOCKMODE lockmode)
10100{
10101 Relation pkrel;
10102 int16 pkattnum[INDEX_MAX_KEYS] = {0};
10103 int16 fkattnum[INDEX_MAX_KEYS] = {0};
10104 Oid pktypoid[INDEX_MAX_KEYS] = {0};
10105 Oid fktypoid[INDEX_MAX_KEYS] = {0};
10106 Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10107 Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10108 Oid opclasses[INDEX_MAX_KEYS] = {0};
10109 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10110 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10111 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10112 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10113 bool with_period;
10114 bool pk_has_without_overlaps;
10115 int i;
10116 int numfks,
10117 numpks,
10118 numfkdelsetcols;
10119 Oid indexOid;
10120 bool old_check_ok;
10121 ObjectAddress address;
10122 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10123
10124 /*
10125 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10126 * delete rows out from under us.
10127 */
10128 if (OidIsValid(fkconstraint->old_pktable_oid))
10129 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10130 else
10131 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10132
10133 /*
10134 * Validity checks (permission checks wait till we have the column
10135 * numbers)
10136 */
10137 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10138 ereport(ERROR,
10139 errcode(ERRCODE_WRONG_OBJECT_TYPE),
10140 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10142 RelationGetRelationName(pkrel)));
10143
10144 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10145 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10146 ereport(ERROR,
10147 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10148 errmsg("referenced relation \"%s\" is not a table",
10149 RelationGetRelationName(pkrel))));
10150
10152 ereport(ERROR,
10153 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10154 errmsg("permission denied: \"%s\" is a system catalog",
10155 RelationGetRelationName(pkrel))));
10156
10157 /*
10158 * References from permanent or unlogged tables to temp tables, and from
10159 * permanent tables to unlogged tables, are disallowed because the
10160 * referenced data can vanish out from under us. References from temp
10161 * tables to any other table type are also disallowed, because other
10162 * backends might need to run the RI triggers on the perm table, but they
10163 * can't reliably see tuples in the local buffers of other backends.
10164 */
10165 switch (rel->rd_rel->relpersistence)
10166 {
10167 case RELPERSISTENCE_PERMANENT:
10168 if (!RelationIsPermanent(pkrel))
10169 ereport(ERROR,
10170 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10171 errmsg("constraints on permanent tables may reference only permanent tables")));
10172 break;
10173 case RELPERSISTENCE_UNLOGGED:
10174 if (!RelationIsPermanent(pkrel)
10175 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10176 ereport(ERROR,
10177 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10178 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10179 break;
10180 case RELPERSISTENCE_TEMP:
10181 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10182 ereport(ERROR,
10183 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10184 errmsg("constraints on temporary tables may reference only temporary tables")));
10185 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10186 ereport(ERROR,
10187 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10188 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10189 break;
10190 }
10191
10192 /*
10193 * Look up the referencing attributes to make sure they exist, and record
10194 * their attnums and type and collation OIDs.
10195 */
10197 fkconstraint->fk_attrs,
10198 fkattnum, fktypoid, fkcolloid);
10199 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10200 if (with_period && !fkconstraint->fk_with_period)
10201 ereport(ERROR,
10202 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10203 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10204
10205 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10206 fkconstraint->fk_del_set_cols,
10207 fkdelsetcols, NULL, NULL);
10208 numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10209 numfkdelsetcols,
10210 fkdelsetcols,
10211 fkconstraint->fk_del_set_cols);
10212
10213 /*
10214 * If the attribute list for the referenced table was omitted, lookup the
10215 * definition of the primary key and use it. Otherwise, validate the
10216 * supplied attribute list. In either case, discover the index OID and
10217 * index opclasses, and the attnums and type and collation OIDs of the
10218 * attributes.
10219 */
10220 if (fkconstraint->pk_attrs == NIL)
10221 {
10222 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10223 &fkconstraint->pk_attrs,
10224 pkattnum, pktypoid, pkcolloid,
10225 opclasses, &pk_has_without_overlaps);
10226
10227 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10228 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10229 ereport(ERROR,
10230 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10231 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10232 }
10233 else
10234 {
10236 fkconstraint->pk_attrs,
10237 pkattnum, pktypoid, pkcolloid);
10238
10239 /* Since we got pk_attrs, one should be a period. */
10240 if (with_period && !fkconstraint->pk_with_period)
10241 ereport(ERROR,
10242 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10243 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10244
10245 /* Look for an index matching the column list */
10246 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10247 with_period, opclasses, &pk_has_without_overlaps);
10248 }
10249
10250 /*
10251 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10252 * must use PERIOD.
10253 */
10254 if (pk_has_without_overlaps && !with_period)
10255 ereport(ERROR,
10256 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10257 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10258
10259 /*
10260 * Now we can check permissions.
10261 */
10262 checkFkeyPermissions(pkrel, pkattnum, numpks);
10263
10264 /*
10265 * Check some things for generated columns.
10266 */
10267 for (i = 0; i < numfks; i++)
10268 {
10269 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10270
10271 if (attgenerated)
10272 {
10273 /*
10274 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10275 */
10276 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10277 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10278 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10279 ereport(ERROR,
10280 (errcode(ERRCODE_SYNTAX_ERROR),
10281 errmsg("invalid %s action for foreign key constraint containing generated column",
10282 "ON UPDATE")));
10283 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10285 ereport(ERROR,
10286 (errcode(ERRCODE_SYNTAX_ERROR),
10287 errmsg("invalid %s action for foreign key constraint containing generated column",
10288 "ON DELETE")));
10289 }
10290
10291 /*
10292 * FKs on virtual columns are not supported. This would require
10293 * various additional support in ri_triggers.c, including special
10294 * handling in ri_NullCheck(), ri_KeysEqual(),
10295 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10296 * as NULL there). Also not really practical as long as you can't
10297 * index virtual columns.
10298 */
10299 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10300 ereport(ERROR,
10301 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10302 errmsg("foreign key constraints on virtual generated columns are not supported")));
10303 }
10304
10305 /*
10306 * Some actions are currently unsupported for foreign keys using PERIOD.
10307 */
10308 if (fkconstraint->fk_with_period)
10309 {
10310 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10311 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10312 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10314 ereport(ERROR,
10315 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10316 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10317 "ON UPDATE"));
10318
10319 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10320 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10321 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10323 ereport(ERROR,
10324 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10325 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10326 "ON DELETE"));
10327 }
10328
10329 /*
10330 * Look up the equality operators to use in the constraint.
10331 *
10332 * Note that we have to be careful about the difference between the actual
10333 * PK column type and the opclass' declared input type, which might be
10334 * only binary-compatible with it. The declared opcintype is the right
10335 * thing to probe pg_amop with.
10336 */
10337 if (numfks != numpks)
10338 ereport(ERROR,
10339 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10340 errmsg("number of referencing and referenced columns for foreign key disagree")));
10341
10342 /*
10343 * On the strength of a previous constraint, we might avoid scanning
10344 * tables to validate this one. See below.
10345 */
10346 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10347 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10348
10349 for (i = 0; i < numpks; i++)
10350 {
10351 Oid pktype = pktypoid[i];
10352 Oid fktype = fktypoid[i];
10353 Oid fktyped;
10354 Oid pkcoll = pkcolloid[i];
10355 Oid fkcoll = fkcolloid[i];
10356 HeapTuple cla_ht;
10357 Form_pg_opclass cla_tup;
10358 Oid amid;
10359 Oid opfamily;
10360 Oid opcintype;
10361 bool for_overlaps;
10362 CompareType cmptype;
10363 Oid pfeqop;
10364 Oid ppeqop;
10365 Oid ffeqop;
10366 int16 eqstrategy;
10367 Oid pfeqop_right;
10368
10369 /* We need several fields out of the pg_opclass entry */
10370 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10371 if (!HeapTupleIsValid(cla_ht))
10372 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10373 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10374 amid = cla_tup->opcmethod;
10375 opfamily = cla_tup->opcfamily;
10376 opcintype = cla_tup->opcintype;
10377 ReleaseSysCache(cla_ht);
10378
10379 /*
10380 * Get strategy number from index AM.
10381 *
10382 * For a normal foreign-key constraint, this should not fail, since we
10383 * already checked that the index is unique and should therefore have
10384 * appropriate equal operators. For a period foreign key, this could
10385 * fail if we selected a non-matching exclusion constraint earlier.
10386 * (XXX Maybe we should do these lookups earlier so we don't end up
10387 * doing that.)
10388 */
10389 for_overlaps = with_period && i == numpks - 1;
10390 cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10391 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10392 if (eqstrategy == InvalidStrategy)
10393 ereport(ERROR,
10394 errcode(ERRCODE_UNDEFINED_OBJECT),
10395 for_overlaps
10396 ? errmsg("could not identify an overlaps operator for foreign key")
10397 : errmsg("could not identify an equality operator for foreign key"),
10398 errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10399 cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10400
10401 /*
10402 * There had better be a primary equality operator for the index.
10403 * We'll use it for PK = PK comparisons.
10404 */
10405 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10406 eqstrategy);
10407
10408 if (!OidIsValid(ppeqop))
10409 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10410 eqstrategy, opcintype, opcintype, opfamily);
10411
10412 /*
10413 * Are there equality operators that take exactly the FK type? Assume
10414 * we should look through any domain here.
10415 */
10416 fktyped = getBaseType(fktype);
10417
10418 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10419 eqstrategy);
10420 if (OidIsValid(pfeqop))
10421 {
10422 pfeqop_right = fktyped;
10423 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10424 eqstrategy);
10425 }
10426 else
10427 {
10428 /* keep compiler quiet */
10429 pfeqop_right = InvalidOid;
10430 ffeqop = InvalidOid;
10431 }
10432
10433 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10434 {
10435 /*
10436 * Otherwise, look for an implicit cast from the FK type to the
10437 * opcintype, and if found, use the primary equality operator.
10438 * This is a bit tricky because opcintype might be a polymorphic
10439 * type such as ANYARRAY or ANYENUM; so what we have to test is
10440 * whether the two actual column types can be concurrently cast to
10441 * that type. (Otherwise, we'd fail to reject combinations such
10442 * as int[] and point[].)
10443 */
10444 Oid input_typeids[2];
10445 Oid target_typeids[2];
10446
10447 input_typeids[0] = pktype;
10448 input_typeids[1] = fktype;
10449 target_typeids[0] = opcintype;
10450 target_typeids[1] = opcintype;
10451 if (can_coerce_type(2, input_typeids, target_typeids,
10453 {
10454 pfeqop = ffeqop = ppeqop;
10455 pfeqop_right = opcintype;
10456 }
10457 }
10458
10459 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10460 ereport(ERROR,
10461 (errcode(ERRCODE_DATATYPE_MISMATCH),
10462 errmsg("foreign key constraint \"%s\" cannot be implemented",
10463 fkconstraint->conname),
10464 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10465 "are of incompatible types: %s and %s.",
10466 strVal(list_nth(fkconstraint->fk_attrs, i)),
10467 strVal(list_nth(fkconstraint->pk_attrs, i)),
10468 format_type_be(fktype),
10469 format_type_be(pktype))));
10470
10471 /*
10472 * This shouldn't be possible, but better check to make sure we have a
10473 * consistent state for the check below.
10474 */
10475 if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10476 elog(ERROR, "key columns are not both collatable");
10477
10478 if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10479 {
10480 bool pkcolldet;
10481 bool fkcolldet;
10482
10483 pkcolldet = get_collation_isdeterministic(pkcoll);
10484 fkcolldet = get_collation_isdeterministic(fkcoll);
10485
10486 /*
10487 * SQL requires that both collations are the same. This is
10488 * because we need a consistent notion of equality on both
10489 * columns. We relax this by allowing different collations if
10490 * they are both deterministic. (This is also for backward
10491 * compatibility, because PostgreSQL has always allowed this.)
10492 */
10493 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10494 ereport(ERROR,
10495 (errcode(ERRCODE_COLLATION_MISMATCH),
10496 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10497 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10498 "have incompatible collations: \"%s\" and \"%s\". "
10499 "If either collation is nondeterministic, then both collations have to be the same.",
10500 strVal(list_nth(fkconstraint->fk_attrs, i)),
10501 strVal(list_nth(fkconstraint->pk_attrs, i)),
10502 get_collation_name(fkcoll),
10503 get_collation_name(pkcoll))));
10504 }
10505
10506 if (old_check_ok)
10507 {
10508 /*
10509 * When a pfeqop changes, revalidate the constraint. We could
10510 * permit intra-opfamily changes, but that adds subtle complexity
10511 * without any concrete benefit for core types. We need not
10512 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10513 */
10514 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10515 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10516 old_pfeqop_item);
10517 }
10518 if (old_check_ok)
10519 {
10520 Oid old_fktype;
10521 Oid new_fktype;
10522 CoercionPathType old_pathtype;
10523 CoercionPathType new_pathtype;
10524 Oid old_castfunc;
10525 Oid new_castfunc;
10526 Oid old_fkcoll;
10527 Oid new_fkcoll;
10529 fkattnum[i] - 1);
10530
10531 /*
10532 * Identify coercion pathways from each of the old and new FK-side
10533 * column types to the right (foreign) operand type of the pfeqop.
10534 * We may assume that pg_constraint.conkey is not changing.
10535 */
10536 old_fktype = attr->atttypid;
10537 new_fktype = fktype;
10538 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10539 &old_castfunc);
10540 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10541 &new_castfunc);
10542
10543 old_fkcoll = attr->attcollation;
10544 new_fkcoll = fkcoll;
10545
10546 /*
10547 * Upon a change to the cast from the FK column to its pfeqop
10548 * operand, revalidate the constraint. For this evaluation, a
10549 * binary coercion cast is equivalent to no cast at all. While
10550 * type implementors should design implicit casts with an eye
10551 * toward consistency of operations like equality, we cannot
10552 * assume here that they have done so.
10553 *
10554 * A function with a polymorphic argument could change behavior
10555 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10556 * when the cast destination is polymorphic, we only avoid
10557 * revalidation if the input type has not changed at all. Given
10558 * just the core data types and operator classes, this requirement
10559 * prevents no would-be optimizations.
10560 *
10561 * If the cast converts from a base type to a domain thereon, then
10562 * that domain type must be the opcintype of the unique index.
10563 * Necessarily, the primary key column must then be of the domain
10564 * type. Since the constraint was previously valid, all values on
10565 * the foreign side necessarily exist on the primary side and in
10566 * turn conform to the domain. Consequently, we need not treat
10567 * domains specially here.
10568 *
10569 * If the collation changes, revalidation is required, unless both
10570 * collations are deterministic, because those share the same
10571 * notion of equality (because texteq reduces to bitwise
10572 * equality).
10573 *
10574 * We need not directly consider the PK type. It's necessarily
10575 * binary coercible to the opcintype of the unique index column,
10576 * and ri_triggers.c will only deal with PK datums in terms of
10577 * that opcintype. Changing the opcintype also changes pfeqop.
10578 */
10579 old_check_ok = (new_pathtype == old_pathtype &&
10580 new_castfunc == old_castfunc &&
10581 (!IsPolymorphicType(pfeqop_right) ||
10582 new_fktype == old_fktype) &&
10583 (new_fkcoll == old_fkcoll ||
10584 (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10585 }
10586
10587 pfeqoperators[i] = pfeqop;
10588 ppeqoperators[i] = ppeqop;
10589 ffeqoperators[i] = ffeqop;
10590 }
10591
10592 /*
10593 * For FKs with PERIOD we need additional operators to check whether the
10594 * referencing row's range is contained by the aggregated ranges of the
10595 * referenced row(s). For rangetypes and multirangetypes this is
10596 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10597 * support for now. FKs will look these up at "runtime", but we should
10598 * make sure the lookup works here, even if we don't use the values.
10599 */
10600 if (with_period)
10601 {
10602 Oid periodoperoid;
10603 Oid aggedperiodoperoid;
10604 Oid intersectoperoid;
10605
10606 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10607 &intersectoperoid);
10608 }
10609
10610 /* First, create the constraint catalog entry itself. */
10612 fkconstraint->conname, fkconstraint, rel, pkrel,
10613 indexOid,
10614 InvalidOid, /* no parent constraint */
10615 numfks,
10616 pkattnum,
10617 fkattnum,
10618 pfeqoperators,
10619 ppeqoperators,
10620 ffeqoperators,
10621 numfkdelsetcols,
10622 fkdelsetcols,
10623 false,
10624 with_period);
10625
10626 /* Next process the action triggers at the referenced side and recurse */
10627 addFkRecurseReferenced(fkconstraint, rel, pkrel,
10628 indexOid,
10629 address.objectId,
10630 numfks,
10631 pkattnum,
10632 fkattnum,
10633 pfeqoperators,
10634 ppeqoperators,
10635 ffeqoperators,
10636 numfkdelsetcols,
10637 fkdelsetcols,
10638 old_check_ok,
10640 with_period);
10641
10642 /* Lastly create the check triggers at the referencing side and recurse */
10643 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10644 indexOid,
10645 address.objectId,
10646 numfks,
10647 pkattnum,
10648 fkattnum,
10649 pfeqoperators,
10650 ppeqoperators,
10651 ffeqoperators,
10652 numfkdelsetcols,
10653 fkdelsetcols,
10654 old_check_ok,
10655 lockmode,
10657 with_period);
10658
10659 /*
10660 * Done. Close pk table, but keep lock until we've committed.
10661 */
10662 table_close(pkrel, NoLock);
10663
10664 return address;
10665}
10666
10667/*
10668 * validateFkOnDeleteSetColumns
10669 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10670 * column lists are valid.
10671 *
10672 * If there are duplicates in the fksetcolsattnums[] array, this silently
10673 * removes the dups. The new count of numfksetcols is returned.
10674 */
10675static int
10676validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10677 int numfksetcols, int16 *fksetcolsattnums,
10678 List *fksetcols)
10679{
10680 int numcolsout = 0;
10681
10682 for (int i = 0; i < numfksetcols; i++)
10683 {
10684 int16 setcol_attnum = fksetcolsattnums[i];
10685 bool seen = false;
10686
10687 /* Make sure it's in fkattnums[] */
10688 for (int j = 0; j < numfks; j++)
10689 {
10690 if (fkattnums[j] == setcol_attnum)
10691 {
10692 seen = true;
10693 break;
10694 }
10695 }
10696
10697 if (!seen)
10698 {
10699 char *col = strVal(list_nth(fksetcols, i));
10700
10701 ereport(ERROR,
10702 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10703 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10704 }
10705
10706 /* Now check for dups */
10707 seen = false;
10708 for (int j = 0; j < numcolsout; j++)
10709 {
10710 if (fksetcolsattnums[j] == setcol_attnum)
10711 {
10712 seen = true;
10713 break;
10714 }
10715 }
10716 if (!seen)
10717 fksetcolsattnums[numcolsout++] = setcol_attnum;
10718 }
10719 return numcolsout;
10720}
10721
10722/*
10723 * addFkConstraint
10724 * Install pg_constraint entries to implement a foreign key constraint.
10725 * Caller must separately invoke addFkRecurseReferenced and
10726 * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10727 * and (for partitioned tables) recurse to partitions.
10728 *
10729 * fkside: the side of the FK (or both) to create. Caller should
10730 * call addFkRecurseReferenced if this is addFkReferencedSide,
10731 * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10732 * addFkBothSides.
10733 * constraintname: the base name for the constraint being added,
10734 * copied to fkconstraint->conname if the latter is not set
10735 * fkconstraint: the constraint being added
10736 * rel: the root referencing relation
10737 * pkrel: the referenced relation; might be a partition, if recursing
10738 * indexOid: the OID of the index (on pkrel) implementing this constraint
10739 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10740 * top-level constraint
10741 * numfks: the number of columns in the foreign key
10742 * pkattnum: the attnum array of referenced attributes
10743 * fkattnum: the attnum array of referencing attributes
10744 * pf/pp/ffeqoperators: OID array of operators between columns
10745 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10746 * (...) clause
10747 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10748 * NULL/DEFAULT clause
10749 * with_period: true if this is a temporal FK
10750 */
10751static ObjectAddress
10753 char *constraintname, Constraint *fkconstraint,
10754 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10755 int numfks, int16 *pkattnum,
10756 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10757 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10758 bool is_internal, bool with_period)
10759{
10760 ObjectAddress address;
10761 Oid constrOid;
10762 char *conname;
10763 bool conislocal;
10764 int16 coninhcount;
10765 bool connoinherit;
10766
10767 /*
10768 * Verify relkind for each referenced partition. At the top level, this
10769 * is redundant with a previous check, but we need it when recursing.
10770 */
10771 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10772 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10773 ereport(ERROR,
10774 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10775 errmsg("referenced relation \"%s\" is not a table",
10776 RelationGetRelationName(pkrel))));
10777
10778 /*
10779 * Caller supplies us with a constraint name; however, it may be used in
10780 * this partition, so come up with a different one in that case. Unless
10781 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10782 * supplied name with an underscore and digit(s) appended.
10783 */
10785 RelationGetRelid(rel),
10786 constraintname))
10787 conname = ChooseConstraintName(constraintname,
10788 NULL,
10789 "",
10791 else
10792 conname = constraintname;
10793
10794 if (fkconstraint->conname == NULL)
10795 fkconstraint->conname = pstrdup(conname);
10796
10797 if (OidIsValid(parentConstr))
10798 {
10799 conislocal = false;
10800 coninhcount = 1;
10801 connoinherit = false;
10802 }
10803 else
10804 {
10805 conislocal = true;
10806 coninhcount = 0;
10807
10808 /*
10809 * always inherit for partitioned tables, never for legacy inheritance
10810 */
10811 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10812 }
10813
10814 /*
10815 * Record the FK constraint in pg_constraint.
10816 */
10817 constrOid = CreateConstraintEntry(conname,
10819 CONSTRAINT_FOREIGN,
10820 fkconstraint->deferrable,
10821 fkconstraint->initdeferred,
10822 fkconstraint->is_enforced,
10823 fkconstraint->initially_valid,
10824 parentConstr,
10825 RelationGetRelid(rel),
10826 fkattnum,
10827 numfks,
10828 numfks,
10829 InvalidOid, /* not a domain constraint */
10830 indexOid,
10831 RelationGetRelid(pkrel),
10832 pkattnum,
10833 pfeqoperators,
10834 ppeqoperators,
10835 ffeqoperators,
10836 numfks,
10837 fkconstraint->fk_upd_action,
10838 fkconstraint->fk_del_action,
10839 fkdelsetcols,
10840 numfkdelsetcols,
10841 fkconstraint->fk_matchtype,
10842 NULL, /* no exclusion constraint */
10843 NULL, /* no check constraint */
10844 NULL,
10845 conislocal, /* islocal */
10846 coninhcount, /* inhcount */
10847 connoinherit, /* conNoInherit */
10848 with_period, /* conPeriod */
10849 is_internal); /* is_internal */
10850
10851 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10852
10853 /*
10854 * In partitioning cases, create the dependency entries for this
10855 * constraint. (For non-partitioned cases, relevant entries were created
10856 * by CreateConstraintEntry.)
10857 *
10858 * On the referenced side, we need the constraint to have an internal
10859 * dependency on its parent constraint; this means that this constraint
10860 * cannot be dropped on its own -- only through the parent constraint. It
10861 * also means the containing partition cannot be dropped on its own, but
10862 * it can be detached, at which point this dependency is removed (after
10863 * verifying that no rows are referenced via this FK.)
10864 *
10865 * When processing the referencing side, we link the constraint via the
10866 * special partitioning dependencies: the parent constraint is the primary
10867 * dependent, and the partition on which the foreign key exists is the
10868 * secondary dependency. That way, this constraint is dropped if either
10869 * of these objects is.
10870 *
10871 * Note that this is only necessary for the subsidiary pg_constraint rows
10872 * in partitions; the topmost row doesn't need any of this.
10873 */
10874 if (OidIsValid(parentConstr))
10875 {
10876 ObjectAddress referenced;
10877
10878 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10879
10880 Assert(fkside != addFkBothSides);
10881 if (fkside == addFkReferencedSide)
10882 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10883 else
10884 {
10885 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10886 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10887 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10888 }
10889 }
10890
10891 /* make new constraint visible, in case we add more */
10893
10894 return address;
10895}
10896
10897/*
10898 * addFkRecurseReferenced
10899 * Recursive helper for the referenced side of foreign key creation,
10900 * which creates the action triggers and recurses
10901 *
10902 * If the referenced relation is a plain relation, create the necessary action
10903 * triggers that implement the constraint. If the referenced relation is a
10904 * partitioned table, then we create a pg_constraint row referencing the parent
10905 * of the referencing side for it and recurse on this routine for each
10906 * partition.
10907 *
10908 * fkconstraint: the constraint being added
10909 * rel: the root referencing relation
10910 * pkrel: the referenced relation; might be a partition, if recursing
10911 * indexOid: the OID of the index (on pkrel) implementing this constraint
10912 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10913 * top-level constraint
10914 * numfks: the number of columns in the foreign key
10915 * pkattnum: the attnum array of referenced attributes
10916 * fkattnum: the attnum array of referencing attributes
10917 * numfkdelsetcols: the number of columns in the ON DELETE SET
10918 * NULL/DEFAULT (...) clause
10919 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10920 * NULL/DEFAULT clause
10921 * pf/pp/ffeqoperators: OID array of operators between columns
10922 * old_check_ok: true if this constraint replaces an existing one that
10923 * was already validated (thus this one doesn't need validation)
10924 * parentDelTrigger and parentUpdTrigger: when recursively called on a
10925 * partition, the OIDs of the parent action triggers for DELETE and
10926 * UPDATE respectively.
10927 * with_period: true if this is a temporal FK
10928 */
10929static void
10931 Relation pkrel, Oid indexOid, Oid parentConstr,
10932 int numfks,
10933 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10934 Oid *ppeqoperators, Oid *ffeqoperators,
10935 int numfkdelsetcols, int16 *fkdelsetcols,
10936 bool old_check_ok,
10937 Oid parentDelTrigger, Oid parentUpdTrigger,
10938 bool with_period)
10939{
10940 Oid deleteTriggerOid = InvalidOid,
10941 updateTriggerOid = InvalidOid;
10942
10945
10946 /*
10947 * Create action triggers to enforce the constraint, or skip them if the
10948 * constraint is NOT ENFORCED.
10949 */
10950 if (fkconstraint->is_enforced)
10952 RelationGetRelid(pkrel),
10953 fkconstraint,
10954 parentConstr, indexOid,
10955 parentDelTrigger, parentUpdTrigger,
10956 &deleteTriggerOid, &updateTriggerOid);
10957
10958 /*
10959 * If the referenced table is partitioned, recurse on ourselves to handle
10960 * each partition. We need one pg_constraint row created for each
10961 * partition in addition to the pg_constraint row for the parent table.
10962 */
10963 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10964 {
10965 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10966
10967 for (int i = 0; i < pd->nparts; i++)
10968 {
10969 Relation partRel;
10970 AttrMap *map;
10971 AttrNumber *mapped_pkattnum;
10972 Oid partIndexId;
10973 ObjectAddress address;
10974
10975 /* XXX would it be better to acquire these locks beforehand? */
10976 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10977
10978 /*
10979 * Map the attribute numbers in the referenced side of the FK
10980 * definition to match the partition's column layout.
10981 */
10983 RelationGetDescr(pkrel),
10984 false);
10985 if (map)
10986 {
10987 mapped_pkattnum = palloc_array(AttrNumber, numfks);
10988 for (int j = 0; j < numfks; j++)
10989 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10990 }
10991 else
10992 mapped_pkattnum = pkattnum;
10993
10994 /* Determine the index to use at this level */
10995 partIndexId = index_get_partition(partRel, indexOid);
10996 if (!OidIsValid(partIndexId))
10997 elog(ERROR, "index for %u not found in partition %s",
10998 indexOid, RelationGetRelationName(partRel));
10999
11000 /* Create entry at this level ... */
11002 fkconstraint->conname, fkconstraint, rel,
11003 partRel, partIndexId, parentConstr,
11004 numfks, mapped_pkattnum,
11005 fkattnum, pfeqoperators, ppeqoperators,
11006 ffeqoperators, numfkdelsetcols,
11007 fkdelsetcols, true, with_period);
11008 /* ... and recurse to our children */
11009 addFkRecurseReferenced(fkconstraint, rel, partRel,
11010 partIndexId, address.objectId, numfks,
11011 mapped_pkattnum, fkattnum,
11012 pfeqoperators, ppeqoperators, ffeqoperators,
11013 numfkdelsetcols, fkdelsetcols,
11014 old_check_ok,
11015 deleteTriggerOid, updateTriggerOid,
11016 with_period);
11017
11018 /* Done -- clean up (but keep the lock) */
11019 table_close(partRel, NoLock);
11020 if (map)
11021 {
11022 pfree(mapped_pkattnum);
11023 free_attrmap(map);
11024 }
11025 }
11026 }
11027}
11028
11029/*
11030 * addFkRecurseReferencing
11031 * Recursive helper for the referencing side of foreign key creation,
11032 * which creates the check triggers and recurses
11033 *
11034 * If the referencing relation is a plain relation, create the necessary check
11035 * triggers that implement the constraint, and set up for Phase 3 constraint
11036 * verification. If the referencing relation is a partitioned table, then
11037 * we create a pg_constraint row for it and recurse on this routine for each
11038 * partition.
11039 *
11040 * We assume that the referenced relation is locked against concurrent
11041 * deletions. If it's a partitioned relation, every partition must be so
11042 * locked.
11043 *
11044 * wqueue: the ALTER TABLE work queue; NULL when not running as part
11045 * of an ALTER TABLE sequence.
11046 * fkconstraint: the constraint being added
11047 * rel: the referencing relation; might be a partition, if recursing
11048 * pkrel: the root referenced relation
11049 * indexOid: the OID of the index (on pkrel) implementing this constraint
11050 * parentConstr: the OID of the parent constraint (there is always one)
11051 * numfks: the number of columns in the foreign key
11052 * pkattnum: the attnum array of referenced attributes
11053 * fkattnum: the attnum array of referencing attributes
11054 * pf/pp/ffeqoperators: OID array of operators between columns
11055 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
11056 * (...) clause
11057 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
11058 * NULL/DEFAULT clause
11059 * old_check_ok: true if this constraint replaces an existing one that
11060 * was already validated (thus this one doesn't need validation)
11061 * lockmode: the lockmode to acquire on partitions when recursing
11062 * parentInsTrigger and parentUpdTrigger: when being recursively called on
11063 * a partition, the OIDs of the parent check triggers for INSERT and
11064 * UPDATE respectively.
11065 * with_period: true if this is a temporal FK
11066 */
11067static void
11069 Relation pkrel, Oid indexOid, Oid parentConstr,
11070 int numfks, int16 *pkattnum, int16 *fkattnum,
11071 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11072 int numfkdelsetcols, int16 *fkdelsetcols,
11073 bool old_check_ok, LOCKMODE lockmode,
11074 Oid parentInsTrigger, Oid parentUpdTrigger,
11075 bool with_period)
11076{
11077 Oid insertTriggerOid = InvalidOid,
11078 updateTriggerOid = InvalidOid;
11079
11080 Assert(OidIsValid(parentConstr));
11083
11084 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11085 ereport(ERROR,
11086 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11087 errmsg("foreign key constraints are not supported on foreign tables")));
11088
11089 /*
11090 * Add check triggers if the constraint is ENFORCED, and if needed,
11091 * schedule them to be checked in Phase 3.
11092 *
11093 * If the relation is partitioned, drill down to do it to its partitions.
11094 */
11095 if (fkconstraint->is_enforced)
11097 RelationGetRelid(pkrel),
11098 fkconstraint,
11099 parentConstr,
11100 indexOid,
11101 parentInsTrigger, parentUpdTrigger,
11102 &insertTriggerOid, &updateTriggerOid);
11103
11104 if (rel->rd_rel->relkind == RELKIND_RELATION)
11105 {
11106 /*
11107 * Tell Phase 3 to check that the constraint is satisfied by existing
11108 * rows. We can skip this during table creation, when constraint is
11109 * specified as NOT ENFORCED, or when requested explicitly by
11110 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11111 * recreating a constraint following a SET DATA TYPE operation that
11112 * did not impugn its validity.
11113 */
11114 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11115 fkconstraint->is_enforced)
11116 {
11117 NewConstraint *newcon;
11118 AlteredTableInfo *tab;
11119
11120 tab = ATGetQueueEntry(wqueue, rel);
11121
11122 newcon = palloc0_object(NewConstraint);
11123 newcon->name = get_constraint_name(parentConstr);
11124 newcon->contype = CONSTR_FOREIGN;
11125 newcon->refrelid = RelationGetRelid(pkrel);
11126 newcon->refindid = indexOid;
11127 newcon->conid = parentConstr;
11128 newcon->conwithperiod = fkconstraint->fk_with_period;
11129 newcon->qual = (Node *) fkconstraint;
11130
11131 tab->constraints = lappend(tab->constraints, newcon);
11132 }
11133 }
11134 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11135 {
11137 Relation trigrel;
11138
11139 /*
11140 * Triggers of the foreign keys will be manipulated a bunch of times
11141 * in the loop below. To avoid repeatedly opening/closing the trigger
11142 * catalog relation, we open it here and pass it to the subroutines
11143 * called below.
11144 */
11145 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11146
11147 /*
11148 * Recurse to take appropriate action on each partition; either we
11149 * find an existing constraint to reparent to ours, or we create a new
11150 * one.
11151 */
11152 for (int i = 0; i < pd->nparts; i++)
11153 {
11154 Relation partition = table_open(pd->oids[i], lockmode);
11155 List *partFKs;
11156 AttrMap *attmap;
11157 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11158 bool attached;
11159 ObjectAddress address;
11160
11161 CheckAlterTableIsSafe(partition);
11162
11163 attmap = build_attrmap_by_name(RelationGetDescr(partition),
11164 RelationGetDescr(rel),
11165 false);
11166 for (int j = 0; j < numfks; j++)
11167 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11168
11169 /* Check whether an existing constraint can be repurposed */
11170 partFKs = copyObject(RelationGetFKeyList(partition));
11171 attached = false;
11172 foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11173 {
11175 fk,
11176 partition,
11177 parentConstr,
11178 numfks,
11179 mapped_fkattnum,
11180 pkattnum,
11181 pfeqoperators,
11182 insertTriggerOid,
11183 updateTriggerOid,
11184 trigrel))
11185 {
11186 attached = true;
11187 break;
11188 }
11189 }
11190 if (attached)
11191 {
11192 table_close(partition, NoLock);
11193 continue;
11194 }
11195
11196 /*
11197 * No luck finding a good constraint to reuse; create our own.
11198 */
11200 fkconstraint->conname, fkconstraint,
11201 partition, pkrel, indexOid, parentConstr,
11202 numfks, pkattnum,
11203 mapped_fkattnum, pfeqoperators,
11204 ppeqoperators, ffeqoperators,
11205 numfkdelsetcols, fkdelsetcols, true,
11206 with_period);
11207
11208 /* call ourselves to finalize the creation and we're done */
11209 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11210 indexOid,
11211 address.objectId,
11212 numfks,
11213 pkattnum,
11214 mapped_fkattnum,
11215 pfeqoperators,
11216 ppeqoperators,
11217 ffeqoperators,
11218 numfkdelsetcols,
11219 fkdelsetcols,
11220 old_check_ok,
11221 lockmode,
11222 insertTriggerOid,
11223 updateTriggerOid,
11224 with_period);
11225
11226 table_close(partition, NoLock);
11227 }
11228
11229 table_close(trigrel, RowExclusiveLock);
11230 }
11231}
11232
11233/*
11234 * CloneForeignKeyConstraints
11235 * Clone foreign keys from a partitioned table to a newly acquired
11236 * partition.
11237 *
11238 * partitionRel is a partition of parentRel, so we can be certain that it has
11239 * the same columns with the same datatypes. The columns may be in different
11240 * order, though.
11241 *
11242 * wqueue must be passed to set up phase 3 constraint checking, unless the
11243 * referencing-side partition is known to be empty (such as in CREATE TABLE /
11244 * PARTITION OF).
11245 */
11246static void
11248 Relation partitionRel)
11249{
11250 /* This only works for declarative partitioning */
11251 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11252
11253 /*
11254 * First, clone constraints where the parent is on the referencing side.
11255 */
11256 CloneFkReferencing(wqueue, parentRel, partitionRel);
11257
11258 /*
11259 * Clone constraints for which the parent is on the referenced side.
11260 */
11261 CloneFkReferenced(parentRel, partitionRel);
11262}
11263
11264/*
11265 * CloneFkReferenced
11266 * Subroutine for CloneForeignKeyConstraints
11267 *
11268 * Find all the FKs that have the parent relation on the referenced side;
11269 * clone those constraints to the given partition. This is to be called
11270 * when the partition is being created or attached.
11271 *
11272 * This recurses to partitions, if the relation being attached is partitioned.
11273 * Recursion is done by calling addFkRecurseReferenced.
11274 */
11275static void
11276CloneFkReferenced(Relation parentRel, Relation partitionRel)
11277{
11278 Relation pg_constraint;
11279 AttrMap *attmap;
11280 ListCell *cell;
11281 SysScanDesc scan;
11282 ScanKeyData key[2];
11283 HeapTuple tuple;
11284 List *clone = NIL;
11285 Relation trigrel;
11286
11287 /*
11288 * Search for any constraints where this partition's parent is in the
11289 * referenced side. However, we must not clone any constraint whose
11290 * parent constraint is also going to be cloned, to avoid duplicates. So
11291 * do it in two steps: first construct the list of constraints to clone,
11292 * then go over that list cloning those whose parents are not in the list.
11293 * (We must not rely on the parent being seen first, since the catalog
11294 * scan could return children first.)
11295 */
11296 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11297 ScanKeyInit(&key[0],
11298 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11299 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11300 ScanKeyInit(&key[1],
11301 Anum_pg_constraint_contype, BTEqualStrategyNumber,
11302 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11303 /* This is a seqscan, as we don't have a usable index ... */
11304 scan = systable_beginscan(pg_constraint, InvalidOid, true,
11305 NULL, 2, key);
11306 while ((tuple = systable_getnext(scan)) != NULL)
11307 {
11308 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11309
11310 clone = lappend_oid(clone, constrForm->oid);
11311 }
11312 systable_endscan(scan);
11313 table_close(pg_constraint, RowShareLock);
11314
11315 /*
11316 * Triggers of the foreign keys will be manipulated a bunch of times in
11317 * the loop below. To avoid repeatedly opening/closing the trigger
11318 * catalog relation, we open it here and pass it to the subroutines called
11319 * below.
11320 */
11321 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11322
11323 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11324 RelationGetDescr(parentRel),
11325 false);
11326 foreach(cell, clone)
11327 {
11328 Oid constrOid = lfirst_oid(cell);
11329 Form_pg_constraint constrForm;
11330 Relation fkRel;
11331 Oid indexOid;
11332 Oid partIndexId;
11333 int numfks;
11334 AttrNumber conkey[INDEX_MAX_KEYS];
11335 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11336 AttrNumber confkey[INDEX_MAX_KEYS];
11337 Oid conpfeqop[INDEX_MAX_KEYS];
11338 Oid conppeqop[INDEX_MAX_KEYS];
11339 Oid conffeqop[INDEX_MAX_KEYS];
11340 int numfkdelsetcols;
11341 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11342 Constraint *fkconstraint;
11343 ObjectAddress address;
11344 Oid deleteTriggerOid = InvalidOid,
11345 updateTriggerOid = InvalidOid;
11346
11347 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11348 if (!HeapTupleIsValid(tuple))
11349 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11350 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11351
11352 /*
11353 * As explained above: don't try to clone a constraint for which we're
11354 * going to clone the parent.
11355 */
11356 if (list_member_oid(clone, constrForm->conparentid))
11357 {
11358 ReleaseSysCache(tuple);
11359 continue;
11360 }
11361
11362 /* We need the same lock level that CreateTrigger will acquire */
11363 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11364
11365 indexOid = constrForm->conindid;
11367 &numfks,
11368 conkey,
11369 confkey,
11370 conpfeqop,
11371 conppeqop,
11372 conffeqop,
11373 &numfkdelsetcols,
11374 confdelsetcols);
11375
11376 for (int i = 0; i < numfks; i++)
11377 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11378
11379 fkconstraint = makeNode(Constraint);
11380 fkconstraint->contype = CONSTRAINT_FOREIGN;
11381 fkconstraint->conname = NameStr(constrForm->conname);
11382 fkconstraint->deferrable = constrForm->condeferrable;
11383 fkconstraint->initdeferred = constrForm->condeferred;
11384 fkconstraint->location = -1;
11385 fkconstraint->pktable = NULL;
11386 /* ->fk_attrs determined below */
11387 fkconstraint->pk_attrs = NIL;
11388 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11389 fkconstraint->fk_upd_action = constrForm->confupdtype;
11390 fkconstraint->fk_del_action = constrForm->confdeltype;
11391 fkconstraint->fk_del_set_cols = NIL;
11392 fkconstraint->old_conpfeqop = NIL;
11393 fkconstraint->old_pktable_oid = InvalidOid;
11394 fkconstraint->is_enforced = constrForm->conenforced;
11395 fkconstraint->skip_validation = false;
11396 fkconstraint->initially_valid = constrForm->convalidated;
11397
11398 /* set up colnames that are used to generate the constraint name */
11399 for (int i = 0; i < numfks; i++)
11400 {
11402
11403 att = TupleDescAttr(RelationGetDescr(fkRel),
11404 conkey[i] - 1);
11405 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11406 makeString(NameStr(att->attname)));
11407 }
11408
11409 /*
11410 * Add the new foreign key constraint pointing to the new partition.
11411 * Because this new partition appears in the referenced side of the
11412 * constraint, we don't need to set up for Phase 3 check.
11413 */
11414 partIndexId = index_get_partition(partitionRel, indexOid);
11415 if (!OidIsValid(partIndexId))
11416 elog(ERROR, "index for %u not found in partition %s",
11417 indexOid, RelationGetRelationName(partitionRel));
11418
11419 /*
11420 * Get the "action" triggers belonging to the constraint to pass as
11421 * parent OIDs for similar triggers that will be created on the
11422 * partition in addFkRecurseReferenced().
11423 */
11424 if (constrForm->conenforced)
11425 GetForeignKeyActionTriggers(trigrel, constrOid,
11426 constrForm->confrelid, constrForm->conrelid,
11427 &deleteTriggerOid, &updateTriggerOid);
11428
11429 /* Add this constraint ... */
11431 fkconstraint->conname, fkconstraint, fkRel,
11432 partitionRel, partIndexId, constrOid,
11433 numfks, mapped_confkey,
11434 conkey, conpfeqop, conppeqop, conffeqop,
11435 numfkdelsetcols, confdelsetcols, false,
11436 constrForm->conperiod);
11437 /* ... and recurse */
11438 addFkRecurseReferenced(fkconstraint,
11439 fkRel,
11440 partitionRel,
11441 partIndexId,
11442 address.objectId,
11443 numfks,
11444 mapped_confkey,
11445 conkey,
11446 conpfeqop,
11447 conppeqop,
11448 conffeqop,
11449 numfkdelsetcols,
11450 confdelsetcols,
11451 true,
11452 deleteTriggerOid,
11453 updateTriggerOid,
11454 constrForm->conperiod);
11455
11456 table_close(fkRel, NoLock);
11457 ReleaseSysCache(tuple);
11458 }
11459
11460 table_close(trigrel, RowExclusiveLock);
11461}
11462
11463/*
11464 * CloneFkReferencing
11465 * Subroutine for CloneForeignKeyConstraints
11466 *
11467 * For each FK constraint of the parent relation in the given list, find an
11468 * equivalent constraint in its partition relation that can be reparented;
11469 * if one cannot be found, create a new constraint in the partition as its
11470 * child.
11471 *
11472 * If wqueue is given, it is used to set up phase-3 verification for each
11473 * cloned constraint; omit it if such verification is not needed
11474 * (example: the partition is being created anew).
11475 */
11476static void
11477CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11478{
11479 AttrMap *attmap;
11480 List *partFKs;
11481 List *clone = NIL;
11482 ListCell *cell;
11483 Relation trigrel;
11484
11485 /* obtain a list of constraints that we need to clone */
11486 foreach(cell, RelationGetFKeyList(parentRel))
11487 {
11488 ForeignKeyCacheInfo *fk = lfirst(cell);
11489
11490 /*
11491 * Refuse to attach a table as partition that this partitioned table
11492 * already has a foreign key to. This isn't useful schema, which is
11493 * proven by the fact that there have been no user complaints that
11494 * it's already impossible to achieve this in the opposite direction,
11495 * i.e., creating a foreign key that references a partition. This
11496 * restriction allows us to dodge some complexities around
11497 * pg_constraint and pg_trigger row creations that would be needed
11498 * during ATTACH/DETACH for this kind of relationship.
11499 */
11500 if (fk->confrelid == RelationGetRelid(partRel))
11501 ereport(ERROR,
11502 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11503 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11504 RelationGetRelationName(partRel),
11506
11507 clone = lappend_oid(clone, fk->conoid);
11508 }
11509
11510 /*
11511 * Silently do nothing if there's nothing to do. In particular, this
11512 * avoids throwing a spurious error for foreign tables.
11513 */
11514 if (clone == NIL)
11515 return;
11516
11517 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11518 ereport(ERROR,
11519 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11520 errmsg("foreign key constraints are not supported on foreign tables")));
11521
11522 /*
11523 * Triggers of the foreign keys will be manipulated a bunch of times in
11524 * the loop below. To avoid repeatedly opening/closing the trigger
11525 * catalog relation, we open it here and pass it to the subroutines called
11526 * below.
11527 */
11528 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11529
11530 /*
11531 * The constraint key may differ, if the columns in the partition are
11532 * different. This map is used to convert them.
11533 */
11534 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11535 RelationGetDescr(parentRel),
11536 false);
11537
11538 partFKs = copyObject(RelationGetFKeyList(partRel));
11539
11540 foreach(cell, clone)
11541 {
11542 Oid parentConstrOid = lfirst_oid(cell);
11543 Form_pg_constraint constrForm;
11544 Relation pkrel;
11545 HeapTuple tuple;
11546 int numfks;
11547 AttrNumber conkey[INDEX_MAX_KEYS];
11548 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11549 AttrNumber confkey[INDEX_MAX_KEYS];
11550 Oid conpfeqop[INDEX_MAX_KEYS];
11551 Oid conppeqop[INDEX_MAX_KEYS];
11552 Oid conffeqop[INDEX_MAX_KEYS];
11553 int numfkdelsetcols;
11554 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11555 Constraint *fkconstraint;
11556 bool attached;
11557 Oid indexOid;
11558 ObjectAddress address;
11559 ListCell *lc;
11560 Oid insertTriggerOid = InvalidOid,
11561 updateTriggerOid = InvalidOid;
11562 bool with_period;
11563
11564 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11565 if (!HeapTupleIsValid(tuple))
11566 elog(ERROR, "cache lookup failed for constraint %u",
11567 parentConstrOid);
11568 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11569
11570 /* Don't clone constraints whose parents are being cloned */
11571 if (list_member_oid(clone, constrForm->conparentid))
11572 {
11573 ReleaseSysCache(tuple);
11574 continue;
11575 }
11576
11577 /*
11578 * Need to prevent concurrent deletions. If pkrel is a partitioned
11579 * relation, that means to lock all partitions.
11580 */
11581 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11582 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11584 ShareRowExclusiveLock, NULL);
11585
11586 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11587 conpfeqop, conppeqop, conffeqop,
11588 &numfkdelsetcols, confdelsetcols);
11589 for (int i = 0; i < numfks; i++)
11590 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11591
11592 /*
11593 * Get the "check" triggers belonging to the constraint, if it is
11594 * ENFORCED, to pass as parent OIDs for similar triggers that will be
11595 * created on the partition in addFkRecurseReferencing(). They are
11596 * also passed to tryAttachPartitionForeignKey() below to simply
11597 * assign as parents to the partition's existing "check" triggers,
11598 * that is, if the corresponding constraints is deemed attachable to
11599 * the parent constraint.
11600 */
11601 if (constrForm->conenforced)
11602 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11603 constrForm->confrelid, constrForm->conrelid,
11604 &insertTriggerOid, &updateTriggerOid);
11605
11606 /*
11607 * Before creating a new constraint, see whether any existing FKs are
11608 * fit for the purpose. If one is, attach the parent constraint to
11609 * it, and don't clone anything. This way we avoid the expensive
11610 * verification step and don't end up with a duplicate FK, and we
11611 * don't need to recurse to partitions for this constraint.
11612 */
11613 attached = false;
11614 foreach(lc, partFKs)
11615 {
11617
11619 fk,
11620 partRel,
11621 parentConstrOid,
11622 numfks,
11623 mapped_conkey,
11624 confkey,
11625 conpfeqop,
11626 insertTriggerOid,
11627 updateTriggerOid,
11628 trigrel))
11629 {
11630 attached = true;
11631 table_close(pkrel, NoLock);
11632 break;
11633 }
11634 }
11635 if (attached)
11636 {
11637 ReleaseSysCache(tuple);
11638 continue;
11639 }
11640
11641 /* No dice. Set up to create our own constraint */
11642 fkconstraint = makeNode(Constraint);
11643 fkconstraint->contype = CONSTRAINT_FOREIGN;
11644 /* ->conname determined below */
11645 fkconstraint->deferrable = constrForm->condeferrable;
11646 fkconstraint->initdeferred = constrForm->condeferred;
11647 fkconstraint->location = -1;
11648 fkconstraint->pktable = NULL;
11649 /* ->fk_attrs determined below */
11650 fkconstraint->pk_attrs = NIL;
11651 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11652 fkconstraint->fk_upd_action = constrForm->confupdtype;
11653 fkconstraint->fk_del_action = constrForm->confdeltype;
11654 fkconstraint->fk_del_set_cols = NIL;
11655 fkconstraint->old_conpfeqop = NIL;
11656 fkconstraint->old_pktable_oid = InvalidOid;
11657 fkconstraint->is_enforced = constrForm->conenforced;
11658 fkconstraint->skip_validation = false;
11659 fkconstraint->initially_valid = constrForm->convalidated;
11660 for (int i = 0; i < numfks; i++)
11661 {
11663
11664 att = TupleDescAttr(RelationGetDescr(partRel),
11665 mapped_conkey[i] - 1);
11666 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11667 makeString(NameStr(att->attname)));
11668 }
11669
11670 indexOid = constrForm->conindid;
11671 with_period = constrForm->conperiod;
11672
11673 /* Create the pg_constraint entry at this level */
11675 NameStr(constrForm->conname), fkconstraint,
11676 partRel, pkrel, indexOid, parentConstrOid,
11677 numfks, confkey,
11678 mapped_conkey, conpfeqop,
11679 conppeqop, conffeqop,
11680 numfkdelsetcols, confdelsetcols,
11681 false, with_period);
11682
11683 /* Done with the cloned constraint's tuple */
11684 ReleaseSysCache(tuple);
11685
11686 /* Create the check triggers, and recurse to partitions, if any */
11688 fkconstraint,
11689 partRel,
11690 pkrel,
11691 indexOid,
11692 address.objectId,
11693 numfks,
11694 confkey,
11695 mapped_conkey,
11696 conpfeqop,
11697 conppeqop,
11698 conffeqop,
11699 numfkdelsetcols,
11700 confdelsetcols,
11701 false, /* no old check exists */
11703 insertTriggerOid,
11704 updateTriggerOid,
11705 with_period);
11706 table_close(pkrel, NoLock);
11707 }
11708
11709 table_close(trigrel, RowExclusiveLock);
11710}
11711
11712/*
11713 * When the parent of a partition receives [the referencing side of] a foreign
11714 * key, we must propagate that foreign key to the partition. However, the
11715 * partition might already have an equivalent foreign key; this routine
11716 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11717 * by the other parameters. If they are equivalent, create the link between
11718 * the two constraints and return true.
11719 *
11720 * If the given FK does not match the one defined by rest of the params,
11721 * return false.
11722 */
11723static bool
11726 Relation partition,
11727 Oid parentConstrOid,
11728 int numfks,
11729 AttrNumber *mapped_conkey,
11730 AttrNumber *confkey,
11731 Oid *conpfeqop,
11732 Oid parentInsTrigger,
11733 Oid parentUpdTrigger,
11734 Relation trigrel)
11735{
11736 HeapTuple parentConstrTup;
11737 Form_pg_constraint parentConstr;
11738 HeapTuple partcontup;
11739 Form_pg_constraint partConstr;
11740
11741 parentConstrTup = SearchSysCache1(CONSTROID,
11742 ObjectIdGetDatum(parentConstrOid));
11743 if (!HeapTupleIsValid(parentConstrTup))
11744 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11745 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11746
11747 /*
11748 * Do some quick & easy initial checks. If any of these fail, we cannot
11749 * use this constraint.
11750 */
11751 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11752 {
11753 ReleaseSysCache(parentConstrTup);
11754 return false;
11755 }
11756 for (int i = 0; i < numfks; i++)
11757 {
11758 if (fk->conkey[i] != mapped_conkey[i] ||
11759 fk->confkey[i] != confkey[i] ||
11760 fk->conpfeqop[i] != conpfeqop[i])
11761 {
11762 ReleaseSysCache(parentConstrTup);
11763 return false;
11764 }
11765 }
11766
11767 /* Looks good so far; perform more extensive checks. */
11768 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11769 if (!HeapTupleIsValid(partcontup))
11770 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11771 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11772
11773 /*
11774 * An error should be raised if the constraint enforceability is
11775 * different. Returning false without raising an error, as we do for other
11776 * attributes, could lead to a duplicate constraint with the same
11777 * enforceability as the parent. While this may be acceptable, it may not
11778 * be ideal. Therefore, it's better to raise an error and allow the user
11779 * to correct the enforceability before proceeding.
11780 */
11781 if (partConstr->conenforced != parentConstr->conenforced)
11782 ereport(ERROR,
11783 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11784 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11785 NameStr(parentConstr->conname),
11786 NameStr(partConstr->conname),
11787 RelationGetRelationName(partition))));
11788
11789 if (OidIsValid(partConstr->conparentid) ||
11790 partConstr->condeferrable != parentConstr->condeferrable ||
11791 partConstr->condeferred != parentConstr->condeferred ||
11792 partConstr->confupdtype != parentConstr->confupdtype ||
11793 partConstr->confdeltype != parentConstr->confdeltype ||
11794 partConstr->confmatchtype != parentConstr->confmatchtype)
11795 {
11796 ReleaseSysCache(parentConstrTup);
11797 ReleaseSysCache(partcontup);
11798 return false;
11799 }
11800
11801 ReleaseSysCache(parentConstrTup);
11802 ReleaseSysCache(partcontup);
11803
11804 /* Looks good! Attach this constraint. */
11805 AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11806 parentConstrOid, parentInsTrigger,
11807 parentUpdTrigger, trigrel);
11808
11809 return true;
11810}
11811
11812/*
11813 * AttachPartitionForeignKey
11814 *
11815 * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11816 * attaching the constraint, removing redundant triggers and entries from
11817 * pg_constraint, and setting the constraint's parent.
11818 */
11819static void
11821 Relation partition,
11822 Oid partConstrOid,
11823 Oid parentConstrOid,
11824 Oid parentInsTrigger,
11825 Oid parentUpdTrigger,
11826 Relation trigrel)
11827{
11828 HeapTuple parentConstrTup;
11829 Form_pg_constraint parentConstr;
11830 HeapTuple partcontup;
11831 Form_pg_constraint partConstr;
11832 bool queueValidation;
11833 Oid partConstrFrelid;
11834 Oid partConstrRelid;
11835 bool parentConstrIsEnforced;
11836
11837 /* Fetch the parent constraint tuple */
11838 parentConstrTup = SearchSysCache1(CONSTROID,
11839 ObjectIdGetDatum(parentConstrOid));
11840 if (!HeapTupleIsValid(parentConstrTup))
11841 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11842 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11843 parentConstrIsEnforced = parentConstr->conenforced;
11844
11845 /* Fetch the child constraint tuple */
11846 partcontup = SearchSysCache1(CONSTROID,
11847 ObjectIdGetDatum(partConstrOid));
11848 if (!HeapTupleIsValid(partcontup))
11849 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11850 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11851 partConstrFrelid = partConstr->confrelid;
11852 partConstrRelid = partConstr->conrelid;
11853
11854 /*
11855 * If the referenced table is partitioned, then the partition we're
11856 * attaching now has extra pg_constraint rows and action triggers that are
11857 * no longer needed. Remove those.
11858 */
11859 if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11860 {
11861 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11862
11863 RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11864 partConstrRelid);
11865
11866 table_close(pg_constraint, RowShareLock);
11867 }
11868
11869 /*
11870 * Will we need to validate this constraint? A valid parent constraint
11871 * implies that all child constraints have been validated, so if this one
11872 * isn't, we must trigger phase 3 validation.
11873 */
11874 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11875
11876 ReleaseSysCache(partcontup);
11877 ReleaseSysCache(parentConstrTup);
11878
11879 /*
11880 * The action triggers in the new partition become redundant -- the parent
11881 * table already has equivalent ones, and those will be able to reach the
11882 * partition. Remove the ones in the partition. We identify them because
11883 * they have our constraint OID, as well as being on the referenced rel.
11884 */
11885 DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11886 partConstrRelid);
11887
11888 ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11889 RelationGetRelid(partition));
11890
11891 /*
11892 * Like the constraint, attach partition's "check" triggers to the
11893 * corresponding parent triggers if the constraint is ENFORCED. NOT
11894 * ENFORCED constraints do not have these triggers.
11895 */
11896 if (parentConstrIsEnforced)
11897 {
11898 Oid insertTriggerOid,
11899 updateTriggerOid;
11900
11902 partConstrOid, partConstrFrelid, partConstrRelid,
11903 &insertTriggerOid, &updateTriggerOid);
11904 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11905 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11906 RelationGetRelid(partition));
11907 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11908 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11909 RelationGetRelid(partition));
11910 }
11911
11912 /*
11913 * We updated this pg_constraint row above to set its parent; validating
11914 * it will cause its convalidated flag to change, so we need CCI here. In
11915 * addition, we need it unconditionally for the rare case where the parent
11916 * table has *two* identical constraints; when reaching this function for
11917 * the second one, we must have made our changes visible, otherwise we
11918 * would try to attach both to this one.
11919 */
11921
11922 /* If validation is needed, put it in the queue now. */
11923 if (queueValidation)
11924 {
11925 Relation conrel;
11926 Oid confrelid;
11927
11928 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11929
11930 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11931 if (!HeapTupleIsValid(partcontup))
11932 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11933
11934 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11935
11936 /* Use the same lock as for AT_ValidateConstraint */
11937 QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11938 partcontup, ShareUpdateExclusiveLock);
11939 ReleaseSysCache(partcontup);
11941 }
11942}
11943
11944/*
11945 * RemoveInheritedConstraint
11946 *
11947 * Removes the constraint and its associated trigger from the specified
11948 * relation, which inherited the given constraint.
11949 */
11950static void
11952 Oid conrelid)
11953{
11954 ObjectAddresses *objs;
11955 HeapTuple consttup;
11957 SysScanDesc scan;
11958 HeapTuple trigtup;
11959
11961 Anum_pg_constraint_conrelid,
11962 BTEqualStrategyNumber, F_OIDEQ,
11963 ObjectIdGetDatum(conrelid));
11964
11965 scan = systable_beginscan(conrel,
11966 ConstraintRelidTypidNameIndexId,
11967 true, NULL, 1, &key);
11968 objs = new_object_addresses();
11969 while ((consttup = systable_getnext(scan)) != NULL)
11970 {
11971 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11972
11973 if (conform->conparentid != conoid)
11974 continue;
11975 else
11976 {
11977 ObjectAddress addr;
11978 SysScanDesc scan2;
11979 ScanKeyData key2;
11981
11982 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11983 add_exact_object_address(&addr, objs);
11984
11985 /*
11986 * First we must delete the dependency record that binds the
11987 * constraint records together.
11988 */
11989 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11990 conform->oid,
11992 ConstraintRelationId,
11993 conoid);
11994 Assert(n == 1); /* actually only one is expected */
11995
11996 /*
11997 * Now search for the triggers for this constraint and set them up
11998 * for deletion too
11999 */
12000 ScanKeyInit(&key2,
12001 Anum_pg_trigger_tgconstraint,
12002 BTEqualStrategyNumber, F_OIDEQ,
12003 ObjectIdGetDatum(conform->oid));
12004 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
12005 true, NULL, 1, &key2);
12006 while ((trigtup = systable_getnext(scan2)) != NULL)
12007 {
12008 ObjectAddressSet(addr, TriggerRelationId,
12009 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
12010 add_exact_object_address(&addr, objs);
12011 }
12012 systable_endscan(scan2);
12013 }
12014 }
12015 /* make the dependency deletions visible */
12019 systable_endscan(scan);
12020}
12021
12022/*
12023 * DropForeignKeyConstraintTriggers
12024 *
12025 * The subroutine for tryAttachPartitionForeignKey handles the deletion of
12026 * action triggers for the foreign key constraint.
12027 *
12028 * If valid confrelid and conrelid values are not provided, the respective
12029 * trigger check will be skipped, and the trigger will be considered for
12030 * removal.
12031 */
12032static void
12034 Oid conrelid)
12035{
12037 SysScanDesc scan;
12038 HeapTuple trigtup;
12039
12041 Anum_pg_trigger_tgconstraint,
12042 BTEqualStrategyNumber, F_OIDEQ,
12043 ObjectIdGetDatum(conoid));
12044 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12045 NULL, 1, &key);
12046 while ((trigtup = systable_getnext(scan)) != NULL)
12047 {
12048 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12049 ObjectAddress trigger;
12050
12051 /* Invalid if trigger is not for a referential integrity constraint */
12052 if (!OidIsValid(trgform->tgconstrrelid))
12053 continue;
12054 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12055 continue;
12056 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12057 continue;
12058
12059 /* We should be dropping trigger related to foreign key constraint */
12060 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12061 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12062 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12063 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12064 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12065 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12066 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12067 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12068 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12069 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12070 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12071 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12072
12073 /*
12074 * The constraint is originally set up to contain this trigger as an
12075 * implementation object, so there's a dependency record that links
12076 * the two; however, since the trigger is no longer needed, we remove
12077 * the dependency link in order to be able to drop the trigger while
12078 * keeping the constraint intact.
12079 */
12080 deleteDependencyRecordsFor(TriggerRelationId,
12081 trgform->oid,
12082 false);
12083 /* make dependency deletion visible to performDeletion */
12085 ObjectAddressSet(trigger, TriggerRelationId,
12086 trgform->oid);
12087 performDeletion(&trigger, DROP_RESTRICT, 0);
12088 /* make trigger drop visible, in case the loop iterates */
12090 }
12091
12092 systable_endscan(scan);
12093}
12094
12095/*
12096 * GetForeignKeyActionTriggers
12097 * Returns delete and update "action" triggers of the given relation
12098 * belonging to the given constraint
12099 */
12100static void
12102 Oid conoid, Oid confrelid, Oid conrelid,
12103 Oid *deleteTriggerOid,
12104 Oid *updateTriggerOid)
12105{
12107 SysScanDesc scan;
12108 HeapTuple trigtup;
12109
12110 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12112 Anum_pg_trigger_tgconstraint,
12113 BTEqualStrategyNumber, F_OIDEQ,
12114 ObjectIdGetDatum(conoid));
12115
12116 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12117 NULL, 1, &key);
12118 while ((trigtup = systable_getnext(scan)) != NULL)
12119 {
12120 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12121
12122 if (trgform->tgconstrrelid != conrelid)
12123 continue;
12124 if (trgform->tgrelid != confrelid)
12125 continue;
12126 /* Only ever look at "action" triggers on the PK side. */
12127 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12128 continue;
12129 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12130 {
12131 Assert(*deleteTriggerOid == InvalidOid);
12132 *deleteTriggerOid = trgform->oid;
12133 }
12134 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12135 {
12136 Assert(*updateTriggerOid == InvalidOid);
12137 *updateTriggerOid = trgform->oid;
12138 }
12139#ifndef USE_ASSERT_CHECKING
12140 /* In an assert-enabled build, continue looking to find duplicates */
12141 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12142 break;
12143#endif
12144 }
12145
12146 if (!OidIsValid(*deleteTriggerOid))
12147 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12148 conoid);
12149 if (!OidIsValid(*updateTriggerOid))
12150 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12151 conoid);
12152
12153 systable_endscan(scan);
12154}
12155
12156/*
12157 * GetForeignKeyCheckTriggers
12158 * Returns insert and update "check" triggers of the given relation
12159 * belonging to the given constraint
12160 */
12161static void
12163 Oid conoid, Oid confrelid, Oid conrelid,
12164 Oid *insertTriggerOid,
12165 Oid *updateTriggerOid)
12166{
12168 SysScanDesc scan;
12169 HeapTuple trigtup;
12170
12171 *insertTriggerOid = *updateTriggerOid = InvalidOid;
12173 Anum_pg_trigger_tgconstraint,
12174 BTEqualStrategyNumber, F_OIDEQ,
12175 ObjectIdGetDatum(conoid));
12176
12177 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12178 NULL, 1, &key);
12179 while ((trigtup = systable_getnext(scan)) != NULL)
12180 {
12181 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12182
12183 if (trgform->tgconstrrelid != confrelid)
12184 continue;
12185 if (trgform->tgrelid != conrelid)
12186 continue;
12187 /* Only ever look at "check" triggers on the FK side. */
12188 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12189 continue;
12190 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12191 {
12192 Assert(*insertTriggerOid == InvalidOid);
12193 *insertTriggerOid = trgform->oid;
12194 }
12195 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12196 {
12197 Assert(*updateTriggerOid == InvalidOid);
12198 *updateTriggerOid = trgform->oid;
12199 }
12200#ifndef USE_ASSERT_CHECKING
12201 /* In an assert-enabled build, continue looking to find duplicates. */
12202 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12203 break;
12204#endif
12205 }
12206
12207 if (!OidIsValid(*insertTriggerOid))
12208 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12209 conoid);
12210 if (!OidIsValid(*updateTriggerOid))
12211 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12212 conoid);
12213
12214 systable_endscan(scan);
12215}
12216
12217/*
12218 * ALTER TABLE ALTER CONSTRAINT
12219 *
12220 * Update the attributes of a constraint.
12221 *
12222 * Currently only works for Foreign Key and not null constraints.
12223 *
12224 * If the constraint is modified, returns its address; otherwise, return
12225 * InvalidObjectAddress.
12226 */
12227static ObjectAddress
12229 bool recurse, LOCKMODE lockmode)
12230{
12231 Relation conrel;
12232 Relation tgrel;
12233 SysScanDesc scan;
12234 ScanKeyData skey[3];
12235 HeapTuple contuple;
12236 Form_pg_constraint currcon;
12237 ObjectAddress address;
12238
12239 /*
12240 * Disallow altering ONLY a partitioned table, as it would make no sense.
12241 * This is okay for legacy inheritance.
12242 */
12243 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12244 ereport(ERROR,
12245 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12246 errmsg("constraint must be altered in child tables too"),
12247 errhint("Do not specify the ONLY keyword."));
12248
12249
12250 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12251 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12252
12253 /*
12254 * Find and check the target constraint
12255 */
12256 ScanKeyInit(&skey[0],
12257 Anum_pg_constraint_conrelid,
12258 BTEqualStrategyNumber, F_OIDEQ,
12260 ScanKeyInit(&skey[1],
12261 Anum_pg_constraint_contypid,
12262 BTEqualStrategyNumber, F_OIDEQ,
12264 ScanKeyInit(&skey[2],
12265 Anum_pg_constraint_conname,
12266 BTEqualStrategyNumber, F_NAMEEQ,
12267 CStringGetDatum(cmdcon->conname));
12268 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12269 true, NULL, 3, skey);
12270
12271 /* There can be at most one matching row */
12272 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12273 ereport(ERROR,
12274 (errcode(ERRCODE_UNDEFINED_OBJECT),
12275 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12276 cmdcon->conname, RelationGetRelationName(rel))));
12277
12278 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12279 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12280 ereport(ERROR,
12281 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12282 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12283 cmdcon->conname, RelationGetRelationName(rel))));
12284 if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12285 ereport(ERROR,
12286 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12287 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12288 cmdcon->conname, RelationGetRelationName(rel))));
12289 if (cmdcon->alterInheritability &&
12290 currcon->contype != CONSTRAINT_NOTNULL)
12291 ereport(ERROR,
12292 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12293 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12294 cmdcon->conname, RelationGetRelationName(rel)));
12295
12296 /* Refuse to modify inheritability of inherited constraints */
12297 if (cmdcon->alterInheritability &&
12298 cmdcon->noinherit && currcon->coninhcount > 0)
12299 ereport(ERROR,
12300 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12301 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12302 NameStr(currcon->conname),
12304
12305 /*
12306 * If it's not the topmost constraint, raise an error.
12307 *
12308 * Altering a non-topmost constraint leaves some triggers untouched, since
12309 * they are not directly connected to this constraint; also, pg_dump would
12310 * ignore the deferrability status of the individual constraint, since it
12311 * only dumps topmost constraints. Avoid these problems by refusing this
12312 * operation and telling the user to alter the parent constraint instead.
12313 */
12314 if (OidIsValid(currcon->conparentid))
12315 {
12316 HeapTuple tp;
12317 Oid parent = currcon->conparentid;
12318 char *ancestorname = NULL;
12319 char *ancestortable = NULL;
12320
12321 /* Loop to find the topmost constraint */
12322 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12323 {
12325
12326 /* If no parent, this is the constraint we want */
12327 if (!OidIsValid(contup->conparentid))
12328 {
12329 ancestorname = pstrdup(NameStr(contup->conname));
12330 ancestortable = get_rel_name(contup->conrelid);
12331 ReleaseSysCache(tp);
12332 break;
12333 }
12334
12335 parent = contup->conparentid;
12336 ReleaseSysCache(tp);
12337 }
12338
12339 ereport(ERROR,
12340 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12341 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12342 cmdcon->conname, RelationGetRelationName(rel)),
12343 ancestorname && ancestortable ?
12344 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12345 cmdcon->conname, ancestorname, ancestortable) : 0,
12346 errhint("You may alter the constraint it derives from instead.")));
12347 }
12348
12349 address = InvalidObjectAddress;
12350
12351 /*
12352 * Do the actual catalog work, and recurse if necessary.
12353 */
12354 if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12355 contuple, recurse, lockmode))
12356 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12357
12358 systable_endscan(scan);
12359
12362
12363 return address;
12364}
12365
12366/*
12367 * A subroutine of ATExecAlterConstraint that calls the respective routines for
12368 * altering constraint's enforceability, deferrability or inheritability.
12369 */
12370static bool
12372 Relation conrel, Relation tgrel, Relation rel,
12373 HeapTuple contuple, bool recurse,
12374 LOCKMODE lockmode)
12375{
12376 Form_pg_constraint currcon;
12377 bool changed = false;
12378 List *otherrelids = NIL;
12379
12380 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12381
12382 /*
12383 * Do the catalog work for the enforceability or deferrability change,
12384 * recurse if necessary.
12385 *
12386 * Note that even if deferrability is requested to be altered along with
12387 * enforceability, we don't need to explicitly update multiple entries in
12388 * pg_trigger related to deferrability.
12389 *
12390 * Modifying enforceability involves either creating or dropping the
12391 * trigger, during which the deferrability setting will be adjusted
12392 * automatically.
12393 */
12394 if (cmdcon->alterEnforceability &&
12395 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12396 currcon->conrelid, currcon->confrelid,
12397 contuple, lockmode, InvalidOid,
12399 changed = true;
12400
12401 else if (cmdcon->alterDeferrability &&
12402 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12403 contuple, recurse, &otherrelids,
12404 lockmode))
12405 {
12406 /*
12407 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12408 * the relations having the constraint itself; here we also invalidate
12409 * for relations that have any triggers that are part of the
12410 * constraint.
12411 */
12412 foreach_oid(relid, otherrelids)
12414
12415 changed = true;
12416 }
12417
12418 /*
12419 * Do the catalog work for the inheritability change.
12420 */
12421 if (cmdcon->alterInheritability &&
12422 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12423 lockmode))
12424 changed = true;
12425
12426 return changed;
12427}
12428
12429/*
12430 * Returns true if the constraint's enforceability is altered.
12431 *
12432 * Depending on whether the constraint is being set to ENFORCED or NOT
12433 * ENFORCED, it creates or drops the trigger accordingly.
12434 *
12435 * Note that we must recurse even when trying to change a constraint to not
12436 * enforced if it is already not enforced, in case descendant constraints
12437 * might be enforced and need to be changed to not enforced. Conversely, we
12438 * should do nothing if a constraint is being set to enforced and is already
12439 * enforced, as descendant constraints cannot be different in that case.
12440 */
12441static bool
12443 Relation conrel, Relation tgrel,
12444 Oid fkrelid, Oid pkrelid,
12445 HeapTuple contuple, LOCKMODE lockmode,
12446 Oid ReferencedParentDelTrigger,
12447 Oid ReferencedParentUpdTrigger,
12448 Oid ReferencingParentInsTrigger,
12449 Oid ReferencingParentUpdTrigger)
12450{
12451 Form_pg_constraint currcon;
12452 Oid conoid;
12453 Relation rel;
12454 bool changed = false;
12455
12456 /* Since this function recurses, it could be driven to stack overflow */
12458
12459 Assert(cmdcon->alterEnforceability);
12460
12461 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12462 conoid = currcon->oid;
12463
12464 /* Should be foreign key constraint */
12465 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12466
12467 rel = table_open(currcon->conrelid, lockmode);
12468
12469 if (currcon->conenforced != cmdcon->is_enforced)
12470 {
12471 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12472 changed = true;
12473 }
12474
12475 /* Drop triggers */
12476 if (!cmdcon->is_enforced)
12477 {
12478 /*
12479 * When setting a constraint to NOT ENFORCED, the constraint triggers
12480 * need to be dropped. Therefore, we must process the child relations
12481 * first, followed by the parent, to account for dependencies.
12482 */
12483 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12484 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12485 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12486 fkrelid, pkrelid, contuple,
12487 lockmode, InvalidOid, InvalidOid,
12489
12490 /* Drop all the triggers */
12492 }
12493 else if (changed) /* Create triggers */
12494 {
12495 Oid ReferencedDelTriggerOid = InvalidOid,
12496 ReferencedUpdTriggerOid = InvalidOid,
12497 ReferencingInsTriggerOid = InvalidOid,
12498 ReferencingUpdTriggerOid = InvalidOid;
12499
12500 /* Prepare the minimal information required for trigger creation. */
12501 Constraint *fkconstraint = makeNode(Constraint);
12502
12503 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12504 fkconstraint->fk_matchtype = currcon->confmatchtype;
12505 fkconstraint->fk_upd_action = currcon->confupdtype;
12506 fkconstraint->fk_del_action = currcon->confdeltype;
12507
12508 /* Create referenced triggers */
12509 if (currcon->conrelid == fkrelid)
12510 createForeignKeyActionTriggers(currcon->conrelid,
12511 currcon->confrelid,
12512 fkconstraint,
12513 conoid,
12514 currcon->conindid,
12515 ReferencedParentDelTrigger,
12516 ReferencedParentUpdTrigger,
12517 &ReferencedDelTriggerOid,
12518 &ReferencedUpdTriggerOid);
12519
12520 /* Create referencing triggers */
12521 if (currcon->confrelid == pkrelid)
12522 createForeignKeyCheckTriggers(currcon->conrelid,
12523 pkrelid,
12524 fkconstraint,
12525 conoid,
12526 currcon->conindid,
12527 ReferencingParentInsTrigger,
12528 ReferencingParentUpdTrigger,
12529 &ReferencingInsTriggerOid,
12530 &ReferencingUpdTriggerOid);
12531
12532 /*
12533 * Tell Phase 3 to check that the constraint is satisfied by existing
12534 * rows. Only applies to leaf partitions, and (for constraints that
12535 * reference a partitioned table) only if this is not one of the
12536 * pg_constraint rows that exist solely to support action triggers.
12537 */
12538 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12539 currcon->confrelid == pkrelid)
12540 {
12541 AlteredTableInfo *tab;
12542 NewConstraint *newcon;
12543
12544 newcon = palloc0_object(NewConstraint);
12545 newcon->name = fkconstraint->conname;
12546 newcon->contype = CONSTR_FOREIGN;
12547 newcon->refrelid = currcon->confrelid;
12548 newcon->refindid = currcon->conindid;
12549 newcon->conid = currcon->oid;
12550 newcon->qual = (Node *) fkconstraint;
12551
12552 /* Find or create work queue entry for this table */
12553 tab = ATGetQueueEntry(wqueue, rel);
12554 tab->constraints = lappend(tab->constraints, newcon);
12555 }
12556
12557 /*
12558 * If the table at either end of the constraint is partitioned, we
12559 * need to recurse and create triggers for each constraint that is a
12560 * child of this one.
12561 */
12562 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12563 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12564 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12565 fkrelid, pkrelid, contuple,
12566 lockmode, ReferencedDelTriggerOid,
12567 ReferencedUpdTriggerOid,
12568 ReferencingInsTriggerOid,
12569 ReferencingUpdTriggerOid);
12570 }
12571
12572 table_close(rel, NoLock);
12573
12574 return changed;
12575}
12576
12577/*
12578 * Returns true if the constraint's deferrability is altered.
12579 *
12580 * *otherrelids is appended OIDs of relations containing affected triggers.
12581 *
12582 * Note that we must recurse even when the values are correct, in case
12583 * indirect descendants have had their constraints altered locally.
12584 * (This could be avoided if we forbade altering constraints in partitions
12585 * but existing releases don't do that.)
12586 */
12587static bool
12589 Relation conrel, Relation tgrel, Relation rel,
12590 HeapTuple contuple, bool recurse,
12591 List **otherrelids, LOCKMODE lockmode)
12592{
12593 Form_pg_constraint currcon;
12594 Oid refrelid;
12595 bool changed = false;
12596
12597 /* since this function recurses, it could be driven to stack overflow */
12599
12600 Assert(cmdcon->alterDeferrability);
12601
12602 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12603 refrelid = currcon->confrelid;
12604
12605 /* Should be foreign key constraint */
12606 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12607
12608 /*
12609 * If called to modify a constraint that's already in the desired state,
12610 * silently do nothing.
12611 */
12612 if (currcon->condeferrable != cmdcon->deferrable ||
12613 currcon->condeferred != cmdcon->initdeferred)
12614 {
12615 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12616 changed = true;
12617
12618 /*
12619 * Now we need to update the multiple entries in pg_trigger that
12620 * implement the constraint.
12621 */
12622 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12623 cmdcon->deferrable,
12624 cmdcon->initdeferred, otherrelids);
12625 }
12626
12627 /*
12628 * If the table at either end of the constraint is partitioned, we need to
12629 * handle every constraint that is a child of this one.
12630 */
12631 if (recurse && changed &&
12632 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12633 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12634 AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12635 contuple, recurse, otherrelids,
12636 lockmode);
12637
12638 return changed;
12639}
12640
12641/*
12642 * Returns true if the constraint's inheritability is altered.
12643 */
12644static bool
12646 Relation conrel, Relation rel,
12647 HeapTuple contuple, LOCKMODE lockmode)
12648{
12649 Form_pg_constraint currcon;
12650 AttrNumber colNum;
12651 char *colName;
12652 List *children;
12653
12654 Assert(cmdcon->alterInheritability);
12655
12656 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12657
12658 /* The current implementation only works for NOT NULL constraints */
12659 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12660
12661 /*
12662 * If called to modify a constraint that's already in the desired state,
12663 * silently do nothing.
12664 */
12665 if (cmdcon->noinherit == currcon->connoinherit)
12666 return false;
12667
12668 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12670
12671 /* Fetch the column number and name */
12672 colNum = extractNotNullColumn(contuple);
12673 colName = get_attname(currcon->conrelid, colNum, false);
12674
12675 /*
12676 * Propagate the change to children. For this subcommand type we don't
12677 * recursively affect children, just the immediate level.
12678 */
12680 lockmode);
12681 foreach_oid(childoid, children)
12682 {
12683 ObjectAddress addr;
12684
12685 if (cmdcon->noinherit)
12686 {
12687 HeapTuple childtup;
12688 Form_pg_constraint childcon;
12689
12690 childtup = findNotNullConstraint(childoid, colName);
12691 if (!childtup)
12692 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12693 colName, childoid);
12694 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12695 Assert(childcon->coninhcount > 0);
12696 childcon->coninhcount--;
12697 childcon->conislocal = true;
12698 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12699 heap_freetuple(childtup);
12700 }
12701 else
12702 {
12703 Relation childrel = table_open(childoid, NoLock);
12704
12705 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12706 colName, true, true, lockmode);
12707 if (OidIsValid(addr.objectId))
12709 table_close(childrel, NoLock);
12710 }
12711 }
12712
12713 return true;
12714}
12715
12716/*
12717 * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12718 * trigger's deferrability.
12719 *
12720 * The arguments to this function have the same meaning as the arguments to
12721 * ATExecAlterConstrDeferrability.
12722 */
12723static void
12725 bool deferrable, bool initdeferred,
12726 List **otherrelids)
12727{
12728 HeapTuple tgtuple;
12729 ScanKeyData tgkey;
12730 SysScanDesc tgscan;
12731
12732 ScanKeyInit(&tgkey,
12733 Anum_pg_trigger_tgconstraint,
12734 BTEqualStrategyNumber, F_OIDEQ,
12735 ObjectIdGetDatum(conoid));
12736 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12737 NULL, 1, &tgkey);
12738 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12739 {
12740 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12741 Form_pg_trigger copy_tg;
12742 HeapTuple tgCopyTuple;
12743
12744 /*
12745 * Remember OIDs of other relation(s) involved in FK constraint.
12746 * (Note: it's likely that we could skip forcing a relcache inval for
12747 * other rels that don't have a trigger whose properties change, but
12748 * let's be conservative.)
12749 */
12750 if (tgform->tgrelid != RelationGetRelid(rel))
12751 *otherrelids = list_append_unique_oid(*otherrelids,
12752 tgform->tgrelid);
12753
12754 /*
12755 * Update enable status and deferrability of RI_FKey_noaction_del,
12756 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12757 * triggers, but not others; see createForeignKeyActionTriggers and
12758 * CreateFKCheckTrigger.
12759 */
12760 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12761 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12762 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12763 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12764 continue;
12765
12766 tgCopyTuple = heap_copytuple(tgtuple);
12767 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12768
12769 copy_tg->tgdeferrable = deferrable;
12770 copy_tg->tginitdeferred = initdeferred;
12771 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12772
12773 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12774
12775 heap_freetuple(tgCopyTuple);
12776 }
12777
12778 systable_endscan(tgscan);
12779}
12780
12781/*
12782 * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12783 * the specified constraint.
12784 *
12785 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12786 * list of child relations and recursing; instead it uses the conparentid
12787 * relationships. This may need to be reconsidered.
12788 *
12789 * The arguments to this function have the same meaning as the arguments to
12790 * ATExecAlterConstrEnforceability.
12791 */
12792static void
12794 Relation conrel, Relation tgrel,
12795 Oid fkrelid, Oid pkrelid,
12796 HeapTuple contuple, LOCKMODE lockmode,
12797 Oid ReferencedParentDelTrigger,
12798 Oid ReferencedParentUpdTrigger,
12799 Oid ReferencingParentInsTrigger,
12800 Oid ReferencingParentUpdTrigger)
12801{
12802 Form_pg_constraint currcon;
12803 Oid conoid;
12804 ScanKeyData pkey;
12805 SysScanDesc pscan;
12806 HeapTuple childtup;
12807
12808 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12809 conoid = currcon->oid;
12810
12811 ScanKeyInit(&pkey,
12812 Anum_pg_constraint_conparentid,
12813 BTEqualStrategyNumber, F_OIDEQ,
12814 ObjectIdGetDatum(conoid));
12815
12816 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12817 true, NULL, 1, &pkey);
12818
12819 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12820 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12821 pkrelid, childtup, lockmode,
12822 ReferencedParentDelTrigger,
12823 ReferencedParentUpdTrigger,
12824 ReferencingParentInsTrigger,
12825 ReferencingParentUpdTrigger);
12826
12827 systable_endscan(pscan);
12828}
12829
12830/*
12831 * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12832 * the specified constraint.
12833 *
12834 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12835 * list of child relations and recursing; instead it uses the conparentid
12836 * relationships. This may need to be reconsidered.
12837 *
12838 * The arguments to this function have the same meaning as the arguments to
12839 * ATExecAlterConstrDeferrability.
12840 */
12841static void
12843 Relation conrel, Relation tgrel, Relation rel,
12844 HeapTuple contuple, bool recurse,
12845 List **otherrelids, LOCKMODE lockmode)
12846{
12847 Form_pg_constraint currcon;
12848 Oid conoid;
12849 ScanKeyData pkey;
12850 SysScanDesc pscan;
12851 HeapTuple childtup;
12852
12853 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12854 conoid = currcon->oid;
12855
12856 ScanKeyInit(&pkey,
12857 Anum_pg_constraint_conparentid,
12858 BTEqualStrategyNumber, F_OIDEQ,
12859 ObjectIdGetDatum(conoid));
12860
12861 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12862 true, NULL, 1, &pkey);
12863
12864 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12865 {
12866 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12867 Relation childrel;
12868
12869 childrel = table_open(childcon->conrelid, lockmode);
12870
12871 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12872 childtup, recurse, otherrelids, lockmode);
12873 table_close(childrel, NoLock);
12874 }
12875
12876 systable_endscan(pscan);
12877}
12878
12879/*
12880 * Update the constraint entry for the given ATAlterConstraint command, and
12881 * invoke the appropriate hooks.
12882 */
12883static void
12885 HeapTuple contuple)
12886{
12887 HeapTuple copyTuple;
12888 Form_pg_constraint copy_con;
12889
12890 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12891 cmdcon->alterInheritability);
12892
12893 copyTuple = heap_copytuple(contuple);
12894 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12895
12896 if (cmdcon->alterEnforceability)
12897 {
12898 copy_con->conenforced = cmdcon->is_enforced;
12899
12900 /*
12901 * NB: The convalidated status is irrelevant when the constraint is
12902 * set to NOT ENFORCED, but for consistency, it should still be set
12903 * appropriately. Similarly, if the constraint is later changed to
12904 * ENFORCED, validation will be performed during phase 3, so it makes
12905 * sense to mark it as valid in that case.
12906 */
12907 copy_con->convalidated = cmdcon->is_enforced;
12908 }
12909 if (cmdcon->alterDeferrability)
12910 {
12911 copy_con->condeferrable = cmdcon->deferrable;
12912 copy_con->condeferred = cmdcon->initdeferred;
12913 }
12914 if (cmdcon->alterInheritability)
12915 copy_con->connoinherit = cmdcon->noinherit;
12916
12917 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12918 InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12919
12920 /* Make new constraint flags visible to others */
12921 CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12922
12923 heap_freetuple(copyTuple);
12924}
12925
12926/*
12927 * ALTER TABLE VALIDATE CONSTRAINT
12928 *
12929 * XXX The reason we handle recursion here rather than at Phase 1 is because
12930 * there's no good way to skip recursing when handling foreign keys: there is
12931 * no need to lock children in that case, yet we wouldn't be able to avoid
12932 * doing so at that level.
12933 *
12934 * Return value is the address of the validated constraint. If the constraint
12935 * was already validated, InvalidObjectAddress is returned.
12936 */
12937static ObjectAddress
12938ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12939 bool recurse, bool recursing, LOCKMODE lockmode)
12940{
12941 Relation conrel;
12942 SysScanDesc scan;
12943 ScanKeyData skey[3];
12944 HeapTuple tuple;
12946 ObjectAddress address;
12947
12948 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12949
12950 /*
12951 * Find and check the target constraint
12952 */
12953 ScanKeyInit(&skey[0],
12954 Anum_pg_constraint_conrelid,
12955 BTEqualStrategyNumber, F_OIDEQ,
12957 ScanKeyInit(&skey[1],
12958 Anum_pg_constraint_contypid,
12959 BTEqualStrategyNumber, F_OIDEQ,
12961 ScanKeyInit(&skey[2],
12962 Anum_pg_constraint_conname,
12963 BTEqualStrategyNumber, F_NAMEEQ,
12964 CStringGetDatum(constrName));
12965 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12966 true, NULL, 3, skey);
12967
12968 /* There can be at most one matching row */
12969 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12970 ereport(ERROR,
12971 (errcode(ERRCODE_UNDEFINED_OBJECT),
12972 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12973 constrName, RelationGetRelationName(rel))));
12974
12975 con = (Form_pg_constraint) GETSTRUCT(tuple);
12976 if (con->contype != CONSTRAINT_FOREIGN &&
12977 con->contype != CONSTRAINT_CHECK &&
12978 con->contype != CONSTRAINT_NOTNULL)
12979 ereport(ERROR,
12980 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12981 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12982 constrName, RelationGetRelationName(rel)),
12983 errdetail("This operation is not supported for this type of constraint."));
12984
12985 if (!con->conenforced)
12986 ereport(ERROR,
12987 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12988 errmsg("cannot validate NOT ENFORCED constraint")));
12989
12990 if (!con->convalidated)
12991 {
12992 if (con->contype == CONSTRAINT_FOREIGN)
12993 {
12994 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12995 tuple, lockmode);
12996 }
12997 else if (con->contype == CONSTRAINT_CHECK)
12998 {
12999 QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
13000 tuple, recurse, recursing, lockmode);
13001 }
13002 else if (con->contype == CONSTRAINT_NOTNULL)
13003 {
13004 QueueNNConstraintValidation(wqueue, conrel, rel,
13005 tuple, recurse, recursing, lockmode);
13006 }
13007
13008 ObjectAddressSet(address, ConstraintRelationId, con->oid);
13009 }
13010 else
13011 address = InvalidObjectAddress; /* already validated */
13012
13013 systable_endscan(scan);
13014
13016
13017 return address;
13018}
13019
13020/*
13021 * QueueFKConstraintValidation
13022 *
13023 * Add an entry to the wqueue to validate the given foreign key constraint in
13024 * Phase 3 and update the convalidated field in the pg_constraint catalog
13025 * for the specified relation and all its children.
13026 */
13027static void
13029 Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
13030{
13032 AlteredTableInfo *tab;
13033 HeapTuple copyTuple;
13034 Form_pg_constraint copy_con;
13035
13036 con = (Form_pg_constraint) GETSTRUCT(contuple);
13037 Assert(con->contype == CONSTRAINT_FOREIGN);
13038 Assert(!con->convalidated);
13039
13040 /*
13041 * Add the validation to phase 3's queue; not needed for partitioned
13042 * tables themselves, only for their partitions.
13043 *
13044 * When the referenced table (pkrelid) is partitioned, the referencing
13045 * table (fkrel) has one pg_constraint row pointing to each partition
13046 * thereof. These rows are there only to support action triggers and no
13047 * table scan is needed, therefore skip this for them as well.
13048 */
13049 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13050 con->confrelid == pkrelid)
13051 {
13052 NewConstraint *newcon;
13053 Constraint *fkconstraint;
13054
13055 /* Queue validation for phase 3 */
13056 fkconstraint = makeNode(Constraint);
13057 /* for now this is all we need */
13058 fkconstraint->conname = pstrdup(NameStr(con->conname));
13059
13060 newcon = palloc0_object(NewConstraint);
13061 newcon->name = fkconstraint->conname;
13062 newcon->contype = CONSTR_FOREIGN;
13063 newcon->refrelid = con->confrelid;
13064 newcon->refindid = con->conindid;
13065 newcon->conid = con->oid;
13066 newcon->qual = (Node *) fkconstraint;
13067
13068 /* Find or create work queue entry for this table */
13069 tab = ATGetQueueEntry(wqueue, fkrel);
13070 tab->constraints = lappend(tab->constraints, newcon);
13071 }
13072
13073 /*
13074 * If the table at either end of the constraint is partitioned, we need to
13075 * recurse and handle every unvalidated constraint that is a child of this
13076 * constraint.
13077 */
13078 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13079 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13080 {
13081 ScanKeyData pkey;
13082 SysScanDesc pscan;
13083 HeapTuple childtup;
13084
13085 ScanKeyInit(&pkey,
13086 Anum_pg_constraint_conparentid,
13087 BTEqualStrategyNumber, F_OIDEQ,
13088 ObjectIdGetDatum(con->oid));
13089
13090 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13091 true, NULL, 1, &pkey);
13092
13093 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13094 {
13095 Form_pg_constraint childcon;
13096 Relation childrel;
13097
13098 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13099
13100 /*
13101 * If the child constraint has already been validated, no further
13102 * action is required for it or its descendants, as they are all
13103 * valid.
13104 */
13105 if (childcon->convalidated)
13106 continue;
13107
13108 childrel = table_open(childcon->conrelid, lockmode);
13109
13110 /*
13111 * NB: Note that pkrelid should be passed as-is during recursion,
13112 * as it is required to identify the root referenced table.
13113 */
13114 QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13115 childtup, lockmode);
13116 table_close(childrel, NoLock);
13117 }
13118
13119 systable_endscan(pscan);
13120 }
13121
13122 /*
13123 * Now mark the pg_constraint row as validated (even if we didn't check,
13124 * notably the ones for partitions on the referenced side).
13125 *
13126 * We rely on transaction abort to roll back this change if phase 3
13127 * ultimately finds violating rows. This is a bit ugly.
13128 */
13129 copyTuple = heap_copytuple(contuple);
13130 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13131 copy_con->convalidated = true;
13132 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13133
13134 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13135
13136 heap_freetuple(copyTuple);
13137}
13138
13139/*
13140 * QueueCheckConstraintValidation
13141 *
13142 * Add an entry to the wqueue to validate the given check constraint in Phase 3
13143 * and update the convalidated field in the pg_constraint catalog for the
13144 * specified relation and all its inheriting children.
13145 */
13146static void
13148 char *constrName, HeapTuple contuple,
13149 bool recurse, bool recursing, LOCKMODE lockmode)
13150{
13152 AlteredTableInfo *tab;
13153 HeapTuple copyTuple;
13154 Form_pg_constraint copy_con;
13155
13156 List *children = NIL;
13157 ListCell *child;
13158 NewConstraint *newcon;
13159 Datum val;
13160 char *conbin;
13161
13162 con = (Form_pg_constraint) GETSTRUCT(contuple);
13163 Assert(con->contype == CONSTRAINT_CHECK);
13164
13165 /*
13166 * If we're recursing, the parent has already done this, so skip it. Also,
13167 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13168 * for it in the children.
13169 */
13170 if (!recursing && !con->connoinherit)
13171 children = find_all_inheritors(RelationGetRelid(rel),
13172 lockmode, NULL);
13173
13174 /*
13175 * For CHECK constraints, we must ensure that we only mark the constraint
13176 * as validated on the parent if it's already validated on the children.
13177 *
13178 * We recurse before validating on the parent, to reduce risk of
13179 * deadlocks.
13180 */
13181 foreach(child, children)
13182 {
13183 Oid childoid = lfirst_oid(child);
13184 Relation childrel;
13185
13186 if (childoid == RelationGetRelid(rel))
13187 continue;
13188
13189 /*
13190 * If we are told not to recurse, there had better not be any child
13191 * tables, because we can't mark the constraint on the parent valid
13192 * unless it is valid for all child tables.
13193 */
13194 if (!recurse)
13195 ereport(ERROR,
13196 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13197 errmsg("constraint must be validated on child tables too")));
13198
13199 /* find_all_inheritors already got lock */
13200 childrel = table_open(childoid, NoLock);
13201
13202 ATExecValidateConstraint(wqueue, childrel, constrName, false,
13203 true, lockmode);
13204 table_close(childrel, NoLock);
13205 }
13206
13207 /* Queue validation for phase 3 */
13208 newcon = palloc0_object(NewConstraint);
13209 newcon->name = constrName;
13210 newcon->contype = CONSTR_CHECK;
13211 newcon->refrelid = InvalidOid;
13212 newcon->refindid = InvalidOid;
13213 newcon->conid = con->oid;
13214
13215 val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13216 Anum_pg_constraint_conbin);
13217 conbin = TextDatumGetCString(val);
13218 newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13219
13220 /* Find or create work queue entry for this table */
13221 tab = ATGetQueueEntry(wqueue, rel);
13222 tab->constraints = lappend(tab->constraints, newcon);
13223
13224 /*
13225 * Invalidate relcache so that others see the new validated constraint.
13226 */
13228
13229 /*
13230 * Now update the catalog, while we have the door open.
13231 */
13232 copyTuple = heap_copytuple(contuple);
13233 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13234 copy_con->convalidated = true;
13235 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13236
13237 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13238
13239 heap_freetuple(copyTuple);
13240}
13241
13242/*
13243 * QueueNNConstraintValidation
13244 *
13245 * Add an entry to the wqueue to validate the given not-null constraint in
13246 * Phase 3 and update the convalidated field in the pg_constraint catalog for
13247 * the specified relation and all its inheriting children.
13248 */
13249static void
13251 HeapTuple contuple, bool recurse, bool recursing,
13252 LOCKMODE lockmode)
13253{
13255 AlteredTableInfo *tab;
13256 HeapTuple copyTuple;
13257 Form_pg_constraint copy_con;
13258 List *children = NIL;
13260 char *colname;
13261
13262 con = (Form_pg_constraint) GETSTRUCT(contuple);
13263 Assert(con->contype == CONSTRAINT_NOTNULL);
13264
13265 attnum = extractNotNullColumn(contuple);
13266
13267 /*
13268 * If we're recursing, we've already done this for parent, so skip it.
13269 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13270 * look for it in the children.
13271 *
13272 * We recurse before validating on the parent, to reduce risk of
13273 * deadlocks.
13274 */
13275 if (!recursing && !con->connoinherit)
13276 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13277
13278 colname = get_attname(RelationGetRelid(rel), attnum, false);
13279 foreach_oid(childoid, children)
13280 {
13281 Relation childrel;
13282 HeapTuple contup;
13283 Form_pg_constraint childcon;
13284 char *conname;
13285
13286 if (childoid == RelationGetRelid(rel))
13287 continue;
13288
13289 /*
13290 * If we are told not to recurse, there had better not be any child
13291 * tables, because we can't mark the constraint on the parent valid
13292 * unless it is valid for all child tables.
13293 */
13294 if (!recurse)
13295 ereport(ERROR,
13296 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13297 errmsg("constraint must be validated on child tables too"));
13298
13299 /*
13300 * The column on child might have a different attnum, so search by
13301 * column name.
13302 */
13303 contup = findNotNullConstraint(childoid, colname);
13304 if (!contup)
13305 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13306 colname, get_rel_name(childoid));
13307 childcon = (Form_pg_constraint) GETSTRUCT(contup);
13308 if (childcon->convalidated)
13309 continue;
13310
13311 /* find_all_inheritors already got lock */
13312 childrel = table_open(childoid, NoLock);
13313 conname = pstrdup(NameStr(childcon->conname));
13314
13315 /* XXX improve ATExecValidateConstraint API to avoid double search */
13316 ATExecValidateConstraint(wqueue, childrel, conname,
13317 false, true, lockmode);
13318 table_close(childrel, NoLock);
13319 }
13320
13321 /* Set attnotnull appropriately without queueing another validation */
13322 set_attnotnull(NULL, rel, attnum, true, false);
13323
13324 tab = ATGetQueueEntry(wqueue, rel);
13325 tab->verify_new_notnull = true;
13326
13327 /*
13328 * Invalidate relcache so that others see the new validated constraint.
13329 */
13331
13332 /*
13333 * Now update the catalogs, while we have the door open.
13334 */
13335 copyTuple = heap_copytuple(contuple);
13336 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13337 copy_con->convalidated = true;
13338 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13339
13340 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13341
13342 heap_freetuple(copyTuple);
13343}
13344
13345/*
13346 * transformColumnNameList - transform list of column names
13347 *
13348 * Lookup each name and return its attnum and, optionally, type and collation
13349 * OIDs
13350 *
13351 * Note: the name of this function suggests that it's general-purpose,
13352 * but actually it's only used to look up names appearing in foreign-key
13353 * clauses. The error messages would need work to use it in other cases,
13354 * and perhaps the validity checks as well.
13355 */
13356static int
13358 int16 *attnums, Oid *atttypids, Oid *attcollids)
13359{
13360 ListCell *l;
13361 int attnum;
13362
13363 attnum = 0;
13364 foreach(l, colList)
13365 {
13366 char *attname = strVal(lfirst(l));
13367 HeapTuple atttuple;
13368 Form_pg_attribute attform;
13369
13370 atttuple = SearchSysCacheAttName(relId, attname);
13371 if (!HeapTupleIsValid(atttuple))
13372 ereport(ERROR,
13373 (errcode(ERRCODE_UNDEFINED_COLUMN),
13374 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13375 attname)));
13376 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13377 if (attform->attnum < 0)
13378 ereport(ERROR,
13379 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13380 errmsg("system columns cannot be used in foreign keys")));
13381 if (attnum >= INDEX_MAX_KEYS)
13382 ereport(ERROR,
13383 (errcode(ERRCODE_TOO_MANY_COLUMNS),
13384 errmsg("cannot have more than %d keys in a foreign key",
13385 INDEX_MAX_KEYS)));
13386 attnums[attnum] = attform->attnum;
13387 if (atttypids != NULL)
13388 atttypids[attnum] = attform->atttypid;
13389 if (attcollids != NULL)
13390 attcollids[attnum] = attform->attcollation;
13391 ReleaseSysCache(atttuple);
13392 attnum++;
13393 }
13394
13395 return attnum;
13396}
13397
13398/*
13399 * transformFkeyGetPrimaryKey -
13400 *
13401 * Look up the names, attnums, types, and collations of the primary key attributes
13402 * for the pkrel. Also return the index OID and index opclasses of the
13403 * index supporting the primary key. Also return whether the index has
13404 * WITHOUT OVERLAPS.
13405 *
13406 * All parameters except pkrel are output parameters. Also, the function
13407 * return value is the number of attributes in the primary key.
13408 *
13409 * Used when the column list in the REFERENCES specification is omitted.
13410 */
13411static int
13413 List **attnamelist,
13414 int16 *attnums, Oid *atttypids, Oid *attcollids,
13415 Oid *opclasses, bool *pk_has_without_overlaps)
13416{
13417 List *indexoidlist;
13418 ListCell *indexoidscan;
13419 HeapTuple indexTuple = NULL;
13420 Form_pg_index indexStruct = NULL;
13421 Datum indclassDatum;
13422 oidvector *indclass;
13423 int i;
13424
13425 /*
13426 * Get the list of index OIDs for the table from the relcache, and look up
13427 * each one in the pg_index syscache until we find one marked primary key
13428 * (hopefully there isn't more than one such). Insist it's valid, too.
13429 */
13430 *indexOid = InvalidOid;
13431
13432 indexoidlist = RelationGetIndexList(pkrel);
13433
13434 foreach(indexoidscan, indexoidlist)
13435 {
13436 Oid indexoid = lfirst_oid(indexoidscan);
13437
13438 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13439 if (!HeapTupleIsValid(indexTuple))
13440 elog(ERROR, "cache lookup failed for index %u", indexoid);
13441 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13442 if (indexStruct->indisprimary && indexStruct->indisvalid)
13443 {
13444 /*
13445 * Refuse to use a deferrable primary key. This is per SQL spec,
13446 * and there would be a lot of interesting semantic problems if we
13447 * tried to allow it.
13448 */
13449 if (!indexStruct->indimmediate)
13450 ereport(ERROR,
13451 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13452 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13453 RelationGetRelationName(pkrel))));
13454
13455 *indexOid = indexoid;
13456 break;
13457 }
13458 ReleaseSysCache(indexTuple);
13459 }
13460
13461 list_free(indexoidlist);
13462
13463 /*
13464 * Check that we found it
13465 */
13466 if (!OidIsValid(*indexOid))
13467 ereport(ERROR,
13468 (errcode(ERRCODE_UNDEFINED_OBJECT),
13469 errmsg("there is no primary key for referenced table \"%s\"",
13470 RelationGetRelationName(pkrel))));
13471
13472 /* Must get indclass the hard way */
13473 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13474 Anum_pg_index_indclass);
13475 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13476
13477 /*
13478 * Now build the list of PK attributes from the indkey definition (we
13479 * assume a primary key cannot have expressional elements)
13480 */
13481 *attnamelist = NIL;
13482 for (i = 0; i < indexStruct->indnkeyatts; i++)
13483 {
13484 int pkattno = indexStruct->indkey.values[i];
13485
13486 attnums[i] = pkattno;
13487 atttypids[i] = attnumTypeId(pkrel, pkattno);
13488 attcollids[i] = attnumCollationId(pkrel, pkattno);
13489 opclasses[i] = indclass->values[i];
13490 *attnamelist = lappend(*attnamelist,
13491 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13492 }
13493
13494 *pk_has_without_overlaps = indexStruct->indisexclusion;
13495
13496 ReleaseSysCache(indexTuple);
13497
13498 return i;
13499}
13500
13501/*
13502 * transformFkeyCheckAttrs -
13503 *
13504 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13505 * reference as part of a foreign key constraint.
13506 *
13507 * Returns the OID of the unique index supporting the constraint and
13508 * populates the caller-provided 'opclasses' array with the opclasses
13509 * associated with the index columns. Also sets whether the index
13510 * uses WITHOUT OVERLAPS.
13511 *
13512 * Raises an ERROR on validation failure.
13513 */
13514static Oid
13516 int numattrs, int16 *attnums,
13517 bool with_period, Oid *opclasses,
13518 bool *pk_has_without_overlaps)
13519{
13520 Oid indexoid = InvalidOid;
13521 bool found = false;
13522 bool found_deferrable = false;
13523 List *indexoidlist;
13524 ListCell *indexoidscan;
13525 int i,
13526 j;
13527
13528 /*
13529 * Reject duplicate appearances of columns in the referenced-columns list.
13530 * Such a case is forbidden by the SQL standard, and even if we thought it
13531 * useful to allow it, there would be ambiguity about how to match the
13532 * list to unique indexes (in particular, it'd be unclear which index
13533 * opclass goes with which FK column).
13534 */
13535 for (i = 0; i < numattrs; i++)
13536 {
13537 for (j = i + 1; j < numattrs; j++)
13538 {
13539 if (attnums[i] == attnums[j])
13540 ereport(ERROR,
13541 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13542 errmsg("foreign key referenced-columns list must not contain duplicates")));
13543 }
13544 }
13545
13546 /*
13547 * Get the list of index OIDs for the table from the relcache, and look up
13548 * each one in the pg_index syscache, and match unique indexes to the list
13549 * of attnums we are given.
13550 */
13551 indexoidlist = RelationGetIndexList(pkrel);
13552
13553 foreach(indexoidscan, indexoidlist)
13554 {
13555 HeapTuple indexTuple;
13556 Form_pg_index indexStruct;
13557
13558 indexoid = lfirst_oid(indexoidscan);
13559 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13560 if (!HeapTupleIsValid(indexTuple))
13561 elog(ERROR, "cache lookup failed for index %u", indexoid);
13562 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13563
13564 /*
13565 * Must have the right number of columns; must be unique (or if
13566 * temporal then exclusion instead) and not a partial index; forget it
13567 * if there are any expressions, too. Invalid indexes are out as well.
13568 */
13569 if (indexStruct->indnkeyatts == numattrs &&
13570 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13571 indexStruct->indisvalid &&
13572 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13573 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13574 {
13575 Datum indclassDatum;
13576 oidvector *indclass;
13577
13578 /* Must get indclass the hard way */
13579 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13580 Anum_pg_index_indclass);
13581 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13582
13583 /*
13584 * The given attnum list may match the index columns in any order.
13585 * Check for a match, and extract the appropriate opclasses while
13586 * we're at it.
13587 *
13588 * We know that attnums[] is duplicate-free per the test at the
13589 * start of this function, and we checked above that the number of
13590 * index columns agrees, so if we find a match for each attnums[]
13591 * entry then we must have a one-to-one match in some order.
13592 */
13593 for (i = 0; i < numattrs; i++)
13594 {
13595 found = false;
13596 for (j = 0; j < numattrs; j++)
13597 {
13598 if (attnums[i] == indexStruct->indkey.values[j])
13599 {
13600 opclasses[i] = indclass->values[j];
13601 found = true;
13602 break;
13603 }
13604 }
13605 if (!found)
13606 break;
13607 }
13608 /* The last attribute in the index must be the PERIOD FK part */
13609 if (found && with_period)
13610 {
13611 int16 periodattnum = attnums[numattrs - 1];
13612
13613 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13614 }
13615
13616 /*
13617 * Refuse to use a deferrable unique/primary key. This is per SQL
13618 * spec, and there would be a lot of interesting semantic problems
13619 * if we tried to allow it.
13620 */
13621 if (found && !indexStruct->indimmediate)
13622 {
13623 /*
13624 * Remember that we found an otherwise matching index, so that
13625 * we can generate a more appropriate error message.
13626 */
13627 found_deferrable = true;
13628 found = false;
13629 }
13630
13631 /* We need to know whether the index has WITHOUT OVERLAPS */
13632 if (found)
13633 *pk_has_without_overlaps = indexStruct->indisexclusion;
13634 }
13635 ReleaseSysCache(indexTuple);
13636 if (found)
13637 break;
13638 }
13639
13640 if (!found)
13641 {
13642 if (found_deferrable)
13643 ereport(ERROR,
13644 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13645 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13646 RelationGetRelationName(pkrel))));
13647 else
13648 ereport(ERROR,
13649 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13650 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13651 RelationGetRelationName(pkrel))));
13652 }
13653
13654 list_free(indexoidlist);
13655
13656 return indexoid;
13657}
13658
13659/*
13660 * findFkeyCast -
13661 *
13662 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13663 * Caller has equal regard for binary coercibility and for an exact match.
13664*/
13665static CoercionPathType
13666findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13667{
13668 CoercionPathType ret;
13669
13670 if (targetTypeId == sourceTypeId)
13671 {
13673 *funcid = InvalidOid;
13674 }
13675 else
13676 {
13677 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13678 COERCION_IMPLICIT, funcid);
13679 if (ret == COERCION_PATH_NONE)
13680 /* A previously-relied-upon cast is now gone. */
13681 elog(ERROR, "could not find cast from %u to %u",
13682 sourceTypeId, targetTypeId);
13683 }
13684
13685 return ret;
13686}
13687
13688/*
13689 * Permissions checks on the referenced table for ADD FOREIGN KEY
13690 *
13691 * Note: we have already checked that the user owns the referencing table,
13692 * else we'd have failed much earlier; no additional checks are needed for it.
13693 */
13694static void
13695checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13696{
13697 Oid roleid = GetUserId();
13698 AclResult aclresult;
13699 int i;
13700
13701 /* Okay if we have relation-level REFERENCES permission */
13702 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13704 if (aclresult == ACLCHECK_OK)
13705 return;
13706 /* Else we must have REFERENCES on each column */
13707 for (i = 0; i < natts; i++)
13708 {
13709 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13710 roleid, ACL_REFERENCES);
13711 if (aclresult != ACLCHECK_OK)
13712 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13714 }
13715}
13716
13717/*
13718 * Scan the existing rows in a table to verify they meet a proposed FK
13719 * constraint.
13720 *
13721 * Caller must have opened and locked both relations appropriately.
13722 */
13723static void
13725 Relation rel,
13726 Relation pkrel,
13727 Oid pkindOid,
13728 Oid constraintOid,
13729 bool hasperiod)
13730{
13731 TupleTableSlot *slot;
13732 TableScanDesc scan;
13733 Trigger trig = {0};
13734 Snapshot snapshot;
13735 MemoryContext oldcxt;
13736 MemoryContext perTupCxt;
13737
13739 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13740
13741 /*
13742 * Build a trigger call structure; we'll need it either way.
13743 */
13744 trig.tgoid = InvalidOid;
13745 trig.tgname = conname;
13747 trig.tgisinternal = true;
13748 trig.tgconstrrelid = RelationGetRelid(pkrel);
13749 trig.tgconstrindid = pkindOid;
13750 trig.tgconstraint = constraintOid;
13751 trig.tgdeferrable = false;
13752 trig.tginitdeferred = false;
13753 /* we needn't fill in remaining fields */
13754
13755 /*
13756 * See if we can do it with a single LEFT JOIN query. A false result
13757 * indicates we must proceed with the fire-the-trigger method. We can't do
13758 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13759 * left joins.
13760 */
13761 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13762 return;
13763
13764 /*
13765 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13766 * if that tuple had just been inserted. If any of those fail, it should
13767 * ereport(ERROR) and that's that.
13768 */
13769 snapshot = RegisterSnapshot(GetLatestSnapshot());
13770 slot = table_slot_create(rel, NULL);
13771 scan = table_beginscan(rel, snapshot, 0, NULL);
13772
13774 "validateForeignKeyConstraint",
13776 oldcxt = MemoryContextSwitchTo(perTupCxt);
13777
13778 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13779 {
13780 LOCAL_FCINFO(fcinfo, 0);
13781 TriggerData trigdata = {0};
13782
13784
13785 /*
13786 * Make a call to the trigger function
13787 *
13788 * No parameters are passed, but we do set a context
13789 */
13790 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13791
13792 /*
13793 * We assume RI_FKey_check_ins won't look at flinfo...
13794 */
13795 trigdata.type = T_TriggerData;
13797 trigdata.tg_relation = rel;
13798 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13799 trigdata.tg_trigslot = slot;
13800 trigdata.tg_trigger = &trig;
13801
13802 fcinfo->context = (Node *) &trigdata;
13803
13804 RI_FKey_check_ins(fcinfo);
13805
13806 MemoryContextReset(perTupCxt);
13807 }
13808
13809 MemoryContextSwitchTo(oldcxt);
13810 MemoryContextDelete(perTupCxt);
13811 table_endscan(scan);
13812 UnregisterSnapshot(snapshot);
13814}
13815
13816/*
13817 * CreateFKCheckTrigger
13818 * Creates the insert (on_insert=true) or update "check" trigger that
13819 * implements a given foreign key
13820 *
13821 * Returns the OID of the so created trigger.
13822 */
13823static Oid
13824CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13825 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13826 bool on_insert)
13827{
13828 ObjectAddress trigAddress;
13829 CreateTrigStmt *fk_trigger;
13830
13831 /*
13832 * Note: for a self-referential FK (referencing and referenced tables are
13833 * the same), it is important that the ON UPDATE action fires before the
13834 * CHECK action, since both triggers will fire on the same row during an
13835 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13836 * state of the row. Triggers fire in name order, so we ensure this by
13837 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13838 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13839 */
13840 fk_trigger = makeNode(CreateTrigStmt);
13841 fk_trigger->replace = false;
13842 fk_trigger->isconstraint = true;
13843 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13844 fk_trigger->relation = NULL;
13845
13846 /* Either ON INSERT or ON UPDATE */
13847 if (on_insert)
13848 {
13849 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13850 fk_trigger->events = TRIGGER_TYPE_INSERT;
13851 }
13852 else
13853 {
13854 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13855 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13856 }
13857
13858 fk_trigger->args = NIL;
13859 fk_trigger->row = true;
13860 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13861 fk_trigger->columns = NIL;
13862 fk_trigger->whenClause = NULL;
13863 fk_trigger->transitionRels = NIL;
13864 fk_trigger->deferrable = fkconstraint->deferrable;
13865 fk_trigger->initdeferred = fkconstraint->initdeferred;
13866 fk_trigger->constrrel = NULL;
13867
13868 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13869 constraintOid, indexOid, InvalidOid,
13870 parentTrigOid, NULL, true, false);
13871
13872 /* Make changes-so-far visible */
13874
13875 return trigAddress.objectId;
13876}
13877
13878/*
13879 * createForeignKeyActionTriggers
13880 * Create the referenced-side "action" triggers that implement a foreign
13881 * key.
13882 *
13883 * Returns the OIDs of the so created triggers in *deleteTrigOid and
13884 * *updateTrigOid.
13885 */
13886static void
13887createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13888 Oid constraintOid, Oid indexOid,
13889 Oid parentDelTrigger, Oid parentUpdTrigger,
13890 Oid *deleteTrigOid, Oid *updateTrigOid)
13891{
13892 CreateTrigStmt *fk_trigger;
13893 ObjectAddress trigAddress;
13894
13895 /*
13896 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13897 * DELETE action on the referenced table.
13898 */
13899 fk_trigger = makeNode(CreateTrigStmt);
13900 fk_trigger->replace = false;
13901 fk_trigger->isconstraint = true;
13902 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13903 fk_trigger->relation = NULL;
13904 fk_trigger->args = NIL;
13905 fk_trigger->row = true;
13906 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13907 fk_trigger->events = TRIGGER_TYPE_DELETE;
13908 fk_trigger->columns = NIL;
13909 fk_trigger->whenClause = NULL;
13910 fk_trigger->transitionRels = NIL;
13911 fk_trigger->constrrel = NULL;
13912
13913 switch (fkconstraint->fk_del_action)
13914 {
13916 fk_trigger->deferrable = fkconstraint->deferrable;
13917 fk_trigger->initdeferred = fkconstraint->initdeferred;
13918 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13919 break;
13921 fk_trigger->deferrable = false;
13922 fk_trigger->initdeferred = false;
13923 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13924 break;
13926 fk_trigger->deferrable = false;
13927 fk_trigger->initdeferred = false;
13928 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13929 break;
13931 fk_trigger->deferrable = false;
13932 fk_trigger->initdeferred = false;
13933 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13934 break;
13936 fk_trigger->deferrable = false;
13937 fk_trigger->initdeferred = false;
13938 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13939 break;
13940 default:
13941 elog(ERROR, "unrecognized FK action type: %d",
13942 (int) fkconstraint->fk_del_action);
13943 break;
13944 }
13945
13946 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13947 constraintOid, indexOid, InvalidOid,
13948 parentDelTrigger, NULL, true, false);
13949 if (deleteTrigOid)
13950 *deleteTrigOid = trigAddress.objectId;
13951
13952 /* Make changes-so-far visible */
13954
13955 /*
13956 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13957 * UPDATE action on the referenced table.
13958 */
13959 fk_trigger = makeNode(CreateTrigStmt);
13960 fk_trigger->replace = false;
13961 fk_trigger->isconstraint = true;
13962 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13963 fk_trigger->relation = NULL;
13964 fk_trigger->args = NIL;
13965 fk_trigger->row = true;
13966 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13967 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13968 fk_trigger->columns = NIL;
13969 fk_trigger->whenClause = NULL;
13970 fk_trigger->transitionRels = NIL;
13971 fk_trigger->constrrel = NULL;
13972
13973 switch (fkconstraint->fk_upd_action)
13974 {
13976 fk_trigger->deferrable = fkconstraint->deferrable;
13977 fk_trigger->initdeferred = fkconstraint->initdeferred;
13978 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13979 break;
13981 fk_trigger->deferrable = false;
13982 fk_trigger->initdeferred = false;
13983 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13984 break;
13986 fk_trigger->deferrable = false;
13987 fk_trigger->initdeferred = false;
13988 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13989 break;
13991 fk_trigger->deferrable = false;
13992 fk_trigger->initdeferred = false;
13993 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13994 break;
13996 fk_trigger->deferrable = false;
13997 fk_trigger->initdeferred = false;
13998 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13999 break;
14000 default:
14001 elog(ERROR, "unrecognized FK action type: %d",
14002 (int) fkconstraint->fk_upd_action);
14003 break;
14004 }
14005
14006 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
14007 constraintOid, indexOid, InvalidOid,
14008 parentUpdTrigger, NULL, true, false);
14009 if (updateTrigOid)
14010 *updateTrigOid = trigAddress.objectId;
14011}
14012
14013/*
14014 * createForeignKeyCheckTriggers
14015 * Create the referencing-side "check" triggers that implement a foreign
14016 * key.
14017 *
14018 * Returns the OIDs of the so created triggers in *insertTrigOid and
14019 * *updateTrigOid.
14020 */
14021static void
14023 Constraint *fkconstraint, Oid constraintOid,
14024 Oid indexOid,
14025 Oid parentInsTrigger, Oid parentUpdTrigger,
14026 Oid *insertTrigOid, Oid *updateTrigOid)
14027{
14028 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14029 constraintOid, indexOid,
14030 parentInsTrigger, true);
14031 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14032 constraintOid, indexOid,
14033 parentUpdTrigger, false);
14034}
14035
14036/*
14037 * ALTER TABLE DROP CONSTRAINT
14038 *
14039 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
14040 */
14041static void
14042ATExecDropConstraint(Relation rel, const char *constrName,
14043 DropBehavior behavior, bool recurse,
14044 bool missing_ok, LOCKMODE lockmode)
14045{
14046 Relation conrel;
14047 SysScanDesc scan;
14048 ScanKeyData skey[3];
14049 HeapTuple tuple;
14050 bool found = false;
14051
14052 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14053
14054 /*
14055 * Find and drop the target constraint
14056 */
14057 ScanKeyInit(&skey[0],
14058 Anum_pg_constraint_conrelid,
14059 BTEqualStrategyNumber, F_OIDEQ,
14061 ScanKeyInit(&skey[1],
14062 Anum_pg_constraint_contypid,
14063 BTEqualStrategyNumber, F_OIDEQ,
14065 ScanKeyInit(&skey[2],
14066 Anum_pg_constraint_conname,
14067 BTEqualStrategyNumber, F_NAMEEQ,
14068 CStringGetDatum(constrName));
14069 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14070 true, NULL, 3, skey);
14071
14072 /* There can be at most one matching row */
14073 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14074 {
14075 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14076 missing_ok, lockmode);
14077 found = true;
14078 }
14079
14080 systable_endscan(scan);
14081
14082 if (!found)
14083 {
14084 if (!missing_ok)
14085 ereport(ERROR,
14086 errcode(ERRCODE_UNDEFINED_OBJECT),
14087 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14088 constrName, RelationGetRelationName(rel)));
14089 else
14091 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14092 constrName, RelationGetRelationName(rel)));
14093 }
14094
14096}
14097
14098/*
14099 * Remove a constraint, using its pg_constraint tuple
14100 *
14101 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14102 * DROP NOT NULL.
14103 *
14104 * Returns the address of the constraint being removed.
14105 */
14106static ObjectAddress
14108 bool recurse, bool recursing, bool missing_ok,
14109 LOCKMODE lockmode)
14110{
14111 Relation conrel;
14113 ObjectAddress conobj;
14114 List *children;
14115 bool is_no_inherit_constraint = false;
14116 char *constrName;
14117 char *colname = NULL;
14118
14119 /* Guard against stack overflow due to overly deep inheritance tree. */
14121
14122 /* At top level, permission check was done in ATPrepCmd, else do it */
14123 if (recursing)
14126
14127 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14128
14129 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14130 constrName = NameStr(con->conname);
14131
14132 /* Don't allow drop of inherited constraints */
14133 if (con->coninhcount > 0 && !recursing)
14134 ereport(ERROR,
14135 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14136 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14137 constrName, RelationGetRelationName(rel))));
14138
14139 /*
14140 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14141 *
14142 * While doing that, we're in a good position to disallow dropping a not-
14143 * null constraint underneath a primary key, a replica identity index, or
14144 * a generated identity column.
14145 */
14146 if (con->contype == CONSTRAINT_NOTNULL)
14147 {
14148 Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14149 AttrNumber attnum = extractNotNullColumn(constraintTup);
14150 Bitmapset *pkattrs;
14151 Bitmapset *irattrs;
14152 HeapTuple atttup;
14153 Form_pg_attribute attForm;
14154
14155 /* save column name for recursion step */
14156 colname = get_attname(RelationGetRelid(rel), attnum, false);
14157
14158 /*
14159 * Disallow if it's in the primary key. For partitioned tables we
14160 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14161 * return NULL if the primary key is invalid; but we still need to
14162 * protect not-null constraints under such a constraint, so check the
14163 * slow way.
14164 */
14166
14167 if (pkattrs == NULL &&
14168 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14169 {
14170 Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14171
14172 if (OidIsValid(pkindex))
14173 {
14174 Relation pk = relation_open(pkindex, AccessShareLock);
14175
14176 pkattrs = NULL;
14177 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14178 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14179
14181 }
14182 }
14183
14184 if (pkattrs &&
14186 ereport(ERROR,
14187 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14188 errmsg("column \"%s\" is in a primary key",
14189 get_attname(RelationGetRelid(rel), attnum, false)));
14190
14191 /* Disallow if it's in the replica identity */
14194 ereport(ERROR,
14195 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14196 errmsg("column \"%s\" is in index used as replica identity",
14197 get_attname(RelationGetRelid(rel), attnum, false)));
14198
14199 /* Disallow if it's a GENERATED AS IDENTITY column */
14201 if (!HeapTupleIsValid(atttup))
14202 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14203 attnum, RelationGetRelid(rel));
14204 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14205 if (attForm->attidentity != '\0')
14206 ereport(ERROR,
14207 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14208 errmsg("column \"%s\" of relation \"%s\" is an identity column",
14210 false),
14212
14213 /* All good -- reset attnotnull if needed */
14214 if (attForm->attnotnull)
14215 {
14216 attForm->attnotnull = false;
14217 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14218 }
14219
14221 }
14222
14223 is_no_inherit_constraint = con->connoinherit;
14224
14225 /*
14226 * If it's a foreign-key constraint, we'd better lock the referenced table
14227 * and check that that's not in use, just as we've already done for the
14228 * constrained table (else we might, eg, be dropping a trigger that has
14229 * unfired events). But we can/must skip that in the self-referential
14230 * case.
14231 */
14232 if (con->contype == CONSTRAINT_FOREIGN &&
14233 con->confrelid != RelationGetRelid(rel))
14234 {
14235 Relation frel;
14236
14237 /* Must match lock taken by RemoveTriggerById: */
14238 frel = table_open(con->confrelid, AccessExclusiveLock);
14240 table_close(frel, NoLock);
14241 }
14242
14243 /*
14244 * Perform the actual constraint deletion
14245 */
14246 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14247 performDeletion(&conobj, behavior, 0);
14248
14249 /*
14250 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14251 * are dropped via the dependency mechanism, so we're done here.
14252 */
14253 if (con->contype != CONSTRAINT_CHECK &&
14254 con->contype != CONSTRAINT_NOTNULL &&
14255 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14256 {
14258 return conobj;
14259 }
14260
14261 /*
14262 * Propagate to children as appropriate. Unlike most other ALTER
14263 * routines, we have to do this one level of recursion at a time; we can't
14264 * use find_all_inheritors to do it in one pass.
14265 */
14266 if (!is_no_inherit_constraint)
14267 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14268 else
14269 children = NIL;
14270
14271 foreach_oid(childrelid, children)
14272 {
14273 Relation childrel;
14274 HeapTuple tuple;
14275 Form_pg_constraint childcon;
14276
14277 /* find_inheritance_children already got lock */
14278 childrel = table_open(childrelid, NoLock);
14279 CheckAlterTableIsSafe(childrel);
14280
14281 /*
14282 * We search for not-null constraints by column name, and others by
14283 * constraint name.
14284 */
14285 if (con->contype == CONSTRAINT_NOTNULL)
14286 {
14287 tuple = findNotNullConstraint(childrelid, colname);
14288 if (!HeapTupleIsValid(tuple))
14289 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14290 colname, RelationGetRelid(childrel));
14291 }
14292 else
14293 {
14294 SysScanDesc scan;
14295 ScanKeyData skey[3];
14296
14297 ScanKeyInit(&skey[0],
14298 Anum_pg_constraint_conrelid,
14299 BTEqualStrategyNumber, F_OIDEQ,
14300 ObjectIdGetDatum(childrelid));
14301 ScanKeyInit(&skey[1],
14302 Anum_pg_constraint_contypid,
14303 BTEqualStrategyNumber, F_OIDEQ,
14305 ScanKeyInit(&skey[2],
14306 Anum_pg_constraint_conname,
14307 BTEqualStrategyNumber, F_NAMEEQ,
14308 CStringGetDatum(constrName));
14309 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14310 true, NULL, 3, skey);
14311 /* There can only be one, so no need to loop */
14312 tuple = systable_getnext(scan);
14313 if (!HeapTupleIsValid(tuple))
14314 ereport(ERROR,
14315 (errcode(ERRCODE_UNDEFINED_OBJECT),
14316 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14317 constrName,
14318 RelationGetRelationName(childrel))));
14319 tuple = heap_copytuple(tuple);
14320 systable_endscan(scan);
14321 }
14322
14323 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14324
14325 /* Right now only CHECK and not-null constraints can be inherited */
14326 if (childcon->contype != CONSTRAINT_CHECK &&
14327 childcon->contype != CONSTRAINT_NOTNULL)
14328 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14329
14330 if (childcon->coninhcount <= 0) /* shouldn't happen */
14331 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14332 childrelid, NameStr(childcon->conname));
14333
14334 if (recurse)
14335 {
14336 /*
14337 * If the child constraint has other definition sources, just
14338 * decrement its inheritance count; if not, recurse to delete it.
14339 */
14340 if (childcon->coninhcount == 1 && !childcon->conislocal)
14341 {
14342 /* Time to delete this child constraint, too */
14343 dropconstraint_internal(childrel, tuple, behavior,
14344 recurse, true, missing_ok,
14345 lockmode);
14346 }
14347 else
14348 {
14349 /* Child constraint must survive my deletion */
14350 childcon->coninhcount--;
14351 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14352
14353 /* Make update visible */
14355 }
14356 }
14357 else
14358 {
14359 /*
14360 * If we were told to drop ONLY in this table (no recursion) and
14361 * there are no further parents for this constraint, we need to
14362 * mark the inheritors' constraints as locally defined rather than
14363 * inherited.
14364 */
14365 childcon->coninhcount--;
14366 if (childcon->coninhcount == 0)
14367 childcon->conislocal = true;
14368
14369 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14370
14371 /* Make update visible */
14373 }
14374
14375 heap_freetuple(tuple);
14376
14377 table_close(childrel, NoLock);
14378 }
14379
14381
14382 return conobj;
14383}
14384
14385/*
14386 * ALTER COLUMN TYPE
14387 *
14388 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14389 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14390 * transformed (and must be, because we rely on some transformed fields).
14391 *
14392 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14393 * table will be done "in parallel" during phase 3, so all the USING
14394 * expressions should be parsed assuming the original column types. Also,
14395 * this allows a USING expression to refer to a field that will be dropped.
14396 *
14397 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14398 * the first two execution steps in phase 2; they must not see the effects
14399 * of any other subcommand types, since the USING expressions are parsed
14400 * against the unmodified table's state.
14401 */
14402static void
14404 AlteredTableInfo *tab, Relation rel,
14405 bool recurse, bool recursing,
14406 AlterTableCmd *cmd, LOCKMODE lockmode,
14407 AlterTableUtilityContext *context)
14408{
14409 char *colName = cmd->name;
14410 ColumnDef *def = (ColumnDef *) cmd->def;
14411 TypeName *typeName = def->typeName;
14412 Node *transform = def->cooked_default;
14413 HeapTuple tuple;
14414 Form_pg_attribute attTup;
14416 Oid targettype;
14417 int32 targettypmod;
14418 Oid targetcollid;
14420 ParseState *pstate = make_parsestate(NULL);
14421 AclResult aclresult;
14422 bool is_expr;
14423
14424 pstate->p_sourcetext = context->queryString;
14425
14426 if (rel->rd_rel->reloftype && !recursing)
14427 ereport(ERROR,
14428 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14429 errmsg("cannot alter column type of typed table"),
14430 parser_errposition(pstate, def->location)));
14431
14432 /* lookup the attribute so we can check inheritance status */
14433 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14434 if (!HeapTupleIsValid(tuple))
14435 ereport(ERROR,
14436 (errcode(ERRCODE_UNDEFINED_COLUMN),
14437 errmsg("column \"%s\" of relation \"%s\" does not exist",
14438 colName, RelationGetRelationName(rel)),
14439 parser_errposition(pstate, def->location)));
14440 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14441 attnum = attTup->attnum;
14442
14443 /* Can't alter a system attribute */
14444 if (attnum <= 0)
14445 ereport(ERROR,
14446 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14447 errmsg("cannot alter system column \"%s\"", colName),
14448 parser_errposition(pstate, def->location)));
14449
14450 /*
14451 * Cannot specify USING when altering type of a generated column, because
14452 * that would violate the generation expression.
14453 */
14454 if (attTup->attgenerated && def->cooked_default)
14455 ereport(ERROR,
14456 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14457 errmsg("cannot specify USING when altering type of generated column"),
14458 errdetail("Column \"%s\" is a generated column.", colName),
14459 parser_errposition(pstate, def->location)));
14460
14461 /*
14462 * Don't alter inherited columns. At outer level, there had better not be
14463 * any inherited definition; when recursing, we assume this was checked at
14464 * the parent level (see below).
14465 */
14466 if (attTup->attinhcount > 0 && !recursing)
14467 ereport(ERROR,
14468 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14469 errmsg("cannot alter inherited column \"%s\"", colName),
14470 parser_errposition(pstate, def->location)));
14471
14472 /* Don't alter columns used in the partition key */
14473 if (has_partition_attrs(rel,
14475 &is_expr))
14476 ereport(ERROR,
14477 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14478 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14479 colName, RelationGetRelationName(rel)),
14480 parser_errposition(pstate, def->location)));
14481
14482 /* Look up the target type */
14483 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14484
14485 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14486 if (aclresult != ACLCHECK_OK)
14487 aclcheck_error_type(aclresult, targettype);
14488
14489 /* And the collation */
14490 targetcollid = GetColumnDefCollation(pstate, def, targettype);
14491
14492 /* make sure datatype is legal for a column */
14493 CheckAttributeType(colName, targettype, targetcollid,
14494 list_make1_oid(rel->rd_rel->reltype),
14495 (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14496
14497 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14498 {
14499 /* do nothing */
14500 }
14501 else if (tab->relkind == RELKIND_RELATION ||
14502 tab->relkind == RELKIND_PARTITIONED_TABLE)
14503 {
14504 /*
14505 * Set up an expression to transform the old data value to the new
14506 * type. If a USING option was given, use the expression as
14507 * transformed by transformAlterTableStmt, else just take the old
14508 * value and try to coerce it. We do this first so that type
14509 * incompatibility can be detected before we waste effort, and because
14510 * we need the expression to be parsed against the original table row
14511 * type.
14512 */
14513 if (!transform)
14514 {
14515 transform = (Node *) makeVar(1, attnum,
14516 attTup->atttypid, attTup->atttypmod,
14517 attTup->attcollation,
14518 0);
14519 }
14520
14521 transform = coerce_to_target_type(pstate,
14522 transform, exprType(transform),
14523 targettype, targettypmod,
14526 -1);
14527 if (transform == NULL)
14528 {
14529 /* error text depends on whether USING was specified or not */
14530 if (def->cooked_default != NULL)
14531 ereport(ERROR,
14532 (errcode(ERRCODE_DATATYPE_MISMATCH),
14533 errmsg("result of USING clause for column \"%s\""
14534 " cannot be cast automatically to type %s",
14535 colName, format_type_be(targettype)),
14536 errhint("You might need to add an explicit cast.")));
14537 else
14538 ereport(ERROR,
14539 (errcode(ERRCODE_DATATYPE_MISMATCH),
14540 errmsg("column \"%s\" cannot be cast automatically to type %s",
14541 colName, format_type_be(targettype)),
14542 !attTup->attgenerated ?
14543 /* translator: USING is SQL, don't translate it */
14544 errhint("You might need to specify \"USING %s::%s\".",
14545 quote_identifier(colName),
14546 format_type_with_typemod(targettype,
14547 targettypmod)) : 0));
14548 }
14549
14550 /* Fix collations after all else */
14551 assign_expr_collations(pstate, transform);
14552
14553 /* Expand virtual generated columns in the expr. */
14554 transform = expand_generated_columns_in_expr(transform, rel, 1);
14555
14556 /* Plan the expr now so we can accurately assess the need to rewrite. */
14557 transform = (Node *) expression_planner((Expr *) transform);
14558
14559 /*
14560 * Add a work queue item to make ATRewriteTable update the column
14561 * contents.
14562 */
14564 newval->attnum = attnum;
14565 newval->expr = (Expr *) transform;
14566 newval->is_generated = false;
14567
14568 tab->newvals = lappend(tab->newvals, newval);
14569 if (ATColumnChangeRequiresRewrite(transform, attnum))
14571 }
14572 else if (transform)
14573 ereport(ERROR,
14574 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14575 errmsg("\"%s\" is not a table",
14577
14578 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14579 {
14580 /*
14581 * For relations or columns without storage, do this check now.
14582 * Regular tables will check it later when the table is being
14583 * rewritten.
14584 */
14585 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14586 }
14587
14588 ReleaseSysCache(tuple);
14589
14590 /*
14591 * Recurse manually by queueing a new command for each child, if
14592 * necessary. We cannot apply ATSimpleRecursion here because we need to
14593 * remap attribute numbers in the USING expression, if any.
14594 *
14595 * If we are told not to recurse, there had better not be any child
14596 * tables; else the alter would put them out of step.
14597 */
14598 if (recurse)
14599 {
14600 Oid relid = RelationGetRelid(rel);
14601 List *child_oids,
14602 *child_numparents;
14603 ListCell *lo,
14604 *li;
14605
14606 child_oids = find_all_inheritors(relid, lockmode,
14607 &child_numparents);
14608
14609 /*
14610 * find_all_inheritors does the recursive search of the inheritance
14611 * hierarchy, so all we have to do is process all of the relids in the
14612 * list that it returns.
14613 */
14614 forboth(lo, child_oids, li, child_numparents)
14615 {
14616 Oid childrelid = lfirst_oid(lo);
14617 int numparents = lfirst_int(li);
14618 Relation childrel;
14619 HeapTuple childtuple;
14620 Form_pg_attribute childattTup;
14621
14622 if (childrelid == relid)
14623 continue;
14624
14625 /* find_all_inheritors already got lock */
14626 childrel = relation_open(childrelid, NoLock);
14627 CheckAlterTableIsSafe(childrel);
14628
14629 /*
14630 * Verify that the child doesn't have any inherited definitions of
14631 * this column that came from outside this inheritance hierarchy.
14632 * (renameatt makes a similar test, though in a different way
14633 * because of its different recursion mechanism.)
14634 */
14635 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14636 colName);
14637 if (!HeapTupleIsValid(childtuple))
14638 ereport(ERROR,
14639 (errcode(ERRCODE_UNDEFINED_COLUMN),
14640 errmsg("column \"%s\" of relation \"%s\" does not exist",
14641 colName, RelationGetRelationName(childrel))));
14642 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14643
14644 if (childattTup->attinhcount > numparents)
14645 ereport(ERROR,
14646 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14647 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14648 colName, RelationGetRelationName(childrel))));
14649
14650 ReleaseSysCache(childtuple);
14651
14652 /*
14653 * Remap the attribute numbers. If no USING expression was
14654 * specified, there is no need for this step.
14655 */
14656 if (def->cooked_default)
14657 {
14658 AttrMap *attmap;
14659 bool found_whole_row;
14660
14661 /* create a copy to scribble on */
14662 cmd = copyObject(cmd);
14663
14664 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14665 RelationGetDescr(rel),
14666 false);
14667 ((ColumnDef *) cmd->def)->cooked_default =
14669 1, 0,
14670 attmap,
14671 InvalidOid, &found_whole_row);
14672 if (found_whole_row)
14673 ereport(ERROR,
14674 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14675 errmsg("cannot convert whole-row table reference"),
14676 errdetail("USING expression contains a whole-row table reference.")));
14677 pfree(attmap);
14678 }
14679 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14680 relation_close(childrel, NoLock);
14681 }
14682 }
14683 else if (!recursing &&
14685 ereport(ERROR,
14686 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14687 errmsg("type of inherited column \"%s\" must be changed in child tables too",
14688 colName)));
14689
14690 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14691 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14692}
14693
14694/*
14695 * When the data type of a column is changed, a rewrite might not be required
14696 * if the new type is sufficiently identical to the old one, and the USING
14697 * clause isn't trying to insert some other value. It's safe to skip the
14698 * rewrite in these cases:
14699 *
14700 * - the old type is binary coercible to the new type
14701 * - the new type is an unconstrained domain over the old type
14702 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14703 *
14704 * In the case of a constrained domain, we could get by with scanning the
14705 * table and checking the constraint rather than actually rewriting it, but we
14706 * don't currently try to do that.
14707 */
14708static bool
14710{
14711 Assert(expr != NULL);
14712
14713 for (;;)
14714 {
14715 /* only one varno, so no need to check that */
14716 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14717 return false;
14718 else if (IsA(expr, RelabelType))
14719 expr = (Node *) ((RelabelType *) expr)->arg;
14720 else if (IsA(expr, CoerceToDomain))
14721 {
14722 CoerceToDomain *d = (CoerceToDomain *) expr;
14723
14725 return true;
14726 expr = (Node *) d->arg;
14727 }
14728 else if (IsA(expr, FuncExpr))
14729 {
14730 FuncExpr *f = (FuncExpr *) expr;
14731
14732 switch (f->funcid)
14733 {
14734 case F_TIMESTAMPTZ_TIMESTAMP:
14735 case F_TIMESTAMP_TIMESTAMPTZ:
14737 return true;
14738 else
14739 expr = linitial(f->args);
14740 break;
14741 default:
14742 return true;
14743 }
14744 }
14745 else
14746 return true;
14747 }
14748}
14749
14750/*
14751 * ALTER COLUMN .. SET DATA TYPE
14752 *
14753 * Return the address of the modified column.
14754 */
14755static ObjectAddress
14757 AlterTableCmd *cmd, LOCKMODE lockmode)
14758{
14759 char *colName = cmd->name;
14760 ColumnDef *def = (ColumnDef *) cmd->def;
14761 TypeName *typeName = def->typeName;
14762 HeapTuple heapTup;
14763 Form_pg_attribute attTup,
14764 attOldTup;
14766 HeapTuple typeTuple;
14767 Form_pg_type tform;
14768 Oid targettype;
14769 int32 targettypmod;
14770 Oid targetcollid;
14771 Node *defaultexpr;
14772 Relation attrelation;
14773 Relation depRel;
14774 ScanKeyData key[3];
14775 SysScanDesc scan;
14776 HeapTuple depTup;
14777 ObjectAddress address;
14778
14779 /*
14780 * Clear all the missing values if we're rewriting the table, since this
14781 * renders them pointless.
14782 */
14783 if (tab->rewrite)
14784 {
14785 Relation newrel;
14786
14787 newrel = table_open(RelationGetRelid(rel), NoLock);
14788 RelationClearMissing(newrel);
14789 relation_close(newrel, NoLock);
14790 /* make sure we don't conflict with later attribute modifications */
14792 }
14793
14794 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14795
14796 /* Look up the target column */
14797 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14798 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14799 ereport(ERROR,
14800 (errcode(ERRCODE_UNDEFINED_COLUMN),
14801 errmsg("column \"%s\" of relation \"%s\" does not exist",
14802 colName, RelationGetRelationName(rel))));
14803 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14804 attnum = attTup->attnum;
14805 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14806
14807 /* Check for multiple ALTER TYPE on same column --- can't cope */
14808 if (attTup->atttypid != attOldTup->atttypid ||
14809 attTup->atttypmod != attOldTup->atttypmod)
14810 ereport(ERROR,
14811 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14812 errmsg("cannot alter type of column \"%s\" twice",
14813 colName)));
14814
14815 /* Look up the target type (should not fail, since prep found it) */
14816 typeTuple = typenameType(NULL, typeName, &targettypmod);
14817 tform = (Form_pg_type) GETSTRUCT(typeTuple);
14818 targettype = tform->oid;
14819 /* And the collation */
14820 targetcollid = GetColumnDefCollation(NULL, def, targettype);
14821
14822 /*
14823 * If there is a default expression for the column, get it and ensure we
14824 * can coerce it to the new datatype. (We must do this before changing
14825 * the column type, because build_column_default itself will try to
14826 * coerce, and will not issue the error message we want if it fails.)
14827 *
14828 * We remove any implicit coercion steps at the top level of the old
14829 * default expression; this has been agreed to satisfy the principle of
14830 * least surprise. (The conversion to the new column type should act like
14831 * it started from what the user sees as the stored expression, and the
14832 * implicit coercions aren't going to be shown.)
14833 */
14834 if (attTup->atthasdef)
14835 {
14836 defaultexpr = build_column_default(rel, attnum);
14837 Assert(defaultexpr);
14838 defaultexpr = strip_implicit_coercions(defaultexpr);
14839 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14840 defaultexpr, exprType(defaultexpr),
14841 targettype, targettypmod,
14844 -1);
14845 if (defaultexpr == NULL)
14846 {
14847 if (attTup->attgenerated)
14848 ereport(ERROR,
14849 (errcode(ERRCODE_DATATYPE_MISMATCH),
14850 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14851 colName, format_type_be(targettype))));
14852 else
14853 ereport(ERROR,
14854 (errcode(ERRCODE_DATATYPE_MISMATCH),
14855 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14856 colName, format_type_be(targettype))));
14857 }
14858 }
14859 else
14860 defaultexpr = NULL;
14861
14862 /*
14863 * Find everything that depends on the column (constraints, indexes, etc),
14864 * and record enough information to let us recreate the objects.
14865 *
14866 * The actual recreation does not happen here, but only after we have
14867 * performed all the individual ALTER TYPE operations. We have to save
14868 * the info before executing ALTER TYPE, though, else the deparser will
14869 * get confused.
14870 */
14872
14873 /*
14874 * Now scan for dependencies of this column on other things. The only
14875 * things we should find are the dependency on the column datatype and
14876 * possibly a collation dependency. Those can be removed.
14877 */
14878 depRel = table_open(DependRelationId, RowExclusiveLock);
14879
14880 ScanKeyInit(&key[0],
14881 Anum_pg_depend_classid,
14882 BTEqualStrategyNumber, F_OIDEQ,
14883 ObjectIdGetDatum(RelationRelationId));
14884 ScanKeyInit(&key[1],
14885 Anum_pg_depend_objid,
14886 BTEqualStrategyNumber, F_OIDEQ,
14888 ScanKeyInit(&key[2],
14889 Anum_pg_depend_objsubid,
14890 BTEqualStrategyNumber, F_INT4EQ,
14892
14893 scan = systable_beginscan(depRel, DependDependerIndexId, true,
14894 NULL, 3, key);
14895
14896 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14897 {
14898 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14899 ObjectAddress foundObject;
14900
14901 foundObject.classId = foundDep->refclassid;
14902 foundObject.objectId = foundDep->refobjid;
14903 foundObject.objectSubId = foundDep->refobjsubid;
14904
14905 if (foundDep->deptype != DEPENDENCY_NORMAL)
14906 elog(ERROR, "found unexpected dependency type '%c'",
14907 foundDep->deptype);
14908 if (!(foundDep->refclassid == TypeRelationId &&
14909 foundDep->refobjid == attTup->atttypid) &&
14910 !(foundDep->refclassid == CollationRelationId &&
14911 foundDep->refobjid == attTup->attcollation))
14912 elog(ERROR, "found unexpected dependency for column: %s",
14913 getObjectDescription(&foundObject, false));
14914
14915 CatalogTupleDelete(depRel, &depTup->t_self);
14916 }
14917
14918 systable_endscan(scan);
14919
14921
14922 /*
14923 * Here we go --- change the recorded column type and collation. (Note
14924 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14925 * fix up the missing value if any.
14926 */
14927 if (attTup->atthasmissing)
14928 {
14929 Datum missingval;
14930 bool missingNull;
14931
14932 /* if rewrite is true the missing value should already be cleared */
14933 Assert(tab->rewrite == 0);
14934
14935 /* Get the missing value datum */
14936 missingval = heap_getattr(heapTup,
14937 Anum_pg_attribute_attmissingval,
14938 attrelation->rd_att,
14939 &missingNull);
14940
14941 /* if it's a null array there is nothing to do */
14942
14943 if (!missingNull)
14944 {
14945 /*
14946 * Get the datum out of the array and repack it in a new array
14947 * built with the new type data. We assume that since the table
14948 * doesn't need rewriting, the actual Datum doesn't need to be
14949 * changed, only the array metadata.
14950 */
14951
14952 int one = 1;
14953 bool isNull;
14954 Datum valuesAtt[Natts_pg_attribute] = {0};
14955 bool nullsAtt[Natts_pg_attribute] = {0};
14956 bool replacesAtt[Natts_pg_attribute] = {0};
14957 HeapTuple newTup;
14958
14959 missingval = array_get_element(missingval,
14960 1,
14961 &one,
14962 0,
14963 attTup->attlen,
14964 attTup->attbyval,
14965 attTup->attalign,
14966 &isNull);
14967 missingval = PointerGetDatum(construct_array(&missingval,
14968 1,
14969 targettype,
14970 tform->typlen,
14971 tform->typbyval,
14972 tform->typalign));
14973
14974 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14975 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14976 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14977
14978 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14979 valuesAtt, nullsAtt, replacesAtt);
14980 heap_freetuple(heapTup);
14981 heapTup = newTup;
14982 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14983 }
14984 }
14985
14986 attTup->atttypid = targettype;
14987 attTup->atttypmod = targettypmod;
14988 attTup->attcollation = targetcollid;
14989 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14990 ereport(ERROR,
14991 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14992 errmsg("too many array dimensions"));
14993 attTup->attndims = list_length(typeName->arrayBounds);
14994 attTup->attlen = tform->typlen;
14995 attTup->attbyval = tform->typbyval;
14996 attTup->attalign = tform->typalign;
14997 attTup->attstorage = tform->typstorage;
14998 attTup->attcompression = InvalidCompressionMethod;
14999
15000 ReleaseSysCache(typeTuple);
15001
15002 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
15003
15004 table_close(attrelation, RowExclusiveLock);
15005
15006 /* Install dependencies on new datatype and collation */
15009
15010 /*
15011 * Drop any pg_statistic entry for the column, since it's now wrong type
15012 */
15014
15015 InvokeObjectPostAlterHook(RelationRelationId,
15016 RelationGetRelid(rel), attnum);
15017
15018 /*
15019 * Update the default, if present, by brute force --- remove and re-add
15020 * the default. Probably unsafe to take shortcuts, since the new version
15021 * may well have additional dependencies. (It's okay to do this now,
15022 * rather than after other ALTER TYPE commands, since the default won't
15023 * depend on other column types.)
15024 */
15025 if (defaultexpr)
15026 {
15027 /*
15028 * If it's a GENERATED default, drop its dependency records, in
15029 * particular its INTERNAL dependency on the column, which would
15030 * otherwise cause dependency.c to refuse to perform the deletion.
15031 */
15032 if (attTup->attgenerated)
15033 {
15034 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
15035
15036 if (!OidIsValid(attrdefoid))
15037 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15038 RelationGetRelid(rel), attnum);
15039 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
15040 }
15041
15042 /*
15043 * Make updates-so-far visible, particularly the new pg_attribute row
15044 * which will be updated again.
15045 */
15047
15048 /*
15049 * We use RESTRICT here for safety, but at present we do not expect
15050 * anything to depend on the default.
15051 */
15053 true);
15054
15055 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15056 }
15057
15058 ObjectAddressSubSet(address, RelationRelationId,
15059 RelationGetRelid(rel), attnum);
15060
15061 /* Cleanup */
15062 heap_freetuple(heapTup);
15063
15064 return address;
15065}
15066
15067/*
15068 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
15069 * that depends on the column (constraints, indexes, etc), and record enough
15070 * information to let us recreate the objects.
15071 */
15072static void
15074 Relation rel, AttrNumber attnum, const char *colName)
15075{
15076 Relation depRel;
15077 ScanKeyData key[3];
15078 SysScanDesc scan;
15079 HeapTuple depTup;
15080
15081 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15082
15083 depRel = table_open(DependRelationId, RowExclusiveLock);
15084
15085 ScanKeyInit(&key[0],
15086 Anum_pg_depend_refclassid,
15087 BTEqualStrategyNumber, F_OIDEQ,
15088 ObjectIdGetDatum(RelationRelationId));
15089 ScanKeyInit(&key[1],
15090 Anum_pg_depend_refobjid,
15091 BTEqualStrategyNumber, F_OIDEQ,
15093 ScanKeyInit(&key[2],
15094 Anum_pg_depend_refobjsubid,
15095 BTEqualStrategyNumber, F_INT4EQ,
15097
15098 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15099 NULL, 3, key);
15100
15101 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15102 {
15103 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15104 ObjectAddress foundObject;
15105
15106 foundObject.classId = foundDep->classid;
15107 foundObject.objectId = foundDep->objid;
15108 foundObject.objectSubId = foundDep->objsubid;
15109
15110 switch (foundObject.classId)
15111 {
15112 case RelationRelationId:
15113 {
15114 char relKind = get_rel_relkind(foundObject.objectId);
15115
15116 if (relKind == RELKIND_INDEX ||
15117 relKind == RELKIND_PARTITIONED_INDEX)
15118 {
15119 Assert(foundObject.objectSubId == 0);
15120 RememberIndexForRebuilding(foundObject.objectId, tab);
15121 }
15122 else if (relKind == RELKIND_SEQUENCE)
15123 {
15124 /*
15125 * This must be a SERIAL column's sequence. We need
15126 * not do anything to it.
15127 */
15128 Assert(foundObject.objectSubId == 0);
15129 }
15130 else
15131 {
15132 /* Not expecting any other direct dependencies... */
15133 elog(ERROR, "unexpected object depending on column: %s",
15134 getObjectDescription(&foundObject, false));
15135 }
15136 break;
15137 }
15138
15139 case ConstraintRelationId:
15140 Assert(foundObject.objectSubId == 0);
15141 RememberConstraintForRebuilding(foundObject.objectId, tab);
15142 break;
15143
15144 case ProcedureRelationId:
15145
15146 /*
15147 * A new-style SQL function can depend on a column, if that
15148 * column is referenced in the parsed function body. Ideally
15149 * we'd automatically update the function by deparsing and
15150 * reparsing it, but that's risky and might well fail anyhow.
15151 * FIXME someday.
15152 *
15153 * This is only a problem for AT_AlterColumnType, not
15154 * AT_SetExpression.
15155 */
15156 if (subtype == AT_AlterColumnType)
15157 ereport(ERROR,
15158 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15159 errmsg("cannot alter type of a column used by a function or procedure"),
15160 errdetail("%s depends on column \"%s\"",
15161 getObjectDescription(&foundObject, false),
15162 colName)));
15163 break;
15164
15165 case RewriteRelationId:
15166
15167 /*
15168 * View/rule bodies have pretty much the same issues as
15169 * function bodies. FIXME someday.
15170 */
15171 if (subtype == AT_AlterColumnType)
15172 ereport(ERROR,
15173 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15174 errmsg("cannot alter type of a column used by a view or rule"),
15175 errdetail("%s depends on column \"%s\"",
15176 getObjectDescription(&foundObject, false),
15177 colName)));
15178 break;
15179
15180 case TriggerRelationId:
15181
15182 /*
15183 * A trigger can depend on a column because the column is
15184 * specified as an update target, or because the column is
15185 * used in the trigger's WHEN condition. The first case would
15186 * not require any extra work, but the second case would
15187 * require updating the WHEN expression, which has the same
15188 * issues as above. Since we can't easily tell which case
15189 * applies, we punt for both. FIXME someday.
15190 */
15191 if (subtype == AT_AlterColumnType)
15192 ereport(ERROR,
15193 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15194 errmsg("cannot alter type of a column used in a trigger definition"),
15195 errdetail("%s depends on column \"%s\"",
15196 getObjectDescription(&foundObject, false),
15197 colName)));
15198 break;
15199
15200 case PolicyRelationId:
15201
15202 /*
15203 * A policy can depend on a column because the column is
15204 * specified in the policy's USING or WITH CHECK qual
15205 * expressions. It might be possible to rewrite and recheck
15206 * the policy expression, but punt for now. It's certainly
15207 * easy enough to remove and recreate the policy; still, FIXME
15208 * someday.
15209 */
15210 if (subtype == AT_AlterColumnType)
15211 ereport(ERROR,
15212 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15213 errmsg("cannot alter type of a column used in a policy definition"),
15214 errdetail("%s depends on column \"%s\"",
15215 getObjectDescription(&foundObject, false),
15216 colName)));
15217 break;
15218
15219 case AttrDefaultRelationId:
15220 {
15222
15223 if (col.objectId == RelationGetRelid(rel) &&
15224 col.objectSubId == attnum)
15225 {
15226 /*
15227 * Ignore the column's own default expression. The
15228 * caller deals with it.
15229 */
15230 }
15231 else
15232 {
15233 /*
15234 * This must be a reference from the expression of a
15235 * generated column elsewhere in the same table.
15236 * Changing the type/generated expression of a column
15237 * that is used by a generated column is not allowed
15238 * by SQL standard, so just punt for now. It might be
15239 * doable with some thinking and effort.
15240 */
15241 if (subtype == AT_AlterColumnType)
15242 ereport(ERROR,
15243 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15244 errmsg("cannot alter type of a column used by a generated column"),
15245 errdetail("Column \"%s\" is used by generated column \"%s\".",
15246 colName,
15248 col.objectSubId,
15249 false))));
15250 }
15251 break;
15252 }
15253
15254 case StatisticExtRelationId:
15255
15256 /*
15257 * Give the extended-stats machinery a chance to fix anything
15258 * that this column type change would break.
15259 */
15260 RememberStatisticsForRebuilding(foundObject.objectId, tab);
15261 break;
15262
15263 case PublicationRelRelationId:
15264
15265 /*
15266 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15267 * clause. Same issues as above. FIXME someday.
15268 */
15269 if (subtype == AT_AlterColumnType)
15270 ereport(ERROR,
15271 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15272 errmsg("cannot alter type of a column used by a publication WHERE clause"),
15273 errdetail("%s depends on column \"%s\"",
15274 getObjectDescription(&foundObject, false),
15275 colName)));
15276 break;
15277
15278 default:
15279
15280 /*
15281 * We don't expect any other sorts of objects to depend on a
15282 * column.
15283 */
15284 elog(ERROR, "unexpected object depending on column: %s",
15285 getObjectDescription(&foundObject, false));
15286 break;
15287 }
15288 }
15289
15290 systable_endscan(scan);
15291 table_close(depRel, NoLock);
15292}
15293
15294/*
15295 * Subroutine for ATExecAlterColumnType: remember that a replica identity
15296 * needs to be reset.
15297 */
15298static void
15300{
15301 if (!get_index_isreplident(indoid))
15302 return;
15303
15304 if (tab->replicaIdentityIndex)
15305 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15306
15307 tab->replicaIdentityIndex = get_rel_name(indoid);
15308}
15309
15310/*
15311 * Subroutine for ATExecAlterColumnType: remember any clustered index.
15312 */
15313static void
15315{
15316 if (!get_index_isclustered(indoid))
15317 return;
15318
15319 if (tab->clusterOnIndex)
15320 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15321
15322 tab->clusterOnIndex = get_rel_name(indoid);
15323}
15324
15325/*
15326 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15327 * to be rebuilt (which we might already know).
15328 */
15329static void
15331{
15332 /*
15333 * This de-duplication check is critical for two independent reasons: we
15334 * mustn't try to recreate the same constraint twice, and if a constraint
15335 * depends on more than one column whose type is to be altered, we must
15336 * capture its definition string before applying any of the column type
15337 * changes. ruleutils.c will get confused if we ask again later.
15338 */
15339 if (!list_member_oid(tab->changedConstraintOids, conoid))
15340 {
15341 /* OK, capture the constraint's existing definition string */
15342 char *defstring = pg_get_constraintdef_command(conoid);
15343 Oid indoid;
15344
15345 /*
15346 * It is critical to create not-null constraints ahead of primary key
15347 * indexes; otherwise, the not-null constraint would be created by the
15348 * primary key, and the constraint name would be wrong.
15349 */
15350 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15351 {
15352 tab->changedConstraintOids = lcons_oid(conoid,
15354 tab->changedConstraintDefs = lcons(defstring,
15356 }
15357 else
15358 {
15359
15361 conoid);
15363 defstring);
15364 }
15365
15366 /*
15367 * For the index of a constraint, if any, remember if it is used for
15368 * the table's replica identity or if it is a clustered index, so that
15369 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15370 * those properties.
15371 */
15372 indoid = get_constraint_index(conoid);
15373 if (OidIsValid(indoid))
15374 {
15376 RememberClusterOnForRebuilding(indoid, tab);
15377 }
15378 }
15379}
15380
15381/*
15382 * Subroutine for ATExecAlterColumnType: remember that an index needs
15383 * to be rebuilt (which we might already know).
15384 */
15385static void
15387{
15388 /*
15389 * This de-duplication check is critical for two independent reasons: we
15390 * mustn't try to recreate the same index twice, and if an index depends
15391 * on more than one column whose type is to be altered, we must capture
15392 * its definition string before applying any of the column type changes.
15393 * ruleutils.c will get confused if we ask again later.
15394 */
15395 if (!list_member_oid(tab->changedIndexOids, indoid))
15396 {
15397 /*
15398 * Before adding it as an index-to-rebuild, we'd better see if it
15399 * belongs to a constraint, and if so rebuild the constraint instead.
15400 * Typically this check fails, because constraint indexes normally
15401 * have only dependencies on their constraint. But it's possible for
15402 * such an index to also have direct dependencies on table columns,
15403 * for example with a partial exclusion constraint.
15404 */
15405 Oid conoid = get_index_constraint(indoid);
15406
15407 if (OidIsValid(conoid))
15408 {
15410 }
15411 else
15412 {
15413 /* OK, capture the index's existing definition string */
15414 char *defstring = pg_get_indexdef_string(indoid);
15415
15417 indoid);
15419 defstring);
15420
15421 /*
15422 * Remember if this index is used for the table's replica identity
15423 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15424 * can queue up commands necessary to restore those properties.
15425 */
15427 RememberClusterOnForRebuilding(indoid, tab);
15428 }
15429 }
15430}
15431
15432/*
15433 * Subroutine for ATExecAlterColumnType: remember that a statistics object
15434 * needs to be rebuilt (which we might already know).
15435 */
15436static void
15438{
15439 /*
15440 * This de-duplication check is critical for two independent reasons: we
15441 * mustn't try to recreate the same statistics object twice, and if the
15442 * statistics object depends on more than one column whose type is to be
15443 * altered, we must capture its definition string before applying any of
15444 * the type changes. ruleutils.c will get confused if we ask again later.
15445 */
15446 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15447 {
15448 /* OK, capture the statistics object's existing definition string */
15449 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15450
15452 stxoid);
15454 defstring);
15455 }
15456}
15457
15458/*
15459 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15460 * operations for a particular relation. We have to drop and recreate all the
15461 * indexes and constraints that depend on the altered columns. We do the
15462 * actual dropping here, but re-creation is managed by adding work queue
15463 * entries to do those steps later.
15464 */
15465static void
15467{
15468 ObjectAddress obj;
15469 ObjectAddresses *objects;
15470 ListCell *def_item;
15471 ListCell *oid_item;
15472
15473 /*
15474 * Collect all the constraints and indexes to drop so we can process them
15475 * in a single call. That way we don't have to worry about dependencies
15476 * among them.
15477 */
15478 objects = new_object_addresses();
15479
15480 /*
15481 * Re-parse the index and constraint definitions, and attach them to the
15482 * appropriate work queue entries. We do this before dropping because in
15483 * the case of a constraint on another table, we might not yet have
15484 * exclusive lock on the table the constraint is attached to, and we need
15485 * to get that before reparsing/dropping. (That's possible at least for
15486 * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15487 * requires a dependency on the target table's composite type in the other
15488 * table's constraint expressions.)
15489 *
15490 * We can't rely on the output of deparsing to tell us which relation to
15491 * operate on, because concurrent activity might have made the name
15492 * resolve differently. Instead, we've got to use the OID of the
15493 * constraint or index we're processing to figure out which relation to
15494 * operate on.
15495 */
15496 forboth(oid_item, tab->changedConstraintOids,
15497 def_item, tab->changedConstraintDefs)
15498 {
15499 Oid oldId = lfirst_oid(oid_item);
15500 HeapTuple tup;
15502 Oid relid;
15503 Oid confrelid;
15504 bool conislocal;
15505
15506 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15507 if (!HeapTupleIsValid(tup)) /* should not happen */
15508 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15509 con = (Form_pg_constraint) GETSTRUCT(tup);
15510 if (OidIsValid(con->conrelid))
15511 relid = con->conrelid;
15512 else
15513 {
15514 /* must be a domain constraint */
15515 relid = get_typ_typrelid(getBaseType(con->contypid));
15516 if (!OidIsValid(relid))
15517 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15518 }
15519 confrelid = con->confrelid;
15520 conislocal = con->conislocal;
15521 ReleaseSysCache(tup);
15522
15523 ObjectAddressSet(obj, ConstraintRelationId, oldId);
15524 add_exact_object_address(&obj, objects);
15525
15526 /*
15527 * If the constraint is inherited (only), we don't want to inject a
15528 * new definition here; it'll get recreated when
15529 * ATAddCheckNNConstraint recurses from adding the parent table's
15530 * constraint. But we had to carry the info this far so that we can
15531 * drop the constraint below.
15532 */
15533 if (!conislocal)
15534 continue;
15535
15536 /*
15537 * When rebuilding another table's constraint that references the
15538 * table we're modifying, we might not yet have any lock on the other
15539 * table, so get one now. We'll need AccessExclusiveLock for the DROP
15540 * CONSTRAINT step, so there's no value in asking for anything weaker.
15541 */
15542 if (relid != tab->relid)
15544
15545 ATPostAlterTypeParse(oldId, relid, confrelid,
15546 (char *) lfirst(def_item),
15547 wqueue, lockmode, tab->rewrite);
15548 }
15549 forboth(oid_item, tab->changedIndexOids,
15550 def_item, tab->changedIndexDefs)
15551 {
15552 Oid oldId = lfirst_oid(oid_item);
15553 Oid relid;
15554
15555 relid = IndexGetRelation(oldId, false);
15556
15557 /*
15558 * As above, make sure we have lock on the index's table if it's not
15559 * the same table.
15560 */
15561 if (relid != tab->relid)
15563
15564 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15565 (char *) lfirst(def_item),
15566 wqueue, lockmode, tab->rewrite);
15567
15568 ObjectAddressSet(obj, RelationRelationId, oldId);
15569 add_exact_object_address(&obj, objects);
15570 }
15571
15572 /* add dependencies for new statistics */
15573 forboth(oid_item, tab->changedStatisticsOids,
15574 def_item, tab->changedStatisticsDefs)
15575 {
15576 Oid oldId = lfirst_oid(oid_item);
15577 Oid relid;
15578
15579 relid = StatisticsGetRelation(oldId, false);
15580
15581 /*
15582 * As above, make sure we have lock on the statistics object's table
15583 * if it's not the same table. However, we take
15584 * ShareUpdateExclusiveLock here, aligning with the lock level used in
15585 * CreateStatistics and RemoveStatisticsById.
15586 *
15587 * CAUTION: this should be done after all cases that grab
15588 * AccessExclusiveLock, else we risk causing deadlock due to needing
15589 * to promote our table lock.
15590 */
15591 if (relid != tab->relid)
15593
15594 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15595 (char *) lfirst(def_item),
15596 wqueue, lockmode, tab->rewrite);
15597
15598 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15599 add_exact_object_address(&obj, objects);
15600 }
15601
15602 /*
15603 * Queue up command to restore replica identity index marking
15604 */
15605 if (tab->replicaIdentityIndex)
15606 {
15609
15610 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15611 subcmd->name = tab->replicaIdentityIndex;
15613 cmd->def = (Node *) subcmd;
15614
15615 /* do it after indexes and constraints */
15616 tab->subcmds[AT_PASS_OLD_CONSTR] =
15617 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15618 }
15619
15620 /*
15621 * Queue up command to restore marking of index used for cluster.
15622 */
15623 if (tab->clusterOnIndex)
15624 {
15626
15627 cmd->subtype = AT_ClusterOn;
15628 cmd->name = tab->clusterOnIndex;
15629
15630 /* do it after indexes and constraints */
15631 tab->subcmds[AT_PASS_OLD_CONSTR] =
15632 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15633 }
15634
15635 /*
15636 * It should be okay to use DROP_RESTRICT here, since nothing else should
15637 * be depending on these objects.
15638 */
15640
15641 free_object_addresses(objects);
15642
15643 /*
15644 * The objects will get recreated during subsequent passes over the work
15645 * queue.
15646 */
15647}
15648
15649/*
15650 * Parse the previously-saved definition string for a constraint, index or
15651 * statistics object against the newly-established column data type(s), and
15652 * queue up the resulting command parsetrees for execution.
15653 *
15654 * This might fail if, for example, you have a WHERE clause that uses an
15655 * operator that's not available for the new column type.
15656 */
15657static void
15658ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15659 List **wqueue, LOCKMODE lockmode, bool rewrite)
15660{
15661 List *raw_parsetree_list;
15662 List *querytree_list;
15663 ListCell *list_item;
15664 Relation rel;
15665
15666 /*
15667 * We expect that we will get only ALTER TABLE and CREATE INDEX
15668 * statements. Hence, there is no need to pass them through
15669 * parse_analyze_*() or the rewriter, but instead we need to pass them
15670 * through parse_utilcmd.c to make them ready for execution.
15671 */
15672 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15673 querytree_list = NIL;
15674 foreach(list_item, raw_parsetree_list)
15675 {
15676 RawStmt *rs = lfirst_node(RawStmt, list_item);
15677 Node *stmt = rs->stmt;
15678
15679 if (IsA(stmt, IndexStmt))
15680 querytree_list = lappend(querytree_list,
15681 transformIndexStmt(oldRelId,
15682 (IndexStmt *) stmt,
15683 cmd));
15684 else if (IsA(stmt, AlterTableStmt))
15685 {
15686 List *beforeStmts;
15687 List *afterStmts;
15688
15689 stmt = (Node *) transformAlterTableStmt(oldRelId,
15690 (AlterTableStmt *) stmt,
15691 cmd,
15692 &beforeStmts,
15693 &afterStmts);
15694 querytree_list = list_concat(querytree_list, beforeStmts);
15695 querytree_list = lappend(querytree_list, stmt);
15696 querytree_list = list_concat(querytree_list, afterStmts);
15697 }
15698 else if (IsA(stmt, CreateStatsStmt))
15699 querytree_list = lappend(querytree_list,
15700 transformStatsStmt(oldRelId,
15702 cmd));
15703 else
15704 querytree_list = lappend(querytree_list, stmt);
15705 }
15706
15707 /* Caller should already have acquired whatever lock we need. */
15708 rel = relation_open(oldRelId, NoLock);
15709
15710 /*
15711 * Attach each generated command to the proper place in the work queue.
15712 * Note this could result in creation of entirely new work-queue entries.
15713 *
15714 * Also note that we have to tweak the command subtypes, because it turns
15715 * out that re-creation of indexes and constraints has to act a bit
15716 * differently from initial creation.
15717 */
15718 foreach(list_item, querytree_list)
15719 {
15720 Node *stm = (Node *) lfirst(list_item);
15721 AlteredTableInfo *tab;
15722
15723 tab = ATGetQueueEntry(wqueue, rel);
15724
15725 if (IsA(stm, IndexStmt))
15726 {
15727 IndexStmt *stmt = (IndexStmt *) stm;
15728 AlterTableCmd *newcmd;
15729
15730 if (!rewrite)
15731 TryReuseIndex(oldId, stmt);
15732 stmt->reset_default_tblspc = true;
15733 /* keep the index's comment */
15734 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15735
15736 newcmd = makeNode(AlterTableCmd);
15737 newcmd->subtype = AT_ReAddIndex;
15738 newcmd->def = (Node *) stmt;
15740 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15741 }
15742 else if (IsA(stm, AlterTableStmt))
15743 {
15745 ListCell *lcmd;
15746
15747 foreach(lcmd, stmt->cmds)
15748 {
15750
15751 if (cmd->subtype == AT_AddIndex)
15752 {
15753 IndexStmt *indstmt;
15754 Oid indoid;
15755
15756 indstmt = castNode(IndexStmt, cmd->def);
15757 indoid = get_constraint_index(oldId);
15758
15759 if (!rewrite)
15760 TryReuseIndex(indoid, indstmt);
15761 /* keep any comment on the index */
15762 indstmt->idxcomment = GetComment(indoid,
15763 RelationRelationId, 0);
15764 indstmt->reset_default_tblspc = true;
15765
15766 cmd->subtype = AT_ReAddIndex;
15768 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15769
15770 /* recreate any comment on the constraint */
15773 oldId,
15774 rel,
15775 NIL,
15776 indstmt->idxname);
15777 }
15778 else if (cmd->subtype == AT_AddConstraint)
15779 {
15780 Constraint *con = castNode(Constraint, cmd->def);
15781
15782 con->old_pktable_oid = refRelId;
15783 /* rewriting neither side of a FK */
15784 if (con->contype == CONSTR_FOREIGN &&
15785 !rewrite && tab->rewrite == 0)
15786 TryReuseForeignKey(oldId, con);
15787 con->reset_default_tblspc = true;
15791
15792 /*
15793 * Recreate any comment on the constraint. If we have
15794 * recreated a primary key, then transformTableConstraint
15795 * has added an unnamed not-null constraint here; skip
15796 * this in that case.
15797 */
15798 if (con->conname)
15801 oldId,
15802 rel,
15803 NIL,
15804 con->conname);
15805 else
15806 Assert(con->contype == CONSTR_NOTNULL);
15807 }
15808 else
15809 elog(ERROR, "unexpected statement subtype: %d",
15810 (int) cmd->subtype);
15811 }
15812 }
15813 else if (IsA(stm, AlterDomainStmt))
15814 {
15816
15817 if (stmt->subtype == AD_AddConstraint)
15818 {
15819 Constraint *con = castNode(Constraint, stmt->def);
15821
15823 cmd->def = (Node *) stmt;
15826
15827 /* recreate any comment on the constraint */
15830 oldId,
15831 NULL,
15832 stmt->typeName,
15833 con->conname);
15834 }
15835 else
15836 elog(ERROR, "unexpected statement subtype: %d",
15837 (int) stmt->subtype);
15838 }
15839 else if (IsA(stm, CreateStatsStmt))
15840 {
15842 AlterTableCmd *newcmd;
15843
15844 /* keep the statistics object's comment */
15845 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15846
15847 newcmd = makeNode(AlterTableCmd);
15848 newcmd->subtype = AT_ReAddStatistics;
15849 newcmd->def = (Node *) stmt;
15850 tab->subcmds[AT_PASS_MISC] =
15851 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15852 }
15853 else
15854 elog(ERROR, "unexpected statement type: %d",
15855 (int) nodeTag(stm));
15856 }
15857
15858 relation_close(rel, NoLock);
15859}
15860
15861/*
15862 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15863 * for a table or domain constraint that is being rebuilt.
15864 *
15865 * objid is the OID of the constraint.
15866 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15867 * as a string list) for a domain constraint.
15868 * (We could dig that info, as well as the conname, out of the pg_constraint
15869 * entry; but callers already have them so might as well pass them.)
15870 */
15871static void
15873 Relation rel, List *domname,
15874 const char *conname)
15875{
15876 CommentStmt *cmd;
15877 char *comment_str;
15878 AlterTableCmd *newcmd;
15879
15880 /* Look for comment for object wanted, and leave if none */
15881 comment_str = GetComment(objid, ConstraintRelationId, 0);
15882 if (comment_str == NULL)
15883 return;
15884
15885 /* Build CommentStmt node, copying all input data for safety */
15886 cmd = makeNode(CommentStmt);
15887 if (rel)
15888 {
15890 cmd->object = (Node *)
15893 makeString(pstrdup(conname)));
15894 }
15895 else
15896 {
15898 cmd->object = (Node *)
15900 makeString(pstrdup(conname)));
15901 }
15902 cmd->comment = comment_str;
15903
15904 /* Append it to list of commands */
15905 newcmd = makeNode(AlterTableCmd);
15906 newcmd->subtype = AT_ReAddComment;
15907 newcmd->def = (Node *) cmd;
15908 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15909}
15910
15911/*
15912 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15913 * for the real analysis, then mutates the IndexStmt based on that verdict.
15914 */
15915static void
15917{
15918 if (CheckIndexCompatible(oldId,
15919 stmt->accessMethod,
15920 stmt->indexParams,
15921 stmt->excludeOpNames,
15922 stmt->iswithoutoverlaps))
15923 {
15924 Relation irel = index_open(oldId, NoLock);
15925
15926 /* If it's a partitioned index, there is no storage to share. */
15927 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15928 {
15929 stmt->oldNumber = irel->rd_locator.relNumber;
15930 stmt->oldCreateSubid = irel->rd_createSubid;
15931 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15932 }
15933 index_close(irel, NoLock);
15934 }
15935}
15936
15937/*
15938 * Subroutine for ATPostAlterTypeParse().
15939 *
15940 * Stash the old P-F equality operator into the Constraint node, for possible
15941 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15942 * this constraint can be skipped.
15943 */
15944static void
15946{
15947 HeapTuple tup;
15948 Datum adatum;
15949 ArrayType *arr;
15950 Oid *rawarr;
15951 int numkeys;
15952 int i;
15953
15954 Assert(con->contype == CONSTR_FOREIGN);
15955 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15956
15957 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15958 if (!HeapTupleIsValid(tup)) /* should not happen */
15959 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15960
15961 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15962 Anum_pg_constraint_conpfeqop);
15963 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15964 numkeys = ARR_DIMS(arr)[0];
15965 /* test follows the one in ri_FetchConstraintInfo() */
15966 if (ARR_NDIM(arr) != 1 ||
15967 ARR_HASNULL(arr) ||
15968 ARR_ELEMTYPE(arr) != OIDOID)
15969 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15970 rawarr = (Oid *) ARR_DATA_PTR(arr);
15971
15972 /* stash a List of the operator Oids in our Constraint node */
15973 for (i = 0; i < numkeys; i++)
15974 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15975
15976 ReleaseSysCache(tup);
15977}
15978
15979/*
15980 * ALTER COLUMN .. OPTIONS ( ... )
15981 *
15982 * Returns the address of the modified column
15983 */
15984static ObjectAddress
15986 const char *colName,
15987 List *options,
15988 LOCKMODE lockmode)
15989{
15990 Relation ftrel;
15991 Relation attrel;
15992 ForeignServer *server;
15993 ForeignDataWrapper *fdw;
15994 HeapTuple tuple;
15995 HeapTuple newtuple;
15996 bool isnull;
15997 Datum repl_val[Natts_pg_attribute];
15998 bool repl_null[Natts_pg_attribute];
15999 bool repl_repl[Natts_pg_attribute];
16000 Datum datum;
16001 Form_pg_foreign_table fttableform;
16002 Form_pg_attribute atttableform;
16004 ObjectAddress address;
16005
16006 if (options == NIL)
16007 return InvalidObjectAddress;
16008
16009 /* First, determine FDW validator associated to the foreign table. */
16010 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
16011 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
16012 if (!HeapTupleIsValid(tuple))
16013 ereport(ERROR,
16014 (errcode(ERRCODE_UNDEFINED_OBJECT),
16015 errmsg("foreign table \"%s\" does not exist",
16017 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16018 server = GetForeignServer(fttableform->ftserver);
16019 fdw = GetForeignDataWrapper(server->fdwid);
16020
16022 ReleaseSysCache(tuple);
16023
16024 attrel = table_open(AttributeRelationId, RowExclusiveLock);
16025 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
16026 if (!HeapTupleIsValid(tuple))
16027 ereport(ERROR,
16028 (errcode(ERRCODE_UNDEFINED_COLUMN),
16029 errmsg("column \"%s\" of relation \"%s\" does not exist",
16030 colName, RelationGetRelationName(rel))));
16031
16032 /* Prevent them from altering a system attribute */
16033 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16034 attnum = atttableform->attnum;
16035 if (attnum <= 0)
16036 ereport(ERROR,
16037 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16038 errmsg("cannot alter system column \"%s\"", colName)));
16039
16040
16041 /* Initialize buffers for new tuple values */
16042 memset(repl_val, 0, sizeof(repl_val));
16043 memset(repl_null, false, sizeof(repl_null));
16044 memset(repl_repl, false, sizeof(repl_repl));
16045
16046 /* Extract the current options */
16047 datum = SysCacheGetAttr(ATTNAME,
16048 tuple,
16049 Anum_pg_attribute_attfdwoptions,
16050 &isnull);
16051 if (isnull)
16052 datum = PointerGetDatum(NULL);
16053
16054 /* Transform the options */
16055 datum = transformGenericOptions(AttributeRelationId,
16056 datum,
16057 options,
16058 fdw->fdwvalidator);
16059
16060 if (DatumGetPointer(datum) != NULL)
16061 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16062 else
16063 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16064
16065 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16066
16067 /* Everything looks good - update the tuple */
16068
16069 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16070 repl_val, repl_null, repl_repl);
16071
16072 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16073
16074 InvokeObjectPostAlterHook(RelationRelationId,
16075 RelationGetRelid(rel),
16076 atttableform->attnum);
16077 ObjectAddressSubSet(address, RelationRelationId,
16078 RelationGetRelid(rel), attnum);
16079
16080 ReleaseSysCache(tuple);
16081
16083
16084 heap_freetuple(newtuple);
16085
16086 return address;
16087}
16088
16089/*
16090 * ALTER TABLE OWNER
16091 *
16092 * recursing is true if we are recursing from a table to its indexes,
16093 * sequences, or toast table. We don't allow the ownership of those things to
16094 * be changed separately from the parent table. Also, we can skip permission
16095 * checks (this is necessary not just an optimization, else we'd fail to
16096 * handle toast tables properly).
16097 *
16098 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
16099 * free-standing composite type.
16100 */
16101void
16102ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
16103{
16104 Relation target_rel;
16105 Relation class_rel;
16106 HeapTuple tuple;
16107 Form_pg_class tuple_class;
16108
16109 /*
16110 * Get exclusive lock till end of transaction on the target table. Use
16111 * relation_open so that we can work on indexes and sequences.
16112 */
16113 target_rel = relation_open(relationOid, lockmode);
16114
16115 /* Get its pg_class tuple, too */
16116 class_rel = table_open(RelationRelationId, RowExclusiveLock);
16117
16118 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16119 if (!HeapTupleIsValid(tuple))
16120 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16121 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16122
16123 /* Can we change the ownership of this tuple? */
16124 switch (tuple_class->relkind)
16125 {
16126 case RELKIND_RELATION:
16127 case RELKIND_VIEW:
16128 case RELKIND_MATVIEW:
16129 case RELKIND_FOREIGN_TABLE:
16130 case RELKIND_PARTITIONED_TABLE:
16131 /* ok to change owner */
16132 break;
16133 case RELKIND_INDEX:
16134 if (!recursing)
16135 {
16136 /*
16137 * Because ALTER INDEX OWNER used to be allowed, and in fact
16138 * is generated by old versions of pg_dump, we give a warning
16139 * and do nothing rather than erroring out. Also, to avoid
16140 * unnecessary chatter while restoring those old dumps, say
16141 * nothing at all if the command would be a no-op anyway.
16142 */
16143 if (tuple_class->relowner != newOwnerId)
16145 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16146 errmsg("cannot change owner of index \"%s\"",
16147 NameStr(tuple_class->relname)),
16148 errhint("Change the ownership of the index's table instead.")));
16149 /* quick hack to exit via the no-op path */
16150 newOwnerId = tuple_class->relowner;
16151 }
16152 break;
16153 case RELKIND_PARTITIONED_INDEX:
16154 if (recursing)
16155 break;
16156 ereport(ERROR,
16157 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16158 errmsg("cannot change owner of index \"%s\"",
16159 NameStr(tuple_class->relname)),
16160 errhint("Change the ownership of the index's table instead.")));
16161 break;
16162 case RELKIND_SEQUENCE:
16163 if (!recursing &&
16164 tuple_class->relowner != newOwnerId)
16165 {
16166 /* if it's an owned sequence, disallow changing it by itself */
16167 Oid tableId;
16168 int32 colId;
16169
16170 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16171 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16172 ereport(ERROR,
16173 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16174 errmsg("cannot change owner of sequence \"%s\"",
16175 NameStr(tuple_class->relname)),
16176 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16177 NameStr(tuple_class->relname),
16178 get_rel_name(tableId))));
16179 }
16180 break;
16181 case RELKIND_COMPOSITE_TYPE:
16182 if (recursing)
16183 break;
16184 ereport(ERROR,
16185 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16186 errmsg("\"%s\" is a composite type",
16187 NameStr(tuple_class->relname)),
16188 /* translator: %s is an SQL ALTER command */
16189 errhint("Use %s instead.",
16190 "ALTER TYPE")));
16191 break;
16192 case RELKIND_TOASTVALUE:
16193 if (recursing)
16194 break;
16195 /* FALL THRU */
16196 default:
16197 ereport(ERROR,
16198 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16199 errmsg("cannot change owner of relation \"%s\"",
16200 NameStr(tuple_class->relname)),
16201 errdetail_relkind_not_supported(tuple_class->relkind)));
16202 }
16203
16204 /*
16205 * If the new owner is the same as the existing owner, consider the
16206 * command to have succeeded. This is for dump restoration purposes.
16207 */
16208 if (tuple_class->relowner != newOwnerId)
16209 {
16210 Datum repl_val[Natts_pg_class];
16211 bool repl_null[Natts_pg_class];
16212 bool repl_repl[Natts_pg_class];
16213 Acl *newAcl;
16214 Datum aclDatum;
16215 bool isNull;
16216 HeapTuple newtuple;
16217
16218 /* skip permission checks when recursing to index or toast table */
16219 if (!recursing)
16220 {
16221 /* Superusers can always do it */
16222 if (!superuser())
16223 {
16224 Oid namespaceOid = tuple_class->relnamespace;
16225 AclResult aclresult;
16226
16227 /* Otherwise, must be owner of the existing object */
16228 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16230 RelationGetRelationName(target_rel));
16231
16232 /* Must be able to become new owner */
16233 check_can_set_role(GetUserId(), newOwnerId);
16234
16235 /* New owner must have CREATE privilege on namespace */
16236 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16237 ACL_CREATE);
16238 if (aclresult != ACLCHECK_OK)
16239 aclcheck_error(aclresult, OBJECT_SCHEMA,
16240 get_namespace_name(namespaceOid));
16241 }
16242 }
16243
16244 memset(repl_null, false, sizeof(repl_null));
16245 memset(repl_repl, false, sizeof(repl_repl));
16246
16247 repl_repl[Anum_pg_class_relowner - 1] = true;
16248 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16249
16250 /*
16251 * Determine the modified ACL for the new owner. This is only
16252 * necessary when the ACL is non-null.
16253 */
16254 aclDatum = SysCacheGetAttr(RELOID, tuple,
16255 Anum_pg_class_relacl,
16256 &isNull);
16257 if (!isNull)
16258 {
16259 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16260 tuple_class->relowner, newOwnerId);
16261 repl_repl[Anum_pg_class_relacl - 1] = true;
16262 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16263 }
16264
16265 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16266
16267 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16268
16269 heap_freetuple(newtuple);
16270
16271 /*
16272 * We must similarly update any per-column ACLs to reflect the new
16273 * owner; for neatness reasons that's split out as a subroutine.
16274 */
16275 change_owner_fix_column_acls(relationOid,
16276 tuple_class->relowner,
16277 newOwnerId);
16278
16279 /*
16280 * Update owner dependency reference, if any. A composite type has
16281 * none, because it's tracked for the pg_type entry instead of here;
16282 * indexes and TOAST tables don't have their own entries either.
16283 */
16284 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16285 tuple_class->relkind != RELKIND_INDEX &&
16286 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16287 tuple_class->relkind != RELKIND_TOASTVALUE)
16288 changeDependencyOnOwner(RelationRelationId, relationOid,
16289 newOwnerId);
16290
16291 /*
16292 * Also change the ownership of the table's row type, if it has one
16293 */
16294 if (OidIsValid(tuple_class->reltype))
16295 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16296
16297 /*
16298 * If we are operating on a table or materialized view, also change
16299 * the ownership of any indexes and sequences that belong to the
16300 * relation, as well as its toast table (if it has one).
16301 */
16302 if (tuple_class->relkind == RELKIND_RELATION ||
16303 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16304 tuple_class->relkind == RELKIND_MATVIEW ||
16305 tuple_class->relkind == RELKIND_TOASTVALUE)
16306 {
16307 List *index_oid_list;
16308 ListCell *i;
16309
16310 /* Find all the indexes belonging to this relation */
16311 index_oid_list = RelationGetIndexList(target_rel);
16312
16313 /* For each index, recursively change its ownership */
16314 foreach(i, index_oid_list)
16315 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16316
16317 list_free(index_oid_list);
16318 }
16319
16320 /* If it has a toast table, recurse to change its ownership */
16321 if (tuple_class->reltoastrelid != InvalidOid)
16322 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16323 true, lockmode);
16324
16325 /* If it has dependent sequences, recurse to change them too */
16326 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16327 }
16328
16329 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16330
16331 ReleaseSysCache(tuple);
16332 table_close(class_rel, RowExclusiveLock);
16333 relation_close(target_rel, NoLock);
16334}
16335
16336/*
16337 * change_owner_fix_column_acls
16338 *
16339 * Helper function for ATExecChangeOwner. Scan the columns of the table
16340 * and fix any non-null column ACLs to reflect the new owner.
16341 */
16342static void
16343change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16344{
16345 Relation attRelation;
16346 SysScanDesc scan;
16347 ScanKeyData key[1];
16348 HeapTuple attributeTuple;
16349
16350 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16351 ScanKeyInit(&key[0],
16352 Anum_pg_attribute_attrelid,
16353 BTEqualStrategyNumber, F_OIDEQ,
16354 ObjectIdGetDatum(relationOid));
16355 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16356 true, NULL, 1, key);
16357 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16358 {
16359 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16360 Datum repl_val[Natts_pg_attribute];
16361 bool repl_null[Natts_pg_attribute];
16362 bool repl_repl[Natts_pg_attribute];
16363 Acl *newAcl;
16364 Datum aclDatum;
16365 bool isNull;
16366 HeapTuple newtuple;
16367
16368 /* Ignore dropped columns */
16369 if (att->attisdropped)
16370 continue;
16371
16372 aclDatum = heap_getattr(attributeTuple,
16373 Anum_pg_attribute_attacl,
16374 RelationGetDescr(attRelation),
16375 &isNull);
16376 /* Null ACLs do not require changes */
16377 if (isNull)
16378 continue;
16379
16380 memset(repl_null, false, sizeof(repl_null));
16381 memset(repl_repl, false, sizeof(repl_repl));
16382
16383 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16384 oldOwnerId, newOwnerId);
16385 repl_repl[Anum_pg_attribute_attacl - 1] = true;
16386 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16387
16388 newtuple = heap_modify_tuple(attributeTuple,
16389 RelationGetDescr(attRelation),
16390 repl_val, repl_null, repl_repl);
16391
16392 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16393
16394 heap_freetuple(newtuple);
16395 }
16396 systable_endscan(scan);
16397 table_close(attRelation, RowExclusiveLock);
16398}
16399
16400/*
16401 * change_owner_recurse_to_sequences
16402 *
16403 * Helper function for ATExecChangeOwner. Examines pg_depend searching
16404 * for sequences that are dependent on serial columns, and changes their
16405 * ownership.
16406 */
16407static void
16408change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16409{
16410 Relation depRel;
16411 SysScanDesc scan;
16412 ScanKeyData key[2];
16413 HeapTuple tup;
16414
16415 /*
16416 * SERIAL sequences are those having an auto dependency on one of the
16417 * table's columns (we don't care *which* column, exactly).
16418 */
16419 depRel = table_open(DependRelationId, AccessShareLock);
16420
16421 ScanKeyInit(&key[0],
16422 Anum_pg_depend_refclassid,
16423 BTEqualStrategyNumber, F_OIDEQ,
16424 ObjectIdGetDatum(RelationRelationId));
16425 ScanKeyInit(&key[1],
16426 Anum_pg_depend_refobjid,
16427 BTEqualStrategyNumber, F_OIDEQ,
16428 ObjectIdGetDatum(relationOid));
16429 /* we leave refobjsubid unspecified */
16430
16431 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16432 NULL, 2, key);
16433
16434 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16435 {
16436 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16437 Relation seqRel;
16438
16439 /* skip dependencies other than auto dependencies on columns */
16440 if (depForm->refobjsubid == 0 ||
16441 depForm->classid != RelationRelationId ||
16442 depForm->objsubid != 0 ||
16443 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16444 continue;
16445
16446 /* Use relation_open just in case it's an index */
16447 seqRel = relation_open(depForm->objid, lockmode);
16448
16449 /* skip non-sequence relations */
16450 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16451 {
16452 /* No need to keep the lock */
16453 relation_close(seqRel, lockmode);
16454 continue;
16455 }
16456
16457 /* We don't need to close the sequence while we alter it. */
16458 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16459
16460 /* Now we can close it. Keep the lock till end of transaction. */
16461 relation_close(seqRel, NoLock);
16462 }
16463
16464 systable_endscan(scan);
16465
16467}
16468
16469/*
16470 * ALTER TABLE CLUSTER ON
16471 *
16472 * The only thing we have to do is to change the indisclustered bits.
16473 *
16474 * Return the address of the new clustering index.
16475 */
16476static ObjectAddress
16477ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16478{
16479 Oid indexOid;
16480 ObjectAddress address;
16481
16482 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16483
16484 if (!OidIsValid(indexOid))
16485 ereport(ERROR,
16486 (errcode(ERRCODE_UNDEFINED_OBJECT),
16487 errmsg("index \"%s\" for table \"%s\" does not exist",
16488 indexName, RelationGetRelationName(rel))));
16489
16490 /* Check index is valid to cluster on */
16491 check_index_is_clusterable(rel, indexOid, lockmode);
16492
16493 /* And do the work */
16494 mark_index_clustered(rel, indexOid, false);
16495
16496 ObjectAddressSet(address,
16497 RelationRelationId, indexOid);
16498
16499 return address;
16500}
16501
16502/*
16503 * ALTER TABLE SET WITHOUT CLUSTER
16504 *
16505 * We have to find any indexes on the table that have indisclustered bit
16506 * set and turn it off.
16507 */
16508static void
16510{
16511 mark_index_clustered(rel, InvalidOid, false);
16512}
16513
16514/*
16515 * Preparation phase for SET ACCESS METHOD
16516 *
16517 * Check that the access method exists and determine whether a change is
16518 * actually needed.
16519 */
16520static void
16522{
16523 Oid amoid;
16524
16525 /*
16526 * Look up the access method name and check that it differs from the
16527 * table's current AM. If DEFAULT was specified for a partitioned table
16528 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16529 */
16530 if (amname != NULL)
16531 amoid = get_table_am_oid(amname, false);
16532 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16533 amoid = InvalidOid;
16534 else
16536
16537 /* if it's a match, phase 3 doesn't need to do anything */
16538 if (rel->rd_rel->relam == amoid)
16539 return;
16540
16541 /* Save info for Phase 3 to do the real work */
16543 tab->newAccessMethod = amoid;
16544 tab->chgAccessMethod = true;
16545}
16546
16547/*
16548 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16549 * storage that have an interest in preserving AM.
16550 *
16551 * Since these have no storage, setting the access method is a catalog only
16552 * operation.
16553 */
16554static void
16556{
16557 Relation pg_class;
16558 Oid oldAccessMethodId;
16559 HeapTuple tuple;
16560 Form_pg_class rd_rel;
16561 Oid reloid = RelationGetRelid(rel);
16562
16563 /*
16564 * Shouldn't be called on relations having storage; these are processed in
16565 * phase 3.
16566 */
16567 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16568
16569 /* Get a modifiable copy of the relation's pg_class row. */
16570 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16571
16572 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16573 if (!HeapTupleIsValid(tuple))
16574 elog(ERROR, "cache lookup failed for relation %u", reloid);
16575 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16576
16577 /* Update the pg_class row. */
16578 oldAccessMethodId = rd_rel->relam;
16579 rd_rel->relam = newAccessMethodId;
16580
16581 /* Leave if no update required */
16582 if (rd_rel->relam == oldAccessMethodId)
16583 {
16584 heap_freetuple(tuple);
16585 table_close(pg_class, RowExclusiveLock);
16586 return;
16587 }
16588
16589 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16590
16591 /*
16592 * Update the dependency on the new access method. No dependency is added
16593 * if the new access method is InvalidOid (default case). Be very careful
16594 * that this has to compare the previous value stored in pg_class with the
16595 * new one.
16596 */
16597 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16598 {
16599 ObjectAddress relobj,
16600 referenced;
16601
16602 /*
16603 * New access method is defined and there was no dependency
16604 * previously, so record a new one.
16605 */
16606 ObjectAddressSet(relobj, RelationRelationId, reloid);
16607 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16608 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16609 }
16610 else if (OidIsValid(oldAccessMethodId) &&
16611 !OidIsValid(rd_rel->relam))
16612 {
16613 /*
16614 * There was an access method defined, and no new one, so just remove
16615 * the existing dependency.
16616 */
16617 deleteDependencyRecordsForClass(RelationRelationId, reloid,
16618 AccessMethodRelationId,
16620 }
16621 else
16622 {
16623 Assert(OidIsValid(oldAccessMethodId) &&
16624 OidIsValid(rd_rel->relam));
16625
16626 /* Both are valid, so update the dependency */
16627 changeDependencyFor(RelationRelationId, reloid,
16628 AccessMethodRelationId,
16629 oldAccessMethodId, rd_rel->relam);
16630 }
16631
16632 /* make the relam and dependency changes visible */
16634
16635 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16636
16637 heap_freetuple(tuple);
16638 table_close(pg_class, RowExclusiveLock);
16639}
16640
16641/*
16642 * ALTER TABLE SET TABLESPACE
16643 */
16644static void
16645ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16646{
16647 Oid tablespaceId;
16648
16649 /* Check that the tablespace exists */
16650 tablespaceId = get_tablespace_oid(tablespacename, false);
16651
16652 /* Check permissions except when moving to database's default */
16653 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16654 {
16655 AclResult aclresult;
16656
16657 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16658 if (aclresult != ACLCHECK_OK)
16659 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16660 }
16661
16662 /* Save info for Phase 3 to do the real work */
16663 if (OidIsValid(tab->newTableSpace))
16664 ereport(ERROR,
16665 (errcode(ERRCODE_SYNTAX_ERROR),
16666 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16667
16668 tab->newTableSpace = tablespaceId;
16669}
16670
16671/*
16672 * Set, reset, or replace reloptions.
16673 */
16674static void
16676 LOCKMODE lockmode)
16677{
16678 Oid relid;
16679 Relation pgclass;
16680 HeapTuple tuple;
16681 HeapTuple newtuple;
16682 Datum datum;
16683 Datum newOptions;
16684 Datum repl_val[Natts_pg_class];
16685 bool repl_null[Natts_pg_class];
16686 bool repl_repl[Natts_pg_class];
16687 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16688
16689 if (defList == NIL && operation != AT_ReplaceRelOptions)
16690 return; /* nothing to do */
16691
16692 pgclass = table_open(RelationRelationId, RowExclusiveLock);
16693
16694 /* Fetch heap tuple */
16695 relid = RelationGetRelid(rel);
16696 tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16697 if (!HeapTupleIsValid(tuple))
16698 elog(ERROR, "cache lookup failed for relation %u", relid);
16699
16700 if (operation == AT_ReplaceRelOptions)
16701 {
16702 /*
16703 * If we're supposed to replace the reloptions list, we just pretend
16704 * there were none before.
16705 */
16706 datum = (Datum) 0;
16707 }
16708 else
16709 {
16710 bool isnull;
16711
16712 /* Get the old reloptions */
16713 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16714 &isnull);
16715 if (isnull)
16716 datum = (Datum) 0;
16717 }
16718
16719 /* Generate new proposed reloptions (text array) */
16720 newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16721 operation == AT_ResetRelOptions);
16722
16723 /* Validate */
16724 switch (rel->rd_rel->relkind)
16725 {
16726 case RELKIND_RELATION:
16727 case RELKIND_MATVIEW:
16728 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16729 break;
16730 case RELKIND_PARTITIONED_TABLE:
16731 (void) partitioned_table_reloptions(newOptions, true);
16732 break;
16733 case RELKIND_VIEW:
16734 (void) view_reloptions(newOptions, true);
16735 break;
16736 case RELKIND_INDEX:
16737 case RELKIND_PARTITIONED_INDEX:
16738 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16739 break;
16740 case RELKIND_TOASTVALUE:
16741 /* fall through to error -- shouldn't ever get here */
16742 default:
16743 ereport(ERROR,
16744 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16745 errmsg("cannot set options for relation \"%s\"",
16747 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16748 break;
16749 }
16750
16751 /* Special-case validation of view options */
16752 if (rel->rd_rel->relkind == RELKIND_VIEW)
16753 {
16754 Query *view_query = get_view_query(rel);
16755 List *view_options = untransformRelOptions(newOptions);
16756 ListCell *cell;
16757 bool check_option = false;
16758
16759 foreach(cell, view_options)
16760 {
16761 DefElem *defel = (DefElem *) lfirst(cell);
16762
16763 if (strcmp(defel->defname, "check_option") == 0)
16764 check_option = true;
16765 }
16766
16767 /*
16768 * If the check option is specified, look to see if the view is
16769 * actually auto-updatable or not.
16770 */
16771 if (check_option)
16772 {
16773 const char *view_updatable_error =
16774 view_query_is_auto_updatable(view_query, true);
16775
16776 if (view_updatable_error)
16777 ereport(ERROR,
16778 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16779 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16780 errhint("%s", _(view_updatable_error))));
16781 }
16782 }
16783
16784 /*
16785 * All we need do here is update the pg_class row; the new options will be
16786 * propagated into relcaches during post-commit cache inval.
16787 */
16788 memset(repl_val, 0, sizeof(repl_val));
16789 memset(repl_null, false, sizeof(repl_null));
16790 memset(repl_repl, false, sizeof(repl_repl));
16791
16792 if (newOptions != (Datum) 0)
16793 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16794 else
16795 repl_null[Anum_pg_class_reloptions - 1] = true;
16796
16797 repl_repl[Anum_pg_class_reloptions - 1] = true;
16798
16799 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16800 repl_val, repl_null, repl_repl);
16801
16802 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16803 UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16804
16805 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16806
16807 heap_freetuple(newtuple);
16808
16809 ReleaseSysCache(tuple);
16810
16811 /* repeat the whole exercise for the toast table, if there's one */
16812 if (OidIsValid(rel->rd_rel->reltoastrelid))
16813 {
16814 Relation toastrel;
16815 Oid toastid = rel->rd_rel->reltoastrelid;
16816
16817 toastrel = table_open(toastid, lockmode);
16818
16819 /* Fetch heap tuple */
16820 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16821 if (!HeapTupleIsValid(tuple))
16822 elog(ERROR, "cache lookup failed for relation %u", toastid);
16823
16824 if (operation == AT_ReplaceRelOptions)
16825 {
16826 /*
16827 * If we're supposed to replace the reloptions list, we just
16828 * pretend there were none before.
16829 */
16830 datum = (Datum) 0;
16831 }
16832 else
16833 {
16834 bool isnull;
16835
16836 /* Get the old reloptions */
16837 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16838 &isnull);
16839 if (isnull)
16840 datum = (Datum) 0;
16841 }
16842
16843 newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16844 false, operation == AT_ResetRelOptions);
16845
16846 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16847
16848 memset(repl_val, 0, sizeof(repl_val));
16849 memset(repl_null, false, sizeof(repl_null));
16850 memset(repl_repl, false, sizeof(repl_repl));
16851
16852 if (newOptions != (Datum) 0)
16853 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16854 else
16855 repl_null[Anum_pg_class_reloptions - 1] = true;
16856
16857 repl_repl[Anum_pg_class_reloptions - 1] = true;
16858
16859 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16860 repl_val, repl_null, repl_repl);
16861
16862 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16863
16864 InvokeObjectPostAlterHookArg(RelationRelationId,
16865 RelationGetRelid(toastrel), 0,
16866 InvalidOid, true);
16867
16868 heap_freetuple(newtuple);
16869
16870 ReleaseSysCache(tuple);
16871
16872 table_close(toastrel, NoLock);
16873 }
16874
16875 table_close(pgclass, RowExclusiveLock);
16876}
16877
16878/*
16879 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16880 * rewriting to be done, so we just want to copy the data as fast as possible.
16881 */
16882static void
16883ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16884{
16885 Relation rel;
16886 Oid reltoastrelid;
16887 RelFileNumber newrelfilenumber;
16888 RelFileLocator newrlocator;
16889 List *reltoastidxids = NIL;
16890 ListCell *lc;
16891
16892 /*
16893 * Need lock here in case we are recursing to toast table or index
16894 */
16895 rel = relation_open(tableOid, lockmode);
16896
16897 /* Check first if relation can be moved to new tablespace */
16898 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16899 {
16900 InvokeObjectPostAlterHook(RelationRelationId,
16901 RelationGetRelid(rel), 0);
16902 relation_close(rel, NoLock);
16903 return;
16904 }
16905
16906 reltoastrelid = rel->rd_rel->reltoastrelid;
16907 /* Fetch the list of indexes on toast relation if necessary */
16908 if (OidIsValid(reltoastrelid))
16909 {
16910 Relation toastRel = relation_open(reltoastrelid, lockmode);
16911
16912 reltoastidxids = RelationGetIndexList(toastRel);
16913 relation_close(toastRel, lockmode);
16914 }
16915
16916 /*
16917 * Relfilenumbers are not unique in databases across tablespaces, so we
16918 * need to allocate a new one in the new tablespace.
16919 */
16920 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16921 rel->rd_rel->relpersistence);
16922
16923 /* Open old and new relation */
16924 newrlocator = rel->rd_locator;
16925 newrlocator.relNumber = newrelfilenumber;
16926 newrlocator.spcOid = newTableSpace;
16927
16928 /* hand off to AM to actually create new rel storage and copy the data */
16929 if (rel->rd_rel->relkind == RELKIND_INDEX)
16930 {
16931 index_copy_data(rel, newrlocator);
16932 }
16933 else
16934 {
16935 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16936 table_relation_copy_data(rel, &newrlocator);
16937 }
16938
16939 /*
16940 * Update the pg_class row.
16941 *
16942 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16943 * executed on pg_class or its indexes (the above copy wouldn't contain
16944 * the updated pg_class entry), but that's forbidden with
16945 * CheckRelationTableSpaceMove().
16946 */
16947 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16948
16949 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16950
16952
16953 relation_close(rel, NoLock);
16954
16955 /* Make sure the reltablespace change is visible */
16957
16958 /* Move associated toast relation and/or indexes, too */
16959 if (OidIsValid(reltoastrelid))
16960 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16961 foreach(lc, reltoastidxids)
16962 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16963
16964 /* Clean up */
16965 list_free(reltoastidxids);
16966}
16967
16968/*
16969 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16970 * storage that have an interest in preserving tablespace.
16971 *
16972 * Since these have no storage the tablespace can be updated with a simple
16973 * metadata only operation to update the tablespace.
16974 */
16975static void
16977{
16978 /*
16979 * Shouldn't be called on relations having storage; these are processed in
16980 * phase 3.
16981 */
16982 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16983
16984 /* check if relation can be moved to its new tablespace */
16985 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16986 {
16987 InvokeObjectPostAlterHook(RelationRelationId,
16988 RelationGetRelid(rel),
16989 0);
16990 return;
16991 }
16992
16993 /* Update can be done, so change reltablespace */
16994 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16995
16996 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16997
16998 /* Make sure the reltablespace change is visible */
17000}
17001
17002/*
17003 * Alter Table ALL ... SET TABLESPACE
17004 *
17005 * Allows a user to move all objects of some type in a given tablespace in the
17006 * current database to another tablespace. Objects can be chosen based on the
17007 * owner of the object also, to allow users to move only their objects.
17008 * The user must have CREATE rights on the new tablespace, as usual. The main
17009 * permissions handling is done by the lower-level table move function.
17010 *
17011 * All to-be-moved objects are locked first. If NOWAIT is specified and the
17012 * lock can't be acquired then we ereport(ERROR).
17013 */
17014Oid
17016{
17017 List *relations = NIL;
17018 ListCell *l;
17019 ScanKeyData key[1];
17020 Relation rel;
17021 TableScanDesc scan;
17022 HeapTuple tuple;
17023 Oid orig_tablespaceoid;
17024 Oid new_tablespaceoid;
17025 List *role_oids = roleSpecsToIds(stmt->roles);
17026
17027 /* Ensure we were not asked to move something we can't */
17028 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17029 stmt->objtype != OBJECT_MATVIEW)
17030 ereport(ERROR,
17031 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17032 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17033
17034 /* Get the orig and new tablespace OIDs */
17035 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17036 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17037
17038 /* Can't move shared relations in to or out of pg_global */
17039 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17040 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
17041 new_tablespaceoid == GLOBALTABLESPACE_OID)
17042 ereport(ERROR,
17043 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17044 errmsg("cannot move relations in to or out of pg_global tablespace")));
17045
17046 /*
17047 * Must have CREATE rights on the new tablespace, unless it is the
17048 * database default tablespace (which all users implicitly have CREATE
17049 * rights on).
17050 */
17051 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
17052 {
17053 AclResult aclresult;
17054
17055 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
17056 ACL_CREATE);
17057 if (aclresult != ACLCHECK_OK)
17059 get_tablespace_name(new_tablespaceoid));
17060 }
17061
17062 /*
17063 * Now that the checks are done, check if we should set either to
17064 * InvalidOid because it is our database's default tablespace.
17065 */
17066 if (orig_tablespaceoid == MyDatabaseTableSpace)
17067 orig_tablespaceoid = InvalidOid;
17068
17069 if (new_tablespaceoid == MyDatabaseTableSpace)
17070 new_tablespaceoid = InvalidOid;
17071
17072 /* no-op */
17073 if (orig_tablespaceoid == new_tablespaceoid)
17074 return new_tablespaceoid;
17075
17076 /*
17077 * Walk the list of objects in the tablespace and move them. This will
17078 * only find objects in our database, of course.
17079 */
17080 ScanKeyInit(&key[0],
17081 Anum_pg_class_reltablespace,
17082 BTEqualStrategyNumber, F_OIDEQ,
17083 ObjectIdGetDatum(orig_tablespaceoid));
17084
17085 rel = table_open(RelationRelationId, AccessShareLock);
17086 scan = table_beginscan_catalog(rel, 1, key);
17087 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17088 {
17089 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17090 Oid relOid = relForm->oid;
17091
17092 /*
17093 * Do not move objects in pg_catalog as part of this, if an admin
17094 * really wishes to do so, they can issue the individual ALTER
17095 * commands directly.
17096 *
17097 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17098 * (TOAST will be moved with the main table).
17099 */
17100 if (IsCatalogNamespace(relForm->relnamespace) ||
17101 relForm->relisshared ||
17102 isAnyTempNamespace(relForm->relnamespace) ||
17103 IsToastNamespace(relForm->relnamespace))
17104 continue;
17105
17106 /* Only move the object type requested */
17107 if ((stmt->objtype == OBJECT_TABLE &&
17108 relForm->relkind != RELKIND_RELATION &&
17109 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17110 (stmt->objtype == OBJECT_INDEX &&
17111 relForm->relkind != RELKIND_INDEX &&
17112 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17113 (stmt->objtype == OBJECT_MATVIEW &&
17114 relForm->relkind != RELKIND_MATVIEW))
17115 continue;
17116
17117 /* Check if we are only moving objects owned by certain roles */
17118 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17119 continue;
17120
17121 /*
17122 * Handle permissions-checking here since we are locking the tables
17123 * and also to avoid doing a bunch of work only to fail part-way. Note
17124 * that permissions will also be checked by AlterTableInternal().
17125 *
17126 * Caller must be considered an owner on the table to move it.
17127 */
17128 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17130 NameStr(relForm->relname));
17131
17132 if (stmt->nowait &&
17134 ereport(ERROR,
17135 (errcode(ERRCODE_OBJECT_IN_USE),
17136 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17137 get_namespace_name(relForm->relnamespace),
17138 NameStr(relForm->relname))));
17139 else
17141
17142 /* Add to our list of objects to move */
17143 relations = lappend_oid(relations, relOid);
17144 }
17145
17146 table_endscan(scan);
17148
17149 if (relations == NIL)
17151 (errcode(ERRCODE_NO_DATA_FOUND),
17152 errmsg("no matching relations in tablespace \"%s\" found",
17153 orig_tablespaceoid == InvalidOid ? "(database default)" :
17154 get_tablespace_name(orig_tablespaceoid))));
17155
17156 /* Everything is locked, loop through and move all of the relations. */
17157 foreach(l, relations)
17158 {
17159 List *cmds = NIL;
17161
17163 cmd->name = stmt->new_tablespacename;
17164
17165 cmds = lappend(cmds, cmd);
17166
17168 /* OID is set by AlterTableInternal */
17169 AlterTableInternal(lfirst_oid(l), cmds, false);
17171 }
17172
17173 return new_tablespaceoid;
17174}
17175
17176static void
17178{
17179 SMgrRelation dstrel;
17180
17181 /*
17182 * Since we copy the file directly without looking at the shared buffers,
17183 * we'd better first flush out any pages of the source relation that are
17184 * in shared buffers. We assume no new changes will be made while we are
17185 * holding exclusive lock on the rel.
17186 */
17188
17189 /*
17190 * Create and copy all forks of the relation, and schedule unlinking of
17191 * old physical files.
17192 *
17193 * NOTE: any conflict in relfilenumber value will be caught in
17194 * RelationCreateStorage().
17195 */
17196 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17197
17198 /* copy main fork */
17200 rel->rd_rel->relpersistence);
17201
17202 /* copy those extra forks that exist */
17203 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17204 forkNum <= MAX_FORKNUM; forkNum++)
17205 {
17206 if (smgrexists(RelationGetSmgr(rel), forkNum))
17207 {
17208 smgrcreate(dstrel, forkNum, false);
17209
17210 /*
17211 * WAL log creation if the relation is persistent, or this is the
17212 * init fork of an unlogged relation.
17213 */
17214 if (RelationIsPermanent(rel) ||
17215 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17216 forkNum == INIT_FORKNUM))
17217 log_smgrcreate(&newrlocator, forkNum);
17218 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17219 rel->rd_rel->relpersistence);
17220 }
17221 }
17222
17223 /* drop old relation, and close new one */
17225 smgrclose(dstrel);
17226}
17227
17228/*
17229 * ALTER TABLE ENABLE/DISABLE TRIGGER
17230 *
17231 * We just pass this off to trigger.c.
17232 */
17233static void
17234ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17235 char fires_when, bool skip_system, bool recurse,
17236 LOCKMODE lockmode)
17237{
17238 EnableDisableTrigger(rel, trigname, InvalidOid,
17239 fires_when, skip_system, recurse,
17240 lockmode);
17241
17242 InvokeObjectPostAlterHook(RelationRelationId,
17243 RelationGetRelid(rel), 0);
17244}
17245
17246/*
17247 * ALTER TABLE ENABLE/DISABLE RULE
17248 *
17249 * We just pass this off to rewriteDefine.c.
17250 */
17251static void
17252ATExecEnableDisableRule(Relation rel, const char *rulename,
17253 char fires_when, LOCKMODE lockmode)
17254{
17255 EnableDisableRule(rel, rulename, fires_when);
17256
17257 InvokeObjectPostAlterHook(RelationRelationId,
17258 RelationGetRelid(rel), 0);
17259}
17260
17261/*
17262 * ALTER TABLE INHERIT
17263 *
17264 * Add a parent to the child's parents. This verifies that all the columns and
17265 * check constraints of the parent appear in the child and that they have the
17266 * same data types and expressions.
17267 */
17268static void
17270{
17271 if (child_rel->rd_rel->reloftype)
17272 ereport(ERROR,
17273 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17274 errmsg("cannot change inheritance of typed table")));
17275
17276 if (child_rel->rd_rel->relispartition)
17277 ereport(ERROR,
17278 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17279 errmsg("cannot change inheritance of a partition")));
17280
17281 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17282 ereport(ERROR,
17283 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17284 errmsg("cannot change inheritance of partitioned table")));
17285}
17286
17287/*
17288 * Return the address of the new parent relation.
17289 */
17290static ObjectAddress
17291ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17292{
17293 Relation parent_rel;
17294 List *children;
17295 ObjectAddress address;
17296 const char *trigger_name;
17297
17298 /*
17299 * A self-exclusive lock is needed here. See the similar case in
17300 * MergeAttributes() for a full explanation.
17301 */
17302 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17303
17304 /*
17305 * Must be owner of both parent and child -- child was checked by
17306 * ATSimplePermissions call in ATPrepCmd
17307 */
17310
17311 /* Permanent rels cannot inherit from temporary ones */
17312 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17313 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17314 ereport(ERROR,
17315 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17316 errmsg("cannot inherit from temporary relation \"%s\"",
17317 RelationGetRelationName(parent_rel))));
17318
17319 /* If parent rel is temp, it must belong to this session */
17320 if (RELATION_IS_OTHER_TEMP(parent_rel))
17321 ereport(ERROR,
17322 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17323 errmsg("cannot inherit from temporary relation of another session")));
17324
17325 /* Ditto for the child */
17326 if (RELATION_IS_OTHER_TEMP(child_rel))
17327 ereport(ERROR,
17328 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17329 errmsg("cannot inherit to temporary relation of another session")));
17330
17331 /* Prevent partitioned tables from becoming inheritance parents */
17332 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17333 ereport(ERROR,
17334 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17335 errmsg("cannot inherit from partitioned table \"%s\"",
17336 parent->relname)));
17337
17338 /* Likewise for partitions */
17339 if (parent_rel->rd_rel->relispartition)
17340 ereport(ERROR,
17341 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17342 errmsg("cannot inherit from a partition")));
17343
17344 /*
17345 * Prevent circularity by seeing if proposed parent inherits from child.
17346 * (In particular, this disallows making a rel inherit from itself.)
17347 *
17348 * This is not completely bulletproof because of race conditions: in
17349 * multi-level inheritance trees, someone else could concurrently be
17350 * making another inheritance link that closes the loop but does not join
17351 * either of the rels we have locked. Preventing that seems to require
17352 * exclusive locks on the entire inheritance tree, which is a cure worse
17353 * than the disease. find_all_inheritors() will cope with circularity
17354 * anyway, so don't sweat it too much.
17355 *
17356 * We use weakest lock we can on child's children, namely AccessShareLock.
17357 */
17358 children = find_all_inheritors(RelationGetRelid(child_rel),
17359 AccessShareLock, NULL);
17360
17361 if (list_member_oid(children, RelationGetRelid(parent_rel)))
17362 ereport(ERROR,
17363 (errcode(ERRCODE_DUPLICATE_TABLE),
17364 errmsg("circular inheritance not allowed"),
17365 errdetail("\"%s\" is already a child of \"%s\".",
17366 parent->relname,
17367 RelationGetRelationName(child_rel))));
17368
17369 /*
17370 * If child_rel has row-level triggers with transition tables, we
17371 * currently don't allow it to become an inheritance child. See also
17372 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17373 */
17374 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17375 if (trigger_name != NULL)
17376 ereport(ERROR,
17377 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17378 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17379 trigger_name, RelationGetRelationName(child_rel)),
17380 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17381
17382 /* OK to create inheritance */
17383 CreateInheritance(child_rel, parent_rel, false);
17384
17385 ObjectAddressSet(address, RelationRelationId,
17386 RelationGetRelid(parent_rel));
17387
17388 /* keep our lock on the parent relation until commit */
17389 table_close(parent_rel, NoLock);
17390
17391 return address;
17392}
17393
17394/*
17395 * CreateInheritance
17396 * Catalog manipulation portion of creating inheritance between a child
17397 * table and a parent table.
17398 *
17399 * Common to ATExecAddInherit() and ATExecAttachPartition().
17400 */
17401static void
17402CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17403{
17404 Relation catalogRelation;
17405 SysScanDesc scan;
17407 HeapTuple inheritsTuple;
17408 int32 inhseqno;
17409
17410 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17411 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17412
17413 /*
17414 * Check for duplicates in the list of parents, and determine the highest
17415 * inhseqno already present; we'll use the next one for the new parent.
17416 * Also, if proposed child is a partition, it cannot already be
17417 * inheriting.
17418 *
17419 * Note: we do not reject the case where the child already inherits from
17420 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17421 */
17423 Anum_pg_inherits_inhrelid,
17424 BTEqualStrategyNumber, F_OIDEQ,
17426 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17427 true, NULL, 1, &key);
17428
17429 /* inhseqno sequences start at 1 */
17430 inhseqno = 0;
17431 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17432 {
17433 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17434
17435 if (inh->inhparent == RelationGetRelid(parent_rel))
17436 ereport(ERROR,
17437 (errcode(ERRCODE_DUPLICATE_TABLE),
17438 errmsg("relation \"%s\" would be inherited from more than once",
17439 RelationGetRelationName(parent_rel))));
17440
17441 if (inh->inhseqno > inhseqno)
17442 inhseqno = inh->inhseqno;
17443 }
17444 systable_endscan(scan);
17445
17446 /* Match up the columns and bump attinhcount as needed */
17447 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17448
17449 /* Match up the constraints and bump coninhcount as needed */
17450 MergeConstraintsIntoExisting(child_rel, parent_rel);
17451
17452 /*
17453 * OK, it looks valid. Make the catalog entries that show inheritance.
17454 */
17456 RelationGetRelid(parent_rel),
17457 inhseqno + 1,
17458 catalogRelation,
17459 parent_rel->rd_rel->relkind ==
17460 RELKIND_PARTITIONED_TABLE);
17461
17462 /* Now we're done with pg_inherits */
17463 table_close(catalogRelation, RowExclusiveLock);
17464}
17465
17466/*
17467 * Obtain the source-text form of the constraint expression for a check
17468 * constraint, given its pg_constraint tuple
17469 */
17470static char *
17472{
17474 bool isnull;
17475 Datum attr;
17476 Datum expr;
17477
17478 con = (Form_pg_constraint) GETSTRUCT(contup);
17479 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17480 if (isnull)
17481 elog(ERROR, "null conbin for constraint %u", con->oid);
17482
17483 expr = DirectFunctionCall2(pg_get_expr, attr,
17484 ObjectIdGetDatum(con->conrelid));
17485 return TextDatumGetCString(expr);
17486}
17487
17488/*
17489 * Determine whether two check constraints are functionally equivalent
17490 *
17491 * The test we apply is to see whether they reverse-compile to the same
17492 * source string. This insulates us from issues like whether attributes
17493 * have the same physical column numbers in parent and child relations.
17494 *
17495 * Note that we ignore enforceability as there are cases where constraints
17496 * with differing enforceability are allowed.
17497 */
17498static bool
17500{
17503
17504 if (acon->condeferrable != bcon->condeferrable ||
17505 acon->condeferred != bcon->condeferred ||
17506 strcmp(decompile_conbin(a, tupleDesc),
17507 decompile_conbin(b, tupleDesc)) != 0)
17508 return false;
17509 else
17510 return true;
17511}
17512
17513/*
17514 * Check columns in child table match up with columns in parent, and increment
17515 * their attinhcount.
17516 *
17517 * Called by CreateInheritance
17518 *
17519 * Currently all parent columns must be found in child. Missing columns are an
17520 * error. One day we might consider creating new columns like CREATE TABLE
17521 * does. However, that is widely unpopular --- in the common use case of
17522 * partitioned tables it's a foot-gun.
17523 *
17524 * The data type must match exactly. If the parent column is NOT NULL then
17525 * the child must be as well. Defaults are not compared, however.
17526 */
17527static void
17528MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17529{
17530 Relation attrrel;
17531 TupleDesc parent_desc;
17532
17533 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17534 parent_desc = RelationGetDescr(parent_rel);
17535
17536 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17537 {
17538 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17539 char *parent_attname = NameStr(parent_att->attname);
17540 HeapTuple tuple;
17541
17542 /* Ignore dropped columns in the parent. */
17543 if (parent_att->attisdropped)
17544 continue;
17545
17546 /* Find same column in child (matching on column name). */
17547 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17548 if (HeapTupleIsValid(tuple))
17549 {
17550 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17551
17552 if (parent_att->atttypid != child_att->atttypid ||
17553 parent_att->atttypmod != child_att->atttypmod)
17554 ereport(ERROR,
17555 (errcode(ERRCODE_DATATYPE_MISMATCH),
17556 errmsg("child table \"%s\" has different type for column \"%s\"",
17557 RelationGetRelationName(child_rel), parent_attname)));
17558
17559 if (parent_att->attcollation != child_att->attcollation)
17560 ereport(ERROR,
17561 (errcode(ERRCODE_COLLATION_MISMATCH),
17562 errmsg("child table \"%s\" has different collation for column \"%s\"",
17563 RelationGetRelationName(child_rel), parent_attname)));
17564
17565 /*
17566 * If the parent has a not-null constraint that's not NO INHERIT,
17567 * make sure the child has one too.
17568 *
17569 * Other constraints are checked elsewhere.
17570 */
17571 if (parent_att->attnotnull && !child_att->attnotnull)
17572 {
17573 HeapTuple contup;
17574
17575 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17576 parent_att->attnum);
17577 if (HeapTupleIsValid(contup) &&
17578 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17579 ereport(ERROR,
17580 errcode(ERRCODE_DATATYPE_MISMATCH),
17581 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17582 parent_attname, RelationGetRelationName(child_rel)));
17583 }
17584
17585 /*
17586 * Child column must be generated if and only if parent column is.
17587 */
17588 if (parent_att->attgenerated && !child_att->attgenerated)
17589 ereport(ERROR,
17590 (errcode(ERRCODE_DATATYPE_MISMATCH),
17591 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17592 if (child_att->attgenerated && !parent_att->attgenerated)
17593 ereport(ERROR,
17594 (errcode(ERRCODE_DATATYPE_MISMATCH),
17595 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17596
17597 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17598 ereport(ERROR,
17599 (errcode(ERRCODE_DATATYPE_MISMATCH),
17600 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17601 errdetail("Parent column is %s, child column is %s.",
17602 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17603 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17604
17605 /*
17606 * Regular inheritance children are independent enough not to
17607 * inherit identity columns. But partitions are integral part of
17608 * a partitioned table and inherit identity column.
17609 */
17610 if (ispartition)
17611 child_att->attidentity = parent_att->attidentity;
17612
17613 /*
17614 * OK, bump the child column's inheritance count. (If we fail
17615 * later on, this change will just roll back.)
17616 */
17617 if (pg_add_s16_overflow(child_att->attinhcount, 1,
17618 &child_att->attinhcount))
17619 ereport(ERROR,
17620 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17621 errmsg("too many inheritance parents"));
17622
17623 /*
17624 * In case of partitions, we must enforce that value of attislocal
17625 * is same in all partitions. (Note: there are only inherited
17626 * attributes in partitions)
17627 */
17628 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17629 {
17630 Assert(child_att->attinhcount == 1);
17631 child_att->attislocal = false;
17632 }
17633
17634 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17635 heap_freetuple(tuple);
17636 }
17637 else
17638 {
17639 ereport(ERROR,
17640 (errcode(ERRCODE_DATATYPE_MISMATCH),
17641 errmsg("child table is missing column \"%s\"", parent_attname)));
17642 }
17643 }
17644
17645 table_close(attrrel, RowExclusiveLock);
17646}
17647
17648/*
17649 * Check constraints in child table match up with constraints in parent,
17650 * and increment their coninhcount.
17651 *
17652 * Constraints that are marked ONLY in the parent are ignored.
17653 *
17654 * Called by CreateInheritance
17655 *
17656 * Currently all constraints in parent must be present in the child. One day we
17657 * may consider adding new constraints like CREATE TABLE does.
17658 *
17659 * XXX This is O(N^2) which may be an issue with tables with hundreds of
17660 * constraints. As long as tables have more like 10 constraints it shouldn't be
17661 * a problem though. Even 100 constraints ought not be the end of the world.
17662 *
17663 * XXX See MergeWithExistingConstraint too if you change this code.
17664 */
17665static void
17667{
17668 Relation constraintrel;
17669 SysScanDesc parent_scan;
17670 ScanKeyData parent_key;
17671 HeapTuple parent_tuple;
17672 Oid parent_relid = RelationGetRelid(parent_rel);
17673 AttrMap *attmap;
17674
17675 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17676
17677 /* Outer loop scans through the parent's constraint definitions */
17678 ScanKeyInit(&parent_key,
17679 Anum_pg_constraint_conrelid,
17680 BTEqualStrategyNumber, F_OIDEQ,
17681 ObjectIdGetDatum(parent_relid));
17682 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17683 true, NULL, 1, &parent_key);
17684
17685 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17686 RelationGetDescr(child_rel),
17687 true);
17688
17689 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17690 {
17691 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17692 SysScanDesc child_scan;
17693 ScanKeyData child_key;
17694 HeapTuple child_tuple;
17695 AttrNumber parent_attno;
17696 bool found = false;
17697
17698 if (parent_con->contype != CONSTRAINT_CHECK &&
17699 parent_con->contype != CONSTRAINT_NOTNULL)
17700 continue;
17701
17702 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17703 if (parent_con->connoinherit)
17704 continue;
17705
17706 if (parent_con->contype == CONSTRAINT_NOTNULL)
17707 parent_attno = extractNotNullColumn(parent_tuple);
17708 else
17709 parent_attno = InvalidAttrNumber;
17710
17711 /* Search for a child constraint matching this one */
17712 ScanKeyInit(&child_key,
17713 Anum_pg_constraint_conrelid,
17714 BTEqualStrategyNumber, F_OIDEQ,
17716 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17717 true, NULL, 1, &child_key);
17718
17719 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17720 {
17721 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17722 HeapTuple child_copy;
17723
17724 if (child_con->contype != parent_con->contype)
17725 continue;
17726
17727 /*
17728 * CHECK constraint are matched by constraint name, NOT NULL ones
17729 * by attribute number.
17730 */
17731 if (child_con->contype == CONSTRAINT_CHECK)
17732 {
17733 if (strcmp(NameStr(parent_con->conname),
17734 NameStr(child_con->conname)) != 0)
17735 continue;
17736 }
17737 else if (child_con->contype == CONSTRAINT_NOTNULL)
17738 {
17739 Form_pg_attribute parent_attr;
17740 Form_pg_attribute child_attr;
17741 AttrNumber child_attno;
17742
17743 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17744 child_attno = extractNotNullColumn(child_tuple);
17745 if (parent_attno != attmap->attnums[child_attno - 1])
17746 continue;
17747
17748 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17749 /* there shouldn't be constraints on dropped columns */
17750 if (parent_attr->attisdropped || child_attr->attisdropped)
17751 elog(ERROR, "found not-null constraint on dropped columns");
17752 }
17753
17754 if (child_con->contype == CONSTRAINT_CHECK &&
17755 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17756 ereport(ERROR,
17757 (errcode(ERRCODE_DATATYPE_MISMATCH),
17758 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17759 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17760
17761 /*
17762 * If the child constraint is "no inherit" then cannot merge
17763 */
17764 if (child_con->connoinherit)
17765 ereport(ERROR,
17766 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17767 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17768 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17769
17770 /*
17771 * If the child constraint is "not valid" then cannot merge with a
17772 * valid parent constraint
17773 */
17774 if (parent_con->convalidated && child_con->conenforced &&
17775 !child_con->convalidated)
17776 ereport(ERROR,
17777 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17778 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17779 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17780
17781 /*
17782 * A NOT ENFORCED child constraint cannot be merged with an
17783 * ENFORCED parent constraint. However, the reverse is allowed,
17784 * where the child constraint is ENFORCED.
17785 */
17786 if (parent_con->conenforced && !child_con->conenforced)
17787 ereport(ERROR,
17788 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17789 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17790 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17791
17792 /*
17793 * OK, bump the child constraint's inheritance count. (If we fail
17794 * later on, this change will just roll back.)
17795 */
17796 child_copy = heap_copytuple(child_tuple);
17797 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17798
17799 if (pg_add_s16_overflow(child_con->coninhcount, 1,
17800 &child_con->coninhcount))
17801 ereport(ERROR,
17802 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17803 errmsg("too many inheritance parents"));
17804
17805 /*
17806 * In case of partitions, an inherited constraint must be
17807 * inherited only once since it cannot have multiple parents and
17808 * it is never considered local.
17809 */
17810 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17811 {
17812 Assert(child_con->coninhcount == 1);
17813 child_con->conislocal = false;
17814 }
17815
17816 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17817 heap_freetuple(child_copy);
17818
17819 found = true;
17820 break;
17821 }
17822
17823 systable_endscan(child_scan);
17824
17825 if (!found)
17826 {
17827 if (parent_con->contype == CONSTRAINT_NOTNULL)
17828 ereport(ERROR,
17829 errcode(ERRCODE_DATATYPE_MISMATCH),
17830 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17831 get_attname(parent_relid,
17832 extractNotNullColumn(parent_tuple),
17833 false),
17834 RelationGetRelationName(child_rel)));
17835
17836 ereport(ERROR,
17837 (errcode(ERRCODE_DATATYPE_MISMATCH),
17838 errmsg("child table is missing constraint \"%s\"",
17839 NameStr(parent_con->conname))));
17840 }
17841 }
17842
17843 systable_endscan(parent_scan);
17844 table_close(constraintrel, RowExclusiveLock);
17845}
17846
17847/*
17848 * ALTER TABLE NO INHERIT
17849 *
17850 * Return value is the address of the relation that is no longer parent.
17851 */
17852static ObjectAddress
17854{
17855 ObjectAddress address;
17856 Relation parent_rel;
17857
17858 if (rel->rd_rel->relispartition)
17859 ereport(ERROR,
17860 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17861 errmsg("cannot change inheritance of a partition")));
17862
17863 /*
17864 * AccessShareLock on the parent is probably enough, seeing that DROP
17865 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17866 * be inspecting the parent's schema.
17867 */
17868 parent_rel = table_openrv(parent, AccessShareLock);
17869
17870 /*
17871 * We don't bother to check ownership of the parent table --- ownership of
17872 * the child is presumed enough rights.
17873 */
17874
17875 /* Off to RemoveInheritance() where most of the work happens */
17876 RemoveInheritance(rel, parent_rel, false);
17877
17878 ObjectAddressSet(address, RelationRelationId,
17879 RelationGetRelid(parent_rel));
17880
17881 /* keep our lock on the parent relation until commit */
17882 table_close(parent_rel, NoLock);
17883
17884 return address;
17885}
17886
17887/*
17888 * MarkInheritDetached
17889 *
17890 * Set inhdetachpending for a partition, for ATExecDetachPartition
17891 * in concurrent mode. While at it, verify that no other partition is
17892 * already pending detach.
17893 */
17894static void
17896{
17897 Relation catalogRelation;
17898 SysScanDesc scan;
17900 HeapTuple inheritsTuple;
17901 bool found = false;
17902
17903 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17904
17905 /*
17906 * Find pg_inherits entries by inhparent. (We need to scan them all in
17907 * order to verify that no other partition is pending detach.)
17908 */
17909 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17911 Anum_pg_inherits_inhparent,
17912 BTEqualStrategyNumber, F_OIDEQ,
17913 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17914 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17915 true, NULL, 1, &key);
17916
17917 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17918 {
17919 Form_pg_inherits inhForm;
17920
17921 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17922 if (inhForm->inhdetachpending)
17923 ereport(ERROR,
17924 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17925 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17926 get_rel_name(inhForm->inhrelid),
17927 get_namespace_name(parent_rel->rd_rel->relnamespace),
17928 RelationGetRelationName(parent_rel)),
17929 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17930
17931 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17932 {
17933 HeapTuple newtup;
17934
17935 newtup = heap_copytuple(inheritsTuple);
17936 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17937
17938 CatalogTupleUpdate(catalogRelation,
17939 &inheritsTuple->t_self,
17940 newtup);
17941 found = true;
17942 heap_freetuple(newtup);
17943 /* keep looking, to ensure we catch others pending detach */
17944 }
17945 }
17946
17947 /* Done */
17948 systable_endscan(scan);
17949 table_close(catalogRelation, RowExclusiveLock);
17950
17951 if (!found)
17952 ereport(ERROR,
17954 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17955 RelationGetRelationName(child_rel),
17956 RelationGetRelationName(parent_rel))));
17957}
17958
17959/*
17960 * RemoveInheritance
17961 *
17962 * Drop a parent from the child's parents. This just adjusts the attinhcount
17963 * and attislocal of the columns and removes the pg_inherit and pg_depend
17964 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17965 *
17966 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17967 * up attislocal stays true, which means if a child is ever removed from a
17968 * parent then its columns will never be automatically dropped which may
17969 * surprise. But at least we'll never surprise by dropping columns someone
17970 * isn't expecting to be dropped which would actually mean data loss.
17971 *
17972 * coninhcount and conislocal for inherited constraints are adjusted in
17973 * exactly the same way.
17974 *
17975 * Common to ATExecDropInherit() and ATExecDetachPartition().
17976 */
17977static void
17978RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17979{
17980 Relation catalogRelation;
17981 SysScanDesc scan;
17982 ScanKeyData key[3];
17983 HeapTuple attributeTuple,
17984 constraintTuple;
17985 AttrMap *attmap;
17986 List *connames;
17987 List *nncolumns;
17988 bool found;
17989 bool is_partitioning;
17990
17991 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17992
17993 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17994 RelationGetRelid(parent_rel),
17995 expect_detached,
17996 RelationGetRelationName(child_rel));
17997 if (!found)
17998 {
17999 if (is_partitioning)
18000 ereport(ERROR,
18002 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18003 RelationGetRelationName(child_rel),
18004 RelationGetRelationName(parent_rel))));
18005 else
18006 ereport(ERROR,
18008 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18009 RelationGetRelationName(parent_rel),
18010 RelationGetRelationName(child_rel))));
18011 }
18012
18013 /*
18014 * Search through child columns looking for ones matching parent rel
18015 */
18016 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
18017 ScanKeyInit(&key[0],
18018 Anum_pg_attribute_attrelid,
18019 BTEqualStrategyNumber, F_OIDEQ,
18021 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
18022 true, NULL, 1, key);
18023 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
18024 {
18025 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
18026
18027 /* Ignore if dropped or not inherited */
18028 if (att->attisdropped)
18029 continue;
18030 if (att->attinhcount <= 0)
18031 continue;
18032
18034 NameStr(att->attname)))
18035 {
18036 /* Decrement inhcount and possibly set islocal to true */
18037 HeapTuple copyTuple = heap_copytuple(attributeTuple);
18038 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
18039
18040 copy_att->attinhcount--;
18041 if (copy_att->attinhcount == 0)
18042 copy_att->attislocal = true;
18043
18044 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18045 heap_freetuple(copyTuple);
18046 }
18047 }
18048 systable_endscan(scan);
18049 table_close(catalogRelation, RowExclusiveLock);
18050
18051 /*
18052 * Likewise, find inherited check and not-null constraints and disinherit
18053 * them. To do this, we first need a list of the names of the parent's
18054 * check constraints. (We cheat a bit by only checking for name matches,
18055 * assuming that the expressions will match.)
18056 *
18057 * For NOT NULL columns, we store column numbers to match, mapping them in
18058 * to the child rel's attribute numbers.
18059 */
18060 attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
18061 RelationGetDescr(parent_rel),
18062 false);
18063
18064 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
18065 ScanKeyInit(&key[0],
18066 Anum_pg_constraint_conrelid,
18067 BTEqualStrategyNumber, F_OIDEQ,
18068 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18069 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18070 true, NULL, 1, key);
18071
18072 connames = NIL;
18073 nncolumns = NIL;
18074
18075 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18076 {
18077 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18078
18079 if (con->connoinherit)
18080 continue;
18081
18082 if (con->contype == CONSTRAINT_CHECK)
18083 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18084 if (con->contype == CONSTRAINT_NOTNULL)
18085 {
18086 AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
18087
18088 nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18089 }
18090 }
18091
18092 systable_endscan(scan);
18093
18094 /* Now scan the child's constraints to find matches */
18095 ScanKeyInit(&key[0],
18096 Anum_pg_constraint_conrelid,
18097 BTEqualStrategyNumber, F_OIDEQ,
18099 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18100 true, NULL, 1, key);
18101
18102 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18103 {
18104 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18105 bool match = false;
18106
18107 /*
18108 * Match CHECK constraints by name, not-null constraints by column
18109 * number, and ignore all others.
18110 */
18111 if (con->contype == CONSTRAINT_CHECK)
18112 {
18113 foreach_ptr(char, chkname, connames)
18114 {
18115 if (con->contype == CONSTRAINT_CHECK &&
18116 strcmp(NameStr(con->conname), chkname) == 0)
18117 {
18118 match = true;
18119 connames = foreach_delete_current(connames, chkname);
18120 break;
18121 }
18122 }
18123 }
18124 else if (con->contype == CONSTRAINT_NOTNULL)
18125 {
18126 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18127
18128 foreach_int(prevattno, nncolumns)
18129 {
18130 if (prevattno == child_attno)
18131 {
18132 match = true;
18133 nncolumns = foreach_delete_current(nncolumns, prevattno);
18134 break;
18135 }
18136 }
18137 }
18138 else
18139 continue;
18140
18141 if (match)
18142 {
18143 /* Decrement inhcount and possibly set islocal to true */
18144 HeapTuple copyTuple = heap_copytuple(constraintTuple);
18145 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18146
18147 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18148 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18149 RelationGetRelid(child_rel), NameStr(copy_con->conname));
18150
18151 copy_con->coninhcount--;
18152 if (copy_con->coninhcount == 0)
18153 copy_con->conislocal = true;
18154
18155 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18156 heap_freetuple(copyTuple);
18157 }
18158 }
18159
18160 /* We should have matched all constraints */
18161 if (connames != NIL || nncolumns != NIL)
18162 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18163 list_length(connames) + list_length(nncolumns),
18164 RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18165
18166 systable_endscan(scan);
18167 table_close(catalogRelation, RowExclusiveLock);
18168
18170 RelationRelationId,
18171 RelationGetRelid(parent_rel),
18172 child_dependency_type(is_partitioning));
18173
18174 /*
18175 * Post alter hook of this inherits. Since object_access_hook doesn't take
18176 * multiple object identifiers, we relay oid of parent relation using
18177 * auxiliary_id argument.
18178 */
18179 InvokeObjectPostAlterHookArg(InheritsRelationId,
18180 RelationGetRelid(child_rel), 0,
18181 RelationGetRelid(parent_rel), false);
18182}
18183
18184/*
18185 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18186 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18187 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18188 * be TypeRelationId). There's no convenient way to do this, so go trawling
18189 * through pg_depend.
18190 */
18191static void
18192drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18193 DependencyType deptype)
18194{
18195 Relation catalogRelation;
18196 SysScanDesc scan;
18197 ScanKeyData key[3];
18198 HeapTuple depTuple;
18199
18200 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18201
18202 ScanKeyInit(&key[0],
18203 Anum_pg_depend_classid,
18204 BTEqualStrategyNumber, F_OIDEQ,
18205 ObjectIdGetDatum(RelationRelationId));
18206 ScanKeyInit(&key[1],
18207 Anum_pg_depend_objid,
18208 BTEqualStrategyNumber, F_OIDEQ,
18209 ObjectIdGetDatum(relid));
18210 ScanKeyInit(&key[2],
18211 Anum_pg_depend_objsubid,
18212 BTEqualStrategyNumber, F_INT4EQ,
18213 Int32GetDatum(0));
18214
18215 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18216 NULL, 3, key);
18217
18218 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18219 {
18220 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18221
18222 if (dep->refclassid == refclassid &&
18223 dep->refobjid == refobjid &&
18224 dep->refobjsubid == 0 &&
18225 dep->deptype == deptype)
18226 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18227 }
18228
18229 systable_endscan(scan);
18230 table_close(catalogRelation, RowExclusiveLock);
18231}
18232
18233/*
18234 * ALTER TABLE OF
18235 *
18236 * Attach a table to a composite type, as though it had been created with CREATE
18237 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18238 * subject table must not have inheritance parents. These restrictions ensure
18239 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18240 *
18241 * The address of the type is returned.
18242 */
18243static ObjectAddress
18244ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18245{
18246 Oid relid = RelationGetRelid(rel);
18247 Type typetuple;
18248 Form_pg_type typeform;
18249 Oid typeid;
18250 Relation inheritsRelation,
18251 relationRelation;
18252 SysScanDesc scan;
18254 AttrNumber table_attno,
18255 type_attno;
18256 TupleDesc typeTupleDesc,
18257 tableTupleDesc;
18258 ObjectAddress tableobj,
18259 typeobj;
18260 HeapTuple classtuple;
18261
18262 /* Validate the type. */
18263 typetuple = typenameType(NULL, ofTypename, NULL);
18264 check_of_type(typetuple);
18265 typeform = (Form_pg_type) GETSTRUCT(typetuple);
18266 typeid = typeform->oid;
18267
18268 /* Fail if the table has any inheritance parents. */
18269 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18271 Anum_pg_inherits_inhrelid,
18272 BTEqualStrategyNumber, F_OIDEQ,
18273 ObjectIdGetDatum(relid));
18274 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18275 true, NULL, 1, &key);
18277 ereport(ERROR,
18278 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18279 errmsg("typed tables cannot inherit")));
18280 systable_endscan(scan);
18281 table_close(inheritsRelation, AccessShareLock);
18282
18283 /*
18284 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18285 * require that the order also match. However, attnotnull need not match.
18286 */
18287 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18288 tableTupleDesc = RelationGetDescr(rel);
18289 table_attno = 1;
18290 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18291 {
18292 Form_pg_attribute type_attr,
18293 table_attr;
18294 const char *type_attname,
18295 *table_attname;
18296
18297 /* Get the next non-dropped type attribute. */
18298 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18299 if (type_attr->attisdropped)
18300 continue;
18301 type_attname = NameStr(type_attr->attname);
18302
18303 /* Get the next non-dropped table attribute. */
18304 do
18305 {
18306 if (table_attno > tableTupleDesc->natts)
18307 ereport(ERROR,
18308 (errcode(ERRCODE_DATATYPE_MISMATCH),
18309 errmsg("table is missing column \"%s\"",
18310 type_attname)));
18311 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18312 table_attno++;
18313 } while (table_attr->attisdropped);
18314 table_attname = NameStr(table_attr->attname);
18315
18316 /* Compare name. */
18317 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18318 ereport(ERROR,
18319 (errcode(ERRCODE_DATATYPE_MISMATCH),
18320 errmsg("table has column \"%s\" where type requires \"%s\"",
18321 table_attname, type_attname)));
18322
18323 /* Compare type. */
18324 if (table_attr->atttypid != type_attr->atttypid ||
18325 table_attr->atttypmod != type_attr->atttypmod ||
18326 table_attr->attcollation != type_attr->attcollation)
18327 ereport(ERROR,
18328 (errcode(ERRCODE_DATATYPE_MISMATCH),
18329 errmsg("table \"%s\" has different type for column \"%s\"",
18330 RelationGetRelationName(rel), type_attname)));
18331 }
18332 ReleaseTupleDesc(typeTupleDesc);
18333
18334 /* Any remaining columns at the end of the table had better be dropped. */
18335 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18336 {
18337 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18338 table_attno - 1);
18339
18340 if (!table_attr->attisdropped)
18341 ereport(ERROR,
18342 (errcode(ERRCODE_DATATYPE_MISMATCH),
18343 errmsg("table has extra column \"%s\"",
18344 NameStr(table_attr->attname))));
18345 }
18346
18347 /* If the table was already typed, drop the existing dependency. */
18348 if (rel->rd_rel->reloftype)
18349 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18351
18352 /* Record a dependency on the new type. */
18353 tableobj.classId = RelationRelationId;
18354 tableobj.objectId = relid;
18355 tableobj.objectSubId = 0;
18356 typeobj.classId = TypeRelationId;
18357 typeobj.objectId = typeid;
18358 typeobj.objectSubId = 0;
18359 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18360
18361 /* Update pg_class.reloftype */
18362 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18363 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18364 if (!HeapTupleIsValid(classtuple))
18365 elog(ERROR, "cache lookup failed for relation %u", relid);
18366 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18367 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18368
18369 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18370
18371 heap_freetuple(classtuple);
18372 table_close(relationRelation, RowExclusiveLock);
18373
18374 ReleaseSysCache(typetuple);
18375
18376 return typeobj;
18377}
18378
18379/*
18380 * ALTER TABLE NOT OF
18381 *
18382 * Detach a typed table from its originating type. Just clear reloftype and
18383 * remove the dependency.
18384 */
18385static void
18387{
18388 Oid relid = RelationGetRelid(rel);
18389 Relation relationRelation;
18390 HeapTuple tuple;
18391
18392 if (!OidIsValid(rel->rd_rel->reloftype))
18393 ereport(ERROR,
18394 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18395 errmsg("\"%s\" is not a typed table",
18397
18398 /*
18399 * We don't bother to check ownership of the type --- ownership of the
18400 * table is presumed enough rights. No lock required on the type, either.
18401 */
18402
18403 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18405
18406 /* Clear pg_class.reloftype */
18407 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18408 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18409 if (!HeapTupleIsValid(tuple))
18410 elog(ERROR, "cache lookup failed for relation %u", relid);
18411 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18412 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18413
18414 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18415
18416 heap_freetuple(tuple);
18417 table_close(relationRelation, RowExclusiveLock);
18418}
18419
18420/*
18421 * relation_mark_replica_identity: Update a table's replica identity
18422 *
18423 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18424 * index. Otherwise, it must be InvalidOid.
18425 *
18426 * Caller had better hold an exclusive lock on the relation, as the results
18427 * of running two of these concurrently wouldn't be pretty.
18428 */
18429static void
18430relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18431 bool is_internal)
18432{
18433 Relation pg_index;
18434 Relation pg_class;
18435 HeapTuple pg_class_tuple;
18436 HeapTuple pg_index_tuple;
18437 Form_pg_class pg_class_form;
18438 Form_pg_index pg_index_form;
18439 ListCell *index;
18440
18441 /*
18442 * Check whether relreplident has changed, and update it if so.
18443 */
18444 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18445 pg_class_tuple = SearchSysCacheCopy1(RELOID,
18447 if (!HeapTupleIsValid(pg_class_tuple))
18448 elog(ERROR, "cache lookup failed for relation \"%s\"",
18450 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18451 if (pg_class_form->relreplident != ri_type)
18452 {
18453 pg_class_form->relreplident = ri_type;
18454 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18455 }
18456 table_close(pg_class, RowExclusiveLock);
18457 heap_freetuple(pg_class_tuple);
18458
18459 /*
18460 * Update the per-index indisreplident flags correctly.
18461 */
18462 pg_index = table_open(IndexRelationId, RowExclusiveLock);
18463 foreach(index, RelationGetIndexList(rel))
18464 {
18465 Oid thisIndexOid = lfirst_oid(index);
18466 bool dirty = false;
18467
18468 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18469 ObjectIdGetDatum(thisIndexOid));
18470 if (!HeapTupleIsValid(pg_index_tuple))
18471 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18472 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18473
18474 if (thisIndexOid == indexOid)
18475 {
18476 /* Set the bit if not already set. */
18477 if (!pg_index_form->indisreplident)
18478 {
18479 dirty = true;
18480 pg_index_form->indisreplident = true;
18481 }
18482 }
18483 else
18484 {
18485 /* Unset the bit if set. */
18486 if (pg_index_form->indisreplident)
18487 {
18488 dirty = true;
18489 pg_index_form->indisreplident = false;
18490 }
18491 }
18492
18493 if (dirty)
18494 {
18495 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18496 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18497 InvalidOid, is_internal);
18498
18499 /*
18500 * Invalidate the relcache for the table, so that after we commit
18501 * all sessions will refresh the table's replica identity index
18502 * before attempting any UPDATE or DELETE on the table. (If we
18503 * changed the table's pg_class row above, then a relcache inval
18504 * is already queued due to that; but we might not have.)
18505 */
18507 }
18508 heap_freetuple(pg_index_tuple);
18509 }
18510
18511 table_close(pg_index, RowExclusiveLock);
18512}
18513
18514/*
18515 * ALTER TABLE <name> REPLICA IDENTITY ...
18516 */
18517static void
18519{
18520 Oid indexOid;
18521 Relation indexRel;
18522 int key;
18523
18524 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18525 {
18526 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18527 return;
18528 }
18529 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18530 {
18531 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18532 return;
18533 }
18534 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18535 {
18536 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18537 return;
18538 }
18539 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18540 {
18541 /* fallthrough */ ;
18542 }
18543 else
18544 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18545
18546 /* Check that the index exists */
18547 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18548 if (!OidIsValid(indexOid))
18549 ereport(ERROR,
18550 (errcode(ERRCODE_UNDEFINED_OBJECT),
18551 errmsg("index \"%s\" for table \"%s\" does not exist",
18552 stmt->name, RelationGetRelationName(rel))));
18553
18554 indexRel = index_open(indexOid, ShareLock);
18555
18556 /* Check that the index is on the relation we're altering. */
18557 if (indexRel->rd_index == NULL ||
18558 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18559 ereport(ERROR,
18560 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18561 errmsg("\"%s\" is not an index for table \"%s\"",
18562 RelationGetRelationName(indexRel),
18564
18565 /*
18566 * The AM must support uniqueness, and the index must in fact be unique.
18567 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18568 * exclusion), we can use that too.
18569 */
18570 if ((!indexRel->rd_indam->amcanunique ||
18571 !indexRel->rd_index->indisunique) &&
18572 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18573 ereport(ERROR,
18574 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18575 errmsg("cannot use non-unique index \"%s\" as replica identity",
18576 RelationGetRelationName(indexRel))));
18577 /* Deferred indexes are not guaranteed to be always unique. */
18578 if (!indexRel->rd_index->indimmediate)
18579 ereport(ERROR,
18580 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18581 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18582 RelationGetRelationName(indexRel))));
18583 /* Expression indexes aren't supported. */
18584 if (RelationGetIndexExpressions(indexRel) != NIL)
18585 ereport(ERROR,
18586 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18587 errmsg("cannot use expression index \"%s\" as replica identity",
18588 RelationGetRelationName(indexRel))));
18589 /* Predicate indexes aren't supported. */
18590 if (RelationGetIndexPredicate(indexRel) != NIL)
18591 ereport(ERROR,
18592 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18593 errmsg("cannot use partial index \"%s\" as replica identity",
18594 RelationGetRelationName(indexRel))));
18595
18596 /* Check index for nullable columns. */
18597 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18598 {
18599 int16 attno = indexRel->rd_index->indkey.values[key];
18600 Form_pg_attribute attr;
18601
18602 /*
18603 * Reject any other system columns. (Going forward, we'll disallow
18604 * indexes containing such columns in the first place, but they might
18605 * exist in older branches.)
18606 */
18607 if (attno <= 0)
18608 ereport(ERROR,
18609 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18610 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18611 RelationGetRelationName(indexRel), attno)));
18612
18613 attr = TupleDescAttr(rel->rd_att, attno - 1);
18614 if (!attr->attnotnull)
18615 ereport(ERROR,
18616 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18617 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18618 RelationGetRelationName(indexRel),
18619 NameStr(attr->attname))));
18620 }
18621
18622 /* This index is suitable for use as a replica identity. Mark it. */
18623 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18624
18625 index_close(indexRel, NoLock);
18626}
18627
18628/*
18629 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18630 */
18631static void
18633{
18634 Relation pg_class;
18635 Oid relid;
18636 HeapTuple tuple;
18637
18638 relid = RelationGetRelid(rel);
18639
18640 /* Pull the record for this relation and update it */
18641 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18642
18643 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18644
18645 if (!HeapTupleIsValid(tuple))
18646 elog(ERROR, "cache lookup failed for relation %u", relid);
18647
18648 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18649 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18650
18651 InvokeObjectPostAlterHook(RelationRelationId,
18652 RelationGetRelid(rel), 0);
18653
18654 table_close(pg_class, RowExclusiveLock);
18655 heap_freetuple(tuple);
18656}
18657
18658/*
18659 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18660 */
18661static void
18663{
18664 Relation pg_class;
18665 Oid relid;
18666 HeapTuple tuple;
18667
18668 relid = RelationGetRelid(rel);
18669
18670 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18671
18672 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18673
18674 if (!HeapTupleIsValid(tuple))
18675 elog(ERROR, "cache lookup failed for relation %u", relid);
18676
18677 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18678 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18679
18680 InvokeObjectPostAlterHook(RelationRelationId,
18681 RelationGetRelid(rel), 0);
18682
18683 table_close(pg_class, RowExclusiveLock);
18684 heap_freetuple(tuple);
18685}
18686
18687/*
18688 * ALTER FOREIGN TABLE <name> OPTIONS (...)
18689 */
18690static void
18692{
18693 Relation ftrel;
18694 ForeignServer *server;
18695 ForeignDataWrapper *fdw;
18696 HeapTuple tuple;
18697 bool isnull;
18698 Datum repl_val[Natts_pg_foreign_table];
18699 bool repl_null[Natts_pg_foreign_table];
18700 bool repl_repl[Natts_pg_foreign_table];
18701 Datum datum;
18702 Form_pg_foreign_table tableform;
18703
18704 if (options == NIL)
18705 return;
18706
18707 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18708
18709 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18710 ObjectIdGetDatum(rel->rd_id));
18711 if (!HeapTupleIsValid(tuple))
18712 ereport(ERROR,
18713 (errcode(ERRCODE_UNDEFINED_OBJECT),
18714 errmsg("foreign table \"%s\" does not exist",
18716 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18717 server = GetForeignServer(tableform->ftserver);
18718 fdw = GetForeignDataWrapper(server->fdwid);
18719
18720 memset(repl_val, 0, sizeof(repl_val));
18721 memset(repl_null, false, sizeof(repl_null));
18722 memset(repl_repl, false, sizeof(repl_repl));
18723
18724 /* Extract the current options */
18725 datum = SysCacheGetAttr(FOREIGNTABLEREL,
18726 tuple,
18727 Anum_pg_foreign_table_ftoptions,
18728 &isnull);
18729 if (isnull)
18730 datum = PointerGetDatum(NULL);
18731
18732 /* Transform the options */
18733 datum = transformGenericOptions(ForeignTableRelationId,
18734 datum,
18735 options,
18736 fdw->fdwvalidator);
18737
18738 if (DatumGetPointer(datum) != NULL)
18739 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18740 else
18741 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18742
18743 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18744
18745 /* Everything looks good - update the tuple */
18746
18747 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18748 repl_val, repl_null, repl_repl);
18749
18750 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18751
18752 /*
18753 * Invalidate relcache so that all sessions will refresh any cached plans
18754 * that might depend on the old options.
18755 */
18757
18758 InvokeObjectPostAlterHook(ForeignTableRelationId,
18759 RelationGetRelid(rel), 0);
18760
18762
18763 heap_freetuple(tuple);
18764}
18765
18766/*
18767 * ALTER TABLE ALTER COLUMN SET COMPRESSION
18768 *
18769 * Return value is the address of the modified column
18770 */
18771static ObjectAddress
18773 const char *column,
18774 Node *newValue,
18775 LOCKMODE lockmode)
18776{
18777 Relation attrel;
18778 HeapTuple tuple;
18779 Form_pg_attribute atttableform;
18781 char *compression;
18782 char cmethod;
18783 ObjectAddress address;
18784
18785 compression = strVal(newValue);
18786
18787 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18788
18789 /* copy the cache entry so we can scribble on it below */
18790 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18791 if (!HeapTupleIsValid(tuple))
18792 ereport(ERROR,
18793 (errcode(ERRCODE_UNDEFINED_COLUMN),
18794 errmsg("column \"%s\" of relation \"%s\" does not exist",
18795 column, RelationGetRelationName(rel))));
18796
18797 /* prevent them from altering a system attribute */
18798 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18799 attnum = atttableform->attnum;
18800 if (attnum <= 0)
18801 ereport(ERROR,
18802 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18803 errmsg("cannot alter system column \"%s\"", column)));
18804
18805 /*
18806 * Check that column type is compressible, then get the attribute
18807 * compression method code
18808 */
18809 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18810
18811 /* update pg_attribute entry */
18812 atttableform->attcompression = cmethod;
18813 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18814
18815 InvokeObjectPostAlterHook(RelationRelationId,
18816 RelationGetRelid(rel),
18817 attnum);
18818
18819 /*
18820 * Apply the change to indexes as well (only for simple index columns,
18821 * matching behavior of index.c ConstructTupleDescriptor()).
18822 */
18823 SetIndexStorageProperties(rel, attrel, attnum,
18824 false, 0,
18825 true, cmethod,
18826 lockmode);
18827
18828 heap_freetuple(tuple);
18829
18831
18832 /* make changes visible */
18834
18835 ObjectAddressSubSet(address, RelationRelationId,
18836 RelationGetRelid(rel), attnum);
18837 return address;
18838}
18839
18840
18841/*
18842 * Preparation phase for SET LOGGED/UNLOGGED
18843 *
18844 * This verifies that we're not trying to change a temp table. Also,
18845 * existing foreign key constraints are checked to avoid ending up with
18846 * permanent tables referencing unlogged tables.
18847 */
18848static void
18850{
18851 Relation pg_constraint;
18852 HeapTuple tuple;
18853 SysScanDesc scan;
18854 ScanKeyData skey[1];
18855
18856 /*
18857 * Disallow changing status for a temp table. Also verify whether we can
18858 * get away with doing nothing; in such cases we don't need to run the
18859 * checks below, either.
18860 */
18861 switch (rel->rd_rel->relpersistence)
18862 {
18863 case RELPERSISTENCE_TEMP:
18864 ereport(ERROR,
18865 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18866 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18868 errtable(rel)));
18869 break;
18870 case RELPERSISTENCE_PERMANENT:
18871 if (toLogged)
18872 /* nothing to do */
18873 return;
18874 break;
18875 case RELPERSISTENCE_UNLOGGED:
18876 if (!toLogged)
18877 /* nothing to do */
18878 return;
18879 break;
18880 }
18881
18882 /*
18883 * Check that the table is not part of any publication when changing to
18884 * UNLOGGED, as UNLOGGED tables can't be published.
18885 */
18886 if (!toLogged &&
18888 ereport(ERROR,
18889 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18890 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18892 errdetail("Unlogged relations cannot be replicated.")));
18893
18894 /*
18895 * Check existing foreign key constraints to preserve the invariant that
18896 * permanent tables cannot reference unlogged ones. Self-referencing
18897 * foreign keys can safely be ignored.
18898 */
18899 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18900
18901 /*
18902 * Scan conrelid if changing to permanent, else confrelid. This also
18903 * determines whether a useful index exists.
18904 */
18905 ScanKeyInit(&skey[0],
18906 toLogged ? Anum_pg_constraint_conrelid :
18907 Anum_pg_constraint_confrelid,
18908 BTEqualStrategyNumber, F_OIDEQ,
18910 scan = systable_beginscan(pg_constraint,
18911 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18912 true, NULL, 1, skey);
18913
18914 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18915 {
18917
18918 if (con->contype == CONSTRAINT_FOREIGN)
18919 {
18920 Oid foreignrelid;
18921 Relation foreignrel;
18922
18923 /* the opposite end of what we used as scankey */
18924 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18925
18926 /* ignore if self-referencing */
18927 if (RelationGetRelid(rel) == foreignrelid)
18928 continue;
18929
18930 foreignrel = relation_open(foreignrelid, AccessShareLock);
18931
18932 if (toLogged)
18933 {
18934 if (!RelationIsPermanent(foreignrel))
18935 ereport(ERROR,
18936 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18937 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18939 RelationGetRelationName(foreignrel)),
18940 errtableconstraint(rel, NameStr(con->conname))));
18941 }
18942 else
18943 {
18944 if (RelationIsPermanent(foreignrel))
18945 ereport(ERROR,
18946 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18947 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18949 RelationGetRelationName(foreignrel)),
18950 errtableconstraint(rel, NameStr(con->conname))));
18951 }
18952
18953 relation_close(foreignrel, AccessShareLock);
18954 }
18955 }
18956
18957 systable_endscan(scan);
18958
18959 table_close(pg_constraint, AccessShareLock);
18960
18961 /* force rewrite if necessary; see comment in ATRewriteTables */
18963 if (toLogged)
18964 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18965 else
18966 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18967 tab->chgPersistence = true;
18968}
18969
18970/*
18971 * Execute ALTER TABLE SET SCHEMA
18972 */
18975{
18976 Relation rel;
18977 Oid relid;
18978 Oid oldNspOid;
18979 Oid nspOid;
18980 RangeVar *newrv;
18981 ObjectAddresses *objsMoved;
18982 ObjectAddress myself;
18983
18985 stmt->missing_ok ? RVR_MISSING_OK : 0,
18987 stmt);
18988
18989 if (!OidIsValid(relid))
18990 {
18992 (errmsg("relation \"%s\" does not exist, skipping",
18993 stmt->relation->relname)));
18994 return InvalidObjectAddress;
18995 }
18996
18997 rel = relation_open(relid, NoLock);
18998
18999 oldNspOid = RelationGetNamespace(rel);
19000
19001 /* If it's an owned sequence, disallow moving it by itself. */
19002 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19003 {
19004 Oid tableId;
19005 int32 colId;
19006
19007 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19008 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
19009 ereport(ERROR,
19010 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19011 errmsg("cannot move an owned sequence into another schema"),
19012 errdetail("Sequence \"%s\" is linked to table \"%s\".",
19014 get_rel_name(tableId))));
19015 }
19016
19017 /* Get and lock schema OID and check its permissions. */
19018 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19019 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
19020
19021 /* common checks on switching namespaces */
19022 CheckSetNamespace(oldNspOid, nspOid);
19023
19024 objsMoved = new_object_addresses();
19025 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
19026 free_object_addresses(objsMoved);
19027
19028 ObjectAddressSet(myself, RelationRelationId, relid);
19029
19030 if (oldschema)
19031 *oldschema = oldNspOid;
19032
19033 /* close rel, but keep lock until commit */
19034 relation_close(rel, NoLock);
19035
19036 return myself;
19037}
19038
19039/*
19040 * The guts of relocating a table or materialized view to another namespace:
19041 * besides moving the relation itself, its dependent objects are relocated to
19042 * the new schema.
19043 */
19044void
19046 ObjectAddresses *objsMoved)
19047{
19048 Relation classRel;
19049
19050 Assert(objsMoved != NULL);
19051
19052 /* OK, modify the pg_class row and pg_depend entry */
19053 classRel = table_open(RelationRelationId, RowExclusiveLock);
19054
19055 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
19056 nspOid, true, objsMoved);
19057
19058 /* Fix the table's row type too, if it has one */
19059 if (OidIsValid(rel->rd_rel->reltype))
19060 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
19061 false, /* isImplicitArray */
19062 false, /* ignoreDependent */
19063 false, /* errorOnTableType */
19064 objsMoved);
19065
19066 /* Fix other dependent stuff */
19067 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19068 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19069 objsMoved, AccessExclusiveLock);
19070 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19071 false, objsMoved);
19072
19073 table_close(classRel, RowExclusiveLock);
19074}
19075
19076/*
19077 * The guts of relocating a relation to another namespace: fix the pg_class
19078 * entry, and the pg_depend entry if any. Caller must already have
19079 * opened and write-locked pg_class.
19080 */
19081void
19083 Oid oldNspOid, Oid newNspOid,
19084 bool hasDependEntry,
19085 ObjectAddresses *objsMoved)
19086{
19087 HeapTuple classTup;
19088 Form_pg_class classForm;
19089 ObjectAddress thisobj;
19090 bool already_done = false;
19091
19092 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19093 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19094 if (!HeapTupleIsValid(classTup))
19095 elog(ERROR, "cache lookup failed for relation %u", relOid);
19096 classForm = (Form_pg_class) GETSTRUCT(classTup);
19097
19098 Assert(classForm->relnamespace == oldNspOid);
19099
19100 thisobj.classId = RelationRelationId;
19101 thisobj.objectId = relOid;
19102 thisobj.objectSubId = 0;
19103
19104 /*
19105 * If the object has already been moved, don't move it again. If it's
19106 * already in the right place, don't move it, but still fire the object
19107 * access hook.
19108 */
19109 already_done = object_address_present(&thisobj, objsMoved);
19110 if (!already_done && oldNspOid != newNspOid)
19111 {
19112 ItemPointerData otid = classTup->t_self;
19113
19114 /* check for duplicate name (more friendly than unique-index failure) */
19115 if (get_relname_relid(NameStr(classForm->relname),
19116 newNspOid) != InvalidOid)
19117 ereport(ERROR,
19118 (errcode(ERRCODE_DUPLICATE_TABLE),
19119 errmsg("relation \"%s\" already exists in schema \"%s\"",
19120 NameStr(classForm->relname),
19121 get_namespace_name(newNspOid))));
19122
19123 /* classTup is a copy, so OK to scribble on */
19124 classForm->relnamespace = newNspOid;
19125
19126 CatalogTupleUpdate(classRel, &otid, classTup);
19127 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19128
19129
19130 /* Update dependency on schema if caller said so */
19131 if (hasDependEntry &&
19132 changeDependencyFor(RelationRelationId,
19133 relOid,
19134 NamespaceRelationId,
19135 oldNspOid,
19136 newNspOid) != 1)
19137 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19138 NameStr(classForm->relname));
19139 }
19140 else
19141 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19142 if (!already_done)
19143 {
19144 add_exact_object_address(&thisobj, objsMoved);
19145
19146 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19147 }
19148
19149 heap_freetuple(classTup);
19150}
19151
19152/*
19153 * Move all indexes for the specified relation to another namespace.
19154 *
19155 * Note: we assume adequate permission checking was done by the caller,
19156 * and that the caller has a suitable lock on the owning relation.
19157 */
19158static void
19160 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19161{
19162 List *indexList;
19163 ListCell *l;
19164
19165 indexList = RelationGetIndexList(rel);
19166
19167 foreach(l, indexList)
19168 {
19169 Oid indexOid = lfirst_oid(l);
19170 ObjectAddress thisobj;
19171
19172 thisobj.classId = RelationRelationId;
19173 thisobj.objectId = indexOid;
19174 thisobj.objectSubId = 0;
19175
19176 /*
19177 * Note: currently, the index will not have its own dependency on the
19178 * namespace, so we don't need to do changeDependencyFor(). There's no
19179 * row type in pg_type, either.
19180 *
19181 * XXX this objsMoved test may be pointless -- surely we have a single
19182 * dependency link from a relation to each index?
19183 */
19184 if (!object_address_present(&thisobj, objsMoved))
19185 {
19186 AlterRelationNamespaceInternal(classRel, indexOid,
19187 oldNspOid, newNspOid,
19188 false, objsMoved);
19189 add_exact_object_address(&thisobj, objsMoved);
19190 }
19191 }
19192
19193 list_free(indexList);
19194}
19195
19196/*
19197 * Move all identity and SERIAL-column sequences of the specified relation to another
19198 * namespace.
19199 *
19200 * Note: we assume adequate permission checking was done by the caller,
19201 * and that the caller has a suitable lock on the owning relation.
19202 */
19203static void
19205 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19206 LOCKMODE lockmode)
19207{
19208 Relation depRel;
19209 SysScanDesc scan;
19210 ScanKeyData key[2];
19211 HeapTuple tup;
19212
19213 /*
19214 * SERIAL sequences are those having an auto dependency on one of the
19215 * table's columns (we don't care *which* column, exactly).
19216 */
19217 depRel = table_open(DependRelationId, AccessShareLock);
19218
19219 ScanKeyInit(&key[0],
19220 Anum_pg_depend_refclassid,
19221 BTEqualStrategyNumber, F_OIDEQ,
19222 ObjectIdGetDatum(RelationRelationId));
19223 ScanKeyInit(&key[1],
19224 Anum_pg_depend_refobjid,
19225 BTEqualStrategyNumber, F_OIDEQ,
19227 /* we leave refobjsubid unspecified */
19228
19229 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19230 NULL, 2, key);
19231
19232 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19233 {
19234 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19235 Relation seqRel;
19236
19237 /* skip dependencies other than auto dependencies on columns */
19238 if (depForm->refobjsubid == 0 ||
19239 depForm->classid != RelationRelationId ||
19240 depForm->objsubid != 0 ||
19241 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19242 continue;
19243
19244 /* Use relation_open just in case it's an index */
19245 seqRel = relation_open(depForm->objid, lockmode);
19246
19247 /* skip non-sequence relations */
19248 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19249 {
19250 /* No need to keep the lock */
19251 relation_close(seqRel, lockmode);
19252 continue;
19253 }
19254
19255 /* Fix the pg_class and pg_depend entries */
19256 AlterRelationNamespaceInternal(classRel, depForm->objid,
19257 oldNspOid, newNspOid,
19258 true, objsMoved);
19259
19260 /*
19261 * Sequences used to have entries in pg_type, but no longer do. If we
19262 * ever re-instate that, we'll need to move the pg_type entry to the
19263 * new namespace, too (using AlterTypeNamespaceInternal).
19264 */
19265 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19266
19267 /* Now we can close it. Keep the lock till end of transaction. */
19268 relation_close(seqRel, NoLock);
19269 }
19270
19271 systable_endscan(scan);
19272
19274}
19275
19276
19277/*
19278 * This code supports
19279 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19280 *
19281 * Because we only support this for TEMP tables, it's sufficient to remember
19282 * the state in a backend-local data structure.
19283 */
19284
19285/*
19286 * Register a newly-created relation's ON COMMIT action.
19287 */
19288void
19290{
19291 OnCommitItem *oc;
19292 MemoryContext oldcxt;
19293
19294 /*
19295 * We needn't bother registering the relation unless there is an ON COMMIT
19296 * action we need to take.
19297 */
19299 return;
19300
19302
19304 oc->relid = relid;
19305 oc->oncommit = action;
19308
19309 /*
19310 * We use lcons() here so that ON COMMIT actions are processed in reverse
19311 * order of registration. That might not be essential but it seems
19312 * reasonable.
19313 */
19315
19316 MemoryContextSwitchTo(oldcxt);
19317}
19318
19319/*
19320 * Unregister any ON COMMIT action when a relation is deleted.
19321 *
19322 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19323 */
19324void
19326{
19327 ListCell *l;
19328
19329 foreach(l, on_commits)
19330 {
19331 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19332
19333 if (oc->relid == relid)
19334 {
19336 break;
19337 }
19338 }
19339}
19340
19341/*
19342 * Perform ON COMMIT actions.
19343 *
19344 * This is invoked just before actually committing, since it's possible
19345 * to encounter errors.
19346 */
19347void
19349{
19350 ListCell *l;
19351 List *oids_to_truncate = NIL;
19352 List *oids_to_drop = NIL;
19353
19354 foreach(l, on_commits)
19355 {
19356 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19357
19358 /* Ignore entry if already dropped in this xact */
19360 continue;
19361
19362 switch (oc->oncommit)
19363 {
19364 case ONCOMMIT_NOOP:
19366 /* Do nothing (there shouldn't be such entries, actually) */
19367 break;
19369
19370 /*
19371 * If this transaction hasn't accessed any temporary
19372 * relations, we can skip truncating ON COMMIT DELETE ROWS
19373 * tables, as they must still be empty.
19374 */
19376 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19377 break;
19378 case ONCOMMIT_DROP:
19379 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19380 break;
19381 }
19382 }
19383
19384 /*
19385 * Truncate relations before dropping so that all dependencies between
19386 * relations are removed after they are worked on. Doing it like this
19387 * might be a waste as it is possible that a relation being truncated will
19388 * be dropped anyway due to its parent being dropped, but this makes the
19389 * code more robust because of not having to re-check that the relation
19390 * exists at truncation time.
19391 */
19392 if (oids_to_truncate != NIL)
19393 heap_truncate(oids_to_truncate);
19394
19395 if (oids_to_drop != NIL)
19396 {
19397 ObjectAddresses *targetObjects = new_object_addresses();
19398
19399 foreach(l, oids_to_drop)
19400 {
19401 ObjectAddress object;
19402
19403 object.classId = RelationRelationId;
19404 object.objectId = lfirst_oid(l);
19405 object.objectSubId = 0;
19406
19407 Assert(!object_address_present(&object, targetObjects));
19408
19409 add_exact_object_address(&object, targetObjects);
19410 }
19411
19412 /*
19413 * Object deletion might involve toast table access (to clean up
19414 * toasted catalog entries), so ensure we have a valid snapshot.
19415 */
19417
19418 /*
19419 * Since this is an automatic drop, rather than one directly initiated
19420 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19421 */
19424
19426
19427#ifdef USE_ASSERT_CHECKING
19428
19429 /*
19430 * Note that table deletion will call remove_on_commit_action, so the
19431 * entry should get marked as deleted.
19432 */
19433 foreach(l, on_commits)
19434 {
19435 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19436
19437 if (oc->oncommit != ONCOMMIT_DROP)
19438 continue;
19439
19441 }
19442#endif
19443 }
19444}
19445
19446/*
19447 * Post-commit or post-abort cleanup for ON COMMIT management.
19448 *
19449 * All we do here is remove no-longer-needed OnCommitItem entries.
19450 *
19451 * During commit, remove entries that were deleted during this transaction;
19452 * during abort, remove those created during this transaction.
19453 */
19454void
19456{
19457 ListCell *cur_item;
19458
19459 foreach(cur_item, on_commits)
19460 {
19461 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19462
19463 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19465 {
19466 /* cur_item must be removed */
19468 pfree(oc);
19469 }
19470 else
19471 {
19472 /* cur_item must be preserved */
19475 }
19476 }
19477}
19478
19479/*
19480 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19481 *
19482 * During subabort, we can immediately remove entries created during this
19483 * subtransaction. During subcommit, just relabel entries marked during
19484 * this subtransaction as being the parent's responsibility.
19485 */
19486void
19488 SubTransactionId parentSubid)
19489{
19490 ListCell *cur_item;
19491
19492 foreach(cur_item, on_commits)
19493 {
19494 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19495
19496 if (!isCommit && oc->creating_subid == mySubid)
19497 {
19498 /* cur_item must be removed */
19500 pfree(oc);
19501 }
19502 else
19503 {
19504 /* cur_item must be preserved */
19505 if (oc->creating_subid == mySubid)
19506 oc->creating_subid = parentSubid;
19507 if (oc->deleting_subid == mySubid)
19508 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19509 }
19510 }
19511}
19512
19513/*
19514 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19515 * the relation to be locked only if (1) it's a plain or partitioned table,
19516 * materialized view, or TOAST table and (2) the current user is the owner (or
19517 * the superuser) or has been granted MAINTAIN. This meets the
19518 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19519 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19520 */
19521void
19523 Oid relId, Oid oldRelId, void *arg)
19524{
19525 char relkind;
19526 AclResult aclresult;
19527
19528 /* Nothing to do if the relation was not found. */
19529 if (!OidIsValid(relId))
19530 return;
19531
19532 /*
19533 * If the relation does exist, check whether it's an index. But note that
19534 * the relation might have been dropped between the time we did the name
19535 * lookup and now. In that case, there's nothing to do.
19536 */
19537 relkind = get_rel_relkind(relId);
19538 if (!relkind)
19539 return;
19540 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19541 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19542 ereport(ERROR,
19543 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19544 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19545
19546 /* Check permissions */
19547 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19548 if (aclresult != ACLCHECK_OK)
19549 aclcheck_error(aclresult,
19551 relation->relname);
19552}
19553
19554/*
19555 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19556 */
19557static void
19559 Oid relId, Oid oldRelId, void *arg)
19560{
19561 HeapTuple tuple;
19562
19563 /* Nothing to do if the relation was not found. */
19564 if (!OidIsValid(relId))
19565 return;
19566
19567 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19568 if (!HeapTupleIsValid(tuple)) /* should not happen */
19569 elog(ERROR, "cache lookup failed for relation %u", relId);
19570
19573
19574 ReleaseSysCache(tuple);
19575}
19576
19577/*
19578 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19579 * the owner of the relation, or superuser.
19580 */
19581void
19583 Oid relId, Oid oldRelId, void *arg)
19584{
19585 HeapTuple tuple;
19586
19587 /* Nothing to do if the relation was not found. */
19588 if (!OidIsValid(relId))
19589 return;
19590
19591 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19592 if (!HeapTupleIsValid(tuple)) /* should not happen */
19593 elog(ERROR, "cache lookup failed for relation %u", relId);
19594
19595 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19597 relation->relname);
19598
19599 if (!allowSystemTableMods &&
19600 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19601 ereport(ERROR,
19602 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19603 errmsg("permission denied: \"%s\" is a system catalog",
19604 relation->relname)));
19605
19606 ReleaseSysCache(tuple);
19607}
19608
19609/*
19610 * Common RangeVarGetRelid callback for rename, set schema, and alter table
19611 * processing.
19612 */
19613static void
19615 void *arg)
19616{
19617 Node *stmt = (Node *) arg;
19618 ObjectType reltype;
19619 HeapTuple tuple;
19620 Form_pg_class classform;
19621 AclResult aclresult;
19622 char relkind;
19623
19624 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19625 if (!HeapTupleIsValid(tuple))
19626 return; /* concurrently dropped */
19627 classform = (Form_pg_class) GETSTRUCT(tuple);
19628 relkind = classform->relkind;
19629
19630 /* Must own relation. */
19631 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19633
19634 /* No system table modifications unless explicitly allowed. */
19635 if (!allowSystemTableMods && IsSystemClass(relid, classform))
19636 ereport(ERROR,
19637 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19638 errmsg("permission denied: \"%s\" is a system catalog",
19639 rv->relname)));
19640
19641 /*
19642 * Extract the specified relation type from the statement parse tree.
19643 *
19644 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19645 * have CREATE rights on the containing namespace.
19646 */
19647 if (IsA(stmt, RenameStmt))
19648 {
19649 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19651 if (aclresult != ACLCHECK_OK)
19652 aclcheck_error(aclresult, OBJECT_SCHEMA,
19653 get_namespace_name(classform->relnamespace));
19654 reltype = ((RenameStmt *) stmt)->renameType;
19655 }
19656 else if (IsA(stmt, AlterObjectSchemaStmt))
19657 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19658
19659 else if (IsA(stmt, AlterTableStmt))
19660 reltype = ((AlterTableStmt *) stmt)->objtype;
19661 else
19662 {
19663 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19664 reltype = OBJECT_TABLE; /* placate compiler */
19665 }
19666
19667 /*
19668 * For compatibility with prior releases, we allow ALTER TABLE to be used
19669 * with most other types of relations (but not composite types). We allow
19670 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19671 * otherwise. Otherwise, the user must select the correct form of the
19672 * command for the relation at issue.
19673 */
19674 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19675 ereport(ERROR,
19676 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19677 errmsg("\"%s\" is not a sequence", rv->relname)));
19678
19679 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19680 ereport(ERROR,
19681 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19682 errmsg("\"%s\" is not a view", rv->relname)));
19683
19684 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19685 ereport(ERROR,
19686 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19687 errmsg("\"%s\" is not a materialized view", rv->relname)));
19688
19689 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19690 ereport(ERROR,
19691 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19692 errmsg("\"%s\" is not a foreign table", rv->relname)));
19693
19694 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19695 ereport(ERROR,
19696 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19697 errmsg("\"%s\" is not a composite type", rv->relname)));
19698
19699 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19700 relkind != RELKIND_PARTITIONED_INDEX
19701 && !IsA(stmt, RenameStmt))
19702 ereport(ERROR,
19703 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19704 errmsg("\"%s\" is not an index", rv->relname)));
19705
19706 /*
19707 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19708 * TYPE for that.
19709 */
19710 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19711 ereport(ERROR,
19712 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19713 errmsg("\"%s\" is a composite type", rv->relname),
19714 /* translator: %s is an SQL ALTER command */
19715 errhint("Use %s instead.",
19716 "ALTER TYPE")));
19717
19718 /*
19719 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19720 * to a different schema, such as indexes and TOAST tables.
19721 */
19723 {
19724 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19725 ereport(ERROR,
19726 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19727 errmsg("cannot change schema of index \"%s\"",
19728 rv->relname),
19729 errhint("Change the schema of the table instead.")));
19730 else if (relkind == RELKIND_COMPOSITE_TYPE)
19731 ereport(ERROR,
19732 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19733 errmsg("cannot change schema of composite type \"%s\"",
19734 rv->relname),
19735 /* translator: %s is an SQL ALTER command */
19736 errhint("Use %s instead.",
19737 "ALTER TYPE")));
19738 else if (relkind == RELKIND_TOASTVALUE)
19739 ereport(ERROR,
19740 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19741 errmsg("cannot change schema of TOAST table \"%s\"",
19742 rv->relname),
19743 errhint("Change the schema of the table instead.")));
19744 }
19745
19746 ReleaseSysCache(tuple);
19747}
19748
19749/*
19750 * Transform any expressions present in the partition key
19751 *
19752 * Returns a transformed PartitionSpec.
19753 */
19754static PartitionSpec *
19756{
19757 PartitionSpec *newspec;
19758 ParseState *pstate;
19759 ParseNamespaceItem *nsitem;
19760 ListCell *l;
19761
19762 newspec = makeNode(PartitionSpec);
19763
19764 newspec->strategy = partspec->strategy;
19765 newspec->partParams = NIL;
19766 newspec->location = partspec->location;
19767
19768 /* Check valid number of columns for strategy */
19769 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19770 list_length(partspec->partParams) != 1)
19771 ereport(ERROR,
19772 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19773 errmsg("cannot use \"list\" partition strategy with more than one column")));
19774
19775 /*
19776 * Create a dummy ParseState and insert the target relation as its sole
19777 * rangetable entry. We need a ParseState for transformExpr.
19778 */
19779 pstate = make_parsestate(NULL);
19780 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19781 NULL, false, true);
19782 addNSItemToQuery(pstate, nsitem, true, true, true);
19783
19784 /* take care of any partition expressions */
19785 foreach(l, partspec->partParams)
19786 {
19788
19789 if (pelem->expr)
19790 {
19791 /* Copy, to avoid scribbling on the input */
19792 pelem = copyObject(pelem);
19793
19794 /* Now do parse transformation of the expression */
19795 pelem->expr = transformExpr(pstate, pelem->expr,
19797
19798 /* we have to fix its collations too */
19799 assign_expr_collations(pstate, pelem->expr);
19800 }
19801
19802 newspec->partParams = lappend(newspec->partParams, pelem);
19803 }
19804
19805 return newspec;
19806}
19807
19808/*
19809 * Compute per-partition-column information from a list of PartitionElems.
19810 * Expressions in the PartitionElems must be parse-analyzed already.
19811 */
19812static void
19813ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19814 List **partexprs, Oid *partopclass, Oid *partcollation,
19815 PartitionStrategy strategy)
19816{
19817 int attn;
19818 ListCell *lc;
19819 Oid am_oid;
19820
19821 attn = 0;
19822 foreach(lc, partParams)
19823 {
19825 Oid atttype;
19826 Oid attcollation;
19827
19828 if (pelem->name != NULL)
19829 {
19830 /* Simple attribute reference */
19831 HeapTuple atttuple;
19832 Form_pg_attribute attform;
19833
19835 pelem->name);
19836 if (!HeapTupleIsValid(atttuple))
19837 ereport(ERROR,
19838 (errcode(ERRCODE_UNDEFINED_COLUMN),
19839 errmsg("column \"%s\" named in partition key does not exist",
19840 pelem->name),
19841 parser_errposition(pstate, pelem->location)));
19842 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19843
19844 if (attform->attnum <= 0)
19845 ereport(ERROR,
19846 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19847 errmsg("cannot use system column \"%s\" in partition key",
19848 pelem->name),
19849 parser_errposition(pstate, pelem->location)));
19850
19851 /*
19852 * Stored generated columns cannot work: They are computed after
19853 * BEFORE triggers, but partition routing is done before all
19854 * triggers. Maybe virtual generated columns could be made to
19855 * work, but then they would need to be handled as an expression
19856 * below.
19857 */
19858 if (attform->attgenerated)
19859 ereport(ERROR,
19860 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19861 errmsg("cannot use generated column in partition key"),
19862 errdetail("Column \"%s\" is a generated column.",
19863 pelem->name),
19864 parser_errposition(pstate, pelem->location)));
19865
19866 partattrs[attn] = attform->attnum;
19867 atttype = attform->atttypid;
19868 attcollation = attform->attcollation;
19869 ReleaseSysCache(atttuple);
19870 }
19871 else
19872 {
19873 /* Expression */
19874 Node *expr = pelem->expr;
19875 char partattname[16];
19876 Bitmapset *expr_attrs = NULL;
19877 int i;
19878
19879 Assert(expr != NULL);
19880 atttype = exprType(expr);
19881 attcollation = exprCollation(expr);
19882
19883 /*
19884 * The expression must be of a storable type (e.g., not RECORD).
19885 * The test is the same as for whether a table column is of a safe
19886 * type (which is why we needn't check for the non-expression
19887 * case).
19888 */
19889 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19890 CheckAttributeType(partattname,
19891 atttype, attcollation,
19893
19894 /*
19895 * Strip any top-level COLLATE clause. This ensures that we treat
19896 * "x COLLATE y" and "(x COLLATE y)" alike.
19897 */
19898 while (IsA(expr, CollateExpr))
19899 expr = (Node *) ((CollateExpr *) expr)->arg;
19900
19901 /*
19902 * Examine all the columns in the partition key expression. When
19903 * the whole-row reference is present, examine all the columns of
19904 * the partitioned table.
19905 */
19906 pull_varattnos(expr, 1, &expr_attrs);
19908 {
19909 expr_attrs = bms_add_range(expr_attrs,
19912 expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
19913 }
19914
19915 i = -1;
19916 while ((i = bms_next_member(expr_attrs, i)) >= 0)
19917 {
19919
19920 Assert(attno != 0);
19921
19922 /*
19923 * Cannot allow system column references, since that would
19924 * make partition routing impossible: their values won't be
19925 * known yet when we need to do that.
19926 */
19927 if (attno < 0)
19928 ereport(ERROR,
19929 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19930 errmsg("partition key expressions cannot contain system column references")));
19931
19932 /*
19933 * Stored generated columns cannot work: They are computed
19934 * after BEFORE triggers, but partition routing is done before
19935 * all triggers. Virtual generated columns could probably
19936 * work, but it would require more work elsewhere (for example
19937 * SET EXPRESSION would need to check whether the column is
19938 * used in partition keys). Seems safer to prohibit for now.
19939 */
19940 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19941 ereport(ERROR,
19942 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19943 errmsg("cannot use generated column in partition key"),
19944 errdetail("Column \"%s\" is a generated column.",
19945 get_attname(RelationGetRelid(rel), attno, false)),
19946 parser_errposition(pstate, pelem->location)));
19947 }
19948
19949 if (IsA(expr, Var) &&
19950 ((Var *) expr)->varattno > 0)
19951 {
19952
19953 /*
19954 * User wrote "(column)" or "(column COLLATE something)".
19955 * Treat it like simple attribute anyway.
19956 */
19957 partattrs[attn] = ((Var *) expr)->varattno;
19958 }
19959 else
19960 {
19961 partattrs[attn] = 0; /* marks the column as expression */
19962 *partexprs = lappend(*partexprs, expr);
19963
19964 /*
19965 * transformPartitionSpec() should have already rejected
19966 * subqueries, aggregates, window functions, and SRFs, based
19967 * on the EXPR_KIND_ for partition expressions.
19968 */
19969
19970 /*
19971 * Preprocess the expression before checking for mutability.
19972 * This is essential for the reasons described in
19973 * contain_mutable_functions_after_planning. However, we call
19974 * expression_planner for ourselves rather than using that
19975 * function, because if constant-folding reduces the
19976 * expression to a constant, we'd like to know that so we can
19977 * complain below.
19978 *
19979 * Like contain_mutable_functions_after_planning, assume that
19980 * expression_planner won't scribble on its input, so this
19981 * won't affect the partexprs entry we saved above.
19982 */
19983 expr = (Node *) expression_planner((Expr *) expr);
19984
19985 /*
19986 * Partition expressions cannot contain mutable functions,
19987 * because a given row must always map to the same partition
19988 * as long as there is no change in the partition boundary
19989 * structure.
19990 */
19991 if (contain_mutable_functions(expr))
19992 ereport(ERROR,
19993 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19994 errmsg("functions in partition key expression must be marked IMMUTABLE")));
19995
19996 /*
19997 * While it is not exactly *wrong* for a partition expression
19998 * to be a constant, it seems better to reject such keys.
19999 */
20000 if (IsA(expr, Const))
20001 ereport(ERROR,
20002 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20003 errmsg("cannot use constant expression as partition key")));
20004 }
20005 }
20006
20007 /*
20008 * Apply collation override if any
20009 */
20010 if (pelem->collation)
20011 attcollation = get_collation_oid(pelem->collation, false);
20012
20013 /*
20014 * Check we have a collation iff it's a collatable type. The only
20015 * expected failures here are (1) COLLATE applied to a noncollatable
20016 * type, or (2) partition expression had an unresolved collation. But
20017 * we might as well code this to be a complete consistency check.
20018 */
20019 if (type_is_collatable(atttype))
20020 {
20021 if (!OidIsValid(attcollation))
20022 ereport(ERROR,
20023 (errcode(ERRCODE_INDETERMINATE_COLLATION),
20024 errmsg("could not determine which collation to use for partition expression"),
20025 errhint("Use the COLLATE clause to set the collation explicitly.")));
20026 }
20027 else
20028 {
20029 if (OidIsValid(attcollation))
20030 ereport(ERROR,
20031 (errcode(ERRCODE_DATATYPE_MISMATCH),
20032 errmsg("collations are not supported by type %s",
20033 format_type_be(atttype))));
20034 }
20035
20036 partcollation[attn] = attcollation;
20037
20038 /*
20039 * Identify the appropriate operator class. For list and range
20040 * partitioning, we use a btree operator class; hash partitioning uses
20041 * a hash operator class.
20042 */
20043 if (strategy == PARTITION_STRATEGY_HASH)
20044 am_oid = HASH_AM_OID;
20045 else
20046 am_oid = BTREE_AM_OID;
20047
20048 if (!pelem->opclass)
20049 {
20050 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20051
20052 if (!OidIsValid(partopclass[attn]))
20053 {
20054 if (strategy == PARTITION_STRATEGY_HASH)
20055 ereport(ERROR,
20056 (errcode(ERRCODE_UNDEFINED_OBJECT),
20057 errmsg("data type %s has no default operator class for access method \"%s\"",
20058 format_type_be(atttype), "hash"),
20059 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20060 else
20061 ereport(ERROR,
20062 (errcode(ERRCODE_UNDEFINED_OBJECT),
20063 errmsg("data type %s has no default operator class for access method \"%s\"",
20064 format_type_be(atttype), "btree"),
20065 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20066 }
20067 }
20068 else
20069 partopclass[attn] = ResolveOpClass(pelem->opclass,
20070 atttype,
20071 am_oid == HASH_AM_OID ? "hash" : "btree",
20072 am_oid);
20073
20074 attn++;
20075 }
20076}
20077
20078/*
20079 * PartConstraintImpliedByRelConstraint
20080 * Do scanrel's existing constraints imply the partition constraint?
20081 *
20082 * "Existing constraints" include its check constraints and column-level
20083 * not-null constraints. partConstraint describes the partition constraint,
20084 * in implicit-AND form.
20085 */
20086bool
20088 List *partConstraint)
20089{
20090 List *existConstraint = NIL;
20091 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20092 int i;
20093
20094 if (constr && constr->has_not_null)
20095 {
20096 int natts = scanrel->rd_att->natts;
20097
20098 for (i = 1; i <= natts; i++)
20099 {
20100 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20101
20102 /* invalid not-null constraint must be ignored here */
20103 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20104 {
20105 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20106 NullTest *ntest = makeNode(NullTest);
20107
20108 ntest->arg = (Expr *) makeVar(1,
20109 i,
20110 wholeatt->atttypid,
20111 wholeatt->atttypmod,
20112 wholeatt->attcollation,
20113 0);
20114 ntest->nulltesttype = IS_NOT_NULL;
20115
20116 /*
20117 * argisrow=false is correct even for a composite column,
20118 * because attnotnull does not represent a SQL-spec IS NOT
20119 * NULL test in such a case, just IS DISTINCT FROM NULL.
20120 */
20121 ntest->argisrow = false;
20122 ntest->location = -1;
20123 existConstraint = lappend(existConstraint, ntest);
20124 }
20125 }
20126 }
20127
20128 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20129}
20130
20131/*
20132 * ConstraintImpliedByRelConstraint
20133 * Do scanrel's existing constraints imply the given constraint?
20134 *
20135 * testConstraint is the constraint to validate. provenConstraint is a
20136 * caller-provided list of conditions which this function may assume
20137 * to be true. Both provenConstraint and testConstraint must be in
20138 * implicit-AND form, must only contain immutable clauses, and must
20139 * contain only Vars with varno = 1.
20140 */
20141bool
20142ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20143{
20144 List *existConstraint = list_copy(provenConstraint);
20145 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20146 int num_check,
20147 i;
20148
20149 num_check = (constr != NULL) ? constr->num_check : 0;
20150 for (i = 0; i < num_check; i++)
20151 {
20152 Node *cexpr;
20153
20154 /*
20155 * If this constraint hasn't been fully validated yet, we must ignore
20156 * it here.
20157 */
20158 if (!constr->check[i].ccvalid)
20159 continue;
20160
20161 /*
20162 * NOT ENFORCED constraints are always marked as invalid, which should
20163 * have been ignored.
20164 */
20165 Assert(constr->check[i].ccenforced);
20166
20167 cexpr = stringToNode(constr->check[i].ccbin);
20168
20169 /*
20170 * Run each expression through const-simplification and
20171 * canonicalization. It is necessary, because we will be comparing it
20172 * to similarly-processed partition constraint expressions, and may
20173 * fail to detect valid matches without this.
20174 */
20175 cexpr = eval_const_expressions(NULL, cexpr);
20176 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20177
20178 existConstraint = list_concat(existConstraint,
20179 make_ands_implicit((Expr *) cexpr));
20180 }
20181
20182 /*
20183 * Try to make the proof. Since we are comparing CHECK constraints, we
20184 * need to use weak implication, i.e., we assume existConstraint is
20185 * not-false and try to prove the same for testConstraint.
20186 *
20187 * Note that predicate_implied_by assumes its first argument is known
20188 * immutable. That should always be true for both NOT NULL and partition
20189 * constraints, so we don't test it here.
20190 */
20191 return predicate_implied_by(testConstraint, existConstraint, true);
20192}
20193
20194/*
20195 * QueuePartitionConstraintValidation
20196 *
20197 * Add an entry to wqueue to have the given partition constraint validated by
20198 * Phase 3, for the given relation, and all its children.
20199 *
20200 * We first verify whether the given constraint is implied by pre-existing
20201 * relation constraints; if it is, there's no need to scan the table to
20202 * validate, so don't queue in that case.
20203 */
20204static void
20206 List *partConstraint,
20207 bool validate_default)
20208{
20209 /*
20210 * Based on the table's existing constraints, determine whether or not we
20211 * may skip scanning the table.
20212 */
20213 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20214 {
20215 if (!validate_default)
20217 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20218 RelationGetRelationName(scanrel))));
20219 else
20221 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20222 RelationGetRelationName(scanrel))));
20223 return;
20224 }
20225
20226 /*
20227 * Constraints proved insufficient. For plain relations, queue a
20228 * validation item now; for partitioned tables, recurse to process each
20229 * partition.
20230 */
20231 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20232 {
20233 AlteredTableInfo *tab;
20234
20235 /* Grab a work queue entry. */
20236 tab = ATGetQueueEntry(wqueue, scanrel);
20237 Assert(tab->partition_constraint == NULL);
20238 tab->partition_constraint = (Expr *) linitial(partConstraint);
20239 tab->validate_default = validate_default;
20240 }
20241 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20242 {
20243 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20244 int i;
20245
20246 for (i = 0; i < partdesc->nparts; i++)
20247 {
20248 Relation part_rel;
20249 List *thisPartConstraint;
20250
20251 /*
20252 * This is the minimum lock we need to prevent deadlocks.
20253 */
20254 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20255
20256 /*
20257 * Adjust the constraint for scanrel so that it matches this
20258 * partition's attribute numbers.
20259 */
20260 thisPartConstraint =
20261 map_partition_varattnos(partConstraint, 1,
20262 part_rel, scanrel);
20263
20264 QueuePartitionConstraintValidation(wqueue, part_rel,
20265 thisPartConstraint,
20266 validate_default);
20267 table_close(part_rel, NoLock); /* keep lock till commit */
20268 }
20269 }
20270}
20271
20272/*
20273 * attachPartitionTable: attach a new partition to the partitioned table
20274 *
20275 * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
20276 * of an ALTER TABLE sequence.
20277 * rel: partitioned relation;
20278 * attachrel: relation of attached partition;
20279 * bound: bounds of attached relation.
20280 */
20281static void
20283{
20284 /*
20285 * Create an inheritance; the relevant checks are performed inside the
20286 * function.
20287 */
20288 CreateInheritance(attachrel, rel, true);
20289
20290 /* Update the pg_class entry. */
20291 StorePartitionBound(attachrel, rel, bound);
20292
20293 /* Ensure there exists a correct set of indexes in the partition. */
20294 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20295
20296 /* and triggers */
20297 CloneRowTriggersToPartition(rel, attachrel);
20298
20299 /*
20300 * Clone foreign key constraints. Callee is responsible for setting up
20301 * for phase 3 constraint verification.
20302 */
20303 CloneForeignKeyConstraints(wqueue, rel, attachrel);
20304}
20305
20306/*
20307 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20308 *
20309 * Return the address of the newly attached partition.
20310 */
20311static ObjectAddress
20313 AlterTableUtilityContext *context)
20314{
20315 Relation attachrel,
20316 catalog;
20317 List *attachrel_children;
20318 List *partConstraint;
20319 SysScanDesc scan;
20320 ScanKeyData skey;
20321 AttrNumber attno;
20322 int natts;
20323 TupleDesc tupleDesc;
20324 ObjectAddress address;
20325 const char *trigger_name;
20326 Oid defaultPartOid;
20327 List *partBoundConstraint;
20328 ParseState *pstate = make_parsestate(NULL);
20329
20330 pstate->p_sourcetext = context->queryString;
20331
20332 /*
20333 * We must lock the default partition if one exists, because attaching a
20334 * new partition will change its partition constraint.
20335 */
20336 defaultPartOid =
20338 if (OidIsValid(defaultPartOid))
20339 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20340
20341 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20342
20343 /*
20344 * XXX I think it'd be a good idea to grab locks on all tables referenced
20345 * by FKs at this point also.
20346 */
20347
20348 /*
20349 * Must be owner of both parent and source table -- parent was checked by
20350 * ATSimplePermissions call in ATPrepCmd
20351 */
20354
20355 /* A partition can only have one parent */
20356 if (attachrel->rd_rel->relispartition)
20357 ereport(ERROR,
20358 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20359 errmsg("\"%s\" is already a partition",
20360 RelationGetRelationName(attachrel))));
20361
20362 if (OidIsValid(attachrel->rd_rel->reloftype))
20363 ereport(ERROR,
20364 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20365 errmsg("cannot attach a typed table as partition")));
20366
20367 /*
20368 * Table being attached should not already be part of inheritance; either
20369 * as a child table...
20370 */
20371 catalog = table_open(InheritsRelationId, AccessShareLock);
20372 ScanKeyInit(&skey,
20373 Anum_pg_inherits_inhrelid,
20374 BTEqualStrategyNumber, F_OIDEQ,
20376 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20377 NULL, 1, &skey);
20379 ereport(ERROR,
20380 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20381 errmsg("cannot attach inheritance child as partition")));
20382 systable_endscan(scan);
20383
20384 /* ...or as a parent table (except the case when it is partitioned) */
20385 ScanKeyInit(&skey,
20386 Anum_pg_inherits_inhparent,
20387 BTEqualStrategyNumber, F_OIDEQ,
20389 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20390 1, &skey);
20392 attachrel->rd_rel->relkind == RELKIND_RELATION)
20393 ereport(ERROR,
20394 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20395 errmsg("cannot attach inheritance parent as partition")));
20396 systable_endscan(scan);
20397 table_close(catalog, AccessShareLock);
20398
20399 /*
20400 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20401 * particular, this disallows making a rel a partition of itself.)
20402 *
20403 * We do that by checking if rel is a member of the list of attachrel's
20404 * partitions provided the latter is partitioned at all. We want to avoid
20405 * having to construct this list again, so we request the strongest lock
20406 * on all partitions. We need the strongest lock, because we may decide
20407 * to scan them if we find out that the table being attached (or its leaf
20408 * partitions) may contain rows that violate the partition constraint. If
20409 * the table has a constraint that would prevent such rows, which by
20410 * definition is present in all the partitions, we need not scan the
20411 * table, nor its partitions. But we cannot risk a deadlock by taking a
20412 * weaker lock now and the stronger one only when needed.
20413 */
20414 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20415 AccessExclusiveLock, NULL);
20416 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20417 ereport(ERROR,
20418 (errcode(ERRCODE_DUPLICATE_TABLE),
20419 errmsg("circular inheritance not allowed"),
20420 errdetail("\"%s\" is already a child of \"%s\".",
20422 RelationGetRelationName(attachrel))));
20423
20424 /* If the parent is permanent, so must be all of its partitions. */
20425 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20426 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20427 ereport(ERROR,
20428 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20429 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20431
20432 /* Temp parent cannot have a partition that is itself not a temp */
20433 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20434 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20435 ereport(ERROR,
20436 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20437 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20439
20440 /* If the parent is temp, it must belong to this session */
20441 if (RELATION_IS_OTHER_TEMP(rel))
20442 ereport(ERROR,
20443 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20444 errmsg("cannot attach as partition of temporary relation of another session")));
20445
20446 /* Ditto for the partition */
20447 if (RELATION_IS_OTHER_TEMP(attachrel))
20448 ereport(ERROR,
20449 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20450 errmsg("cannot attach temporary relation of another session as partition")));
20451
20452 /*
20453 * Check if attachrel has any identity columns or any columns that aren't
20454 * in the parent.
20455 */
20456 tupleDesc = RelationGetDescr(attachrel);
20457 natts = tupleDesc->natts;
20458 for (attno = 1; attno <= natts; attno++)
20459 {
20460 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20461 char *attributeName = NameStr(attribute->attname);
20462
20463 /* Ignore dropped */
20464 if (attribute->attisdropped)
20465 continue;
20466
20467 if (attribute->attidentity)
20468 ereport(ERROR,
20469 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20470 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20471 RelationGetRelationName(attachrel), attributeName),
20472 errdetail("The new partition may not contain an identity column."));
20473
20474 /* Try to find the column in parent (matching on column name) */
20475 if (!SearchSysCacheExists2(ATTNAME,
20477 CStringGetDatum(attributeName)))
20478 ereport(ERROR,
20479 (errcode(ERRCODE_DATATYPE_MISMATCH),
20480 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20481 RelationGetRelationName(attachrel), attributeName,
20483 errdetail("The new partition may contain only the columns present in parent.")));
20484 }
20485
20486 /*
20487 * If child_rel has row-level triggers with transition tables, we
20488 * currently don't allow it to become a partition. See also prohibitions
20489 * in ATExecAddInherit() and CreateTrigger().
20490 */
20491 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20492 if (trigger_name != NULL)
20493 ereport(ERROR,
20494 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20495 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20496 trigger_name, RelationGetRelationName(attachrel)),
20497 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20498
20499 /*
20500 * Check that the new partition's bound is valid and does not overlap any
20501 * of existing partitions of the parent - note that it does not return on
20502 * error.
20503 */
20505 cmd->bound, pstate);
20506
20507 attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
20508
20509 /*
20510 * Generate a partition constraint from the partition bound specification.
20511 * If the parent itself is a partition, make sure to include its
20512 * constraint as well.
20513 */
20514 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20515
20516 /*
20517 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20518 * since it's needed later to construct the constraint expression for
20519 * validating against the default partition, if any.
20520 */
20521 partConstraint = list_concat_copy(partBoundConstraint,
20523
20524 /* Skip validation if there are no constraints to validate. */
20525 if (partConstraint)
20526 {
20527 /*
20528 * Run the partition quals through const-simplification similar to
20529 * check constraints. We skip canonicalize_qual, though, because
20530 * partition quals should be in canonical form already.
20531 */
20532 partConstraint =
20534 (Node *) partConstraint);
20535
20536 /* XXX this sure looks wrong */
20537 partConstraint = list_make1(make_ands_explicit(partConstraint));
20538
20539 /*
20540 * Adjust the generated constraint to match this partition's attribute
20541 * numbers.
20542 */
20543 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20544 rel);
20545
20546 /* Validate partition constraints against the table being attached. */
20547 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20548 false);
20549 }
20550
20551 /*
20552 * If we're attaching a partition other than the default partition and a
20553 * default one exists, then that partition's partition constraint changes,
20554 * so add an entry to the work queue to validate it, too. (We must not do
20555 * this when the partition being attached is the default one; we already
20556 * did it above!)
20557 */
20558 if (OidIsValid(defaultPartOid))
20559 {
20560 Relation defaultrel;
20561 List *defPartConstraint;
20562
20563 Assert(!cmd->bound->is_default);
20564
20565 /* we already hold a lock on the default partition */
20566 defaultrel = table_open(defaultPartOid, NoLock);
20567 defPartConstraint =
20568 get_proposed_default_constraint(partBoundConstraint);
20569
20570 /*
20571 * Map the Vars in the constraint expression from rel's attnos to
20572 * defaultrel's.
20573 */
20574 defPartConstraint =
20575 map_partition_varattnos(defPartConstraint,
20576 1, defaultrel, rel);
20577 QueuePartitionConstraintValidation(wqueue, defaultrel,
20578 defPartConstraint, true);
20579
20580 /* keep our lock until commit. */
20581 table_close(defaultrel, NoLock);
20582 }
20583
20584 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20585
20586 /*
20587 * If the partition we just attached is partitioned itself, invalidate
20588 * relcache for all descendent partitions too to ensure that their
20589 * rd_partcheck expression trees are rebuilt; partitions already locked at
20590 * the beginning of this function.
20591 */
20592 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20593 {
20594 ListCell *l;
20595
20596 foreach(l, attachrel_children)
20597 {
20599 }
20600 }
20601
20602 /* keep our lock until commit */
20603 table_close(attachrel, NoLock);
20604
20605 return address;
20606}
20607
20608/*
20609 * AttachPartitionEnsureIndexes
20610 * subroutine for ATExecAttachPartition to create/match indexes
20611 *
20612 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20613 * PARTITION: every partition must have an index attached to each index on the
20614 * partitioned table.
20615 */
20616static void
20618{
20619 List *idxes;
20620 List *attachRelIdxs;
20621 Relation *attachrelIdxRels;
20622 IndexInfo **attachInfos;
20623 ListCell *cell;
20624 MemoryContext cxt;
20625 MemoryContext oldcxt;
20626
20628 "AttachPartitionEnsureIndexes",
20630 oldcxt = MemoryContextSwitchTo(cxt);
20631
20632 idxes = RelationGetIndexList(rel);
20633 attachRelIdxs = RelationGetIndexList(attachrel);
20634 attachrelIdxRels = palloc_array(Relation, list_length(attachRelIdxs));
20635 attachInfos = palloc_array(IndexInfo *, list_length(attachRelIdxs));
20636
20637 /* Build arrays of all existing indexes and their IndexInfos */
20638 foreach_oid(cldIdxId, attachRelIdxs)
20639 {
20640 int i = foreach_current_index(cldIdxId);
20641
20642 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20643 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20644 }
20645
20646 /*
20647 * If we're attaching a foreign table, we must fail if any of the indexes
20648 * is a constraint index; otherwise, there's nothing to do here. Do this
20649 * before starting work, to avoid wasting the effort of building a few
20650 * non-unique indexes before coming across a unique one.
20651 */
20652 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20653 {
20654 foreach(cell, idxes)
20655 {
20656 Oid idx = lfirst_oid(cell);
20658
20659 if (idxRel->rd_index->indisunique ||
20660 idxRel->rd_index->indisprimary)
20661 ereport(ERROR,
20662 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20663 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20664 RelationGetRelationName(attachrel),
20666 errdetail("Partitioned table \"%s\" contains unique indexes.",
20669 }
20670
20671 goto out;
20672 }
20673
20674 /*
20675 * For each index on the partitioned table, find a matching one in the
20676 * partition-to-be; if one is not found, create one.
20677 */
20678 foreach(cell, idxes)
20679 {
20680 Oid idx = lfirst_oid(cell);
20682 IndexInfo *info;
20683 AttrMap *attmap;
20684 bool found = false;
20685 Oid constraintOid;
20686
20687 /*
20688 * Ignore indexes in the partitioned table other than partitioned
20689 * indexes.
20690 */
20691 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20692 {
20694 continue;
20695 }
20696
20697 /* construct an indexinfo to compare existing indexes against */
20698 info = BuildIndexInfo(idxRel);
20699 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20700 RelationGetDescr(rel),
20701 false);
20703
20704 /*
20705 * Scan the list of existing indexes in the partition-to-be, and mark
20706 * the first matching, valid, unattached one we find, if any, as
20707 * partition of the parent index. If we find one, we're done.
20708 */
20709 for (int i = 0; i < list_length(attachRelIdxs); i++)
20710 {
20711 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20712 Oid cldConstrOid = InvalidOid;
20713
20714 /* does this index have a parent? if so, can't use it */
20715 if (attachrelIdxRels[i]->rd_rel->relispartition)
20716 continue;
20717
20718 /* If this index is invalid, can't use it */
20719 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20720 continue;
20721
20722 if (CompareIndexInfo(attachInfos[i], info,
20723 attachrelIdxRels[i]->rd_indcollation,
20724 idxRel->rd_indcollation,
20725 attachrelIdxRels[i]->rd_opfamily,
20726 idxRel->rd_opfamily,
20727 attmap))
20728 {
20729 /*
20730 * If this index is being created in the parent because of a
20731 * constraint, then the child needs to have a constraint also,
20732 * so look for one. If there is no such constraint, this
20733 * index is no good, so keep looking.
20734 */
20735 if (OidIsValid(constraintOid))
20736 {
20737 cldConstrOid =
20739 cldIdxId);
20740 /* no dice */
20741 if (!OidIsValid(cldConstrOid))
20742 continue;
20743
20744 /* Ensure they're both the same type of constraint */
20745 if (get_constraint_type(constraintOid) !=
20746 get_constraint_type(cldConstrOid))
20747 continue;
20748 }
20749
20750 /* bingo. */
20751 IndexSetParentIndex(attachrelIdxRels[i], idx);
20752 if (OidIsValid(constraintOid))
20753 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20754 RelationGetRelid(attachrel));
20755 found = true;
20756
20758 break;
20759 }
20760 }
20761
20762 /*
20763 * If no suitable index was found in the partition-to-be, create one
20764 * now. Note that if this is a PK, not-null constraints must already
20765 * exist.
20766 */
20767 if (!found)
20768 {
20769 IndexStmt *stmt;
20770 Oid conOid;
20771
20773 idxRel, attmap,
20774 &conOid);
20775 DefineIndex(NULL,
20776 RelationGetRelid(attachrel), stmt, InvalidOid,
20777 RelationGetRelid(idxRel),
20778 conOid,
20779 -1,
20780 true, false, false, false, false);
20781 }
20782
20784 }
20785
20786out:
20787 /* Clean up. */
20788 for (int i = 0; i < list_length(attachRelIdxs); i++)
20789 index_close(attachrelIdxRels[i], AccessShareLock);
20790 MemoryContextSwitchTo(oldcxt);
20792}
20793
20794/*
20795 * CloneRowTriggersToPartition
20796 * subroutine for ATExecAttachPartition/DefineRelation to create row
20797 * triggers on partitions
20798 */
20799static void
20801{
20802 Relation pg_trigger;
20804 SysScanDesc scan;
20805 HeapTuple tuple;
20806 MemoryContext perTupCxt;
20807
20808 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20809 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20810 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20811 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20812 true, NULL, 1, &key);
20813
20815 "clone trig", ALLOCSET_SMALL_SIZES);
20816
20817 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20818 {
20819 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20820 CreateTrigStmt *trigStmt;
20821 Node *qual = NULL;
20822 Datum value;
20823 bool isnull;
20824 List *cols = NIL;
20825 List *trigargs = NIL;
20826 MemoryContext oldcxt;
20827
20828 /*
20829 * Ignore statement-level triggers; those are not cloned.
20830 */
20831 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20832 continue;
20833
20834 /*
20835 * Don't clone internal triggers, because the constraint cloning code
20836 * will.
20837 */
20838 if (trigForm->tgisinternal)
20839 continue;
20840
20841 /*
20842 * Complain if we find an unexpected trigger type.
20843 */
20844 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20845 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20846 elog(ERROR, "unexpected trigger \"%s\" found",
20847 NameStr(trigForm->tgname));
20848
20849 /* Use short-lived context for CREATE TRIGGER */
20850 oldcxt = MemoryContextSwitchTo(perTupCxt);
20851
20852 /*
20853 * If there is a WHEN clause, generate a 'cooked' version of it that's
20854 * appropriate for the partition.
20855 */
20856 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20857 RelationGetDescr(pg_trigger), &isnull);
20858 if (!isnull)
20859 {
20861 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20862 partition, parent);
20863 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20864 partition, parent);
20865 }
20866
20867 /*
20868 * If there is a column list, transform it to a list of column names.
20869 * Note we don't need to map this list in any way ...
20870 */
20871 if (trigForm->tgattr.dim1 > 0)
20872 {
20873 int i;
20874
20875 for (i = 0; i < trigForm->tgattr.dim1; i++)
20876 {
20878
20879 col = TupleDescAttr(parent->rd_att,
20880 trigForm->tgattr.values[i] - 1);
20881 cols = lappend(cols,
20882 makeString(pstrdup(NameStr(col->attname))));
20883 }
20884 }
20885
20886 /* Reconstruct trigger arguments list. */
20887 if (trigForm->tgnargs > 0)
20888 {
20889 char *p;
20890
20891 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20892 RelationGetDescr(pg_trigger), &isnull);
20893 if (isnull)
20894 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20895 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20896
20897 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20898
20899 for (int i = 0; i < trigForm->tgnargs; i++)
20900 {
20901 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20902 p += strlen(p) + 1;
20903 }
20904 }
20905
20906 trigStmt = makeNode(CreateTrigStmt);
20907 trigStmt->replace = false;
20908 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20909 trigStmt->trigname = NameStr(trigForm->tgname);
20910 trigStmt->relation = NULL;
20911 trigStmt->funcname = NULL; /* passed separately */
20912 trigStmt->args = trigargs;
20913 trigStmt->row = true;
20914 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20915 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20916 trigStmt->columns = cols;
20917 trigStmt->whenClause = NULL; /* passed separately */
20918 trigStmt->transitionRels = NIL; /* not supported at present */
20919 trigStmt->deferrable = trigForm->tgdeferrable;
20920 trigStmt->initdeferred = trigForm->tginitdeferred;
20921 trigStmt->constrrel = NULL; /* passed separately */
20922
20923 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20924 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20925 trigForm->tgfoid, trigForm->oid, qual,
20926 false, true, trigForm->tgenabled);
20927
20928 MemoryContextSwitchTo(oldcxt);
20929 MemoryContextReset(perTupCxt);
20930 }
20931
20932 MemoryContextDelete(perTupCxt);
20933
20934 systable_endscan(scan);
20935 table_close(pg_trigger, RowExclusiveLock);
20936}
20937
20938/*
20939 * ALTER TABLE DETACH PARTITION
20940 *
20941 * Return the address of the relation that is no longer a partition of rel.
20942 *
20943 * If concurrent mode is requested, we run in two transactions. A side-
20944 * effect is that this command cannot run in a multi-part ALTER TABLE.
20945 * Currently, that's enforced by the grammar.
20946 *
20947 * The strategy for concurrency is to first modify the partition's
20948 * pg_inherit catalog row to make it visible to everyone that the
20949 * partition is detached, lock the partition against writes, and commit
20950 * the transaction; anyone who requests the partition descriptor from
20951 * that point onwards has to ignore such a partition. In a second
20952 * transaction, we wait until all transactions that could have seen the
20953 * partition as attached are gone, then we remove the rest of partition
20954 * metadata (pg_inherits and pg_class.relpartbounds).
20955 */
20956static ObjectAddress
20958 RangeVar *name, bool concurrent)
20959{
20960 Relation partRel;
20961 ObjectAddress address;
20962 Oid defaultPartOid;
20963
20964 /*
20965 * We must lock the default partition, because detaching this partition
20966 * will change its partition constraint.
20967 */
20968 defaultPartOid =
20970 if (OidIsValid(defaultPartOid))
20971 {
20972 /*
20973 * Concurrent detaching when a default partition exists is not
20974 * supported. The main problem is that the default partition
20975 * constraint would change. And there's a definitional problem: what
20976 * should happen to the tuples that are being inserted that belong to
20977 * the partition being detached? Putting them on the partition being
20978 * detached would be wrong, since they'd become "lost" after the
20979 * detaching completes but we cannot put them in the default partition
20980 * either until we alter its partition constraint.
20981 *
20982 * I think we could solve this problem if we effected the constraint
20983 * change before committing the first transaction. But the lock would
20984 * have to remain AEL and it would cause concurrent query planning to
20985 * be blocked, so changing it that way would be even worse.
20986 */
20987 if (concurrent)
20988 ereport(ERROR,
20989 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20990 errmsg("cannot detach partitions concurrently when a default partition exists")));
20991 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20992 }
20993
20994 /*
20995 * In concurrent mode, the partition is locked with share-update-exclusive
20996 * in the first transaction. This allows concurrent transactions to be
20997 * doing DML to the partition.
20998 */
20999 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
21001
21002 /*
21003 * Check inheritance conditions and either delete the pg_inherits row (in
21004 * non-concurrent mode) or just set the inhdetachpending flag.
21005 */
21006 if (!concurrent)
21007 RemoveInheritance(partRel, rel, false);
21008 else
21009 MarkInheritDetached(partRel, rel);
21010
21011 /*
21012 * Ensure that foreign keys still hold after this detach. This keeps
21013 * locks on the referencing tables, which prevents concurrent transactions
21014 * from adding rows that we wouldn't see. For this to work in concurrent
21015 * mode, it is critical that the partition appears as no longer attached
21016 * for the RI queries as soon as the first transaction commits.
21017 */
21019
21020 /*
21021 * Concurrent mode has to work harder; first we add a new constraint to
21022 * the partition that matches the partition constraint. Then we close our
21023 * existing transaction, and in a new one wait for all processes to catch
21024 * up on the catalog updates we've done so far; at that point we can
21025 * complete the operation.
21026 */
21027 if (concurrent)
21028 {
21029 Oid partrelid,
21030 parentrelid;
21031 LOCKTAG tag;
21032 char *parentrelname;
21033 char *partrelname;
21034
21035 /*
21036 * We're almost done now; the only traces that remain are the
21037 * pg_inherits tuple and the partition's relpartbounds. Before we can
21038 * remove those, we need to wait until all transactions that know that
21039 * this is a partition are gone.
21040 */
21041
21042 /*
21043 * Remember relation OIDs to re-acquire them later; and relation names
21044 * too, for error messages if something is dropped in between.
21045 */
21046 partrelid = RelationGetRelid(partRel);
21047 parentrelid = RelationGetRelid(rel);
21048 parentrelname = MemoryContextStrdup(PortalContext,
21050 partrelname = MemoryContextStrdup(PortalContext,
21051 RelationGetRelationName(partRel));
21052
21053 /* Invalidate relcache entries for the parent -- must be before close */
21055
21056 table_close(partRel, NoLock);
21057 table_close(rel, NoLock);
21058 tab->rel = NULL;
21059
21060 /* Make updated catalog entry visible */
21063
21065
21066 /*
21067 * Now wait. This ensures that all queries that were planned
21068 * including the partition are finished before we remove the rest of
21069 * catalog entries. We don't need or indeed want to acquire this
21070 * lock, though -- that would block later queries.
21071 *
21072 * We don't need to concern ourselves with waiting for a lock on the
21073 * partition itself, since we will acquire AccessExclusiveLock below.
21074 */
21075 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
21077
21078 /*
21079 * Now acquire locks in both relations again. Note they may have been
21080 * removed in the meantime, so care is required.
21081 */
21082 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
21083 partRel = try_relation_open(partrelid, AccessExclusiveLock);
21084
21085 /* If the relations aren't there, something bad happened; bail out */
21086 if (rel == NULL)
21087 {
21088 if (partRel != NULL) /* shouldn't happen */
21089 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21090 partrelname);
21091 ereport(ERROR,
21092 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21093 errmsg("partitioned table \"%s\" was removed concurrently",
21094 parentrelname)));
21095 }
21096 if (partRel == NULL)
21097 ereport(ERROR,
21098 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21099 errmsg("partition \"%s\" was removed concurrently", partrelname)));
21100
21101 tab->rel = rel;
21102 }
21103
21104 /*
21105 * Detaching the partition might involve TOAST table access, so ensure we
21106 * have a valid snapshot.
21107 */
21109
21110 /* Do the final part of detaching */
21111 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21112
21114
21115 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21116
21117 /* keep our lock until commit */
21118 table_close(partRel, NoLock);
21119
21120 return address;
21121}
21122
21123/*
21124 * Second part of ALTER TABLE .. DETACH.
21125 *
21126 * This is separate so that it can be run independently when the second
21127 * transaction of the concurrent algorithm fails (crash or abort).
21128 */
21129static void
21130DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
21131 Oid defaultPartOid)
21132{
21133 Relation classRel;
21134 List *fks;
21135 ListCell *cell;
21136 List *indexes;
21137 Datum new_val[Natts_pg_class];
21138 bool new_null[Natts_pg_class],
21139 new_repl[Natts_pg_class];
21140 HeapTuple tuple,
21141 newtuple;
21142 Relation trigrel = NULL;
21143 List *fkoids = NIL;
21144
21145 if (concurrent)
21146 {
21147 /*
21148 * We can remove the pg_inherits row now. (In the non-concurrent case,
21149 * this was already done).
21150 */
21151 RemoveInheritance(partRel, rel, true);
21152 }
21153
21154 /* Drop any triggers that were cloned on creation/attach. */
21156
21157 /*
21158 * Detach any foreign keys that are inherited. This includes creating
21159 * additional action triggers.
21160 */
21161 fks = copyObject(RelationGetFKeyList(partRel));
21162 if (fks != NIL)
21163 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21164
21165 /*
21166 * It's possible that the partition being detached has a foreign key that
21167 * references a partitioned table. When that happens, there are multiple
21168 * pg_constraint rows for the partition: one points to the partitioned
21169 * table itself, while the others point to each of its partitions. Only
21170 * the topmost one is to be considered here; the child constraints must be
21171 * left alone, because conceptually those aren't coming from our parent
21172 * partitioned table, but from this partition itself.
21173 *
21174 * We implement this by collecting all the constraint OIDs in a first scan
21175 * of the FK array, and skipping in the loop below those constraints whose
21176 * parents are listed here.
21177 */
21179 fkoids = lappend_oid(fkoids, fk->conoid);
21180
21181 foreach(cell, fks)
21182 {
21183 ForeignKeyCacheInfo *fk = lfirst(cell);
21184 HeapTuple contup;
21185 Form_pg_constraint conform;
21186
21187 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21188 if (!HeapTupleIsValid(contup))
21189 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21190 conform = (Form_pg_constraint) GETSTRUCT(contup);
21191
21192 /*
21193 * Consider only inherited foreign keys, and only if their parents
21194 * aren't in the list.
21195 */
21196 if (conform->contype != CONSTRAINT_FOREIGN ||
21197 !OidIsValid(conform->conparentid) ||
21198 list_member_oid(fkoids, conform->conparentid))
21199 {
21200 ReleaseSysCache(contup);
21201 continue;
21202 }
21203
21204 /*
21205 * The constraint on this table must be marked no longer a child of
21206 * the parent's constraint, as do its check triggers.
21207 */
21209
21210 /*
21211 * Also, look up the partition's "check" triggers corresponding to the
21212 * ENFORCED constraint being detached and detach them from the parent
21213 * triggers. NOT ENFORCED constraints do not have these triggers;
21214 * therefore, this step is not needed.
21215 */
21216 if (fk->conenforced)
21217 {
21218 Oid insertTriggerOid,
21219 updateTriggerOid;
21220
21222 fk->conoid, fk->confrelid, fk->conrelid,
21223 &insertTriggerOid, &updateTriggerOid);
21224 Assert(OidIsValid(insertTriggerOid));
21225 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21226 RelationGetRelid(partRel));
21227 Assert(OidIsValid(updateTriggerOid));
21228 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21229 RelationGetRelid(partRel));
21230 }
21231
21232 /*
21233 * Lastly, create the action triggers on the referenced table, using
21234 * addFkRecurseReferenced, which requires some elaborate setup (so put
21235 * it in a separate block). While at it, if the table is partitioned,
21236 * that function will recurse to create the pg_constraint rows and
21237 * action triggers for each partition.
21238 *
21239 * Note there's no need to do addFkConstraint() here, because the
21240 * pg_constraint row already exists.
21241 */
21242 {
21243 Constraint *fkconstraint;
21244 int numfks;
21245 AttrNumber conkey[INDEX_MAX_KEYS];
21246 AttrNumber confkey[INDEX_MAX_KEYS];
21247 Oid conpfeqop[INDEX_MAX_KEYS];
21248 Oid conppeqop[INDEX_MAX_KEYS];
21249 Oid conffeqop[INDEX_MAX_KEYS];
21250 int numfkdelsetcols;
21251 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21252 Relation refdRel;
21253
21255 &numfks,
21256 conkey,
21257 confkey,
21258 conpfeqop,
21259 conppeqop,
21260 conffeqop,
21261 &numfkdelsetcols,
21262 confdelsetcols);
21263
21264 /* Create a synthetic node we'll use throughout */
21265 fkconstraint = makeNode(Constraint);
21266 fkconstraint->contype = CONSTRAINT_FOREIGN;
21267 fkconstraint->conname = pstrdup(NameStr(conform->conname));
21268 fkconstraint->deferrable = conform->condeferrable;
21269 fkconstraint->initdeferred = conform->condeferred;
21270 fkconstraint->is_enforced = conform->conenforced;
21271 fkconstraint->skip_validation = true;
21272 fkconstraint->initially_valid = conform->convalidated;
21273 /* a few irrelevant fields omitted here */
21274 fkconstraint->pktable = NULL;
21275 fkconstraint->fk_attrs = NIL;
21276 fkconstraint->pk_attrs = NIL;
21277 fkconstraint->fk_matchtype = conform->confmatchtype;
21278 fkconstraint->fk_upd_action = conform->confupdtype;
21279 fkconstraint->fk_del_action = conform->confdeltype;
21280 fkconstraint->fk_del_set_cols = NIL;
21281 fkconstraint->old_conpfeqop = NIL;
21282 fkconstraint->old_pktable_oid = InvalidOid;
21283 fkconstraint->location = -1;
21284
21285 /* set up colnames, used to generate the constraint name */
21286 for (int i = 0; i < numfks; i++)
21287 {
21289
21290 att = TupleDescAttr(RelationGetDescr(partRel),
21291 conkey[i] - 1);
21292
21293 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21294 makeString(NameStr(att->attname)));
21295 }
21296
21298
21299 addFkRecurseReferenced(fkconstraint, partRel,
21300 refdRel,
21301 conform->conindid,
21302 fk->conoid,
21303 numfks,
21304 confkey,
21305 conkey,
21306 conpfeqop,
21307 conppeqop,
21308 conffeqop,
21309 numfkdelsetcols,
21310 confdelsetcols,
21311 true,
21313 conform->conperiod);
21314 table_close(refdRel, NoLock); /* keep lock till end of xact */
21315 }
21316
21317 ReleaseSysCache(contup);
21318 }
21319 list_free_deep(fks);
21320 if (trigrel)
21321 table_close(trigrel, RowExclusiveLock);
21322
21323 /*
21324 * Any sub-constraints that are in the referenced-side of a larger
21325 * constraint have to be removed. This partition is no longer part of the
21326 * key space of the constraint.
21327 */
21328 foreach(cell, GetParentedForeignKeyRefs(partRel))
21329 {
21330 Oid constrOid = lfirst_oid(cell);
21331 ObjectAddress constraint;
21332
21334 deleteDependencyRecordsForClass(ConstraintRelationId,
21335 constrOid,
21336 ConstraintRelationId,
21339
21340 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21341 performDeletion(&constraint, DROP_RESTRICT, 0);
21342 }
21343
21344 /* Now we can detach indexes */
21345 indexes = RelationGetIndexList(partRel);
21346 foreach(cell, indexes)
21347 {
21348 Oid idxid = lfirst_oid(cell);
21349 Oid parentidx;
21350 Relation idx;
21351 Oid constrOid;
21352 Oid parentConstrOid;
21353
21354 if (!has_superclass(idxid))
21355 continue;
21356
21357 parentidx = get_partition_parent(idxid, false);
21358 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21359
21362
21363 /*
21364 * If there's a constraint associated with the index, detach it too.
21365 * Careful: it is possible for a constraint index in a partition to be
21366 * the child of a non-constraint index, so verify whether the parent
21367 * index does actually have a constraint.
21368 */
21370 idxid);
21372 parentidx);
21373 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21375
21377 }
21378
21379 /* Update pg_class tuple */
21380 classRel = table_open(RelationRelationId, RowExclusiveLock);
21381 tuple = SearchSysCacheCopy1(RELOID,
21383 if (!HeapTupleIsValid(tuple))
21384 elog(ERROR, "cache lookup failed for relation %u",
21385 RelationGetRelid(partRel));
21386 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21387
21388 /* Clear relpartbound and reset relispartition */
21389 memset(new_val, 0, sizeof(new_val));
21390 memset(new_null, false, sizeof(new_null));
21391 memset(new_repl, false, sizeof(new_repl));
21392 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21393 new_null[Anum_pg_class_relpartbound - 1] = true;
21394 new_repl[Anum_pg_class_relpartbound - 1] = true;
21395 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21396 new_val, new_null, new_repl);
21397
21398 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21399 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21400 heap_freetuple(newtuple);
21401 table_close(classRel, RowExclusiveLock);
21402
21403 /*
21404 * Drop identity property from all identity columns of partition.
21405 */
21406 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21407 {
21408 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21409
21410 if (!attr->attisdropped && attr->attidentity)
21411 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21412 AccessExclusiveLock, true, true);
21413 }
21414
21415 if (OidIsValid(defaultPartOid))
21416 {
21417 /*
21418 * If the relation being detached is the default partition itself,
21419 * remove it from the parent's pg_partitioned_table entry.
21420 *
21421 * If not, we must invalidate default partition's relcache entry, as
21422 * in StorePartitionBound: its partition constraint depends on every
21423 * other partition's partition constraint.
21424 */
21425 if (RelationGetRelid(partRel) == defaultPartOid)
21427 else
21428 CacheInvalidateRelcacheByRelid(defaultPartOid);
21429 }
21430
21431 /*
21432 * Invalidate the parent's relcache so that the partition is no longer
21433 * included in its partition descriptor.
21434 */
21436
21437 /*
21438 * If the partition we just detached is partitioned itself, invalidate
21439 * relcache for all descendent partitions too to ensure that their
21440 * rd_partcheck expression trees are rebuilt; must lock partitions before
21441 * doing so, using the same lockmode as what partRel has been locked with
21442 * by the caller.
21443 */
21444 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21445 {
21446 List *children;
21447
21448 children = find_all_inheritors(RelationGetRelid(partRel),
21449 AccessExclusiveLock, NULL);
21450 foreach(cell, children)
21451 {
21453 }
21454 }
21455}
21456
21457/*
21458 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21459 *
21460 * To use when a DETACH PARTITION command previously did not run to
21461 * completion; this completes the detaching process.
21462 */
21463static ObjectAddress
21465{
21466 Relation partRel;
21467 ObjectAddress address;
21468 Snapshot snap = GetActiveSnapshot();
21469
21471
21472 /*
21473 * Wait until existing snapshots are gone. This is important if the
21474 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21475 * user could immediately run DETACH FINALIZE without actually waiting for
21476 * existing transactions. We must not complete the detach action until
21477 * all such queries are complete (otherwise we would present them with an
21478 * inconsistent view of catalogs).
21479 */
21480 WaitForOlderSnapshots(snap->xmin, false);
21481
21482 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21483
21484 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21485
21486 table_close(partRel, NoLock);
21487
21488 return address;
21489}
21490
21491/*
21492 * DropClonedTriggersFromPartition
21493 * subroutine for ATExecDetachPartition to remove any triggers that were
21494 * cloned to the partition when it was created-as-partition or attached.
21495 * This undoes what CloneRowTriggersToPartition did.
21496 */
21497static void
21499{
21500 ScanKeyData skey;
21501 SysScanDesc scan;
21502 HeapTuple trigtup;
21503 Relation tgrel;
21504 ObjectAddresses *objects;
21505
21506 objects = new_object_addresses();
21507
21508 /*
21509 * Scan pg_trigger to search for all triggers on this rel.
21510 */
21511 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21512 F_OIDEQ, ObjectIdGetDatum(partitionId));
21513 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21514 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21515 true, NULL, 1, &skey);
21516 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21517 {
21518 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21519 ObjectAddress trig;
21520
21521 /* Ignore triggers that weren't cloned */
21522 if (!OidIsValid(pg_trigger->tgparentid))
21523 continue;
21524
21525 /*
21526 * Ignore internal triggers that are implementation objects of foreign
21527 * keys, because these will be detached when the foreign keys
21528 * themselves are.
21529 */
21530 if (OidIsValid(pg_trigger->tgconstrrelid))
21531 continue;
21532
21533 /*
21534 * This is ugly, but necessary: remove the dependency markings on the
21535 * trigger so that it can be removed.
21536 */
21537 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21538 TriggerRelationId,
21540 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21541 RelationRelationId,
21543
21544 /* remember this trigger to remove it below */
21545 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21546 add_exact_object_address(&trig, objects);
21547 }
21548
21549 /* make the dependency removal visible to the deletion below */
21552
21553 /* done */
21554 free_object_addresses(objects);
21555 systable_endscan(scan);
21557}
21558
21559/*
21560 * Before acquiring lock on an index, acquire the same lock on the owning
21561 * table.
21562 */
21564{
21568};
21569
21570static void
21571RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21572 void *arg)
21573{
21575 Form_pg_class classform;
21576 HeapTuple tuple;
21577
21578 state = (struct AttachIndexCallbackState *) arg;
21579
21580 if (!state->lockedParentTbl)
21581 {
21582 LockRelationOid(state->parentTblOid, AccessShareLock);
21583 state->lockedParentTbl = true;
21584 }
21585
21586 /*
21587 * If we previously locked some other heap, and the name we're looking up
21588 * no longer refers to an index on that relation, release the now-useless
21589 * lock. XXX maybe we should do *after* we verify whether the index does
21590 * not actually belong to the same relation ...
21591 */
21592 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21593 {
21594 UnlockRelationOid(state->partitionOid, AccessShareLock);
21595 state->partitionOid = InvalidOid;
21596 }
21597
21598 /* Didn't find a relation, so no need for locking or permission checks. */
21599 if (!OidIsValid(relOid))
21600 return;
21601
21602 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21603 if (!HeapTupleIsValid(tuple))
21604 return; /* concurrently dropped, so nothing to do */
21605 classform = (Form_pg_class) GETSTRUCT(tuple);
21606 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21607 classform->relkind != RELKIND_INDEX)
21608 ereport(ERROR,
21609 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21610 errmsg("\"%s\" is not an index", rv->relname)));
21611 ReleaseSysCache(tuple);
21612
21613 /*
21614 * Since we need only examine the heap's tupledesc, an access share lock
21615 * on it (preventing any DDL) is sufficient.
21616 */
21617 state->partitionOid = IndexGetRelation(relOid, false);
21618 LockRelationOid(state->partitionOid, AccessShareLock);
21619}
21620
21621/*
21622 * ALTER INDEX i1 ATTACH PARTITION i2
21623 */
21624static ObjectAddress
21626{
21627 Relation partIdx;
21628 Relation partTbl;
21629 Relation parentTbl;
21630 ObjectAddress address;
21631 Oid partIdxId;
21632 Oid currParent;
21634
21635 /*
21636 * We need to obtain lock on the index 'name' to modify it, but we also
21637 * need to read its owning table's tuple descriptor -- so we need to lock
21638 * both. To avoid deadlocks, obtain lock on the table before doing so on
21639 * the index. Furthermore, we need to examine the parent table of the
21640 * partition, so lock that one too.
21641 */
21642 state.partitionOid = InvalidOid;
21643 state.parentTblOid = parentIdx->rd_index->indrelid;
21644 state.lockedParentTbl = false;
21645 partIdxId =
21648 &state);
21649 /* Not there? */
21650 if (!OidIsValid(partIdxId))
21651 ereport(ERROR,
21652 (errcode(ERRCODE_UNDEFINED_OBJECT),
21653 errmsg("index \"%s\" does not exist", name->relname)));
21654
21655 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21656 partIdx = relation_open(partIdxId, AccessExclusiveLock);
21657
21658 /* we already hold locks on both tables, so this is safe: */
21659 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21660 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21661
21662 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21663
21664 /* Silently do nothing if already in the right state */
21665 currParent = partIdx->rd_rel->relispartition ?
21666 get_partition_parent(partIdxId, false) : InvalidOid;
21667 if (currParent != RelationGetRelid(parentIdx))
21668 {
21669 IndexInfo *childInfo;
21670 IndexInfo *parentInfo;
21671 AttrMap *attmap;
21672 bool found;
21673 int i;
21674 PartitionDesc partDesc;
21675 Oid constraintOid,
21676 cldConstrId = InvalidOid;
21677
21678 /*
21679 * If this partition already has an index attached, refuse the
21680 * operation.
21681 */
21682 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21683
21684 if (OidIsValid(currParent))
21685 ereport(ERROR,
21686 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21687 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21688 RelationGetRelationName(partIdx),
21689 RelationGetRelationName(parentIdx)),
21690 errdetail("Index \"%s\" is already attached to another index.",
21691 RelationGetRelationName(partIdx))));
21692
21693 /* Make sure it indexes a partition of the other index's table */
21694 partDesc = RelationGetPartitionDesc(parentTbl, true);
21695 found = false;
21696 for (i = 0; i < partDesc->nparts; i++)
21697 {
21698 if (partDesc->oids[i] == state.partitionOid)
21699 {
21700 found = true;
21701 break;
21702 }
21703 }
21704 if (!found)
21705 ereport(ERROR,
21706 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21707 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21708 RelationGetRelationName(partIdx),
21709 RelationGetRelationName(parentIdx)),
21710 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21711 RelationGetRelationName(partIdx),
21712 RelationGetRelationName(parentTbl))));
21713
21714 /* Ensure the indexes are compatible */
21715 childInfo = BuildIndexInfo(partIdx);
21716 parentInfo = BuildIndexInfo(parentIdx);
21717 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21718 RelationGetDescr(parentTbl),
21719 false);
21720 if (!CompareIndexInfo(childInfo, parentInfo,
21721 partIdx->rd_indcollation,
21722 parentIdx->rd_indcollation,
21723 partIdx->rd_opfamily,
21724 parentIdx->rd_opfamily,
21725 attmap))
21726 ereport(ERROR,
21727 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21728 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21729 RelationGetRelationName(partIdx),
21730 RelationGetRelationName(parentIdx)),
21731 errdetail("The index definitions do not match.")));
21732
21733 /*
21734 * If there is a constraint in the parent, make sure there is one in
21735 * the child too.
21736 */
21737 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21738 RelationGetRelid(parentIdx));
21739
21740 if (OidIsValid(constraintOid))
21741 {
21743 partIdxId);
21744 if (!OidIsValid(cldConstrId))
21745 ereport(ERROR,
21746 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21747 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21748 RelationGetRelationName(partIdx),
21749 RelationGetRelationName(parentIdx)),
21750 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21751 RelationGetRelationName(parentIdx),
21752 RelationGetRelationName(parentTbl),
21753 RelationGetRelationName(partIdx))));
21754 }
21755
21756 /*
21757 * If it's a primary key, make sure the columns in the partition are
21758 * NOT NULL.
21759 */
21760 if (parentIdx->rd_index->indisprimary)
21761 verifyPartitionIndexNotNull(childInfo, partTbl);
21762
21763 /* All good -- do it */
21764 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21765 if (OidIsValid(constraintOid))
21766 ConstraintSetParentConstraint(cldConstrId, constraintOid,
21767 RelationGetRelid(partTbl));
21768
21769 free_attrmap(attmap);
21770
21771 validatePartitionedIndex(parentIdx, parentTbl);
21772 }
21773
21774 relation_close(parentTbl, AccessShareLock);
21775 /* keep these locks till commit */
21776 relation_close(partTbl, NoLock);
21777 relation_close(partIdx, NoLock);
21778
21779 return address;
21780}
21781
21782/*
21783 * Verify whether the given partition already contains an index attached
21784 * to the given partitioned index. If so, raise an error.
21785 */
21786static void
21787refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21788{
21789 Oid existingIdx;
21790
21791 existingIdx = index_get_partition(partitionTbl,
21792 RelationGetRelid(parentIdx));
21793 if (OidIsValid(existingIdx))
21794 ereport(ERROR,
21795 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21796 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21797 RelationGetRelationName(partIdx),
21798 RelationGetRelationName(parentIdx)),
21799 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21800 get_rel_name(existingIdx),
21801 RelationGetRelationName(partitionTbl))));
21802}
21803
21804/*
21805 * Verify whether the set of attached partition indexes to a parent index on
21806 * a partitioned table is complete. If it is, mark the parent index valid.
21807 *
21808 * This should be called each time a partition index is attached.
21809 */
21810static void
21812{
21813 Relation inheritsRel;
21814 SysScanDesc scan;
21816 int tuples = 0;
21817 HeapTuple inhTup;
21818 bool updated = false;
21819
21820 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21821
21822 /*
21823 * Scan pg_inherits for this parent index. Count each valid index we find
21824 * (verifying the pg_index entry for each), and if we reach the total
21825 * amount we expect, we can mark this parent index as valid.
21826 */
21827 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21828 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21829 BTEqualStrategyNumber, F_OIDEQ,
21831 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21832 NULL, 1, &key);
21833 while ((inhTup = systable_getnext(scan)) != NULL)
21834 {
21835 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21836 HeapTuple indTup;
21837 Form_pg_index indexForm;
21838
21839 indTup = SearchSysCache1(INDEXRELID,
21840 ObjectIdGetDatum(inhForm->inhrelid));
21841 if (!HeapTupleIsValid(indTup))
21842 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21843 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21844 if (indexForm->indisvalid)
21845 tuples += 1;
21846 ReleaseSysCache(indTup);
21847 }
21848
21849 /* Done with pg_inherits */
21850 systable_endscan(scan);
21851 table_close(inheritsRel, AccessShareLock);
21852
21853 /*
21854 * If we found as many inherited indexes as the partitioned table has
21855 * partitions, we're good; update pg_index to set indisvalid.
21856 */
21857 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21858 {
21859 Relation idxRel;
21860 HeapTuple indTup;
21861 Form_pg_index indexForm;
21862
21863 idxRel = table_open(IndexRelationId, RowExclusiveLock);
21864 indTup = SearchSysCacheCopy1(INDEXRELID,
21866 if (!HeapTupleIsValid(indTup))
21867 elog(ERROR, "cache lookup failed for index %u",
21868 RelationGetRelid(partedIdx));
21869 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21870
21871 indexForm->indisvalid = true;
21872 updated = true;
21873
21874 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21875
21877 heap_freetuple(indTup);
21878 }
21879
21880 /*
21881 * If this index is in turn a partition of a larger index, validating it
21882 * might cause the parent to become valid also. Try that.
21883 */
21884 if (updated && partedIdx->rd_rel->relispartition)
21885 {
21886 Oid parentIdxId,
21887 parentTblId;
21888 Relation parentIdx,
21889 parentTbl;
21890
21891 /* make sure we see the validation we just did */
21893
21894 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21895 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21896 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21897 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21898 Assert(!parentIdx->rd_index->indisvalid);
21899
21900 validatePartitionedIndex(parentIdx, parentTbl);
21901
21904 }
21905}
21906
21907/*
21908 * When attaching an index as a partition of a partitioned index which is a
21909 * primary key, verify that all the columns in the partition are marked NOT
21910 * NULL.
21911 */
21912static void
21914{
21915 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21916 {
21918 iinfo->ii_IndexAttrNumbers[i] - 1);
21919
21920 if (!att->attnotnull)
21921 ereport(ERROR,
21922 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21923 errmsg("invalid primary key definition"),
21924 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21925 NameStr(att->attname),
21926 RelationGetRelationName(partition)));
21927 }
21928}
21929
21930/*
21931 * Return an OID list of constraints that reference the given relation
21932 * that are marked as having a parent constraints.
21933 */
21934static List *
21936{
21937 Relation pg_constraint;
21938 HeapTuple tuple;
21939 SysScanDesc scan;
21940 ScanKeyData key[2];
21941 List *constraints = NIL;
21942
21943 /*
21944 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21945 * scan.
21946 */
21947 if (RelationGetIndexList(partition) == NIL ||
21950 return NIL;
21951
21952 /* Search for constraints referencing this table */
21953 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21954 ScanKeyInit(&key[0],
21955 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21956 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21957 ScanKeyInit(&key[1],
21958 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21959 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21960
21961 /* XXX This is a seqscan, as we don't have a usable index */
21962 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21963 while ((tuple = systable_getnext(scan)) != NULL)
21964 {
21965 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21966
21967 /*
21968 * We only need to process constraints that are part of larger ones.
21969 */
21970 if (!OidIsValid(constrForm->conparentid))
21971 continue;
21972
21973 constraints = lappend_oid(constraints, constrForm->oid);
21974 }
21975
21976 systable_endscan(scan);
21977 table_close(pg_constraint, AccessShareLock);
21978
21979 return constraints;
21980}
21981
21982/*
21983 * During DETACH PARTITION, verify that any foreign keys pointing to the
21984 * partitioned table would not become invalid. An error is raised if any
21985 * referenced values exist.
21986 */
21987static void
21989{
21990 List *constraints;
21991 ListCell *cell;
21992
21993 constraints = GetParentedForeignKeyRefs(partition);
21994
21995 foreach(cell, constraints)
21996 {
21997 Oid constrOid = lfirst_oid(cell);
21998 HeapTuple tuple;
21999 Form_pg_constraint constrForm;
22000 Relation rel;
22001 Trigger trig = {0};
22002
22003 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
22004 if (!HeapTupleIsValid(tuple))
22005 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22006 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
22007
22008 Assert(OidIsValid(constrForm->conparentid));
22009 Assert(constrForm->confrelid == RelationGetRelid(partition));
22010
22011 /* prevent data changes into the referencing table until commit */
22012 rel = table_open(constrForm->conrelid, ShareLock);
22013
22014 trig.tgoid = InvalidOid;
22015 trig.tgname = NameStr(constrForm->conname);
22017 trig.tgisinternal = true;
22018 trig.tgconstrrelid = RelationGetRelid(partition);
22019 trig.tgconstrindid = constrForm->conindid;
22020 trig.tgconstraint = constrForm->oid;
22021 trig.tgdeferrable = false;
22022 trig.tginitdeferred = false;
22023 /* we needn't fill in remaining fields */
22024
22025 RI_PartitionRemove_Check(&trig, rel, partition);
22026
22027 ReleaseSysCache(tuple);
22028
22029 table_close(rel, NoLock);
22030 }
22031}
22032
22033/*
22034 * resolve column compression specification to compression method.
22035 */
22036static char
22037GetAttributeCompression(Oid atttypid, const char *compression)
22038{
22039 char cmethod;
22040
22041 if (compression == NULL || strcmp(compression, "default") == 0)
22043
22044 /*
22045 * To specify a nondefault method, the column data type must be toastable.
22046 * Note this says nothing about whether the column's attstorage setting
22047 * permits compression; we intentionally allow attstorage and
22048 * attcompression to be independent. But with a non-toastable type,
22049 * attstorage could not be set to a value that would permit compression.
22050 *
22051 * We don't actually need to enforce this, since nothing bad would happen
22052 * if attcompression were non-default; it would never be consulted. But
22053 * it seems more user-friendly to complain about a certainly-useless
22054 * attempt to set the property.
22055 */
22056 if (!TypeIsToastable(atttypid))
22057 ereport(ERROR,
22058 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22059 errmsg("column data type %s does not support compression",
22060 format_type_be(atttypid))));
22061
22062 cmethod = CompressionNameToMethod(compression);
22063 if (!CompressionMethodIsValid(cmethod))
22064 ereport(ERROR,
22065 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22066 errmsg("invalid compression method \"%s\"", compression)));
22067
22068 return cmethod;
22069}
22070
22071/*
22072 * resolve column storage specification
22073 */
22074static char
22075GetAttributeStorage(Oid atttypid, const char *storagemode)
22076{
22077 char cstorage = 0;
22078
22079 if (pg_strcasecmp(storagemode, "plain") == 0)
22080 cstorage = TYPSTORAGE_PLAIN;
22081 else if (pg_strcasecmp(storagemode, "external") == 0)
22082 cstorage = TYPSTORAGE_EXTERNAL;
22083 else if (pg_strcasecmp(storagemode, "extended") == 0)
22084 cstorage = TYPSTORAGE_EXTENDED;
22085 else if (pg_strcasecmp(storagemode, "main") == 0)
22086 cstorage = TYPSTORAGE_MAIN;
22087 else if (pg_strcasecmp(storagemode, "default") == 0)
22088 cstorage = get_typstorage(atttypid);
22089 else
22090 ereport(ERROR,
22091 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22092 errmsg("invalid storage type \"%s\"",
22093 storagemode)));
22094
22095 /*
22096 * safety check: do not allow toasted storage modes unless column datatype
22097 * is TOAST-aware.
22098 */
22099 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22100 ereport(ERROR,
22101 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22102 errmsg("column data type %s can only have storage PLAIN",
22103 format_type_be(atttypid))));
22104
22105 return cstorage;
22106}
22107
22108/*
22109 * buildExpressionExecutionStates: build the needed expression execution states
22110 * for new partition (newPartRel) checks and initialize expressions for
22111 * generated columns. All expressions should be created in "tab"
22112 * (AlteredTableInfo structure).
22113 */
22114static void
22116{
22117 /*
22118 * Build the needed expression execution states. Here, we expect only NOT
22119 * NULL and CHECK constraint.
22120 */
22122 {
22123 switch (con->contype)
22124 {
22125 case CONSTR_CHECK:
22126
22127 /*
22128 * We already expanded virtual expression in
22129 * createTableConstraints.
22130 */
22131 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22132 break;
22133 case CONSTR_NOTNULL:
22134 /* Nothing to do here. */
22135 break;
22136 default:
22137 elog(ERROR, "unrecognized constraint type: %d",
22138 (int) con->contype);
22139 }
22140 }
22141
22142 /* Expression already planned in createTableConstraints */
22144 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22145}
22146
22147/*
22148 * evaluateGeneratedExpressionsAndCheckConstraints: evaluate any generated
22149 * expressions for "tab" (AlteredTableInfo structure) whose inputs come from
22150 * the new tuple (insertslot) of the new partition (newPartRel).
22151 */
22152static void
22154 Relation newPartRel,
22155 TupleTableSlot *insertslot,
22156 ExprContext *econtext)
22157{
22158 econtext->ecxt_scantuple = insertslot;
22159
22161 {
22162 if (!ex->is_generated)
22163 continue;
22164
22165 insertslot->tts_values[ex->attnum - 1]
22166 = ExecEvalExpr(ex->exprstate,
22167 econtext,
22168 &insertslot->tts_isnull[ex->attnum - 1]);
22169 }
22170
22172 {
22173 switch (con->contype)
22174 {
22175 case CONSTR_CHECK:
22176 if (!ExecCheck(con->qualstate, econtext))
22177 ereport(ERROR,
22178 errcode(ERRCODE_CHECK_VIOLATION),
22179 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22180 con->name, RelationGetRelationName(newPartRel)),
22181 errtableconstraint(newPartRel, con->name));
22182 break;
22183 case CONSTR_NOTNULL:
22184 case CONSTR_FOREIGN:
22185 /* Nothing to do here */
22186 break;
22187 default:
22188 elog(ERROR, "unrecognized constraint type: %d",
22189 (int) con->contype);
22190 }
22191 }
22192}
22193
22194/*
22195 * getAttributesList: build a list of columns (ColumnDef) based on parent_rel
22196 */
22197static List *
22199{
22200 AttrNumber parent_attno;
22201 TupleDesc modelDesc;
22202 List *colList = NIL;
22203
22204 modelDesc = RelationGetDescr(parent_rel);
22205
22206 for (parent_attno = 1; parent_attno <= modelDesc->natts;
22207 parent_attno++)
22208 {
22209 Form_pg_attribute attribute = TupleDescAttr(modelDesc,
22210 parent_attno - 1);
22211 ColumnDef *def;
22212
22213 /* Ignore dropped columns in the parent. */
22214 if (attribute->attisdropped)
22215 continue;
22216
22217 def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22218 attribute->atttypmod, attribute->attcollation);
22219
22220 def->is_not_null = attribute->attnotnull;
22221
22222 /* Copy identity. */
22223 def->identity = attribute->attidentity;
22224
22225 /* Copy attgenerated. */
22226 def->generated = attribute->attgenerated;
22227
22228 def->storage = attribute->attstorage;
22229
22230 /* Likewise, copy compression. */
22231 if (CompressionMethodIsValid(attribute->attcompression))
22232 def->compression =
22233 pstrdup(GetCompressionMethodName(attribute->attcompression));
22234 else
22235 def->compression = NULL;
22236
22237 /* Add to column list. */
22238 colList = lappend(colList, def);
22239 }
22240
22241 return colList;
22242}
22243
22244/*
22245 * createTableConstraints:
22246 * create check constraints, default values, and generated values for newRel
22247 * based on parent_rel. tab is pending-work queue for newRel, we may need it in
22248 * MergePartitionsMoveRows.
22249 */
22250static void
22252 Relation parent_rel, Relation newRel)
22253{
22254 TupleDesc tupleDesc;
22255 TupleConstr *constr;
22256 AttrMap *attmap;
22257 AttrNumber parent_attno;
22258 int ccnum;
22259 List *constraints = NIL;
22260 List *cookedConstraints = NIL;
22261
22262 tupleDesc = RelationGetDescr(parent_rel);
22263 constr = tupleDesc->constr;
22264
22265 if (!constr)
22266 return;
22267
22268 /*
22269 * Construct a map from the parent relation's attnos to the child rel's.
22270 * This re-checks type match, etc, although it shouldn't be possible to
22271 * have a failure since both tables are locked.
22272 */
22273 attmap = build_attrmap_by_name(RelationGetDescr(newRel),
22274 tupleDesc,
22275 false);
22276
22277 /* Cycle for default values. */
22278 for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
22279 {
22280 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
22281 parent_attno - 1);
22282
22283 /* Ignore dropped columns in the parent. */
22284 if (attribute->attisdropped)
22285 continue;
22286
22287 /* Copy the default, if present, and it should be copied. */
22288 if (attribute->atthasdef)
22289 {
22290 Node *this_default = NULL;
22291 bool found_whole_row;
22292 AttrNumber num;
22293 Node *def;
22295
22296 if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
22297 this_default = build_generation_expression(parent_rel, attribute->attnum);
22298 else
22299 {
22300 this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
22301 if (this_default == NULL)
22302 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
22303 attribute->attnum, RelationGetRelationName(parent_rel));
22304 }
22305
22306 num = attmap->attnums[parent_attno - 1];
22307 def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
22308
22309 if (found_whole_row && attribute->attgenerated != '\0')
22310 elog(ERROR, "cannot convert whole-row table reference");
22311
22312 /* Add a pre-cooked default expression. */
22313 StoreAttrDefault(newRel, num, def, true);
22314
22315 /*
22316 * Stored generated column expressions in parent_rel might
22317 * reference the tableoid. newRel, parent_rel tableoid clear is
22318 * not the same. If so, these stored generated columns require
22319 * recomputation for newRel within MergePartitionsMoveRows.
22320 */
22321 if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
22322 {
22324 newval->attnum = num;
22325 newval->expr = expression_planner((Expr *) def);
22326 newval->is_generated = (attribute->attgenerated != '\0');
22327 tab->newvals = lappend(tab->newvals, newval);
22328 }
22329 }
22330 }
22331
22332 /* Cycle for CHECK constraints. */
22333 for (ccnum = 0; ccnum < constr->num_check; ccnum++)
22334 {
22335 char *ccname = constr->check[ccnum].ccname;
22336 char *ccbin = constr->check[ccnum].ccbin;
22337 bool ccenforced = constr->check[ccnum].ccenforced;
22338 bool ccnoinherit = constr->check[ccnum].ccnoinherit;
22339 bool ccvalid = constr->check[ccnum].ccvalid;
22340 Node *ccbin_node;
22341 bool found_whole_row;
22342 Constraint *constr;
22343
22344 /*
22345 * The partitioned table can not have a NO INHERIT check constraint
22346 * (see StoreRelCheck function for details).
22347 */
22348 Assert(!ccnoinherit);
22349
22350 ccbin_node = map_variable_attnos(stringToNode(ccbin),
22351 1, 0,
22352 attmap,
22353 InvalidOid, &found_whole_row);
22354
22355 /*
22356 * For the moment we have to reject whole-row variables (as for CREATE
22357 * TABLE LIKE and inheritances).
22358 */
22359 if (found_whole_row)
22360 elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
22361 ccname,
22362 RelationGetRelationName(parent_rel));
22363
22364 constr = makeNode(Constraint);
22365 constr->contype = CONSTR_CHECK;
22366 constr->conname = pstrdup(ccname);
22367 constr->deferrable = false;
22368 constr->initdeferred = false;
22369 constr->is_enforced = ccenforced;
22370 constr->skip_validation = !ccvalid;
22371 constr->initially_valid = ccvalid;
22372 constr->is_no_inherit = ccnoinherit;
22373 constr->raw_expr = NULL;
22374 constr->cooked_expr = nodeToString(ccbin_node);
22375 constr->location = -1;
22376 constraints = lappend(constraints, constr);
22377 }
22378
22379 /* Install all CHECK constraints. */
22380 cookedConstraints = AddRelationNewConstraints(newRel, NIL, constraints,
22381 false, true, true, NULL);
22382
22383 /* Make the additional catalog changes visible. */
22385
22386 /*
22387 * parent_rel check constraint expression may reference tableoid, so later
22388 * in MergePartitionsMoveRows, we need to evaluate the check constraint
22389 * again for the newRel. We can check whether the check constraint
22390 * contains a tableoid reference via pull_varattnos.
22391 */
22392 foreach_ptr(CookedConstraint, ccon, cookedConstraints)
22393 {
22394 if (!ccon->skip_validation)
22395 {
22396 Node *qual;
22397 Bitmapset *attnums = NULL;
22398
22399 Assert(ccon->contype == CONSTR_CHECK);
22400 qual = expand_generated_columns_in_expr(ccon->expr, newRel, 1);
22401 pull_varattnos(qual, 1, &attnums);
22402
22403 /*
22404 * Add a check only if it contains a tableoid
22405 * (TableOidAttributeNumber).
22406 */
22408 attnums))
22409 {
22410 NewConstraint *newcon;
22411
22412 newcon = palloc0_object(NewConstraint);
22413 newcon->name = ccon->name;
22414 newcon->contype = CONSTR_CHECK;
22415 newcon->qual = qual;
22416
22417 tab->constraints = lappend(tab->constraints, newcon);
22418 }
22419 }
22420 }
22421
22422 /* Don't need the cookedConstraints anymore. */
22423 list_free_deep(cookedConstraints);
22424
22425 /* Reproduce not-null constraints. */
22426 if (constr->has_not_null)
22427 {
22428 List *nnconstraints;
22429
22430 /*
22431 * The "include_noinh" argument is false because a partitioned table
22432 * can't have NO INHERIT constraint.
22433 */
22434 nnconstraints = RelationGetNotNullConstraints(RelationGetRelid(parent_rel),
22435 false, false);
22436
22437 Assert(list_length(nnconstraints) > 0);
22438
22439 /*
22440 * We already set pg_attribute.attnotnull in createPartitionTable. No
22441 * need call set_attnotnull again.
22442 */
22443 AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, true, NULL);
22444 }
22445}
22446
22447/*
22448 * createPartitionTable:
22449 *
22450 * Create a new partition (newPartName) for the partitioned table (parent_rel).
22451 * ownerId is determined by the partition on which the operation is performed,
22452 * so it is passed separately. The new partition will inherit the access method
22453 * and persistence type from the parent table.
22454 *
22455 * Returns the created relation (locked in AccessExclusiveLock mode).
22456 */
22457static Relation
22458createPartitionTable(List **wqueue, RangeVar *newPartName,
22459 Relation parent_rel, Oid ownerId)
22460{
22461 Relation newRel;
22462 Oid newRelId;
22463 Oid existingRelid;
22465 List *colList = NIL;
22466 Oid relamId;
22467 Oid namespaceId;
22468 AlteredTableInfo *new_partrel_tab;
22469 Form_pg_class parent_relform = parent_rel->rd_rel;
22470
22471 /* If the existing rel is temp, it must belong to this session. */
22472 if (RELATION_IS_OTHER_TEMP(parent_rel))
22473 ereport(ERROR,
22474 errcode(ERRCODE_WRONG_OBJECT_TYPE),
22475 errmsg("cannot create as partition of temporary relation of another session"));
22476
22477 /* Look up inheritance ancestors and generate the relation schema. */
22478 colList = getAttributesList(parent_rel);
22479
22480 /* Create a tuple descriptor from the relation schema. */
22482
22483 /* Look up the access method for the new relation. */
22484 relamId = (parent_relform->relam != InvalidOid) ? parent_relform->relam : HEAP_TABLE_AM_OID;
22485
22486 /* Look up the namespace in which we are supposed to create the relation. */
22487 namespaceId =
22488 RangeVarGetAndCheckCreationNamespace(newPartName, NoLock, &existingRelid);
22489 if (OidIsValid(existingRelid))
22490 ereport(ERROR,
22491 errcode(ERRCODE_DUPLICATE_TABLE),
22492 errmsg("relation \"%s\" already exists", newPartName->relname));
22493
22494 /*
22495 * We intended to create the partition with the same persistence as the
22496 * parent table, but we still need to recheck because that might be
22497 * affected by the search_path. If the parent is permanent, so must be
22498 * all of its partitions.
22499 */
22500 if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22501 newPartName->relpersistence == RELPERSISTENCE_TEMP)
22502 ereport(ERROR,
22503 errcode(ERRCODE_WRONG_OBJECT_TYPE),
22504 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22505 RelationGetRelationName(parent_rel)));
22506
22507 /* Permanent rels cannot be partitions belonging to a temporary parent. */
22508 if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22509 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22510 ereport(ERROR,
22511 errcode(ERRCODE_WRONG_OBJECT_TYPE),
22512 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
22513 RelationGetRelationName(parent_rel)));
22514
22515 /* Create the relation. */
22516 newRelId = heap_create_with_catalog(newPartName->relname,
22517 namespaceId,
22518 parent_relform->reltablespace,
22519 InvalidOid,
22520 InvalidOid,
22521 InvalidOid,
22522 ownerId,
22523 relamId,
22524 descriptor,
22525 NIL,
22526 RELKIND_RELATION,
22527 newPartName->relpersistence,
22528 false,
22529 false,
22531 (Datum) 0,
22532 true,
22534 true,
22535 InvalidOid,
22536 NULL);
22537
22538 /*
22539 * We must bump the command counter to make the newly-created relation
22540 * tuple visible for opening.
22541 */
22543
22544 /*
22545 * Open the new partition with no lock, because we already have an
22546 * AccessExclusiveLock placed there after creation.
22547 */
22548 newRel = table_open(newRelId, NoLock);
22549
22550 /* Find or create a work queue entry for the newly created table. */
22551 new_partrel_tab = ATGetQueueEntry(wqueue, newRel);
22552
22553 /* Create constraints, default values, and generated values. */
22554 createTableConstraints(wqueue, new_partrel_tab, parent_rel, newRel);
22555
22556 /*
22557 * Need to call CommandCounterIncrement, so a fresh relcache entry has
22558 * newly installed constraint info.
22559 */
22561
22562 return newRel;
22563}
22564
22565/*
22566 * MergePartitionsMoveRows: scan partitions to be merged (mergingPartitions)
22567 * of the partitioned table and move rows into the new partition
22568 * (newPartRel). We also verify check constraints against these rows.
22569 */
22570static void
22571MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
22572{
22573 CommandId mycid;
22574 EState *estate;
22575 AlteredTableInfo *tab;
22576 ListCell *ltab;
22577
22578 /* The FSM is empty, so don't bother using it. */
22579 int ti_options = TABLE_INSERT_SKIP_FSM;
22580 BulkInsertState bistate; /* state of bulk inserts for partition */
22581 TupleTableSlot *dstslot;
22582
22583 /* Find the work queue entry for the new partition table: newPartRel. */
22584 tab = ATGetQueueEntry(wqueue, newPartRel);
22585
22586 /* Generate the constraint and default execution states. */
22587 estate = CreateExecutorState();
22588
22589 buildExpressionExecutionStates(tab, newPartRel, estate);
22590
22591 mycid = GetCurrentCommandId(true);
22592
22593 /* Prepare a BulkInsertState for table_tuple_insert. */
22594 bistate = GetBulkInsertState();
22595
22596 /* Create the necessary tuple slot. */
22597 dstslot = table_slot_create(newPartRel, NULL);
22598
22599 foreach_oid(merging_oid, mergingPartitions)
22600 {
22601 ExprContext *econtext;
22602 TupleTableSlot *srcslot;
22603 TupleConversionMap *tuple_map;
22604 TableScanDesc scan;
22605 MemoryContext oldCxt;
22606 Snapshot snapshot;
22607 Relation mergingPartition;
22608
22609 econtext = GetPerTupleExprContext(estate);
22610
22611 /*
22612 * Partition is already locked in the transformPartitionCmdForMerge
22613 * function.
22614 */
22615 mergingPartition = table_open(merging_oid, NoLock);
22616
22617 /* Create a source tuple slot for the partition being merged. */
22618 srcslot = table_slot_create(mergingPartition, NULL);
22619
22620 /*
22621 * Map computing for moving attributes of the merged partition to the
22622 * new partition.
22623 */
22624 tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
22625 RelationGetDescr(newPartRel));
22626
22627 /* Scan through the rows. */
22628 snapshot = RegisterSnapshot(GetLatestSnapshot());
22629 scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
22630
22631 /*
22632 * Switch to per-tuple memory context and reset it for each tuple
22633 * produced, so we don't leak memory.
22634 */
22636
22637 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
22638 {
22639 TupleTableSlot *insertslot;
22640
22642
22643 if (tuple_map)
22644 {
22645 /* Need to use a map to copy attributes. */
22646 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
22647 }
22648 else
22649 {
22650 slot_getallattrs(srcslot);
22651
22652 /* Copy attributes directly. */
22653 insertslot = dstslot;
22654
22655 ExecClearTuple(insertslot);
22656
22657 memcpy(insertslot->tts_values, srcslot->tts_values,
22658 sizeof(Datum) * srcslot->tts_nvalid);
22659 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
22660 sizeof(bool) * srcslot->tts_nvalid);
22661
22662 ExecStoreVirtualTuple(insertslot);
22663 }
22664
22665 /*
22666 * Constraints and GENERATED expressions might reference the
22667 * tableoid column, so fill tts_tableOid with the desired value.
22668 * (We must do this each time, because it gets overwritten with
22669 * newrel's OID during storing.)
22670 */
22671 insertslot->tts_tableOid = RelationGetRelid(newPartRel);
22672
22673 /*
22674 * Now, evaluate any generated expressions whose inputs come from
22675 * the new tuple. We assume these columns won't reference each
22676 * other, so that there's no ordering dependency.
22677 */
22679 insertslot, econtext);
22680
22681 /* Write the tuple out to the new relation. */
22682 table_tuple_insert(newPartRel, insertslot, mycid,
22683 ti_options, bistate);
22684
22685 ResetExprContext(econtext);
22686 }
22687
22688 MemoryContextSwitchTo(oldCxt);
22689 table_endscan(scan);
22690 UnregisterSnapshot(snapshot);
22691
22692 if (tuple_map)
22693 free_conversion_map(tuple_map);
22694
22696 table_close(mergingPartition, NoLock);
22697 }
22698
22699 FreeExecutorState(estate);
22701 FreeBulkInsertState(bistate);
22702
22703 table_finish_bulk_insert(newPartRel, ti_options);
22704
22705 /*
22706 * We don't need to process this newPartRel since we already processed it
22707 * here, so delete the ALTER TABLE queue for it.
22708 */
22709 foreach(ltab, *wqueue)
22710 {
22711 tab = (AlteredTableInfo *) lfirst(ltab);
22712 if (tab->relid == RelationGetRelid(newPartRel))
22713 {
22714 *wqueue = list_delete_cell(*wqueue, ltab);
22715 break;
22716 }
22717 }
22718}
22719
22720/*
22721 * detachPartitionTable: detach partition "child_rel" from partitioned table
22722 * "parent_rel" with default partition identifier "defaultPartOid"
22723 */
22724static void
22725detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
22726{
22727 /* Remove the pg_inherits row first. */
22728 RemoveInheritance(child_rel, parent_rel, false);
22729
22730 /*
22731 * Detaching the partition might involve TOAST table access, so ensure we
22732 * have a valid snapshot.
22733 */
22735
22736 /* Do the final part of detaching. */
22737 DetachPartitionFinalize(parent_rel, child_rel, false, defaultPartOid);
22738
22740}
22741
22742/*
22743 * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
22744 */
22745static void
22748{
22749 Relation newPartRel;
22750 List *mergingPartitions = NIL;
22751 Oid defaultPartOid;
22752 Oid existingRelid;
22753 Oid ownerId = InvalidOid;
22754 Oid save_userid;
22755 int save_sec_context;
22756 int save_nestlevel;
22757
22758 /*
22759 * Check ownership of merged partitions - partitions with different owners
22760 * cannot be merged. Also, collect the OIDs of these partitions during the
22761 * check.
22762 */
22764 {
22765 Relation mergingPartition;
22766
22767 /*
22768 * We are going to detach and remove this partition. We already took
22769 * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
22770 * NoLock is fine.
22771 */
22772 mergingPartition = table_openrv_extended(name, NoLock, false);
22773 Assert(CheckRelationLockedByMe(mergingPartition, AccessExclusiveLock, false));
22774
22775 if (OidIsValid(ownerId))
22776 {
22777 /* Do the partitions being merged have different owners? */
22778 if (ownerId != mergingPartition->rd_rel->relowner)
22779 ereport(ERROR,
22780 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22781 errmsg("partitions being merged have different owners"));
22782 }
22783 else
22784 ownerId = mergingPartition->rd_rel->relowner;
22785
22786 /* Store the next merging partition into the list. */
22787 mergingPartitions = lappend_oid(mergingPartitions,
22788 RelationGetRelid(mergingPartition));
22789
22790 table_close(mergingPartition, NoLock);
22791 }
22792
22793 /* Look up the existing relation by the new partition name. */
22794 RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, &existingRelid);
22795
22796 /*
22797 * Check if this name is already taken. This helps us to detect the
22798 * situation when one of the merging partitions has the same name as the
22799 * new partition. Otherwise, this would fail later on anyway, but
22800 * catching this here allows us to emit a nicer error message.
22801 */
22802 if (OidIsValid(existingRelid))
22803 {
22804 if (list_member_oid(mergingPartitions, existingRelid))
22805 {
22806 /*
22807 * The new partition has the same name as one of the merging
22808 * partitions.
22809 */
22810 char tmpRelName[NAMEDATALEN];
22811
22812 /* Generate a temporary name. */
22813 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
22814
22815 /*
22816 * Rename the existing partition with a temporary name, leaving it
22817 * free for the new partition. We don't need to care about this
22818 * in the future because we're going to eventually drop the
22819 * existing partition anyway.
22820 */
22821 RenameRelationInternal(existingRelid, tmpRelName, true, false);
22822
22823 /*
22824 * We must bump the command counter to make the new partition
22825 * tuple visible for rename.
22826 */
22828 }
22829 else
22830 {
22831 ereport(ERROR,
22832 errcode(ERRCODE_DUPLICATE_TABLE),
22833 errmsg("relation \"%s\" already exists", cmd->name->relname));
22834 }
22835 }
22836
22837 defaultPartOid =
22839
22840 /* Detach all merging partitions. */
22841 foreach_oid(mergingPartitionOid, mergingPartitions)
22842 {
22843 Relation child_rel;
22844
22845 child_rel = table_open(mergingPartitionOid, NoLock);
22846
22847 detachPartitionTable(rel, child_rel, defaultPartOid);
22848
22849 table_close(child_rel, NoLock);
22850 }
22851
22852 /*
22853 * Perform a preliminary check to determine whether it's safe to drop all
22854 * merging partitions before we actually do so later. After merging rows
22855 * into the new partitions via MergePartitionsMoveRows, all old partitions
22856 * need to be dropped. However, since the drop behavior is DROP_RESTRICT
22857 * and the merge process (MergePartitionsMoveRows) can be time-consuming,
22858 * performing an early check on the drop eligibility of old partitions is
22859 * preferable.
22860 */
22861 foreach_oid(mergingPartitionOid, mergingPartitions)
22862 {
22863 ObjectAddress object;
22864
22865 /* Get oid of the later to be dropped relation. */
22866 object.objectId = mergingPartitionOid;
22867 object.classId = RelationRelationId;
22868 object.objectSubId = 0;
22869
22871 }
22872
22873 /*
22874 * Create a table for the new partition, using the partitioned table as a
22875 * model.
22876 */
22877 Assert(OidIsValid(ownerId));
22878 newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
22879
22880 /*
22881 * Switch to the table owner's userid, so that any index functions are run
22882 * as that user. Also, lockdown security-restricted operations and
22883 * arrange to make GUC variable changes local to this command.
22884 *
22885 * Need to do it after determining the namespace in the
22886 * createPartitionTable() call.
22887 */
22888 GetUserIdAndSecContext(&save_userid, &save_sec_context);
22889 SetUserIdAndSecContext(ownerId,
22890 save_sec_context | SECURITY_RESTRICTED_OPERATION);
22891 save_nestlevel = NewGUCNestLevel();
22893
22894 /* Copy data from merged partitions to the new partition. */
22895 MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel);
22896
22897 /* Drop the current partitions before attaching the new one. */
22898 foreach_oid(mergingPartitionOid, mergingPartitions)
22899 {
22900 ObjectAddress object;
22901
22902 object.objectId = mergingPartitionOid;
22903 object.classId = RelationRelationId;
22904 object.objectSubId = 0;
22905
22906 performDeletion(&object, DROP_RESTRICT, 0);
22907 }
22908
22909 list_free(mergingPartitions);
22910
22911 /*
22912 * Attach a new partition to the partitioned table. wqueue = NULL:
22913 * verification for each cloned constraint is not needed.
22914 */
22915 attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
22916
22917 /* Keep the lock until commit. */
22918 table_close(newPartRel, NoLock);
22919
22920 /* Roll back any GUC changes executed by index functions. */
22921 AtEOXact_GUC(false, save_nestlevel);
22922
22923 /* Restore the userid and security context. */
22924 SetUserIdAndSecContext(save_userid, save_sec_context);
22925}
22926
22927/*
22928 * Struct with the context of the new partition for inserting rows from the
22929 * split partition.
22930 */
22932{
22933 ExprState *partqualstate; /* expression for checking a slot for a
22934 * partition (NULL for DEFAULT partition) */
22935 BulkInsertState bistate; /* state of bulk inserts for partition */
22936 TupleTableSlot *dstslot; /* slot for inserting row into partition */
22937 AlteredTableInfo *tab; /* structure with generated column expressions
22938 * and check constraint expressions. */
22939 Relation partRel; /* relation for partition */
22941
22942/*
22943 * createSplitPartitionContext: create context for partition and fill it
22944 */
22945static SplitPartitionContext *
22947{
22949
22951 pc->partRel = partRel;
22952
22953 /*
22954 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
22955 * don't bother using it.
22956 */
22958
22959 /* Create a destination tuple slot for the new partition. */
22960 pc->dstslot = table_slot_create(pc->partRel, NULL);
22961
22962 return pc;
22963}
22964
22965/*
22966 * deleteSplitPartitionContext: delete context for partition
22967 */
22968static void
22970{
22971 ListCell *ltab;
22972
22975
22976 table_finish_bulk_insert(pc->partRel, ti_options);
22977
22978 /*
22979 * We don't need to process this pc->partRel so delete the ALTER TABLE
22980 * queue of it.
22981 */
22982 foreach(ltab, *wqueue)
22983 {
22984 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
22985
22986 if (tab->relid == RelationGetRelid(pc->partRel))
22987 {
22988 *wqueue = list_delete_cell(*wqueue, ltab);
22989 break;
22990 }
22991 }
22992
22993 pfree(pc);
22994}
22995
22996/*
22997 * SplitPartitionMoveRows: scan split partition (splitRel) of partitioned table
22998 * (rel) and move rows into new partitions.
22999 *
23000 * New partitions description:
23001 * partlist: list of pointers to SinglePartitionSpec structures. It contains
23002 * the partition specification details for all new partitions.
23003 * newPartRels: list of Relations, new partitions created in
23004 * ATExecSplitPartition.
23005 */
23006static void
23008 List *partlist, List *newPartRels)
23009{
23010 /* The FSM is empty, so don't bother using it. */
23011 int ti_options = TABLE_INSERT_SKIP_FSM;
23012 CommandId mycid;
23013 EState *estate;
23014 ListCell *listptr,
23015 *listptr2;
23016 TupleTableSlot *srcslot;
23017 ExprContext *econtext;
23018 TableScanDesc scan;
23019 Snapshot snapshot;
23020 MemoryContext oldCxt;
23021 List *partContexts = NIL;
23022 TupleConversionMap *tuple_map;
23023 SplitPartitionContext *defaultPartCtx = NULL,
23024 *pc;
23025
23026 mycid = GetCurrentCommandId(true);
23027
23028 estate = CreateExecutorState();
23029
23030 forboth(listptr, partlist, listptr2, newPartRels)
23031 {
23033
23035
23036 /* Find the work queue entry for the new partition table: newPartRel. */
23037 pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
23038
23039 buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
23040
23041 if (sps->bound->is_default)
23042 {
23043 /*
23044 * We should not create a structure to check the partition
23045 * constraint for the new DEFAULT partition.
23046 */
23047 defaultPartCtx = pc;
23048 }
23049 else
23050 {
23051 List *partConstraint;
23052
23053 /* Build expression execution states for partition check quals. */
23054 partConstraint = get_qual_from_partbound(rel, sps->bound);
23055 partConstraint =
23057 (Node *) partConstraint);
23058 /* Make a boolean expression for ExecCheck(). */
23059 partConstraint = list_make1(make_ands_explicit(partConstraint));
23060
23061 /*
23062 * Map the vars in the constraint expression from rel's attnos to
23063 * splitRel's.
23064 */
23065 partConstraint = map_partition_varattnos(partConstraint,
23066 1, splitRel, rel);
23067
23068 pc->partqualstate =
23069 ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
23070 Assert(pc->partqualstate != NULL);
23071 }
23072
23073 /* Store partition context into a list. */
23074 partContexts = lappend(partContexts, pc);
23075 }
23076
23077 econtext = GetPerTupleExprContext(estate);
23078
23079 /* Create the necessary tuple slot. */
23080 srcslot = table_slot_create(splitRel, NULL);
23081
23082 /*
23083 * Map computing for moving attributes of the split partition to the new
23084 * partition (for the first new partition, but other new partitions can
23085 * use the same map).
23086 */
23087 pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
23088 tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
23089 RelationGetDescr(pc->partRel));
23090
23091 /* Scan through the rows. */
23092 snapshot = RegisterSnapshot(GetLatestSnapshot());
23093 scan = table_beginscan(splitRel, snapshot, 0, NULL);
23094
23095 /*
23096 * Switch to per-tuple memory context and reset it for each tuple
23097 * produced, so we don't leak memory.
23098 */
23100
23101 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
23102 {
23103 bool found = false;
23104 TupleTableSlot *insertslot;
23105
23107
23108 econtext->ecxt_scantuple = srcslot;
23109
23110 /* Search partition for the current slot, srcslot. */
23111 foreach(listptr, partContexts)
23112 {
23113 pc = (SplitPartitionContext *) lfirst(listptr);
23114
23115 /* skip DEFAULT partition */
23116 if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
23117 {
23118 found = true;
23119 break;
23120 }
23121 }
23122 if (!found)
23123 {
23124 /* Use the DEFAULT partition if it exists. */
23125 if (defaultPartCtx)
23126 pc = defaultPartCtx;
23127 else
23128 ereport(ERROR,
23129 errcode(ERRCODE_CHECK_VIOLATION),
23130 errmsg("can not find partition for split partition row"),
23131 errtable(splitRel));
23132 }
23133
23134 if (tuple_map)
23135 {
23136 /* Need to use a map to copy attributes. */
23137 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
23138 }
23139 else
23140 {
23141 /* Extract data from the old tuple. */
23142 slot_getallattrs(srcslot);
23143
23144 /* Copy attributes directly. */
23145 insertslot = pc->dstslot;
23146
23147 ExecClearTuple(insertslot);
23148
23149 memcpy(insertslot->tts_values, srcslot->tts_values,
23150 sizeof(Datum) * srcslot->tts_nvalid);
23151 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23152 sizeof(bool) * srcslot->tts_nvalid);
23153
23154 ExecStoreVirtualTuple(insertslot);
23155 }
23156
23157 /*
23158 * Constraints and GENERATED expressions might reference the tableoid
23159 * column, so fill tts_tableOid with the desired value. (We must do
23160 * this each time, because it gets overwritten with newrel's OID
23161 * during storing.)
23162 */
23163 insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
23164
23165 /*
23166 * Now, evaluate any generated expressions whose inputs come from the
23167 * new tuple. We assume these columns won't reference each other, so
23168 * that there's no ordering dependency.
23169 */
23171 insertslot, econtext);
23172
23173 /* Write the tuple out to the new relation. */
23174 table_tuple_insert(pc->partRel, insertslot, mycid,
23175 ti_options, pc->bistate);
23176
23177 ResetExprContext(econtext);
23178 }
23179
23180 MemoryContextSwitchTo(oldCxt);
23181
23182 table_endscan(scan);
23183 UnregisterSnapshot(snapshot);
23184
23185 if (tuple_map)
23186 free_conversion_map(tuple_map);
23187
23189
23190 FreeExecutorState(estate);
23191
23192 foreach_ptr(SplitPartitionContext, spc, partContexts)
23193 deleteSplitPartitionContext(spc, wqueue, ti_options);
23194}
23195
23196/*
23197 * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
23198 */
23199static void
23202{
23203 Relation splitRel;
23204 Oid splitRelOid;
23205 ListCell *listptr,
23206 *listptr2;
23207 bool isSameName = false;
23208 char tmpRelName[NAMEDATALEN];
23209 List *newPartRels = NIL;
23210 ObjectAddress object;
23211 Oid defaultPartOid;
23212 Oid save_userid;
23213 int save_sec_context;
23214 int save_nestlevel;
23215
23216 defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
23217
23218 /*
23219 * Partition is already locked in the transformPartitionCmdForSplit
23220 * function.
23221 */
23222 splitRel = table_openrv(cmd->name, NoLock);
23223
23224 splitRelOid = RelationGetRelid(splitRel);
23225
23226 /* Check descriptions of new partitions. */
23228 {
23229 Oid existingRelid;
23230
23231 /* Look up the existing relation by the new partition name. */
23232 RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, &existingRelid);
23233
23234 /*
23235 * This would fail later on anyway if the relation already exists. But
23236 * by catching it here, we can emit a nicer error message.
23237 */
23238 if (existingRelid == splitRelOid && !isSameName)
23239 /* One new partition can have the same name as a split partition. */
23240 isSameName = true;
23241 else if (OidIsValid(existingRelid))
23242 ereport(ERROR,
23243 errcode(ERRCODE_DUPLICATE_TABLE),
23244 errmsg("relation \"%s\" already exists", sps->name->relname));
23245 }
23246
23247 /* Detach the split partition. */
23248 detachPartitionTable(rel, splitRel, defaultPartOid);
23249
23250 /*
23251 * Perform a preliminary check to determine whether it's safe to drop the
23252 * split partition before we actually do so later. After merging rows into
23253 * the new partitions via SplitPartitionMoveRows, all old partitions need
23254 * to be dropped. However, since the drop behavior is DROP_RESTRICT and
23255 * the merge process (SplitPartitionMoveRows) can be time-consuming,
23256 * performing an early check on the drop eligibility of old partitions is
23257 * preferable.
23258 */
23259 object.objectId = splitRelOid;
23260 object.classId = RelationRelationId;
23261 object.objectSubId = 0;
23263
23264 /*
23265 * If a new partition has the same name as the split partition, then we
23266 * should rename the split partition to reuse its name.
23267 */
23268 if (isSameName)
23269 {
23270 /*
23271 * We must bump the command counter to make the split partition tuple
23272 * visible for renaming.
23273 */
23275 /* Rename partition. */
23276 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23277 RenameRelationInternal(splitRelOid, tmpRelName, true, false);
23278
23279 /*
23280 * We must bump the command counter to make the split partition tuple
23281 * visible after renaming.
23282 */
23284 }
23285
23286 /* Create new partitions (like a split partition), without indexes. */
23288 {
23289 Relation newPartRel;
23290
23291 newPartRel = createPartitionTable(wqueue, sps->name, rel,
23292 splitRel->rd_rel->relowner);
23293 newPartRels = lappend(newPartRels, newPartRel);
23294 }
23295
23296 /*
23297 * Switch to the table owner's userid, so that any index functions are run
23298 * as that user. Also, lockdown security-restricted operations and
23299 * arrange to make GUC variable changes local to this command.
23300 *
23301 * Need to do it after determining the namespace in the
23302 * createPartitionTable() call.
23303 */
23304 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23305 SetUserIdAndSecContext(splitRel->rd_rel->relowner,
23306 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23307 save_nestlevel = NewGUCNestLevel();
23309
23310 /* Copy data from the split partition to the new partitions. */
23311 SplitPartitionMoveRows(wqueue, rel, splitRel, cmd->partlist, newPartRels);
23312 /* Keep the lock until commit. */
23313 table_close(splitRel, NoLock);
23314
23315 /* Attach new partitions to the partitioned table. */
23316 forboth(listptr, cmd->partlist, listptr2, newPartRels)
23317 {
23319 Relation newPartRel = (Relation) lfirst(listptr2);
23320
23321 /*
23322 * wqueue = NULL: verification for each cloned constraint is not
23323 * needed.
23324 */
23325 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
23326 /* Keep the lock until commit. */
23327 table_close(newPartRel, NoLock);
23328 }
23329
23330 /* Drop the split partition. */
23331 object.classId = RelationRelationId;
23332 object.objectId = splitRelOid;
23333 object.objectSubId = 0;
23334 /* Probably DROP_CASCADE is not needed. */
23335 performDeletion(&object, DROP_RESTRICT, 0);
23336
23337 /* Roll back any GUC changes executed by index functions. */
23338 AtEOXact_GUC(false, save_nestlevel);
23339
23340 /* Restore the userid and security context. */
23341 SetUserIdAndSecContext(save_userid, save_sec_context);
23342}
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:2654
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3868
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3836
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4090
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:2973
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4039
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition: amapi.c:161
Oid get_table_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:173
char * get_am_name(Oid amOid)
Definition: amcmds.c:192
#define ARR_NDIM(a)
Definition: array.h:290
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
#define ARR_DIMS(a)
Definition: array.h:294
#define ARR_HASNULL(a)
Definition: array.h:291
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3362
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1821
void free_attrmap(AttrMap *map)
Definition: attmap.c:56
AttrMap * make_attrmap(int maplen)
Definition: attmap.c:40
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:175
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:261
int16 AttrNumber
Definition: attnum.h:21
#define InvalidAttrNumber
Definition: attnum.h:23
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1472
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
Definition: tablespace.c:1426
Oid GetDefaultTablespace(char relpersistence, bool partitioned)
Definition: tablespace.c:1143
List * raw_parser(const char *str, RawParseMode mode)
Definition: parser.c:42
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6417
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1305
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:1018
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:867
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:814
#define bms_is_empty(a)
Definition: bitmapset.h:118
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:5037
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define NameStr(name)
Definition: c.h:765
uint16 bits16
Definition: c.h:560
uint32 SubTransactionId
Definition: c.h:675
#define gettext_noop(x)
Definition: c.h:1173
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:229
#define InvalidSubTransactionId
Definition: c.h:677
int16_t int16
Definition: c.h:547
int32_t int32
Definition: c.h:548
#define MemSet(start, val, len)
Definition: c.h:1011
uint32 CommandId
Definition: c.h:685
#define PG_INT16_MAX
Definition: c.h:605
#define OidIsValid(objectId)
Definition: c.h:788
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:382
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2270
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:550
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:255
void SequenceChangePersistence(Oid relid, char newrelpersistence)
Definition: sequence.c:542
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:383
void performDeletionCheck(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:333
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:274
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
Definition: dependency.c:2769
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2709
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2664
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2949
#define PERFORM_DELETION_CONCURRENTLY
Definition: dependency.h:93
DependencyType
Definition: dependency.h:32
@ DEPENDENCY_AUTO
Definition: dependency.h:34
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition: dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition: dependency.h:37
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
#define PERFORM_DELETION_QUIETLY
Definition: dependency.h:94
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:92
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:952
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:358
void hash_destroy(HTAB *hashp)
Definition: dynahash.c:865
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1415
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1380
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1170
int errdetail(const char *fmt,...)
Definition: elog.c:1216
int errhint(const char *fmt,...)
Definition: elog.c:1330
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define _(x)
Definition: elog.c:91
#define PG_TRY(...)
Definition: elog.h:372
#define WARNING
Definition: elog.h:36
#define PG_END_TRY(...)
Definition: elog.h:397
#define DEBUG1
Definition: elog.h:30
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define NOTICE
Definition: elog.h:35
#define PG_FINALLY(...)
Definition: elog.h:389
#define ereport(elevel,...)
Definition: elog.h:150
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
void EventTriggerAlterTableStart(Node *parsetree)
void EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
void EventTriggerAlterTableRelid(Oid objectId)
void EventTriggerAlterTableEnd(void)
void EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
#define AT_REWRITE_ALTER_PERSISTENCE
Definition: event_trigger.h:40
#define AT_REWRITE_DEFAULT_VAL
Definition: event_trigger.h:41
#define AT_REWRITE_ACCESS_METHOD
Definition: event_trigger.h:43
#define AT_REWRITE_COLUMN_REWRITE
Definition: event_trigger.h:42
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:143
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:765
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:872
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition: execMain.c:1243
AttrNumber ExecRelGenVirtualNotNull(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, List *notnull_virtual_attrs)
Definition: execMain.c:2094
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1427
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1443
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1741
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1833
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1765
void FreeExecutorState(EState *estate)
Definition: execUtils.c:192
EState * CreateExecutorState(void)
Definition: execUtils.c:88
struct ResultRelInfo ResultRelInfo
#define GetPerTupleExprContext(estate)
Definition: executor.h:656
#define ResetExprContext(econtext)
Definition: executor.h:650
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:661
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:393
#define palloc_object(type)
Definition: fe_memutils.h:74
#define palloc_array(type, count)
Definition: fe_memutils.h:76
#define palloc0_object(type)
Definition: fe_memutils.h:75
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:684
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
#define SizeForFunctionCallInfo(nargs)
Definition: fmgr.h:102
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition: foreign.c:38
FdwRoutine * GetFdwRoutineByServerId(Oid serverid)
Definition: foreign.c:378
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:112
Oid GetForeignServerIdByRelId(Oid relid)
Definition: foreign.c:356
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
Definition: foreigncmds.c:121
char * format_type_with_typemod(Oid type_oid, int32 typemod)
Definition: format_type.c:362
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:388
struct RelationData * Relation
Definition: genam.h:30
bool IsBinaryUpgrade
Definition: globals.c:121
int MyProcPid
Definition: globals.c:47
bool allowSystemTableMods
Definition: globals.c:130
Oid MyDatabaseTableSpace
Definition: globals.c:96
Oid MyDatabaseId
Definition: globals.c:94
int NewGUCNestLevel(void)
Definition: guc.c:2110
#define newval
void RestrictSearchPath(void)
Definition: guc.c:2121
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2137
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:1364
BulkInsertState GetBulkInsertState(void)
Definition: heapam.c:2039
void FreeBulkInsertState(BulkInsertState bistate)
Definition: heapam.c:2056
#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:3581
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:2535
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition: index.c:3946
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2426
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:1883
#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(ParseState *pstate, 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:544
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4467
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2365
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition: indexcmds.c:178
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:435
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2280
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition: indexing.c:313
void CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
Definition: indexing.c:365
long val
Definition: informix.c:689
static struct @171 value
static bool pg_add_s16_overflow(int16 a, int16 b, int16 *result)
Definition: int.h:67
void AcceptInvalidationMessages(void)
Definition: inval.c:930
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1635
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1691
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1669
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:1092
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_delete_cell(List *list, ListCell *cell)
Definition: list.c:841
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:2078
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:934
Oid get_constraint_index(Oid conoid)
Definition: lsyscache.c:1189
char get_typstorage(Oid typid)
Definition: lsyscache.c:2569
bool get_index_isreplident(Oid index_oid)
Definition: lsyscache.c:3705
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2153
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3206
char * get_collation_name(Oid colloid)
Definition: lsyscache.c:1111
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3751
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:1157
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:903
bool get_collation_isdeterministic(Oid colloid)
Definition: lsyscache.c:1130
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1403
Oid get_rel_relam(Oid relid)
Definition: lsyscache.c:2250
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3231
Oid get_rel_tablespace(Oid relid)
Definition: lsyscache.c:2204
Oid get_typ_typrelid(Oid typid)
Definition: lsyscache.c:2881
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2688
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2671
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3516
char get_constraint_type(Oid conoid)
Definition: lsyscache.c:1219
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:2035
#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:1768
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:403
char * pstrdup(const char *in)
Definition: mcxt.c:1781
void pfree(void *pointer)
Definition: mcxt.c:1616
void * palloc(Size size)
Definition: mcxt.c:1387
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
MemoryContext CacheMemoryContext
Definition: mcxt.c:169
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:472
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 SECURITY_RESTRICTED_OPERATION
Definition: miscadmin.h:319
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:612
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:639
Oid GetUserId(void)
Definition: miscinit.c:469
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:619
MultiXactId ReadNextMultiXactId(void)
Definition: multixact.c:622
void namestrcpy(Name name, const char *str)
Definition: name.c:233
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition: namespace.c:738
bool isAnyTempNamespace(Oid namespaceId)
Definition: namespace.c:3757
Oid get_collation_oid(List *collname, bool missing_ok)
Definition: namespace.c:4041
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition: namespace.c:3529
RangeVar * makeRangeVarFromNameList(const List *names)
Definition: namespace.c:3624
Oid LookupNamespaceNoError(const char *nspname)
Definition: namespace.c:3425
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:440
@ RVR_MISSING_OK
Definition: namespace.h:90
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:98
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:821
Node * strip_implicit_coercions(Node *node)
Definition: nodeFuncs.c:705
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
#define copyObject(obj)
Definition: nodes.h:232
#define nodeTag(nodeptr)
Definition: nodes.h:139
#define makeNode(_type_)
Definition: nodes.h:161
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
#define InvokeObjectTruncateHook(objectId)
Definition: objectaccess.h:191
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:200
ObjectType get_relkind_objtype(char relkind)
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define ObjectAddressSubSet(addr, class_id, object_id, object_sub_id)
Definition: objectaddress.h:33
char * nodeToString(const void *obj)
Definition: outfuncs.c:802
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid)
bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, CoercionContext ccontext)
Definition: parse_coerce.c:558
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:79
CoercionPathType
Definition: parse_coerce.h:25
@ COERCION_PATH_NONE
Definition: parse_coerce.h:26
@ COERCION_PATH_RELABELTYPE
Definition: parse_coerce.h:28
void assign_expr_collations(ParseState *pstate, Node *expr)
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:119
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:39
@ EXPR_KIND_PARTITION_EXPRESSION
Definition: parse_node.h:80
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
Oid attnumCollationId(Relation rd, int attid)
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Oid attnumTypeId(Relation rd, int attid)
const NameData * attnumAttName(Relation rd, int attid)
void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, Oid *typeid_p, int32 *typmod_p)
Definition: parse_type.c:310
Type typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
Definition: parse_type.c:264
Oid GetColumnDefCollation(ParseState *pstate, const ColumnDef *coldef, Oid typeOid)
Definition: parse_type.c:540
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:291
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
AlterTableStmt * transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, const char *queryString, List **beforeStmts, List **afterStmts)
CreateStatsStmt * transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
PartitionBoundSpec * transformPartitionBound(ParseState *pstate, Relation parent, PartitionBoundSpec *spec)
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
@ AD_AddConstraint
Definition: parsenodes.h:2576
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2849
#define ACL_MAINTAIN
Definition: parsenodes.h:90
#define ACL_USAGE
Definition: parsenodes.h:84
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2852
PartitionStrategy
Definition: parsenodes.h:900
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:903
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:901
ConstrType
Definition: parsenodes.h:2827
@ CONSTR_FOREIGN
Definition: parsenodes.h:2838
@ CONSTR_UNIQUE
Definition: parsenodes.h:2836
@ CONSTR_DEFAULT
Definition: parsenodes.h:2831
@ CONSTR_NOTNULL
Definition: parsenodes.h:2830
@ CONSTR_CHECK
Definition: parsenodes.h:2834
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2837
@ CONSTR_PRIMARY
Definition: parsenodes.h:2835
DropBehavior
Definition: parsenodes.h:2424
@ DROP_CASCADE
Definition: parsenodes.h:2426
@ DROP_RESTRICT
Definition: parsenodes.h:2425
ObjectType
Definition: parsenodes.h:2351
@ OBJECT_MATVIEW
Definition: parsenodes.h:2375
@ OBJECT_SCHEMA
Definition: parsenodes.h:2388
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2370
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2394
@ OBJECT_INDEX
Definition: parsenodes.h:2372
@ OBJECT_SEQUENCE
Definition: parsenodes.h:2389
@ OBJECT_TABLE
Definition: parsenodes.h:2393
@ OBJECT_VIEW
Definition: parsenodes.h:2403
@ OBJECT_TYPE
Definition: parsenodes.h:2401
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2392
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2365
AlterTableType
Definition: parsenodes.h:2443
@ AT_AddIndexConstraint
Definition: parsenodes.h:2465
@ AT_MergePartitions
Definition: parsenodes.h:2507
@ AT_DropOf
Definition: parsenodes.h:2496
@ AT_SetOptions
Definition: parsenodes.h:2453
@ AT_DropIdentity
Definition: parsenodes.h:2510
@ AT_DisableTrigUser
Definition: parsenodes.h:2488
@ AT_DropNotNull
Definition: parsenodes.h:2448
@ AT_AddOf
Definition: parsenodes.h:2495
@ AT_ResetOptions
Definition: parsenodes.h:2454
@ AT_ReplicaIdentity
Definition: parsenodes.h:2497
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2480
@ AT_EnableRowSecurity
Definition: parsenodes.h:2498
@ AT_AddColumnToView
Definition: parsenodes.h:2445
@ AT_ResetRelOptions
Definition: parsenodes.h:2479
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2483
@ AT_DropOids
Definition: parsenodes.h:2475
@ AT_SetIdentity
Definition: parsenodes.h:2509
@ AT_ReAddStatistics
Definition: parsenodes.h:2511
@ AT_SetUnLogged
Definition: parsenodes.h:2474
@ AT_DisableTrig
Definition: parsenodes.h:2484
@ AT_SetCompression
Definition: parsenodes.h:2456
@ AT_DropExpression
Definition: parsenodes.h:2451
@ AT_AddIndex
Definition: parsenodes.h:2458
@ AT_EnableReplicaRule
Definition: parsenodes.h:2491
@ AT_ReAddIndex
Definition: parsenodes.h:2459
@ AT_DropConstraint
Definition: parsenodes.h:2466
@ AT_SetNotNull
Definition: parsenodes.h:2449
@ AT_ClusterOn
Definition: parsenodes.h:2471
@ AT_AddIdentity
Definition: parsenodes.h:2508
@ AT_ForceRowSecurity
Definition: parsenodes.h:2500
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2490
@ AT_SetAccessMethod
Definition: parsenodes.h:2476
@ AT_AlterColumnType
Definition: parsenodes.h:2468
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2505
@ AT_AddInherit
Definition: parsenodes.h:2493
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2462
@ AT_EnableTrig
Definition: parsenodes.h:2481
@ AT_DropColumn
Definition: parsenodes.h:2457
@ AT_ReAddComment
Definition: parsenodes.h:2467
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2469
@ AT_DisableTrigAll
Definition: parsenodes.h:2486
@ AT_EnableRule
Definition: parsenodes.h:2489
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2501
@ AT_DetachPartition
Definition: parsenodes.h:2504
@ AT_SetStatistics
Definition: parsenodes.h:2452
@ AT_AttachPartition
Definition: parsenodes.h:2503
@ AT_AddConstraint
Definition: parsenodes.h:2460
@ AT_DropInherit
Definition: parsenodes.h:2494
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2482
@ AT_SetLogged
Definition: parsenodes.h:2473
@ AT_SetStorage
Definition: parsenodes.h:2455
@ AT_DisableRule
Definition: parsenodes.h:2492
@ AT_DisableRowSecurity
Definition: parsenodes.h:2499
@ AT_SetRelOptions
Definition: parsenodes.h:2478
@ AT_ChangeOwner
Definition: parsenodes.h:2470
@ AT_EnableTrigUser
Definition: parsenodes.h:2487
@ AT_SetExpression
Definition: parsenodes.h:2450
@ AT_ReAddConstraint
Definition: parsenodes.h:2461
@ AT_SetTableSpace
Definition: parsenodes.h:2477
@ AT_GenericOptions
Definition: parsenodes.h:2502
@ AT_ColumnDefault
Definition: parsenodes.h:2446
@ AT_CookedColumnDefault
Definition: parsenodes.h:2447
@ AT_AlterConstraint
Definition: parsenodes.h:2463
@ AT_EnableTrigAll
Definition: parsenodes.h:2485
@ AT_SplitPartition
Definition: parsenodes.h:2506
@ AT_DropCluster
Definition: parsenodes.h:2472
@ AT_ValidateConstraint
Definition: parsenodes.h:2464
@ AT_AddColumn
Definition: parsenodes.h:2444
#define ACL_REFERENCES
Definition: parsenodes.h:81
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2850
#define ACL_TRUNCATE
Definition: parsenodes.h:80
#define ACL_CREATE
Definition: parsenodes.h:85
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2851
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2848
@ 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:2889
List * get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:250
void check_default_partition_contents(Relation parent, Relation default_rel, PartitionBoundSpec *new_spec)
Definition: partbounds.c:3244
List * RelationGetPartitionQual(Relation rel)
Definition: partcache.c:277
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:71
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition: partdesc.c:501
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:222
bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
Definition: partition.c:255
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:370
void update_default_partition_oid(Oid parentId, Oid defaultPartId)
Definition: partition.c:340
Oid index_get_partition(Relation partition, Oid indexId)
Definition: partition.c:176
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition: partition.c:53
Oid StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr, bool is_internal)
Definition: pg_attrdef.c:36
Oid GetAttrDefaultOid(Oid relid, AttrNumber attnum)
Definition: pg_attrdef.c:279
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid)
Definition: pg_attrdef.c:321
void RemoveAttrDefault(Oid relid, AttrNumber attnum, DropBehavior behavior, bool complain, bool internal)
Definition: pg_attrdef.c:153
NameData attname
Definition: pg_attribute.h:41
int16 attnum
Definition: pg_attribute.h:74
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
bool attnotnull
Definition: pg_attribute.h:123
void * arg
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
NameData relname
Definition: pg_class.h:38
FormData_pg_class * Form_pg_class
Definition: pg_class.h:156
#define PARTITION_MAX_KEYS
#define INDEX_MAX_KEYS
#define NAMEDATALEN
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isEnforced, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int16 conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
Definition: pg_constraint.c:51
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, const char *conname)
void FindFKPeriodOpers(Oid opclass, Oid *containedbyoperoid, Oid *aggedcontainedbyoperoid, Oid *intersectoperoid)
void RenameConstraintById(Oid conId, const char *newname)
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
void DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, AttrNumber *conkey, AttrNumber *confkey, Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs, int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)
void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType, ObjectAddresses *objsMoved)
List * RelationGetNotNullConstraints(Oid relid, bool cooked, bool include_noinh)
char * ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others)
Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
AttrNumber extractNotNullColumn(HeapTuple constrTup)
Oid get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
FormData_pg_constraint * Form_pg_constraint
@ CONSTRAINT_RELATION
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:45
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition: pg_depend.c:457
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:351
List * getOwnedSequences(Oid relid)
Definition: pg_depend.c:936
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:301
long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype, Oid refclassId, Oid refobjectId)
Definition: pg_depend.c:398
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:945
Oid get_index_constraint(Oid indexId)
Definition: pg_depend.c:988
bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
Definition: pg_depend.c:828
FormData_pg_depend * Form_pg_depend
Definition: pg_depend.h:72
FormData_pg_foreign_table * Form_pg_foreign_table
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
Definition: pg_inherits.c:552
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition: pg_inherits.c:58
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
Definition: pg_inherits.c:508
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:377
bool PartitionHasPendingDetach(Oid partoid)
Definition: pg_inherits.c:620
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h:45
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
#define lfirst_int(lc)
Definition: pg_list.h:173
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
#define list_make1_oid(x1)
Definition: pg_list.h:242
#define list_make1(x1)
Definition: pg_list.h:212
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469
#define for_each_from(cell, lst, N)
Definition: pg_list.h:414
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define linitial(l)
Definition: pg_list.h:178
#define list_make3(x1, x2, x3)
Definition: pg_list.h:216
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
#define foreach_oid(var, lst)
Definition: pg_list.h:471
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define linitial_oid(l)
Definition: pg_list.h:180
#define lfirst_oid(lc)
Definition: pg_list.h:174
#define list_make2(x1, x2)
Definition: pg_list.h:214
#define foreach_int(var, lst)
Definition: pg_list.h:470
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
List * GetRelationPublications(Oid relid)
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
Definition: pg_shdepend.c:316
void changeDependencyOnTablespace(Oid classId, Oid objectId, Oid newTablespaceId)
Definition: pg_shdepend.c:391
static char buf[DEFAULT_XLOG_SEG_SIZE]
Definition: pg_test_fsync.c:71
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:6764
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:32
#define sprintf
Definition: port.h:262
#define snprintf
Definition: port.h:260
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:4417
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3121
bool predicate_implied_by(List *predicate_list, List *clause_list, bool weak)
Definition: predtest.c:152
Expr * canonicalize_qual(Expr *qual, bool is_check)
Definition: prepqual.c:293
char * c
#define PRS2_OLD_VARNO
Definition: primnodes.h:250
#define PRS2_NEW_VARNO
Definition: primnodes.h:251
OnCommitAction
Definition: primnodes.h:57
@ ONCOMMIT_DELETE_ROWS
Definition: primnodes.h:60
@ ONCOMMIT_NOOP
Definition: primnodes.h:58
@ ONCOMMIT_PRESERVE_ROWS
Definition: primnodes.h:59
@ ONCOMMIT_DROP
Definition: primnodes.h:61
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:768
@ IS_NOT_NULL
Definition: primnodes.h:1977
@ COERCION_ASSIGNMENT
Definition: primnodes.h:747
@ COERCION_IMPLICIT
Definition: primnodes.h:746
void * stringToNode(const char *str)
Definition: read.c:90
#define RelationGetForm(relation)
Definition: rel.h:509
#define RelationGetRelid(relation)
Definition: rel.h:515
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:711
#define RelationIsUsedAsCatalogTable(relation)
Definition: rel.h:398
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:577
#define RelationGetDescr(relation)
Definition: rel.h:541
#define RelationIsMapped(relation)
Definition: rel.h:564
#define RelationGetNumberOfAttributes(relation)
Definition: rel.h:521
#define RelationGetRelationName(relation)
Definition: rel.h:549
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:668
#define RelationGetNamespace(relation)
Definition: rel.h:556
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:534
#define RelationIsPermanent(relation)
Definition: rel.h:627
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4831
Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
Definition: relcache.c:5042
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:6098
int errtablecol(Relation rel, int attnum)
Definition: relcache.c:6061
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5205
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition: relcache.c:5298
void RelationSetNewRelfilenumber(Relation relation, char persistence)
Definition: relcache.c:3768
List * RelationGetFKeyList(Relation relation)
Definition: relcache.c:4726
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5092
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3971
int errtable(Relation rel)
Definition: relcache.c:6044
@ INDEX_ATTR_BITMAP_KEY
Definition: relcache.h:69
@ INDEX_ATTR_BITMAP_PRIMARY_KEY
Definition: relcache.h:70
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition: relcache.h:71
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1360
bytea * view_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2045
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2101
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2031
Datum transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1176
LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList)
Definition: reloptions.c:2155
bytea * attribute_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2116
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
Definition: reloptions.c:2066
#define HEAP_RELOPT_NAMESPACES
Definition: reloptions.h:61
Oid RelFileNumber
Definition: relpath.h:25
ForkNumber
Definition: relpath.h:56
@ MAIN_FORKNUM
Definition: relpath.h:58
@ INIT_FORKNUM
Definition: relpath.h:61
#define MAX_FORKNUM
Definition: relpath.h:70
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
void EnableDisableRule(Relation rel, const char *rulename, char fires_when)
#define RULE_FIRES_ON_ORIGIN
Definition: rewriteDefine.h:21
#define RULE_FIRES_ON_REPLICA
Definition: rewriteDefine.h:23
#define RULE_FIRES_ALWAYS
Definition: rewriteDefine.h:22
#define RULE_DISABLED
Definition: rewriteDefine.h:24
Query * get_view_query(Relation view)
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
Node * build_generation_expression(Relation rel, int attrno)
Node * build_column_default(Relation rel, int attrno)
Node * expand_generated_columns_in_expr(Node *node, Relation rel, int rt_index)
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
Datum RI_FKey_check_ins(PG_FUNCTION_ARGS)
Definition: ri_triggers.c:474
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1519
void RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1813
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3210
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition: ruleutils.c:1626
char * pg_get_indexdef_string(Oid indexrelid)
Definition: ruleutils.c:1225
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:13062
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition: ruleutils.c:2674
char * pg_get_constraintdef_command(Oid constraintId)
Definition: ruleutils.c:2183
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
@ ForwardScanDirection
Definition: sdir.h:28
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition: smgr.c:481
void smgrclose(SMgrRelation reln)
Definition: smgr.c:374
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:462
TransactionId RecentXmin
Definition: snapmgr.c:160
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:272
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:354
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:866
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:682
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:824
void PopActiveSnapshot(void)
Definition: snapmgr.c:775
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:800
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:88
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
void check_stack_depth(void)
Definition: stack_depth.c:95
ObjectAddress CreateStatistics(CreateStatsStmt *stmt, bool check_rights)
Definition: statscmds.c:63
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:938
void RelationPreserveStorage(RelFileLocator rlocator, bool atCommit)
Definition: storage.c:252
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition: storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition: storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition: storage.c:187
void RelationDropStorage(Relation rel)
Definition: storage.c:207
#define InvalidStrategy
Definition: stratnum.h:24
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
RoleSpec * newowner
Definition: parsenodes.h:2522
DropBehavior behavior
Definition: parsenodes.h:2525
AlterTableType subtype
Definition: parsenodes.h:2517
RangeVar * relation
Definition: parsenodes.h:2436
ObjectType objtype
Definition: parsenodes.h:2438
const char * queryString
Definition: utility.h:33
List * constraints
Definition: tablecmds.c:188
bool verify_new_notnull
Definition: tablecmds.c:191
List * changedConstraintDefs
Definition: tablecmds.c:204
Expr * partition_constraint
Definition: tablecmds.c:199
char newrelpersistence
Definition: tablecmds.c:198
List * changedStatisticsDefs
Definition: tablecmds.c:210
bool chgAccessMethod
Definition: tablecmds.c:193
List * afterStmts
Definition: tablecmds.c:190
char * clusterOnIndex
Definition: tablecmds.c:208
char * replicaIdentityIndex
Definition: tablecmds.c:207
List * changedStatisticsOids
Definition: tablecmds.c:209
List * changedIndexDefs
Definition: tablecmds.c:206
List * changedIndexOids
Definition: tablecmds.c:205
List * changedConstraintOids
Definition: tablecmds.c:203
bool validate_default
Definition: tablecmds.c:201
List * subcmds[AT_NUM_PASSES]
Definition: tablecmds.c:186
TupleDesc oldDesc
Definition: tablecmds.c:174
Relation rel
Definition: tablecmds.c:183
Definition: attmap.h:35
AttrNumber * attnums
Definition: attmap.h:36
bool is_not_null
Definition: parsenodes.h:759
char identity
Definition: parsenodes.h:765
RangeVar * identitySequence
Definition: parsenodes.h:766
List * constraints
Definition: parsenodes.h:771
Node * cooked_default
Definition: parsenodes.h:764
char * storage_name
Definition: parsenodes.h:762
char * colname
Definition: parsenodes.h:754
TypeName * typeName
Definition: parsenodes.h:755
char generated
Definition: parsenodes.h:768
bool is_from_type
Definition: parsenodes.h:760
Node * raw_default
Definition: parsenodes.h:763
char storage
Definition: parsenodes.h:761
bool is_local
Definition: parsenodes.h:758
int16 inhcount
Definition: parsenodes.h:757
char * compression
Definition: parsenodes.h:756
ParseLoc location
Definition: parsenodes.h:773
char * comment
Definition: parsenodes.h:3391
ObjectType objtype
Definition: parsenodes.h:3389
Node * object
Definition: parsenodes.h:3390
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 ccnoinherit
Definition: tupdesc.h:34
bool ccvalid
Definition: tupdesc.h:33
char * ccbin
Definition: tupdesc.h:31
bool initdeferred
Definition: parsenodes.h:2865
ParseLoc location
Definition: parsenodes.h:2906
bool reset_default_tblspc
Definition: parsenodes.h:2887
List * keys
Definition: parsenodes.h:2877
List * pk_attrs
Definition: parsenodes.h:2895
List * fk_del_set_cols
Definition: parsenodes.h:2901
bool fk_with_period
Definition: parsenodes.h:2896
ConstrType contype
Definition: parsenodes.h:2862
Oid old_pktable_oid
Definition: parsenodes.h:2903
bool is_no_inherit
Definition: parsenodes.h:2869
char fk_upd_action
Definition: parsenodes.h:2899
List * old_conpfeqop
Definition: parsenodes.h:2902
bool is_enforced
Definition: parsenodes.h:2866
char fk_matchtype
Definition: parsenodes.h:2898
bool pk_with_period
Definition: parsenodes.h:2897
char * cooked_expr
Definition: parsenodes.h:2872
bool initially_valid
Definition: parsenodes.h:2868
bool skip_validation
Definition: parsenodes.h:2867
bool deferrable
Definition: parsenodes.h:2864
Node * raw_expr
Definition: parsenodes.h:2870
char * conname
Definition: parsenodes.h:2863
RangeVar * pktable
Definition: parsenodes.h:2893
char fk_del_action
Definition: parsenodes.h:2900
List * fk_attrs
Definition: parsenodes.h:2894
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:3150
List * transitionRels
Definition: parsenodes.h:3152
RangeVar * constrrel
Definition: parsenodes.h:3156
RangeVar * relation
Definition: parsenodes.h:3141
char * defname
Definition: parsenodes.h:844
bool missing_ok
Definition: parsenodes.h:3366
List * objects
Definition: parsenodes.h:3363
ObjectType removeType
Definition: parsenodes.h:3364
bool concurrent
Definition: parsenodes.h:3367
DropBehavior behavior
Definition: parsenodes.h:3365
int es_instrument
Definition: execnodes.h:720
MemoryContext es_query_cxt
Definition: execnodes.h:710
List * es_opened_result_relations
Definition: execnodes.h:688
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:273
ExecForeignTruncate_function ExecForeignTruncate
Definition: fdwapi.h:263
bool conenforced
Definition: rel.h:288
Oid funcid
Definition: primnodes.h:782
List * args
Definition: primnodes.h:800
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86
Definition: dynahash.c:222
ItemPointerData t_self
Definition: htup.h:65
amoptions_function amoptions
Definition: amapi.h:304
bool amcanunique
Definition: amapi.h:258
bool ii_Unique
Definition: execnodes.h:200
int ii_NumIndexKeyAttrs
Definition: execnodes.h:169
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:175
bool reset_default_tblspc
Definition: parsenodes.h:3540
char * idxname
Definition: parsenodes.h:3514
char * idxcomment
Definition: parsenodes.h:3524
Definition: lock.h:167
Definition: pg_list.h:54
AttrNumber attnum
Definition: tablecmds.c:237
bool is_generated
Definition: tablecmds.c:240
ExprState * exprstate
Definition: tablecmds.c:239
char * name
Definition: tablecmds.c:217
ConstrType contype
Definition: tablecmds.c:218
bool conwithperiod
Definition: tablecmds.c:221
Node * qual
Definition: tablecmds.c:223
ExprState * qualstate
Definition: tablecmds.c:224
Definition: nodes.h:135
NullTestType nulltesttype
Definition: primnodes.h:1984
ParseLoc location
Definition: primnodes.h:1987
Expr * arg
Definition: primnodes.h:1983
SubTransactionId creating_subid
Definition: tablecmds.c:128
SubTransactionId deleting_subid
Definition: tablecmds.c:129
OnCommitAction oncommit
Definition: tablecmds.c:119
const char * p_sourcetext
Definition: parse_node.h:195
PartitionBoundSpec * bound
Definition: parsenodes.h:993
List * partlist
Definition: parsenodes.h:1001
RangeVar * name
Definition: parsenodes.h:990
List * collation
Definition: parsenodes.h:894
ParseLoc location
Definition: parsenodes.h:896
List * opclass
Definition: parsenodes.h:895
List * partParams
Definition: parsenodes.h:915
ParseLoc location
Definition: parsenodes.h:916
PartitionStrategy strategy
Definition: parsenodes.h:914
char * relname
Definition: primnodes.h:83
bool inh
Definition: primnodes.h:86
char relpersistence
Definition: primnodes.h:89
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:2115
RelFileNumber relNumber
const 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
PartitionBoundSpec * bound
Definition: parsenodes.h:978
TransactionId xmin
Definition: snapshot.h:153
TupleTableSlot * dstslot
Definition: tablecmds.c:22936
BulkInsertState bistate
Definition: tablecmds.c:22935
ExprState * partqualstate
Definition: tablecmds.c:22933
AlteredTableInfo * tab
Definition: tablecmds.c:22937
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
AttrMap * attrMap
Definition: tupconvert.h:28
TupleConstr * constr
Definition: tupdesc.h:141
Oid tts_tableOid
Definition: tuptable.h:129
AttrNumber tts_nvalid
Definition: tuptable.h:119
bool * tts_isnull
Definition: tuptable.h:126
Datum * tts_values
Definition: tuptable.h:124
bool setof
Definition: parsenodes.h:287
List * arrayBounds
Definition: parsenodes.h:291
Definition: primnodes.h:262
const char * skipping_msg
Definition: tablecmds.c:251
int nonexistent_code
Definition: tablecmds.c:249
const char * nonexistent_msg
Definition: tablecmds.c:250
const char * drophint_msg
Definition: tablecmds.c:253
const char * nota_msg
Definition: tablecmds.c:252
Definition: type.h:96
Definition: c.h:745
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:752
Definition: regguts.h:323
bool superuser(void)
Definition: superuser.c:46
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
#define TableOidAttributeNumber
Definition: sysattr.h:26
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
Relation table_openrv_extended(const RangeVar *relation, LOCKMODE lockmode, bool missing_ok)
Definition: table.c:103
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:1377
static void table_finish_bulk_insert(Relation rel, int options)
Definition: tableam.h:1574
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition: tableam.h:1630
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:13028
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition: tablecmds.c:3842
void ResetRelRewrite(Oid myrelid)
Definition: tablecmds.c:4361
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
Definition: tablecmds.c:10676
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17528
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17895
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode)
Definition: tablecmds.c:15985
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5311
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:12793
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17291
ObjectAddress RenameRelation(RenameStmt *stmt)
Definition: tablecmds.c:4204
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6587
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition: tablecmds.c:9607
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:18386
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12228
static ColumnDef * MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3414
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:11068
#define ATT_TABLE
Definition: tablecmds.c:329
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
Definition: tablecmds.c:20142
static const char * storage_name(char c)
Definition: tablecmds.c:2460
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8831
void AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
Definition: tablecmds.c:19487
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition: tablecmds.c:7728
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8155
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
Definition: tablecmds.c:15658
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16883
static void MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
Definition: tablecmds.c:22571
static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
Definition: tablecmds.c:18192
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
Definition: tablecmds.c:12033
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17978
#define AT_NUM_PASSES
Definition: tablecmds.c:167
void PreCommit_on_commit_actions(void)
Definition: tablecmds.c:19348
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14107
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
Definition: tablecmds.c:11951
struct SplitPartitionContext SplitPartitionContext
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8517
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:22037
static int findAttrByName(const char *attributeName, const List *columns)
Definition: tablecmds.c:3608
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15386
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
Definition: tablecmds.c:14709
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9900
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12938
void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4532
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15437
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
Definition: tablecmds.c:13666
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition: tablecmds.c:7746
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7771
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13515
#define ATT_SEQUENCE
Definition: tablecmds.c:336
void RemoveRelations(DropStmt *drop)
Definition: tablecmds.c:1534
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
Definition: tablecmds.c:16645
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6920
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15299
static List * GetParentedForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21935
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19082
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9826
ObjectAddress renameatt(RenameStmt *stmt)
Definition: tablecmds.c:4007
void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:16102
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:19614
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4447
static void DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
Definition: tablecmds.c:1507
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4606
struct ForeignTruncateInfo ForeignTruncateInfo
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:20957
static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
Definition: tablecmds.c:19204
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:3987
TupleDesc BuildDescForRelation(const List *columns)
Definition: tablecmds.c:1376
static void attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
Definition: tablecmds.c:20282
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:18662
static void createTableConstraints(List **wqueue, AlteredTableInfo *tab, Relation parent_rel, Relation newRel)
Definition: tablecmds.c:22251
void AtEOXact_on_commit_actions(bool isCommit)
Definition: tablecmds.c:19455
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:13695
struct OnCommitItem OnCommitItem
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4414
static Relation createPartitionTable(List **wqueue, RangeVar *newPartName, Relation parent_rel, Oid ownerId)
Definition: tablecmds.c:22458
static void SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)
Definition: tablecmds.c:23007
static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12645
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13887
static void evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab, Relation newPartRel, TupleTableSlot *insertslot, ExprContext *econtext)
Definition: tablecmds.c:22153
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:17234
static void AttachPartitionForeignKey(List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11820
static List * MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
Definition: tablecmds.c:3165
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition: tablecmds.c:3793
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:21571
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:6890
static void truncate_check_activity(Relation rel)
Definition: tablecmds.c:2437
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:21811
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
Definition: tablecmds.c:12884
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:10097
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15916
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12162
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:10752
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15314
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:9080
static List * getAttributesList(Relation parent_rel)
Definition: tablecmds.c:22198
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:12442
static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13250
static void detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
Definition: tablecmds.c:22725
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:9313
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11477
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9159
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4873
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3691
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:18632
void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName)
Definition: tablecmds.c:6965
static void truncate_check_perms(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2419
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2368
#define ATT_INDEX
Definition: tablecmds.c:332
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:16408
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3563
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
Definition: tablecmds.c:15073
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:8785
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12371
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
Definition: tablecmds.c:21913
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8240
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:4045
static void DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
Definition: tablecmds.c:1459
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
Definition: tablecmds.c:15466
static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:22746
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5736
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19045
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9942
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11276
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17666
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9285
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3645
static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3244
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6768
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6621
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:16675
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8400
ObjectAddress AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
Definition: tablecmds.c:18974
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15330
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:1857
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)
Definition: tablecmds.c:18430
#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:7222
static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, PartitionStrategy strategy)
Definition: tablecmds.c:19813
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:20800
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:20617
static void StoreCatalogInheritance(Oid relationId, List *supers, bool child_is_partition)
Definition: tablecmds.c:3519
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:18691
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15945
struct AlteredTableInfo AlteredTableInfo
Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:4473
static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12842
struct NewConstraint NewConstraint
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8935
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6845
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:21130
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15872
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11247
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:10930
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition: tablecmds.c:7675
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:7246
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:14756
static void validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, bool hasperiod)
Definition: tablecmds.c:13724
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17471
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13147
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3748
void remove_on_commit_action(Oid relid)
Definition: tablecmds.c:19325
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16509
#define ATT_PARTITIONED_INDEX
Definition: tablecmds.c:335
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17402
static const struct dropmsgstrings dropmsgstringarray[]
Definition: tablecmds.c:256
static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
Definition: tablecmds.c:18849
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8631
ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, ObjectAddress *typaddress, const char *queryString)
Definition: tablecmds.c:769
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13824
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
Definition: tablecmds.c:12724
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9528
static void DropClonedTriggersFromPartition(Oid partitionId)
Definition: tablecmds.c:21498
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:20205
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:18244
static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:1698
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4561
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12101
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7172
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17853
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21988
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9734
#define ATT_VIEW
Definition: tablecmds.c:330
static SplitPartitionContext * createSplitPartitionContext(Relation partRel)
Definition: tablecmds.c:22946
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19159
#define ATT_PARTITIONED_TABLE
Definition: tablecmds.c:337
static void deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, int ti_options)
Definition: tablecmds.c:22969
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5385
static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12588
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:18518
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:14022
static void ATPrepAddInherit(Relation child_rel)
Definition: tablecmds.c:17269
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:21464
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9222
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20312
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:7123
Oid AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
Definition: tablecmds.c:17015
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:13412
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8269
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:16555
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14042
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9649
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:18772
static PartitionSpec * transformPartitionSpec(Relation rel, PartitionSpec *partspec)
Definition: tablecmds.c:19755
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4908
static void buildExpressionExecutionStates(AlteredTableInfo *tab, Relation newPartRel, EState *estate)
Definition: tablecmds.c:22115
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:17177
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:7942
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4268
static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
Definition: tablecmds.c:7869
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:11724
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16976
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:14403
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
Definition: tablecmds.c:13357
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:16343
#define ATT_COMPOSITE_TYPE
Definition: tablecmds.c:333
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:21625
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:20087
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19522
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:23200
static List * on_commits
Definition: tablecmds.c:132
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:17499
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9713
ObjectAddress RenameConstraint(RenameStmt *stmt)
Definition: tablecmds.c:4154
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5863
void register_on_commit_action(Oid relid, OnCommitAction action)
Definition: tablecmds.c:19289
static void RangeVarCallbackForTruncate(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19558
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:16477
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:22075
void RangeVarCallbackOwnsRelation(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19582
#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:21787
void ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
Definition: tablecmds.c:1981
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
Definition: tablecmds.c:6151
static List * MergeAttributes(List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
Definition: tablecmds.c:2545
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:17252
struct NewColumnValue NewColumnValue
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
Definition: tablecmds.c:16521
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:8118
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
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: tupconvert.c:103
void free_conversion_map(TupleConversionMap *map)
Definition: tupconvert.c:300
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:193
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:340
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition: tupdesc.c:1092
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:182
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition: tupdesc.c:117
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition: tupdesc.c:1026
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:842
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:219
#define ATTNULLABLE_VALID
Definition: tupdesc.h:86
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:175
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:457
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:371
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition: tuptable.h:384
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1921
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1488
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition: typecmds.c:4000
ObjectAddress AlterDomainAddConstraint(List *names, Node *newConstraint, ObjectAddress *constrAddr)
Definition: typecmds.c:2938
void checkDomainOwner(HeapTuple tup)
Definition: typecmds.c:3499
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4169
List * roleSpecsToIds(List *memberNames)
Definition: user.c:1652
void SwitchToUntrustedUser(Oid userid, UserContext *context)
Definition: usercontext.c:33
void RestoreUserContext(UserContext *context)
Definition: usercontext.c:87
void ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
Definition: utility.c:1959
#define MAX_STATISTICS_TARGET
Definition: vacuum.h:329
String * makeString(char *str)
Definition: value.c:63
#define intVal(v)
Definition: value.h:79
#define strVal(v)
Definition: value.h:82
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:296
static char * VARDATA_ANY(const void *PTR)
Definition: varatt.h:486
const char * name
SubTransactionId GetCurrentSubTransactionId(void)
Definition: xact.c:792
void CommandCounterIncrement(void)
Definition: xact.c:1101
void StartTransactionCommand(void)
Definition: xact.c:3080
void CommitTransactionCommand(void)
Definition: xact.c:3178
int MyXactFlags
Definition: xact.c:137
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:830
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:103
#define XLogLogicalInfoActive()
Definition: xlog.h:136
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:165
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:478
void XLogRegisterData(const void *data, uint32 len)
Definition: xloginsert.c:368
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:460
void XLogBeginInsert(void)
Definition: xloginsert.c:152