PostgreSQL Source Code git master
tablecmds.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/commands/tablecmds.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include "access/attmap.h"
18#include "access/genam.h"
19#include "access/gist.h"
20#include "access/heapam.h"
21#include "access/heapam_xlog.h"
22#include "access/multixact.h"
23#include "access/reloptions.h"
24#include "access/relscan.h"
25#include "access/sysattr.h"
26#include "access/tableam.h"
28#include "access/xact.h"
29#include "access/xlog.h"
30#include "access/xloginsert.h"
31#include "catalog/catalog.h"
32#include "catalog/heap.h"
33#include "catalog/index.h"
34#include "catalog/namespace.h"
36#include "catalog/partition.h"
37#include "catalog/pg_am.h"
38#include "catalog/pg_attrdef.h"
41#include "catalog/pg_depend.h"
43#include "catalog/pg_inherits.h"
47#include "catalog/pg_opclass.h"
48#include "catalog/pg_policy.h"
49#include "catalog/pg_proc.h"
51#include "catalog/pg_rewrite.h"
54#include "catalog/pg_trigger.h"
55#include "catalog/pg_type.h"
56#include "catalog/storage.h"
58#include "catalog/toasting.h"
59#include "commands/cluster.h"
60#include "commands/comment.h"
61#include "commands/defrem.h"
63#include "commands/sequence.h"
64#include "commands/tablecmds.h"
65#include "commands/tablespace.h"
66#include "commands/trigger.h"
67#include "commands/typecmds.h"
68#include "commands/user.h"
69#include "commands/vacuum.h"
70#include "common/int.h"
71#include "executor/executor.h"
72#include "foreign/fdwapi.h"
73#include "foreign/foreign.h"
74#include "miscadmin.h"
75#include "nodes/makefuncs.h"
76#include "nodes/nodeFuncs.h"
77#include "nodes/parsenodes.h"
78#include "optimizer/optimizer.h"
79#include "parser/parse_coerce.h"
81#include "parser/parse_expr.h"
83#include "parser/parse_type.h"
85#include "parser/parser.h"
88#include "pgstat.h"
92#include "storage/bufmgr.h"
93#include "storage/lmgr.h"
94#include "storage/lock.h"
95#include "storage/predicate.h"
96#include "storage/smgr.h"
97#include "tcop/utility.h"
98#include "utils/acl.h"
99#include "utils/builtins.h"
100#include "utils/fmgroids.h"
101#include "utils/inval.h"
102#include "utils/lsyscache.h"
103#include "utils/memutils.h"
104#include "utils/partcache.h"
105#include "utils/relcache.h"
106#include "utils/ruleutils.h"
107#include "utils/snapmgr.h"
108#include "utils/syscache.h"
109#include "utils/timestamp.h"
110#include "utils/typcache.h"
111#include "utils/usercontext.h"
112
113/*
114 * ON COMMIT action list
115 */
116typedef struct OnCommitItem
117{
118 Oid relid; /* relid of relation */
119 OnCommitAction oncommit; /* what to do at end of xact */
120
121 /*
122 * If this entry was created during the current transaction,
123 * creating_subid is the ID of the creating subxact; if created in a prior
124 * transaction, creating_subid is zero. If deleted during the current
125 * transaction, deleting_subid is the ID of the deleting subxact; if no
126 * deletion request is pending, deleting_subid is zero.
127 */
131
133
134
135/*
136 * State information for ALTER TABLE
137 *
138 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
139 * structs, one for each table modified by the operation (the named table
140 * plus any child tables that are affected). We save lists of subcommands
141 * to apply to this table (possibly modified by parse transformation steps);
142 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
143 * necessary information is stored in the constraints and newvals lists.
144 *
145 * Phase 2 is divided into multiple passes; subcommands are executed in
146 * a pass determined by subcommand type.
147 */
148
149typedef enum AlterTablePass
150{
151 AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
152 AT_PASS_DROP, /* DROP (all flavors) */
153 AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
154 AT_PASS_ADD_COL, /* ADD COLUMN */
155 AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
156 AT_PASS_OLD_INDEX, /* re-add existing indexes */
157 AT_PASS_OLD_CONSTR, /* re-add existing constraints */
158 /* We could support a RENAME COLUMN pass here, but not currently used */
159 AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
160 AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
161 AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
162 AT_PASS_ADD_INDEX, /* ADD indexes */
163 AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
164 AT_PASS_MISC, /* other stuff */
166
167#define AT_NUM_PASSES (AT_PASS_MISC + 1)
168
169typedef struct AlteredTableInfo
170{
171 /* Information saved before any work commences: */
172 Oid relid; /* Relation to work on */
173 char relkind; /* Its relkind */
174 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
175
176 /*
177 * Transiently set during Phase 2, normally set to NULL.
178 *
179 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
180 * returns control. This can be exploited by ATExecCmd subroutines to
181 * close/reopen across transaction boundaries.
182 */
184
185 /* Information saved by Phase 1 for Phase 2: */
186 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
187 /* Information saved by Phases 1/2 for Phase 3: */
188 List *constraints; /* List of NewConstraint */
189 List *newvals; /* List of NewColumnValue */
190 List *afterStmts; /* List of utility command parsetrees */
191 bool verify_new_notnull; /* T if we should recheck NOT NULL */
192 int rewrite; /* Reason for forced rewrite, if any */
193 bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
194 Oid newAccessMethod; /* new access method; 0 means no change,
195 * if above is true */
196 Oid newTableSpace; /* new tablespace; 0 means no change */
197 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
198 char newrelpersistence; /* if above is true */
199 Expr *partition_constraint; /* for attach partition validation */
200 /* true, if validating default due to some other attach/detach */
202 /* Objects to rebuild after completing ALTER TYPE operations */
203 List *changedConstraintOids; /* OIDs of constraints to rebuild */
204 List *changedConstraintDefs; /* string definitions of same */
205 List *changedIndexOids; /* OIDs of indexes to rebuild */
206 List *changedIndexDefs; /* string definitions of same */
207 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
208 char *clusterOnIndex; /* index to use for CLUSTER */
209 List *changedStatisticsOids; /* OIDs of statistics to rebuild */
210 List *changedStatisticsDefs; /* string definitions of same */
212
213/* Struct describing one new constraint to check in Phase 3 scan */
214/* Note: new not-null constraints are handled elsewhere */
215typedef struct NewConstraint
216{
217 char *name; /* Constraint name, or NULL if none */
218 ConstrType contype; /* CHECK or FOREIGN */
219 Oid refrelid; /* PK rel, if FOREIGN */
220 Oid refindid; /* OID of PK's index, if FOREIGN */
221 bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
222 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
223 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
224 ExprState *qualstate; /* Execution state for CHECK expr */
226
227/*
228 * Struct describing one new column value that needs to be computed during
229 * Phase 3 copy (this could be either a new column with a non-null default, or
230 * a column that we're changing the type of). Columns without such an entry
231 * are just copied from the old table during ATRewriteTable. Note that the
232 * expr is an expression over *old* table values, except when is_generated
233 * is true; then it is an expression over columns of the *new* tuple.
234 */
235typedef struct NewColumnValue
236{
237 AttrNumber attnum; /* which column */
238 Expr *expr; /* expression to compute */
239 ExprState *exprstate; /* execution state */
240 bool is_generated; /* is it a GENERATED expression? */
242
243/*
244 * Error-reporting support for RemoveRelations
245 */
247{
248 char kind;
250 const char *nonexistent_msg;
251 const char *skipping_msg;
252 const char *nota_msg;
253 const char *drophint_msg;
254};
255
256static const struct dropmsgstrings dropmsgstringarray[] = {
257 {RELKIND_RELATION,
259 gettext_noop("table \"%s\" does not exist"),
260 gettext_noop("table \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a table"),
262 gettext_noop("Use DROP TABLE to remove a table.")},
263 {RELKIND_SEQUENCE,
265 gettext_noop("sequence \"%s\" does not exist"),
266 gettext_noop("sequence \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not a sequence"),
268 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
269 {RELKIND_VIEW,
271 gettext_noop("view \"%s\" does not exist"),
272 gettext_noop("view \"%s\" does not exist, skipping"),
273 gettext_noop("\"%s\" is not a view"),
274 gettext_noop("Use DROP VIEW to remove a view.")},
275 {RELKIND_MATVIEW,
277 gettext_noop("materialized view \"%s\" does not exist"),
278 gettext_noop("materialized view \"%s\" does not exist, skipping"),
279 gettext_noop("\"%s\" is not a materialized view"),
280 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
281 {RELKIND_INDEX,
282 ERRCODE_UNDEFINED_OBJECT,
283 gettext_noop("index \"%s\" does not exist"),
284 gettext_noop("index \"%s\" does not exist, skipping"),
285 gettext_noop("\"%s\" is not an index"),
286 gettext_noop("Use DROP INDEX to remove an index.")},
287 {RELKIND_COMPOSITE_TYPE,
288 ERRCODE_UNDEFINED_OBJECT,
289 gettext_noop("type \"%s\" does not exist"),
290 gettext_noop("type \"%s\" does not exist, skipping"),
291 gettext_noop("\"%s\" is not a type"),
292 gettext_noop("Use DROP TYPE to remove a type.")},
293 {RELKIND_FOREIGN_TABLE,
294 ERRCODE_UNDEFINED_OBJECT,
295 gettext_noop("foreign table \"%s\" does not exist"),
296 gettext_noop("foreign table \"%s\" does not exist, skipping"),
297 gettext_noop("\"%s\" is not a foreign table"),
298 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
299 {RELKIND_PARTITIONED_TABLE,
301 gettext_noop("table \"%s\" does not exist"),
302 gettext_noop("table \"%s\" does not exist, skipping"),
303 gettext_noop("\"%s\" is not a table"),
304 gettext_noop("Use DROP TABLE to remove a table.")},
305 {RELKIND_PARTITIONED_INDEX,
306 ERRCODE_UNDEFINED_OBJECT,
307 gettext_noop("index \"%s\" does not exist"),
308 gettext_noop("index \"%s\" does not exist, skipping"),
309 gettext_noop("\"%s\" is not an index"),
310 gettext_noop("Use DROP INDEX to remove an index.")},
311 {'\0', 0, NULL, NULL, NULL, NULL}
312};
313
314/* communication between RemoveRelations and RangeVarCallbackForDropRelation */
316{
317 /* These fields are set by RemoveRelations: */
320 /* These fields are state to track which subsidiary locks are held: */
323 /* These fields are passed back by RangeVarCallbackForDropRelation: */
326};
327
328/* Alter table target-type flags for ATSimplePermissions */
329#define ATT_TABLE 0x0001
330#define ATT_VIEW 0x0002
331#define ATT_MATVIEW 0x0004
332#define ATT_INDEX 0x0008
333#define ATT_COMPOSITE_TYPE 0x0010
334#define ATT_FOREIGN_TABLE 0x0020
335#define ATT_PARTITIONED_INDEX 0x0040
336#define ATT_SEQUENCE 0x0080
337#define ATT_PARTITIONED_TABLE 0x0100
338
339/*
340 * ForeignTruncateInfo
341 *
342 * Information related to truncation of foreign tables. This is used for
343 * the elements in a hash table. It uses the server OID as lookup key,
344 * and includes a per-server list of all foreign tables involved in the
345 * truncation.
346 */
348{
352
353/* Partial or complete FK creation in addFkConstraint() */
355{
360
361/*
362 * Partition tables are expected to be dropped when the parent partitioned
363 * table gets dropped. Hence for partitioning we use AUTO dependency.
364 * Otherwise, for regular inheritance use NORMAL dependency.
365 */
366#define child_dependency_type(child_is_partition) \
367 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
368
369static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
370static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
371static void truncate_check_activity(Relation rel);
372static void RangeVarCallbackForTruncate(const RangeVar *relation,
373 Oid relId, Oid oldRelId, void *arg);
374static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
375 bool is_partition, List **supconstr,
376 List **supnotnulls);
377static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
378static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
379static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
380static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
381static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
382static void StoreCatalogInheritance(Oid relationId, List *supers,
383 bool child_is_partition);
384static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
385 int32 seqNumber, Relation inhRelation,
386 bool child_is_partition);
387static int findAttrByName(const char *attributeName, const List *columns);
388static void AlterIndexNamespaces(Relation classRel, Relation rel,
389 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
390static void AlterSeqNamespaces(Relation classRel, Relation rel,
391 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
392 LOCKMODE lockmode);
394 ATAlterConstraint *cmdcon,
395 bool recurse, LOCKMODE lockmode);
396static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
397 Relation tgrel, Relation rel, HeapTuple contuple,
398 bool recurse, LOCKMODE lockmode);
399static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
400 Relation conrel, Relation tgrel,
401 Oid fkrelid, Oid pkrelid,
402 HeapTuple contuple, LOCKMODE lockmode,
403 Oid ReferencedParentDelTrigger,
404 Oid ReferencedParentUpdTrigger,
405 Oid ReferencingParentInsTrigger,
406 Oid ReferencingParentUpdTrigger);
407static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
408 Relation conrel, Relation tgrel, Relation rel,
409 HeapTuple contuple, bool recurse,
410 List **otherrelids, LOCKMODE lockmode);
411static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
412 Relation conrel, Relation rel,
413 HeapTuple contuple, LOCKMODE lockmode);
414static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
415 bool deferrable, bool initdeferred,
416 List **otherrelids);
417static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
418 Relation conrel, Relation tgrel,
419 Oid fkrelid, Oid pkrelid,
420 HeapTuple contuple, LOCKMODE lockmode,
421 Oid ReferencedParentDelTrigger,
422 Oid ReferencedParentUpdTrigger,
423 Oid ReferencingParentInsTrigger,
424 Oid ReferencingParentUpdTrigger);
425static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
426 Relation conrel, Relation tgrel, Relation rel,
427 HeapTuple contuple, bool recurse,
428 List **otherrelids, LOCKMODE lockmode);
430 HeapTuple contuple);
432 Relation rel, char *constrName,
433 bool recurse, bool recursing, LOCKMODE lockmode);
434static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
435 Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
436static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
437 char *constrName, HeapTuple contuple,
438 bool recurse, bool recursing, LOCKMODE lockmode);
439static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
440 HeapTuple contuple, bool recurse, bool recursing,
441 LOCKMODE lockmode);
442static int transformColumnNameList(Oid relId, List *colList,
443 int16 *attnums, Oid *atttypids, Oid *attcollids);
444static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
445 List **attnamelist,
446 int16 *attnums, Oid *atttypids, Oid *attcollids,
447 Oid *opclasses, bool *pk_has_without_overlaps);
449 int numattrs, int16 *attnums,
450 bool with_period, Oid *opclasses,
451 bool *pk_has_without_overlaps);
452static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
453static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
454 Oid *funcid);
455static void validateForeignKeyConstraint(char *conname,
456 Relation rel, Relation pkrel,
457 Oid pkindOid, Oid constraintOid, bool hasperiod);
458static void CheckAlterTableIsSafe(Relation rel);
459static void ATController(AlterTableStmt *parsetree,
460 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
461 AlterTableUtilityContext *context);
462static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
463 bool recurse, bool recursing, LOCKMODE lockmode,
464 AlterTableUtilityContext *context);
465static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
466 AlterTableUtilityContext *context);
467static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
468 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
469 AlterTableUtilityContext *context);
471 Relation rel, AlterTableCmd *cmd,
472 bool recurse, LOCKMODE lockmode,
473 AlterTablePass cur_pass,
474 AlterTableUtilityContext *context);
475static void ATRewriteTables(AlterTableStmt *parsetree,
476 List **wqueue, LOCKMODE lockmode,
477 AlterTableUtilityContext *context);
478static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
479static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
480static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
481static void ATSimpleRecursion(List **wqueue, Relation rel,
482 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
483 AlterTableUtilityContext *context);
484static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
485static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
486 LOCKMODE lockmode,
487 AlterTableUtilityContext *context);
488static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
489 DropBehavior behavior);
490static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
491 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
492 AlterTableUtilityContext *context);
494 Relation rel, AlterTableCmd **cmd,
495 bool recurse, bool recursing,
496 LOCKMODE lockmode, AlterTablePass cur_pass,
497 AlterTableUtilityContext *context);
498static bool check_for_column_name_collision(Relation rel, const char *colname,
499 bool if_not_exists);
500static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
502static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
503 LOCKMODE lockmode);
504static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
505 bool is_valid, bool queue_validation);
506static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
507 char *conName, char *colName,
508 bool recurse, bool recursing,
509 LOCKMODE lockmode);
512 List *testConstraint, List *provenConstraint);
513static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
514 Node *newDefault, LOCKMODE lockmode);
516 Node *newDefault);
517static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
518 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
519static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
520 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
521static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
522 bool recurse, bool recursing);
523static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
524 Node *newExpr, LOCKMODE lockmode);
525static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
526static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
527static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
528 Node *newValue, LOCKMODE lockmode);
529static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
530 Node *options, bool isReset, LOCKMODE lockmode);
531static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
532 Node *newValue, LOCKMODE lockmode);
533static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
534 AlterTableCmd *cmd, LOCKMODE lockmode,
535 AlterTableUtilityContext *context);
536static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
537 DropBehavior behavior,
538 bool recurse, bool recursing,
539 bool missing_ok, LOCKMODE lockmode,
540 ObjectAddresses *addrs);
541static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
542 bool recurse, LOCKMODE lockmode,
543 AlterTableUtilityContext *context);
544static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
546 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
548 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
550 AlteredTableInfo *tab, Relation rel,
551 Constraint *newConstraint, bool recurse, bool is_readd,
552 LOCKMODE lockmode);
553static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
555 IndexStmt *stmt, LOCKMODE lockmode);
557 AlteredTableInfo *tab, Relation rel,
558 Constraint *constr,
559 bool recurse, bool recursing, bool is_readd,
560 LOCKMODE lockmode);
562 Relation rel, Constraint *fkconstraint,
563 bool recurse, bool recursing,
564 LOCKMODE lockmode);
565static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
566 int numfksetcols, int16 *fksetcolsattnums,
567 List *fksetcols);
569 char *constraintname,
570 Constraint *fkconstraint, Relation rel,
571 Relation pkrel, Oid indexOid,
572 Oid parentConstr,
573 int numfks, int16 *pkattnum, int16 *fkattnum,
574 Oid *pfeqoperators, Oid *ppeqoperators,
575 Oid *ffeqoperators, int numfkdelsetcols,
576 int16 *fkdelsetcols, bool is_internal,
577 bool with_period);
578static void addFkRecurseReferenced(Constraint *fkconstraint,
579 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
580 int numfks, int16 *pkattnum, int16 *fkattnum,
581 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
582 int numfkdelsetcols, int16 *fkdelsetcols,
583 bool old_check_ok,
584 Oid parentDelTrigger, Oid parentUpdTrigger,
585 bool with_period);
586static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
587 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
588 int numfks, int16 *pkattnum, int16 *fkattnum,
589 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
590 int numfkdelsetcols, int16 *fkdelsetcols,
591 bool old_check_ok, LOCKMODE lockmode,
592 Oid parentInsTrigger, Oid parentUpdTrigger,
593 bool with_period);
594static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
595 Relation partitionRel);
596static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
597static void CloneFkReferencing(List **wqueue, Relation parentRel,
598 Relation partRel);
599static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
600 Constraint *fkconstraint, Oid constraintOid,
601 Oid indexOid,
602 Oid parentInsTrigger, Oid parentUpdTrigger,
603 Oid *insertTrigOid, Oid *updateTrigOid);
604static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
605 Constraint *fkconstraint, Oid constraintOid,
606 Oid indexOid,
607 Oid parentDelTrigger, Oid parentUpdTrigger,
608 Oid *deleteTrigOid, Oid *updateTrigOid);
609static bool tryAttachPartitionForeignKey(List **wqueue,
611 Relation partition,
612 Oid parentConstrOid, int numfks,
613 AttrNumber *mapped_conkey, AttrNumber *confkey,
614 Oid *conpfeqop,
615 Oid parentInsTrigger,
616 Oid parentUpdTrigger,
617 Relation trigrel);
618static void AttachPartitionForeignKey(List **wqueue, Relation partition,
619 Oid partConstrOid, Oid parentConstrOid,
620 Oid parentInsTrigger, Oid parentUpdTrigger,
621 Relation trigrel);
622static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
623 Oid conoid, Oid conrelid);
624static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
625 Oid confrelid, Oid conrelid);
626static void GetForeignKeyActionTriggers(Relation trigrel,
627 Oid conoid, Oid confrelid, Oid conrelid,
628 Oid *deleteTriggerOid,
629 Oid *updateTriggerOid);
630static void GetForeignKeyCheckTriggers(Relation trigrel,
631 Oid conoid, Oid confrelid, Oid conrelid,
632 Oid *insertTriggerOid,
633 Oid *updateTriggerOid);
634static void ATExecDropConstraint(Relation rel, const char *constrName,
635 DropBehavior behavior, bool recurse,
636 bool missing_ok, LOCKMODE lockmode);
638 HeapTuple constraintTup, DropBehavior behavior,
639 bool recurse, bool recursing,
640 bool missing_ok, LOCKMODE lockmode);
641static void ATPrepAlterColumnType(List **wqueue,
642 AlteredTableInfo *tab, Relation rel,
643 bool recurse, bool recursing,
644 AlterTableCmd *cmd, LOCKMODE lockmode,
645 AlterTableUtilityContext *context);
646static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
648 AlterTableCmd *cmd, LOCKMODE lockmode);
650 Relation rel, AttrNumber attnum, const char *colName);
652static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
654static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
655 LOCKMODE lockmode);
656static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
657 char *cmd, List **wqueue, LOCKMODE lockmode,
658 bool rewrite);
660 Oid objid, Relation rel, List *domname,
661 const char *conname);
662static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
663static void TryReuseForeignKey(Oid oldId, Constraint *con);
664static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
665 List *options, LOCKMODE lockmode);
666static void change_owner_fix_column_acls(Oid relationOid,
667 Oid oldOwnerId, Oid newOwnerId);
668static void change_owner_recurse_to_sequences(Oid relationOid,
669 Oid newOwnerId, LOCKMODE lockmode);
670static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
671 LOCKMODE lockmode);
672static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
673static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
674static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
676 bool toLogged);
677static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
678 const char *tablespacename, LOCKMODE lockmode);
679static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
680static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
681static void ATExecSetRelOptions(Relation rel, List *defList,
682 AlterTableType operation,
683 LOCKMODE lockmode);
684static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
685 char fires_when, bool skip_system, bool recurse,
686 LOCKMODE lockmode);
687static void ATExecEnableDisableRule(Relation rel, const char *rulename,
688 char fires_when, LOCKMODE lockmode);
689static void ATPrepAddInherit(Relation child_rel);
690static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
691static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
692static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
693 DependencyType deptype);
694static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
695static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
697static void ATExecGenericOptions(Relation rel, List *options);
698static void ATExecSetRowSecurity(Relation rel, bool rls);
699static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
701 const char *column, Node *newValue, LOCKMODE lockmode);
702
703static void index_copy_data(Relation rel, RelFileLocator newrlocator);
704static const char *storage_name(char c);
705
706static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
707 Oid oldRelOid, void *arg);
708static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
709 Oid oldrelid, void *arg);
711static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
712 List **partexprs, Oid *partopclass, Oid *partcollation,
713 PartitionStrategy strategy);
714static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
715static void RemoveInheritance(Relation child_rel, Relation parent_rel,
716 bool expect_detached);
718 PartitionCmd *cmd,
719 AlterTableUtilityContext *context);
720static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
721static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
722 List *partConstraint,
723 bool validate_default);
724static void CloneRowTriggersToPartition(Relation parent, Relation partition);
725static void DropClonedTriggersFromPartition(Oid partitionId);
727 Relation rel, RangeVar *name,
728 bool concurrent);
729static void DetachPartitionFinalize(Relation rel, Relation partRel,
730 bool concurrent, Oid defaultPartOid);
732static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
733 RangeVar *name);
734static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
735static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
736 Relation partitionTbl);
737static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
738static List *GetParentedForeignKeyRefs(Relation partition);
739static void ATDetachCheckNoForeignKeyRefs(Relation partition);
740static char GetAttributeCompression(Oid atttypid, const char *compression);
741static char GetAttributeStorage(Oid atttypid, const char *storagemode);
742
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);
1306 idxstmt,
1307 InvalidOid,
1308 RelationGetRelid(idxRel),
1309 constraintOid,
1310 -1,
1311 false, false, false, false, false);
1312
1314 }
1315
1316 list_free(idxlist);
1317
1318 /*
1319 * If there are any row-level triggers, clone them to the new
1320 * partition.
1321 */
1322 if (parent->trigdesc != NULL)
1323 CloneRowTriggersToPartition(parent, rel);
1324
1325 /*
1326 * And foreign keys too. Note that because we're freshly creating the
1327 * table, there is no need to verify these new constraints.
1328 */
1329 CloneForeignKeyConstraints(NULL, parent, rel);
1330
1331 table_close(parent, NoLock);
1332 }
1333
1334 /*
1335 * Now add any newly specified CHECK constraints to the new relation. Same
1336 * as for defaults above, but these need to come after partitioning is set
1337 * up.
1338 */
1339 if (stmt->constraints)
1340 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1341 true, true, false, queryString);
1342
1343 /*
1344 * Finally, merge the not-null constraints that are declared directly with
1345 * those that come from parent relations (making sure to count inheritance
1346 * appropriately for each), create them, and set the attnotnull flag on
1347 * columns that don't yet have it.
1348 */
1349 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1350 old_notnulls);
1351 foreach_int(attrnum, nncols)
1352 set_attnotnull(NULL, rel, attrnum, true, false);
1353
1354 ObjectAddressSet(address, RelationRelationId, relationId);
1355
1356 /*
1357 * Clean up. We keep lock on new relation (although it shouldn't be
1358 * visible to anyone else anyway, until commit).
1359 */
1360 relation_close(rel, NoLock);
1361
1362 return address;
1363}
1364
1365/*
1366 * BuildDescForRelation
1367 *
1368 * Given a list of ColumnDef nodes, build a TupleDesc.
1369 *
1370 * Note: This is only for the limited purpose of table and view creation. Not
1371 * everything is filled in. A real tuple descriptor should be obtained from
1372 * the relcache.
1373 */
1376{
1377 int natts;
1379 ListCell *l;
1380 TupleDesc desc;
1381 char *attname;
1382 Oid atttypid;
1383 int32 atttypmod;
1384 Oid attcollation;
1385 int attdim;
1386
1387 /*
1388 * allocate a new tuple descriptor
1389 */
1390 natts = list_length(columns);
1391 desc = CreateTemplateTupleDesc(natts);
1392
1393 attnum = 0;
1394
1395 foreach(l, columns)
1396 {
1397 ColumnDef *entry = lfirst(l);
1398 AclResult aclresult;
1400
1401 /*
1402 * for each entry in the list, get the name and type information from
1403 * the list and have TupleDescInitEntry fill in the attribute
1404 * information we need.
1405 */
1406 attnum++;
1407
1408 attname = entry->colname;
1409 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1410
1411 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1412 if (aclresult != ACLCHECK_OK)
1413 aclcheck_error_type(aclresult, atttypid);
1414
1415 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1416 attdim = list_length(entry->typeName->arrayBounds);
1417 if (attdim > PG_INT16_MAX)
1418 ereport(ERROR,
1419 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1420 errmsg("too many array dimensions"));
1421
1422 if (entry->typeName->setof)
1423 ereport(ERROR,
1424 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1425 errmsg("column \"%s\" cannot be declared SETOF",
1426 attname)));
1427
1429 atttypid, atttypmod, attdim);
1430 att = TupleDescAttr(desc, attnum - 1);
1431
1432 /* Override TupleDescInitEntry's settings as requested */
1433 TupleDescInitEntryCollation(desc, attnum, attcollation);
1434
1435 /* Fill in additional stuff not handled by TupleDescInitEntry */
1436 att->attnotnull = entry->is_not_null;
1437 att->attislocal = entry->is_local;
1438 att->attinhcount = entry->inhcount;
1439 att->attidentity = entry->identity;
1440 att->attgenerated = entry->generated;
1441 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1442 if (entry->storage)
1443 att->attstorage = entry->storage;
1444 else if (entry->storage_name)
1445 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1446
1448 }
1449
1450 return desc;
1451}
1452
1453/*
1454 * Emit the right error or warning message for a "DROP" command issued on a
1455 * non-existent relation
1456 */
1457static void
1458DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1459{
1460 const struct dropmsgstrings *rentry;
1461
1462 if (rel->schemaname != NULL &&
1464 {
1465 if (!missing_ok)
1466 {
1467 ereport(ERROR,
1468 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1469 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1470 }
1471 else
1472 {
1474 (errmsg("schema \"%s\" does not exist, skipping",
1475 rel->schemaname)));
1476 }
1477 return;
1478 }
1479
1480 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1481 {
1482 if (rentry->kind == rightkind)
1483 {
1484 if (!missing_ok)
1485 {
1486 ereport(ERROR,
1487 (errcode(rentry->nonexistent_code),
1488 errmsg(rentry->nonexistent_msg, rel->relname)));
1489 }
1490 else
1491 {
1492 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1493 break;
1494 }
1495 }
1496 }
1497
1498 Assert(rentry->kind != '\0'); /* Should be impossible */
1499}
1500
1501/*
1502 * Emit the right error message for a "DROP" command issued on a
1503 * relation of the wrong type
1504 */
1505static void
1506DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1507{
1508 const struct dropmsgstrings *rentry;
1509 const struct dropmsgstrings *wentry;
1510
1511 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1512 if (rentry->kind == rightkind)
1513 break;
1514 Assert(rentry->kind != '\0');
1515
1516 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1517 if (wentry->kind == wrongkind)
1518 break;
1519 /* wrongkind could be something we don't have in our table... */
1520
1521 ereport(ERROR,
1522 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1523 errmsg(rentry->nota_msg, relname),
1524 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1525}
1526
1527/*
1528 * RemoveRelations
1529 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1530 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1531 */
1532void
1534{
1535 ObjectAddresses *objects;
1536 char relkind;
1537 ListCell *cell;
1538 int flags = 0;
1539 LOCKMODE lockmode = AccessExclusiveLock;
1540
1541 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1542 if (drop->concurrent)
1543 {
1544 /*
1545 * Note that for temporary relations this lock may get upgraded later
1546 * on, but as no other session can access a temporary relation, this
1547 * is actually fine.
1548 */
1549 lockmode = ShareUpdateExclusiveLock;
1550 Assert(drop->removeType == OBJECT_INDEX);
1551 if (list_length(drop->objects) != 1)
1552 ereport(ERROR,
1553 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1554 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1555 if (drop->behavior == DROP_CASCADE)
1556 ereport(ERROR,
1557 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1558 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1559 }
1560
1561 /*
1562 * First we identify all the relations, then we delete them in a single
1563 * performMultipleDeletions() call. This is to avoid unwanted DROP
1564 * RESTRICT errors if one of the relations depends on another.
1565 */
1566
1567 /* Determine required relkind */
1568 switch (drop->removeType)
1569 {
1570 case OBJECT_TABLE:
1571 relkind = RELKIND_RELATION;
1572 break;
1573
1574 case OBJECT_INDEX:
1575 relkind = RELKIND_INDEX;
1576 break;
1577
1578 case OBJECT_SEQUENCE:
1579 relkind = RELKIND_SEQUENCE;
1580 break;
1581
1582 case OBJECT_VIEW:
1583 relkind = RELKIND_VIEW;
1584 break;
1585
1586 case OBJECT_MATVIEW:
1587 relkind = RELKIND_MATVIEW;
1588 break;
1589
1591 relkind = RELKIND_FOREIGN_TABLE;
1592 break;
1593
1594 default:
1595 elog(ERROR, "unrecognized drop object type: %d",
1596 (int) drop->removeType);
1597 relkind = 0; /* keep compiler quiet */
1598 break;
1599 }
1600
1601 /* Lock and validate each relation; build a list of object addresses */
1602 objects = new_object_addresses();
1603
1604 foreach(cell, drop->objects)
1605 {
1606 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1607 Oid relOid;
1608 ObjectAddress obj;
1610
1611 /*
1612 * These next few steps are a great deal like relation_openrv, but we
1613 * don't bother building a relcache entry since we don't need it.
1614 *
1615 * Check for shared-cache-inval messages before trying to access the
1616 * relation. This is needed to cover the case where the name
1617 * identifies a rel that has been dropped and recreated since the
1618 * start of our transaction: if we don't flush the old syscache entry,
1619 * then we'll latch onto that entry and suffer an error later.
1620 */
1622
1623 /* Look up the appropriate relation using namespace search. */
1624 state.expected_relkind = relkind;
1625 state.heap_lockmode = drop->concurrent ?
1627 /* We must initialize these fields to show that no locks are held: */
1628 state.heapOid = InvalidOid;
1629 state.partParentOid = InvalidOid;
1630
1631 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1633 &state);
1634
1635 /* Not there? */
1636 if (!OidIsValid(relOid))
1637 {
1638 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1639 continue;
1640 }
1641
1642 /*
1643 * Decide if concurrent mode needs to be used here or not. The
1644 * callback retrieved the rel's persistence for us.
1645 */
1646 if (drop->concurrent &&
1647 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1648 {
1649 Assert(list_length(drop->objects) == 1 &&
1650 drop->removeType == OBJECT_INDEX);
1652 }
1653
1654 /*
1655 * Concurrent index drop cannot be used with partitioned indexes,
1656 * either.
1657 */
1658 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1659 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1660 ereport(ERROR,
1661 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1662 errmsg("cannot drop partitioned index \"%s\" concurrently",
1663 rel->relname)));
1664
1665 /*
1666 * If we're told to drop a partitioned index, we must acquire lock on
1667 * all the children of its parent partitioned table before proceeding.
1668 * Otherwise we'd try to lock the child index partitions before their
1669 * tables, leading to potential deadlock against other sessions that
1670 * will lock those objects in the other order.
1671 */
1672 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1673 (void) find_all_inheritors(state.heapOid,
1674 state.heap_lockmode,
1675 NULL);
1676
1677 /* OK, we're ready to delete this one */
1678 obj.classId = RelationRelationId;
1679 obj.objectId = relOid;
1680 obj.objectSubId = 0;
1681
1682 add_exact_object_address(&obj, objects);
1683 }
1684
1685 performMultipleDeletions(objects, drop->behavior, flags);
1686
1687 free_object_addresses(objects);
1688}
1689
1690/*
1691 * Before acquiring a table lock, check whether we have sufficient rights.
1692 * In the case of DROP INDEX, also try to lock the table before the index.
1693 * Also, if the table to be dropped is a partition, we try to lock the parent
1694 * first.
1695 */
1696static void
1697RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1698 void *arg)
1699{
1700 HeapTuple tuple;
1702 char expected_relkind;
1703 bool is_partition;
1704 Form_pg_class classform;
1706 bool invalid_system_index = false;
1707
1708 state = (struct DropRelationCallbackState *) arg;
1709 heap_lockmode = state->heap_lockmode;
1710
1711 /*
1712 * If we previously locked some other index's heap, and the name we're
1713 * looking up no longer refers to that relation, release the now-useless
1714 * lock.
1715 */
1716 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1717 {
1719 state->heapOid = InvalidOid;
1720 }
1721
1722 /*
1723 * Similarly, if we previously locked some other partition's heap, and the
1724 * name we're looking up no longer refers to that relation, release the
1725 * now-useless lock.
1726 */
1727 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1728 {
1730 state->partParentOid = InvalidOid;
1731 }
1732
1733 /* Didn't find a relation, so no need for locking or permission checks. */
1734 if (!OidIsValid(relOid))
1735 return;
1736
1737 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1738 if (!HeapTupleIsValid(tuple))
1739 return; /* concurrently dropped, so nothing to do */
1740 classform = (Form_pg_class) GETSTRUCT(tuple);
1741 is_partition = classform->relispartition;
1742
1743 /* Pass back some data to save lookups in RemoveRelations */
1744 state->actual_relkind = classform->relkind;
1745 state->actual_relpersistence = classform->relpersistence;
1746
1747 /*
1748 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1749 * but RemoveRelations() can only pass one relkind for a given relation.
1750 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1751 * That means we must be careful before giving the wrong type error when
1752 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1753 * exists with indexes.
1754 */
1755 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1756 expected_relkind = RELKIND_RELATION;
1757 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1758 expected_relkind = RELKIND_INDEX;
1759 else
1760 expected_relkind = classform->relkind;
1761
1762 if (state->expected_relkind != expected_relkind)
1763 DropErrorMsgWrongType(rel->relname, classform->relkind,
1764 state->expected_relkind);
1765
1766 /* Allow DROP to either table owner or schema owner */
1767 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1768 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1770 get_relkind_objtype(classform->relkind),
1771 rel->relname);
1772
1773 /*
1774 * Check the case of a system index that might have been invalidated by a
1775 * failed concurrent process and allow its drop. For the time being, this
1776 * only concerns indexes of toast relations that became invalid during a
1777 * REINDEX CONCURRENTLY process.
1778 */
1779 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1780 {
1781 HeapTuple locTuple;
1782 Form_pg_index indexform;
1783 bool indisvalid;
1784
1785 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1786 if (!HeapTupleIsValid(locTuple))
1787 {
1788 ReleaseSysCache(tuple);
1789 return;
1790 }
1791
1792 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1793 indisvalid = indexform->indisvalid;
1794 ReleaseSysCache(locTuple);
1795
1796 /* Mark object as being an invalid index of system catalogs */
1797 if (!indisvalid)
1798 invalid_system_index = true;
1799 }
1800
1801 /* In the case of an invalid index, it is fine to bypass this check */
1802 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1803 ereport(ERROR,
1804 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1805 errmsg("permission denied: \"%s\" is a system catalog",
1806 rel->relname)));
1807
1808 ReleaseSysCache(tuple);
1809
1810 /*
1811 * In DROP INDEX, attempt to acquire lock on the parent table before
1812 * locking the index. index_drop() will need this anyway, and since
1813 * regular queries lock tables before their indexes, we risk deadlock if
1814 * we do it the other way around. No error if we don't find a pg_index
1815 * entry, though --- the relation may have been dropped. Note that this
1816 * code will execute for either plain or partitioned indexes.
1817 */
1818 if (expected_relkind == RELKIND_INDEX &&
1819 relOid != oldRelOid)
1820 {
1821 state->heapOid = IndexGetRelation(relOid, true);
1822 if (OidIsValid(state->heapOid))
1824 }
1825
1826 /*
1827 * Similarly, if the relation is a partition, we must acquire lock on its
1828 * parent before locking the partition. That's because queries lock the
1829 * parent before its partitions, so we risk deadlock if we do it the other
1830 * way around.
1831 */
1832 if (is_partition && relOid != oldRelOid)
1833 {
1834 state->partParentOid = get_partition_parent(relOid, true);
1835 if (OidIsValid(state->partParentOid))
1836 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1837 }
1838}
1839
1840/*
1841 * ExecuteTruncate
1842 * Executes a TRUNCATE command.
1843 *
1844 * This is a multi-relation truncate. We first open and grab exclusive
1845 * lock on all relations involved, checking permissions and otherwise
1846 * verifying that the relation is OK for truncation. Note that if relations
1847 * are foreign tables, at this stage, we have not yet checked that their
1848 * foreign data in external data sources are OK for truncation. These are
1849 * checked when foreign data are actually truncated later. In CASCADE mode,
1850 * relations having FK references to the targeted relations are automatically
1851 * added to the group; in RESTRICT mode, we check that all FK references are
1852 * internal to the group that's being truncated. Finally all the relations
1853 * are truncated and reindexed.
1854 */
1855void
1857{
1858 List *rels = NIL;
1859 List *relids = NIL;
1860 List *relids_logged = NIL;
1861 ListCell *cell;
1862
1863 /*
1864 * Open, exclusive-lock, and check all the explicitly-specified relations
1865 */
1866 foreach(cell, stmt->relations)
1867 {
1868 RangeVar *rv = lfirst(cell);
1869 Relation rel;
1870 bool recurse = rv->inh;
1871 Oid myrelid;
1872 LOCKMODE lockmode = AccessExclusiveLock;
1873
1874 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1876 NULL);
1877
1878 /* don't throw error for "TRUNCATE foo, foo" */
1879 if (list_member_oid(relids, myrelid))
1880 continue;
1881
1882 /* open the relation, we already hold a lock on it */
1883 rel = table_open(myrelid, NoLock);
1884
1885 /*
1886 * RangeVarGetRelidExtended() has done most checks with its callback,
1887 * but other checks with the now-opened Relation remain.
1888 */
1890
1891 rels = lappend(rels, rel);
1892 relids = lappend_oid(relids, myrelid);
1893
1894 /* Log this relation only if needed for logical decoding */
1896 relids_logged = lappend_oid(relids_logged, myrelid);
1897
1898 if (recurse)
1899 {
1900 ListCell *child;
1901 List *children;
1902
1903 children = find_all_inheritors(myrelid, lockmode, NULL);
1904
1905 foreach(child, children)
1906 {
1907 Oid childrelid = lfirst_oid(child);
1908
1909 if (list_member_oid(relids, childrelid))
1910 continue;
1911
1912 /* find_all_inheritors already got lock */
1913 rel = table_open(childrelid, NoLock);
1914
1915 /*
1916 * It is possible that the parent table has children that are
1917 * temp tables of other backends. We cannot safely access
1918 * such tables (because of buffering issues), and the best
1919 * thing to do is to silently ignore them. Note that this
1920 * check is the same as one of the checks done in
1921 * truncate_check_activity() called below, still it is kept
1922 * here for simplicity.
1923 */
1924 if (RELATION_IS_OTHER_TEMP(rel))
1925 {
1926 table_close(rel, lockmode);
1927 continue;
1928 }
1929
1930 /*
1931 * Inherited TRUNCATE commands perform access permission
1932 * checks on the parent table only. So we skip checking the
1933 * children's permissions and don't call
1934 * truncate_check_perms() here.
1935 */
1938
1939 rels = lappend(rels, rel);
1940 relids = lappend_oid(relids, childrelid);
1941
1942 /* Log this relation only if needed for logical decoding */
1944 relids_logged = lappend_oid(relids_logged, childrelid);
1945 }
1946 }
1947 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1948 ereport(ERROR,
1949 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1950 errmsg("cannot truncate only a partitioned table"),
1951 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1952 }
1953
1954 ExecuteTruncateGuts(rels, relids, relids_logged,
1955 stmt->behavior, stmt->restart_seqs, false);
1956
1957 /* And close the rels */
1958 foreach(cell, rels)
1959 {
1960 Relation rel = (Relation) lfirst(cell);
1961
1962 table_close(rel, NoLock);
1963 }
1964}
1965
1966/*
1967 * ExecuteTruncateGuts
1968 *
1969 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1970 * command (see above) as well as replication subscribers that execute a
1971 * replicated TRUNCATE action.
1972 *
1973 * explicit_rels is the list of Relations to truncate that the command
1974 * specified. relids is the list of Oids corresponding to explicit_rels.
1975 * relids_logged is the list of Oids (a subset of relids) that require
1976 * WAL-logging. This is all a bit redundant, but the existing callers have
1977 * this information handy in this form.
1978 */
1979void
1981 List *relids,
1982 List *relids_logged,
1983 DropBehavior behavior, bool restart_seqs,
1984 bool run_as_table_owner)
1985{
1986 List *rels;
1987 List *seq_relids = NIL;
1988 HTAB *ft_htab = NULL;
1989 EState *estate;
1990 ResultRelInfo *resultRelInfos;
1991 ResultRelInfo *resultRelInfo;
1992 SubTransactionId mySubid;
1993 ListCell *cell;
1994 Oid *logrelids;
1995
1996 /*
1997 * Check the explicitly-specified relations.
1998 *
1999 * In CASCADE mode, suck in all referencing relations as well. This
2000 * requires multiple iterations to find indirectly-dependent relations. At
2001 * each phase, we need to exclusive-lock new rels before looking for their
2002 * dependencies, else we might miss something. Also, we check each rel as
2003 * soon as we open it, to avoid a faux pas such as holding lock for a long
2004 * time on a rel we have no permissions for.
2005 */
2006 rels = list_copy(explicit_rels);
2007 if (behavior == DROP_CASCADE)
2008 {
2009 for (;;)
2010 {
2011 List *newrelids;
2012
2013 newrelids = heap_truncate_find_FKs(relids);
2014 if (newrelids == NIL)
2015 break; /* nothing else to add */
2016
2017 foreach(cell, newrelids)
2018 {
2019 Oid relid = lfirst_oid(cell);
2020 Relation rel;
2021
2022 rel = table_open(relid, AccessExclusiveLock);
2024 (errmsg("truncate cascades to table \"%s\"",
2026 truncate_check_rel(relid, rel->rd_rel);
2027 truncate_check_perms(relid, rel->rd_rel);
2029 rels = lappend(rels, rel);
2030 relids = lappend_oid(relids, relid);
2031
2032 /* Log this relation only if needed for logical decoding */
2034 relids_logged = lappend_oid(relids_logged, relid);
2035 }
2036 }
2037 }
2038
2039 /*
2040 * Check foreign key references. In CASCADE mode, this should be
2041 * unnecessary since we just pulled in all the references; but as a
2042 * cross-check, do it anyway if in an Assert-enabled build.
2043 */
2044#ifdef USE_ASSERT_CHECKING
2045 heap_truncate_check_FKs(rels, false);
2046#else
2047 if (behavior == DROP_RESTRICT)
2048 heap_truncate_check_FKs(rels, false);
2049#endif
2050
2051 /*
2052 * If we are asked to restart sequences, find all the sequences, lock them
2053 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2054 * We want to do this early since it's pointless to do all the truncation
2055 * work only to fail on sequence permissions.
2056 */
2057 if (restart_seqs)
2058 {
2059 foreach(cell, rels)
2060 {
2061 Relation rel = (Relation) lfirst(cell);
2062 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2063 ListCell *seqcell;
2064
2065 foreach(seqcell, seqlist)
2066 {
2067 Oid seq_relid = lfirst_oid(seqcell);
2068 Relation seq_rel;
2069
2070 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2071
2072 /* This check must match AlterSequence! */
2073 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2075 RelationGetRelationName(seq_rel));
2076
2077 seq_relids = lappend_oid(seq_relids, seq_relid);
2078
2079 relation_close(seq_rel, NoLock);
2080 }
2081 }
2082 }
2083
2084 /* Prepare to catch AFTER triggers. */
2086
2087 /*
2088 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2089 * each relation. We don't need to call ExecOpenIndices, though.
2090 *
2091 * We put the ResultRelInfos in the es_opened_result_relations list, even
2092 * though we don't have a range table and don't populate the
2093 * es_result_relations array. That's a bit bogus, but it's enough to make
2094 * ExecGetTriggerResultRel() find them.
2095 */
2096 estate = CreateExecutorState();
2097 resultRelInfos = (ResultRelInfo *)
2098 palloc(list_length(rels) * sizeof(ResultRelInfo));
2099 resultRelInfo = resultRelInfos;
2100 foreach(cell, rels)
2101 {
2102 Relation rel = (Relation) lfirst(cell);
2103
2104 InitResultRelInfo(resultRelInfo,
2105 rel,
2106 0, /* dummy rangetable index */
2107 NULL,
2108 0);
2110 lappend(estate->es_opened_result_relations, resultRelInfo);
2111 resultRelInfo++;
2112 }
2113
2114 /*
2115 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2116 * truncating (this is because one of them might throw an error). Also, if
2117 * we were to allow them to prevent statement execution, that would need
2118 * to be handled here.
2119 */
2120 resultRelInfo = resultRelInfos;
2121 foreach(cell, rels)
2122 {
2123 UserContext ucxt;
2124
2125 if (run_as_table_owner)
2126 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2127 &ucxt);
2128 ExecBSTruncateTriggers(estate, resultRelInfo);
2129 if (run_as_table_owner)
2130 RestoreUserContext(&ucxt);
2131 resultRelInfo++;
2132 }
2133
2134 /*
2135 * OK, truncate each table.
2136 */
2137 mySubid = GetCurrentSubTransactionId();
2138
2139 foreach(cell, rels)
2140 {
2141 Relation rel = (Relation) lfirst(cell);
2142
2143 /* Skip partitioned tables as there is nothing to do */
2144 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2145 continue;
2146
2147 /*
2148 * Build the lists of foreign tables belonging to each foreign server
2149 * and pass each list to the foreign data wrapper's callback function,
2150 * so that each server can truncate its all foreign tables in bulk.
2151 * Each list is saved as a single entry in a hash table that uses the
2152 * server OID as lookup key.
2153 */
2154 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2155 {
2157 bool found;
2158 ForeignTruncateInfo *ft_info;
2159
2160 /* First time through, initialize hashtable for foreign tables */
2161 if (!ft_htab)
2162 {
2163 HASHCTL hctl;
2164
2165 memset(&hctl, 0, sizeof(HASHCTL));
2166 hctl.keysize = sizeof(Oid);
2167 hctl.entrysize = sizeof(ForeignTruncateInfo);
2169
2170 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2171 32, /* start small and extend */
2172 &hctl,
2174 }
2175
2176 /* Find or create cached entry for the foreign table */
2177 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2178 if (!found)
2179 ft_info->rels = NIL;
2180
2181 /*
2182 * Save the foreign table in the entry of the server that the
2183 * foreign table belongs to.
2184 */
2185 ft_info->rels = lappend(ft_info->rels, rel);
2186 continue;
2187 }
2188
2189 /*
2190 * Normally, we need a transaction-safe truncation here. However, if
2191 * the table was either created in the current (sub)transaction or has
2192 * a new relfilenumber in the current (sub)transaction, then we can
2193 * just truncate it in-place, because a rollback would cause the whole
2194 * table or the current physical file to be thrown away anyway.
2195 */
2196 if (rel->rd_createSubid == mySubid ||
2197 rel->rd_newRelfilelocatorSubid == mySubid)
2198 {
2199 /* Immediate, non-rollbackable truncation is OK */
2201 }
2202 else
2203 {
2204 Oid heap_relid;
2205 Oid toast_relid;
2206 ReindexParams reindex_params = {0};
2207
2208 /*
2209 * This effectively deletes all rows in the table, and may be done
2210 * in a serializable transaction. In that case we must record a
2211 * rw-conflict in to this transaction from each transaction
2212 * holding a predicate lock on the table.
2213 */
2215
2216 /*
2217 * Need the full transaction-safe pushups.
2218 *
2219 * Create a new empty storage file for the relation, and assign it
2220 * as the relfilenumber value. The old storage file is scheduled
2221 * for deletion at commit.
2222 */
2223 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2224
2225 heap_relid = RelationGetRelid(rel);
2226
2227 /*
2228 * The same for the toast table, if any.
2229 */
2230 toast_relid = rel->rd_rel->reltoastrelid;
2231 if (OidIsValid(toast_relid))
2232 {
2233 Relation toastrel = relation_open(toast_relid,
2235
2237 toastrel->rd_rel->relpersistence);
2238 table_close(toastrel, NoLock);
2239 }
2240
2241 /*
2242 * Reconstruct the indexes to match, and we're done.
2243 */
2245 &reindex_params);
2246 }
2247
2249 }
2250
2251 /* Now go through the hash table, and truncate foreign tables */
2252 if (ft_htab)
2253 {
2254 ForeignTruncateInfo *ft_info;
2255 HASH_SEQ_STATUS seq;
2256
2257 hash_seq_init(&seq, ft_htab);
2258
2259 PG_TRY();
2260 {
2261 while ((ft_info = hash_seq_search(&seq)) != NULL)
2262 {
2263 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2264
2265 /* truncate_check_rel() has checked that already */
2266 Assert(routine->ExecForeignTruncate != NULL);
2267
2268 routine->ExecForeignTruncate(ft_info->rels,
2269 behavior,
2270 restart_seqs);
2271 }
2272 }
2273 PG_FINALLY();
2274 {
2275 hash_destroy(ft_htab);
2276 }
2277 PG_END_TRY();
2278 }
2279
2280 /*
2281 * Restart owned sequences if we were asked to.
2282 */
2283 foreach(cell, seq_relids)
2284 {
2285 Oid seq_relid = lfirst_oid(cell);
2286
2287 ResetSequence(seq_relid);
2288 }
2289
2290 /*
2291 * Write a WAL record to allow this set of actions to be logically
2292 * decoded.
2293 *
2294 * Assemble an array of relids so we can write a single WAL record for the
2295 * whole action.
2296 */
2297 if (relids_logged != NIL)
2298 {
2299 xl_heap_truncate xlrec;
2300 int i = 0;
2301
2302 /* should only get here if wal_level >= logical */
2304
2305 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2306 foreach(cell, relids_logged)
2307 logrelids[i++] = lfirst_oid(cell);
2308
2309 xlrec.dbId = MyDatabaseId;
2310 xlrec.nrelids = list_length(relids_logged);
2311 xlrec.flags = 0;
2312 if (behavior == DROP_CASCADE)
2313 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2314 if (restart_seqs)
2316
2319 XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2320
2322
2323 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2324 }
2325
2326 /*
2327 * Process all AFTER STATEMENT TRUNCATE triggers.
2328 */
2329 resultRelInfo = resultRelInfos;
2330 foreach(cell, rels)
2331 {
2332 UserContext ucxt;
2333
2334 if (run_as_table_owner)
2335 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2336 &ucxt);
2337 ExecASTruncateTriggers(estate, resultRelInfo);
2338 if (run_as_table_owner)
2339 RestoreUserContext(&ucxt);
2340 resultRelInfo++;
2341 }
2342
2343 /* Handle queued AFTER triggers */
2344 AfterTriggerEndQuery(estate);
2345
2346 /* We can clean up the EState now */
2347 FreeExecutorState(estate);
2348
2349 /*
2350 * Close any rels opened by CASCADE (can't do this while EState still
2351 * holds refs)
2352 */
2353 rels = list_difference_ptr(rels, explicit_rels);
2354 foreach(cell, rels)
2355 {
2356 Relation rel = (Relation) lfirst(cell);
2357
2358 table_close(rel, NoLock);
2359 }
2360}
2361
2362/*
2363 * Check that a given relation is safe to truncate. Subroutine for
2364 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2365 */
2366static void
2368{
2369 char *relname = NameStr(reltuple->relname);
2370
2371 /*
2372 * Only allow truncate on regular tables, foreign tables using foreign
2373 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2374 * latter are only being included here for the following checks; no
2375 * physical truncation will occur in their case.).
2376 */
2377 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2378 {
2379 Oid serverid = GetForeignServerIdByRelId(relid);
2380 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2381
2382 if (!fdwroutine->ExecForeignTruncate)
2383 ereport(ERROR,
2384 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2385 errmsg("cannot truncate foreign table \"%s\"",
2386 relname)));
2387 }
2388 else if (reltuple->relkind != RELKIND_RELATION &&
2389 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2390 ereport(ERROR,
2391 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2392 errmsg("\"%s\" is not a table", relname)));
2393
2394 /*
2395 * Most system catalogs can't be truncated at all, or at least not unless
2396 * allow_system_table_mods=on. As an exception, however, we allow
2397 * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2398 * pg_upgrade, because we need to change its relfilenode to match the old
2399 * cluster, and allowing a TRUNCATE command to be executed is the easiest
2400 * way of doing that.
2401 */
2402 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2403 && (!IsBinaryUpgrade ||
2404 (relid != LargeObjectRelationId &&
2405 relid != LargeObjectMetadataRelationId)))
2406 ereport(ERROR,
2407 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2408 errmsg("permission denied: \"%s\" is a system catalog",
2409 relname)));
2410
2412}
2413
2414/*
2415 * Check that current user has the permission to truncate given relation.
2416 */
2417static void
2419{
2420 char *relname = NameStr(reltuple->relname);
2421 AclResult aclresult;
2422
2423 /* Permissions checks */
2424 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2425 if (aclresult != ACLCHECK_OK)
2426 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2427 relname);
2428}
2429
2430/*
2431 * Set of extra sanity checks to check if a given relation is safe to
2432 * truncate. This is split with truncate_check_rel() as
2433 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2434 */
2435static void
2437{
2438 /*
2439 * Don't allow truncate on temp tables of other backends ... their local
2440 * buffer manager is not going to cope.
2441 */
2442 if (RELATION_IS_OTHER_TEMP(rel))
2443 ereport(ERROR,
2444 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2445 errmsg("cannot truncate temporary tables of other sessions")));
2446
2447 /*
2448 * Also check for active uses of the relation in the current transaction,
2449 * including open scans and pending AFTER trigger events.
2450 */
2451 CheckTableNotInUse(rel, "TRUNCATE");
2452}
2453
2454/*
2455 * storage_name
2456 * returns the name corresponding to a typstorage/attstorage enum value
2457 */
2458static const char *
2460{
2461 switch (c)
2462 {
2463 case TYPSTORAGE_PLAIN:
2464 return "PLAIN";
2465 case TYPSTORAGE_EXTERNAL:
2466 return "EXTERNAL";
2467 case TYPSTORAGE_EXTENDED:
2468 return "EXTENDED";
2469 case TYPSTORAGE_MAIN:
2470 return "MAIN";
2471 default:
2472 return "???";
2473 }
2474}
2475
2476/*----------
2477 * MergeAttributes
2478 * Returns new schema given initial schema and superclasses.
2479 *
2480 * Input arguments:
2481 * 'columns' is the column/attribute definition for the table. (It's a list
2482 * of ColumnDef's.) It is destructively changed.
2483 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2484 * 'relpersistence' is the persistence type of the table.
2485 * 'is_partition' tells if the table is a partition.
2486 *
2487 * Output arguments:
2488 * 'supconstr' receives a list of CookedConstraint representing
2489 * CHECK constraints belonging to parent relations, updated as
2490 * necessary to be valid for the child.
2491 * 'supnotnulls' receives a list of CookedConstraint representing
2492 * not-null constraints based on those from parent relations.
2493 *
2494 * Return value:
2495 * Completed schema list.
2496 *
2497 * Notes:
2498 * The order in which the attributes are inherited is very important.
2499 * Intuitively, the inherited attributes should come first. If a table
2500 * inherits from multiple parents, the order of those attributes are
2501 * according to the order of the parents specified in CREATE TABLE.
2502 *
2503 * Here's an example:
2504 *
2505 * create table person (name text, age int4, location point);
2506 * create table emp (salary int4, manager text) inherits(person);
2507 * create table student (gpa float8) inherits (person);
2508 * create table stud_emp (percent int4) inherits (emp, student);
2509 *
2510 * The order of the attributes of stud_emp is:
2511 *
2512 * person {1:name, 2:age, 3:location}
2513 * / \
2514 * {6:gpa} student emp {4:salary, 5:manager}
2515 * \ /
2516 * stud_emp {7:percent}
2517 *
2518 * If the same attribute name appears multiple times, then it appears
2519 * in the result table in the proper location for its first appearance.
2520 *
2521 * Constraints (including not-null constraints) for the child table
2522 * are the union of all relevant constraints, from both the child schema
2523 * and parent tables. In addition, in legacy inheritance, each column that
2524 * appears in a primary key in any of the parents also gets a NOT NULL
2525 * constraint (partitioning doesn't need this, because the PK itself gets
2526 * inherited.)
2527 *
2528 * The default value for a child column is defined as:
2529 * (1) If the child schema specifies a default, that value is used.
2530 * (2) If neither the child nor any parent specifies a default, then
2531 * the column will not have a default.
2532 * (3) If conflicting defaults are inherited from different parents
2533 * (and not overridden by the child), an error is raised.
2534 * (4) Otherwise the inherited default is used.
2535 *
2536 * Note that the default-value infrastructure is used for generated
2537 * columns' expressions too, so most of the preceding paragraph applies
2538 * to generation expressions too. We insist that a child column be
2539 * generated if and only if its parent(s) are, but it need not have
2540 * the same generation expression.
2541 *----------
2542 */
2543static List *
2544MergeAttributes(List *columns, const List *supers, char relpersistence,
2545 bool is_partition, List **supconstr, List **supnotnulls)
2546{
2547 List *inh_columns = NIL;
2548 List *constraints = NIL;
2549 List *nnconstraints = NIL;
2550 bool have_bogus_defaults = false;
2551 int child_attno;
2552 static Node bogus_marker = {0}; /* marks conflicting defaults */
2553 List *saved_columns = NIL;
2554 ListCell *lc;
2555
2556 /*
2557 * Check for and reject tables with too many columns. We perform this
2558 * check relatively early for two reasons: (a) we don't run the risk of
2559 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2560 * okay if we're processing <= 1600 columns, but could take minutes to
2561 * execute if the user attempts to create a table with hundreds of
2562 * thousands of columns.
2563 *
2564 * Note that we also need to check that we do not exceed this figure after
2565 * including columns from inherited relations.
2566 */
2567 if (list_length(columns) > MaxHeapAttributeNumber)
2568 ereport(ERROR,
2569 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2570 errmsg("tables can have at most %d columns",
2572
2573 /*
2574 * Check for duplicate names in the explicit list of attributes.
2575 *
2576 * Although we might consider merging such entries in the same way that we
2577 * handle name conflicts for inherited attributes, it seems to make more
2578 * sense to assume such conflicts are errors.
2579 *
2580 * We don't use foreach() here because we have two nested loops over the
2581 * columns list, with possible element deletions in the inner one. If we
2582 * used foreach_delete_current() it could only fix up the state of one of
2583 * the loops, so it seems cleaner to use looping over list indexes for
2584 * both loops. Note that any deletion will happen beyond where the outer
2585 * loop is, so its index never needs adjustment.
2586 */
2587 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2588 {
2589 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2590
2591 if (!is_partition && coldef->typeName == NULL)
2592 {
2593 /*
2594 * Typed table column option that does not belong to a column from
2595 * the type. This works because the columns from the type come
2596 * first in the list. (We omit this check for partition column
2597 * lists; those are processed separately below.)
2598 */
2599 ereport(ERROR,
2600 (errcode(ERRCODE_UNDEFINED_COLUMN),
2601 errmsg("column \"%s\" does not exist",
2602 coldef->colname)));
2603 }
2604
2605 /* restpos scans all entries beyond coldef; incr is in loop body */
2606 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2607 {
2608 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2609
2610 if (strcmp(coldef->colname, restdef->colname) == 0)
2611 {
2612 if (coldef->is_from_type)
2613 {
2614 /*
2615 * merge the column options into the column from the type
2616 */
2617 coldef->is_not_null = restdef->is_not_null;
2618 coldef->raw_default = restdef->raw_default;
2619 coldef->cooked_default = restdef->cooked_default;
2620 coldef->constraints = restdef->constraints;
2621 coldef->is_from_type = false;
2622 columns = list_delete_nth_cell(columns, restpos);
2623 }
2624 else
2625 ereport(ERROR,
2626 (errcode(ERRCODE_DUPLICATE_COLUMN),
2627 errmsg("column \"%s\" specified more than once",
2628 coldef->colname)));
2629 }
2630 else
2631 restpos++;
2632 }
2633 }
2634
2635 /*
2636 * In case of a partition, there are no new column definitions, only dummy
2637 * ColumnDefs created for column constraints. Set them aside for now and
2638 * process them at the end.
2639 */
2640 if (is_partition)
2641 {
2642 saved_columns = columns;
2643 columns = NIL;
2644 }
2645
2646 /*
2647 * Scan the parents left-to-right, and merge their attributes to form a
2648 * list of inherited columns (inh_columns).
2649 */
2650 child_attno = 0;
2651 foreach(lc, supers)
2652 {
2653 Oid parent = lfirst_oid(lc);
2654 Relation relation;
2655 TupleDesc tupleDesc;
2656 TupleConstr *constr;
2657 AttrMap *newattmap;
2658 List *inherited_defaults;
2659 List *cols_with_defaults;
2660 List *nnconstrs;
2661 ListCell *lc1;
2662 ListCell *lc2;
2663 Bitmapset *nncols = NULL;
2664
2665 /* caller already got lock */
2666 relation = table_open(parent, NoLock);
2667
2668 /*
2669 * Check for active uses of the parent partitioned table in the
2670 * current transaction, such as being used in some manner by an
2671 * enclosing command.
2672 */
2673 if (is_partition)
2674 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2675
2676 /*
2677 * We do not allow partitioned tables and partitions to participate in
2678 * regular inheritance.
2679 */
2680 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2681 ereport(ERROR,
2682 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2683 errmsg("cannot inherit from partitioned table \"%s\"",
2684 RelationGetRelationName(relation))));
2685 if (relation->rd_rel->relispartition && !is_partition)
2686 ereport(ERROR,
2687 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2688 errmsg("cannot inherit from partition \"%s\"",
2689 RelationGetRelationName(relation))));
2690
2691 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2692 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2693 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2694 ereport(ERROR,
2695 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2696 errmsg("inherited relation \"%s\" is not a table or foreign table",
2697 RelationGetRelationName(relation))));
2698
2699 /*
2700 * If the parent is permanent, so must be all of its partitions. Note
2701 * that inheritance allows that case.
2702 */
2703 if (is_partition &&
2704 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2705 relpersistence == RELPERSISTENCE_TEMP)
2706 ereport(ERROR,
2707 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2708 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2709 RelationGetRelationName(relation))));
2710
2711 /* Permanent rels cannot inherit from temporary ones */
2712 if (relpersistence != RELPERSISTENCE_TEMP &&
2713 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2714 ereport(ERROR,
2715 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2716 errmsg(!is_partition
2717 ? "cannot inherit from temporary relation \"%s\""
2718 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2719 RelationGetRelationName(relation))));
2720
2721 /* If existing rel is temp, it must belong to this session */
2722 if (RELATION_IS_OTHER_TEMP(relation))
2723 ereport(ERROR,
2724 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2725 errmsg(!is_partition
2726 ? "cannot inherit from temporary relation of another session"
2727 : "cannot create as partition of temporary relation of another session")));
2728
2729 /*
2730 * We should have an UNDER permission flag for this, but for now,
2731 * demand that creator of a child table own the parent.
2732 */
2733 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2735 RelationGetRelationName(relation));
2736
2737 tupleDesc = RelationGetDescr(relation);
2738 constr = tupleDesc->constr;
2739
2740 /*
2741 * newattmap->attnums[] will contain the child-table attribute numbers
2742 * for the attributes of this parent table. (They are not the same
2743 * for parents after the first one, nor if we have dropped columns.)
2744 */
2745 newattmap = make_attrmap(tupleDesc->natts);
2746
2747 /* We can't process inherited defaults until newattmap is complete. */
2748 inherited_defaults = cols_with_defaults = NIL;
2749
2750 /*
2751 * Request attnotnull on columns that have a not-null constraint
2752 * that's not marked NO INHERIT (even if not valid).
2753 */
2754 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2755 true, false);
2756 foreach_ptr(CookedConstraint, cc, nnconstrs)
2757 nncols = bms_add_member(nncols, cc->attnum);
2758
2759 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2760 parent_attno++)
2761 {
2762 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2763 parent_attno - 1);
2764 char *attributeName = NameStr(attribute->attname);
2765 int exist_attno;
2766 ColumnDef *newdef;
2767 ColumnDef *mergeddef;
2768
2769 /*
2770 * Ignore dropped columns in the parent.
2771 */
2772 if (attribute->attisdropped)
2773 continue; /* leave newattmap->attnums entry as zero */
2774
2775 /*
2776 * Create new column definition
2777 */
2778 newdef = makeColumnDef(attributeName, attribute->atttypid,
2779 attribute->atttypmod, attribute->attcollation);
2780 newdef->storage = attribute->attstorage;
2781 newdef->generated = attribute->attgenerated;
2782 if (CompressionMethodIsValid(attribute->attcompression))
2783 newdef->compression =
2784 pstrdup(GetCompressionMethodName(attribute->attcompression));
2785
2786 /*
2787 * Regular inheritance children are independent enough not to
2788 * inherit identity columns. But partitions are integral part of
2789 * a partitioned table and inherit identity column.
2790 */
2791 if (is_partition)
2792 newdef->identity = attribute->attidentity;
2793
2794 /*
2795 * Does it match some previously considered column from another
2796 * parent?
2797 */
2798 exist_attno = findAttrByName(attributeName, inh_columns);
2799 if (exist_attno > 0)
2800 {
2801 /*
2802 * Yes, try to merge the two column definitions.
2803 */
2804 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2805
2806 newattmap->attnums[parent_attno - 1] = exist_attno;
2807
2808 /*
2809 * Partitions have only one parent, so conflict should never
2810 * occur.
2811 */
2812 Assert(!is_partition);
2813 }
2814 else
2815 {
2816 /*
2817 * No, create a new inherited column
2818 */
2819 newdef->inhcount = 1;
2820 newdef->is_local = false;
2821 inh_columns = lappend(inh_columns, newdef);
2822
2823 newattmap->attnums[parent_attno - 1] = ++child_attno;
2824 mergeddef = newdef;
2825 }
2826
2827 /*
2828 * mark attnotnull if parent has it
2829 */
2830 if (bms_is_member(parent_attno, nncols))
2831 mergeddef->is_not_null = true;
2832
2833 /*
2834 * Locate default/generation expression if any
2835 */
2836 if (attribute->atthasdef)
2837 {
2838 Node *this_default;
2839
2840 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2841 if (this_default == NULL)
2842 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2843 parent_attno, RelationGetRelationName(relation));
2844
2845 /*
2846 * If it's a GENERATED default, it might contain Vars that
2847 * need to be mapped to the inherited column(s)' new numbers.
2848 * We can't do that till newattmap is ready, so just remember
2849 * all the inherited default expressions for the moment.
2850 */
2851 inherited_defaults = lappend(inherited_defaults, this_default);
2852 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2853 }
2854 }
2855
2856 /*
2857 * Now process any inherited default expressions, adjusting attnos
2858 * using the completed newattmap map.
2859 */
2860 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2861 {
2862 Node *this_default = (Node *) lfirst(lc1);
2863 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2864 bool found_whole_row;
2865
2866 /* Adjust Vars to match new table's column numbering */
2867 this_default = map_variable_attnos(this_default,
2868 1, 0,
2869 newattmap,
2870 InvalidOid, &found_whole_row);
2871
2872 /*
2873 * For the moment we have to reject whole-row variables. We could
2874 * convert them, if we knew the new table's rowtype OID, but that
2875 * hasn't been assigned yet. (A variable could only appear in a
2876 * generation expression, so the error message is correct.)
2877 */
2878 if (found_whole_row)
2879 ereport(ERROR,
2880 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2881 errmsg("cannot convert whole-row table reference"),
2882 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2883 def->colname,
2884 RelationGetRelationName(relation))));
2885
2886 /*
2887 * If we already had a default from some prior parent, check to
2888 * see if they are the same. If so, no problem; if not, mark the
2889 * column as having a bogus default. Below, we will complain if
2890 * the bogus default isn't overridden by the child columns.
2891 */
2892 Assert(def->raw_default == NULL);
2893 if (def->cooked_default == NULL)
2894 def->cooked_default = this_default;
2895 else if (!equal(def->cooked_default, this_default))
2896 {
2897 def->cooked_default = &bogus_marker;
2898 have_bogus_defaults = true;
2899 }
2900 }
2901
2902 /*
2903 * Now copy the CHECK constraints of this parent, adjusting attnos
2904 * using the completed newattmap map. Identically named constraints
2905 * are merged if possible, else we throw error.
2906 */
2907 if (constr && constr->num_check > 0)
2908 {
2909 ConstrCheck *check = constr->check;
2910
2911 for (int i = 0; i < constr->num_check; i++)
2912 {
2913 char *name = check[i].ccname;
2914 Node *expr;
2915 bool found_whole_row;
2916
2917 /* ignore if the constraint is non-inheritable */
2918 if (check[i].ccnoinherit)
2919 continue;
2920
2921 /* Adjust Vars to match new table's column numbering */
2922 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2923 1, 0,
2924 newattmap,
2925 InvalidOid, &found_whole_row);
2926
2927 /*
2928 * For the moment we have to reject whole-row variables. We
2929 * could convert them, if we knew the new table's rowtype OID,
2930 * but that hasn't been assigned yet.
2931 */
2932 if (found_whole_row)
2933 ereport(ERROR,
2934 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2935 errmsg("cannot convert whole-row table reference"),
2936 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2937 name,
2938 RelationGetRelationName(relation))));
2939
2940 constraints = MergeCheckConstraint(constraints, name, expr,
2941 check[i].ccenforced);
2942 }
2943 }
2944
2945 /*
2946 * Also copy the not-null constraints from this parent. The
2947 * attnotnull markings were already installed above.
2948 */
2949 foreach_ptr(CookedConstraint, nn, nnconstrs)
2950 {
2951 Assert(nn->contype == CONSTR_NOTNULL);
2952
2953 nn->attnum = newattmap->attnums[nn->attnum - 1];
2954
2955 nnconstraints = lappend(nnconstraints, nn);
2956 }
2957
2958 free_attrmap(newattmap);
2959
2960 /*
2961 * Close the parent rel, but keep our lock on it until xact commit.
2962 * That will prevent someone else from deleting or ALTERing the parent
2963 * before the child is committed.
2964 */
2965 table_close(relation, NoLock);
2966 }
2967
2968 /*
2969 * If we had no inherited attributes, the result columns are just the
2970 * explicitly declared columns. Otherwise, we need to merge the declared
2971 * columns into the inherited column list. Although, we never have any
2972 * explicitly declared columns if the table is a partition.
2973 */
2974 if (inh_columns != NIL)
2975 {
2976 int newcol_attno = 0;
2977
2978 foreach(lc, columns)
2979 {
2980 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2981 char *attributeName = newdef->colname;
2982 int exist_attno;
2983
2984 /*
2985 * Partitions have only one parent and have no column definitions
2986 * of their own, so conflict should never occur.
2987 */
2988 Assert(!is_partition);
2989
2990 newcol_attno++;
2991
2992 /*
2993 * Does it match some inherited column?
2994 */
2995 exist_attno = findAttrByName(attributeName, inh_columns);
2996 if (exist_attno > 0)
2997 {
2998 /*
2999 * Yes, try to merge the two column definitions.
3000 */
3001 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
3002 }
3003 else
3004 {
3005 /*
3006 * No, attach new column unchanged to result columns.
3007 */
3008 inh_columns = lappend(inh_columns, newdef);
3009 }
3010 }
3011
3012 columns = inh_columns;
3013
3014 /*
3015 * Check that we haven't exceeded the legal # of columns after merging
3016 * in inherited columns.
3017 */
3018 if (list_length(columns) > MaxHeapAttributeNumber)
3019 ereport(ERROR,
3020 (errcode(ERRCODE_TOO_MANY_COLUMNS),
3021 errmsg("tables can have at most %d columns",
3023 }
3024
3025 /*
3026 * Now that we have the column definition list for a partition, we can
3027 * check whether the columns referenced in the column constraint specs
3028 * actually exist. Also, merge column defaults.
3029 */
3030 if (is_partition)
3031 {
3032 foreach(lc, saved_columns)
3033 {
3034 ColumnDef *restdef = lfirst(lc);
3035 bool found = false;
3036 ListCell *l;
3037
3038 foreach(l, columns)
3039 {
3040 ColumnDef *coldef = lfirst(l);
3041
3042 if (strcmp(coldef->colname, restdef->colname) == 0)
3043 {
3044 found = true;
3045
3046 /*
3047 * Check for conflicts related to generated columns.
3048 *
3049 * Same rules as above: generated-ness has to match the
3050 * parent, but the contents of the generation expression
3051 * can be different.
3052 */
3053 if (coldef->generated)
3054 {
3055 if (restdef->raw_default && !restdef->generated)
3056 ereport(ERROR,
3057 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3058 errmsg("column \"%s\" inherits from generated column but specifies default",
3059 restdef->colname)));
3060 if (restdef->identity)
3061 ereport(ERROR,
3062 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3063 errmsg("column \"%s\" inherits from generated column but specifies identity",
3064 restdef->colname)));
3065 }
3066 else
3067 {
3068 if (restdef->generated)
3069 ereport(ERROR,
3070 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3071 errmsg("child column \"%s\" specifies generation expression",
3072 restdef->colname),
3073 errhint("A child table column cannot be generated unless its parent column is.")));
3074 }
3075
3076 if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3077 ereport(ERROR,
3078 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3079 errmsg("column \"%s\" inherits from generated column of different kind",
3080 restdef->colname),
3081 errdetail("Parent column is %s, child column is %s.",
3082 coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3083 restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3084
3085 /*
3086 * Override the parent's default value for this column
3087 * (coldef->cooked_default) with the partition's local
3088 * definition (restdef->raw_default), if there's one. It
3089 * should be physically impossible to get a cooked default
3090 * in the local definition or a raw default in the
3091 * inherited definition, but make sure they're nulls, for
3092 * future-proofing.
3093 */
3094 Assert(restdef->cooked_default == NULL);
3095 Assert(coldef->raw_default == NULL);
3096 if (restdef->raw_default)
3097 {
3098 coldef->raw_default = restdef->raw_default;
3099 coldef->cooked_default = NULL;
3100 }
3101 }
3102 }
3103
3104 /* complain for constraints on columns not in parent */
3105 if (!found)
3106 ereport(ERROR,
3107 (errcode(ERRCODE_UNDEFINED_COLUMN),
3108 errmsg("column \"%s\" does not exist",
3109 restdef->colname)));
3110 }
3111 }
3112
3113 /*
3114 * If we found any conflicting parent default values, check to make sure
3115 * they were overridden by the child.
3116 */
3117 if (have_bogus_defaults)
3118 {
3119 foreach(lc, columns)
3120 {
3121 ColumnDef *def = lfirst(lc);
3122
3123 if (def->cooked_default == &bogus_marker)
3124 {
3125 if (def->generated)
3126 ereport(ERROR,
3127 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3128 errmsg("column \"%s\" inherits conflicting generation expressions",
3129 def->colname),
3130 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3131 else
3132 ereport(ERROR,
3133 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3134 errmsg("column \"%s\" inherits conflicting default values",
3135 def->colname),
3136 errhint("To resolve the conflict, specify a default explicitly.")));
3137 }
3138 }
3139 }
3140
3141 *supconstr = constraints;
3142 *supnotnulls = nnconstraints;
3143
3144 return columns;
3145}
3146
3147
3148/*
3149 * MergeCheckConstraint
3150 * Try to merge an inherited CHECK constraint with previous ones
3151 *
3152 * If we inherit identically-named constraints from multiple parents, we must
3153 * merge them, or throw an error if they don't have identical definitions.
3154 *
3155 * constraints is a list of CookedConstraint structs for previous constraints.
3156 *
3157 * If the new constraint matches an existing one, then the existing
3158 * constraint's inheritance count is updated. If there is a conflict (same
3159 * name but different expression), throw an error. If the constraint neither
3160 * matches nor conflicts with an existing one, a new constraint is appended to
3161 * the list.
3162 */
3163static List *
3164MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3165{
3166 ListCell *lc;
3167 CookedConstraint *newcon;
3168
3169 foreach(lc, constraints)
3170 {
3172
3173 Assert(ccon->contype == CONSTR_CHECK);
3174
3175 /* Non-matching names never conflict */
3176 if (strcmp(ccon->name, name) != 0)
3177 continue;
3178
3179 if (equal(expr, ccon->expr))
3180 {
3181 /* OK to merge constraint with existing */
3182 if (pg_add_s16_overflow(ccon->inhcount, 1,
3183 &ccon->inhcount))
3184 ereport(ERROR,
3185 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3186 errmsg("too many inheritance parents"));
3187
3188 /*
3189 * When enforceability differs, the merged constraint should be
3190 * marked as ENFORCED because one of the parents is ENFORCED.
3191 */
3192 if (!ccon->is_enforced && is_enforced)
3193 {
3194 ccon->is_enforced = true;
3195 ccon->skip_validation = false;
3196 }
3197
3198 return constraints;
3199 }
3200
3201 ereport(ERROR,
3203 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3204 name)));
3205 }
3206
3207 /*
3208 * Constraint couldn't be merged with an existing one and also didn't
3209 * conflict with an existing one, so add it as a new one to the list.
3210 */
3212 newcon->contype = CONSTR_CHECK;
3213 newcon->name = pstrdup(name);
3214 newcon->expr = expr;
3215 newcon->inhcount = 1;
3216 newcon->is_enforced = is_enforced;
3217 newcon->skip_validation = !is_enforced;
3218 return lappend(constraints, newcon);
3219}
3220
3221/*
3222 * MergeChildAttribute
3223 * Merge given child attribute definition into given inherited attribute.
3224 *
3225 * Input arguments:
3226 * 'inh_columns' is the list of inherited ColumnDefs.
3227 * 'exist_attno' is the number of the inherited attribute in inh_columns
3228 * 'newcol_attno' is the attribute number in child table's schema definition
3229 * 'newdef' is the column/attribute definition from the child table.
3230 *
3231 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3232 * ColumnDef remains unchanged.
3233 *
3234 * Notes:
3235 * - The attribute is merged according to the rules laid out in the prologue
3236 * of MergeAttributes().
3237 * - If matching inherited attribute exists but the child attribute can not be
3238 * merged into it, the function throws respective errors.
3239 * - A partition can not have its own column definitions. Hence this function
3240 * is applicable only to a regular inheritance child.
3241 */
3242static void
3243MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3244{
3245 char *attributeName = newdef->colname;
3246 ColumnDef *inhdef;
3247 Oid inhtypeid,
3248 newtypeid;
3249 int32 inhtypmod,
3250 newtypmod;
3251 Oid inhcollid,
3252 newcollid;
3253
3254 if (exist_attno == newcol_attno)
3256 (errmsg("merging column \"%s\" with inherited definition",
3257 attributeName)));
3258 else
3260 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3261 errdetail("User-specified column moved to the position of the inherited column.")));
3262
3263 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3264
3265 /*
3266 * Must have the same type and typmod
3267 */
3268 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3269 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3270 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3271 ereport(ERROR,
3272 (errcode(ERRCODE_DATATYPE_MISMATCH),
3273 errmsg("column \"%s\" has a type conflict",
3274 attributeName),
3275 errdetail("%s versus %s",
3276 format_type_with_typemod(inhtypeid, inhtypmod),
3277 format_type_with_typemod(newtypeid, newtypmod))));
3278
3279 /*
3280 * Must have the same collation
3281 */
3282 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3283 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3284 if (inhcollid != newcollid)
3285 ereport(ERROR,
3286 (errcode(ERRCODE_COLLATION_MISMATCH),
3287 errmsg("column \"%s\" has a collation conflict",
3288 attributeName),
3289 errdetail("\"%s\" versus \"%s\"",
3290 get_collation_name(inhcollid),
3291 get_collation_name(newcollid))));
3292
3293 /*
3294 * Identity is never inherited by a regular inheritance child. Pick
3295 * child's identity definition if there's one.
3296 */
3297 inhdef->identity = newdef->identity;
3298
3299 /*
3300 * Copy storage parameter
3301 */
3302 if (inhdef->storage == 0)
3303 inhdef->storage = newdef->storage;
3304 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3305 ereport(ERROR,
3306 (errcode(ERRCODE_DATATYPE_MISMATCH),
3307 errmsg("column \"%s\" has a storage parameter conflict",
3308 attributeName),
3309 errdetail("%s versus %s",
3310 storage_name(inhdef->storage),
3311 storage_name(newdef->storage))));
3312
3313 /*
3314 * Copy compression parameter
3315 */
3316 if (inhdef->compression == NULL)
3317 inhdef->compression = newdef->compression;
3318 else if (newdef->compression != NULL)
3319 {
3320 if (strcmp(inhdef->compression, newdef->compression) != 0)
3321 ereport(ERROR,
3322 (errcode(ERRCODE_DATATYPE_MISMATCH),
3323 errmsg("column \"%s\" has a compression method conflict",
3324 attributeName),
3325 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3326 }
3327
3328 /*
3329 * Merge of not-null constraints = OR 'em together
3330 */
3331 inhdef->is_not_null |= newdef->is_not_null;
3332
3333 /*
3334 * Check for conflicts related to generated columns.
3335 *
3336 * If the parent column is generated, the child column will be made a
3337 * generated column if it isn't already. If it is a generated column,
3338 * we'll take its generation expression in preference to the parent's. We
3339 * must check that the child column doesn't specify a default value or
3340 * identity, which matches the rules for a single column in
3341 * parse_utilcmd.c.
3342 *
3343 * Conversely, if the parent column is not generated, the child column
3344 * can't be either. (We used to allow that, but it results in being able
3345 * to override the generation expression via UPDATEs through the parent.)
3346 */
3347 if (inhdef->generated)
3348 {
3349 if (newdef->raw_default && !newdef->generated)
3350 ereport(ERROR,
3351 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3352 errmsg("column \"%s\" inherits from generated column but specifies default",
3353 inhdef->colname)));
3354 if (newdef->identity)
3355 ereport(ERROR,
3356 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3357 errmsg("column \"%s\" inherits from generated column but specifies identity",
3358 inhdef->colname)));
3359 }
3360 else
3361 {
3362 if (newdef->generated)
3363 ereport(ERROR,
3364 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3365 errmsg("child column \"%s\" specifies generation expression",
3366 inhdef->colname),
3367 errhint("A child table column cannot be generated unless its parent column is.")));
3368 }
3369
3370 if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3371 ereport(ERROR,
3372 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3373 errmsg("column \"%s\" inherits from generated column of different kind",
3374 inhdef->colname),
3375 errdetail("Parent column is %s, child column is %s.",
3376 inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3377 newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3378
3379 /*
3380 * If new def has a default, override previous default
3381 */
3382 if (newdef->raw_default != NULL)
3383 {
3384 inhdef->raw_default = newdef->raw_default;
3385 inhdef->cooked_default = newdef->cooked_default;
3386 }
3387
3388 /* Mark the column as locally defined */
3389 inhdef->is_local = true;
3390}
3391
3392/*
3393 * MergeInheritedAttribute
3394 * Merge given parent attribute definition into specified attribute
3395 * inherited from the previous parents.
3396 *
3397 * Input arguments:
3398 * 'inh_columns' is the list of previously inherited ColumnDefs.
3399 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3400 * 'newdef' is the new parent column/attribute definition to be merged.
3401 *
3402 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3403 *
3404 * Notes:
3405 * - The attribute is merged according to the rules laid out in the prologue
3406 * of MergeAttributes().
3407 * - If matching inherited attribute exists but the new attribute can not be
3408 * merged into it, the function throws respective errors.
3409 * - A partition inherits from only a single parent. Hence this function is
3410 * applicable only to a regular inheritance.
3411 */
3412static ColumnDef *
3414 int exist_attno,
3415 const ColumnDef *newdef)
3416{
3417 char *attributeName = newdef->colname;
3418 ColumnDef *prevdef;
3419 Oid prevtypeid,
3420 newtypeid;
3421 int32 prevtypmod,
3422 newtypmod;
3423 Oid prevcollid,
3424 newcollid;
3425
3427 (errmsg("merging multiple inherited definitions of column \"%s\"",
3428 attributeName)));
3429 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3430
3431 /*
3432 * Must have the same type and typmod
3433 */
3434 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3435 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3436 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3437 ereport(ERROR,
3438 (errcode(ERRCODE_DATATYPE_MISMATCH),
3439 errmsg("inherited column \"%s\" has a type conflict",
3440 attributeName),
3441 errdetail("%s versus %s",
3442 format_type_with_typemod(prevtypeid, prevtypmod),
3443 format_type_with_typemod(newtypeid, newtypmod))));
3444
3445 /*
3446 * Must have the same collation
3447 */
3448 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3449 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3450 if (prevcollid != newcollid)
3451 ereport(ERROR,
3452 (errcode(ERRCODE_COLLATION_MISMATCH),
3453 errmsg("inherited column \"%s\" has a collation conflict",
3454 attributeName),
3455 errdetail("\"%s\" versus \"%s\"",
3456 get_collation_name(prevcollid),
3457 get_collation_name(newcollid))));
3458
3459 /*
3460 * Copy/check storage parameter
3461 */
3462 if (prevdef->storage == 0)
3463 prevdef->storage = newdef->storage;
3464 else if (prevdef->storage != newdef->storage)
3465 ereport(ERROR,
3466 (errcode(ERRCODE_DATATYPE_MISMATCH),
3467 errmsg("inherited column \"%s\" has a storage parameter conflict",
3468 attributeName),
3469 errdetail("%s versus %s",
3470 storage_name(prevdef->storage),
3471 storage_name(newdef->storage))));
3472
3473 /*
3474 * Copy/check compression parameter
3475 */
3476 if (prevdef->compression == NULL)
3477 prevdef->compression = newdef->compression;
3478 else if (newdef->compression != NULL)
3479 {
3480 if (strcmp(prevdef->compression, newdef->compression) != 0)
3481 ereport(ERROR,
3482 (errcode(ERRCODE_DATATYPE_MISMATCH),
3483 errmsg("column \"%s\" has a compression method conflict",
3484 attributeName),
3485 errdetail("%s versus %s",
3486 prevdef->compression, newdef->compression)));
3487 }
3488
3489 /*
3490 * Check for GENERATED conflicts
3491 */
3492 if (prevdef->generated != newdef->generated)
3493 ereport(ERROR,
3494 (errcode(ERRCODE_DATATYPE_MISMATCH),
3495 errmsg("inherited column \"%s\" has a generation conflict",
3496 attributeName)));
3497
3498 /*
3499 * Default and other constraints are handled by the caller.
3500 */
3501
3502 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3503 &prevdef->inhcount))
3504 ereport(ERROR,
3505 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3506 errmsg("too many inheritance parents"));
3507
3508 return prevdef;
3509}
3510
3511/*
3512 * StoreCatalogInheritance
3513 * Updates the system catalogs with proper inheritance information.
3514 *
3515 * supers is a list of the OIDs of the new relation's direct ancestors.
3516 */
3517static void
3519 bool child_is_partition)
3520{
3521 Relation relation;
3522 int32 seqNumber;
3523 ListCell *entry;
3524
3525 /*
3526 * sanity checks
3527 */
3528 Assert(OidIsValid(relationId));
3529
3530 if (supers == NIL)
3531 return;
3532
3533 /*
3534 * Store INHERITS information in pg_inherits using direct ancestors only.
3535 * Also enter dependencies on the direct ancestors, and make sure they are
3536 * marked with relhassubclass = true.
3537 *
3538 * (Once upon a time, both direct and indirect ancestors were found here
3539 * and then entered into pg_ipl. Since that catalog doesn't exist
3540 * anymore, there's no need to look for indirect ancestors.)
3541 */
3542 relation = table_open(InheritsRelationId, RowExclusiveLock);
3543
3544 seqNumber = 1;
3545 foreach(entry, supers)
3546 {
3547 Oid parentOid = lfirst_oid(entry);
3548
3549 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3550 child_is_partition);
3551 seqNumber++;
3552 }
3553
3554 table_close(relation, RowExclusiveLock);
3555}
3556
3557/*
3558 * Make catalog entries showing relationId as being an inheritance child
3559 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3560 */
3561static void
3562StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3563 int32 seqNumber, Relation inhRelation,
3564 bool child_is_partition)
3565{
3566 ObjectAddress childobject,
3567 parentobject;
3568
3569 /* store the pg_inherits row */
3570 StoreSingleInheritance(relationId, parentOid, seqNumber);
3571
3572 /*
3573 * Store a dependency too
3574 */
3575 parentobject.classId = RelationRelationId;
3576 parentobject.objectId = parentOid;
3577 parentobject.objectSubId = 0;
3578 childobject.classId = RelationRelationId;
3579 childobject.objectId = relationId;
3580 childobject.objectSubId = 0;
3581
3582 recordDependencyOn(&childobject, &parentobject,
3583 child_dependency_type(child_is_partition));
3584
3585 /*
3586 * Post creation hook of this inheritance. Since object_access_hook
3587 * doesn't take multiple object identifiers, we relay oid of parent
3588 * relation using auxiliary_id argument.
3589 */
3590 InvokeObjectPostAlterHookArg(InheritsRelationId,
3591 relationId, 0,
3592 parentOid, false);
3593
3594 /*
3595 * Mark the parent as having subclasses.
3596 */
3597 SetRelationHasSubclass(parentOid, true);
3598}
3599
3600/*
3601 * Look for an existing column entry with the given name.
3602 *
3603 * Returns the index (starting with 1) if attribute already exists in columns,
3604 * 0 if it doesn't.
3605 */
3606static int
3607findAttrByName(const char *attributeName, const List *columns)
3608{
3609 ListCell *lc;
3610 int i = 1;
3611
3612 foreach(lc, columns)
3613 {
3614 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3615 return i;
3616
3617 i++;
3618 }
3619 return 0;
3620}
3621
3622
3623/*
3624 * SetRelationHasSubclass
3625 * Set the value of the relation's relhassubclass field in pg_class.
3626 *
3627 * It's always safe to set this field to true, because all SQL commands are
3628 * ready to see true and then find no children. On the other hand, commands
3629 * generally assume zero children if this is false.
3630 *
3631 * Caller must hold any self-exclusive lock until end of transaction. If the
3632 * new value is false, caller must have acquired that lock before reading the
3633 * evidence that justified the false value. That way, it properly waits if
3634 * another backend is simultaneously concluding no need to change the tuple
3635 * (new and old values are true).
3636 *
3637 * NOTE: an important side-effect of this operation is that an SI invalidation
3638 * message is sent out to all backends --- including me --- causing plans
3639 * referencing the relation to be rebuilt with the new list of children.
3640 * This must happen even if we find that no change is needed in the pg_class
3641 * row.
3642 */
3643void
3644SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3645{
3646 Relation relationRelation;
3647 HeapTuple tuple;
3648 Form_pg_class classtuple;
3649
3651 ShareUpdateExclusiveLock, false) ||
3652 CheckRelationOidLockedByMe(relationId,
3653 ShareRowExclusiveLock, true));
3654
3655 /*
3656 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3657 */
3658 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3659 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3660 if (!HeapTupleIsValid(tuple))
3661 elog(ERROR, "cache lookup failed for relation %u", relationId);
3662 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3663
3664 if (classtuple->relhassubclass != relhassubclass)
3665 {
3666 classtuple->relhassubclass = relhassubclass;
3667 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3668 }
3669 else
3670 {
3671 /* no need to change tuple, but force relcache rebuild anyway */
3673 }
3674
3675 heap_freetuple(tuple);
3676 table_close(relationRelation, RowExclusiveLock);
3677}
3678
3679/*
3680 * CheckRelationTableSpaceMove
3681 * Check if relation can be moved to new tablespace.
3682 *
3683 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3684 *
3685 * Returns true if the relation can be moved to the new tablespace; raises
3686 * an error if it is not possible to do the move; returns false if the move
3687 * would have no effect.
3688 */
3689bool
3691{
3692 Oid oldTableSpaceId;
3693
3694 /*
3695 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3696 * stored as 0.
3697 */
3698 oldTableSpaceId = rel->rd_rel->reltablespace;
3699 if (newTableSpaceId == oldTableSpaceId ||
3700 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3701 return false;
3702
3703 /*
3704 * We cannot support moving mapped relations into different tablespaces.
3705 * (In particular this eliminates all shared catalogs.)
3706 */
3707 if (RelationIsMapped(rel))
3708 ereport(ERROR,
3709 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3710 errmsg("cannot move system relation \"%s\"",
3712
3713 /* Cannot move a non-shared relation into pg_global */
3714 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3715 ereport(ERROR,
3716 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3717 errmsg("only shared relations can be placed in pg_global tablespace")));
3718
3719 /*
3720 * Do not allow moving temp tables of other backends ... their local
3721 * buffer manager is not going to cope.
3722 */
3723 if (RELATION_IS_OTHER_TEMP(rel))
3724 ereport(ERROR,
3725 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3726 errmsg("cannot move temporary tables of other sessions")));
3727
3728 return true;
3729}
3730
3731/*
3732 * SetRelationTableSpace
3733 * Set new reltablespace and relfilenumber in pg_class entry.
3734 *
3735 * newTableSpaceId is the new tablespace for the relation, and
3736 * newRelFilenumber its new filenumber. If newRelFilenumber is
3737 * InvalidRelFileNumber, this field is not updated.
3738 *
3739 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3740 *
3741 * The caller of this routine had better check if a relation can be
3742 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3743 * first, and is responsible for making the change visible with
3744 * CommandCounterIncrement().
3745 */
3746void
3748 Oid newTableSpaceId,
3749 RelFileNumber newRelFilenumber)
3750{
3751 Relation pg_class;
3752 HeapTuple tuple;
3753 ItemPointerData otid;
3754 Form_pg_class rd_rel;
3755 Oid reloid = RelationGetRelid(rel);
3756
3757 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3758
3759 /* Get a modifiable copy of the relation's pg_class row. */
3760 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3761
3762 tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3763 if (!HeapTupleIsValid(tuple))
3764 elog(ERROR, "cache lookup failed for relation %u", reloid);
3765 otid = tuple->t_self;
3766 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3767
3768 /* Update the pg_class row. */
3769 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3770 InvalidOid : newTableSpaceId;
3771 if (RelFileNumberIsValid(newRelFilenumber))
3772 rd_rel->relfilenode = newRelFilenumber;
3773 CatalogTupleUpdate(pg_class, &otid, tuple);
3774 UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3775
3776 /*
3777 * Record dependency on tablespace. This is only required for relations
3778 * that have no physical storage.
3779 */
3780 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3781 changeDependencyOnTablespace(RelationRelationId, reloid,
3782 rd_rel->reltablespace);
3783
3784 heap_freetuple(tuple);
3785 table_close(pg_class, RowExclusiveLock);
3786}
3787
3788/*
3789 * renameatt_check - basic sanity checks before attribute rename
3790 */
3791static void
3792renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3793{
3794 char relkind = classform->relkind;
3795
3796 if (classform->reloftype && !recursing)
3797 ereport(ERROR,
3798 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3799 errmsg("cannot rename column of typed table")));
3800
3801 /*
3802 * Renaming the columns of sequences or toast tables doesn't actually
3803 * break anything from the system's point of view, since internal
3804 * references are by attnum. But it doesn't seem right to allow users to
3805 * change names that are hardcoded into the system, hence the following
3806 * restriction.
3807 */
3808 if (relkind != RELKIND_RELATION &&
3809 relkind != RELKIND_VIEW &&
3810 relkind != RELKIND_MATVIEW &&
3811 relkind != RELKIND_COMPOSITE_TYPE &&
3812 relkind != RELKIND_INDEX &&
3813 relkind != RELKIND_PARTITIONED_INDEX &&
3814 relkind != RELKIND_FOREIGN_TABLE &&
3815 relkind != RELKIND_PARTITIONED_TABLE)
3816 ereport(ERROR,
3817 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3818 errmsg("cannot rename columns of relation \"%s\"",
3819 NameStr(classform->relname)),
3821
3822 /*
3823 * permissions checking. only the owner of a class can change its schema.
3824 */
3825 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3827 NameStr(classform->relname));
3828 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3829 ereport(ERROR,
3830 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3831 errmsg("permission denied: \"%s\" is a system catalog",
3832 NameStr(classform->relname))));
3833}
3834
3835/*
3836 * renameatt_internal - workhorse for renameatt
3837 *
3838 * Return value is the attribute number in the 'myrelid' relation.
3839 */
3840static AttrNumber
3842 const char *oldattname,
3843 const char *newattname,
3844 bool recurse,
3845 bool recursing,
3846 int expected_parents,
3847 DropBehavior behavior)
3848{
3849 Relation targetrelation;
3850 Relation attrelation;
3851 HeapTuple atttup;
3852 Form_pg_attribute attform;
3854
3855 /*
3856 * Grab an exclusive lock on the target table, which we will NOT release
3857 * until end of transaction.
3858 */
3859 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3860 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3861
3862 /*
3863 * if the 'recurse' flag is set then we are supposed to rename this
3864 * attribute in all classes that inherit from 'relname' (as well as in
3865 * 'relname').
3866 *
3867 * any permissions or problems with duplicate attributes will cause the
3868 * whole transaction to abort, which is what we want -- all or nothing.
3869 */
3870 if (recurse)
3871 {
3872 List *child_oids,
3873 *child_numparents;
3874 ListCell *lo,
3875 *li;
3876
3877 /*
3878 * we need the number of parents for each child so that the recursive
3879 * calls to renameatt() can determine whether there are any parents
3880 * outside the inheritance hierarchy being processed.
3881 */
3882 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3883 &child_numparents);
3884
3885 /*
3886 * find_all_inheritors does the recursive search of the inheritance
3887 * hierarchy, so all we have to do is process all of the relids in the
3888 * list that it returns.
3889 */
3890 forboth(lo, child_oids, li, child_numparents)
3891 {
3892 Oid childrelid = lfirst_oid(lo);
3893 int numparents = lfirst_int(li);
3894
3895 if (childrelid == myrelid)
3896 continue;
3897 /* note we need not recurse again */
3898 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3899 }
3900 }
3901 else
3902 {
3903 /*
3904 * If we are told not to recurse, there had better not be any child
3905 * tables; else the rename would put them out of step.
3906 *
3907 * expected_parents will only be 0 if we are not already recursing.
3908 */
3909 if (expected_parents == 0 &&
3911 ereport(ERROR,
3912 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3913 errmsg("inherited column \"%s\" must be renamed in child tables too",
3914 oldattname)));
3915 }
3916
3917 /* rename attributes in typed tables of composite type */
3918 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3919 {
3920 List *child_oids;
3921 ListCell *lo;
3922
3923 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3924 RelationGetRelationName(targetrelation),
3925 behavior);
3926
3927 foreach(lo, child_oids)
3928 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3929 }
3930
3931 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3932
3933 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3934 if (!HeapTupleIsValid(atttup))
3935 ereport(ERROR,
3936 (errcode(ERRCODE_UNDEFINED_COLUMN),
3937 errmsg("column \"%s\" does not exist",
3938 oldattname)));
3939 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3940
3941 attnum = attform->attnum;
3942 if (attnum <= 0)
3943 ereport(ERROR,
3944 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3945 errmsg("cannot rename system column \"%s\"",
3946 oldattname)));
3947
3948 /*
3949 * if the attribute is inherited, forbid the renaming. if this is a
3950 * top-level call to renameatt(), then expected_parents will be 0, so the
3951 * effect of this code will be to prohibit the renaming if the attribute
3952 * is inherited at all. if this is a recursive call to renameatt(),
3953 * expected_parents will be the number of parents the current relation has
3954 * within the inheritance hierarchy being processed, so we'll prohibit the
3955 * renaming only if there are additional parents from elsewhere.
3956 */
3957 if (attform->attinhcount > expected_parents)
3958 ereport(ERROR,
3959 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3960 errmsg("cannot rename inherited column \"%s\"",
3961 oldattname)));
3962
3963 /* new name should not already exist */
3964 (void) check_for_column_name_collision(targetrelation, newattname, false);
3965
3966 /* apply the update */
3967 namestrcpy(&(attform->attname), newattname);
3968
3969 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3970
3971 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3972
3973 heap_freetuple(atttup);
3974
3975 table_close(attrelation, RowExclusiveLock);
3976
3977 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3978
3979 return attnum;
3980}
3981
3982/*
3983 * Perform permissions and integrity checks before acquiring a relation lock.
3984 */
3985static void
3987 void *arg)
3988{
3989 HeapTuple tuple;
3990 Form_pg_class form;
3991
3992 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3993 if (!HeapTupleIsValid(tuple))
3994 return; /* concurrently dropped */
3995 form = (Form_pg_class) GETSTRUCT(tuple);
3996 renameatt_check(relid, form, false);
3997 ReleaseSysCache(tuple);
3998}
3999
4000/*
4001 * renameatt - changes the name of an attribute in a relation
4002 *
4003 * The returned ObjectAddress is that of the renamed column.
4004 */
4007{
4008 Oid relid;
4010 ObjectAddress address;
4011
4012 /* lock level taken here should match renameatt_internal */
4014 stmt->missing_ok ? RVR_MISSING_OK : 0,
4016 NULL);
4017
4018 if (!OidIsValid(relid))
4019 {
4021 (errmsg("relation \"%s\" does not exist, skipping",
4022 stmt->relation->relname)));
4023 return InvalidObjectAddress;
4024 }
4025
4026 attnum =
4027 renameatt_internal(relid,
4028 stmt->subname, /* old att name */
4029 stmt->newname, /* new att name */
4030 stmt->relation->inh, /* recursive? */
4031 false, /* recursing? */
4032 0, /* expected inhcount */
4033 stmt->behavior);
4034
4035 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4036
4037 return address;
4038}
4039
4040/*
4041 * same logic as renameatt_internal
4042 */
4043static ObjectAddress
4045 Oid mytypid,
4046 const char *oldconname,
4047 const char *newconname,
4048 bool recurse,
4049 bool recursing,
4050 int expected_parents)
4051{
4052 Relation targetrelation = NULL;
4053 Oid constraintOid;
4054 HeapTuple tuple;
4056 ObjectAddress address;
4057
4058 Assert(!myrelid || !mytypid);
4059
4060 if (mytypid)
4061 {
4062 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4063 }
4064 else
4065 {
4066 targetrelation = relation_open(myrelid, AccessExclusiveLock);
4067
4068 /*
4069 * don't tell it whether we're recursing; we allow changing typed
4070 * tables here
4071 */
4072 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4073
4074 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4075 }
4076
4077 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4078 if (!HeapTupleIsValid(tuple))
4079 elog(ERROR, "cache lookup failed for constraint %u",
4080 constraintOid);
4081 con = (Form_pg_constraint) GETSTRUCT(tuple);
4082
4083 if (myrelid &&
4084 (con->contype == CONSTRAINT_CHECK ||
4085 con->contype == CONSTRAINT_NOTNULL) &&
4086 !con->connoinherit)
4087 {
4088 if (recurse)
4089 {
4090 List *child_oids,
4091 *child_numparents;
4092 ListCell *lo,
4093 *li;
4094
4095 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4096 &child_numparents);
4097
4098 forboth(lo, child_oids, li, child_numparents)
4099 {
4100 Oid childrelid = lfirst_oid(lo);
4101 int numparents = lfirst_int(li);
4102
4103 if (childrelid == myrelid)
4104 continue;
4105
4106 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4107 }
4108 }
4109 else
4110 {
4111 if (expected_parents == 0 &&
4113 ereport(ERROR,
4114 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4115 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4116 oldconname)));
4117 }
4118
4119 if (con->coninhcount > expected_parents)
4120 ereport(ERROR,
4121 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4122 errmsg("cannot rename inherited constraint \"%s\"",
4123 oldconname)));
4124 }
4125
4126 if (con->conindid
4127 && (con->contype == CONSTRAINT_PRIMARY
4128 || con->contype == CONSTRAINT_UNIQUE
4129 || con->contype == CONSTRAINT_EXCLUSION))
4130 /* rename the index; this renames the constraint as well */
4131 RenameRelationInternal(con->conindid, newconname, false, true);
4132 else
4133 RenameConstraintById(constraintOid, newconname);
4134
4135 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4136
4137 ReleaseSysCache(tuple);
4138
4139 if (targetrelation)
4140 {
4141 /*
4142 * Invalidate relcache so as others can see the new constraint name.
4143 */
4144 CacheInvalidateRelcache(targetrelation);
4145
4146 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4147 }
4148
4149 return address;
4150}
4151
4154{
4155 Oid relid = InvalidOid;
4156 Oid typid = InvalidOid;
4157
4158 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4159 {
4160 Relation rel;
4161 HeapTuple tup;
4162
4163 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4164 rel = table_open(TypeRelationId, RowExclusiveLock);
4165 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4166 if (!HeapTupleIsValid(tup))
4167 elog(ERROR, "cache lookup failed for type %u", typid);
4168 checkDomainOwner(tup);
4169 ReleaseSysCache(tup);
4170 table_close(rel, NoLock);
4171 }
4172 else
4173 {
4174 /* lock level taken here should match rename_constraint_internal */
4176 stmt->missing_ok ? RVR_MISSING_OK : 0,
4178 NULL);
4179 if (!OidIsValid(relid))
4180 {
4182 (errmsg("relation \"%s\" does not exist, skipping",
4183 stmt->relation->relname)));
4184 return InvalidObjectAddress;
4185 }
4186 }
4187
4188 return
4189 rename_constraint_internal(relid, typid,
4190 stmt->subname,
4191 stmt->newname,
4192 (stmt->relation &&
4193 stmt->relation->inh), /* recursive? */
4194 false, /* recursing? */
4195 0 /* expected inhcount */ );
4196}
4197
4198/*
4199 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4200 * RENAME
4201 */
4204{
4205 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4206 Oid relid;
4207 ObjectAddress address;
4208
4209 /*
4210 * Grab an exclusive lock on the target table, index, sequence, view,
4211 * materialized view, or foreign table, which we will NOT release until
4212 * end of transaction.
4213 *
4214 * Lock level used here should match RenameRelationInternal, to avoid lock
4215 * escalation. However, because ALTER INDEX can be used with any relation
4216 * type, we mustn't believe without verification.
4217 */
4218 for (;;)
4219 {
4220 LOCKMODE lockmode;
4221 char relkind;
4222 bool obj_is_index;
4223
4224 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4225
4226 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4227 stmt->missing_ok ? RVR_MISSING_OK : 0,
4229 stmt);
4230
4231 if (!OidIsValid(relid))
4232 {
4234 (errmsg("relation \"%s\" does not exist, skipping",
4235 stmt->relation->relname)));
4236 return InvalidObjectAddress;
4237 }
4238
4239 /*
4240 * We allow mismatched statement and object types (e.g., ALTER INDEX
4241 * to rename a table), but we might've used the wrong lock level. If
4242 * that happens, retry with the correct lock level. We don't bother
4243 * if we already acquired AccessExclusiveLock with an index, however.
4244 */
4245 relkind = get_rel_relkind(relid);
4246 obj_is_index = (relkind == RELKIND_INDEX ||
4247 relkind == RELKIND_PARTITIONED_INDEX);
4248 if (obj_is_index || is_index_stmt == obj_is_index)
4249 break;
4250
4251 UnlockRelationOid(relid, lockmode);
4252 is_index_stmt = obj_is_index;
4253 }
4254
4255 /* Do the work */
4256 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4257
4258 ObjectAddressSet(address, RelationRelationId, relid);
4259
4260 return address;
4261}
4262
4263/*
4264 * RenameRelationInternal - change the name of a relation
4265 */
4266void
4267RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4268{
4269 Relation targetrelation;
4270 Relation relrelation; /* for RELATION relation */
4271 ItemPointerData otid;
4272 HeapTuple reltup;
4273 Form_pg_class relform;
4274 Oid namespaceId;
4275
4276 /*
4277 * Grab a lock on the target relation, which we will NOT release until end
4278 * of transaction. We need at least a self-exclusive lock so that
4279 * concurrent DDL doesn't overwrite the rename if they start updating
4280 * while still seeing the old version. The lock also guards against
4281 * triggering relcache reloads in concurrent sessions, which might not
4282 * handle this information changing under them. For indexes, we can use a
4283 * reduced lock level because RelationReloadIndexInfo() handles indexes
4284 * specially.
4285 */
4286 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4287 namespaceId = RelationGetNamespace(targetrelation);
4288
4289 /*
4290 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4291 */
4292 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4293
4294 reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4295 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4296 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4297 otid = reltup->t_self;
4298 relform = (Form_pg_class) GETSTRUCT(reltup);
4299
4300 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4301 ereport(ERROR,
4302 (errcode(ERRCODE_DUPLICATE_TABLE),
4303 errmsg("relation \"%s\" already exists",
4304 newrelname)));
4305
4306 /*
4307 * RenameRelation is careful not to believe the caller's idea of the
4308 * relation kind being handled. We don't have to worry about this, but
4309 * let's not be totally oblivious to it. We can process an index as
4310 * not-an-index, but not the other way around.
4311 */
4312 Assert(!is_index ||
4313 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4314 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4315
4316 /*
4317 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4318 * because it's a copy...)
4319 */
4320 namestrcpy(&(relform->relname), newrelname);
4321
4322 CatalogTupleUpdate(relrelation, &otid, reltup);
4323 UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4324
4325 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4326 InvalidOid, is_internal);
4327
4328 heap_freetuple(reltup);
4329 table_close(relrelation, RowExclusiveLock);
4330
4331 /*
4332 * Also rename the associated type, if any.
4333 */
4334 if (OidIsValid(targetrelation->rd_rel->reltype))
4335 RenameTypeInternal(targetrelation->rd_rel->reltype,
4336 newrelname, namespaceId);
4337
4338 /*
4339 * Also rename the associated constraint, if any.
4340 */
4341 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4342 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4343 {
4344 Oid constraintId = get_index_constraint(myrelid);
4345
4346 if (OidIsValid(constraintId))
4347 RenameConstraintById(constraintId, newrelname);
4348 }
4349
4350 /*
4351 * Close rel, but keep lock!
4352 */
4353 relation_close(targetrelation, NoLock);
4354}
4355
4356/*
4357 * ResetRelRewrite - reset relrewrite
4358 */
4359void
4361{
4362 Relation relrelation; /* for RELATION relation */
4363 HeapTuple reltup;
4364 Form_pg_class relform;
4365
4366 /*
4367 * Find relation's pg_class tuple.
4368 */
4369 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4370
4371 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4372 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4373 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4374 relform = (Form_pg_class) GETSTRUCT(reltup);
4375
4376 /*
4377 * Update pg_class tuple.
4378 */
4379 relform->relrewrite = InvalidOid;
4380
4381 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4382
4383 heap_freetuple(reltup);
4384 table_close(relrelation, RowExclusiveLock);
4385}
4386
4387/*
4388 * Disallow ALTER TABLE (and similar commands) when the current backend has
4389 * any open reference to the target table besides the one just acquired by
4390 * the calling command; this implies there's an open cursor or active plan.
4391 * We need this check because our lock doesn't protect us against stomping
4392 * on our own foot, only other people's feet!
4393 *
4394 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4395 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4396 * possibly be relaxed to only error out for certain types of alterations.
4397 * But the use-case for allowing any of these things is not obvious, so we
4398 * won't work hard at it for now.
4399 *
4400 * We also reject these commands if there are any pending AFTER trigger events
4401 * for the rel. This is certainly necessary for the rewriting variants of
4402 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4403 * events would try to fetch the wrong tuples. It might be overly cautious
4404 * in other cases, but again it seems better to err on the side of paranoia.
4405 *
4406 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4407 * we are worried about active indexscans on the index. The trigger-event
4408 * check can be skipped, since we are doing no damage to the parent table.
4409 *
4410 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4411 */
4412void
4414{
4415 int expected_refcnt;
4416
4417 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4418 if (rel->rd_refcnt != expected_refcnt)
4419 ereport(ERROR,
4420 (errcode(ERRCODE_OBJECT_IN_USE),
4421 /* translator: first %s is a SQL command, eg ALTER TABLE */
4422 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4424
4425 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4426 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4428 ereport(ERROR,
4429 (errcode(ERRCODE_OBJECT_IN_USE),
4430 /* translator: first %s is a SQL command, eg ALTER TABLE */
4431 errmsg("cannot %s \"%s\" because it has pending trigger events",
4433}
4434
4435/*
4436 * CheckAlterTableIsSafe
4437 * Verify that it's safe to allow ALTER TABLE on this relation.
4438 *
4439 * This consists of CheckTableNotInUse() plus a check that the relation
4440 * isn't another session's temp table. We must split out the temp-table
4441 * check because there are callers of CheckTableNotInUse() that don't want
4442 * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4443 * an orphaned temp schema.) Compare truncate_check_activity().
4444 */
4445static void
4447{
4448 /*
4449 * Don't allow ALTER on temp tables of other backends. Their local buffer
4450 * manager is not going to cope if we need to change the table's contents.
4451 * Even if we don't, there may be optimizations that assume temp tables
4452 * aren't subject to such interference.
4453 */
4454 if (RELATION_IS_OTHER_TEMP(rel))
4455 ereport(ERROR,
4456 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4457 errmsg("cannot alter temporary tables of other sessions")));
4458
4459 /*
4460 * Also check for active uses of the relation in the current transaction,
4461 * including open scans and pending AFTER trigger events.
4462 */
4463 CheckTableNotInUse(rel, "ALTER TABLE");
4464}
4465
4466/*
4467 * AlterTableLookupRelation
4468 * Look up, and lock, the OID for the relation named by an alter table
4469 * statement.
4470 */
4471Oid
4473{
4474 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4475 stmt->missing_ok ? RVR_MISSING_OK : 0,
4477 stmt);
4478}
4479
4480/*
4481 * AlterTable
4482 * Execute ALTER TABLE, which can be a list of subcommands
4483 *
4484 * ALTER TABLE is performed in three phases:
4485 * 1. Examine subcommands and perform pre-transformation checking.
4486 * 2. Validate and transform subcommands, and update system catalogs.
4487 * 3. Scan table(s) to check new constraints, and optionally recopy
4488 * the data into new table(s).
4489 * Phase 3 is not performed unless one or more of the subcommands requires
4490 * it. The intention of this design is to allow multiple independent
4491 * updates of the table schema to be performed with only one pass over the
4492 * data.
4493 *
4494 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4495 * each table to be affected (there may be multiple affected tables if the
4496 * commands traverse a table inheritance hierarchy). Also we do preliminary
4497 * validation of the subcommands. Because earlier subcommands may change
4498 * the catalog state seen by later commands, there are limits to what can
4499 * be done in this phase. Generally, this phase acquires table locks,
4500 * checks permissions and relkind, and recurses to find child tables.
4501 *
4502 * ATRewriteCatalogs performs phase 2 for each affected table.
4503 * Certain subcommands need to be performed before others to avoid
4504 * unnecessary conflicts; for example, DROP COLUMN should come before
4505 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4506 * lists, one for each logical "pass" of phase 2.
4507 *
4508 * ATRewriteTables performs phase 3 for those tables that need it.
4509 *
4510 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4511 * since phase 1 already does it. However, for certain subcommand types
4512 * it is only possible to determine how to recurse at phase 2 time; for
4513 * those cases, phase 1 sets the cmd->recurse flag.
4514 *
4515 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4516 * the whole operation; we don't have to do anything special to clean up.
4517 *
4518 * The caller must lock the relation, with an appropriate lock level
4519 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4520 * or higher. We pass the lock level down
4521 * so that we can apply it recursively to inherited tables. Note that the
4522 * lock level we want as we recurse might well be higher than required for
4523 * that specific subcommand. So we pass down the overall lock requirement,
4524 * rather than reassess it at lower levels.
4525 *
4526 * The caller also provides a "context" which is to be passed back to
4527 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4528 * Some of the fields therein, such as the relid, are used here as well.
4529 */
4530void
4532 AlterTableUtilityContext *context)
4533{
4534 Relation rel;
4535
4536 /* Caller is required to provide an adequate lock. */
4537 rel = relation_open(context->relid, NoLock);
4538
4540
4541 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4542}
4543
4544/*
4545 * AlterTableInternal
4546 *
4547 * ALTER TABLE with target specified by OID
4548 *
4549 * We do not reject if the relation is already open, because it's quite
4550 * likely that one or more layers of caller have it open. That means it
4551 * is unsafe to use this entry point for alterations that could break
4552 * existing query plans. On the assumption it's not used for such, we
4553 * don't have to reject pending AFTER triggers, either.
4554 *
4555 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4556 * used for any subcommand types that require parse transformation or
4557 * could generate subcommands that have to be passed to ProcessUtility.
4558 */
4559void
4560AlterTableInternal(Oid relid, List *cmds, bool recurse)
4561{
4562 Relation rel;
4563 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4564
4565 rel = relation_open(relid, lockmode);
4566
4568
4569 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4570}
4571
4572/*
4573 * AlterTableGetLockLevel
4574 *
4575 * Sets the overall lock level required for the supplied list of subcommands.
4576 * Policy for doing this set according to needs of AlterTable(), see
4577 * comments there for overall explanation.
4578 *
4579 * Function is called before and after parsing, so it must give same
4580 * answer each time it is called. Some subcommands are transformed
4581 * into other subcommand types, so the transform must never be made to a
4582 * lower lock level than previously assigned. All transforms are noted below.
4583 *
4584 * Since this is called before we lock the table we cannot use table metadata
4585 * to influence the type of lock we acquire.
4586 *
4587 * There should be no lockmodes hardcoded into the subcommand functions. All
4588 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4589 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4590 * and does not travel through this section of code and cannot be combined with
4591 * any of the subcommands given here.
4592 *
4593 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4594 * so any changes that might affect SELECTs running on standbys need to use
4595 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4596 * have a solution for that also.
4597 *
4598 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4599 * that takes a lock less than AccessExclusiveLock can change object definitions
4600 * while pg_dump is running. Be careful to check that the appropriate data is
4601 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4602 * otherwise we might end up with an inconsistent dump that can't restore.
4603 */
4606{
4607 /*
4608 * This only works if we read catalog tables using MVCC snapshots.
4609 */
4610 ListCell *lcmd;
4612
4613 foreach(lcmd, cmds)
4614 {
4615 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4616 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4617
4618 switch (cmd->subtype)
4619 {
4620 /*
4621 * These subcommands rewrite the heap, so require full locks.
4622 */
4623 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4624 * to SELECT */
4625 case AT_SetAccessMethod: /* must rewrite heap */
4626 case AT_SetTableSpace: /* must rewrite heap */
4627 case AT_AlterColumnType: /* must rewrite heap */
4628 cmd_lockmode = AccessExclusiveLock;
4629 break;
4630
4631 /*
4632 * These subcommands may require addition of toast tables. If
4633 * we add a toast table to a table currently being scanned, we
4634 * might miss data added to the new toast table by concurrent
4635 * insert transactions.
4636 */
4637 case AT_SetStorage: /* may add toast tables, see
4638 * ATRewriteCatalogs() */
4639 cmd_lockmode = AccessExclusiveLock;
4640 break;
4641
4642 /*
4643 * Removing constraints can affect SELECTs that have been
4644 * optimized assuming the constraint holds true. See also
4645 * CloneFkReferenced.
4646 */
4647 case AT_DropConstraint: /* as DROP INDEX */
4648 case AT_DropNotNull: /* may change some SQL plans */
4649 cmd_lockmode = AccessExclusiveLock;
4650 break;
4651
4652 /*
4653 * Subcommands that may be visible to concurrent SELECTs
4654 */
4655 case AT_DropColumn: /* change visible to SELECT */
4656 case AT_AddColumnToView: /* CREATE VIEW */
4657 case AT_DropOids: /* used to equiv to DropColumn */
4658 case AT_EnableAlwaysRule: /* may change SELECT rules */
4659 case AT_EnableReplicaRule: /* may change SELECT rules */
4660 case AT_EnableRule: /* may change SELECT rules */
4661 case AT_DisableRule: /* may change SELECT rules */
4662 cmd_lockmode = AccessExclusiveLock;
4663 break;
4664
4665 /*
4666 * Changing owner may remove implicit SELECT privileges
4667 */
4668 case AT_ChangeOwner: /* change visible to SELECT */
4669 cmd_lockmode = AccessExclusiveLock;
4670 break;
4671
4672 /*
4673 * Changing foreign table options may affect optimization.
4674 */
4675 case AT_GenericOptions:
4677 cmd_lockmode = AccessExclusiveLock;
4678 break;
4679
4680 /*
4681 * These subcommands affect write operations only.
4682 */
4683 case AT_EnableTrig:
4686 case AT_EnableTrigAll:
4687 case AT_EnableTrigUser:
4688 case AT_DisableTrig:
4689 case AT_DisableTrigAll:
4690 case AT_DisableTrigUser:
4691 cmd_lockmode = ShareRowExclusiveLock;
4692 break;
4693
4694 /*
4695 * These subcommands affect write operations only. XXX
4696 * Theoretically, these could be ShareRowExclusiveLock.
4697 */
4698 case AT_ColumnDefault:
4700 case AT_AlterConstraint:
4701 case AT_AddIndex: /* from ADD CONSTRAINT */
4703 case AT_ReplicaIdentity:
4704 case AT_SetNotNull:
4709 case AT_AddIdentity:
4710 case AT_DropIdentity:
4711 case AT_SetIdentity:
4712 case AT_SetExpression:
4713 case AT_DropExpression:
4714 case AT_SetCompression:
4715 cmd_lockmode = AccessExclusiveLock;
4716 break;
4717
4718 case AT_AddConstraint:
4719 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4720 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4721 if (IsA(cmd->def, Constraint))
4722 {
4723 Constraint *con = (Constraint *) cmd->def;
4724
4725 switch (con->contype)
4726 {
4727 case CONSTR_EXCLUSION:
4728 case CONSTR_PRIMARY:
4729 case CONSTR_UNIQUE:
4730
4731 /*
4732 * Cases essentially the same as CREATE INDEX. We
4733 * could reduce the lock strength to ShareLock if
4734 * we can work out how to allow concurrent catalog
4735 * updates. XXX Might be set down to
4736 * ShareRowExclusiveLock but requires further
4737 * analysis.
4738 */
4739 cmd_lockmode = AccessExclusiveLock;
4740 break;
4741 case CONSTR_FOREIGN:
4742
4743 /*
4744 * We add triggers to both tables when we add a
4745 * Foreign Key, so the lock level must be at least
4746 * as strong as CREATE TRIGGER.
4747 */
4748 cmd_lockmode = ShareRowExclusiveLock;
4749 break;
4750
4751 default:
4752 cmd_lockmode = AccessExclusiveLock;
4753 }
4754 }
4755 break;
4756
4757 /*
4758 * These subcommands affect inheritance behaviour. Queries
4759 * started before us will continue to see the old inheritance
4760 * behaviour, while queries started after we commit will see
4761 * new behaviour. No need to prevent reads or writes to the
4762 * subtable while we hook it up though. Changing the TupDesc
4763 * may be a problem, so keep highest lock.
4764 */
4765 case AT_AddInherit:
4766 case AT_DropInherit:
4767 cmd_lockmode = AccessExclusiveLock;
4768 break;
4769
4770 /*
4771 * These subcommands affect implicit row type conversion. They
4772 * have affects similar to CREATE/DROP CAST on queries. don't
4773 * provide for invalidating parse trees as a result of such
4774 * changes, so we keep these at AccessExclusiveLock.
4775 */
4776 case AT_AddOf:
4777 case AT_DropOf:
4778 cmd_lockmode = AccessExclusiveLock;
4779 break;
4780
4781 /*
4782 * Only used by CREATE OR REPLACE VIEW which must conflict
4783 * with an SELECTs currently using the view.
4784 */
4786 cmd_lockmode = AccessExclusiveLock;
4787 break;
4788
4789 /*
4790 * These subcommands affect general strategies for performance
4791 * and maintenance, though don't change the semantic results
4792 * from normal data reads and writes. Delaying an ALTER TABLE
4793 * behind currently active writes only delays the point where
4794 * the new strategy begins to take effect, so there is no
4795 * benefit in waiting. In this case the minimum restriction
4796 * applies: we don't currently allow concurrent catalog
4797 * updates.
4798 */
4799 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4800 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4801 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4802 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4803 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4804 cmd_lockmode = ShareUpdateExclusiveLock;
4805 break;
4806
4807 case AT_SetLogged:
4808 case AT_SetUnLogged:
4809 cmd_lockmode = AccessExclusiveLock;
4810 break;
4811
4812 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4813 cmd_lockmode = ShareUpdateExclusiveLock;
4814 break;
4815
4816 /*
4817 * Rel options are more complex than first appears. Options
4818 * are set here for tables, views and indexes; for historical
4819 * reasons these can all be used with ALTER TABLE, so we can't
4820 * decide between them using the basic grammar.
4821 */
4822 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4823 * getTables() */
4824 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4825 * getTables() */
4826 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4827 break;
4828
4829 case AT_AttachPartition:
4830 cmd_lockmode = ShareUpdateExclusiveLock;
4831 break;
4832
4833 case AT_DetachPartition:
4834 if (((PartitionCmd *) cmd->def)->concurrent)
4835 cmd_lockmode = ShareUpdateExclusiveLock;
4836 else
4837 cmd_lockmode = AccessExclusiveLock;
4838 break;
4839
4841 cmd_lockmode = ShareUpdateExclusiveLock;
4842 break;
4843
4844 case AT_MergePartitions:
4845 case AT_SplitPartition:
4846 cmd_lockmode = AccessExclusiveLock;
4847 break;
4848
4849 default: /* oops */
4850 elog(ERROR, "unrecognized alter table type: %d",
4851 (int) cmd->subtype);
4852 break;
4853 }
4854
4855 /*
4856 * Take the greatest lockmode from any subcommand
4857 */
4858 if (cmd_lockmode > lockmode)
4859 lockmode = cmd_lockmode;
4860 }
4861
4862 return lockmode;
4863}
4864
4865/*
4866 * ATController provides top level control over the phases.
4867 *
4868 * parsetree is passed in to allow it to be passed to event triggers
4869 * when requested.
4870 */
4871static void
4873 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4874 AlterTableUtilityContext *context)
4875{
4876 List *wqueue = NIL;
4877 ListCell *lcmd;
4878
4879 /* Phase 1: preliminary examination of commands, create work queue */
4880 foreach(lcmd, cmds)
4881 {
4882 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4883
4884 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4885 }
4886
4887 /* Close the relation, but keep lock until commit */
4888 relation_close(rel, NoLock);
4889
4890 /* Phase 2: update system catalogs */
4891 ATRewriteCatalogs(&wqueue, lockmode, context);
4892
4893 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4894 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4895}
4896
4897/*
4898 * ATPrepCmd
4899 *
4900 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4901 * recursion and permission checks.
4902 *
4903 * Caller must have acquired appropriate lock type on relation already.
4904 * This lock should be held until commit.
4905 */
4906static void
4908 bool recurse, bool recursing, LOCKMODE lockmode,
4909 AlterTableUtilityContext *context)
4910{
4911 AlteredTableInfo *tab;
4913
4914 /* Find or create work queue entry for this table */
4915 tab = ATGetQueueEntry(wqueue, rel);
4916
4917 /*
4918 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4919 * partitions that are pending detach.
4920 */
4921 if (rel->rd_rel->relispartition &&
4924 ereport(ERROR,
4925 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4926 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4928 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4929
4930 /*
4931 * Copy the original subcommand for each table, so we can scribble on it.
4932 * This avoids conflicts when different child tables need to make
4933 * different parse transformations (for example, the same column may have
4934 * different column numbers in different children).
4935 */
4936 cmd = copyObject(cmd);
4937
4938 /*
4939 * Do permissions and relkind checking, recursion to child tables if
4940 * needed, and any additional phase-1 processing needed. (But beware of
4941 * adding any processing that looks at table details that another
4942 * subcommand could change. In some cases we reject multiple subcommands
4943 * that could try to change the same state in contrary ways.)
4944 */
4945 switch (cmd->subtype)
4946 {
4947 case AT_AddColumn: /* ADD COLUMN */
4948 ATSimplePermissions(cmd->subtype, rel,
4951 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4952 lockmode, context);
4953 /* Recursion occurs during execution phase */
4954 pass = AT_PASS_ADD_COL;
4955 break;
4956 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4958 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4959 lockmode, context);
4960 /* Recursion occurs during execution phase */
4961 pass = AT_PASS_ADD_COL;
4962 break;
4963 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4964
4965 /*
4966 * We allow defaults on views so that INSERT into a view can have
4967 * default-ish behavior. This works because the rewriter
4968 * substitutes default values into INSERTs before it expands
4969 * rules.
4970 */
4971 ATSimplePermissions(cmd->subtype, rel,
4974 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4975 /* No command-specific prep needed */
4977 break;
4978 case AT_CookedColumnDefault: /* add a pre-cooked default */
4979 /* This is currently used only in CREATE TABLE */
4980 /* (so the permission check really isn't necessary) */
4981 ATSimplePermissions(cmd->subtype, rel,
4983 /* This command never recurses */
4985 break;
4986 case AT_AddIdentity:
4987 ATSimplePermissions(cmd->subtype, rel,
4990 /* Set up recursion for phase 2; no other prep needed */
4991 if (recurse)
4992 cmd->recurse = true;
4994 break;
4995 case AT_SetIdentity:
4996 ATSimplePermissions(cmd->subtype, rel,
4999 /* Set up recursion for phase 2; no other prep needed */
5000 if (recurse)
5001 cmd->recurse = true;
5002 /* This should run after AddIdentity, so do it in MISC pass */
5003 pass = AT_PASS_MISC;
5004 break;
5005 case AT_DropIdentity:
5006 ATSimplePermissions(cmd->subtype, rel,
5009 /* Set up recursion for phase 2; no other prep needed */
5010 if (recurse)
5011 cmd->recurse = true;
5012 pass = AT_PASS_DROP;
5013 break;
5014 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5015 ATSimplePermissions(cmd->subtype, rel,
5017 /* Set up recursion for phase 2; no other prep needed */
5018 if (recurse)
5019 cmd->recurse = true;
5020 pass = AT_PASS_DROP;
5021 break;
5022 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5023 ATSimplePermissions(cmd->subtype, rel,
5025 /* Set up recursion for phase 2; no other prep needed */
5026 if (recurse)
5027 cmd->recurse = true;
5028 pass = AT_PASS_COL_ATTRS;
5029 break;
5030 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5031 ATSimplePermissions(cmd->subtype, rel,
5033 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5035 break;
5036 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5037 ATSimplePermissions(cmd->subtype, rel,
5039 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5040 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5041 pass = AT_PASS_DROP;
5042 break;
5043 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5044 ATSimplePermissions(cmd->subtype, rel,
5047 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5048 /* No command-specific prep needed */
5049 pass = AT_PASS_MISC;
5050 break;
5051 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5052 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5053 ATSimplePermissions(cmd->subtype, rel,
5056 /* This command never recurses */
5057 pass = AT_PASS_MISC;
5058 break;
5059 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5060 ATSimplePermissions(cmd->subtype, rel,
5063 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5064 /* No command-specific prep needed */
5065 pass = AT_PASS_MISC;
5066 break;
5067 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5068 ATSimplePermissions(cmd->subtype, rel,
5070 /* This command never recurses */
5071 /* No command-specific prep needed */
5072 pass = AT_PASS_MISC;
5073 break;
5074 case AT_DropColumn: /* DROP COLUMN */
5075 ATSimplePermissions(cmd->subtype, rel,
5078 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5079 lockmode, context);
5080 /* Recursion occurs during execution phase */
5081 pass = AT_PASS_DROP;
5082 break;
5083 case AT_AddIndex: /* ADD INDEX */
5085 /* This command never recurses */
5086 /* No command-specific prep needed */
5087 pass = AT_PASS_ADD_INDEX;
5088 break;
5089 case AT_AddConstraint: /* ADD CONSTRAINT */
5090 ATSimplePermissions(cmd->subtype, rel,
5092 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5093 if (recurse)
5094 {
5095 /* recurses at exec time; lock descendants and set flag */
5096 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5097 cmd->recurse = true;
5098 }
5099 pass = AT_PASS_ADD_CONSTR;
5100 break;
5101 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5103 /* This command never recurses */
5104 /* No command-specific prep needed */
5106 break;
5107 case AT_DropConstraint: /* DROP CONSTRAINT */
5108 ATSimplePermissions(cmd->subtype, rel,
5110 ATCheckPartitionsNotInUse(rel, lockmode);
5111 /* Other recursion occurs during execution phase */
5112 /* No command-specific prep needed except saving recurse flag */
5113 if (recurse)
5114 cmd->recurse = true;
5115 pass = AT_PASS_DROP;
5116 break;
5117 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5118 ATSimplePermissions(cmd->subtype, rel,
5121 /* See comments for ATPrepAlterColumnType */
5122 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5123 AT_PASS_UNSET, context);
5124 Assert(cmd != NULL);
5125 /* Performs own recursion */
5126 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5127 lockmode, context);
5128 pass = AT_PASS_ALTER_TYPE;
5129 break;
5132 /* This command never recurses */
5133 /* No command-specific prep needed */
5134 pass = AT_PASS_MISC;
5135 break;
5136 case AT_ChangeOwner: /* ALTER OWNER */
5137 /* This command never recurses */
5138 /* No command-specific prep needed */
5139 pass = AT_PASS_MISC;
5140 break;
5141 case AT_ClusterOn: /* CLUSTER ON */
5142 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5143 ATSimplePermissions(cmd->subtype, rel,
5145 /* These commands never recurse */
5146 /* No command-specific prep needed */
5147 pass = AT_PASS_MISC;
5148 break;
5149 case AT_SetLogged: /* SET LOGGED */
5150 case AT_SetUnLogged: /* SET UNLOGGED */
5152 if (tab->chgPersistence)
5153 ereport(ERROR,
5154 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5155 errmsg("cannot change persistence setting twice")));
5156 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5157 pass = AT_PASS_MISC;
5158 break;
5159 case AT_DropOids: /* SET WITHOUT OIDS */
5160 ATSimplePermissions(cmd->subtype, rel,
5162 pass = AT_PASS_DROP;
5163 break;
5164 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5165 ATSimplePermissions(cmd->subtype, rel,
5167
5168 /* check if another access method change was already requested */
5169 if (tab->chgAccessMethod)
5170 ereport(ERROR,
5171 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5172 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5173
5174 ATPrepSetAccessMethod(tab, rel, cmd->name);
5175 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5176 break;
5177 case AT_SetTableSpace: /* SET TABLESPACE */
5180 /* This command never recurses */
5181 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5182 pass = AT_PASS_MISC; /* doesn't actually matter */
5183 break;
5184 case AT_SetRelOptions: /* SET (...) */
5185 case AT_ResetRelOptions: /* RESET (...) */
5186 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5187 ATSimplePermissions(cmd->subtype, rel,
5190 /* This command never recurses */
5191 /* No command-specific prep needed */
5192 pass = AT_PASS_MISC;
5193 break;
5194 case AT_AddInherit: /* INHERIT */
5195 ATSimplePermissions(cmd->subtype, rel,
5197 /* This command never recurses */
5198 ATPrepAddInherit(rel);
5199 pass = AT_PASS_MISC;
5200 break;
5201 case AT_DropInherit: /* NO INHERIT */
5202 ATSimplePermissions(cmd->subtype, rel,
5204 /* This command never recurses */
5205 /* No command-specific prep needed */
5206 pass = AT_PASS_MISC;
5207 break;
5208 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5209 ATSimplePermissions(cmd->subtype, rel,
5211 /* Recursion occurs during execution phase */
5212 if (recurse)
5213 cmd->recurse = true;
5214 pass = AT_PASS_MISC;
5215 break;
5216 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5217 ATSimplePermissions(cmd->subtype, rel,
5219 /* Recursion occurs during execution phase */
5220 /* No command-specific prep needed except saving recurse flag */
5221 if (recurse)
5222 cmd->recurse = true;
5223 pass = AT_PASS_MISC;
5224 break;
5225 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5226 ATSimplePermissions(cmd->subtype, rel,
5228 pass = AT_PASS_MISC;
5229 /* This command never recurses */
5230 /* No command-specific prep needed */
5231 break;
5232 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5235 case AT_EnableTrigAll:
5236 case AT_EnableTrigUser:
5237 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5238 case AT_DisableTrigAll:
5239 case AT_DisableTrigUser:
5240 ATSimplePermissions(cmd->subtype, rel,
5242 /* Set up recursion for phase 2; no other prep needed */
5243 if (recurse)
5244 cmd->recurse = true;
5245 pass = AT_PASS_MISC;
5246 break;
5247 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5250 case AT_DisableRule:
5251 case AT_AddOf: /* OF */
5252 case AT_DropOf: /* NOT OF */
5257 ATSimplePermissions(cmd->subtype, rel,
5259 /* These commands never recurse */
5260 /* No command-specific prep needed */
5261 pass = AT_PASS_MISC;
5262 break;
5263 case AT_GenericOptions:
5265 /* No command-specific prep needed */
5266 pass = AT_PASS_MISC;
5267 break;
5268 case AT_AttachPartition:
5269 ATSimplePermissions(cmd->subtype, rel,
5271 /* No command-specific prep needed */
5272 pass = AT_PASS_MISC;
5273 break;
5274 case AT_DetachPartition:
5276 /* No command-specific prep needed */
5277 pass = AT_PASS_MISC;
5278 break;
5281 /* No command-specific prep needed */
5282 pass = AT_PASS_MISC;
5283 break;
5284 case AT_MergePartitions:
5285 case AT_SplitPartition:
5287 /* No command-specific prep needed */
5288 pass = AT_PASS_MISC;
5289 break;
5290 default: /* oops */
5291 elog(ERROR, "unrecognized alter table type: %d",
5292 (int) cmd->subtype);
5293 pass = AT_PASS_UNSET; /* keep compiler quiet */
5294 break;
5295 }
5296 Assert(pass > AT_PASS_UNSET);
5297
5298 /* Add the subcommand to the appropriate list for phase 2 */
5299 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5300}
5301
5302/*
5303 * ATRewriteCatalogs
5304 *
5305 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5306 * dispatched in a "safe" execution order (designed to avoid unnecessary
5307 * conflicts).
5308 */
5309static void
5311 AlterTableUtilityContext *context)
5312{
5313 ListCell *ltab;
5314
5315 /*
5316 * We process all the tables "in parallel", one pass at a time. This is
5317 * needed because we may have to propagate work from one table to another
5318 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5319 * re-adding of the foreign key constraint to the other table). Work can
5320 * only be propagated into later passes, however.
5321 */
5322 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5323 {
5324 /* Go through each table that needs to be processed */
5325 foreach(ltab, *wqueue)
5326 {
5327 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5328 List *subcmds = tab->subcmds[pass];
5329 ListCell *lcmd;
5330
5331 if (subcmds == NIL)
5332 continue;
5333
5334 /*
5335 * Open the relation and store it in tab. This allows subroutines
5336 * close and reopen, if necessary. Appropriate lock was obtained
5337 * by phase 1, needn't get it again.
5338 */
5339 tab->rel = relation_open(tab->relid, NoLock);
5340
5341 foreach(lcmd, subcmds)
5342 ATExecCmd(wqueue, tab,
5344 lockmode, pass, context);
5345
5346 /*
5347 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5348 * (this is not done in ATExecAlterColumnType since it should be
5349 * done only once if multiple columns of a table are altered).
5350 */
5351 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5352 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5353
5354 if (tab->rel)
5355 {
5356 relation_close(tab->rel, NoLock);
5357 tab->rel = NULL;
5358 }
5359 }
5360 }
5361
5362 /* Check to see if a toast table must be added. */
5363 foreach(ltab, *wqueue)
5364 {
5365 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5366
5367 /*
5368 * If the table is source table of ATTACH PARTITION command, we did
5369 * not modify anything about it that will change its toasting
5370 * requirement, so no need to check.
5371 */
5372 if (((tab->relkind == RELKIND_RELATION ||
5373 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5374 tab->partition_constraint == NULL) ||
5375 tab->relkind == RELKIND_MATVIEW)
5376 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5377 }
5378}
5379
5380/*
5381 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5382 */
5383static void
5385 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5386 AlterTableUtilityContext *context)
5387{
5389 Relation rel = tab->rel;
5390
5391 switch (cmd->subtype)
5392 {
5393 case AT_AddColumn: /* ADD COLUMN */
5394 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5395 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5396 cmd->recurse, false,
5397 lockmode, cur_pass, context);
5398 break;
5399 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5400 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5401 break;
5402 case AT_CookedColumnDefault: /* add a pre-cooked default */
5403 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5404 break;
5405 case AT_AddIdentity:
5406 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5407 cur_pass, context);
5408 Assert(cmd != NULL);
5409 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5410 break;
5411 case AT_SetIdentity:
5412 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5413 cur_pass, context);
5414 Assert(cmd != NULL);
5415 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5416 break;
5417 case AT_DropIdentity:
5418 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5419 break;
5420 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5421 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5422 break;
5423 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5424 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5425 cmd->recurse, false, lockmode);
5426 break;
5427 case AT_SetExpression:
5428 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5429 break;
5430 case AT_DropExpression:
5431 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5432 break;
5433 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5434 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5435 break;
5436 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5437 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5438 break;
5439 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5440 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5441 break;
5442 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5443 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5444 break;
5445 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5446 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5447 lockmode);
5448 break;
5449 case AT_DropColumn: /* DROP COLUMN */
5450 address = ATExecDropColumn(wqueue, rel, cmd->name,
5451 cmd->behavior, cmd->recurse, false,
5452 cmd->missing_ok, lockmode,
5453 NULL);
5454 break;
5455 case AT_AddIndex: /* ADD INDEX */
5456 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5457 lockmode);
5458 break;
5459 case AT_ReAddIndex: /* ADD INDEX */
5460 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5461 lockmode);
5462 break;
5463 case AT_ReAddStatistics: /* ADD STATISTICS */
5464 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5465 true, lockmode);
5466 break;
5467 case AT_AddConstraint: /* ADD CONSTRAINT */
5468 /* Transform the command only during initial examination */
5469 if (cur_pass == AT_PASS_ADD_CONSTR)
5470 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5471 cmd->recurse, lockmode,
5472 cur_pass, context);
5473 /* Depending on constraint type, might be no more work to do now */
5474 if (cmd != NULL)
5475 address =
5476 ATExecAddConstraint(wqueue, tab, rel,
5477 (Constraint *) cmd->def,
5478 cmd->recurse, false, lockmode);
5479 break;
5480 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5481 address =
5482 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5483 true, true, lockmode);
5484 break;
5485 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5486 * constraint */
5487 address =
5488 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5489 ((AlterDomainStmt *) cmd->def)->def,
5490 NULL);
5491 break;
5492 case AT_ReAddComment: /* Re-add existing comment */
5493 address = CommentObject((CommentStmt *) cmd->def);
5494 break;
5495 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5496 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5497 lockmode);
5498 break;
5499 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5500 address = ATExecAlterConstraint(wqueue, rel,
5502 cmd->recurse, lockmode);
5503 break;
5504 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5505 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5506 false, lockmode);
5507 break;
5508 case AT_DropConstraint: /* DROP CONSTRAINT */
5509 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5510 cmd->recurse,
5511 cmd->missing_ok, lockmode);
5512 break;
5513 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5514 /* parse transformation was done earlier */
5515 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5516 break;
5517 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5518 address =
5520 (List *) cmd->def, lockmode);
5521 break;
5522 case AT_ChangeOwner: /* ALTER OWNER */
5524 get_rolespec_oid(cmd->newowner, false),
5525 false, lockmode);
5526 break;
5527 case AT_ClusterOn: /* CLUSTER ON */
5528 address = ATExecClusterOn(rel, cmd->name, lockmode);
5529 break;
5530 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5531 ATExecDropCluster(rel, lockmode);
5532 break;
5533 case AT_SetLogged: /* SET LOGGED */
5534 case AT_SetUnLogged: /* SET UNLOGGED */
5535 break;
5536 case AT_DropOids: /* SET WITHOUT OIDS */
5537 /* nothing to do here, oid columns don't exist anymore */
5538 break;
5539 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5540
5541 /*
5542 * Only do this for partitioned tables, for which this is just a
5543 * catalog change. Tables with storage are handled by Phase 3.
5544 */
5545 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5546 tab->chgAccessMethod)
5548 break;
5549 case AT_SetTableSpace: /* SET TABLESPACE */
5550
5551 /*
5552 * Only do this for partitioned tables and indexes, for which this
5553 * is just a catalog change. Other relation types which have
5554 * storage are handled by Phase 3.
5555 */
5556 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5557 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5559
5560 break;
5561 case AT_SetRelOptions: /* SET (...) */
5562 case AT_ResetRelOptions: /* RESET (...) */
5563 case AT_ReplaceRelOptions: /* replace entire option list */
5564 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5565 break;
5566 case AT_EnableTrig: /* ENABLE TRIGGER name */
5569 cmd->recurse,
5570 lockmode);
5571 break;
5572 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5574 TRIGGER_FIRES_ALWAYS, false,
5575 cmd->recurse,
5576 lockmode);
5577 break;
5578 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5581 cmd->recurse,
5582 lockmode);
5583 break;
5584 case AT_DisableTrig: /* DISABLE TRIGGER name */
5586 TRIGGER_DISABLED, false,
5587 cmd->recurse,
5588 lockmode);
5589 break;
5590 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5593 cmd->recurse,
5594 lockmode);
5595 break;
5596 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5598 TRIGGER_DISABLED, false,
5599 cmd->recurse,
5600 lockmode);
5601 break;
5602 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5605 cmd->recurse,
5606 lockmode);
5607 break;
5608 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5610 TRIGGER_DISABLED, true,
5611 cmd->recurse,
5612 lockmode);
5613 break;
5614
5615 case AT_EnableRule: /* ENABLE RULE name */
5616 ATExecEnableDisableRule(rel, cmd->name,
5617 RULE_FIRES_ON_ORIGIN, lockmode);
5618 break;
5619 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5620 ATExecEnableDisableRule(rel, cmd->name,
5621 RULE_FIRES_ALWAYS, lockmode);
5622 break;
5623 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5624 ATExecEnableDisableRule(rel, cmd->name,
5625 RULE_FIRES_ON_REPLICA, lockmode);
5626 break;
5627 case AT_DisableRule: /* DISABLE RULE name */
5628 ATExecEnableDisableRule(rel, cmd->name,
5629 RULE_DISABLED, lockmode);
5630 break;
5631
5632 case AT_AddInherit:
5633 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5634 break;
5635 case AT_DropInherit:
5636 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5637 break;
5638 case AT_AddOf:
5639 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5640 break;
5641 case AT_DropOf:
5642 ATExecDropOf(rel, lockmode);
5643 break;
5644 case AT_ReplicaIdentity:
5645 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5646 break;
5648 ATExecSetRowSecurity(rel, true);
5649 break;
5651 ATExecSetRowSecurity(rel, false);
5652 break;
5655 break;
5658 break;
5659 case AT_GenericOptions:
5660 ATExecGenericOptions(rel, (List *) cmd->def);
5661 break;
5662 case AT_AttachPartition:
5663 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5664 cur_pass, context);
5665 Assert(cmd != NULL);
5666 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5667 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5668 context);
5669 else
5670 address = ATExecAttachPartitionIdx(wqueue, rel,
5671 ((PartitionCmd *) cmd->def)->name);
5672 break;
5673 case AT_DetachPartition:
5674 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5675 cur_pass, context);
5676 Assert(cmd != NULL);
5677 /* ATPrepCmd ensures it must be a table */
5678 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5679 address = ATExecDetachPartition(wqueue, tab, rel,
5680 ((PartitionCmd *) cmd->def)->name,
5681 ((PartitionCmd *) cmd->def)->concurrent);
5682 break;
5684 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5685 break;
5686 case AT_MergePartitions:
5687 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5688 cur_pass, context);
5689 Assert(cmd != NULL);
5690 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5691 ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5692 context);
5693 break;
5694 case AT_SplitPartition:
5695 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5696 cur_pass, context);
5697 Assert(cmd != NULL);
5698 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5699 ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5700 context);
5701 break;
5702 default: /* oops */
5703 elog(ERROR, "unrecognized alter table type: %d",
5704 (int) cmd->subtype);
5705 break;
5706 }
5707
5708 /*
5709 * Report the subcommand to interested event triggers.
5710 */
5711 if (cmd)
5712 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5713
5714 /*
5715 * Bump the command counter to ensure the next subcommand in the sequence
5716 * can see the changes so far
5717 */
5719}
5720
5721/*
5722 * ATParseTransformCmd: perform parse transformation for one subcommand
5723 *
5724 * Returns the transformed subcommand tree, if there is one, else NULL.
5725 *
5726 * The parser may hand back additional AlterTableCmd(s) and/or other
5727 * utility statements, either before or after the original subcommand.
5728 * Other AlterTableCmds are scheduled into the appropriate slot of the
5729 * AlteredTableInfo (they had better be for later passes than the current one).
5730 * Utility statements that are supposed to happen before the AlterTableCmd
5731 * are executed immediately. Those that are supposed to happen afterwards
5732 * are added to the tab->afterStmts list to be done at the very end.
5733 */
5734static AlterTableCmd *
5736 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5737 AlterTablePass cur_pass, AlterTableUtilityContext *context)
5738{
5739 AlterTableCmd *newcmd = NULL;
5741 List *beforeStmts;
5742 List *afterStmts;
5743 ListCell *lc;
5744
5745 /* Gin up an AlterTableStmt with just this subcommand and this table */
5746 atstmt->relation =
5749 -1);
5750 atstmt->relation->inh = recurse;
5751 atstmt->cmds = list_make1(cmd);
5752 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5753 atstmt->missing_ok = false;
5754
5755 /* Transform the AlterTableStmt */
5757 atstmt,
5758 context->queryString,
5759 &beforeStmts,
5760 &afterStmts);
5761
5762 /* Execute any statements that should happen before these subcommand(s) */
5763 foreach(lc, beforeStmts)
5764 {
5765 Node *stmt = (Node *) lfirst(lc);
5766
5769 }
5770
5771 /* Examine the transformed subcommands and schedule them appropriately */
5772 foreach(lc, atstmt->cmds)
5773 {
5775 AlterTablePass pass;
5776
5777 /*
5778 * This switch need only cover the subcommand types that can be added
5779 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5780 * executing the subcommand immediately, as a substitute for the
5781 * original subcommand. (Note, however, that this does cause
5782 * AT_AddConstraint subcommands to be rescheduled into later passes,
5783 * which is important for index and foreign key constraints.)
5784 *
5785 * We assume we needn't do any phase-1 checks for added subcommands.
5786 */
5787 switch (cmd2->subtype)
5788 {
5789 case AT_AddIndex:
5790 pass = AT_PASS_ADD_INDEX;
5791 break;
5794 break;
5795 case AT_AddConstraint:
5796 /* Recursion occurs during execution phase */
5797 if (recurse)
5798 cmd2->recurse = true;
5799 switch (castNode(Constraint, cmd2->def)->contype)
5800 {
5801 case CONSTR_NOTNULL:
5802 pass = AT_PASS_COL_ATTRS;
5803 break;
5804 case CONSTR_PRIMARY:
5805 case CONSTR_UNIQUE:
5806 case CONSTR_EXCLUSION:
5808 break;
5809 default:
5811 break;
5812 }
5813 break;
5815 /* This command never recurses */
5816 /* No command-specific prep needed */
5817 pass = AT_PASS_MISC;
5818 break;
5819 default:
5820 pass = cur_pass;
5821 break;
5822 }
5823
5824 if (pass < cur_pass)
5825 {
5826 /* Cannot schedule into a pass we already finished */
5827 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5828 pass);
5829 }
5830 else if (pass > cur_pass)
5831 {
5832 /* OK, queue it up for later */
5833 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5834 }
5835 else
5836 {
5837 /*
5838 * We should see at most one subcommand for the current pass,
5839 * which is the transformed version of the original subcommand.
5840 */
5841 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5842 {
5843 /* Found the transformed version of our subcommand */
5844 newcmd = cmd2;
5845 }
5846 else
5847 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5848 pass);
5849 }
5850 }
5851
5852 /* Queue up any after-statements to happen at the end */
5853 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5854
5855 return newcmd;
5856}
5857
5858/*
5859 * ATRewriteTables: ALTER TABLE phase 3
5860 */
5861static void
5862ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5863 AlterTableUtilityContext *context)
5864{
5865 ListCell *ltab;
5866
5867 /* Go through each table that needs to be checked or rewritten */
5868 foreach(ltab, *wqueue)
5869 {
5870 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5871
5872 /* Relations without storage may be ignored here */
5873 if (!RELKIND_HAS_STORAGE(tab->relkind))
5874 continue;
5875
5876 /*
5877 * If we change column data types, the operation has to be propagated
5878 * to tables that use this table's rowtype as a column type.
5879 * tab->newvals will also be non-NULL in the case where we're adding a
5880 * column with a default. We choose to forbid that case as well,
5881 * since composite types might eventually support defaults.
5882 *
5883 * (Eventually we'll probably need to check for composite type
5884 * dependencies even when we're just scanning the table without a
5885 * rewrite, but at the moment a composite type does not enforce any
5886 * constraints, so it's not necessary/appropriate to enforce them just
5887 * during ALTER.)
5888 */
5889 if (tab->newvals != NIL || tab->rewrite > 0)
5890 {
5891 Relation rel;
5892
5893 rel = table_open(tab->relid, NoLock);
5894 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5895 table_close(rel, NoLock);
5896 }
5897
5898 /*
5899 * We only need to rewrite the table if at least one column needs to
5900 * be recomputed, or we are changing its persistence or access method.
5901 *
5902 * There are two reasons for requiring a rewrite when changing
5903 * persistence: on one hand, we need to ensure that the buffers
5904 * belonging to each of the two relations are marked with or without
5905 * BM_PERMANENT properly. On the other hand, since rewriting creates
5906 * and assigns a new relfilenumber, we automatically create or drop an
5907 * init fork for the relation as appropriate.
5908 */
5909 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5910 {
5911 /* Build a temporary relation and copy data */
5912 Relation OldHeap;
5913 Oid OIDNewHeap;
5914 Oid NewAccessMethod;
5915 Oid NewTableSpace;
5916 char persistence;
5917
5918 OldHeap = table_open(tab->relid, NoLock);
5919
5920 /*
5921 * We don't support rewriting of system catalogs; there are too
5922 * many corner cases and too little benefit. In particular this
5923 * is certainly not going to work for mapped catalogs.
5924 */
5925 if (IsSystemRelation(OldHeap))
5926 ereport(ERROR,
5927 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5928 errmsg("cannot rewrite system relation \"%s\"",
5929 RelationGetRelationName(OldHeap))));
5930
5931 if (RelationIsUsedAsCatalogTable(OldHeap))
5932 ereport(ERROR,
5933 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5934 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5935 RelationGetRelationName(OldHeap))));
5936
5937 /*
5938 * Don't allow rewrite on temp tables of other backends ... their
5939 * local buffer manager is not going to cope. (This is redundant
5940 * with the check in CheckAlterTableIsSafe, but for safety we'll
5941 * check here too.)
5942 */
5943 if (RELATION_IS_OTHER_TEMP(OldHeap))
5944 ereport(ERROR,
5945 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5946 errmsg("cannot rewrite temporary tables of other sessions")));
5947
5948 /*
5949 * Select destination tablespace (same as original unless user
5950 * requested a change)
5951 */
5952 if (tab->newTableSpace)
5953 NewTableSpace = tab->newTableSpace;
5954 else
5955 NewTableSpace = OldHeap->rd_rel->reltablespace;
5956
5957 /*
5958 * Select destination access method (same as original unless user
5959 * requested a change)
5960 */
5961 if (tab->chgAccessMethod)
5962 NewAccessMethod = tab->newAccessMethod;
5963 else
5964 NewAccessMethod = OldHeap->rd_rel->relam;
5965
5966 /*
5967 * Select persistence of transient table (same as original unless
5968 * user requested a change)
5969 */
5970 persistence = tab->chgPersistence ?
5971 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5972
5973 table_close(OldHeap, NoLock);
5974
5975 /*
5976 * Fire off an Event Trigger now, before actually rewriting the
5977 * table.
5978 *
5979 * We don't support Event Trigger for nested commands anywhere,
5980 * here included, and parsetree is given NULL when coming from
5981 * AlterTableInternal.
5982 *
5983 * And fire it only once.
5984 */
5985 if (parsetree)
5986 EventTriggerTableRewrite((Node *) parsetree,
5987 tab->relid,
5988 tab->rewrite);
5989
5990 /*
5991 * Create transient table that will receive the modified data.
5992 *
5993 * Ensure it is marked correctly as logged or unlogged. We have
5994 * to do this here so that buffers for the new relfilenumber will
5995 * have the right persistence set, and at the same time ensure
5996 * that the original filenumbers's buffers will get read in with
5997 * the correct setting (i.e. the original one). Otherwise a
5998 * rollback after the rewrite would possibly result with buffers
5999 * for the original filenumbers having the wrong persistence
6000 * setting.
6001 *
6002 * NB: This relies on swap_relation_files() also swapping the
6003 * persistence. That wouldn't work for pg_class, but that can't be
6004 * unlogged anyway.
6005 */
6006 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
6007 persistence, lockmode);
6008
6009 /*
6010 * Copy the heap data into the new table with the desired
6011 * modifications, and test the current data within the table
6012 * against new constraints generated by ALTER TABLE commands.
6013 */
6014 ATRewriteTable(tab, OIDNewHeap);
6015
6016 /*
6017 * Swap the physical files of the old and new heaps, then rebuild
6018 * indexes and discard the old heap. We can use RecentXmin for
6019 * the table's new relfrozenxid because we rewrote all the tuples
6020 * in ATRewriteTable, so no older Xid remains in the table. Also,
6021 * we never try to swap toast tables by content, since we have no
6022 * interest in letting this code work on system catalogs.
6023 */
6024 finish_heap_swap(tab->relid, OIDNewHeap,
6025 false, false, true,
6027 RecentXmin,
6029 persistence);
6030
6031 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
6032 }
6033 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6034 {
6035 if (tab->chgPersistence)
6037 }
6038 else
6039 {
6040 /*
6041 * If required, test the current data within the table against new
6042 * constraints generated by ALTER TABLE commands, but don't
6043 * rebuild data.
6044 */
6045 if (tab->constraints != NIL || tab->verify_new_notnull ||
6046 tab->partition_constraint != NULL)
6048
6049 /*
6050 * If we had SET TABLESPACE but no reason to reconstruct tuples,
6051 * just do a block-by-block copy.
6052 */
6053 if (tab->newTableSpace)
6054 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6055 }
6056
6057 /*
6058 * Also change persistence of owned sequences, so that it matches the
6059 * table persistence.
6060 */
6061 if (tab->chgPersistence)
6062 {
6063 List *seqlist = getOwnedSequences(tab->relid);
6064 ListCell *lc;
6065
6066 foreach(lc, seqlist)
6067 {
6068 Oid seq_relid = lfirst_oid(lc);
6069
6071 }
6072 }
6073 }
6074
6075 /*
6076 * Foreign key constraints are checked in a final pass, since (a) it's
6077 * generally best to examine each one separately, and (b) it's at least
6078 * theoretically possible that we have changed both relations of the
6079 * foreign key, and we'd better have finished both rewrites before we try
6080 * to read the tables.
6081 */
6082 foreach(ltab, *wqueue)
6083 {
6084 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6085 Relation rel = NULL;
6086 ListCell *lcon;
6087
6088 /* Relations without storage may be ignored here too */
6089 if (!RELKIND_HAS_STORAGE(tab->relkind))
6090 continue;
6091
6092 foreach(lcon, tab->constraints)
6093 {
6094 NewConstraint *con = lfirst(lcon);
6095
6096 if (con->contype == CONSTR_FOREIGN)
6097 {
6098 Constraint *fkconstraint = (Constraint *) con->qual;
6099 Relation refrel;
6100
6101 if (rel == NULL)
6102 {
6103 /* Long since locked, no need for another */
6104 rel = table_open(tab->relid, NoLock);
6105 }
6106
6107 refrel = table_open(con->refrelid, RowShareLock);
6108
6109 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6110 con->refindid,
6111 con->conid,
6112 con->conwithperiod);
6113
6114 /*
6115 * No need to mark the constraint row as validated, we did
6116 * that when we inserted the row earlier.
6117 */
6118
6119 table_close(refrel, NoLock);
6120 }
6121 }
6122
6123 if (rel)
6124 table_close(rel, NoLock);
6125 }
6126
6127 /* Finally, run any afterStmts that were queued up */
6128 foreach(ltab, *wqueue)
6129 {
6130 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6131 ListCell *lc;
6132
6133 foreach(lc, tab->afterStmts)
6134 {
6135 Node *stmt = (Node *) lfirst(lc);
6136
6139 }
6140 }
6141}
6142
6143/*
6144 * ATRewriteTable: scan or rewrite one table
6145 *
6146 * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6147 * must already hold AccessExclusiveLock on it.
6148 */
6149static void
6151{
6152 Relation oldrel;
6153 Relation newrel;
6154 TupleDesc oldTupDesc;
6155 TupleDesc newTupDesc;
6156 bool needscan = false;
6157 List *notnull_attrs;
6158 List *notnull_virtual_attrs;
6159 int i;
6160 ListCell *l;
6161 EState *estate;
6162 CommandId mycid;
6163 BulkInsertState bistate;
6164 int ti_options;
6165 ExprState *partqualstate = NULL;
6166
6167 /*
6168 * Open the relation(s). We have surely already locked the existing
6169 * table.
6170 */
6171 oldrel = table_open(tab->relid, NoLock);
6172 oldTupDesc = tab->oldDesc;
6173 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6174
6175 if (OidIsValid(OIDNewHeap))
6176 {
6178 false));
6179 newrel = table_open(OIDNewHeap, NoLock);
6180 }
6181 else
6182 newrel = NULL;
6183
6184 /*
6185 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6186 * is empty, so don't bother using it.
6187 */
6188 if (newrel)
6189 {
6190 mycid = GetCurrentCommandId(true);
6191 bistate = GetBulkInsertState();
6192 ti_options = TABLE_INSERT_SKIP_FSM;
6193 }
6194 else
6195 {
6196 /* keep compiler quiet about using these uninitialized */
6197 mycid = 0;
6198 bistate = NULL;
6199 ti_options = 0;
6200 }
6201
6202 /*
6203 * Generate the constraint and default execution states
6204 */
6205
6206 estate = CreateExecutorState();
6207
6208 /* Build the needed expression execution states */
6209 foreach(l, tab->constraints)
6210 {
6211 NewConstraint *con = lfirst(l);
6212
6213 switch (con->contype)
6214 {
6215 case CONSTR_CHECK:
6216 needscan = true;
6217 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6218 break;
6219 case CONSTR_FOREIGN:
6220 /* Nothing to do here */
6221 break;
6222 default:
6223 elog(ERROR, "unrecognized constraint type: %d",
6224 (int) con->contype);
6225 }
6226 }
6227
6228 /* Build expression execution states for partition check quals */
6229 if (tab->partition_constraint)
6230 {
6231 needscan = true;
6232 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6233 }
6234
6235 foreach(l, tab->newvals)
6236 {
6237 NewColumnValue *ex = lfirst(l);
6238
6239 /* expr already planned */
6240 ex->exprstate = ExecInitExpr(ex->expr, NULL);
6241 }
6242
6243 notnull_attrs = notnull_virtual_attrs = NIL;
6244 if (newrel || tab->verify_new_notnull)
6245 {
6246 /*
6247 * If we are rebuilding the tuples OR if we added any new but not
6248 * verified not-null constraints, check all *valid* not-null
6249 * constraints. This is a bit of overkill but it minimizes risk of
6250 * bugs.
6251 *
6252 * notnull_attrs does *not* collect attribute numbers for valid
6253 * not-null constraints over virtual generated columns; instead, they
6254 * are collected in notnull_virtual_attrs for verification elsewhere.
6255 */
6256 for (i = 0; i < newTupDesc->natts; i++)
6257 {
6258 CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6259
6260 if (attr->attnullability == ATTNULLABLE_VALID &&
6261 !attr->attisdropped)
6262 {
6263 Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6264
6265 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6266 notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6267 else
6268 notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6269 wholeatt->attnum);
6270 }
6271 }
6272 if (notnull_attrs || notnull_virtual_attrs)
6273 needscan = true;
6274 }
6275
6276 if (newrel || needscan)
6277 {
6278 ExprContext *econtext;
6279 TupleTableSlot *oldslot;
6280 TupleTableSlot *newslot;
6281 TableScanDesc scan;
6282 MemoryContext oldCxt;
6283 List *dropped_attrs = NIL;
6284 ListCell *lc;
6285 Snapshot snapshot;
6286 ResultRelInfo *rInfo = NULL;
6287
6288 /*
6289 * When adding or changing a virtual generated column with a not-null
6290 * constraint, we need to evaluate whether the generation expression
6291 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6292 * prepare a dummy ResultRelInfo.
6293 */
6294 if (notnull_virtual_attrs != NIL)
6295 {
6296 MemoryContext oldcontext;
6297
6298 Assert(newTupDesc->constr->has_generated_virtual);
6299 Assert(newTupDesc->constr->has_not_null);
6300 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6301 rInfo = makeNode(ResultRelInfo);
6302 InitResultRelInfo(rInfo,
6303 oldrel,
6304 0, /* dummy rangetable index */
6305 NULL,
6306 estate->es_instrument);
6307 MemoryContextSwitchTo(oldcontext);
6308 }
6309
6310 if (newrel)
6312 (errmsg_internal("rewriting table \"%s\"",
6313 RelationGetRelationName(oldrel))));
6314 else
6316 (errmsg_internal("verifying table \"%s\"",
6317 RelationGetRelationName(oldrel))));
6318
6319 if (newrel)
6320 {
6321 /*
6322 * All predicate locks on the tuples or pages are about to be made
6323 * invalid, because we move tuples around. Promote them to
6324 * relation locks.
6325 */
6327 }
6328
6329 econtext = GetPerTupleExprContext(estate);
6330
6331 /*
6332 * Create necessary tuple slots. When rewriting, two slots are needed,
6333 * otherwise one suffices. In the case where one slot suffices, we
6334 * need to use the new tuple descriptor, otherwise some constraints
6335 * can't be evaluated. Note that even when the tuple layout is the
6336 * same and no rewrite is required, the tupDescs might not be
6337 * (consider ADD COLUMN without a default).
6338 */
6339 if (tab->rewrite)
6340 {
6341 Assert(newrel != NULL);
6342 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6343 table_slot_callbacks(oldrel));
6344 newslot = MakeSingleTupleTableSlot(newTupDesc,
6345 table_slot_callbacks(newrel));
6346
6347 /*
6348 * Set all columns in the new slot to NULL initially, to ensure
6349 * columns added as part of the rewrite are initialized to NULL.
6350 * That is necessary as tab->newvals will not contain an
6351 * expression for columns with a NULL default, e.g. when adding a
6352 * column without a default together with a column with a default
6353 * requiring an actual rewrite.
6354 */
6355 ExecStoreAllNullTuple(newslot);
6356 }
6357 else
6358 {
6359 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6360 table_slot_callbacks(oldrel));
6361 newslot = NULL;
6362 }
6363
6364 /*
6365 * Any attributes that are dropped according to the new tuple
6366 * descriptor can be set to NULL. We precompute the list of dropped
6367 * attributes to avoid needing to do so in the per-tuple loop.
6368 */
6369 for (i = 0; i < newTupDesc->natts; i++)
6370 {
6371 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6372 dropped_attrs = lappend_int(dropped_attrs, i);
6373 }
6374
6375 /*
6376 * Scan through the rows, generating a new row if needed and then
6377 * checking all the constraints.
6378 */
6379 snapshot = RegisterSnapshot(GetLatestSnapshot());
6380 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6381
6382 /*
6383 * Switch to per-tuple memory context and reset it for each tuple
6384 * produced, so we don't leak memory.
6385 */
6387
6388 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6389 {
6390 TupleTableSlot *insertslot;
6391
6392 if (tab->rewrite > 0)
6393 {
6394 /* Extract data from old tuple */
6395 slot_getallattrs(oldslot);
6396 ExecClearTuple(newslot);
6397
6398 /* copy attributes */
6399 memcpy(newslot->tts_values, oldslot->tts_values,
6400 sizeof(Datum) * oldslot->tts_nvalid);
6401 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6402 sizeof(bool) * oldslot->tts_nvalid);
6403
6404 /* Set dropped attributes to null in new tuple */
6405 foreach(lc, dropped_attrs)
6406 newslot->tts_isnull[lfirst_int(lc)] = true;
6407
6408 /*
6409 * Constraints and GENERATED expressions might reference the
6410 * tableoid column, so fill tts_tableOid with the desired
6411 * value. (We must do this each time, because it gets
6412 * overwritten with newrel's OID during storing.)
6413 */
6414 newslot->tts_tableOid = RelationGetRelid(oldrel);
6415
6416 /*
6417 * Process supplied expressions to replace selected columns.
6418 *
6419 * First, evaluate expressions whose inputs come from the old
6420 * tuple.
6421 */
6422 econtext->ecxt_scantuple = oldslot;
6423
6424 foreach(l, tab->newvals)
6425 {
6426 NewColumnValue *ex = lfirst(l);
6427
6428 if (ex->is_generated)
6429 continue;
6430
6431 newslot->tts_values[ex->attnum - 1]
6432 = ExecEvalExpr(ex->exprstate,
6433 econtext,
6434 &newslot->tts_isnull[ex->attnum - 1]);
6435 }
6436
6437 ExecStoreVirtualTuple(newslot);
6438
6439 /*
6440 * Now, evaluate any expressions whose inputs come from the
6441 * new tuple. We assume these columns won't reference each
6442 * other, so that there's no ordering dependency.
6443 */
6444 econtext->ecxt_scantuple = newslot;
6445
6446 foreach(l, tab->newvals)
6447 {
6448 NewColumnValue *ex = lfirst(l);
6449
6450 if (!ex->is_generated)
6451 continue;
6452
6453 newslot->tts_values[ex->attnum - 1]
6454 = ExecEvalExpr(ex->exprstate,
6455 econtext,
6456 &newslot->tts_isnull[ex->attnum - 1]);
6457 }
6458
6459 insertslot = newslot;
6460 }
6461 else
6462 {
6463 /*
6464 * If there's no rewrite, old and new table are guaranteed to
6465 * have the same AM, so we can just use the old slot to verify
6466 * new constraints etc.
6467 */
6468 insertslot = oldslot;
6469 }
6470
6471 /* Now check any constraints on the possibly-changed tuple */
6472 econtext->ecxt_scantuple = insertslot;
6473
6474 foreach_int(attn, notnull_attrs)
6475 {
6476 if (slot_attisnull(insertslot, attn))
6477 {
6478 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6479
6480 ereport(ERROR,
6481 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6482 errmsg("column \"%s\" of relation \"%s\" contains null values",
6483 NameStr(attr->attname),
6484 RelationGetRelationName(oldrel)),
6485 errtablecol(oldrel, attn)));
6486 }
6487 }
6488
6489 if (notnull_virtual_attrs != NIL)
6490 {
6492
6493 attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6494 estate,
6495 notnull_virtual_attrs);
6497 {
6498 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6499
6500 ereport(ERROR,
6501 errcode(ERRCODE_NOT_NULL_VIOLATION),
6502 errmsg("column \"%s\" of relation \"%s\" contains null values",
6503 NameStr(attr->attname),
6504 RelationGetRelationName(oldrel)),
6505 errtablecol(oldrel, attnum));
6506 }
6507 }
6508
6509 foreach(l, tab->constraints)
6510 {
6511 NewConstraint *con = lfirst(l);
6512
6513 switch (con->contype)
6514 {
6515 case CONSTR_CHECK:
6516 if (!ExecCheck(con->qualstate, econtext))
6517 ereport(ERROR,
6518 (errcode(ERRCODE_CHECK_VIOLATION),
6519 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6520 con->name,
6521 RelationGetRelationName(oldrel)),
6522 errtableconstraint(oldrel, con->name)));
6523 break;
6524 case CONSTR_NOTNULL:
6525 case CONSTR_FOREIGN:
6526 /* Nothing to do here */
6527 break;
6528 default:
6529 elog(ERROR, "unrecognized constraint type: %d",
6530 (int) con->contype);
6531 }
6532 }
6533
6534 if (partqualstate && !ExecCheck(partqualstate, econtext))
6535 {
6536 if (tab->validate_default)
6537 ereport(ERROR,
6538 (errcode(ERRCODE_CHECK_VIOLATION),
6539 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6540 RelationGetRelationName(oldrel)),
6541 errtable(oldrel)));
6542 else
6543 ereport(ERROR,
6544 (errcode(ERRCODE_CHECK_VIOLATION),
6545 errmsg("partition constraint of relation \"%s\" is violated by some row",
6546 RelationGetRelationName(oldrel)),
6547 errtable(oldrel)));
6548 }
6549
6550 /* Write the tuple out to the new relation */
6551 if (newrel)
6552 table_tuple_insert(newrel, insertslot, mycid,
6553 ti_options, bistate);
6554
6555 ResetExprContext(econtext);
6556
6558 }
6559
6560 MemoryContextSwitchTo(oldCxt);
6561 table_endscan(scan);
6562 UnregisterSnapshot(snapshot);
6563
6565 if (newslot)
6567 }
6568
6569 FreeExecutorState(estate);
6570
6571 table_close(oldrel, NoLock);
6572 if (newrel)
6573 {
6574 FreeBulkInsertState(bistate);
6575
6576 table_finish_bulk_insert(newrel, ti_options);
6577
6578 table_close(newrel, NoLock);
6579 }
6580}
6581
6582/*
6583 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6584 */
6585static AlteredTableInfo *
6587{
6588 Oid relid = RelationGetRelid(rel);
6589 AlteredTableInfo *tab;
6590 ListCell *ltab;
6591
6592 foreach(ltab, *wqueue)
6593 {
6594 tab = (AlteredTableInfo *) lfirst(ltab);
6595 if (tab->relid == relid)
6596 return tab;
6597 }
6598
6599 /*
6600 * Not there, so add it. Note that we make a copy of the relation's
6601 * existing descriptor before anything interesting can happen to it.
6602 */
6604 tab->relid = relid;
6605 tab->rel = NULL; /* set later */
6606 tab->relkind = rel->rd_rel->relkind;
6609 tab->chgAccessMethod = false;
6611 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6612 tab->chgPersistence = false;
6613
6614 *wqueue = lappend(*wqueue, tab);
6615
6616 return tab;
6617}
6618
6619static const char *
6621{
6622 switch (cmdtype)
6623 {
6624 case AT_AddColumn:
6625 case AT_AddColumnToView:
6626 return "ADD COLUMN";
6627 case AT_ColumnDefault:
6629 return "ALTER COLUMN ... SET DEFAULT";
6630 case AT_DropNotNull:
6631 return "ALTER COLUMN ... DROP NOT NULL";
6632 case AT_SetNotNull:
6633 return "ALTER COLUMN ... SET NOT NULL";
6634 case AT_SetExpression:
6635 return "ALTER COLUMN ... SET EXPRESSION";
6636 case AT_DropExpression:
6637 return "ALTER COLUMN ... DROP EXPRESSION";
6638 case AT_SetStatistics:
6639 return "ALTER COLUMN ... SET STATISTICS";
6640 case AT_SetOptions:
6641 return "ALTER COLUMN ... SET";
6642 case AT_ResetOptions:
6643 return "ALTER COLUMN ... RESET";
6644 case AT_SetStorage:
6645 return "ALTER COLUMN ... SET STORAGE";
6646 case AT_SetCompression:
6647 return "ALTER COLUMN ... SET COMPRESSION";
6648 case AT_DropColumn:
6649 return "DROP COLUMN";
6650 case AT_AddIndex:
6651 case AT_ReAddIndex:
6652 return NULL; /* not real grammar */
6653 case AT_AddConstraint:
6654 case AT_ReAddConstraint:
6657 return "ADD CONSTRAINT";
6658 case AT_AlterConstraint:
6659 return "ALTER CONSTRAINT";
6661 return "VALIDATE CONSTRAINT";
6662 case AT_DropConstraint:
6663 return "DROP CONSTRAINT";
6664 case AT_ReAddComment:
6665 return NULL; /* not real grammar */
6666 case AT_AlterColumnType:
6667 return "ALTER COLUMN ... SET DATA TYPE";
6669 return "ALTER COLUMN ... OPTIONS";
6670 case AT_ChangeOwner:
6671 return "OWNER TO";
6672 case AT_ClusterOn:
6673 return "CLUSTER ON";
6674 case AT_DropCluster:
6675 return "SET WITHOUT CLUSTER";
6676 case AT_SetAccessMethod:
6677 return "SET ACCESS METHOD";
6678 case AT_SetLogged:
6679 return "SET LOGGED";
6680 case AT_SetUnLogged:
6681 return "SET UNLOGGED";
6682 case AT_DropOids:
6683 return "SET WITHOUT OIDS";
6684 case AT_SetTableSpace:
6685 return "SET TABLESPACE";
6686 case AT_SetRelOptions:
6687 return "SET";
6688 case AT_ResetRelOptions:
6689 return "RESET";
6691 return NULL; /* not real grammar */
6692 case AT_EnableTrig:
6693 return "ENABLE TRIGGER";
6695 return "ENABLE ALWAYS TRIGGER";
6697 return "ENABLE REPLICA TRIGGER";
6698 case AT_DisableTrig:
6699 return "DISABLE TRIGGER";
6700 case AT_EnableTrigAll:
6701 return "ENABLE TRIGGER ALL";
6702 case AT_DisableTrigAll:
6703 return "DISABLE TRIGGER ALL";
6704 case AT_EnableTrigUser:
6705 return "ENABLE TRIGGER USER";
6706 case AT_DisableTrigUser:
6707 return "DISABLE TRIGGER USER";
6708 case AT_EnableRule:
6709 return "ENABLE RULE";
6711 return "ENABLE ALWAYS RULE";
6713 return "ENABLE REPLICA RULE";
6714 case AT_DisableRule:
6715 return "DISABLE RULE";
6716 case AT_AddInherit:
6717 return "INHERIT";
6718 case AT_DropInherit:
6719 return "NO INHERIT";
6720 case AT_AddOf:
6721 return "OF";
6722 case AT_DropOf:
6723 return "NOT OF";
6724 case AT_ReplicaIdentity:
6725 return "REPLICA IDENTITY";
6727 return "ENABLE ROW SECURITY";
6729 return "DISABLE ROW SECURITY";
6731 return "FORCE ROW SECURITY";
6733 return "NO FORCE ROW SECURITY";
6734 case AT_GenericOptions:
6735 return "OPTIONS";
6736 case AT_AttachPartition:
6737 return "ATTACH PARTITION";
6738 case AT_DetachPartition:
6739 return "DETACH PARTITION";
6741 return "DETACH PARTITION ... FINALIZE";
6742 case AT_MergePartitions:
6743 return "MERGE PARTITIONS";
6744 case AT_SplitPartition:
6745 return "SPLIT PARTITION";
6746 case AT_AddIdentity:
6747 return "ALTER COLUMN ... ADD IDENTITY";
6748 case AT_SetIdentity:
6749 return "ALTER COLUMN ... SET";
6750 case AT_DropIdentity:
6751 return "ALTER COLUMN ... DROP IDENTITY";
6752 case AT_ReAddStatistics:
6753 return NULL; /* not real grammar */
6754 }
6755
6756 return NULL;
6757}
6758
6759/*
6760 * ATSimplePermissions
6761 *
6762 * - Ensure that it is a relation (or possibly a view)
6763 * - Ensure this user is the owner
6764 * - Ensure that it is not a system table
6765 */
6766static void
6767ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6768{
6769 int actual_target;
6770
6771 switch (rel->rd_rel->relkind)
6772 {
6773 case RELKIND_RELATION:
6774 actual_target = ATT_TABLE;
6775 break;
6776 case RELKIND_PARTITIONED_TABLE:
6777 actual_target = ATT_PARTITIONED_TABLE;
6778 break;
6779 case RELKIND_VIEW:
6780 actual_target = ATT_VIEW;
6781 break;
6782 case RELKIND_MATVIEW:
6783 actual_target = ATT_MATVIEW;
6784 break;
6785 case RELKIND_INDEX:
6786 actual_target = ATT_INDEX;
6787 break;
6788 case RELKIND_PARTITIONED_INDEX:
6789 actual_target = ATT_PARTITIONED_INDEX;
6790 break;
6791 case RELKIND_COMPOSITE_TYPE:
6792 actual_target = ATT_COMPOSITE_TYPE;
6793 break;
6794 case RELKIND_FOREIGN_TABLE:
6795 actual_target = ATT_FOREIGN_TABLE;
6796 break;
6797 case RELKIND_SEQUENCE:
6798 actual_target = ATT_SEQUENCE;
6799 break;
6800 default:
6801 actual_target = 0;
6802 break;
6803 }
6804
6805 /* Wrong target type? */
6806 if ((actual_target & allowed_targets) == 0)
6807 {
6808 const char *action_str = alter_table_type_to_string(cmdtype);
6809
6810 if (action_str)
6811 ereport(ERROR,
6812 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6813 /* translator: %s is a group of some SQL keywords */
6814 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6815 action_str, RelationGetRelationName(rel)),
6816 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6817 else
6818 /* internal error? */
6819 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6821 }
6822
6823 /* Permissions checks */
6824 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6827
6829 ereport(ERROR,
6830 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6831 errmsg("permission denied: \"%s\" is a system catalog",
6833}
6834
6835/*
6836 * ATSimpleRecursion
6837 *
6838 * Simple table recursion sufficient for most ALTER TABLE operations.
6839 * All direct and indirect children are processed in an unspecified order.
6840 * Note that if a child inherits from the original table via multiple
6841 * inheritance paths, it will be visited just once.
6842 */
6843static void
6845 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6846 AlterTableUtilityContext *context)
6847{
6848 /*
6849 * Propagate to children, if desired and if there are (or might be) any
6850 * children.
6851 */
6852 if (recurse && rel->rd_rel->relhassubclass)
6853 {
6854 Oid relid = RelationGetRelid(rel);
6855 ListCell *child;
6856 List *children;
6857
6858 children = find_all_inheritors(relid, lockmode, NULL);
6859
6860 /*
6861 * find_all_inheritors does the recursive search of the inheritance
6862 * hierarchy, so all we have to do is process all of the relids in the
6863 * list that it returns.
6864 */
6865 foreach(child, children)
6866 {
6867 Oid childrelid = lfirst_oid(child);
6868 Relation childrel;
6869
6870 if (childrelid == relid)
6871 continue;
6872 /* find_all_inheritors already got lock */
6873 childrel = relation_open(childrelid, NoLock);
6874 CheckAlterTableIsSafe(childrel);
6875 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6876 relation_close(childrel, NoLock);
6877 }
6878 }
6879}
6880
6881/*
6882 * Obtain list of partitions of the given table, locking them all at the given
6883 * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6884 *
6885 * This function is a no-op if the given relation is not a partitioned table;
6886 * in particular, nothing is done if it's a legacy inheritance parent.
6887 */
6888static void
6890{
6891 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6892 {
6893 List *inh;
6894 ListCell *cell;
6895
6896 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6897 /* first element is the parent rel; must ignore it */
6898 for_each_from(cell, inh, 1)
6899 {
6900 Relation childrel;
6901
6902 /* find_all_inheritors already got lock */
6903 childrel = table_open(lfirst_oid(cell), NoLock);
6904 CheckAlterTableIsSafe(childrel);
6905 table_close(childrel, NoLock);
6906 }
6907 list_free(inh);
6908 }
6909}
6910
6911/*
6912 * ATTypedTableRecursion
6913 *
6914 * Propagate ALTER TYPE operations to the typed tables of that type.
6915 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6916 * recursion to inheritance children of the typed tables.
6917 */
6918static void
6920 LOCKMODE lockmode, AlterTableUtilityContext *context)
6921{
6922 ListCell *child;
6923 List *children;
6924
6925 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6926
6927 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6929 cmd->behavior);
6930
6931 foreach(child, children)
6932 {
6933 Oid childrelid = lfirst_oid(child);
6934 Relation childrel;
6935
6936 childrel = relation_open(childrelid, lockmode);
6937 CheckAlterTableIsSafe(childrel);
6938 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6939 relation_close(childrel, NoLock);
6940 }
6941}
6942
6943
6944/*
6945 * find_composite_type_dependencies
6946 *
6947 * Check to see if the type "typeOid" is being used as a column in some table
6948 * (possibly nested several levels deep in composite types, arrays, etc!).
6949 * Eventually, we'd like to propagate the check or rewrite operation
6950 * into such tables, but for now, just error out if we find any.
6951 *
6952 * Caller should provide either the associated relation of a rowtype,
6953 * or a type name (not both) for use in the error message, if any.
6954 *
6955 * Note that "typeOid" is not necessarily a composite type; it could also be
6956 * another container type such as an array or range, or a domain over one of
6957 * these things. The name of this function is therefore somewhat historical,
6958 * but it's not worth changing.
6959 *
6960 * We assume that functions and views depending on the type are not reasons
6961 * to reject the ALTER. (How safe is this really?)
6962 */
6963void
6965 const char *origTypeName)
6966{
6967 Relation depRel;
6968 ScanKeyData key[2];
6969 SysScanDesc depScan;
6970 HeapTuple depTup;
6971
6972 /* since this function recurses, it could be driven to stack overflow */
6974
6975 /*
6976 * We scan pg_depend to find those things that depend on the given type.
6977 * (We assume we can ignore refobjsubid for a type.)
6978 */
6979 depRel = table_open(DependRelationId, AccessShareLock);
6980
6981 ScanKeyInit(&key[0],
6982 Anum_pg_depend_refclassid,
6983 BTEqualStrategyNumber, F_OIDEQ,
6984 ObjectIdGetDatum(TypeRelationId));
6985 ScanKeyInit(&key[1],
6986 Anum_pg_depend_refobjid,
6987 BTEqualStrategyNumber, F_OIDEQ,
6988 ObjectIdGetDatum(typeOid));
6989
6990 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6991 NULL, 2, key);
6992
6993 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6994 {
6995 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6996 Relation rel;
6997 TupleDesc tupleDesc;
6999
7000 /* Check for directly dependent types */
7001 if (pg_depend->classid == TypeRelationId)
7002 {
7003 /*
7004 * This must be an array, domain, or range containing the given
7005 * type, so recursively check for uses of this type. Note that
7006 * any error message will mention the original type not the
7007 * container; this is intentional.
7008 */
7009 find_composite_type_dependencies(pg_depend->objid,
7010 origRelation, origTypeName);
7011 continue;
7012 }
7013
7014 /* Else, ignore dependees that aren't relations */
7015 if (pg_depend->classid != RelationRelationId)
7016 continue;
7017
7018 rel = relation_open(pg_depend->objid, AccessShareLock);
7019 tupleDesc = RelationGetDescr(rel);
7020
7021 /*
7022 * If objsubid identifies a specific column, refer to that in error
7023 * messages. Otherwise, search to see if there's a user column of the
7024 * type. (We assume system columns are never of interesting types.)
7025 * The search is needed because an index containing an expression
7026 * column of the target type will just be recorded as a whole-relation
7027 * dependency. If we do not find a column of the type, the dependency
7028 * must indicate that the type is transiently referenced in an index
7029 * expression but not stored on disk, which we assume is OK, just as
7030 * we do for references in views. (It could also be that the target
7031 * type is embedded in some container type that is stored in an index
7032 * column, but the previous recursion should catch such cases.)
7033 */
7034 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
7035 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7036 else
7037 {
7038 att = NULL;
7039 for (int attno = 1; attno <= tupleDesc->natts; attno++)
7040 {
7041 att = TupleDescAttr(tupleDesc, attno - 1);
7042 if (att->atttypid == typeOid && !att->attisdropped)
7043 break;
7044 att = NULL;
7045 }
7046 if (att == NULL)
7047 {
7048 /* No such column, so assume OK */
7050 continue;
7051 }
7052 }
7053
7054 /*
7055 * We definitely should reject if the relation has storage. If it's
7056 * partitioned, then perhaps we don't have to reject: if there are
7057 * partitions then we'll fail when we find one, else there is no
7058 * stored data to worry about. However, it's possible that the type
7059 * change would affect conclusions about whether the type is sortable
7060 * or hashable and thus (if it's a partitioning column) break the
7061 * partitioning rule. For now, reject for partitioned rels too.
7062 */
7063 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7064 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7065 {
7066 if (origTypeName)
7067 ereport(ERROR,
7068 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7069 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7070 origTypeName,
7072 NameStr(att->attname))));
7073 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7074 ereport(ERROR,
7075 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7076 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7077 RelationGetRelationName(origRelation),
7079 NameStr(att->attname))));
7080 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7081 ereport(ERROR,
7082 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7083 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7084 RelationGetRelationName(origRelation),
7086 NameStr(att->attname))));
7087 else
7088 ereport(ERROR,
7089 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7090 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7091 RelationGetRelationName(origRelation),
7093 NameStr(att->attname))));
7094 }
7095 else if (OidIsValid(rel->rd_rel->reltype))
7096 {
7097 /*
7098 * A view or composite type itself isn't a problem, but we must
7099 * recursively check for indirect dependencies via its rowtype.
7100 */
7102 origRelation, origTypeName);
7103 }
7104
7106 }
7107
7108 systable_endscan(depScan);
7109
7111}
7112
7113
7114/*
7115 * find_typed_table_dependencies
7116 *
7117 * Check to see if a composite type is being used as the type of a
7118 * typed table. Abort if any are found and behavior is RESTRICT.
7119 * Else return the list of tables.
7120 */
7121static List *
7122find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7123{
7124 Relation classRel;
7125 ScanKeyData key[1];
7126 TableScanDesc scan;
7127 HeapTuple tuple;
7128 List *result = NIL;
7129
7130 classRel = table_open(RelationRelationId, AccessShareLock);
7131
7132 ScanKeyInit(&key[0],
7133 Anum_pg_class_reloftype,
7134 BTEqualStrategyNumber, F_OIDEQ,
7135 ObjectIdGetDatum(typeOid));
7136
7137 scan = table_beginscan_catalog(classRel, 1, key);
7138
7139 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7140 {
7141 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7142
7143 if (behavior == DROP_RESTRICT)
7144 ereport(ERROR,
7145 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7146 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7147 typeName),
7148 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7149 else
7150 result = lappend_oid(result, classform->oid);
7151 }
7152
7153 table_endscan(scan);
7154 table_close(classRel, AccessShareLock);
7155
7156 return result;
7157}
7158
7159
7160/*
7161 * check_of_type
7162 *
7163 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7164 * isn't suitable, throw an error. Currently, we require that the type
7165 * originated with CREATE TYPE AS. We could support any row type, but doing so
7166 * would require handling a number of extra corner cases in the DDL commands.
7167 * (Also, allowing domain-over-composite would open up a can of worms about
7168 * whether and how the domain's constraints should apply to derived tables.)
7169 */
7170void
7172{
7173 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7174 bool typeOk = false;
7175
7176 if (typ->typtype == TYPTYPE_COMPOSITE)
7177 {
7178 Relation typeRelation;
7179
7180 Assert(OidIsValid(typ->typrelid));
7181 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7182 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7183
7184 /*
7185 * Close the parent rel, but keep our AccessShareLock on it until xact
7186 * commit. That will prevent someone else from deleting or ALTERing
7187 * the type before the typed table creation/conversion commits.
7188 */
7189 relation_close(typeRelation, NoLock);
7190
7191 if (!typeOk)
7192 ereport(ERROR,
7193 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7194 errmsg("type %s is the row type of another table",
7195 format_type_be(typ->oid)),
7196 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7197 }
7198 else
7199 ereport(ERROR,
7200 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7201 errmsg("type %s is not a composite type",
7202 format_type_be(typ->oid))));
7203}
7204
7205
7206/*
7207 * ALTER TABLE ADD COLUMN
7208 *
7209 * Adds an additional attribute to a relation making the assumption that
7210 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7211 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7212 * AlterTableCmd's.
7213 *
7214 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7215 * have to decide at runtime whether to recurse or not depending on whether we
7216 * actually add a column or merely merge with an existing column. (We can't
7217 * check this in a static pre-pass because it won't handle multiple inheritance
7218 * situations correctly.)
7219 */
7220static void
7221ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7222 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7223 AlterTableUtilityContext *context)
7224{
7225 if (rel->rd_rel->reloftype && !recursing)
7226 ereport(ERROR,
7227 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7228 errmsg("cannot add column to typed table")));
7229
7230 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7231 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7232
7233 if (recurse && !is_view)
7234 cmd->recurse = true;
7235}
7236
7237/*
7238 * Add a column to a table. The return value is the address of the
7239 * new column in the parent relation.
7240 *
7241 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7242 * copy (but that happens only after we check for IF NOT EXISTS).
7243 */
7244static ObjectAddress
7246 AlterTableCmd **cmd, bool recurse, bool recursing,
7247 LOCKMODE lockmode, AlterTablePass cur_pass,
7248 AlterTableUtilityContext *context)
7249{
7250 Oid myrelid = RelationGetRelid(rel);
7251 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7252 bool if_not_exists = (*cmd)->missing_ok;
7253 Relation pgclass,
7254 attrdesc;
7255 HeapTuple reltup;
7256 Form_pg_class relform;
7257 Form_pg_attribute attribute;
7258 int newattnum;
7259 char relkind;
7260 Expr *defval;
7261 List *children;
7262 ListCell *child;
7263 AlterTableCmd *childcmd;
7264 ObjectAddress address;
7265 TupleDesc tupdesc;
7266
7267 /* since this function recurses, it could be driven to stack overflow */
7269
7270 /* At top level, permission check was done in ATPrepCmd, else do it */
7271 if (recursing)
7272 ATSimplePermissions((*cmd)->subtype, rel,
7274
7275 if (rel->rd_rel->relispartition && !recursing)
7276 ereport(ERROR,
7277 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7278 errmsg("cannot add column to a partition")));
7279
7280 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7281
7282 /*
7283 * Are we adding the column to a recursion child? If so, check whether to
7284 * merge with an existing definition for the column. If we do merge, we
7285 * must not recurse. Children will already have the column, and recursing
7286 * into them would mess up attinhcount.
7287 */
7288 if (colDef->inhcount > 0)
7289 {
7290 HeapTuple tuple;
7291
7292 /* Does child already have a column by this name? */
7293 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7294 if (HeapTupleIsValid(tuple))
7295 {
7296 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7297 Oid ctypeId;
7298 int32 ctypmod;
7299 Oid ccollid;
7300
7301 /* Child column must match on type, typmod, and collation */
7302 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7303 if (ctypeId != childatt->atttypid ||
7304 ctypmod != childatt->atttypmod)
7305 ereport(ERROR,
7306 (errcode(ERRCODE_DATATYPE_MISMATCH),
7307 errmsg("child table \"%s\" has different type for column \"%s\"",
7308 RelationGetRelationName(rel), colDef->colname)));
7309 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7310 if (ccollid != childatt->attcollation)
7311 ereport(ERROR,
7312 (errcode(ERRCODE_COLLATION_MISMATCH),
7313 errmsg("child table \"%s\" has different collation for column \"%s\"",
7314 RelationGetRelationName(rel), colDef->colname),
7315 errdetail("\"%s\" versus \"%s\"",
7316 get_collation_name(ccollid),
7317 get_collation_name(childatt->attcollation))));
7318
7319 /* Bump the existing child att's inhcount */
7320 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7321 &childatt->attinhcount))
7322 ereport(ERROR,
7323 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7324 errmsg("too many inheritance parents"));
7325 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7326
7327 heap_freetuple(tuple);
7328
7329 /* Inform the user about the merge */
7331 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7332 colDef->colname, RelationGetRelationName(rel))));
7333
7334 table_close(attrdesc, RowExclusiveLock);
7335
7336 /* Make the child column change visible */
7338
7339 return InvalidObjectAddress;
7340 }
7341 }
7342
7343 /* skip if the name already exists and if_not_exists is true */
7344 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7345 {
7346 table_close(attrdesc, RowExclusiveLock);
7347 return InvalidObjectAddress;
7348 }
7349
7350 /*
7351 * Okay, we need to add the column, so go ahead and do parse
7352 * transformation. This can result in queueing up, or even immediately
7353 * executing, subsidiary operations (such as creation of unique indexes);
7354 * so we mustn't do it until we have made the if_not_exists check.
7355 *
7356 * When recursing, the command was already transformed and we needn't do
7357 * so again. Also, if context isn't given we can't transform. (That
7358 * currently happens only for AT_AddColumnToView; we expect that view.c
7359 * passed us a ColumnDef that doesn't need work.)
7360 */
7361 if (context != NULL && !recursing)
7362 {
7363 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7364 cur_pass, context);
7365 Assert(*cmd != NULL);
7366 colDef = castNode(ColumnDef, (*cmd)->def);
7367 }
7368
7369 /*
7370 * Regular inheritance children are independent enough not to inherit the
7371 * identity column from parent hence cannot recursively add identity
7372 * column if the table has inheritance children.
7373 *
7374 * Partitions, on the other hand, are integral part of a partitioned table
7375 * and inherit identity column. Hence propagate identity column down the
7376 * partition hierarchy.
7377 */
7378 if (colDef->identity &&
7379 recurse &&
7380 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7382 ereport(ERROR,
7383 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7384 errmsg("cannot recursively add identity column to table that has child tables")));
7385
7386 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7387
7388 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7389 if (!HeapTupleIsValid(reltup))
7390 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7391 relform = (Form_pg_class) GETSTRUCT(reltup);
7392 relkind = relform->relkind;
7393
7394 /* Determine the new attribute's number */
7395 newattnum = relform->relnatts + 1;
7396 if (newattnum > MaxHeapAttributeNumber)
7397 ereport(ERROR,
7398 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7399 errmsg("tables can have at most %d columns",
7401
7402 /*
7403 * Construct new attribute's pg_attribute entry.
7404 */
7405 tupdesc = BuildDescForRelation(list_make1(colDef));
7406
7407 attribute = TupleDescAttr(tupdesc, 0);
7408
7409 /* Fix up attribute number */
7410 attribute->attnum = newattnum;
7411
7412 /* make sure datatype is legal for a column */
7413 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7414 list_make1_oid(rel->rd_rel->reltype),
7415 (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7416
7417 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7418
7419 table_close(attrdesc, RowExclusiveLock);
7420
7421 /*
7422 * Update pg_class tuple as appropriate
7423 */
7424 relform->relnatts = newattnum;
7425
7426 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7427
7428 heap_freetuple(reltup);
7429
7430 /* Post creation hook for new attribute */
7431 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7432
7433 table_close(pgclass, RowExclusiveLock);
7434
7435 /* Make the attribute's catalog entry visible */
7437
7438 /*
7439 * Store the DEFAULT, if any, in the catalogs
7440 */
7441 if (colDef->raw_default)
7442 {
7443 RawColumnDefault *rawEnt;
7444
7446 rawEnt->attnum = attribute->attnum;
7447 rawEnt->raw_default = copyObject(colDef->raw_default);
7448 rawEnt->generated = colDef->generated;
7449
7450 /*
7451 * This function is intended for CREATE TABLE, so it processes a
7452 * _list_ of defaults, but we just do one.
7453 */
7455 false, true, false, NULL);
7456
7457 /* Make the additional catalog changes visible */
7459 }
7460
7461 /*
7462 * Tell Phase 3 to fill in the default expression, if there is one.
7463 *
7464 * If there is no default, Phase 3 doesn't have to do anything, because
7465 * that effectively means that the default is NULL. The heap tuple access
7466 * routines always check for attnum > # of attributes in tuple, and return
7467 * NULL if so, so without any modification of the tuple data we will get
7468 * the effect of NULL values in the new column.
7469 *
7470 * An exception occurs when the new column is of a domain type: the domain
7471 * might have a not-null constraint, or a check constraint that indirectly
7472 * rejects nulls. If there are any domain constraints then we construct
7473 * an explicit NULL default value that will be passed through
7474 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7475 * rewriting the table which we really wouldn't have to do; but we do it
7476 * to preserve the historical behavior that such a failure will be raised
7477 * only if the table currently contains some rows.)
7478 *
7479 * Note: we use build_column_default, and not just the cooked default
7480 * returned by AddRelationNewConstraints, so that the right thing happens
7481 * when a datatype's default applies.
7482 *
7483 * Note: it might seem that this should happen at the end of Phase 2, so
7484 * that the effects of subsequent subcommands can be taken into account.
7485 * It's intentional that we do it now, though. The new column should be
7486 * filled according to what is said in the ADD COLUMN subcommand, so that
7487 * the effects are the same as if this subcommand had been run by itself
7488 * and the later subcommands had been issued in new ALTER TABLE commands.
7489 *
7490 * We can skip this entirely for relations without storage, since Phase 3
7491 * is certainly not going to touch them.
7492 */
7493 if (RELKIND_HAS_STORAGE(relkind))
7494 {
7495 bool has_domain_constraints;
7496 bool has_missing = false;
7497
7498 /*
7499 * For an identity column, we can't use build_column_default(),
7500 * because the sequence ownership isn't set yet. So do it manually.
7501 */
7502 if (colDef->identity)
7503 {
7505
7506 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7507 nve->typeId = attribute->atttypid;
7508
7509 defval = (Expr *) nve;
7510 }
7511 else
7512 defval = (Expr *) build_column_default(rel, attribute->attnum);
7513
7514 /* Build CoerceToDomain(NULL) expression if needed */
7515 has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7516 if (!defval && has_domain_constraints)
7517 {
7518 Oid baseTypeId;
7519 int32 baseTypeMod;
7520 Oid baseTypeColl;
7521
7522 baseTypeMod = attribute->atttypmod;
7523 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7524 baseTypeColl = get_typcollation(baseTypeId);
7525 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7526 defval = (Expr *) coerce_to_target_type(NULL,
7527 (Node *) defval,
7528 baseTypeId,
7529 attribute->atttypid,
7530 attribute->atttypmod,
7533 -1);
7534 if (defval == NULL) /* should not happen */
7535 elog(ERROR, "failed to coerce base type to domain");
7536 }
7537
7538 if (defval)
7539 {
7541
7542 /* Prepare defval for execution, either here or in Phase 3 */
7543 defval = expression_planner(defval);
7544
7545 /* Add the new default to the newvals list */
7547 newval->attnum = attribute->attnum;
7548 newval->expr = defval;
7549 newval->is_generated = (colDef->generated != '\0');
7550
7551 tab->newvals = lappend(tab->newvals, newval);
7552
7553 /*
7554 * Attempt to skip a complete table rewrite by storing the
7555 * specified DEFAULT value outside of the heap. This is only
7556 * allowed for plain relations and non-generated columns, and the
7557 * default expression can't be volatile (stable is OK). Note that
7558 * contain_volatile_functions deems CoerceToDomain immutable, but
7559 * here we consider that coercion to a domain with constraints is
7560 * volatile; else it might fail even when the table is empty.
7561 */
7562 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7563 !colDef->generated &&
7564 !has_domain_constraints &&
7565 !contain_volatile_functions((Node *) defval))
7566 {
7567 EState *estate;
7568 ExprState *exprState;
7569 Datum missingval;
7570 bool missingIsNull;
7571
7572 /* Evaluate the default expression */
7573 estate = CreateExecutorState();
7574 exprState = ExecPrepareExpr(defval, estate);
7575 missingval = ExecEvalExpr(exprState,
7576 GetPerTupleExprContext(estate),
7577 &missingIsNull);
7578 /* If it turns out NULL, nothing to do; else store it */
7579 if (!missingIsNull)
7580 {
7581 StoreAttrMissingVal(rel, attribute->attnum, missingval);
7582 /* Make the additional catalog change visible */
7584 has_missing = true;
7585 }
7586 FreeExecutorState(estate);
7587 }
7588 else
7589 {
7590 /*
7591 * Failed to use missing mode. We have to do a table rewrite
7592 * to install the value --- unless it's a virtual generated
7593 * column.
7594 */
7595 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7597 }
7598 }
7599
7600 if (!has_missing)
7601 {
7602 /*
7603 * If the new column is NOT NULL, and there is no missing value,
7604 * tell Phase 3 it needs to check for NULLs.
7605 */
7606 tab->verify_new_notnull |= colDef->is_not_null;
7607 }
7608 }
7609
7610 /*
7611 * Add needed dependency entries for the new column.
7612 */
7613 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7614 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7615
7616 /*
7617 * Propagate to children as appropriate. Unlike most other ALTER
7618 * routines, we have to do this one level of recursion at a time; we can't
7619 * use find_all_inheritors to do it in one pass.
7620 */
7621 children =
7623
7624 /*
7625 * If we are told not to recurse, there had better not be any child
7626 * tables; else the addition would put them out of step.
7627 */
7628 if (children && !recurse)
7629 ereport(ERROR,
7630 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7631 errmsg("column must be added to child tables too")));
7632
7633 /* Children should see column as singly inherited */
7634 if (!recursing)
7635 {
7636 childcmd = copyObject(*cmd);
7637 colDef = castNode(ColumnDef, childcmd->def);
7638 colDef->inhcount = 1;
7639 colDef->is_local = false;
7640 }
7641 else
7642 childcmd = *cmd; /* no need to copy again */
7643
7644 foreach(child, children)
7645 {
7646 Oid childrelid = lfirst_oid(child);
7647 Relation childrel;
7648 AlteredTableInfo *childtab;
7649
7650 /* find_inheritance_children already got lock */
7651 childrel = table_open(childrelid, NoLock);
7652 CheckAlterTableIsSafe(childrel);
7653
7654 /* Find or create work queue entry for this table */
7655 childtab = ATGetQueueEntry(wqueue, childrel);
7656
7657 /* Recurse to child; return value is ignored */
7658 ATExecAddColumn(wqueue, childtab, childrel,
7659 &childcmd, recurse, true,
7660 lockmode, cur_pass, context);
7661
7662 table_close(childrel, NoLock);
7663 }
7664
7665 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7666 return address;
7667}
7668
7669/*
7670 * If a new or renamed column will collide with the name of an existing
7671 * column and if_not_exists is false then error out, else do nothing.
7672 */
7673static bool
7675 bool if_not_exists)
7676{
7677 HeapTuple attTuple;
7678 int attnum;
7679
7680 /*
7681 * this test is deliberately not attisdropped-aware, since if one tries to
7682 * add a column matching a dropped column name, it's gonna fail anyway.
7683 */
7684 attTuple = SearchSysCache2(ATTNAME,
7686 PointerGetDatum(colname));
7687 if (!HeapTupleIsValid(attTuple))
7688 return true;
7689
7690 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7691 ReleaseSysCache(attTuple);
7692
7693 /*
7694 * We throw a different error message for conflicts with system column
7695 * names, since they are normally not shown and the user might otherwise
7696 * be confused about the reason for the conflict.
7697 */
7698 if (attnum <= 0)
7699 ereport(ERROR,
7700 (errcode(ERRCODE_DUPLICATE_COLUMN),
7701 errmsg("column name \"%s\" conflicts with a system column name",
7702 colname)));
7703 else
7704 {
7705 if (if_not_exists)
7706 {
7708 (errcode(ERRCODE_DUPLICATE_COLUMN),
7709 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7710 colname, RelationGetRelationName(rel))));
7711 return false;
7712 }
7713
7714 ereport(ERROR,
7715 (errcode(ERRCODE_DUPLICATE_COLUMN),
7716 errmsg("column \"%s\" of relation \"%s\" already exists",
7717 colname, RelationGetRelationName(rel))));
7718 }
7719
7720 return true;
7721}
7722
7723/*
7724 * Install a column's dependency on its datatype.
7725 */
7726static void
7728{
7729 ObjectAddress myself,
7730 referenced;
7731
7732 myself.classId = RelationRelationId;
7733 myself.objectId = relid;
7734 myself.objectSubId = attnum;
7735 referenced.classId = TypeRelationId;
7736 referenced.objectId = typid;
7737 referenced.objectSubId = 0;
7738 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7739}
7740
7741/*
7742 * Install a column's dependency on its collation.
7743 */
7744static void
7746{
7747 ObjectAddress myself,
7748 referenced;
7749
7750 /* We know the default collation is pinned, so don't bother recording it */
7751 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7752 {
7753 myself.classId = RelationRelationId;
7754 myself.objectId = relid;
7755 myself.objectSubId = attnum;
7756 referenced.classId = CollationRelationId;
7757 referenced.objectId = collid;
7758 referenced.objectSubId = 0;
7759 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7760 }
7761}
7762
7763/*
7764 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7765 *
7766 * Return the address of the modified column. If the column was already
7767 * nullable, InvalidObjectAddress is returned.
7768 */
7769static ObjectAddress
7770ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7771 LOCKMODE lockmode)
7772{
7773 HeapTuple tuple;
7774 HeapTuple conTup;
7775 Form_pg_attribute attTup;
7777 Relation attr_rel;
7778 ObjectAddress address;
7779
7780 /*
7781 * lookup the attribute
7782 */
7783 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7784
7785 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7786 if (!HeapTupleIsValid(tuple))
7787 ereport(ERROR,
7788 (errcode(ERRCODE_UNDEFINED_COLUMN),
7789 errmsg("column \"%s\" of relation \"%s\" does not exist",
7790 colName, RelationGetRelationName(rel))));
7791 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7792 attnum = attTup->attnum;
7793 ObjectAddressSubSet(address, RelationRelationId,
7794 RelationGetRelid(rel), attnum);
7795
7796 /* If the column is already nullable there's nothing to do. */
7797 if (!attTup->attnotnull)
7798 {
7799 table_close(attr_rel, RowExclusiveLock);
7800 return InvalidObjectAddress;
7801 }
7802
7803 /* Prevent them from altering a system attribute */
7804 if (attnum <= 0)
7805 ereport(ERROR,
7806 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7807 errmsg("cannot alter system column \"%s\"",
7808 colName)));
7809
7810 if (attTup->attidentity)
7811 ereport(ERROR,
7812 (errcode(ERRCODE_SYNTAX_ERROR),
7813 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7814 colName, RelationGetRelationName(rel))));
7815
7816 /*
7817 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7818 */
7819 if (rel->rd_rel->relispartition)
7820 {
7821 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7822 Relation parent = table_open(parentId, AccessShareLock);
7823 TupleDesc tupDesc = RelationGetDescr(parent);
7824 AttrNumber parent_attnum;
7825
7826 parent_attnum = get_attnum(parentId, colName);
7827 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7828 ereport(ERROR,
7829 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7830 errmsg("column \"%s\" is marked NOT NULL in parent table",
7831 colName)));
7833 }
7834
7835 /*
7836 * Find the constraint that makes this column NOT NULL, and drop it.
7837 * dropconstraint_internal() resets attnotnull.
7838 */
7840 if (conTup == NULL)
7841 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7842 colName, RelationGetRelationName(rel));
7843
7844 /* The normal case: we have a pg_constraint row, remove it */
7845 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7846 false, lockmode);
7847 heap_freetuple(conTup);
7848
7849 InvokeObjectPostAlterHook(RelationRelationId,
7850 RelationGetRelid(rel), attnum);
7851
7852 table_close(attr_rel, RowExclusiveLock);
7853
7854 return address;
7855}
7856
7857/*
7858 * set_attnotnull
7859 * Helper to update/validate the pg_attribute status of a not-null
7860 * constraint
7861 *
7862 * pg_attribute.attnotnull is set true, if it isn't already.
7863 * If queue_validation is true, also set up wqueue to validate the constraint.
7864 * wqueue may be given as NULL when validation is not needed (e.g., on table
7865 * creation).
7866 */
7867static void
7869 bool is_valid, bool queue_validation)
7870{
7871 Form_pg_attribute attr;
7872 CompactAttribute *thisatt;
7873
7874 Assert(!queue_validation || wqueue);
7875
7877
7878 /*
7879 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7880 * attribute.
7881 */
7882 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7883 if (attr->attisdropped)
7884 return;
7885
7886 if (!attr->attnotnull)
7887 {
7888 Relation attr_rel;
7889 HeapTuple tuple;
7890
7891 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7892
7894 if (!HeapTupleIsValid(tuple))
7895 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7896 attnum, RelationGetRelid(rel));
7897
7898 thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7900
7901 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7902
7903 attr->attnotnull = true;
7904 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7905
7906 /*
7907 * If the nullness isn't already proven by validated constraints, have
7908 * ALTER TABLE phase 3 test for it.
7909 */
7910 if (queue_validation && wqueue &&
7912 {
7913 AlteredTableInfo *tab;
7914
7915 tab = ATGetQueueEntry(wqueue, rel);
7916 tab->verify_new_notnull = true;
7917 }
7918
7920
7921 table_close(attr_rel, RowExclusiveLock);
7922 heap_freetuple(tuple);
7923 }
7924 else
7925 {
7927 }
7928}
7929
7930/*
7931 * ALTER TABLE ALTER COLUMN SET NOT NULL
7932 *
7933 * Add a not-null constraint to a single table and its children. Returns
7934 * the address of the constraint added to the parent relation, if one gets
7935 * added, or InvalidObjectAddress otherwise.
7936 *
7937 * We must recurse to child tables during execution, rather than using
7938 * ALTER TABLE's normal prep-time recursion.
7939 */
7940static ObjectAddress
7941ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7942 bool recurse, bool recursing, LOCKMODE lockmode)
7943{
7944 HeapTuple tuple;
7946 ObjectAddress address;
7947 Constraint *constraint;
7948 CookedConstraint *ccon;
7949 List *cooked;
7950 bool is_no_inherit = false;
7951
7952 /* Guard against stack overflow due to overly deep inheritance tree. */
7954
7955 /* At top level, permission check was done in ATPrepCmd, else do it */
7956 if (recursing)
7957 {
7960 Assert(conName != NULL);
7961 }
7962
7963 attnum = get_attnum(RelationGetRelid(rel), colName);
7965 ereport(ERROR,
7966 (errcode(ERRCODE_UNDEFINED_COLUMN),
7967 errmsg("column \"%s\" of relation \"%s\" does not exist",
7968 colName, RelationGetRelationName(rel))));
7969
7970 /* Prevent them from altering a system attribute */
7971 if (attnum <= 0)
7972 ereport(ERROR,
7973 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7974 errmsg("cannot alter system column \"%s\"",
7975 colName)));
7976
7977 /* See if there's already a constraint */
7979 if (HeapTupleIsValid(tuple))
7980 {
7982 bool changed = false;
7983
7984 /*
7985 * Don't let a NO INHERIT constraint be changed into inherit.
7986 */
7987 if (conForm->connoinherit && recurse)
7988 ereport(ERROR,
7989 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7990 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7991 NameStr(conForm->conname),
7993
7994 /*
7995 * If we find an appropriate constraint, we're almost done, but just
7996 * need to change some properties on it: if we're recursing, increment
7997 * coninhcount; if not, set conislocal if not already set.
7998 */
7999 if (recursing)
8000 {
8001 if (pg_add_s16_overflow(conForm->coninhcount, 1,
8002 &conForm->coninhcount))
8003 ereport(ERROR,
8004 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
8005 errmsg("too many inheritance parents"));
8006 changed = true;
8007 }
8008 else if (!conForm->conislocal)
8009 {
8010 conForm->conislocal = true;
8011 changed = true;
8012 }
8013 else if (!conForm->convalidated)
8014 {
8015 /*
8016 * Flip attnotnull and convalidated, and also validate the
8017 * constraint.
8018 */
8019 return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
8020 recurse, recursing, lockmode);
8021 }
8022
8023 if (changed)
8024 {
8025 Relation constr_rel;
8026
8027 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
8028
8029 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
8030 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
8031 table_close(constr_rel, RowExclusiveLock);
8032 }
8033
8034 if (changed)
8035 return address;
8036 else
8037 return InvalidObjectAddress;
8038 }
8039
8040 /*
8041 * If we're asked not to recurse, and children exist, raise an error for
8042 * partitioned tables. For inheritance, we act as if NO INHERIT had been
8043 * specified.
8044 */
8045 if (!recurse &&
8047 NoLock) != NIL)
8048 {
8049 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8050 ereport(ERROR,
8051 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8052 errmsg("constraint must be added to child tables too"),
8053 errhint("Do not specify the ONLY keyword."));
8054 else
8055 is_no_inherit = true;
8056 }
8057
8058 /*
8059 * No constraint exists; we must add one. First determine a name to use,
8060 * if we haven't already.
8061 */
8062 if (!recursing)
8063 {
8064 Assert(conName == NULL);
8066 colName, "not_null",
8068 NIL);
8069 }
8070
8071 constraint = makeNotNullConstraint(makeString(colName));
8072 constraint->is_no_inherit = is_no_inherit;
8073 constraint->conname = conName;
8074
8075 /* and do it */
8076 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8077 false, !recursing, false, NULL);
8078 ccon = linitial(cooked);
8079 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8080
8081 InvokeObjectPostAlterHook(RelationRelationId,
8082 RelationGetRelid(rel), attnum);
8083
8084 /* Mark pg_attribute.attnotnull for the column and queue validation */
8085 set_attnotnull(wqueue, rel, attnum, true, true);
8086
8087 /*
8088 * Recurse to propagate the constraint to children that don't have one.
8089 */
8090 if (recurse)
8091 {
8092 List *children;
8093
8095 lockmode);
8096
8097 foreach_oid(childoid, children)
8098 {
8099 Relation childrel = table_open(childoid, NoLock);
8100
8102
8103 ATExecSetNotNull(wqueue, childrel, conName, colName,
8104 recurse, true, lockmode);
8105 table_close(childrel, NoLock);
8106 }
8107 }
8108
8109 return address;
8110}
8111
8112/*
8113 * NotNullImpliedByRelConstraints
8114 * Does rel's existing constraints imply NOT NULL for the given attribute?
8115 */
8116static bool
8118{
8119 NullTest *nnulltest = makeNode(NullTest);
8120
8121 nnulltest->arg = (Expr *) makeVar(1,
8122 attr->attnum,
8123 attr->atttypid,
8124 attr->atttypmod,
8125 attr->attcollation,
8126 0);
8127 nnulltest->nulltesttype = IS_NOT_NULL;
8128
8129 /*
8130 * argisrow = false is correct even for a composite column, because
8131 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8132 * case, just IS DISTINCT FROM NULL.
8133 */
8134 nnulltest->argisrow = false;
8135 nnulltest->location = -1;
8136
8137 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8138 {
8140 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8141 RelationGetRelationName(rel), NameStr(attr->attname))));
8142 return true;
8143 }
8144
8145 return false;
8146}
8147
8148/*
8149 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8150 *
8151 * Return the address of the affected column.
8152 */
8153static ObjectAddress
8154ATExecColumnDefault(Relation rel, const char *colName,
8155 Node *newDefault, LOCKMODE lockmode)
8156{
8157 TupleDesc tupdesc = RelationGetDescr(rel);
8159 ObjectAddress address;
8160
8161 /*
8162 * get the number of the attribute
8163 */
8164 attnum = get_attnum(RelationGetRelid(rel), colName);
8166 ereport(ERROR,
8167 (errcode(ERRCODE_UNDEFINED_COLUMN),
8168 errmsg("column \"%s\" of relation \"%s\" does not exist",
8169 colName, RelationGetRelationName(rel))));
8170
8171 /* Prevent them from altering a system attribute */
8172 if (attnum <= 0)
8173 ereport(ERROR,
8174 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8175 errmsg("cannot alter system column \"%s\"",
8176 colName)));
8177
8178 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8179 ereport(ERROR,
8180 (errcode(ERRCODE_SYNTAX_ERROR),
8181 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8182 colName, RelationGetRelationName(rel)),
8183 /* translator: %s is an SQL ALTER command */
8184 newDefault ? 0 : errhint("Use %s instead.",
8185 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8186
8187 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8188 ereport(ERROR,
8189 (errcode(ERRCODE_SYNTAX_ERROR),
8190 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8191 colName, RelationGetRelationName(rel)),
8192 newDefault ?
8193 /* translator: %s is an SQL ALTER command */
8194 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8195 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8196 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8197
8198 /*
8199 * Remove any old default for the column. We use RESTRICT here for
8200 * safety, but at present we do not expect anything to depend on the
8201 * default.
8202 *
8203 * We treat removing the existing default as an internal operation when it
8204 * is preparatory to adding a new default, but as a user-initiated
8205 * operation when the user asked for a drop.
8206 */
8208 newDefault != NULL);
8209
8210 if (newDefault)
8211 {
8212 /* SET DEFAULT */
8213 RawColumnDefault *rawEnt;
8214
8216 rawEnt->attnum = attnum;
8217 rawEnt->raw_default = newDefault;
8218 rawEnt->generated = '\0';
8219
8220 /*
8221 * This function is intended for CREATE TABLE, so it processes a
8222 * _list_ of defaults, but we just do one.
8223 */
8225 false, true, false, NULL);
8226 }
8227
8228 ObjectAddressSubSet(address, RelationRelationId,
8229 RelationGetRelid(rel), attnum);
8230 return address;
8231}
8232
8233/*
8234 * Add a pre-cooked default expression.
8235 *
8236 * Return the address of the affected column.
8237 */
8238static ObjectAddress
8240 Node *newDefault)
8241{
8242 ObjectAddress address;
8243
8244 /* We assume no checking is required */
8245
8246 /*
8247 * Remove any old default for the column. We use RESTRICT here for
8248 * safety, but at present we do not expect anything to depend on the
8249 * default. (In ordinary cases, there could not be a default in place
8250 * anyway, but it's possible when combining LIKE with inheritance.)
8251 */
8253 true);
8254
8255 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8256
8257 ObjectAddressSubSet(address, RelationRelationId,
8258 RelationGetRelid(rel), attnum);
8259 return address;
8260}
8261
8262/*
8263 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8264 *
8265 * Return the address of the affected column.
8266 */
8267static ObjectAddress
8268ATExecAddIdentity(Relation rel, const char *colName,
8269 Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8270{
8271 Relation attrelation;
8272 HeapTuple tuple;
8273 Form_pg_attribute attTup;
8275 ObjectAddress address;
8276 ColumnDef *cdef = castNode(ColumnDef, def);
8277 bool ispartitioned;
8278
8279 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8280 if (ispartitioned && !recurse)
8281 ereport(ERROR,
8282 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8283 errmsg("cannot add identity to a column of only the partitioned table"),
8284 errhint("Do not specify the ONLY keyword.")));
8285
8286 if (rel->rd_rel->relispartition && !recursing)
8287 ereport(ERROR,
8288 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8289 errmsg("cannot add identity to a column of a partition"));
8290
8291 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8292
8293 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8294 if (!HeapTupleIsValid(tuple))
8295 ereport(ERROR,
8296 (errcode(ERRCODE_UNDEFINED_COLUMN),
8297 errmsg("column \"%s\" of relation \"%s\" does not exist",
8298 colName, RelationGetRelationName(rel))));
8299 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8300 attnum = attTup->attnum;
8301
8302 /* Can't alter a system attribute */
8303 if (attnum <= 0)
8304 ereport(ERROR,
8305 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8306 errmsg("cannot alter system column \"%s\"",
8307 colName)));
8308
8309 /*
8310 * Creating a column as identity implies NOT NULL, so adding the identity
8311 * to an existing column that is not NOT NULL would create a state that
8312 * cannot be reproduced without contortions.
8313 */
8314 if (!attTup->attnotnull)
8315 ereport(ERROR,
8316 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8317 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8318 colName, RelationGetRelationName(rel))));
8319
8320 /*
8321 * On the other hand, if a not-null constraint exists, then verify that
8322 * it's compatible.
8323 */
8324 if (attTup->attnotnull)
8325 {
8326 HeapTuple contup;
8327 Form_pg_constraint conForm;
8328
8330 attnum);
8331 if (!HeapTupleIsValid(contup))
8332 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8333 colName, RelationGetRelationName(rel));
8334
8335 conForm = (Form_pg_constraint) GETSTRUCT(contup);
8336 if (!conForm->convalidated)
8337 ereport(ERROR,
8338 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8339 errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8340 NameStr(conForm->conname), RelationGetRelationName(rel)),
8341 errhint("You might need to validate it using %s.",
8342 "ALTER TABLE ... VALIDATE CONSTRAINT"));
8343 }
8344
8345 if (attTup->attidentity)
8346 ereport(ERROR,
8347 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8348 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8349 colName, RelationGetRelationName(rel))));
8350
8351 if (attTup->atthasdef)
8352 ereport(ERROR,
8353 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8354 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8355 colName, RelationGetRelationName(rel))));
8356
8357 attTup->attidentity = cdef->identity;
8358 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8359
8360 InvokeObjectPostAlterHook(RelationRelationId,
8361 RelationGetRelid(rel),
8362 attTup->attnum);
8363 ObjectAddressSubSet(address, RelationRelationId,
8364 RelationGetRelid(rel), attnum);
8365 heap_freetuple(tuple);
8366
8367 table_close(attrelation, RowExclusiveLock);
8368
8369 /*
8370 * Recurse to propagate the identity column to partitions. Identity is
8371 * not inherited in regular inheritance children.
8372 */
8373 if (recurse && ispartitioned)
8374 {
8375 List *children;
8376 ListCell *lc;
8377
8378 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8379
8380 foreach(lc, children)
8381 {
8382 Relation childrel;
8383
8384 childrel = table_open(lfirst_oid(lc), NoLock);
8385 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8386 table_close(childrel, NoLock);
8387 }
8388 }
8389
8390 return address;
8391}
8392
8393/*
8394 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8395 *
8396 * Return the address of the affected column.
8397 */
8398static ObjectAddress
8399ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8400 LOCKMODE lockmode, bool recurse, bool recursing)
8401{
8403 DefElem *generatedEl = NULL;
8404 HeapTuple tuple;
8405 Form_pg_attribute attTup;
8407 Relation attrelation;
8408 ObjectAddress address;
8409 bool ispartitioned;
8410
8411 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8412 if (ispartitioned && !recurse)
8413 ereport(ERROR,
8414 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8415 errmsg("cannot change identity column of only the partitioned table"),
8416 errhint("Do not specify the ONLY keyword.")));
8417
8418 if (rel->rd_rel->relispartition && !recursing)
8419 ereport(ERROR,
8420 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8421 errmsg("cannot change identity column of a partition"));
8422
8423 foreach(option, castNode(List, def))
8424 {
8425 DefElem *defel = lfirst_node(DefElem, option);
8426
8427 if (strcmp(defel->defname, "generated") == 0)
8428 {
8429 if (generatedEl)
8430 ereport(ERROR,
8431 (errcode(ERRCODE_SYNTAX_ERROR),
8432 errmsg("conflicting or redundant options")));
8433 generatedEl = defel;
8434 }
8435 else
8436 elog(ERROR, "option \"%s\" not recognized",
8437 defel->defname);
8438 }
8439
8440 /*
8441 * Even if there is nothing to change here, we run all the checks. There
8442 * will be a subsequent ALTER SEQUENCE that relies on everything being
8443 * there.
8444 */
8445
8446 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8447 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8448 if (!HeapTupleIsValid(tuple))
8449 ereport(ERROR,
8450 (errcode(ERRCODE_UNDEFINED_COLUMN),
8451 errmsg("column \"%s\" of relation \"%s\" does not exist",
8452 colName, RelationGetRelationName(rel))));
8453
8454 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8455 attnum = attTup->attnum;
8456
8457 if (attnum <= 0)
8458 ereport(ERROR,
8459 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8460 errmsg("cannot alter system column \"%s\"",
8461 colName)));
8462
8463 if (!attTup->attidentity)
8464 ereport(ERROR,
8465 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8466 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8467 colName, RelationGetRelationName(rel))));
8468
8469 if (generatedEl)
8470 {
8471 attTup->attidentity = defGetInt32(generatedEl);
8472 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8473
8474 InvokeObjectPostAlterHook(RelationRelationId,
8475 RelationGetRelid(rel),
8476 attTup->attnum);
8477 ObjectAddressSubSet(address, RelationRelationId,
8478 RelationGetRelid(rel), attnum);
8479 }
8480 else
8481 address = InvalidObjectAddress;
8482
8483 heap_freetuple(tuple);
8484 table_close(attrelation, RowExclusiveLock);
8485
8486 /*
8487 * Recurse to propagate the identity change to partitions. Identity is not
8488 * inherited in regular inheritance children.
8489 */
8490 if (generatedEl && recurse && ispartitioned)
8491 {
8492 List *children;
8493 ListCell *lc;
8494
8495 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8496
8497 foreach(lc, children)
8498 {
8499 Relation childrel;
8500
8501 childrel = table_open(lfirst_oid(lc), NoLock);
8502 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8503 table_close(childrel, NoLock);
8504 }
8505 }
8506
8507 return address;
8508}
8509
8510/*
8511 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8512 *
8513 * Return the address of the affected column.
8514 */
8515static ObjectAddress
8516ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8517 bool recurse, bool recursing)
8518{
8519 HeapTuple tuple;
8520 Form_pg_attribute attTup;
8522 Relation attrelation;
8523 ObjectAddress address;
8524 Oid seqid;
8525 ObjectAddress seqaddress;
8526 bool ispartitioned;
8527
8528 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8529 if (ispartitioned && !recurse)
8530 ereport(ERROR,
8531 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8532 errmsg("cannot drop identity from a column of only the partitioned table"),
8533 errhint("Do not specify the ONLY keyword.")));
8534
8535 if (rel->rd_rel->relispartition && !recursing)
8536 ereport(ERROR,
8537 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8538 errmsg("cannot drop identity from a column of a partition"));
8539
8540 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8541 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8542 if (!HeapTupleIsValid(tuple))
8543 ereport(ERROR,
8544 (errcode(ERRCODE_UNDEFINED_COLUMN),
8545 errmsg("column \"%s\" of relation \"%s\" does not exist",
8546 colName, RelationGetRelationName(rel))));
8547
8548 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8549 attnum = attTup->attnum;
8550
8551 if (attnum <= 0)
8552 ereport(ERROR,
8553 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8554 errmsg("cannot alter system column \"%s\"",
8555 colName)));
8556
8557 if (!attTup->attidentity)
8558 {
8559 if (!missing_ok)
8560 ereport(ERROR,
8561 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8562 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8563 colName, RelationGetRelationName(rel))));
8564 else
8565 {
8567 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8568 colName, RelationGetRelationName(rel))));
8569 heap_freetuple(tuple);
8570 table_close(attrelation, RowExclusiveLock);
8571 return InvalidObjectAddress;
8572 }
8573 }
8574
8575 attTup->attidentity = '\0';
8576 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8577
8578 InvokeObjectPostAlterHook(RelationRelationId,
8579 RelationGetRelid(rel),
8580 attTup->attnum);
8581 ObjectAddressSubSet(address, RelationRelationId,
8582 RelationGetRelid(rel), attnum);
8583 heap_freetuple(tuple);
8584
8585 table_close(attrelation, RowExclusiveLock);
8586
8587 /*
8588 * Recurse to drop the identity from column in partitions. Identity is
8589 * not inherited in regular inheritance children so ignore them.
8590 */
8591 if (recurse && ispartitioned)
8592 {
8593 List *children;
8594 ListCell *lc;
8595
8596 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8597
8598 foreach(lc, children)
8599 {
8600 Relation childrel;
8601
8602 childrel = table_open(lfirst_oid(lc), NoLock);
8603 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8604 table_close(childrel, NoLock);
8605 }
8606 }
8607
8608 if (!recursing)
8609 {
8610 /* drop the internal sequence */
8611 seqid = getIdentitySequence(rel, attnum, false);
8612 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8613 RelationRelationId, DEPENDENCY_INTERNAL);
8615 seqaddress.classId = RelationRelationId;
8616 seqaddress.objectId = seqid;
8617 seqaddress.objectSubId = 0;
8619 }
8620
8621 return address;
8622}
8623
8624/*
8625 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8626 *
8627 * Return the address of the affected column.
8628 */
8629static ObjectAddress
8630ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8631 Node *newExpr, LOCKMODE lockmode)
8632{
8633 HeapTuple tuple;
8634 Form_pg_attribute attTup;
8636 char attgenerated;
8637 bool rewrite;
8638 Oid attrdefoid;
8639 ObjectAddress address;
8640 Expr *defval;
8642 RawColumnDefault *rawEnt;
8643
8644 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8645 if (!HeapTupleIsValid(tuple))
8646 ereport(ERROR,
8647 (errcode(ERRCODE_UNDEFINED_COLUMN),
8648 errmsg("column \"%s\" of relation \"%s\" does not exist",
8649 colName, RelationGetRelationName(rel))));
8650
8651 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8652
8653 attnum = attTup->attnum;
8654 if (attnum <= 0)
8655 ereport(ERROR,
8656 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8657 errmsg("cannot alter system column \"%s\"",
8658 colName)));
8659
8660 attgenerated = attTup->attgenerated;
8661 if (!attgenerated)
8662 ereport(ERROR,
8663 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8664 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8665 colName, RelationGetRelationName(rel))));
8666
8667 /*
8668 * TODO: This could be done, just need to recheck any constraints
8669 * afterwards.
8670 */
8671 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8672 rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8673 ereport(ERROR,
8674 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8675 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
8676 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8677 colName, RelationGetRelationName(rel))));
8678
8679 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8680 tab->verify_new_notnull = true;
8681
8682 /*
8683 * We need to prevent this because a change of expression could affect a
8684 * row filter and inject expressions that are not permitted in a row
8685 * filter. XXX We could try to have a more precise check to catch only
8686 * publications with row filters, or even re-verify the row filter
8687 * expressions.
8688 */
8689 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8691 ereport(ERROR,
8692 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8693 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8694 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8695 colName, RelationGetRelationName(rel))));
8696
8697 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8698
8699 ReleaseSysCache(tuple);
8700
8701 if (rewrite)
8702 {
8703 /*
8704 * Clear all the missing values if we're rewriting the table, since
8705 * this renders them pointless.
8706 */
8708
8709 /* make sure we don't conflict with later attribute modifications */
8711
8712 /*
8713 * Find everything that depends on the column (constraints, indexes,
8714 * etc), and record enough information to let us recreate the objects
8715 * after rewrite.
8716 */
8718 }
8719
8720 /*
8721 * Drop the dependency records of the GENERATED expression, in particular
8722 * its INTERNAL dependency on the column, which would otherwise cause
8723 * dependency.c to refuse to perform the deletion.
8724 */
8725 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8726 if (!OidIsValid(attrdefoid))
8727 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8728 RelationGetRelid(rel), attnum);
8729 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8730
8731 /* Make above changes visible */
8733
8734 /*
8735 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8736 * safety, but at present we do not expect anything to depend on the
8737 * expression.
8738 */
8740 false, false);
8741
8742 /* Prepare to store the new expression, in the catalogs */
8744 rawEnt->attnum = attnum;
8745 rawEnt->raw_default = newExpr;
8746 rawEnt->generated = attgenerated;
8747
8748 /* Store the generated expression */
8750 false, true, false, NULL);
8751
8752 /* Make above new expression visible */
8754
8755 if (rewrite)
8756 {
8757 /* Prepare for table rewrite */
8758 defval = (Expr *) build_column_default(rel, attnum);
8759
8761 newval->attnum = attnum;
8762 newval->expr = expression_planner(defval);
8763 newval->is_generated = true;
8764
8765 tab->newvals = lappend(tab->newvals, newval);
8767 }
8768
8769 /* Drop any pg_statistic entry for the column */
8771
8772 InvokeObjectPostAlterHook(RelationRelationId,
8773 RelationGetRelid(rel), attnum);
8774
8775 ObjectAddressSubSet(address, RelationRelationId,
8776 RelationGetRelid(rel), attnum);
8777 return address;
8778}
8779
8780/*
8781 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8782 */
8783static void
8784ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8785{
8786 /*
8787 * Reject ONLY if there are child tables. We could implement this, but it
8788 * is a bit complicated. GENERATED clauses must be attached to the column
8789 * definition and cannot be added later like DEFAULT, so if a child table
8790 * has a generation expression that the parent does not have, the child
8791 * column will necessarily be an attislocal column. So to implement ONLY
8792 * here, we'd need extra code to update attislocal of the direct child
8793 * tables, somewhat similar to how DROP COLUMN does it, so that the
8794 * resulting state can be properly dumped and restored.
8795 */
8796 if (!recurse &&
8798 ereport(ERROR,
8799 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8800 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8801
8802 /*
8803 * Cannot drop generation expression from inherited columns.
8804 */
8805 if (!recursing)
8806 {
8807 HeapTuple tuple;
8808 Form_pg_attribute attTup;
8809
8811 if (!HeapTupleIsValid(tuple))
8812 ereport(ERROR,
8813 (errcode(ERRCODE_UNDEFINED_COLUMN),
8814 errmsg("column \"%s\" of relation \"%s\" does not exist",
8815 cmd->name, RelationGetRelationName(rel))));
8816
8817 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8818
8819 if (attTup->attinhcount > 0)
8820 ereport(ERROR,
8821 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8822 errmsg("cannot drop generation expression from inherited column")));
8823 }
8824}
8825
8826/*
8827 * Return the address of the affected column.
8828 */
8829static ObjectAddress
8830ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8831{
8832 HeapTuple tuple;
8833 Form_pg_attribute attTup;
8835 Relation attrelation;
8836 Oid attrdefoid;
8837 ObjectAddress address;
8838
8839 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8840 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8841 if (!HeapTupleIsValid(tuple))
8842 ereport(ERROR,
8843 (errcode(ERRCODE_UNDEFINED_COLUMN),
8844 errmsg("column \"%s\" of relation \"%s\" does not exist",
8845 colName, RelationGetRelationName(rel))));
8846
8847 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8848 attnum = attTup->attnum;
8849
8850 if (attnum <= 0)
8851 ereport(ERROR,
8852 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8853 errmsg("cannot alter system column \"%s\"",
8854 colName)));
8855
8856 /*
8857 * TODO: This could be done, but it would need a table rewrite to
8858 * materialize the generated values. Note that for the time being, we
8859 * still error with missing_ok, so that we don't silently leave the column
8860 * as generated.
8861 */
8862 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8863 ereport(ERROR,
8864 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8865 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8866 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8867 colName, RelationGetRelationName(rel))));
8868
8869 if (!attTup->attgenerated)
8870 {
8871 if (!missing_ok)
8872 ereport(ERROR,
8873 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8874 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8875 colName, RelationGetRelationName(rel))));
8876 else
8877 {
8879 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8880 colName, RelationGetRelationName(rel))));
8881 heap_freetuple(tuple);
8882 table_close(attrelation, RowExclusiveLock);
8883 return InvalidObjectAddress;
8884 }
8885 }
8886
8887 /*
8888 * Mark the column as no longer generated. (The atthasdef flag needs to
8889 * get cleared too, but RemoveAttrDefault will handle that.)
8890 */
8891 attTup->attgenerated = '\0';
8892 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8893
8894 InvokeObjectPostAlterHook(RelationRelationId,
8895 RelationGetRelid(rel),
8896 attnum);
8897 heap_freetuple(tuple);
8898
8899 table_close(attrelation, RowExclusiveLock);
8900
8901 /*
8902 * Drop the dependency records of the GENERATED expression, in particular
8903 * its INTERNAL dependency on the column, which would otherwise cause
8904 * dependency.c to refuse to perform the deletion.
8905 */
8906 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8907 if (!OidIsValid(attrdefoid))
8908 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8909 RelationGetRelid(rel), attnum);
8910 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8911
8912 /* Make above changes visible */
8914
8915 /*
8916 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8917 * safety, but at present we do not expect anything to depend on the
8918 * default.
8919 */
8921 false, false);
8922
8923 ObjectAddressSubSet(address, RelationRelationId,
8924 RelationGetRelid(rel), attnum);
8925 return address;
8926}
8927
8928/*
8929 * ALTER TABLE ALTER COLUMN SET STATISTICS
8930 *
8931 * Return value is the address of the modified column
8932 */
8933static ObjectAddress
8934ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8935{
8936 int newtarget = 0;
8937 bool newtarget_default;
8938 Relation attrelation;
8939 HeapTuple tuple,
8940 newtuple;
8941 Form_pg_attribute attrtuple;
8943 ObjectAddress address;
8944 Datum repl_val[Natts_pg_attribute];
8945 bool repl_null[Natts_pg_attribute];
8946 bool repl_repl[Natts_pg_attribute];
8947
8948 /*
8949 * We allow referencing columns by numbers only for indexes, since table
8950 * column numbers could contain gaps if columns are later dropped.
8951 */
8952 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8953 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8954 !colName)
8955 ereport(ERROR,
8956 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8957 errmsg("cannot refer to non-index column by number")));
8958
8959 /* -1 was used in previous versions for the default setting */
8960 if (newValue && intVal(newValue) != -1)
8961 {
8962 newtarget = intVal(newValue);
8963 newtarget_default = false;
8964 }
8965 else
8966 newtarget_default = true;
8967
8968 if (!newtarget_default)
8969 {
8970 /*
8971 * Limit target to a sane range
8972 */
8973 if (newtarget < 0)
8974 {
8975 ereport(ERROR,
8976 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8977 errmsg("statistics target %d is too low",
8978 newtarget)));
8979 }
8980 else if (newtarget > MAX_STATISTICS_TARGET)
8981 {
8982 newtarget = MAX_STATISTICS_TARGET;
8984 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8985 errmsg("lowering statistics target to %d",
8986 newtarget)));
8987 }
8988 }
8989
8990 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8991
8992 if (colName)
8993 {
8994 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8995
8996 if (!HeapTupleIsValid(tuple))
8997 ereport(ERROR,
8998 (errcode(ERRCODE_UNDEFINED_COLUMN),
8999 errmsg("column \"%s\" of relation \"%s\" does not exist",
9000 colName, RelationGetRelationName(rel))));
9001 }
9002 else
9003 {
9004 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
9005
9006 if (!HeapTupleIsValid(tuple))
9007 ereport(ERROR,
9008 (errcode(ERRCODE_UNDEFINED_COLUMN),
9009 errmsg("column number %d of relation \"%s\" does not exist",
9010 colNum, RelationGetRelationName(rel))));
9011 }
9012
9013 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9014
9015 attnum = attrtuple->attnum;
9016 if (attnum <= 0)
9017 ereport(ERROR,
9018 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9019 errmsg("cannot alter system column \"%s\"",
9020 colName)));
9021
9022 /*
9023 * Prevent this as long as the ANALYZE code skips virtual generated
9024 * columns.
9025 */
9026 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9027 ereport(ERROR,
9028 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9029 errmsg("cannot alter statistics on virtual generated column \"%s\"",
9030 colName)));
9031
9032 if (rel->rd_rel->relkind == RELKIND_INDEX ||
9033 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9034 {
9035 if (attnum > rel->rd_index->indnkeyatts)
9036 ereport(ERROR,
9037 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9038 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9039 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9040 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9041 ereport(ERROR,
9042 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9043 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9044 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9045 errhint("Alter statistics on table column instead.")));
9046 }
9047
9048 /* Build new tuple. */
9049 memset(repl_null, false, sizeof(repl_null));
9050 memset(repl_repl, false, sizeof(repl_repl));
9051 if (!newtarget_default)
9052 repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
9053 else
9054 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
9055 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
9056 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9057 repl_val, repl_null, repl_repl);
9058 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9059
9060 InvokeObjectPostAlterHook(RelationRelationId,
9061 RelationGetRelid(rel),
9062 attrtuple->attnum);
9063 ObjectAddressSubSet(address, RelationRelationId,
9064 RelationGetRelid(rel), attnum);
9065
9066 heap_freetuple(newtuple);
9067
9068 ReleaseSysCache(tuple);
9069
9070 table_close(attrelation, RowExclusiveLock);
9071
9072 return address;
9073}
9074
9075/*
9076 * Return value is the address of the modified column
9077 */
9078static ObjectAddress
9079ATExecSetOptions(Relation rel, const char *colName, Node *options,
9080 bool isReset, LOCKMODE lockmode)
9081{
9082 Relation attrelation;
9083 HeapTuple tuple,
9084 newtuple;
9085 Form_pg_attribute attrtuple;
9087 Datum datum,
9088 newOptions;
9089 bool isnull;
9090 ObjectAddress address;
9091 Datum repl_val[Natts_pg_attribute];
9092 bool repl_null[Natts_pg_attribute];
9093 bool repl_repl[Natts_pg_attribute];
9094
9095 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9096
9097 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9098
9099 if (!HeapTupleIsValid(tuple))
9100 ereport(ERROR,
9101 (errcode(ERRCODE_UNDEFINED_COLUMN),
9102 errmsg("column \"%s\" of relation \"%s\" does not exist",
9103 colName, RelationGetRelationName(rel))));
9104 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9105
9106 attnum = attrtuple->attnum;
9107 if (attnum <= 0)
9108 ereport(ERROR,
9109 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9110 errmsg("cannot alter system column \"%s\"",
9111 colName)));
9112
9113 /* Generate new proposed attoptions (text array) */
9114 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9115 &isnull);
9116 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9117 castNode(List, options), NULL, NULL,
9118 false, isReset);
9119 /* Validate new options */
9120 (void) attribute_reloptions(newOptions, true);
9121
9122 /* Build new tuple. */
9123 memset(repl_null, false, sizeof(repl_null));
9124 memset(repl_repl, false, sizeof(repl_repl));
9125 if (newOptions != (Datum) 0)
9126 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9127 else
9128 repl_null[Anum_pg_attribute_attoptions - 1] = true;
9129 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9130 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9131 repl_val, repl_null, repl_repl);
9132
9133 /* Update system catalog. */
9134 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9135
9136 InvokeObjectPostAlterHook(RelationRelationId,
9137 RelationGetRelid(rel),
9138 attrtuple->attnum);
9139 ObjectAddressSubSet(address, RelationRelationId,
9140 RelationGetRelid(rel), attnum);
9141
9142 heap_freetuple(newtuple);
9143
9144 ReleaseSysCache(tuple);
9145
9146 table_close(attrelation, RowExclusiveLock);
9147
9148 return address;
9149}
9150
9151/*
9152 * Helper function for ATExecSetStorage and ATExecSetCompression
9153 *
9154 * Set the attstorage and/or attcompression fields for index columns
9155 * associated with the specified table column.
9156 */
9157static void
9160 bool setstorage, char newstorage,
9161 bool setcompression, char newcompression,
9162 LOCKMODE lockmode)
9163{
9164 ListCell *lc;
9165
9166 foreach(lc, RelationGetIndexList(rel))
9167 {
9168 Oid indexoid = lfirst_oid(lc);
9169 Relation indrel;
9170 AttrNumber indattnum = 0;
9171 HeapTuple tuple;
9172
9173 indrel = index_open(indexoid, lockmode);
9174
9175 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9176 {
9177 if (indrel->rd_index->indkey.values[i] == attnum)
9178 {
9179 indattnum = i + 1;
9180 break;
9181 }
9182 }
9183
9184 if (indattnum == 0)
9185 {
9186 index_close(indrel, lockmode);
9187 continue;
9188 }
9189
9190 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9191
9192 if (HeapTupleIsValid(tuple))
9193 {
9194 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9195
9196 if (setstorage)
9197 attrtuple->attstorage = newstorage;
9198
9199 if (setcompression)
9200 attrtuple->attcompression = newcompression;
9201
9202 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9203
9204 InvokeObjectPostAlterHook(RelationRelationId,
9205 RelationGetRelid(rel),
9206 attrtuple->attnum);
9207
9208 heap_freetuple(tuple);
9209 }
9210
9211 index_close(indrel, lockmode);
9212 }
9213}
9214
9215/*
9216 * ALTER TABLE ALTER COLUMN SET STORAGE
9217 *
9218 * Return value is the address of the modified column
9219 */
9220static ObjectAddress
9221ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9222{
9223 Relation attrelation;
9224 HeapTuple tuple;
9225 Form_pg_attribute attrtuple;
9227 ObjectAddress address;
9228
9229 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9230
9231 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9232
9233 if (!HeapTupleIsValid(tuple))
9234 ereport(ERROR,
9235 (errcode(ERRCODE_UNDEFINED_COLUMN),
9236 errmsg("column \"%s\" of relation \"%s\" does not exist",
9237 colName, RelationGetRelationName(rel))));
9238 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9239
9240 attnum = attrtuple->attnum;
9241 if (attnum <= 0)
9242 ereport(ERROR,
9243 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9244 errmsg("cannot alter system column \"%s\"",
9245 colName)));
9246
9247 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9248
9249 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9250
9251 InvokeObjectPostAlterHook(RelationRelationId,
9252 RelationGetRelid(rel),
9253 attrtuple->attnum);
9254
9255 /*
9256 * Apply the change to indexes as well (only for simple index columns,
9257 * matching behavior of index.c ConstructTupleDescriptor()).
9258 */
9259 SetIndexStorageProperties(rel, attrelation, attnum,
9260 true, attrtuple->attstorage,
9261 false, 0,
9262 lockmode);
9263
9264 heap_freetuple(tuple);
9265
9266 table_close(attrelation, RowExclusiveLock);
9267
9268 ObjectAddressSubSet(address, RelationRelationId,
9269 RelationGetRelid(rel), attnum);
9270 return address;
9271}
9272
9273
9274/*
9275 * ALTER TABLE DROP COLUMN
9276 *
9277 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9278 * because we have to decide at runtime whether to recurse or not depending
9279 * on whether attinhcount goes to zero or not. (We can't check this in a
9280 * static pre-pass because it won't handle multiple inheritance situations
9281 * correctly.)
9282 */
9283static void
9284ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9285 AlterTableCmd *cmd, LOCKMODE lockmode,
9286 AlterTableUtilityContext *context)
9287{
9288 if (rel->rd_rel->reloftype && !recursing)
9289 ereport(ERROR,
9290 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9291 errmsg("cannot drop column from typed table")));
9292
9293 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9294 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9295
9296 if (recurse)
9297 cmd->recurse = true;
9298}
9299
9300/*
9301 * Drops column 'colName' from relation 'rel' and returns the address of the
9302 * dropped column. The column is also dropped (or marked as no longer
9303 * inherited from relation) from the relation's inheritance children, if any.
9304 *
9305 * In the recursive invocations for inheritance child relations, instead of
9306 * dropping the column directly (if to be dropped at all), its object address
9307 * is added to 'addrs', which must be non-NULL in such invocations. All
9308 * columns are dropped at the same time after all the children have been
9309 * checked recursively.
9310 */
9311static ObjectAddress
9312ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9313 DropBehavior behavior,
9314 bool recurse, bool recursing,
9315 bool missing_ok, LOCKMODE lockmode,
9316 ObjectAddresses *addrs)
9317{
9318 HeapTuple tuple;
9319 Form_pg_attribute targetatt;
9321 List *children;
9322 ObjectAddress object;
9323 bool is_expr;
9324
9325 /* At top level, permission check was done in ATPrepCmd, else do it */
9326 if (recursing)
9329
9330 /* Initialize addrs on the first invocation */
9331 Assert(!recursing || addrs != NULL);
9332
9333 /* since this function recurses, it could be driven to stack overflow */
9335
9336 if (!recursing)
9337 addrs = new_object_addresses();
9338
9339 /*
9340 * get the number of the attribute
9341 */
9342 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9343 if (!HeapTupleIsValid(tuple))
9344 {
9345 if (!missing_ok)
9346 {
9347 ereport(ERROR,
9348 (errcode(ERRCODE_UNDEFINED_COLUMN),
9349 errmsg("column \"%s\" of relation \"%s\" does not exist",
9350 colName, RelationGetRelationName(rel))));
9351 }
9352 else
9353 {
9355 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9356 colName, RelationGetRelationName(rel))));
9357 return InvalidObjectAddress;
9358 }
9359 }
9360 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9361
9362 attnum = targetatt->attnum;
9363
9364 /* Can't drop a system attribute */
9365 if (attnum <= 0)
9366 ereport(ERROR,
9367 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9368 errmsg("cannot drop system column \"%s\"",
9369 colName)));
9370
9371 /*
9372 * Don't drop inherited columns, unless recursing (presumably from a drop
9373 * of the parent column)
9374 */
9375 if (targetatt->attinhcount > 0 && !recursing)
9376 ereport(ERROR,
9377 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9378 errmsg("cannot drop inherited column \"%s\"",
9379 colName)));
9380
9381 /*
9382 * Don't drop columns used in the partition key, either. (If we let this
9383 * go through, the key column's dependencies would cause a cascaded drop
9384 * of the whole table, which is surely not what the user expected.)
9385 */
9386 if (has_partition_attrs(rel,
9388 &is_expr))
9389 ereport(ERROR,
9390 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9391 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9392 colName, RelationGetRelationName(rel))));
9393
9394 ReleaseSysCache(tuple);
9395
9396 /*
9397 * Propagate to children as appropriate. Unlike most other ALTER
9398 * routines, we have to do this one level of recursion at a time; we can't
9399 * use find_all_inheritors to do it in one pass.
9400 */
9401 children =
9403
9404 if (children)
9405 {
9406 Relation attr_rel;
9407 ListCell *child;
9408
9409 /*
9410 * In case of a partitioned table, the column must be dropped from the
9411 * partitions as well.
9412 */
9413 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9414 ereport(ERROR,
9415 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9416 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9417 errhint("Do not specify the ONLY keyword.")));
9418
9419 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9420 foreach(child, children)
9421 {
9422 Oid childrelid = lfirst_oid(child);
9423 Relation childrel;
9424 Form_pg_attribute childatt;
9425
9426 /* find_inheritance_children already got lock */
9427 childrel = table_open(childrelid, NoLock);
9428 CheckAlterTableIsSafe(childrel);
9429
9430 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9431 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9432 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9433 colName, childrelid);
9434 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9435
9436 if (childatt->attinhcount <= 0) /* shouldn't happen */
9437 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9438 childrelid, colName);
9439
9440 if (recurse)
9441 {
9442 /*
9443 * If the child column has other definition sources, just
9444 * decrement its inheritance count; if not, recurse to delete
9445 * it.
9446 */
9447 if (childatt->attinhcount == 1 && !childatt->attislocal)
9448 {
9449 /* Time to delete this child column, too */
9450 ATExecDropColumn(wqueue, childrel, colName,
9451 behavior, true, true,
9452 false, lockmode, addrs);
9453 }
9454 else
9455 {
9456 /* Child column must survive my deletion */
9457 childatt->attinhcount--;
9458
9459 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9460
9461 /* Make update visible */
9463 }
9464 }
9465 else
9466 {
9467 /*
9468 * If we were told to drop ONLY in this table (no recursion),
9469 * we need to mark the inheritors' attributes as locally
9470 * defined rather than inherited.
9471 */
9472 childatt->attinhcount--;
9473 childatt->attislocal = true;
9474
9475 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9476
9477 /* Make update visible */
9479 }
9480
9481 heap_freetuple(tuple);
9482
9483 table_close(childrel, NoLock);
9484 }
9485 table_close(attr_rel, RowExclusiveLock);
9486 }
9487
9488 /* Add object to delete */
9489 object.classId = RelationRelationId;
9490 object.objectId = RelationGetRelid(rel);
9491 object.objectSubId = attnum;
9492 add_exact_object_address(&object, addrs);
9493
9494 if (!recursing)
9495 {
9496 /* Recursion has ended, drop everything that was collected */
9497 performMultipleDeletions(addrs, behavior, 0);
9498 free_object_addresses(addrs);
9499 }
9500
9501 return object;
9502}
9503
9504/*
9505 * Prepare to add a primary key on a table, by adding not-null constraints
9506 * on all columns.
9507 *
9508 * The not-null constraints for a primary key must cover the whole inheritance
9509 * hierarchy (failing to ensure that leads to funny corner cases). For the
9510 * normal case where we're asked to recurse, this routine checks if the
9511 * not-null constraints exist already, and if not queues a requirement for
9512 * them to be created by phase 2.
9513 *
9514 * For the case where we're asked not to recurse, we verify that a not-null
9515 * constraint exists on each column of each (direct) child table, throwing an
9516 * error if not. Not throwing an error would also work, because a not-null
9517 * constraint would be created anyway, but it'd cause a silent scan of the
9518 * child table to verify absence of nulls. We prefer to let the user know so
9519 * that they can add the constraint manually without having to hold
9520 * AccessExclusiveLock while at it.
9521 *
9522 * However, it's also important that we do not acquire locks on children if
9523 * the not-null constraints already exist on the parent, to avoid risking
9524 * deadlocks during parallel pg_restore of PKs on partitioned tables.
9525 */
9526static void
9528 bool recurse, LOCKMODE lockmode,
9529 AlterTableUtilityContext *context)
9530{
9531 Constraint *pkconstr;
9532 List *children = NIL;
9533 bool got_children = false;
9534
9535 pkconstr = castNode(Constraint, cmd->def);
9536 if (pkconstr->contype != CONSTR_PRIMARY)
9537 return;
9538
9539 /* Verify that columns are not-null, or request that they be made so */
9540 foreach_node(String, column, pkconstr->keys)
9541 {
9542 AlterTableCmd *newcmd;
9543 Constraint *nnconstr;
9544 HeapTuple tuple;
9545
9546 /*
9547 * First check if a suitable constraint exists. If it does, we don't
9548 * need to request another one. We do need to bail out if it's not
9549 * valid, though.
9550 */
9551 tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9552 if (tuple != NULL)
9553 {
9554 verifyNotNullPKCompatible(tuple, strVal(column));
9555
9556 /* All good with this one; don't request another */
9557 heap_freetuple(tuple);
9558 continue;
9559 }
9560 else if (!recurse)
9561 {
9562 /*
9563 * No constraint on this column. Asked not to recurse, we won't
9564 * create one here, but verify that all children have one.
9565 */
9566 if (!got_children)
9567 {
9569 lockmode);
9570 /* only search for children on the first time through */
9571 got_children = true;
9572 }
9573
9574 foreach_oid(childrelid, children)
9575 {
9576 HeapTuple tup;
9577
9578 tup = findNotNullConstraint(childrelid, strVal(column));
9579 if (!tup)
9580 ereport(ERROR,
9581 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9582 strVal(column), get_rel_name(childrelid)));
9583 /* verify it's good enough */
9584 verifyNotNullPKCompatible(tup, strVal(column));
9585 }
9586 }
9587
9588 /* This column is not already not-null, so add it to the queue */
9589 nnconstr = makeNotNullConstraint(column);
9590
9591 newcmd = makeNode(AlterTableCmd);
9592 newcmd->subtype = AT_AddConstraint;
9593 /* note we force recurse=true here; see above */
9594 newcmd->recurse = true;
9595 newcmd->def = (Node *) nnconstr;
9596
9597 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9598 }
9599}
9600
9601/*
9602 * Verify whether the given not-null constraint is compatible with a
9603 * primary key. If not, an error is thrown.
9604 */
9605static void
9606verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9607{
9609
9610 if (conForm->contype != CONSTRAINT_NOTNULL)
9611 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9612
9613 /* a NO INHERIT constraint is no good */
9614 if (conForm->connoinherit)
9615 ereport(ERROR,
9616 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9617 errmsg("cannot create primary key on column \"%s\"", colname),
9618 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9619 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9620 NameStr(conForm->conname), colname,
9621 get_rel_name(conForm->conrelid), "NO INHERIT"),
9622 errhint("You might need to make the existing constraint inheritable using %s.",
9623 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9624
9625 /* an unvalidated constraint is no good */
9626 if (!conForm->convalidated)
9627 ereport(ERROR,
9628 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9629 errmsg("cannot create primary key on column \"%s\"", colname),
9630 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9631 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9632 NameStr(conForm->conname), colname,
9633 get_rel_name(conForm->conrelid), "NOT VALID"),
9634 errhint("You might need to validate it using %s.",
9635 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9636}
9637
9638/*
9639 * ALTER TABLE ADD INDEX
9640 *
9641 * There is no such command in the grammar, but parse_utilcmd.c converts
9642 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9643 * us schedule creation of the index at the appropriate time during ALTER.
9644 *
9645 * Return value is the address of the new index.
9646 */
9647static ObjectAddress
9649 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9650{
9651 bool check_rights;
9652 bool skip_build;
9653 bool quiet;
9654 ObjectAddress address;
9655
9657 Assert(!stmt->concurrent);
9658
9659 /* The IndexStmt has already been through transformIndexStmt */
9660 Assert(stmt->transformed);
9661
9662 /* suppress schema rights check when rebuilding existing index */
9663 check_rights = !is_rebuild;
9664 /* skip index build if phase 3 will do it or we're reusing an old one */
9665 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9666 /* suppress notices when rebuilding existing index */
9667 quiet = is_rebuild;
9668
9669 address = DefineIndex(RelationGetRelid(rel),
9670 stmt,
9671 InvalidOid, /* no predefined OID */
9672 InvalidOid, /* no parent index */
9673 InvalidOid, /* no parent constraint */
9674 -1, /* total_parts unknown */
9675 true, /* is_alter_table */
9676 check_rights,
9677 false, /* check_not_in_use - we did it already */
9678 skip_build,
9679 quiet);
9680
9681 /*
9682 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9683 * new index instead of building from scratch. Restore associated fields.
9684 * This may store InvalidSubTransactionId in both fields, in which case
9685 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9686 * this after the CCI that made catalog rows visible to any rebuild. The
9687 * DROP of the old edition of this index will have scheduled the storage
9688 * for deletion at commit, so cancel that pending deletion.
9689 */
9690 if (RelFileNumberIsValid(stmt->oldNumber))
9691 {
9692 Relation irel = index_open(address.objectId, NoLock);
9693
9694 irel->rd_createSubid = stmt->oldCreateSubid;
9695 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9697 index_close(irel, NoLock);
9698 }
9699
9700 return address;
9701}
9702
9703/*
9704 * ALTER TABLE ADD STATISTICS
9705 *
9706 * This is no such command in the grammar, but we use this internally to add
9707 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9708 * column type change.
9709 */
9710static ObjectAddress
9712 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9713{
9714 ObjectAddress address;
9715
9717
9718 /* The CreateStatsStmt has already been through transformStatsStmt */
9719 Assert(stmt->transformed);
9720
9721 address = CreateStatistics(stmt, !is_rebuild);
9722
9723 return address;
9724}
9725
9726/*
9727 * ALTER TABLE ADD CONSTRAINT USING INDEX
9728 *
9729 * Returns the address of the new constraint.
9730 */
9731static ObjectAddress
9733 IndexStmt *stmt, LOCKMODE lockmode)
9734{
9735 Oid index_oid = stmt->indexOid;
9736 Relation indexRel;
9737 char *indexName;
9738 IndexInfo *indexInfo;
9739 char *constraintName;
9740 char constraintType;
9741 ObjectAddress address;
9742 bits16 flags;
9743
9745 Assert(OidIsValid(index_oid));
9746 Assert(stmt->isconstraint);
9747
9748 /*
9749 * Doing this on partitioned tables is not a simple feature to implement,
9750 * so let's punt for now.
9751 */
9752 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9753 ereport(ERROR,
9754 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9755 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9756
9757 indexRel = index_open(index_oid, AccessShareLock);
9758
9759 indexName = pstrdup(RelationGetRelationName(indexRel));
9760
9761 indexInfo = BuildIndexInfo(indexRel);
9762
9763 /* this should have been checked at parse time */
9764 if (!indexInfo->ii_Unique)
9765 elog(ERROR, "index \"%s\" is not unique", indexName);
9766
9767 /*
9768 * Determine name to assign to constraint. We require a constraint to
9769 * have the same name as the underlying index; therefore, use the index's
9770 * existing name as the default constraint name, and if the user
9771 * explicitly gives some other name for the constraint, rename the index
9772 * to match.
9773 */
9774 constraintName = stmt->idxname;
9775 if (constraintName == NULL)
9776 constraintName = indexName;
9777 else if (strcmp(constraintName, indexName) != 0)
9778 {
9780 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9781 indexName, constraintName)));
9782 RenameRelationInternal(index_oid, constraintName, false, true);
9783 }
9784
9785 /* Extra checks needed if making primary key */
9786 if (stmt->primary)
9787 index_check_primary_key(rel, indexInfo, true, stmt);
9788
9789 /* Note we currently don't support EXCLUSION constraints here */
9790 if (stmt->primary)
9791 constraintType = CONSTRAINT_PRIMARY;
9792 else
9793 constraintType = CONSTRAINT_UNIQUE;
9794
9795 /* Create the catalog entries for the constraint */
9798 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9799 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9801
9802 address = index_constraint_create(rel,
9803 index_oid,
9804 InvalidOid,
9805 indexInfo,
9806 constraintName,
9807 constraintType,
9808 flags,
9810 false); /* is_internal */
9811
9812 index_close(indexRel, NoLock);
9813
9814 return address;
9815}
9816
9817/*
9818 * ALTER TABLE ADD CONSTRAINT
9819 *
9820 * Return value is the address of the new constraint; if no constraint was
9821 * added, InvalidObjectAddress is returned.
9822 */
9823static ObjectAddress
9825 Constraint *newConstraint, bool recurse, bool is_readd,
9826 LOCKMODE lockmode)
9827{
9829
9830 Assert(IsA(newConstraint, Constraint));
9831
9832 /*
9833 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9834 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9835 * parse_utilcmd.c).
9836 */
9837 switch (newConstraint->contype)
9838 {
9839 case CONSTR_CHECK:
9840 case CONSTR_NOTNULL:
9841 address =
9842 ATAddCheckNNConstraint(wqueue, tab, rel,
9843 newConstraint, recurse, false, is_readd,
9844 lockmode);
9845 break;
9846
9847 case CONSTR_FOREIGN:
9848
9849 /*
9850 * Assign or validate constraint name
9851 */
9852 if (newConstraint->conname)
9853 {
9855 RelationGetRelid(rel),
9856 newConstraint->conname))
9857 ereport(ERROR,
9859 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9860 newConstraint->conname,
9862 }
9863 else
9864 newConstraint->conname =
9867 "fkey",
9869 NIL);
9870
9871 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9872 newConstraint,
9873 recurse, false,
9874 lockmode);
9875 break;
9876
9877 default:
9878 elog(ERROR, "unrecognized constraint type: %d",
9879 (int) newConstraint->contype);
9880 }
9881
9882 return address;
9883}
9884
9885/*
9886 * Generate the column-name portion of the constraint name for a new foreign
9887 * key given the list of column names that reference the referenced
9888 * table. This will be passed to ChooseConstraintName along with the parent
9889 * table name and the "fkey" suffix.
9890 *
9891 * We know that less than NAMEDATALEN characters will actually be used, so we
9892 * can truncate the result once we've generated that many.
9893 *
9894 * XXX see also ChooseExtendedStatisticNameAddition and
9895 * ChooseIndexNameAddition.
9896 */
9897static char *
9899{
9900 char buf[NAMEDATALEN * 2];
9901 int buflen = 0;
9902 ListCell *lc;
9903
9904 buf[0] = '\0';
9905 foreach(lc, colnames)
9906 {
9907 const char *name = strVal(lfirst(lc));
9908
9909 if (buflen > 0)
9910 buf[buflen++] = '_'; /* insert _ between names */
9911
9912 /*
9913 * At this point we have buflen <= NAMEDATALEN. name should be less
9914 * than NAMEDATALEN already, but use strlcpy for paranoia.
9915 */
9916 strlcpy(buf + buflen, name, NAMEDATALEN);
9917 buflen += strlen(buf + buflen);
9918 if (buflen >= NAMEDATALEN)
9919 break;
9920 }
9921 return pstrdup(buf);
9922}
9923
9924/*
9925 * Add a check or not-null constraint to a single table and its children.
9926 * Returns the address of the constraint added to the parent relation,
9927 * if one gets added, or InvalidObjectAddress otherwise.
9928 *
9929 * Subroutine for ATExecAddConstraint.
9930 *
9931 * We must recurse to child tables during execution, rather than using
9932 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9933 * constraints *must* be given the same name, else they won't be seen as
9934 * related later. If the user didn't explicitly specify a name, then
9935 * AddRelationNewConstraints would normally assign different names to the
9936 * child constraints. To fix that, we must capture the name assigned at
9937 * the parent table and pass that down.
9938 */
9939static ObjectAddress
9941 Constraint *constr, bool recurse, bool recursing,
9942 bool is_readd, LOCKMODE lockmode)
9943{
9944 List *newcons;
9945 ListCell *lcon;
9946 List *children;
9947 ListCell *child;
9949
9950 /* Guard against stack overflow due to overly deep inheritance tree. */
9952
9953 /* At top level, permission check was done in ATPrepCmd, else do it */
9954 if (recursing)
9957
9958 /*
9959 * Call AddRelationNewConstraints to do the work, making sure it works on
9960 * a copy of the Constraint so transformExpr can't modify the original. It
9961 * returns a list of cooked constraints.
9962 *
9963 * If the constraint ends up getting merged with a pre-existing one, it's
9964 * omitted from the returned list, which is what we want: we do not need
9965 * to do any validation work. That can only happen at child tables,
9966 * though, since we disallow merging at the top level.
9967 */
9968 newcons = AddRelationNewConstraints(rel, NIL,
9969 list_make1(copyObject(constr)),
9970 recursing || is_readd, /* allow_merge */
9971 !recursing, /* is_local */
9972 is_readd, /* is_internal */
9973 NULL); /* queryString not available
9974 * here */
9975
9976 /* we don't expect more than one constraint here */
9977 Assert(list_length(newcons) <= 1);
9978
9979 /* Add each to-be-validated constraint to Phase 3's queue */
9980 foreach(lcon, newcons)
9981 {
9982 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9983
9984 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9985 {
9986 NewConstraint *newcon;
9987
9988 newcon = palloc0_object(NewConstraint);
9989 newcon->name = ccon->name;
9990 newcon->contype = ccon->contype;
9991 newcon->qual = ccon->expr;
9992
9993 tab->constraints = lappend(tab->constraints, newcon);
9994 }
9995
9996 /* Save the actually assigned name if it was defaulted */
9997 if (constr->conname == NULL)
9998 constr->conname = ccon->name;
9999
10000 /*
10001 * If adding a valid not-null constraint, set the pg_attribute flag
10002 * and tell phase 3 to verify existing rows, if needed. For an
10003 * invalid constraint, just set attnotnull, without queueing
10004 * verification.
10005 */
10006 if (constr->contype == CONSTR_NOTNULL)
10007 set_attnotnull(wqueue, rel, ccon->attnum,
10008 !constr->skip_validation,
10009 !constr->skip_validation);
10010
10011 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
10012 }
10013
10014 /* At this point we must have a locked-down name to use */
10015 Assert(newcons == NIL || constr->conname != NULL);
10016
10017 /* Advance command counter in case same table is visited multiple times */
10019
10020 /*
10021 * If the constraint got merged with an existing constraint, we're done.
10022 * We mustn't recurse to child tables in this case, because they've
10023 * already got the constraint, and visiting them again would lead to an
10024 * incorrect value for coninhcount.
10025 */
10026 if (newcons == NIL)
10027 return address;
10028
10029 /*
10030 * If adding a NO INHERIT constraint, no need to find our children.
10031 */
10032 if (constr->is_no_inherit)
10033 return address;
10034
10035 /*
10036 * Propagate to children as appropriate. Unlike most other ALTER
10037 * routines, we have to do this one level of recursion at a time; we can't
10038 * use find_all_inheritors to do it in one pass.
10039 */
10040 children =
10042
10043 /*
10044 * Check if ONLY was specified with ALTER TABLE. If so, allow the
10045 * constraint creation only if there are no children currently. Error out
10046 * otherwise.
10047 */
10048 if (!recurse && children != NIL)
10049 ereport(ERROR,
10050 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10051 errmsg("constraint must be added to child tables too")));
10052
10053 /*
10054 * Recurse to create the constraint on each child.
10055 */
10056 foreach(child, children)
10057 {
10058 Oid childrelid = lfirst_oid(child);
10059 Relation childrel;
10060 AlteredTableInfo *childtab;
10061
10062 /* find_inheritance_children already got lock */
10063 childrel = table_open(childrelid, NoLock);
10064 CheckAlterTableIsSafe(childrel);
10065
10066 /* Find or create work queue entry for this table */
10067 childtab = ATGetQueueEntry(wqueue, childrel);
10068
10069 /* Recurse to this child */
10070 ATAddCheckNNConstraint(wqueue, childtab, childrel,
10071 constr, recurse, true, is_readd, lockmode);
10072
10073 table_close(childrel, NoLock);
10074 }
10075
10076 return address;
10077}
10078
10079/*
10080 * Add a foreign-key constraint to a single table; return the new constraint's
10081 * address.
10082 *
10083 * Subroutine for ATExecAddConstraint. Must already hold exclusive
10084 * lock on the rel, and have done appropriate validity checks for it.
10085 * We do permissions checks here, however.
10086 *
10087 * When the referenced or referencing tables (or both) are partitioned,
10088 * multiple pg_constraint rows are required -- one for each partitioned table
10089 * and each partition on each side (fortunately, not one for every combination
10090 * thereof). We also need action triggers on each leaf partition on the
10091 * referenced side, and check triggers on each leaf partition on the
10092 * referencing side.
10093 */
10094static ObjectAddress
10096 Constraint *fkconstraint,
10097 bool recurse, bool recursing, LOCKMODE lockmode)
10098{
10099 Relation pkrel;
10100 int16 pkattnum[INDEX_MAX_KEYS] = {0};
10101 int16 fkattnum[INDEX_MAX_KEYS] = {0};
10102 Oid pktypoid[INDEX_MAX_KEYS] = {0};
10103 Oid fktypoid[INDEX_MAX_KEYS] = {0};
10104 Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10105 Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10106 Oid opclasses[INDEX_MAX_KEYS] = {0};
10107 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10108 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10109 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10110 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10111 bool with_period;
10112 bool pk_has_without_overlaps;
10113 int i;
10114 int numfks,
10115 numpks,
10116 numfkdelsetcols;
10117 Oid indexOid;
10118 bool old_check_ok;
10119 ObjectAddress address;
10120 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10121
10122 /*
10123 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10124 * delete rows out from under us.
10125 */
10126 if (OidIsValid(fkconstraint->old_pktable_oid))
10127 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10128 else
10129 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10130
10131 /*
10132 * Validity checks (permission checks wait till we have the column
10133 * numbers)
10134 */
10135 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10136 ereport(ERROR,
10137 errcode(ERRCODE_WRONG_OBJECT_TYPE),
10138 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10140 RelationGetRelationName(pkrel)));
10141
10142 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10143 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10144 ereport(ERROR,
10145 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10146 errmsg("referenced relation \"%s\" is not a table",
10147 RelationGetRelationName(pkrel))));
10148
10150 ereport(ERROR,
10151 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10152 errmsg("permission denied: \"%s\" is a system catalog",
10153 RelationGetRelationName(pkrel))));
10154
10155 /*
10156 * References from permanent or unlogged tables to temp tables, and from
10157 * permanent tables to unlogged tables, are disallowed because the
10158 * referenced data can vanish out from under us. References from temp
10159 * tables to any other table type are also disallowed, because other
10160 * backends might need to run the RI triggers on the perm table, but they
10161 * can't reliably see tuples in the local buffers of other backends.
10162 */
10163 switch (rel->rd_rel->relpersistence)
10164 {
10165 case RELPERSISTENCE_PERMANENT:
10166 if (!RelationIsPermanent(pkrel))
10167 ereport(ERROR,
10168 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10169 errmsg("constraints on permanent tables may reference only permanent tables")));
10170 break;
10171 case RELPERSISTENCE_UNLOGGED:
10172 if (!RelationIsPermanent(pkrel)
10173 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10174 ereport(ERROR,
10175 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10176 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10177 break;
10178 case RELPERSISTENCE_TEMP:
10179 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10180 ereport(ERROR,
10181 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10182 errmsg("constraints on temporary tables may reference only temporary tables")));
10183 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10184 ereport(ERROR,
10185 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10186 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10187 break;
10188 }
10189
10190 /*
10191 * Look up the referencing attributes to make sure they exist, and record
10192 * their attnums and type and collation OIDs.
10193 */
10195 fkconstraint->fk_attrs,
10196 fkattnum, fktypoid, fkcolloid);
10197 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10198 if (with_period && !fkconstraint->fk_with_period)
10199 ereport(ERROR,
10200 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10201 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10202
10203 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10204 fkconstraint->fk_del_set_cols,
10205 fkdelsetcols, NULL, NULL);
10206 numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10207 numfkdelsetcols,
10208 fkdelsetcols,
10209 fkconstraint->fk_del_set_cols);
10210
10211 /*
10212 * If the attribute list for the referenced table was omitted, lookup the
10213 * definition of the primary key and use it. Otherwise, validate the
10214 * supplied attribute list. In either case, discover the index OID and
10215 * index opclasses, and the attnums and type and collation OIDs of the
10216 * attributes.
10217 */
10218 if (fkconstraint->pk_attrs == NIL)
10219 {
10220 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10221 &fkconstraint->pk_attrs,
10222 pkattnum, pktypoid, pkcolloid,
10223 opclasses, &pk_has_without_overlaps);
10224
10225 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10226 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10227 ereport(ERROR,
10228 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10229 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10230 }
10231 else
10232 {
10234 fkconstraint->pk_attrs,
10235 pkattnum, pktypoid, pkcolloid);
10236
10237 /* Since we got pk_attrs, one should be a period. */
10238 if (with_period && !fkconstraint->pk_with_period)
10239 ereport(ERROR,
10240 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10241 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10242
10243 /* Look for an index matching the column list */
10244 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10245 with_period, opclasses, &pk_has_without_overlaps);
10246 }
10247
10248 /*
10249 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10250 * must use PERIOD.
10251 */
10252 if (pk_has_without_overlaps && !with_period)
10253 ereport(ERROR,
10254 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10255 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10256
10257 /*
10258 * Now we can check permissions.
10259 */
10260 checkFkeyPermissions(pkrel, pkattnum, numpks);
10261
10262 /*
10263 * Check some things for generated columns.
10264 */
10265 for (i = 0; i < numfks; i++)
10266 {
10267 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10268
10269 if (attgenerated)
10270 {
10271 /*
10272 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10273 */
10274 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10275 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10276 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10277 ereport(ERROR,
10278 (errcode(ERRCODE_SYNTAX_ERROR),
10279 errmsg("invalid %s action for foreign key constraint containing generated column",
10280 "ON UPDATE")));
10281 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10283 ereport(ERROR,
10284 (errcode(ERRCODE_SYNTAX_ERROR),
10285 errmsg("invalid %s action for foreign key constraint containing generated column",
10286 "ON DELETE")));
10287 }
10288
10289 /*
10290 * FKs on virtual columns are not supported. This would require
10291 * various additional support in ri_triggers.c, including special
10292 * handling in ri_NullCheck(), ri_KeysEqual(),
10293 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10294 * as NULL there). Also not really practical as long as you can't
10295 * index virtual columns.
10296 */
10297 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10298 ereport(ERROR,
10299 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10300 errmsg("foreign key constraints on virtual generated columns are not supported")));
10301 }
10302
10303 /*
10304 * Some actions are currently unsupported for foreign keys using PERIOD.
10305 */
10306 if (fkconstraint->fk_with_period)
10307 {
10308 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10309 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10310 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10312 ereport(ERROR,
10313 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10314 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10315 "ON UPDATE"));
10316
10317 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10318 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10319 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10321 ereport(ERROR,
10322 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10323 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10324 "ON DELETE"));
10325 }
10326
10327 /*
10328 * Look up the equality operators to use in the constraint.
10329 *
10330 * Note that we have to be careful about the difference between the actual
10331 * PK column type and the opclass' declared input type, which might be
10332 * only binary-compatible with it. The declared opcintype is the right
10333 * thing to probe pg_amop with.
10334 */
10335 if (numfks != numpks)
10336 ereport(ERROR,
10337 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10338 errmsg("number of referencing and referenced columns for foreign key disagree")));
10339
10340 /*
10341 * On the strength of a previous constraint, we might avoid scanning
10342 * tables to validate this one. See below.
10343 */
10344 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10345 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10346
10347 for (i = 0; i < numpks; i++)
10348 {
10349 Oid pktype = pktypoid[i];
10350 Oid fktype = fktypoid[i];
10351 Oid fktyped;
10352 Oid pkcoll = pkcolloid[i];
10353 Oid fkcoll = fkcolloid[i];
10354 HeapTuple cla_ht;
10355 Form_pg_opclass cla_tup;
10356 Oid amid;
10357 Oid opfamily;
10358 Oid opcintype;
10359 bool for_overlaps;
10360 CompareType cmptype;
10361 Oid pfeqop;
10362 Oid ppeqop;
10363 Oid ffeqop;
10364 int16 eqstrategy;
10365 Oid pfeqop_right;
10366
10367 /* We need several fields out of the pg_opclass entry */
10368 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10369 if (!HeapTupleIsValid(cla_ht))
10370 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10371 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10372 amid = cla_tup->opcmethod;
10373 opfamily = cla_tup->opcfamily;
10374 opcintype = cla_tup->opcintype;
10375 ReleaseSysCache(cla_ht);
10376
10377 /*
10378 * Get strategy number from index AM.
10379 *
10380 * For a normal foreign-key constraint, this should not fail, since we
10381 * already checked that the index is unique and should therefore have
10382 * appropriate equal operators. For a period foreign key, this could
10383 * fail if we selected a non-matching exclusion constraint earlier.
10384 * (XXX Maybe we should do these lookups earlier so we don't end up
10385 * doing that.)
10386 */
10387 for_overlaps = with_period && i == numpks - 1;
10388 cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10389 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10390 if (eqstrategy == InvalidStrategy)
10391 ereport(ERROR,
10392 errcode(ERRCODE_UNDEFINED_OBJECT),
10393 for_overlaps
10394 ? errmsg("could not identify an overlaps operator for foreign key")
10395 : errmsg("could not identify an equality operator for foreign key"),
10396 errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10397 cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10398
10399 /*
10400 * There had better be a primary equality operator for the index.
10401 * We'll use it for PK = PK comparisons.
10402 */
10403 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10404 eqstrategy);
10405
10406 if (!OidIsValid(ppeqop))
10407 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10408 eqstrategy, opcintype, opcintype, opfamily);
10409
10410 /*
10411 * Are there equality operators that take exactly the FK type? Assume
10412 * we should look through any domain here.
10413 */
10414 fktyped = getBaseType(fktype);
10415
10416 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10417 eqstrategy);
10418 if (OidIsValid(pfeqop))
10419 {
10420 pfeqop_right = fktyped;
10421 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10422 eqstrategy);
10423 }
10424 else
10425 {
10426 /* keep compiler quiet */
10427 pfeqop_right = InvalidOid;
10428 ffeqop = InvalidOid;
10429 }
10430
10431 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10432 {
10433 /*
10434 * Otherwise, look for an implicit cast from the FK type to the
10435 * opcintype, and if found, use the primary equality operator.
10436 * This is a bit tricky because opcintype might be a polymorphic
10437 * type such as ANYARRAY or ANYENUM; so what we have to test is
10438 * whether the two actual column types can be concurrently cast to
10439 * that type. (Otherwise, we'd fail to reject combinations such
10440 * as int[] and point[].)
10441 */
10442 Oid input_typeids[2];
10443 Oid target_typeids[2];
10444
10445 input_typeids[0] = pktype;
10446 input_typeids[1] = fktype;
10447 target_typeids[0] = opcintype;
10448 target_typeids[1] = opcintype;
10449 if (can_coerce_type(2, input_typeids, target_typeids,
10451 {
10452 pfeqop = ffeqop = ppeqop;
10453 pfeqop_right = opcintype;
10454 }
10455 }
10456
10457 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10458 ereport(ERROR,
10459 (errcode(ERRCODE_DATATYPE_MISMATCH),
10460 errmsg("foreign key constraint \"%s\" cannot be implemented",
10461 fkconstraint->conname),
10462 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10463 "are of incompatible types: %s and %s.",
10464 strVal(list_nth(fkconstraint->fk_attrs, i)),
10465 strVal(list_nth(fkconstraint->pk_attrs, i)),
10466 format_type_be(fktype),
10467 format_type_be(pktype))));
10468
10469 /*
10470 * This shouldn't be possible, but better check to make sure we have a
10471 * consistent state for the check below.
10472 */
10473 if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10474 elog(ERROR, "key columns are not both collatable");
10475
10476 if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10477 {
10478 bool pkcolldet;
10479 bool fkcolldet;
10480
10481 pkcolldet = get_collation_isdeterministic(pkcoll);
10482 fkcolldet = get_collation_isdeterministic(fkcoll);
10483
10484 /*
10485 * SQL requires that both collations are the same. This is
10486 * because we need a consistent notion of equality on both
10487 * columns. We relax this by allowing different collations if
10488 * they are both deterministic. (This is also for backward
10489 * compatibility, because PostgreSQL has always allowed this.)
10490 */
10491 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10492 ereport(ERROR,
10493 (errcode(ERRCODE_COLLATION_MISMATCH),
10494 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10495 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10496 "have incompatible collations: \"%s\" and \"%s\". "
10497 "If either collation is nondeterministic, then both collations have to be the same.",
10498 strVal(list_nth(fkconstraint->fk_attrs, i)),
10499 strVal(list_nth(fkconstraint->pk_attrs, i)),
10500 get_collation_name(fkcoll),
10501 get_collation_name(pkcoll))));
10502 }
10503
10504 if (old_check_ok)
10505 {
10506 /*
10507 * When a pfeqop changes, revalidate the constraint. We could
10508 * permit intra-opfamily changes, but that adds subtle complexity
10509 * without any concrete benefit for core types. We need not
10510 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10511 */
10512 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10513 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10514 old_pfeqop_item);
10515 }
10516 if (old_check_ok)
10517 {
10518 Oid old_fktype;
10519 Oid new_fktype;
10520 CoercionPathType old_pathtype;
10521 CoercionPathType new_pathtype;
10522 Oid old_castfunc;
10523 Oid new_castfunc;
10524 Oid old_fkcoll;
10525 Oid new_fkcoll;
10527 fkattnum[i] - 1);
10528
10529 /*
10530 * Identify coercion pathways from each of the old and new FK-side
10531 * column types to the right (foreign) operand type of the pfeqop.
10532 * We may assume that pg_constraint.conkey is not changing.
10533 */
10534 old_fktype = attr->atttypid;
10535 new_fktype = fktype;
10536 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10537 &old_castfunc);
10538 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10539 &new_castfunc);
10540
10541 old_fkcoll = attr->attcollation;
10542 new_fkcoll = fkcoll;
10543
10544 /*
10545 * Upon a change to the cast from the FK column to its pfeqop
10546 * operand, revalidate the constraint. For this evaluation, a
10547 * binary coercion cast is equivalent to no cast at all. While
10548 * type implementors should design implicit casts with an eye
10549 * toward consistency of operations like equality, we cannot
10550 * assume here that they have done so.
10551 *
10552 * A function with a polymorphic argument could change behavior
10553 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10554 * when the cast destination is polymorphic, we only avoid
10555 * revalidation if the input type has not changed at all. Given
10556 * just the core data types and operator classes, this requirement
10557 * prevents no would-be optimizations.
10558 *
10559 * If the cast converts from a base type to a domain thereon, then
10560 * that domain type must be the opcintype of the unique index.
10561 * Necessarily, the primary key column must then be of the domain
10562 * type. Since the constraint was previously valid, all values on
10563 * the foreign side necessarily exist on the primary side and in
10564 * turn conform to the domain. Consequently, we need not treat
10565 * domains specially here.
10566 *
10567 * If the collation changes, revalidation is required, unless both
10568 * collations are deterministic, because those share the same
10569 * notion of equality (because texteq reduces to bitwise
10570 * equality).
10571 *
10572 * We need not directly consider the PK type. It's necessarily
10573 * binary coercible to the opcintype of the unique index column,
10574 * and ri_triggers.c will only deal with PK datums in terms of
10575 * that opcintype. Changing the opcintype also changes pfeqop.
10576 */
10577 old_check_ok = (new_pathtype == old_pathtype &&
10578 new_castfunc == old_castfunc &&
10579 (!IsPolymorphicType(pfeqop_right) ||
10580 new_fktype == old_fktype) &&
10581 (new_fkcoll == old_fkcoll ||
10582 (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10583 }
10584
10585 pfeqoperators[i] = pfeqop;
10586 ppeqoperators[i] = ppeqop;
10587 ffeqoperators[i] = ffeqop;
10588 }
10589
10590 /*
10591 * For FKs with PERIOD we need additional operators to check whether the
10592 * referencing row's range is contained by the aggregated ranges of the
10593 * referenced row(s). For rangetypes and multirangetypes this is
10594 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10595 * support for now. FKs will look these up at "runtime", but we should
10596 * make sure the lookup works here, even if we don't use the values.
10597 */
10598 if (with_period)
10599 {
10600 Oid periodoperoid;
10601 Oid aggedperiodoperoid;
10602 Oid intersectoperoid;
10603
10604 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10605 &intersectoperoid);
10606 }
10607
10608 /* First, create the constraint catalog entry itself. */
10610 fkconstraint->conname, fkconstraint, rel, pkrel,
10611 indexOid,
10612 InvalidOid, /* no parent constraint */
10613 numfks,
10614 pkattnum,
10615 fkattnum,
10616 pfeqoperators,
10617 ppeqoperators,
10618 ffeqoperators,
10619 numfkdelsetcols,
10620 fkdelsetcols,
10621 false,
10622 with_period);
10623
10624 /* Next process the action triggers at the referenced side and recurse */
10625 addFkRecurseReferenced(fkconstraint, rel, pkrel,
10626 indexOid,
10627 address.objectId,
10628 numfks,
10629 pkattnum,
10630 fkattnum,
10631 pfeqoperators,
10632 ppeqoperators,
10633 ffeqoperators,
10634 numfkdelsetcols,
10635 fkdelsetcols,
10636 old_check_ok,
10638 with_period);
10639
10640 /* Lastly create the check triggers at the referencing side and recurse */
10641 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10642 indexOid,
10643 address.objectId,
10644 numfks,
10645 pkattnum,
10646 fkattnum,
10647 pfeqoperators,
10648 ppeqoperators,
10649 ffeqoperators,
10650 numfkdelsetcols,
10651 fkdelsetcols,
10652 old_check_ok,
10653 lockmode,
10655 with_period);
10656
10657 /*
10658 * Done. Close pk table, but keep lock until we've committed.
10659 */
10660 table_close(pkrel, NoLock);
10661
10662 return address;
10663}
10664
10665/*
10666 * validateFkOnDeleteSetColumns
10667 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10668 * column lists are valid.
10669 *
10670 * If there are duplicates in the fksetcolsattnums[] array, this silently
10671 * removes the dups. The new count of numfksetcols is returned.
10672 */
10673static int
10674validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10675 int numfksetcols, int16 *fksetcolsattnums,
10676 List *fksetcols)
10677{
10678 int numcolsout = 0;
10679
10680 for (int i = 0; i < numfksetcols; i++)
10681 {
10682 int16 setcol_attnum = fksetcolsattnums[i];
10683 bool seen = false;
10684
10685 /* Make sure it's in fkattnums[] */
10686 for (int j = 0; j < numfks; j++)
10687 {
10688 if (fkattnums[j] == setcol_attnum)
10689 {
10690 seen = true;
10691 break;
10692 }
10693 }
10694
10695 if (!seen)
10696 {
10697 char *col = strVal(list_nth(fksetcols, i));
10698
10699 ereport(ERROR,
10700 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10701 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10702 }
10703
10704 /* Now check for dups */
10705 seen = false;
10706 for (int j = 0; j < numcolsout; j++)
10707 {
10708 if (fksetcolsattnums[j] == setcol_attnum)
10709 {
10710 seen = true;
10711 break;
10712 }
10713 }
10714 if (!seen)
10715 fksetcolsattnums[numcolsout++] = setcol_attnum;
10716 }
10717 return numcolsout;
10718}
10719
10720/*
10721 * addFkConstraint
10722 * Install pg_constraint entries to implement a foreign key constraint.
10723 * Caller must separately invoke addFkRecurseReferenced and
10724 * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10725 * and (for partitioned tables) recurse to partitions.
10726 *
10727 * fkside: the side of the FK (or both) to create. Caller should
10728 * call addFkRecurseReferenced if this is addFkReferencedSide,
10729 * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10730 * addFkBothSides.
10731 * constraintname: the base name for the constraint being added,
10732 * copied to fkconstraint->conname if the latter is not set
10733 * fkconstraint: the constraint being added
10734 * rel: the root referencing relation
10735 * pkrel: the referenced relation; might be a partition, if recursing
10736 * indexOid: the OID of the index (on pkrel) implementing this constraint
10737 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10738 * top-level constraint
10739 * numfks: the number of columns in the foreign key
10740 * pkattnum: the attnum array of referenced attributes
10741 * fkattnum: the attnum array of referencing attributes
10742 * pf/pp/ffeqoperators: OID array of operators between columns
10743 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10744 * (...) clause
10745 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10746 * NULL/DEFAULT clause
10747 * with_period: true if this is a temporal FK
10748 */
10749static ObjectAddress
10751 char *constraintname, Constraint *fkconstraint,
10752 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10753 int numfks, int16 *pkattnum,
10754 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10755 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10756 bool is_internal, bool with_period)
10757{
10758 ObjectAddress address;
10759 Oid constrOid;
10760 char *conname;
10761 bool conislocal;
10762 int16 coninhcount;
10763 bool connoinherit;
10764
10765 /*
10766 * Verify relkind for each referenced partition. At the top level, this
10767 * is redundant with a previous check, but we need it when recursing.
10768 */
10769 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10770 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10771 ereport(ERROR,
10772 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10773 errmsg("referenced relation \"%s\" is not a table",
10774 RelationGetRelationName(pkrel))));
10775
10776 /*
10777 * Caller supplies us with a constraint name; however, it may be used in
10778 * this partition, so come up with a different one in that case. Unless
10779 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10780 * supplied name with an underscore and digit(s) appended.
10781 */
10783 RelationGetRelid(rel),
10784 constraintname))
10785 conname = ChooseConstraintName(constraintname,
10786 NULL,
10787 "",
10789 else
10790 conname = constraintname;
10791
10792 if (fkconstraint->conname == NULL)
10793 fkconstraint->conname = pstrdup(conname);
10794
10795 if (OidIsValid(parentConstr))
10796 {
10797 conislocal = false;
10798 coninhcount = 1;
10799 connoinherit = false;
10800 }
10801 else
10802 {
10803 conislocal = true;
10804 coninhcount = 0;
10805
10806 /*
10807 * always inherit for partitioned tables, never for legacy inheritance
10808 */
10809 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10810 }
10811
10812 /*
10813 * Record the FK constraint in pg_constraint.
10814 */
10815 constrOid = CreateConstraintEntry(conname,
10817 CONSTRAINT_FOREIGN,
10818 fkconstraint->deferrable,
10819 fkconstraint->initdeferred,
10820 fkconstraint->is_enforced,
10821 fkconstraint->initially_valid,
10822 parentConstr,
10823 RelationGetRelid(rel),
10824 fkattnum,
10825 numfks,
10826 numfks,
10827 InvalidOid, /* not a domain constraint */
10828 indexOid,
10829 RelationGetRelid(pkrel),
10830 pkattnum,
10831 pfeqoperators,
10832 ppeqoperators,
10833 ffeqoperators,
10834 numfks,
10835 fkconstraint->fk_upd_action,
10836 fkconstraint->fk_del_action,
10837 fkdelsetcols,
10838 numfkdelsetcols,
10839 fkconstraint->fk_matchtype,
10840 NULL, /* no exclusion constraint */
10841 NULL, /* no check constraint */
10842 NULL,
10843 conislocal, /* islocal */
10844 coninhcount, /* inhcount */
10845 connoinherit, /* conNoInherit */
10846 with_period, /* conPeriod */
10847 is_internal); /* is_internal */
10848
10849 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10850
10851 /*
10852 * In partitioning cases, create the dependency entries for this
10853 * constraint. (For non-partitioned cases, relevant entries were created
10854 * by CreateConstraintEntry.)
10855 *
10856 * On the referenced side, we need the constraint to have an internal
10857 * dependency on its parent constraint; this means that this constraint
10858 * cannot be dropped on its own -- only through the parent constraint. It
10859 * also means the containing partition cannot be dropped on its own, but
10860 * it can be detached, at which point this dependency is removed (after
10861 * verifying that no rows are referenced via this FK.)
10862 *
10863 * When processing the referencing side, we link the constraint via the
10864 * special partitioning dependencies: the parent constraint is the primary
10865 * dependent, and the partition on which the foreign key exists is the
10866 * secondary dependency. That way, this constraint is dropped if either
10867 * of these objects is.
10868 *
10869 * Note that this is only necessary for the subsidiary pg_constraint rows
10870 * in partitions; the topmost row doesn't need any of this.
10871 */
10872 if (OidIsValid(parentConstr))
10873 {
10874 ObjectAddress referenced;
10875
10876 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10877
10878 Assert(fkside != addFkBothSides);
10879 if (fkside == addFkReferencedSide)
10880 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10881 else
10882 {
10883 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10884 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10885 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10886 }
10887 }
10888
10889 /* make new constraint visible, in case we add more */
10891
10892 return address;
10893}
10894
10895/*
10896 * addFkRecurseReferenced
10897 * Recursive helper for the referenced side of foreign key creation,
10898 * which creates the action triggers and recurses
10899 *
10900 * If the referenced relation is a plain relation, create the necessary action
10901 * triggers that implement the constraint. If the referenced relation is a
10902 * partitioned table, then we create a pg_constraint row referencing the parent
10903 * of the referencing side for it and recurse on this routine for each
10904 * partition.
10905 *
10906 * fkconstraint: the constraint being added
10907 * rel: the root referencing relation
10908 * pkrel: the referenced relation; might be a partition, if recursing
10909 * indexOid: the OID of the index (on pkrel) implementing this constraint
10910 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10911 * top-level constraint
10912 * numfks: the number of columns in the foreign key
10913 * pkattnum: the attnum array of referenced attributes
10914 * fkattnum: the attnum array of referencing attributes
10915 * numfkdelsetcols: the number of columns in the ON DELETE SET
10916 * NULL/DEFAULT (...) clause
10917 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10918 * NULL/DEFAULT clause
10919 * pf/pp/ffeqoperators: OID array of operators between columns
10920 * old_check_ok: true if this constraint replaces an existing one that
10921 * was already validated (thus this one doesn't need validation)
10922 * parentDelTrigger and parentUpdTrigger: when recursively called on a
10923 * partition, the OIDs of the parent action triggers for DELETE and
10924 * UPDATE respectively.
10925 * with_period: true if this is a temporal FK
10926 */
10927static void
10929 Relation pkrel, Oid indexOid, Oid parentConstr,
10930 int numfks,
10931 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10932 Oid *ppeqoperators, Oid *ffeqoperators,
10933 int numfkdelsetcols, int16 *fkdelsetcols,
10934 bool old_check_ok,
10935 Oid parentDelTrigger, Oid parentUpdTrigger,
10936 bool with_period)
10937{
10938 Oid deleteTriggerOid = InvalidOid,
10939 updateTriggerOid = InvalidOid;
10940
10943
10944 /*
10945 * Create action triggers to enforce the constraint, or skip them if the
10946 * constraint is NOT ENFORCED.
10947 */
10948 if (fkconstraint->is_enforced)
10950 RelationGetRelid(pkrel),
10951 fkconstraint,
10952 parentConstr, indexOid,
10953 parentDelTrigger, parentUpdTrigger,
10954 &deleteTriggerOid, &updateTriggerOid);
10955
10956 /*
10957 * If the referenced table is partitioned, recurse on ourselves to handle
10958 * each partition. We need one pg_constraint row created for each
10959 * partition in addition to the pg_constraint row for the parent table.
10960 */
10961 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10962 {
10963 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10964
10965 for (int i = 0; i < pd->nparts; i++)
10966 {
10967 Relation partRel;
10968 AttrMap *map;
10969 AttrNumber *mapped_pkattnum;
10970 Oid partIndexId;
10971 ObjectAddress address;
10972
10973 /* XXX would it be better to acquire these locks beforehand? */
10974 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10975
10976 /*
10977 * Map the attribute numbers in the referenced side of the FK
10978 * definition to match the partition's column layout.
10979 */
10981 RelationGetDescr(pkrel),
10982 false);
10983 if (map)
10984 {
10985 mapped_pkattnum = palloc_array(AttrNumber, numfks);
10986 for (int j = 0; j < numfks; j++)
10987 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10988 }
10989 else
10990 mapped_pkattnum = pkattnum;
10991
10992 /* Determine the index to use at this level */
10993 partIndexId = index_get_partition(partRel, indexOid);
10994 if (!OidIsValid(partIndexId))
10995 elog(ERROR, "index for %u not found in partition %s",
10996 indexOid, RelationGetRelationName(partRel));
10997
10998 /* Create entry at this level ... */
11000 fkconstraint->conname, fkconstraint, rel,
11001 partRel, partIndexId, parentConstr,
11002 numfks, mapped_pkattnum,
11003 fkattnum, pfeqoperators, ppeqoperators,
11004 ffeqoperators, numfkdelsetcols,
11005 fkdelsetcols, true, with_period);
11006 /* ... and recurse to our children */
11007 addFkRecurseReferenced(fkconstraint, rel, partRel,
11008 partIndexId, address.objectId, numfks,
11009 mapped_pkattnum, fkattnum,
11010 pfeqoperators, ppeqoperators, ffeqoperators,
11011 numfkdelsetcols, fkdelsetcols,
11012 old_check_ok,
11013 deleteTriggerOid, updateTriggerOid,
11014 with_period);
11015
11016 /* Done -- clean up (but keep the lock) */
11017 table_close(partRel, NoLock);
11018 if (map)
11019 {
11020 pfree(mapped_pkattnum);
11021 free_attrmap(map);
11022 }
11023 }
11024 }
11025}
11026
11027/*
11028 * addFkRecurseReferencing
11029 * Recursive helper for the referencing side of foreign key creation,
11030 * which creates the check triggers and recurses
11031 *
11032 * If the referencing relation is a plain relation, create the necessary check
11033 * triggers that implement the constraint, and set up for Phase 3 constraint
11034 * verification. If the referencing relation is a partitioned table, then
11035 * we create a pg_constraint row for it and recurse on this routine for each
11036 * partition.
11037 *
11038 * We assume that the referenced relation is locked against concurrent
11039 * deletions. If it's a partitioned relation, every partition must be so
11040 * locked.
11041 *
11042 * wqueue: the ALTER TABLE work queue; NULL when not running as part
11043 * of an ALTER TABLE sequence.
11044 * fkconstraint: the constraint being added
11045 * rel: the referencing relation; might be a partition, if recursing
11046 * pkrel: the root referenced relation
11047 * indexOid: the OID of the index (on pkrel) implementing this constraint
11048 * parentConstr: the OID of the parent constraint (there is always one)
11049 * numfks: the number of columns in the foreign key
11050 * pkattnum: the attnum array of referenced attributes
11051 * fkattnum: the attnum array of referencing attributes
11052 * pf/pp/ffeqoperators: OID array of operators between columns
11053 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
11054 * (...) clause
11055 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
11056 * NULL/DEFAULT clause
11057 * old_check_ok: true if this constraint replaces an existing one that
11058 * was already validated (thus this one doesn't need validation)
11059 * lockmode: the lockmode to acquire on partitions when recursing
11060 * parentInsTrigger and parentUpdTrigger: when being recursively called on
11061 * a partition, the OIDs of the parent check triggers for INSERT and
11062 * UPDATE respectively.
11063 * with_period: true if this is a temporal FK
11064 */
11065static void
11067 Relation pkrel, Oid indexOid, Oid parentConstr,
11068 int numfks, int16 *pkattnum, int16 *fkattnum,
11069 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11070 int numfkdelsetcols, int16 *fkdelsetcols,
11071 bool old_check_ok, LOCKMODE lockmode,
11072 Oid parentInsTrigger, Oid parentUpdTrigger,
11073 bool with_period)
11074{
11075 Oid insertTriggerOid = InvalidOid,
11076 updateTriggerOid = InvalidOid;
11077
11078 Assert(OidIsValid(parentConstr));
11081
11082 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11083 ereport(ERROR,
11084 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11085 errmsg("foreign key constraints are not supported on foreign tables")));
11086
11087 /*
11088 * Add check triggers if the constraint is ENFORCED, and if needed,
11089 * schedule them to be checked in Phase 3.
11090 *
11091 * If the relation is partitioned, drill down to do it to its partitions.
11092 */
11093 if (fkconstraint->is_enforced)
11095 RelationGetRelid(pkrel),
11096 fkconstraint,
11097 parentConstr,
11098 indexOid,
11099 parentInsTrigger, parentUpdTrigger,
11100 &insertTriggerOid, &updateTriggerOid);
11101
11102 if (rel->rd_rel->relkind == RELKIND_RELATION)
11103 {
11104 /*
11105 * Tell Phase 3 to check that the constraint is satisfied by existing
11106 * rows. We can skip this during table creation, when constraint is
11107 * specified as NOT ENFORCED, or when requested explicitly by
11108 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11109 * recreating a constraint following a SET DATA TYPE operation that
11110 * did not impugn its validity.
11111 */
11112 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11113 fkconstraint->is_enforced)
11114 {
11115 NewConstraint *newcon;
11116 AlteredTableInfo *tab;
11117
11118 tab = ATGetQueueEntry(wqueue, rel);
11119
11120 newcon = palloc0_object(NewConstraint);
11121 newcon->name = get_constraint_name(parentConstr);
11122 newcon->contype = CONSTR_FOREIGN;
11123 newcon->refrelid = RelationGetRelid(pkrel);
11124 newcon->refindid = indexOid;
11125 newcon->conid = parentConstr;
11126 newcon->conwithperiod = fkconstraint->fk_with_period;
11127 newcon->qual = (Node *) fkconstraint;
11128
11129 tab->constraints = lappend(tab->constraints, newcon);
11130 }
11131 }
11132 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11133 {
11135 Relation trigrel;
11136
11137 /*
11138 * Triggers of the foreign keys will be manipulated a bunch of times
11139 * in the loop below. To avoid repeatedly opening/closing the trigger
11140 * catalog relation, we open it here and pass it to the subroutines
11141 * called below.
11142 */
11143 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11144
11145 /*
11146 * Recurse to take appropriate action on each partition; either we
11147 * find an existing constraint to reparent to ours, or we create a new
11148 * one.
11149 */
11150 for (int i = 0; i < pd->nparts; i++)
11151 {
11152 Relation partition = table_open(pd->oids[i], lockmode);
11153 List *partFKs;
11154 AttrMap *attmap;
11155 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11156 bool attached;
11157 ObjectAddress address;
11158
11159 CheckAlterTableIsSafe(partition);
11160
11161 attmap = build_attrmap_by_name(RelationGetDescr(partition),
11162 RelationGetDescr(rel),
11163 false);
11164 for (int j = 0; j < numfks; j++)
11165 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11166
11167 /* Check whether an existing constraint can be repurposed */
11168 partFKs = copyObject(RelationGetFKeyList(partition));
11169 attached = false;
11170 foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11171 {
11173 fk,
11174 partition,
11175 parentConstr,
11176 numfks,
11177 mapped_fkattnum,
11178 pkattnum,
11179 pfeqoperators,
11180 insertTriggerOid,
11181 updateTriggerOid,
11182 trigrel))
11183 {
11184 attached = true;
11185 break;
11186 }
11187 }
11188 if (attached)
11189 {
11190 table_close(partition, NoLock);
11191 continue;
11192 }
11193
11194 /*
11195 * No luck finding a good constraint to reuse; create our own.
11196 */
11198 fkconstraint->conname, fkconstraint,
11199 partition, pkrel, indexOid, parentConstr,
11200 numfks, pkattnum,
11201 mapped_fkattnum, pfeqoperators,
11202 ppeqoperators, ffeqoperators,
11203 numfkdelsetcols, fkdelsetcols, true,
11204 with_period);
11205
11206 /* call ourselves to finalize the creation and we're done */
11207 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11208 indexOid,
11209 address.objectId,
11210 numfks,
11211 pkattnum,
11212 mapped_fkattnum,
11213 pfeqoperators,
11214 ppeqoperators,
11215 ffeqoperators,
11216 numfkdelsetcols,
11217 fkdelsetcols,
11218 old_check_ok,
11219 lockmode,
11220 insertTriggerOid,
11221 updateTriggerOid,
11222 with_period);
11223
11224 table_close(partition, NoLock);
11225 }
11226
11227 table_close(trigrel, RowExclusiveLock);
11228 }
11229}
11230
11231/*
11232 * CloneForeignKeyConstraints
11233 * Clone foreign keys from a partitioned table to a newly acquired
11234 * partition.
11235 *
11236 * partitionRel is a partition of parentRel, so we can be certain that it has
11237 * the same columns with the same datatypes. The columns may be in different
11238 * order, though.
11239 *
11240 * wqueue must be passed to set up phase 3 constraint checking, unless the
11241 * referencing-side partition is known to be empty (such as in CREATE TABLE /
11242 * PARTITION OF).
11243 */
11244static void
11246 Relation partitionRel)
11247{
11248 /* This only works for declarative partitioning */
11249 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11250
11251 /*
11252 * First, clone constraints where the parent is on the referencing side.
11253 */
11254 CloneFkReferencing(wqueue, parentRel, partitionRel);
11255
11256 /*
11257 * Clone constraints for which the parent is on the referenced side.
11258 */
11259 CloneFkReferenced(parentRel, partitionRel);
11260}
11261
11262/*
11263 * CloneFkReferenced
11264 * Subroutine for CloneForeignKeyConstraints
11265 *
11266 * Find all the FKs that have the parent relation on the referenced side;
11267 * clone those constraints to the given partition. This is to be called
11268 * when the partition is being created or attached.
11269 *
11270 * This recurses to partitions, if the relation being attached is partitioned.
11271 * Recursion is done by calling addFkRecurseReferenced.
11272 */
11273static void
11274CloneFkReferenced(Relation parentRel, Relation partitionRel)
11275{
11276 Relation pg_constraint;
11277 AttrMap *attmap;
11278 ListCell *cell;
11279 SysScanDesc scan;
11280 ScanKeyData key[2];
11281 HeapTuple tuple;
11282 List *clone = NIL;
11283 Relation trigrel;
11284
11285 /*
11286 * Search for any constraints where this partition's parent is in the
11287 * referenced side. However, we must not clone any constraint whose
11288 * parent constraint is also going to be cloned, to avoid duplicates. So
11289 * do it in two steps: first construct the list of constraints to clone,
11290 * then go over that list cloning those whose parents are not in the list.
11291 * (We must not rely on the parent being seen first, since the catalog
11292 * scan could return children first.)
11293 */
11294 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11295 ScanKeyInit(&key[0],
11296 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11297 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11298 ScanKeyInit(&key[1],
11299 Anum_pg_constraint_contype, BTEqualStrategyNumber,
11300 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11301 /* This is a seqscan, as we don't have a usable index ... */
11302 scan = systable_beginscan(pg_constraint, InvalidOid, true,
11303 NULL, 2, key);
11304 while ((tuple = systable_getnext(scan)) != NULL)
11305 {
11306 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11307
11308 clone = lappend_oid(clone, constrForm->oid);
11309 }
11310 systable_endscan(scan);
11311 table_close(pg_constraint, RowShareLock);
11312
11313 /*
11314 * Triggers of the foreign keys will be manipulated a bunch of times in
11315 * the loop below. To avoid repeatedly opening/closing the trigger
11316 * catalog relation, we open it here and pass it to the subroutines called
11317 * below.
11318 */
11319 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11320
11321 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11322 RelationGetDescr(parentRel),
11323 false);
11324 foreach(cell, clone)
11325 {
11326 Oid constrOid = lfirst_oid(cell);
11327 Form_pg_constraint constrForm;
11328 Relation fkRel;
11329 Oid indexOid;
11330 Oid partIndexId;
11331 int numfks;
11332 AttrNumber conkey[INDEX_MAX_KEYS];
11333 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11334 AttrNumber confkey[INDEX_MAX_KEYS];
11335 Oid conpfeqop[INDEX_MAX_KEYS];
11336 Oid conppeqop[INDEX_MAX_KEYS];
11337 Oid conffeqop[INDEX_MAX_KEYS];
11338 int numfkdelsetcols;
11339 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11340 Constraint *fkconstraint;
11341 ObjectAddress address;
11342 Oid deleteTriggerOid = InvalidOid,
11343 updateTriggerOid = InvalidOid;
11344
11345 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11346 if (!HeapTupleIsValid(tuple))
11347 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11348 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11349
11350 /*
11351 * As explained above: don't try to clone a constraint for which we're
11352 * going to clone the parent.
11353 */
11354 if (list_member_oid(clone, constrForm->conparentid))
11355 {
11356 ReleaseSysCache(tuple);
11357 continue;
11358 }
11359
11360 /* We need the same lock level that CreateTrigger will acquire */
11361 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11362
11363 indexOid = constrForm->conindid;
11365 &numfks,
11366 conkey,
11367 confkey,
11368 conpfeqop,
11369 conppeqop,
11370 conffeqop,
11371 &numfkdelsetcols,
11372 confdelsetcols);
11373
11374 for (int i = 0; i < numfks; i++)
11375 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11376
11377 fkconstraint = makeNode(Constraint);
11378 fkconstraint->contype = CONSTRAINT_FOREIGN;
11379 fkconstraint->conname = NameStr(constrForm->conname);
11380 fkconstraint->deferrable = constrForm->condeferrable;
11381 fkconstraint->initdeferred = constrForm->condeferred;
11382 fkconstraint->location = -1;
11383 fkconstraint->pktable = NULL;
11384 /* ->fk_attrs determined below */
11385 fkconstraint->pk_attrs = NIL;
11386 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11387 fkconstraint->fk_upd_action = constrForm->confupdtype;
11388 fkconstraint->fk_del_action = constrForm->confdeltype;
11389 fkconstraint->fk_del_set_cols = NIL;
11390 fkconstraint->old_conpfeqop = NIL;
11391 fkconstraint->old_pktable_oid = InvalidOid;
11392 fkconstraint->is_enforced = constrForm->conenforced;
11393 fkconstraint->skip_validation = false;
11394 fkconstraint->initially_valid = constrForm->convalidated;
11395
11396 /* set up colnames that are used to generate the constraint name */
11397 for (int i = 0; i < numfks; i++)
11398 {
11400
11401 att = TupleDescAttr(RelationGetDescr(fkRel),
11402 conkey[i] - 1);
11403 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11404 makeString(NameStr(att->attname)));
11405 }
11406
11407 /*
11408 * Add the new foreign key constraint pointing to the new partition.
11409 * Because this new partition appears in the referenced side of the
11410 * constraint, we don't need to set up for Phase 3 check.
11411 */
11412 partIndexId = index_get_partition(partitionRel, indexOid);
11413 if (!OidIsValid(partIndexId))
11414 elog(ERROR, "index for %u not found in partition %s",
11415 indexOid, RelationGetRelationName(partitionRel));
11416
11417 /*
11418 * Get the "action" triggers belonging to the constraint to pass as
11419 * parent OIDs for similar triggers that will be created on the
11420 * partition in addFkRecurseReferenced().
11421 */
11422 if (constrForm->conenforced)
11423 GetForeignKeyActionTriggers(trigrel, constrOid,
11424 constrForm->confrelid, constrForm->conrelid,
11425 &deleteTriggerOid, &updateTriggerOid);
11426
11427 /* Add this constraint ... */
11429 fkconstraint->conname, fkconstraint, fkRel,
11430 partitionRel, partIndexId, constrOid,
11431 numfks, mapped_confkey,
11432 conkey, conpfeqop, conppeqop, conffeqop,
11433 numfkdelsetcols, confdelsetcols, false,
11434 constrForm->conperiod);
11435 /* ... and recurse */
11436 addFkRecurseReferenced(fkconstraint,
11437 fkRel,
11438 partitionRel,
11439 partIndexId,
11440 address.objectId,
11441 numfks,
11442 mapped_confkey,
11443 conkey,
11444 conpfeqop,
11445 conppeqop,
11446 conffeqop,
11447 numfkdelsetcols,
11448 confdelsetcols,
11449 true,
11450 deleteTriggerOid,
11451 updateTriggerOid,
11452 constrForm->conperiod);
11453
11454 table_close(fkRel, NoLock);
11455 ReleaseSysCache(tuple);
11456 }
11457
11458 table_close(trigrel, RowExclusiveLock);
11459}
11460
11461/*
11462 * CloneFkReferencing
11463 * Subroutine for CloneForeignKeyConstraints
11464 *
11465 * For each FK constraint of the parent relation in the given list, find an
11466 * equivalent constraint in its partition relation that can be reparented;
11467 * if one cannot be found, create a new constraint in the partition as its
11468 * child.
11469 *
11470 * If wqueue is given, it is used to set up phase-3 verification for each
11471 * cloned constraint; omit it if such verification is not needed
11472 * (example: the partition is being created anew).
11473 */
11474static void
11475CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11476{
11477 AttrMap *attmap;
11478 List *partFKs;
11479 List *clone = NIL;
11480 ListCell *cell;
11481 Relation trigrel;
11482
11483 /* obtain a list of constraints that we need to clone */
11484 foreach(cell, RelationGetFKeyList(parentRel))
11485 {
11486 ForeignKeyCacheInfo *fk = lfirst(cell);
11487
11488 /*
11489 * Refuse to attach a table as partition that this partitioned table
11490 * already has a foreign key to. This isn't useful schema, which is
11491 * proven by the fact that there have been no user complaints that
11492 * it's already impossible to achieve this in the opposite direction,
11493 * i.e., creating a foreign key that references a partition. This
11494 * restriction allows us to dodge some complexities around
11495 * pg_constraint and pg_trigger row creations that would be needed
11496 * during ATTACH/DETACH for this kind of relationship.
11497 */
11498 if (fk->confrelid == RelationGetRelid(partRel))
11499 ereport(ERROR,
11500 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11501 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11502 RelationGetRelationName(partRel),
11504
11505 clone = lappend_oid(clone, fk->conoid);
11506 }
11507
11508 /*
11509 * Silently do nothing if there's nothing to do. In particular, this
11510 * avoids throwing a spurious error for foreign tables.
11511 */
11512 if (clone == NIL)
11513 return;
11514
11515 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11516 ereport(ERROR,
11517 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11518 errmsg("foreign key constraints are not supported on foreign tables")));
11519
11520 /*
11521 * Triggers of the foreign keys will be manipulated a bunch of times in
11522 * the loop below. To avoid repeatedly opening/closing the trigger
11523 * catalog relation, we open it here and pass it to the subroutines called
11524 * below.
11525 */
11526 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11527
11528 /*
11529 * The constraint key may differ, if the columns in the partition are
11530 * different. This map is used to convert them.
11531 */
11532 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11533 RelationGetDescr(parentRel),
11534 false);
11535
11536 partFKs = copyObject(RelationGetFKeyList(partRel));
11537
11538 foreach(cell, clone)
11539 {
11540 Oid parentConstrOid = lfirst_oid(cell);
11541 Form_pg_constraint constrForm;
11542 Relation pkrel;
11543 HeapTuple tuple;
11544 int numfks;
11545 AttrNumber conkey[INDEX_MAX_KEYS];
11546 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11547 AttrNumber confkey[INDEX_MAX_KEYS];
11548 Oid conpfeqop[INDEX_MAX_KEYS];
11549 Oid conppeqop[INDEX_MAX_KEYS];
11550 Oid conffeqop[INDEX_MAX_KEYS];
11551 int numfkdelsetcols;
11552 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11553 Constraint *fkconstraint;
11554 bool attached;
11555 Oid indexOid;
11556 ObjectAddress address;
11557 ListCell *lc;
11558 Oid insertTriggerOid = InvalidOid,
11559 updateTriggerOid = InvalidOid;
11560 bool with_period;
11561
11562 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11563 if (!HeapTupleIsValid(tuple))
11564 elog(ERROR, "cache lookup failed for constraint %u",
11565 parentConstrOid);
11566 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11567
11568 /* Don't clone constraints whose parents are being cloned */
11569 if (list_member_oid(clone, constrForm->conparentid))
11570 {
11571 ReleaseSysCache(tuple);
11572 continue;
11573 }
11574
11575 /*
11576 * Need to prevent concurrent deletions. If pkrel is a partitioned
11577 * relation, that means to lock all partitions.
11578 */
11579 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11580 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11582 ShareRowExclusiveLock, NULL);
11583
11584 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11585 conpfeqop, conppeqop, conffeqop,
11586 &numfkdelsetcols, confdelsetcols);
11587 for (int i = 0; i < numfks; i++)
11588 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11589
11590 /*
11591 * Get the "check" triggers belonging to the constraint, if it is
11592 * ENFORCED, to pass as parent OIDs for similar triggers that will be
11593 * created on the partition in addFkRecurseReferencing(). They are
11594 * also passed to tryAttachPartitionForeignKey() below to simply
11595 * assign as parents to the partition's existing "check" triggers,
11596 * that is, if the corresponding constraints is deemed attachable to
11597 * the parent constraint.
11598 */
11599 if (constrForm->conenforced)
11600 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11601 constrForm->confrelid, constrForm->conrelid,
11602 &insertTriggerOid, &updateTriggerOid);
11603
11604 /*
11605 * Before creating a new constraint, see whether any existing FKs are
11606 * fit for the purpose. If one is, attach the parent constraint to
11607 * it, and don't clone anything. This way we avoid the expensive
11608 * verification step and don't end up with a duplicate FK, and we
11609 * don't need to recurse to partitions for this constraint.
11610 */
11611 attached = false;
11612 foreach(lc, partFKs)
11613 {
11615
11617 fk,
11618 partRel,
11619 parentConstrOid,
11620 numfks,
11621 mapped_conkey,
11622 confkey,
11623 conpfeqop,
11624 insertTriggerOid,
11625 updateTriggerOid,
11626 trigrel))
11627 {
11628 attached = true;
11629 table_close(pkrel, NoLock);
11630 break;
11631 }
11632 }
11633 if (attached)
11634 {
11635 ReleaseSysCache(tuple);
11636 continue;
11637 }
11638
11639 /* No dice. Set up to create our own constraint */
11640 fkconstraint = makeNode(Constraint);
11641 fkconstraint->contype = CONSTRAINT_FOREIGN;
11642 /* ->conname determined below */
11643 fkconstraint->deferrable = constrForm->condeferrable;
11644 fkconstraint->initdeferred = constrForm->condeferred;
11645 fkconstraint->location = -1;
11646 fkconstraint->pktable = NULL;
11647 /* ->fk_attrs determined below */
11648 fkconstraint->pk_attrs = NIL;
11649 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11650 fkconstraint->fk_upd_action = constrForm->confupdtype;
11651 fkconstraint->fk_del_action = constrForm->confdeltype;
11652 fkconstraint->fk_del_set_cols = NIL;
11653 fkconstraint->old_conpfeqop = NIL;
11654 fkconstraint->old_pktable_oid = InvalidOid;
11655 fkconstraint->is_enforced = constrForm->conenforced;
11656 fkconstraint->skip_validation = false;
11657 fkconstraint->initially_valid = constrForm->convalidated;
11658 for (int i = 0; i < numfks; i++)
11659 {
11661
11662 att = TupleDescAttr(RelationGetDescr(partRel),
11663 mapped_conkey[i] - 1);
11664 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11665 makeString(NameStr(att->attname)));
11666 }
11667
11668 indexOid = constrForm->conindid;
11669 with_period = constrForm->conperiod;
11670
11671 /* Create the pg_constraint entry at this level */
11673 NameStr(constrForm->conname), fkconstraint,
11674 partRel, pkrel, indexOid, parentConstrOid,
11675 numfks, confkey,
11676 mapped_conkey, conpfeqop,
11677 conppeqop, conffeqop,
11678 numfkdelsetcols, confdelsetcols,
11679 false, with_period);
11680
11681 /* Done with the cloned constraint's tuple */
11682 ReleaseSysCache(tuple);
11683
11684 /* Create the check triggers, and recurse to partitions, if any */
11686 fkconstraint,
11687 partRel,
11688 pkrel,
11689 indexOid,
11690 address.objectId,
11691 numfks,
11692 confkey,
11693 mapped_conkey,
11694 conpfeqop,
11695 conppeqop,
11696 conffeqop,
11697 numfkdelsetcols,
11698 confdelsetcols,
11699 false, /* no old check exists */
11701 insertTriggerOid,
11702 updateTriggerOid,
11703 with_period);
11704 table_close(pkrel, NoLock);
11705 }
11706
11707 table_close(trigrel, RowExclusiveLock);
11708}
11709
11710/*
11711 * When the parent of a partition receives [the referencing side of] a foreign
11712 * key, we must propagate that foreign key to the partition. However, the
11713 * partition might already have an equivalent foreign key; this routine
11714 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11715 * by the other parameters. If they are equivalent, create the link between
11716 * the two constraints and return true.
11717 *
11718 * If the given FK does not match the one defined by rest of the params,
11719 * return false.
11720 */
11721static bool
11724 Relation partition,
11725 Oid parentConstrOid,
11726 int numfks,
11727 AttrNumber *mapped_conkey,
11728 AttrNumber *confkey,
11729 Oid *conpfeqop,
11730 Oid parentInsTrigger,
11731 Oid parentUpdTrigger,
11732 Relation trigrel)
11733{
11734 HeapTuple parentConstrTup;
11735 Form_pg_constraint parentConstr;
11736 HeapTuple partcontup;
11737 Form_pg_constraint partConstr;
11738
11739 parentConstrTup = SearchSysCache1(CONSTROID,
11740 ObjectIdGetDatum(parentConstrOid));
11741 if (!HeapTupleIsValid(parentConstrTup))
11742 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11743 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11744
11745 /*
11746 * Do some quick & easy initial checks. If any of these fail, we cannot
11747 * use this constraint.
11748 */
11749 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11750 {
11751 ReleaseSysCache(parentConstrTup);
11752 return false;
11753 }
11754 for (int i = 0; i < numfks; i++)
11755 {
11756 if (fk->conkey[i] != mapped_conkey[i] ||
11757 fk->confkey[i] != confkey[i] ||
11758 fk->conpfeqop[i] != conpfeqop[i])
11759 {
11760 ReleaseSysCache(parentConstrTup);
11761 return false;
11762 }
11763 }
11764
11765 /* Looks good so far; perform more extensive checks. */
11766 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11767 if (!HeapTupleIsValid(partcontup))
11768 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11769 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11770
11771 /*
11772 * An error should be raised if the constraint enforceability is
11773 * different. Returning false without raising an error, as we do for other
11774 * attributes, could lead to a duplicate constraint with the same
11775 * enforceability as the parent. While this may be acceptable, it may not
11776 * be ideal. Therefore, it's better to raise an error and allow the user
11777 * to correct the enforceability before proceeding.
11778 */
11779 if (partConstr->conenforced != parentConstr->conenforced)
11780 ereport(ERROR,
11781 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11782 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11783 NameStr(parentConstr->conname),
11784 NameStr(partConstr->conname),
11785 RelationGetRelationName(partition))));
11786
11787 if (OidIsValid(partConstr->conparentid) ||
11788 partConstr->condeferrable != parentConstr->condeferrable ||
11789 partConstr->condeferred != parentConstr->condeferred ||
11790 partConstr->confupdtype != parentConstr->confupdtype ||
11791 partConstr->confdeltype != parentConstr->confdeltype ||
11792 partConstr->confmatchtype != parentConstr->confmatchtype)
11793 {
11794 ReleaseSysCache(parentConstrTup);
11795 ReleaseSysCache(partcontup);
11796 return false;
11797 }
11798
11799 ReleaseSysCache(parentConstrTup);
11800 ReleaseSysCache(partcontup);
11801
11802 /* Looks good! Attach this constraint. */
11803 AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11804 parentConstrOid, parentInsTrigger,
11805 parentUpdTrigger, trigrel);
11806
11807 return true;
11808}
11809
11810/*
11811 * AttachPartitionForeignKey
11812 *
11813 * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11814 * attaching the constraint, removing redundant triggers and entries from
11815 * pg_constraint, and setting the constraint's parent.
11816 */
11817static void
11819 Relation partition,
11820 Oid partConstrOid,
11821 Oid parentConstrOid,
11822 Oid parentInsTrigger,
11823 Oid parentUpdTrigger,
11824 Relation trigrel)
11825{
11826 HeapTuple parentConstrTup;
11827 Form_pg_constraint parentConstr;
11828 HeapTuple partcontup;
11829 Form_pg_constraint partConstr;
11830 bool queueValidation;
11831 Oid partConstrFrelid;
11832 Oid partConstrRelid;
11833 bool parentConstrIsEnforced;
11834
11835 /* Fetch the parent constraint tuple */
11836 parentConstrTup = SearchSysCache1(CONSTROID,
11837 ObjectIdGetDatum(parentConstrOid));
11838 if (!HeapTupleIsValid(parentConstrTup))
11839 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11840 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11841 parentConstrIsEnforced = parentConstr->conenforced;
11842
11843 /* Fetch the child constraint tuple */
11844 partcontup = SearchSysCache1(CONSTROID,
11845 ObjectIdGetDatum(partConstrOid));
11846 if (!HeapTupleIsValid(partcontup))
11847 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11848 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11849 partConstrFrelid = partConstr->confrelid;
11850 partConstrRelid = partConstr->conrelid;
11851
11852 /*
11853 * If the referenced table is partitioned, then the partition we're
11854 * attaching now has extra pg_constraint rows and action triggers that are
11855 * no longer needed. Remove those.
11856 */
11857 if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11858 {
11859 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11860
11861 RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11862 partConstrRelid);
11863
11864 table_close(pg_constraint, RowShareLock);
11865 }
11866
11867 /*
11868 * Will we need to validate this constraint? A valid parent constraint
11869 * implies that all child constraints have been validated, so if this one
11870 * isn't, we must trigger phase 3 validation.
11871 */
11872 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11873
11874 ReleaseSysCache(partcontup);
11875 ReleaseSysCache(parentConstrTup);
11876
11877 /*
11878 * The action triggers in the new partition become redundant -- the parent
11879 * table already has equivalent ones, and those will be able to reach the
11880 * partition. Remove the ones in the partition. We identify them because
11881 * they have our constraint OID, as well as being on the referenced rel.
11882 */
11883 DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11884 partConstrRelid);
11885
11886 ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11887 RelationGetRelid(partition));
11888
11889 /*
11890 * Like the constraint, attach partition's "check" triggers to the
11891 * corresponding parent triggers if the constraint is ENFORCED. NOT
11892 * ENFORCED constraints do not have these triggers.
11893 */
11894 if (parentConstrIsEnforced)
11895 {
11896 Oid insertTriggerOid,
11897 updateTriggerOid;
11898
11900 partConstrOid, partConstrFrelid, partConstrRelid,
11901 &insertTriggerOid, &updateTriggerOid);
11902 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11903 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11904 RelationGetRelid(partition));
11905 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11906 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11907 RelationGetRelid(partition));
11908 }
11909
11910 /*
11911 * We updated this pg_constraint row above to set its parent; validating
11912 * it will cause its convalidated flag to change, so we need CCI here. In
11913 * addition, we need it unconditionally for the rare case where the parent
11914 * table has *two* identical constraints; when reaching this function for
11915 * the second one, we must have made our changes visible, otherwise we
11916 * would try to attach both to this one.
11917 */
11919
11920 /* If validation is needed, put it in the queue now. */
11921 if (queueValidation)
11922 {
11923 Relation conrel;
11924 Oid confrelid;
11925
11926 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11927
11928 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11929 if (!HeapTupleIsValid(partcontup))
11930 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11931
11932 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11933
11934 /* Use the same lock as for AT_ValidateConstraint */
11935 QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11936 partcontup, ShareUpdateExclusiveLock);
11937 ReleaseSysCache(partcontup);
11939 }
11940}
11941
11942/*
11943 * RemoveInheritedConstraint
11944 *
11945 * Removes the constraint and its associated trigger from the specified
11946 * relation, which inherited the given constraint.
11947 */
11948static void
11950 Oid conrelid)
11951{
11952 ObjectAddresses *objs;
11953 HeapTuple consttup;
11955 SysScanDesc scan;
11956 HeapTuple trigtup;
11957
11959 Anum_pg_constraint_conrelid,
11960 BTEqualStrategyNumber, F_OIDEQ,
11961 ObjectIdGetDatum(conrelid));
11962
11963 scan = systable_beginscan(conrel,
11964 ConstraintRelidTypidNameIndexId,
11965 true, NULL, 1, &key);
11966 objs = new_object_addresses();
11967 while ((consttup = systable_getnext(scan)) != NULL)
11968 {
11969 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11970
11971 if (conform->conparentid != conoid)
11972 continue;
11973 else
11974 {
11975 ObjectAddress addr;
11976 SysScanDesc scan2;
11977 ScanKeyData key2;
11979
11980 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11981 add_exact_object_address(&addr, objs);
11982
11983 /*
11984 * First we must delete the dependency record that binds the
11985 * constraint records together.
11986 */
11987 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11988 conform->oid,
11990 ConstraintRelationId,
11991 conoid);
11992 Assert(n == 1); /* actually only one is expected */
11993
11994 /*
11995 * Now search for the triggers for this constraint and set them up
11996 * for deletion too
11997 */
11998 ScanKeyInit(&key2,
11999 Anum_pg_trigger_tgconstraint,
12000 BTEqualStrategyNumber, F_OIDEQ,
12001 ObjectIdGetDatum(conform->oid));
12002 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
12003 true, NULL, 1, &key2);
12004 while ((trigtup = systable_getnext(scan2)) != NULL)
12005 {
12006 ObjectAddressSet(addr, TriggerRelationId,
12007 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
12008 add_exact_object_address(&addr, objs);
12009 }
12010 systable_endscan(scan2);
12011 }
12012 }
12013 /* make the dependency deletions visible */
12017 systable_endscan(scan);
12018}
12019
12020/*
12021 * DropForeignKeyConstraintTriggers
12022 *
12023 * The subroutine for tryAttachPartitionForeignKey handles the deletion of
12024 * action triggers for the foreign key constraint.
12025 *
12026 * If valid confrelid and conrelid values are not provided, the respective
12027 * trigger check will be skipped, and the trigger will be considered for
12028 * removal.
12029 */
12030static void
12032 Oid conrelid)
12033{
12035 SysScanDesc scan;
12036 HeapTuple trigtup;
12037
12039 Anum_pg_trigger_tgconstraint,
12040 BTEqualStrategyNumber, F_OIDEQ,
12041 ObjectIdGetDatum(conoid));
12042 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12043 NULL, 1, &key);
12044 while ((trigtup = systable_getnext(scan)) != NULL)
12045 {
12046 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12047 ObjectAddress trigger;
12048
12049 /* Invalid if trigger is not for a referential integrity constraint */
12050 if (!OidIsValid(trgform->tgconstrrelid))
12051 continue;
12052 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12053 continue;
12054 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12055 continue;
12056
12057 /* We should be dropping trigger related to foreign key constraint */
12058 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12059 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12060 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12061 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12062 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12063 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12064 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12065 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12066 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12067 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12068 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12069 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12070
12071 /*
12072 * The constraint is originally set up to contain this trigger as an
12073 * implementation object, so there's a dependency record that links
12074 * the two; however, since the trigger is no longer needed, we remove
12075 * the dependency link in order to be able to drop the trigger while
12076 * keeping the constraint intact.
12077 */
12078 deleteDependencyRecordsFor(TriggerRelationId,
12079 trgform->oid,
12080 false);
12081 /* make dependency deletion visible to performDeletion */
12083 ObjectAddressSet(trigger, TriggerRelationId,
12084 trgform->oid);
12085 performDeletion(&trigger, DROP_RESTRICT, 0);
12086 /* make trigger drop visible, in case the loop iterates */
12088 }
12089
12090 systable_endscan(scan);
12091}
12092
12093/*
12094 * GetForeignKeyActionTriggers
12095 * Returns delete and update "action" triggers of the given relation
12096 * belonging to the given constraint
12097 */
12098static void
12100 Oid conoid, Oid confrelid, Oid conrelid,
12101 Oid *deleteTriggerOid,
12102 Oid *updateTriggerOid)
12103{
12105 SysScanDesc scan;
12106 HeapTuple trigtup;
12107
12108 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12110 Anum_pg_trigger_tgconstraint,
12111 BTEqualStrategyNumber, F_OIDEQ,
12112 ObjectIdGetDatum(conoid));
12113
12114 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12115 NULL, 1, &key);
12116 while ((trigtup = systable_getnext(scan)) != NULL)
12117 {
12118 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12119
12120 if (trgform->tgconstrrelid != conrelid)
12121 continue;
12122 if (trgform->tgrelid != confrelid)
12123 continue;
12124 /* Only ever look at "action" triggers on the PK side. */
12125 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12126 continue;
12127 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12128 {
12129 Assert(*deleteTriggerOid == InvalidOid);
12130 *deleteTriggerOid = trgform->oid;
12131 }
12132 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12133 {
12134 Assert(*updateTriggerOid == InvalidOid);
12135 *updateTriggerOid = trgform->oid;
12136 }
12137#ifndef USE_ASSERT_CHECKING
12138 /* In an assert-enabled build, continue looking to find duplicates */
12139 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12140 break;
12141#endif
12142 }
12143
12144 if (!OidIsValid(*deleteTriggerOid))
12145 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12146 conoid);
12147 if (!OidIsValid(*updateTriggerOid))
12148 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12149 conoid);
12150
12151 systable_endscan(scan);
12152}
12153
12154/*
12155 * GetForeignKeyCheckTriggers
12156 * Returns insert and update "check" triggers of the given relation
12157 * belonging to the given constraint
12158 */
12159static void
12161 Oid conoid, Oid confrelid, Oid conrelid,
12162 Oid *insertTriggerOid,
12163 Oid *updateTriggerOid)
12164{
12166 SysScanDesc scan;
12167 HeapTuple trigtup;
12168
12169 *insertTriggerOid = *updateTriggerOid = InvalidOid;
12171 Anum_pg_trigger_tgconstraint,
12172 BTEqualStrategyNumber, F_OIDEQ,
12173 ObjectIdGetDatum(conoid));
12174
12175 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12176 NULL, 1, &key);
12177 while ((trigtup = systable_getnext(scan)) != NULL)
12178 {
12179 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12180
12181 if (trgform->tgconstrrelid != confrelid)
12182 continue;
12183 if (trgform->tgrelid != conrelid)
12184 continue;
12185 /* Only ever look at "check" triggers on the FK side. */
12186 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12187 continue;
12188 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12189 {
12190 Assert(*insertTriggerOid == InvalidOid);
12191 *insertTriggerOid = trgform->oid;
12192 }
12193 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12194 {
12195 Assert(*updateTriggerOid == InvalidOid);
12196 *updateTriggerOid = trgform->oid;
12197 }
12198#ifndef USE_ASSERT_CHECKING
12199 /* In an assert-enabled build, continue looking to find duplicates. */
12200 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12201 break;
12202#endif
12203 }
12204
12205 if (!OidIsValid(*insertTriggerOid))
12206 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12207 conoid);
12208 if (!OidIsValid(*updateTriggerOid))
12209 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12210 conoid);
12211
12212 systable_endscan(scan);
12213}
12214
12215/*
12216 * ALTER TABLE ALTER CONSTRAINT
12217 *
12218 * Update the attributes of a constraint.
12219 *
12220 * Currently only works for Foreign Key and not null constraints.
12221 *
12222 * If the constraint is modified, returns its address; otherwise, return
12223 * InvalidObjectAddress.
12224 */
12225static ObjectAddress
12227 bool recurse, LOCKMODE lockmode)
12228{
12229 Relation conrel;
12230 Relation tgrel;
12231 SysScanDesc scan;
12232 ScanKeyData skey[3];
12233 HeapTuple contuple;
12234 Form_pg_constraint currcon;
12235 ObjectAddress address;
12236
12237 /*
12238 * Disallow altering ONLY a partitioned table, as it would make no sense.
12239 * This is okay for legacy inheritance.
12240 */
12241 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12242 ereport(ERROR,
12243 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12244 errmsg("constraint must be altered in child tables too"),
12245 errhint("Do not specify the ONLY keyword."));
12246
12247
12248 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12249 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12250
12251 /*
12252 * Find and check the target constraint
12253 */
12254 ScanKeyInit(&skey[0],
12255 Anum_pg_constraint_conrelid,
12256 BTEqualStrategyNumber, F_OIDEQ,
12258 ScanKeyInit(&skey[1],
12259 Anum_pg_constraint_contypid,
12260 BTEqualStrategyNumber, F_OIDEQ,
12262 ScanKeyInit(&skey[2],
12263 Anum_pg_constraint_conname,
12264 BTEqualStrategyNumber, F_NAMEEQ,
12265 CStringGetDatum(cmdcon->conname));
12266 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12267 true, NULL, 3, skey);
12268
12269 /* There can be at most one matching row */
12270 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12271 ereport(ERROR,
12272 (errcode(ERRCODE_UNDEFINED_OBJECT),
12273 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12274 cmdcon->conname, RelationGetRelationName(rel))));
12275
12276 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12277 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12278 ereport(ERROR,
12279 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12280 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12281 cmdcon->conname, RelationGetRelationName(rel))));
12282 if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12283 ereport(ERROR,
12284 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12285 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12286 cmdcon->conname, RelationGetRelationName(rel))));
12287 if (cmdcon->alterInheritability &&
12288 currcon->contype != CONSTRAINT_NOTNULL)
12289 ereport(ERROR,
12290 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12291 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12292 cmdcon->conname, RelationGetRelationName(rel)));
12293
12294 /* Refuse to modify inheritability of inherited constraints */
12295 if (cmdcon->alterInheritability &&
12296 cmdcon->noinherit && currcon->coninhcount > 0)
12297 ereport(ERROR,
12298 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12299 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12300 NameStr(currcon->conname),
12302
12303 /*
12304 * If it's not the topmost constraint, raise an error.
12305 *
12306 * Altering a non-topmost constraint leaves some triggers untouched, since
12307 * they are not directly connected to this constraint; also, pg_dump would
12308 * ignore the deferrability status of the individual constraint, since it
12309 * only dumps topmost constraints. Avoid these problems by refusing this
12310 * operation and telling the user to alter the parent constraint instead.
12311 */
12312 if (OidIsValid(currcon->conparentid))
12313 {
12314 HeapTuple tp;
12315 Oid parent = currcon->conparentid;
12316 char *ancestorname = NULL;
12317 char *ancestortable = NULL;
12318
12319 /* Loop to find the topmost constraint */
12320 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12321 {
12323
12324 /* If no parent, this is the constraint we want */
12325 if (!OidIsValid(contup->conparentid))
12326 {
12327 ancestorname = pstrdup(NameStr(contup->conname));
12328 ancestortable = get_rel_name(contup->conrelid);
12329 ReleaseSysCache(tp);
12330 break;
12331 }
12332
12333 parent = contup->conparentid;
12334 ReleaseSysCache(tp);
12335 }
12336
12337 ereport(ERROR,
12338 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12339 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12340 cmdcon->conname, RelationGetRelationName(rel)),
12341 ancestorname && ancestortable ?
12342 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12343 cmdcon->conname, ancestorname, ancestortable) : 0,
12344 errhint("You may alter the constraint it derives from instead.")));
12345 }
12346
12347 address = InvalidObjectAddress;
12348
12349 /*
12350 * Do the actual catalog work, and recurse if necessary.
12351 */
12352 if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12353 contuple, recurse, lockmode))
12354 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12355
12356 systable_endscan(scan);
12357
12360
12361 return address;
12362}
12363
12364/*
12365 * A subroutine of ATExecAlterConstraint that calls the respective routines for
12366 * altering constraint's enforceability, deferrability or inheritability.
12367 */
12368static bool
12370 Relation conrel, Relation tgrel, Relation rel,
12371 HeapTuple contuple, bool recurse,
12372 LOCKMODE lockmode)
12373{
12374 Form_pg_constraint currcon;
12375 bool changed = false;
12376 List *otherrelids = NIL;
12377
12378 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12379
12380 /*
12381 * Do the catalog work for the enforceability or deferrability change,
12382 * recurse if necessary.
12383 *
12384 * Note that even if deferrability is requested to be altered along with
12385 * enforceability, we don't need to explicitly update multiple entries in
12386 * pg_trigger related to deferrability.
12387 *
12388 * Modifying enforceability involves either creating or dropping the
12389 * trigger, during which the deferrability setting will be adjusted
12390 * automatically.
12391 */
12392 if (cmdcon->alterEnforceability &&
12393 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12394 currcon->conrelid, currcon->confrelid,
12395 contuple, lockmode, InvalidOid,
12397 changed = true;
12398
12399 else if (cmdcon->alterDeferrability &&
12400 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12401 contuple, recurse, &otherrelids,
12402 lockmode))
12403 {
12404 /*
12405 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12406 * the relations having the constraint itself; here we also invalidate
12407 * for relations that have any triggers that are part of the
12408 * constraint.
12409 */
12410 foreach_oid(relid, otherrelids)
12412
12413 changed = true;
12414 }
12415
12416 /*
12417 * Do the catalog work for the inheritability change.
12418 */
12419 if (cmdcon->alterInheritability &&
12420 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12421 lockmode))
12422 changed = true;
12423
12424 return changed;
12425}
12426
12427/*
12428 * Returns true if the constraint's enforceability is altered.
12429 *
12430 * Depending on whether the constraint is being set to ENFORCED or NOT
12431 * ENFORCED, it creates or drops the trigger accordingly.
12432 *
12433 * Note that we must recurse even when trying to change a constraint to not
12434 * enforced if it is already not enforced, in case descendant constraints
12435 * might be enforced and need to be changed to not enforced. Conversely, we
12436 * should do nothing if a constraint is being set to enforced and is already
12437 * enforced, as descendant constraints cannot be different in that case.
12438 */
12439static bool
12441 Relation conrel, Relation tgrel,
12442 Oid fkrelid, Oid pkrelid,
12443 HeapTuple contuple, LOCKMODE lockmode,
12444 Oid ReferencedParentDelTrigger,
12445 Oid ReferencedParentUpdTrigger,
12446 Oid ReferencingParentInsTrigger,
12447 Oid ReferencingParentUpdTrigger)
12448{
12449 Form_pg_constraint currcon;
12450 Oid conoid;
12451 Relation rel;
12452 bool changed = false;
12453
12454 /* Since this function recurses, it could be driven to stack overflow */
12456
12457 Assert(cmdcon->alterEnforceability);
12458
12459 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12460 conoid = currcon->oid;
12461
12462 /* Should be foreign key constraint */
12463 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12464
12465 rel = table_open(currcon->conrelid, lockmode);
12466
12467 if (currcon->conenforced != cmdcon->is_enforced)
12468 {
12469 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12470 changed = true;
12471 }
12472
12473 /* Drop triggers */
12474 if (!cmdcon->is_enforced)
12475 {
12476 /*
12477 * When setting a constraint to NOT ENFORCED, the constraint triggers
12478 * need to be dropped. Therefore, we must process the child relations
12479 * first, followed by the parent, to account for dependencies.
12480 */
12481 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12482 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12483 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12484 fkrelid, pkrelid, contuple,
12485 lockmode, InvalidOid, InvalidOid,
12487
12488 /* Drop all the triggers */
12490 }
12491 else if (changed) /* Create triggers */
12492 {
12493 Oid ReferencedDelTriggerOid = InvalidOid,
12494 ReferencedUpdTriggerOid = InvalidOid,
12495 ReferencingInsTriggerOid = InvalidOid,
12496 ReferencingUpdTriggerOid = InvalidOid;
12497
12498 /* Prepare the minimal information required for trigger creation. */
12499 Constraint *fkconstraint = makeNode(Constraint);
12500
12501 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12502 fkconstraint->fk_matchtype = currcon->confmatchtype;
12503 fkconstraint->fk_upd_action = currcon->confupdtype;
12504 fkconstraint->fk_del_action = currcon->confdeltype;
12505
12506 /* Create referenced triggers */
12507 if (currcon->conrelid == fkrelid)
12508 createForeignKeyActionTriggers(currcon->conrelid,
12509 currcon->confrelid,
12510 fkconstraint,
12511 conoid,
12512 currcon->conindid,
12513 ReferencedParentDelTrigger,
12514 ReferencedParentUpdTrigger,
12515 &ReferencedDelTriggerOid,
12516 &ReferencedUpdTriggerOid);
12517
12518 /* Create referencing triggers */
12519 if (currcon->confrelid == pkrelid)
12520 createForeignKeyCheckTriggers(currcon->conrelid,
12521 pkrelid,
12522 fkconstraint,
12523 conoid,
12524 currcon->conindid,
12525 ReferencingParentInsTrigger,
12526 ReferencingParentUpdTrigger,
12527 &ReferencingInsTriggerOid,
12528 &ReferencingUpdTriggerOid);
12529
12530 /*
12531 * Tell Phase 3 to check that the constraint is satisfied by existing
12532 * rows. Only applies to leaf partitions, and (for constraints that
12533 * reference a partitioned table) only if this is not one of the
12534 * pg_constraint rows that exist solely to support action triggers.
12535 */
12536 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12537 currcon->confrelid == pkrelid)
12538 {
12539 AlteredTableInfo *tab;
12540 NewConstraint *newcon;
12541
12542 newcon = palloc0_object(NewConstraint);
12543 newcon->name = fkconstraint->conname;
12544 newcon->contype = CONSTR_FOREIGN;
12545 newcon->refrelid = currcon->confrelid;
12546 newcon->refindid = currcon->conindid;
12547 newcon->conid = currcon->oid;
12548 newcon->qual = (Node *) fkconstraint;
12549
12550 /* Find or create work queue entry for this table */
12551 tab = ATGetQueueEntry(wqueue, rel);
12552 tab->constraints = lappend(tab->constraints, newcon);
12553 }
12554
12555 /*
12556 * If the table at either end of the constraint is partitioned, we
12557 * need to recurse and create triggers for each constraint that is a
12558 * child of this one.
12559 */
12560 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12561 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12562 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12563 fkrelid, pkrelid, contuple,
12564 lockmode, ReferencedDelTriggerOid,
12565 ReferencedUpdTriggerOid,
12566 ReferencingInsTriggerOid,
12567 ReferencingUpdTriggerOid);
12568 }
12569
12570 table_close(rel, NoLock);
12571
12572 return changed;
12573}
12574
12575/*
12576 * Returns true if the constraint's deferrability is altered.
12577 *
12578 * *otherrelids is appended OIDs of relations containing affected triggers.
12579 *
12580 * Note that we must recurse even when the values are correct, in case
12581 * indirect descendants have had their constraints altered locally.
12582 * (This could be avoided if we forbade altering constraints in partitions
12583 * but existing releases don't do that.)
12584 */
12585static bool
12587 Relation conrel, Relation tgrel, Relation rel,
12588 HeapTuple contuple, bool recurse,
12589 List **otherrelids, LOCKMODE lockmode)
12590{
12591 Form_pg_constraint currcon;
12592 Oid refrelid;
12593 bool changed = false;
12594
12595 /* since this function recurses, it could be driven to stack overflow */
12597
12598 Assert(cmdcon->alterDeferrability);
12599
12600 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12601 refrelid = currcon->confrelid;
12602
12603 /* Should be foreign key constraint */
12604 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12605
12606 /*
12607 * If called to modify a constraint that's already in the desired state,
12608 * silently do nothing.
12609 */
12610 if (currcon->condeferrable != cmdcon->deferrable ||
12611 currcon->condeferred != cmdcon->initdeferred)
12612 {
12613 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12614 changed = true;
12615
12616 /*
12617 * Now we need to update the multiple entries in pg_trigger that
12618 * implement the constraint.
12619 */
12620 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12621 cmdcon->deferrable,
12622 cmdcon->initdeferred, otherrelids);
12623 }
12624
12625 /*
12626 * If the table at either end of the constraint is partitioned, we need to
12627 * handle every constraint that is a child of this one.
12628 */
12629 if (recurse && changed &&
12630 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12631 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12632 AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12633 contuple, recurse, otherrelids,
12634 lockmode);
12635
12636 return changed;
12637}
12638
12639/*
12640 * Returns true if the constraint's inheritability is altered.
12641 */
12642static bool
12644 Relation conrel, Relation rel,
12645 HeapTuple contuple, LOCKMODE lockmode)
12646{
12647 Form_pg_constraint currcon;
12648 AttrNumber colNum;
12649 char *colName;
12650 List *children;
12651
12652 Assert(cmdcon->alterInheritability);
12653
12654 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12655
12656 /* The current implementation only works for NOT NULL constraints */
12657 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12658
12659 /*
12660 * If called to modify a constraint that's already in the desired state,
12661 * silently do nothing.
12662 */
12663 if (cmdcon->noinherit == currcon->connoinherit)
12664 return false;
12665
12666 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12668
12669 /* Fetch the column number and name */
12670 colNum = extractNotNullColumn(contuple);
12671 colName = get_attname(currcon->conrelid, colNum, false);
12672
12673 /*
12674 * Propagate the change to children. For this subcommand type we don't
12675 * recursively affect children, just the immediate level.
12676 */
12678 lockmode);
12679 foreach_oid(childoid, children)
12680 {
12681 ObjectAddress addr;
12682
12683 if (cmdcon->noinherit)
12684 {
12685 HeapTuple childtup;
12686 Form_pg_constraint childcon;
12687
12688 childtup = findNotNullConstraint(childoid, colName);
12689 if (!childtup)
12690 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12691 colName, childoid);
12692 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12693 Assert(childcon->coninhcount > 0);
12694 childcon->coninhcount--;
12695 childcon->conislocal = true;
12696 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12697 heap_freetuple(childtup);
12698 }
12699 else
12700 {
12701 Relation childrel = table_open(childoid, NoLock);
12702
12703 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12704 colName, true, true, lockmode);
12705 if (OidIsValid(addr.objectId))
12707 table_close(childrel, NoLock);
12708 }
12709 }
12710
12711 return true;
12712}
12713
12714/*
12715 * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12716 * trigger's deferrability.
12717 *
12718 * The arguments to this function have the same meaning as the arguments to
12719 * ATExecAlterConstrDeferrability.
12720 */
12721static void
12723 bool deferrable, bool initdeferred,
12724 List **otherrelids)
12725{
12726 HeapTuple tgtuple;
12727 ScanKeyData tgkey;
12728 SysScanDesc tgscan;
12729
12730 ScanKeyInit(&tgkey,
12731 Anum_pg_trigger_tgconstraint,
12732 BTEqualStrategyNumber, F_OIDEQ,
12733 ObjectIdGetDatum(conoid));
12734 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12735 NULL, 1, &tgkey);
12736 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12737 {
12738 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12739 Form_pg_trigger copy_tg;
12740 HeapTuple tgCopyTuple;
12741
12742 /*
12743 * Remember OIDs of other relation(s) involved in FK constraint.
12744 * (Note: it's likely that we could skip forcing a relcache inval for
12745 * other rels that don't have a trigger whose properties change, but
12746 * let's be conservative.)
12747 */
12748 if (tgform->tgrelid != RelationGetRelid(rel))
12749 *otherrelids = list_append_unique_oid(*otherrelids,
12750 tgform->tgrelid);
12751
12752 /*
12753 * Update enable status and deferrability of RI_FKey_noaction_del,
12754 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12755 * triggers, but not others; see createForeignKeyActionTriggers and
12756 * CreateFKCheckTrigger.
12757 */
12758 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12759 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12760 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12761 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12762 continue;
12763
12764 tgCopyTuple = heap_copytuple(tgtuple);
12765 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12766
12767 copy_tg->tgdeferrable = deferrable;
12768 copy_tg->tginitdeferred = initdeferred;
12769 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12770
12771 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12772
12773 heap_freetuple(tgCopyTuple);
12774 }
12775
12776 systable_endscan(tgscan);
12777}
12778
12779/*
12780 * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12781 * the specified constraint.
12782 *
12783 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12784 * list of child relations and recursing; instead it uses the conparentid
12785 * relationships. This may need to be reconsidered.
12786 *
12787 * The arguments to this function have the same meaning as the arguments to
12788 * ATExecAlterConstrEnforceability.
12789 */
12790static void
12792 Relation conrel, Relation tgrel,
12793 Oid fkrelid, Oid pkrelid,
12794 HeapTuple contuple, LOCKMODE lockmode,
12795 Oid ReferencedParentDelTrigger,
12796 Oid ReferencedParentUpdTrigger,
12797 Oid ReferencingParentInsTrigger,
12798 Oid ReferencingParentUpdTrigger)
12799{
12800 Form_pg_constraint currcon;
12801 Oid conoid;
12802 ScanKeyData pkey;
12803 SysScanDesc pscan;
12804 HeapTuple childtup;
12805
12806 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12807 conoid = currcon->oid;
12808
12809 ScanKeyInit(&pkey,
12810 Anum_pg_constraint_conparentid,
12811 BTEqualStrategyNumber, F_OIDEQ,
12812 ObjectIdGetDatum(conoid));
12813
12814 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12815 true, NULL, 1, &pkey);
12816
12817 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12818 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12819 pkrelid, childtup, lockmode,
12820 ReferencedParentDelTrigger,
12821 ReferencedParentUpdTrigger,
12822 ReferencingParentInsTrigger,
12823 ReferencingParentUpdTrigger);
12824
12825 systable_endscan(pscan);
12826}
12827
12828/*
12829 * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12830 * the specified constraint.
12831 *
12832 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12833 * list of child relations and recursing; instead it uses the conparentid
12834 * relationships. This may need to be reconsidered.
12835 *
12836 * The arguments to this function have the same meaning as the arguments to
12837 * ATExecAlterConstrDeferrability.
12838 */
12839static void
12841 Relation conrel, Relation tgrel, Relation rel,
12842 HeapTuple contuple, bool recurse,
12843 List **otherrelids, LOCKMODE lockmode)
12844{
12845 Form_pg_constraint currcon;
12846 Oid conoid;
12847 ScanKeyData pkey;
12848 SysScanDesc pscan;
12849 HeapTuple childtup;
12850
12851 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12852 conoid = currcon->oid;
12853
12854 ScanKeyInit(&pkey,
12855 Anum_pg_constraint_conparentid,
12856 BTEqualStrategyNumber, F_OIDEQ,
12857 ObjectIdGetDatum(conoid));
12858
12859 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12860 true, NULL, 1, &pkey);
12861
12862 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12863 {
12864 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12865 Relation childrel;
12866
12867 childrel = table_open(childcon->conrelid, lockmode);
12868
12869 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12870 childtup, recurse, otherrelids, lockmode);
12871 table_close(childrel, NoLock);
12872 }
12873
12874 systable_endscan(pscan);
12875}
12876
12877/*
12878 * Update the constraint entry for the given ATAlterConstraint command, and
12879 * invoke the appropriate hooks.
12880 */
12881static void
12883 HeapTuple contuple)
12884{
12885 HeapTuple copyTuple;
12886 Form_pg_constraint copy_con;
12887
12888 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12889 cmdcon->alterInheritability);
12890
12891 copyTuple = heap_copytuple(contuple);
12892 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12893
12894 if (cmdcon->alterEnforceability)
12895 {
12896 copy_con->conenforced = cmdcon->is_enforced;
12897
12898 /*
12899 * NB: The convalidated status is irrelevant when the constraint is
12900 * set to NOT ENFORCED, but for consistency, it should still be set
12901 * appropriately. Similarly, if the constraint is later changed to
12902 * ENFORCED, validation will be performed during phase 3, so it makes
12903 * sense to mark it as valid in that case.
12904 */
12905 copy_con->convalidated = cmdcon->is_enforced;
12906 }
12907 if (cmdcon->alterDeferrability)
12908 {
12909 copy_con->condeferrable = cmdcon->deferrable;
12910 copy_con->condeferred = cmdcon->initdeferred;
12911 }
12912 if (cmdcon->alterInheritability)
12913 copy_con->connoinherit = cmdcon->noinherit;
12914
12915 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12916 InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12917
12918 /* Make new constraint flags visible to others */
12919 CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12920
12921 heap_freetuple(copyTuple);
12922}
12923
12924/*
12925 * ALTER TABLE VALIDATE CONSTRAINT
12926 *
12927 * XXX The reason we handle recursion here rather than at Phase 1 is because
12928 * there's no good way to skip recursing when handling foreign keys: there is
12929 * no need to lock children in that case, yet we wouldn't be able to avoid
12930 * doing so at that level.
12931 *
12932 * Return value is the address of the validated constraint. If the constraint
12933 * was already validated, InvalidObjectAddress is returned.
12934 */
12935static ObjectAddress
12936ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12937 bool recurse, bool recursing, LOCKMODE lockmode)
12938{
12939 Relation conrel;
12940 SysScanDesc scan;
12941 ScanKeyData skey[3];
12942 HeapTuple tuple;
12944 ObjectAddress address;
12945
12946 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12947
12948 /*
12949 * Find and check the target constraint
12950 */
12951 ScanKeyInit(&skey[0],
12952 Anum_pg_constraint_conrelid,
12953 BTEqualStrategyNumber, F_OIDEQ,
12955 ScanKeyInit(&skey[1],
12956 Anum_pg_constraint_contypid,
12957 BTEqualStrategyNumber, F_OIDEQ,
12959 ScanKeyInit(&skey[2],
12960 Anum_pg_constraint_conname,
12961 BTEqualStrategyNumber, F_NAMEEQ,
12962 CStringGetDatum(constrName));
12963 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12964 true, NULL, 3, skey);
12965
12966 /* There can be at most one matching row */
12967 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12968 ereport(ERROR,
12969 (errcode(ERRCODE_UNDEFINED_OBJECT),
12970 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12971 constrName, RelationGetRelationName(rel))));
12972
12973 con = (Form_pg_constraint) GETSTRUCT(tuple);
12974 if (con->contype != CONSTRAINT_FOREIGN &&
12975 con->contype != CONSTRAINT_CHECK &&
12976 con->contype != CONSTRAINT_NOTNULL)
12977 ereport(ERROR,
12978 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12979 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12980 constrName, RelationGetRelationName(rel)),
12981 errdetail("This operation is not supported for this type of constraint."));
12982
12983 if (!con->conenforced)
12984 ereport(ERROR,
12985 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12986 errmsg("cannot validate NOT ENFORCED constraint")));
12987
12988 if (!con->convalidated)
12989 {
12990 if (con->contype == CONSTRAINT_FOREIGN)
12991 {
12992 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12993 tuple, lockmode);
12994 }
12995 else if (con->contype == CONSTRAINT_CHECK)
12996 {
12997 QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12998 tuple, recurse, recursing, lockmode);
12999 }
13000 else if (con->contype == CONSTRAINT_NOTNULL)
13001 {
13002 QueueNNConstraintValidation(wqueue, conrel, rel,
13003 tuple, recurse, recursing, lockmode);
13004 }
13005
13006 ObjectAddressSet(address, ConstraintRelationId, con->oid);
13007 }
13008 else
13009 address = InvalidObjectAddress; /* already validated */
13010
13011 systable_endscan(scan);
13012
13014
13015 return address;
13016}
13017
13018/*
13019 * QueueFKConstraintValidation
13020 *
13021 * Add an entry to the wqueue to validate the given foreign key constraint in
13022 * Phase 3 and update the convalidated field in the pg_constraint catalog
13023 * for the specified relation and all its children.
13024 */
13025static void
13027 Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
13028{
13030 AlteredTableInfo *tab;
13031 HeapTuple copyTuple;
13032 Form_pg_constraint copy_con;
13033
13034 con = (Form_pg_constraint) GETSTRUCT(contuple);
13035 Assert(con->contype == CONSTRAINT_FOREIGN);
13036 Assert(!con->convalidated);
13037
13038 /*
13039 * Add the validation to phase 3's queue; not needed for partitioned
13040 * tables themselves, only for their partitions.
13041 *
13042 * When the referenced table (pkrelid) is partitioned, the referencing
13043 * table (fkrel) has one pg_constraint row pointing to each partition
13044 * thereof. These rows are there only to support action triggers and no
13045 * table scan is needed, therefore skip this for them as well.
13046 */
13047 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13048 con->confrelid == pkrelid)
13049 {
13050 NewConstraint *newcon;
13051 Constraint *fkconstraint;
13052
13053 /* Queue validation for phase 3 */
13054 fkconstraint = makeNode(Constraint);
13055 /* for now this is all we need */
13056 fkconstraint->conname = pstrdup(NameStr(con->conname));
13057
13058 newcon = palloc0_object(NewConstraint);
13059 newcon->name = fkconstraint->conname;
13060 newcon->contype = CONSTR_FOREIGN;
13061 newcon->refrelid = con->confrelid;
13062 newcon->refindid = con->conindid;
13063 newcon->conid = con->oid;
13064 newcon->qual = (Node *) fkconstraint;
13065
13066 /* Find or create work queue entry for this table */
13067 tab = ATGetQueueEntry(wqueue, fkrel);
13068 tab->constraints = lappend(tab->constraints, newcon);
13069 }
13070
13071 /*
13072 * If the table at either end of the constraint is partitioned, we need to
13073 * recurse and handle every unvalidate constraint that is a child of this
13074 * constraint.
13075 */
13076 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13077 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13078 {
13079 ScanKeyData pkey;
13080 SysScanDesc pscan;
13081 HeapTuple childtup;
13082
13083 ScanKeyInit(&pkey,
13084 Anum_pg_constraint_conparentid,
13085 BTEqualStrategyNumber, F_OIDEQ,
13086 ObjectIdGetDatum(con->oid));
13087
13088 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13089 true, NULL, 1, &pkey);
13090
13091 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13092 {
13093 Form_pg_constraint childcon;
13094 Relation childrel;
13095
13096 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13097
13098 /*
13099 * If the child constraint has already been validated, no further
13100 * action is required for it or its descendants, as they are all
13101 * valid.
13102 */
13103 if (childcon->convalidated)
13104 continue;
13105
13106 childrel = table_open(childcon->conrelid, lockmode);
13107
13108 /*
13109 * NB: Note that pkrelid should be passed as-is during recursion,
13110 * as it is required to identify the root referenced table.
13111 */
13112 QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13113 childtup, lockmode);
13114 table_close(childrel, NoLock);
13115 }
13116
13117 systable_endscan(pscan);
13118 }
13119
13120 /*
13121 * Now mark the pg_constraint row as validated (even if we didn't check,
13122 * notably the ones for partitions on the referenced side).
13123 *
13124 * We rely on transaction abort to roll back this change if phase 3
13125 * ultimately finds violating rows. This is a bit ugly.
13126 */
13127 copyTuple = heap_copytuple(contuple);
13128 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13129 copy_con->convalidated = true;
13130 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13131
13132 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13133
13134 heap_freetuple(copyTuple);
13135}
13136
13137/*
13138 * QueueCheckConstraintValidation
13139 *
13140 * Add an entry to the wqueue to validate the given check constraint in Phase 3
13141 * and update the convalidated field in the pg_constraint catalog for the
13142 * specified relation and all its inheriting children.
13143 */
13144static void
13146 char *constrName, HeapTuple contuple,
13147 bool recurse, bool recursing, LOCKMODE lockmode)
13148{
13150 AlteredTableInfo *tab;
13151 HeapTuple copyTuple;
13152 Form_pg_constraint copy_con;
13153
13154 List *children = NIL;
13155 ListCell *child;
13156 NewConstraint *newcon;
13157 Datum val;
13158 char *conbin;
13159
13160 con = (Form_pg_constraint) GETSTRUCT(contuple);
13161 Assert(con->contype == CONSTRAINT_CHECK);
13162
13163 /*
13164 * If we're recursing, the parent has already done this, so skip it. Also,
13165 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13166 * for it in the children.
13167 */
13168 if (!recursing && !con->connoinherit)
13169 children = find_all_inheritors(RelationGetRelid(rel),
13170 lockmode, NULL);
13171
13172 /*
13173 * For CHECK constraints, we must ensure that we only mark the constraint
13174 * as validated on the parent if it's already validated on the children.
13175 *
13176 * We recurse before validating on the parent, to reduce risk of
13177 * deadlocks.
13178 */
13179 foreach(child, children)
13180 {
13181 Oid childoid = lfirst_oid(child);
13182 Relation childrel;
13183
13184 if (childoid == RelationGetRelid(rel))
13185 continue;
13186
13187 /*
13188 * If we are told not to recurse, there had better not be any child
13189 * tables, because we can't mark the constraint on the parent valid
13190 * unless it is valid for all child tables.
13191 */
13192 if (!recurse)
13193 ereport(ERROR,
13194 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13195 errmsg("constraint must be validated on child tables too")));
13196
13197 /* find_all_inheritors already got lock */
13198 childrel = table_open(childoid, NoLock);
13199
13200 ATExecValidateConstraint(wqueue, childrel, constrName, false,
13201 true, lockmode);
13202 table_close(childrel, NoLock);
13203 }
13204
13205 /* Queue validation for phase 3 */
13206 newcon = palloc0_object(NewConstraint);
13207 newcon->name = constrName;
13208 newcon->contype = CONSTR_CHECK;
13209 newcon->refrelid = InvalidOid;
13210 newcon->refindid = InvalidOid;
13211 newcon->conid = con->oid;
13212
13213 val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13214 Anum_pg_constraint_conbin);
13215 conbin = TextDatumGetCString(val);
13216 newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13217
13218 /* Find or create work queue entry for this table */
13219 tab = ATGetQueueEntry(wqueue, rel);
13220 tab->constraints = lappend(tab->constraints, newcon);
13221
13222 /*
13223 * Invalidate relcache so that others see the new validated constraint.
13224 */
13226
13227 /*
13228 * Now update the catalog, while we have the door open.
13229 */
13230 copyTuple = heap_copytuple(contuple);
13231 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13232 copy_con->convalidated = true;
13233 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13234
13235 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13236
13237 heap_freetuple(copyTuple);
13238}
13239
13240/*
13241 * QueueNNConstraintValidation
13242 *
13243 * Add an entry to the wqueue to validate the given not-null constraint in
13244 * Phase 3 and update the convalidated field in the pg_constraint catalog for
13245 * the specified relation and all its inheriting children.
13246 */
13247static void
13249 HeapTuple contuple, bool recurse, bool recursing,
13250 LOCKMODE lockmode)
13251{
13253 AlteredTableInfo *tab;
13254 HeapTuple copyTuple;
13255 Form_pg_constraint copy_con;
13256 List *children = NIL;
13258 char *colname;
13259
13260 con = (Form_pg_constraint) GETSTRUCT(contuple);
13261 Assert(con->contype == CONSTRAINT_NOTNULL);
13262
13263 attnum = extractNotNullColumn(contuple);
13264
13265 /*
13266 * If we're recursing, we've already done this for parent, so skip it.
13267 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13268 * look for it in the children.
13269 *
13270 * We recurse before validating on the parent, to reduce risk of
13271 * deadlocks.
13272 */
13273 if (!recursing && !con->connoinherit)
13274 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13275
13276 colname = get_attname(RelationGetRelid(rel), attnum, false);
13277 foreach_oid(childoid, children)
13278 {
13279 Relation childrel;
13280 HeapTuple contup;
13281 Form_pg_constraint childcon;
13282 char *conname;
13283
13284 if (childoid == RelationGetRelid(rel))
13285 continue;
13286
13287 /*
13288 * If we are told not to recurse, there had better not be any child
13289 * tables, because we can't mark the constraint on the parent valid
13290 * unless it is valid for all child tables.
13291 */
13292 if (!recurse)
13293 ereport(ERROR,
13294 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13295 errmsg("constraint must be validated on child tables too"));
13296
13297 /*
13298 * The column on child might have a different attnum, so search by
13299 * column name.
13300 */
13301 contup = findNotNullConstraint(childoid, colname);
13302 if (!contup)
13303 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13304 colname, get_rel_name(childoid));
13305 childcon = (Form_pg_constraint) GETSTRUCT(contup);
13306 if (childcon->convalidated)
13307 continue;
13308
13309 /* find_all_inheritors already got lock */
13310 childrel = table_open(childoid, NoLock);
13311 conname = pstrdup(NameStr(childcon->conname));
13312
13313 /* XXX improve ATExecValidateConstraint API to avoid double search */
13314 ATExecValidateConstraint(wqueue, childrel, conname,
13315 false, true, lockmode);
13316 table_close(childrel, NoLock);
13317 }
13318
13319 /* Set attnotnull appropriately without queueing another validation */
13320 set_attnotnull(NULL, rel, attnum, true, false);
13321
13322 tab = ATGetQueueEntry(wqueue, rel);
13323 tab->verify_new_notnull = true;
13324
13325 /*
13326 * Invalidate relcache so that others see the new validated constraint.
13327 */
13329
13330 /*
13331 * Now update the catalogs, while we have the door open.
13332 */
13333 copyTuple = heap_copytuple(contuple);
13334 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13335 copy_con->convalidated = true;
13336 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13337
13338 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13339
13340 heap_freetuple(copyTuple);
13341}
13342
13343/*
13344 * transformColumnNameList - transform list of column names
13345 *
13346 * Lookup each name and return its attnum and, optionally, type and collation
13347 * OIDs
13348 *
13349 * Note: the name of this function suggests that it's general-purpose,
13350 * but actually it's only used to look up names appearing in foreign-key
13351 * clauses. The error messages would need work to use it in other cases,
13352 * and perhaps the validity checks as well.
13353 */
13354static int
13356 int16 *attnums, Oid *atttypids, Oid *attcollids)
13357{
13358 ListCell *l;
13359 int attnum;
13360
13361 attnum = 0;
13362 foreach(l, colList)
13363 {
13364 char *attname = strVal(lfirst(l));
13365 HeapTuple atttuple;
13366 Form_pg_attribute attform;
13367
13368 atttuple = SearchSysCacheAttName(relId, attname);
13369 if (!HeapTupleIsValid(atttuple))
13370 ereport(ERROR,
13371 (errcode(ERRCODE_UNDEFINED_COLUMN),
13372 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13373 attname)));
13374 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13375 if (attform->attnum < 0)
13376 ereport(ERROR,
13377 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13378 errmsg("system columns cannot be used in foreign keys")));
13379 if (attnum >= INDEX_MAX_KEYS)
13380 ereport(ERROR,
13381 (errcode(ERRCODE_TOO_MANY_COLUMNS),
13382 errmsg("cannot have more than %d keys in a foreign key",
13383 INDEX_MAX_KEYS)));
13384 attnums[attnum] = attform->attnum;
13385 if (atttypids != NULL)
13386 atttypids[attnum] = attform->atttypid;
13387 if (attcollids != NULL)
13388 attcollids[attnum] = attform->attcollation;
13389 ReleaseSysCache(atttuple);
13390 attnum++;
13391 }
13392
13393 return attnum;
13394}
13395
13396/*
13397 * transformFkeyGetPrimaryKey -
13398 *
13399 * Look up the names, attnums, types, and collations of the primary key attributes
13400 * for the pkrel. Also return the index OID and index opclasses of the
13401 * index supporting the primary key. Also return whether the index has
13402 * WITHOUT OVERLAPS.
13403 *
13404 * All parameters except pkrel are output parameters. Also, the function
13405 * return value is the number of attributes in the primary key.
13406 *
13407 * Used when the column list in the REFERENCES specification is omitted.
13408 */
13409static int
13411 List **attnamelist,
13412 int16 *attnums, Oid *atttypids, Oid *attcollids,
13413 Oid *opclasses, bool *pk_has_without_overlaps)
13414{
13415 List *indexoidlist;
13416 ListCell *indexoidscan;
13417 HeapTuple indexTuple = NULL;
13418 Form_pg_index indexStruct = NULL;
13419 Datum indclassDatum;
13420 oidvector *indclass;
13421 int i;
13422
13423 /*
13424 * Get the list of index OIDs for the table from the relcache, and look up
13425 * each one in the pg_index syscache until we find one marked primary key
13426 * (hopefully there isn't more than one such). Insist it's valid, too.
13427 */
13428 *indexOid = InvalidOid;
13429
13430 indexoidlist = RelationGetIndexList(pkrel);
13431
13432 foreach(indexoidscan, indexoidlist)
13433 {
13434 Oid indexoid = lfirst_oid(indexoidscan);
13435
13436 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13437 if (!HeapTupleIsValid(indexTuple))
13438 elog(ERROR, "cache lookup failed for index %u", indexoid);
13439 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13440 if (indexStruct->indisprimary && indexStruct->indisvalid)
13441 {
13442 /*
13443 * Refuse to use a deferrable primary key. This is per SQL spec,
13444 * and there would be a lot of interesting semantic problems if we
13445 * tried to allow it.
13446 */
13447 if (!indexStruct->indimmediate)
13448 ereport(ERROR,
13449 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13450 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13451 RelationGetRelationName(pkrel))));
13452
13453 *indexOid = indexoid;
13454 break;
13455 }
13456 ReleaseSysCache(indexTuple);
13457 }
13458
13459 list_free(indexoidlist);
13460
13461 /*
13462 * Check that we found it
13463 */
13464 if (!OidIsValid(*indexOid))
13465 ereport(ERROR,
13466 (errcode(ERRCODE_UNDEFINED_OBJECT),
13467 errmsg("there is no primary key for referenced table \"%s\"",
13468 RelationGetRelationName(pkrel))));
13469
13470 /* Must get indclass the hard way */
13471 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13472 Anum_pg_index_indclass);
13473 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13474
13475 /*
13476 * Now build the list of PK attributes from the indkey definition (we
13477 * assume a primary key cannot have expressional elements)
13478 */
13479 *attnamelist = NIL;
13480 for (i = 0; i < indexStruct->indnkeyatts; i++)
13481 {
13482 int pkattno = indexStruct->indkey.values[i];
13483
13484 attnums[i] = pkattno;
13485 atttypids[i] = attnumTypeId(pkrel, pkattno);
13486 attcollids[i] = attnumCollationId(pkrel, pkattno);
13487 opclasses[i] = indclass->values[i];
13488 *attnamelist = lappend(*attnamelist,
13489 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13490 }
13491
13492 *pk_has_without_overlaps = indexStruct->indisexclusion;
13493
13494 ReleaseSysCache(indexTuple);
13495
13496 return i;
13497}
13498
13499/*
13500 * transformFkeyCheckAttrs -
13501 *
13502 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13503 * reference as part of a foreign key constraint.
13504 *
13505 * Returns the OID of the unique index supporting the constraint and
13506 * populates the caller-provided 'opclasses' array with the opclasses
13507 * associated with the index columns. Also sets whether the index
13508 * uses WITHOUT OVERLAPS.
13509 *
13510 * Raises an ERROR on validation failure.
13511 */
13512static Oid
13514 int numattrs, int16 *attnums,
13515 bool with_period, Oid *opclasses,
13516 bool *pk_has_without_overlaps)
13517{
13518 Oid indexoid = InvalidOid;
13519 bool found = false;
13520 bool found_deferrable = false;
13521 List *indexoidlist;
13522 ListCell *indexoidscan;
13523 int i,
13524 j;
13525
13526 /*
13527 * Reject duplicate appearances of columns in the referenced-columns list.
13528 * Such a case is forbidden by the SQL standard, and even if we thought it
13529 * useful to allow it, there would be ambiguity about how to match the
13530 * list to unique indexes (in particular, it'd be unclear which index
13531 * opclass goes with which FK column).
13532 */
13533 for (i = 0; i < numattrs; i++)
13534 {
13535 for (j = i + 1; j < numattrs; j++)
13536 {
13537 if (attnums[i] == attnums[j])
13538 ereport(ERROR,
13539 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13540 errmsg("foreign key referenced-columns list must not contain duplicates")));
13541 }
13542 }
13543
13544 /*
13545 * Get the list of index OIDs for the table from the relcache, and look up
13546 * each one in the pg_index syscache, and match unique indexes to the list
13547 * of attnums we are given.
13548 */
13549 indexoidlist = RelationGetIndexList(pkrel);
13550
13551 foreach(indexoidscan, indexoidlist)
13552 {
13553 HeapTuple indexTuple;
13554 Form_pg_index indexStruct;
13555
13556 indexoid = lfirst_oid(indexoidscan);
13557 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13558 if (!HeapTupleIsValid(indexTuple))
13559 elog(ERROR, "cache lookup failed for index %u", indexoid);
13560 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13561
13562 /*
13563 * Must have the right number of columns; must be unique (or if
13564 * temporal then exclusion instead) and not a partial index; forget it
13565 * if there are any expressions, too. Invalid indexes are out as well.
13566 */
13567 if (indexStruct->indnkeyatts == numattrs &&
13568 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13569 indexStruct->indisvalid &&
13570 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13571 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13572 {
13573 Datum indclassDatum;
13574 oidvector *indclass;
13575
13576 /* Must get indclass the hard way */
13577 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13578 Anum_pg_index_indclass);
13579 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13580
13581 /*
13582 * The given attnum list may match the index columns in any order.
13583 * Check for a match, and extract the appropriate opclasses while
13584 * we're at it.
13585 *
13586 * We know that attnums[] is duplicate-free per the test at the
13587 * start of this function, and we checked above that the number of
13588 * index columns agrees, so if we find a match for each attnums[]
13589 * entry then we must have a one-to-one match in some order.
13590 */
13591 for (i = 0; i < numattrs; i++)
13592 {
13593 found = false;
13594 for (j = 0; j < numattrs; j++)
13595 {
13596 if (attnums[i] == indexStruct->indkey.values[j])
13597 {
13598 opclasses[i] = indclass->values[j];
13599 found = true;
13600 break;
13601 }
13602 }
13603 if (!found)
13604 break;
13605 }
13606 /* The last attribute in the index must be the PERIOD FK part */
13607 if (found && with_period)
13608 {
13609 int16 periodattnum = attnums[numattrs - 1];
13610
13611 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13612 }
13613
13614 /*
13615 * Refuse to use a deferrable unique/primary key. This is per SQL
13616 * spec, and there would be a lot of interesting semantic problems
13617 * if we tried to allow it.
13618 */
13619 if (found && !indexStruct->indimmediate)
13620 {
13621 /*
13622 * Remember that we found an otherwise matching index, so that
13623 * we can generate a more appropriate error message.
13624 */
13625 found_deferrable = true;
13626 found = false;
13627 }
13628
13629 /* We need to know whether the index has WITHOUT OVERLAPS */
13630 if (found)
13631 *pk_has_without_overlaps = indexStruct->indisexclusion;
13632 }
13633 ReleaseSysCache(indexTuple);
13634 if (found)
13635 break;
13636 }
13637
13638 if (!found)
13639 {
13640 if (found_deferrable)
13641 ereport(ERROR,
13642 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13643 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13644 RelationGetRelationName(pkrel))));
13645 else
13646 ereport(ERROR,
13647 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13648 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13649 RelationGetRelationName(pkrel))));
13650 }
13651
13652 list_free(indexoidlist);
13653
13654 return indexoid;
13655}
13656
13657/*
13658 * findFkeyCast -
13659 *
13660 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13661 * Caller has equal regard for binary coercibility and for an exact match.
13662*/
13663static CoercionPathType
13664findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13665{
13666 CoercionPathType ret;
13667
13668 if (targetTypeId == sourceTypeId)
13669 {
13671 *funcid = InvalidOid;
13672 }
13673 else
13674 {
13675 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13676 COERCION_IMPLICIT, funcid);
13677 if (ret == COERCION_PATH_NONE)
13678 /* A previously-relied-upon cast is now gone. */
13679 elog(ERROR, "could not find cast from %u to %u",
13680 sourceTypeId, targetTypeId);
13681 }
13682
13683 return ret;
13684}
13685
13686/*
13687 * Permissions checks on the referenced table for ADD FOREIGN KEY
13688 *
13689 * Note: we have already checked that the user owns the referencing table,
13690 * else we'd have failed much earlier; no additional checks are needed for it.
13691 */
13692static void
13693checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13694{
13695 Oid roleid = GetUserId();
13696 AclResult aclresult;
13697 int i;
13698
13699 /* Okay if we have relation-level REFERENCES permission */
13700 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13702 if (aclresult == ACLCHECK_OK)
13703 return;
13704 /* Else we must have REFERENCES on each column */
13705 for (i = 0; i < natts; i++)
13706 {
13707 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13708 roleid, ACL_REFERENCES);
13709 if (aclresult != ACLCHECK_OK)
13710 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13712 }
13713}
13714
13715/*
13716 * Scan the existing rows in a table to verify they meet a proposed FK
13717 * constraint.
13718 *
13719 * Caller must have opened and locked both relations appropriately.
13720 */
13721static void
13723 Relation rel,
13724 Relation pkrel,
13725 Oid pkindOid,
13726 Oid constraintOid,
13727 bool hasperiod)
13728{
13729 TupleTableSlot *slot;
13730 TableScanDesc scan;
13731 Trigger trig = {0};
13732 Snapshot snapshot;
13733 MemoryContext oldcxt;
13734 MemoryContext perTupCxt;
13735
13737 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13738
13739 /*
13740 * Build a trigger call structure; we'll need it either way.
13741 */
13742 trig.tgoid = InvalidOid;
13743 trig.tgname = conname;
13745 trig.tgisinternal = true;
13746 trig.tgconstrrelid = RelationGetRelid(pkrel);
13747 trig.tgconstrindid = pkindOid;
13748 trig.tgconstraint = constraintOid;
13749 trig.tgdeferrable = false;
13750 trig.tginitdeferred = false;
13751 /* we needn't fill in remaining fields */
13752
13753 /*
13754 * See if we can do it with a single LEFT JOIN query. A false result
13755 * indicates we must proceed with the fire-the-trigger method. We can't do
13756 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13757 * left joins.
13758 */
13759 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13760 return;
13761
13762 /*
13763 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13764 * if that tuple had just been inserted. If any of those fail, it should
13765 * ereport(ERROR) and that's that.
13766 */
13767 snapshot = RegisterSnapshot(GetLatestSnapshot());
13768 slot = table_slot_create(rel, NULL);
13769 scan = table_beginscan(rel, snapshot, 0, NULL);
13770
13772 "validateForeignKeyConstraint",
13774 oldcxt = MemoryContextSwitchTo(perTupCxt);
13775
13776 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13777 {
13778 LOCAL_FCINFO(fcinfo, 0);
13779 TriggerData trigdata = {0};
13780
13782
13783 /*
13784 * Make a call to the trigger function
13785 *
13786 * No parameters are passed, but we do set a context
13787 */
13788 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13789
13790 /*
13791 * We assume RI_FKey_check_ins won't look at flinfo...
13792 */
13793 trigdata.type = T_TriggerData;
13795 trigdata.tg_relation = rel;
13796 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13797 trigdata.tg_trigslot = slot;
13798 trigdata.tg_trigger = &trig;
13799
13800 fcinfo->context = (Node *) &trigdata;
13801
13802 RI_FKey_check_ins(fcinfo);
13803
13804 MemoryContextReset(perTupCxt);
13805 }
13806
13807 MemoryContextSwitchTo(oldcxt);
13808 MemoryContextDelete(perTupCxt);
13809 table_endscan(scan);
13810 UnregisterSnapshot(snapshot);
13812}
13813
13814/*
13815 * CreateFKCheckTrigger
13816 * Creates the insert (on_insert=true) or update "check" trigger that
13817 * implements a given foreign key
13818 *
13819 * Returns the OID of the so created trigger.
13820 */
13821static Oid
13822CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13823 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13824 bool on_insert)
13825{
13826 ObjectAddress trigAddress;
13827 CreateTrigStmt *fk_trigger;
13828
13829 /*
13830 * Note: for a self-referential FK (referencing and referenced tables are
13831 * the same), it is important that the ON UPDATE action fires before the
13832 * CHECK action, since both triggers will fire on the same row during an
13833 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13834 * state of the row. Triggers fire in name order, so we ensure this by
13835 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13836 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13837 */
13838 fk_trigger = makeNode(CreateTrigStmt);
13839 fk_trigger->replace = false;
13840 fk_trigger->isconstraint = true;
13841 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13842 fk_trigger->relation = NULL;
13843
13844 /* Either ON INSERT or ON UPDATE */
13845 if (on_insert)
13846 {
13847 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13848 fk_trigger->events = TRIGGER_TYPE_INSERT;
13849 }
13850 else
13851 {
13852 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13853 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13854 }
13855
13856 fk_trigger->args = NIL;
13857 fk_trigger->row = true;
13858 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13859 fk_trigger->columns = NIL;
13860 fk_trigger->whenClause = NULL;
13861 fk_trigger->transitionRels = NIL;
13862 fk_trigger->deferrable = fkconstraint->deferrable;
13863 fk_trigger->initdeferred = fkconstraint->initdeferred;
13864 fk_trigger->constrrel = NULL;
13865
13866 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13867 constraintOid, indexOid, InvalidOid,
13868 parentTrigOid, NULL, true, false);
13869
13870 /* Make changes-so-far visible */
13872
13873 return trigAddress.objectId;
13874}
13875
13876/*
13877 * createForeignKeyActionTriggers
13878 * Create the referenced-side "action" triggers that implement a foreign
13879 * key.
13880 *
13881 * Returns the OIDs of the so created triggers in *deleteTrigOid and
13882 * *updateTrigOid.
13883 */
13884static void
13885createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13886 Oid constraintOid, Oid indexOid,
13887 Oid parentDelTrigger, Oid parentUpdTrigger,
13888 Oid *deleteTrigOid, Oid *updateTrigOid)
13889{
13890 CreateTrigStmt *fk_trigger;
13891 ObjectAddress trigAddress;
13892
13893 /*
13894 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13895 * DELETE action on the referenced table.
13896 */
13897 fk_trigger = makeNode(CreateTrigStmt);
13898 fk_trigger->replace = false;
13899 fk_trigger->isconstraint = true;
13900 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13901 fk_trigger->relation = NULL;
13902 fk_trigger->args = NIL;
13903 fk_trigger->row = true;
13904 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13905 fk_trigger->events = TRIGGER_TYPE_DELETE;
13906 fk_trigger->columns = NIL;
13907 fk_trigger->whenClause = NULL;
13908 fk_trigger->transitionRels = NIL;
13909 fk_trigger->constrrel = NULL;
13910
13911 switch (fkconstraint->fk_del_action)
13912 {
13914 fk_trigger->deferrable = fkconstraint->deferrable;
13915 fk_trigger->initdeferred = fkconstraint->initdeferred;
13916 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13917 break;
13919 fk_trigger->deferrable = false;
13920 fk_trigger->initdeferred = false;
13921 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13922 break;
13924 fk_trigger->deferrable = false;
13925 fk_trigger->initdeferred = false;
13926 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13927 break;
13929 fk_trigger->deferrable = false;
13930 fk_trigger->initdeferred = false;
13931 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13932 break;
13934 fk_trigger->deferrable = false;
13935 fk_trigger->initdeferred = false;
13936 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13937 break;
13938 default:
13939 elog(ERROR, "unrecognized FK action type: %d",
13940 (int) fkconstraint->fk_del_action);
13941 break;
13942 }
13943
13944 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13945 constraintOid, indexOid, InvalidOid,
13946 parentDelTrigger, NULL, true, false);
13947 if (deleteTrigOid)
13948 *deleteTrigOid = trigAddress.objectId;
13949
13950 /* Make changes-so-far visible */
13952
13953 /*
13954 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13955 * UPDATE action on the referenced table.
13956 */
13957 fk_trigger = makeNode(CreateTrigStmt);
13958 fk_trigger->replace = false;
13959 fk_trigger->isconstraint = true;
13960 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13961 fk_trigger->relation = NULL;
13962 fk_trigger->args = NIL;
13963 fk_trigger->row = true;
13964 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13965 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13966 fk_trigger->columns = NIL;
13967 fk_trigger->whenClause = NULL;
13968 fk_trigger->transitionRels = NIL;
13969 fk_trigger->constrrel = NULL;
13970
13971 switch (fkconstraint->fk_upd_action)
13972 {
13974 fk_trigger->deferrable = fkconstraint->deferrable;
13975 fk_trigger->initdeferred = fkconstraint->initdeferred;
13976 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13977 break;
13979 fk_trigger->deferrable = false;
13980 fk_trigger->initdeferred = false;
13981 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13982 break;
13984 fk_trigger->deferrable = false;
13985 fk_trigger->initdeferred = false;
13986 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13987 break;
13989 fk_trigger->deferrable = false;
13990 fk_trigger->initdeferred = false;
13991 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13992 break;
13994 fk_trigger->deferrable = false;
13995 fk_trigger->initdeferred = false;
13996 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13997 break;
13998 default:
13999 elog(ERROR, "unrecognized FK action type: %d",
14000 (int) fkconstraint->fk_upd_action);
14001 break;
14002 }
14003
14004 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
14005 constraintOid, indexOid, InvalidOid,
14006 parentUpdTrigger, NULL, true, false);
14007 if (updateTrigOid)
14008 *updateTrigOid = trigAddress.objectId;
14009}
14010
14011/*
14012 * createForeignKeyCheckTriggers
14013 * Create the referencing-side "check" triggers that implement a foreign
14014 * key.
14015 *
14016 * Returns the OIDs of the so created triggers in *insertTrigOid and
14017 * *updateTrigOid.
14018 */
14019static void
14021 Constraint *fkconstraint, Oid constraintOid,
14022 Oid indexOid,
14023 Oid parentInsTrigger, Oid parentUpdTrigger,
14024 Oid *insertTrigOid, Oid *updateTrigOid)
14025{
14026 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14027 constraintOid, indexOid,
14028 parentInsTrigger, true);
14029 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14030 constraintOid, indexOid,
14031 parentUpdTrigger, false);
14032}
14033
14034/*
14035 * ALTER TABLE DROP CONSTRAINT
14036 *
14037 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
14038 */
14039static void
14040ATExecDropConstraint(Relation rel, const char *constrName,
14041 DropBehavior behavior, bool recurse,
14042 bool missing_ok, LOCKMODE lockmode)
14043{
14044 Relation conrel;
14045 SysScanDesc scan;
14046 ScanKeyData skey[3];
14047 HeapTuple tuple;
14048 bool found = false;
14049
14050 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14051
14052 /*
14053 * Find and drop the target constraint
14054 */
14055 ScanKeyInit(&skey[0],
14056 Anum_pg_constraint_conrelid,
14057 BTEqualStrategyNumber, F_OIDEQ,
14059 ScanKeyInit(&skey[1],
14060 Anum_pg_constraint_contypid,
14061 BTEqualStrategyNumber, F_OIDEQ,
14063 ScanKeyInit(&skey[2],
14064 Anum_pg_constraint_conname,
14065 BTEqualStrategyNumber, F_NAMEEQ,
14066 CStringGetDatum(constrName));
14067 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14068 true, NULL, 3, skey);
14069
14070 /* There can be at most one matching row */
14071 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14072 {
14073 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14074 missing_ok, lockmode);
14075 found = true;
14076 }
14077
14078 systable_endscan(scan);
14079
14080 if (!found)
14081 {
14082 if (!missing_ok)
14083 ereport(ERROR,
14084 errcode(ERRCODE_UNDEFINED_OBJECT),
14085 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14086 constrName, RelationGetRelationName(rel)));
14087 else
14089 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14090 constrName, RelationGetRelationName(rel)));
14091 }
14092
14094}
14095
14096/*
14097 * Remove a constraint, using its pg_constraint tuple
14098 *
14099 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14100 * DROP NOT NULL.
14101 *
14102 * Returns the address of the constraint being removed.
14103 */
14104static ObjectAddress
14106 bool recurse, bool recursing, bool missing_ok,
14107 LOCKMODE lockmode)
14108{
14109 Relation conrel;
14111 ObjectAddress conobj;
14112 List *children;
14113 bool is_no_inherit_constraint = false;
14114 char *constrName;
14115 char *colname = NULL;
14116
14117 /* Guard against stack overflow due to overly deep inheritance tree. */
14119
14120 /* At top level, permission check was done in ATPrepCmd, else do it */
14121 if (recursing)
14124
14125 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14126
14127 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14128 constrName = NameStr(con->conname);
14129
14130 /* Don't allow drop of inherited constraints */
14131 if (con->coninhcount > 0 && !recursing)
14132 ereport(ERROR,
14133 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14134 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14135 constrName, RelationGetRelationName(rel))));
14136
14137 /*
14138 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14139 *
14140 * While doing that, we're in a good position to disallow dropping a not-
14141 * null constraint underneath a primary key, a replica identity index, or
14142 * a generated identity column.
14143 */
14144 if (con->contype == CONSTRAINT_NOTNULL)
14145 {
14146 Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14147 AttrNumber attnum = extractNotNullColumn(constraintTup);
14148 Bitmapset *pkattrs;
14149 Bitmapset *irattrs;
14150 HeapTuple atttup;
14151 Form_pg_attribute attForm;
14152
14153 /* save column name for recursion step */
14154 colname = get_attname(RelationGetRelid(rel), attnum, false);
14155
14156 /*
14157 * Disallow if it's in the primary key. For partitioned tables we
14158 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14159 * return NULL if the primary key is invalid; but we still need to
14160 * protect not-null constraints under such a constraint, so check the
14161 * slow way.
14162 */
14164
14165 if (pkattrs == NULL &&
14166 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14167 {
14168 Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14169
14170 if (OidIsValid(pkindex))
14171 {
14172 Relation pk = relation_open(pkindex, AccessShareLock);
14173
14174 pkattrs = NULL;
14175 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14176 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14177
14179 }
14180 }
14181
14182 if (pkattrs &&
14184 ereport(ERROR,
14185 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14186 errmsg("column \"%s\" is in a primary key",
14187 get_attname(RelationGetRelid(rel), attnum, false)));
14188
14189 /* Disallow if it's in the replica identity */
14192 ereport(ERROR,
14193 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14194 errmsg("column \"%s\" is in index used as replica identity",
14195 get_attname(RelationGetRelid(rel), attnum, false)));
14196
14197 /* Disallow if it's a GENERATED AS IDENTITY column */
14199 if (!HeapTupleIsValid(atttup))
14200 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14201 attnum, RelationGetRelid(rel));
14202 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14203 if (attForm->attidentity != '\0')
14204 ereport(ERROR,
14205 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14206 errmsg("column \"%s\" of relation \"%s\" is an identity column",
14208 false),
14210
14211 /* All good -- reset attnotnull if needed */
14212 if (attForm->attnotnull)
14213 {
14214 attForm->attnotnull = false;
14215 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14216 }
14217
14219 }
14220
14221 is_no_inherit_constraint = con->connoinherit;
14222
14223 /*
14224 * If it's a foreign-key constraint, we'd better lock the referenced table
14225 * and check that that's not in use, just as we've already done for the
14226 * constrained table (else we might, eg, be dropping a trigger that has
14227 * unfired events). But we can/must skip that in the self-referential
14228 * case.
14229 */
14230 if (con->contype == CONSTRAINT_FOREIGN &&
14231 con->confrelid != RelationGetRelid(rel))
14232 {
14233 Relation frel;
14234
14235 /* Must match lock taken by RemoveTriggerById: */
14236 frel = table_open(con->confrelid, AccessExclusiveLock);
14238 table_close(frel, NoLock);
14239 }
14240
14241 /*
14242 * Perform the actual constraint deletion
14243 */
14244 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14245 performDeletion(&conobj, behavior, 0);
14246
14247 /*
14248 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14249 * are dropped via the dependency mechanism, so we're done here.
14250 */
14251 if (con->contype != CONSTRAINT_CHECK &&
14252 con->contype != CONSTRAINT_NOTNULL &&
14253 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14254 {
14256 return conobj;
14257 }
14258
14259 /*
14260 * Propagate to children as appropriate. Unlike most other ALTER
14261 * routines, we have to do this one level of recursion at a time; we can't
14262 * use find_all_inheritors to do it in one pass.
14263 */
14264 if (!is_no_inherit_constraint)
14265 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14266 else
14267 children = NIL;
14268
14269 foreach_oid(childrelid, children)
14270 {
14271 Relation childrel;
14272 HeapTuple tuple;
14273 Form_pg_constraint childcon;
14274
14275 /* find_inheritance_children already got lock */
14276 childrel = table_open(childrelid, NoLock);
14277 CheckAlterTableIsSafe(childrel);
14278
14279 /*
14280 * We search for not-null constraints by column name, and others by
14281 * constraint name.
14282 */
14283 if (con->contype == CONSTRAINT_NOTNULL)
14284 {
14285 tuple = findNotNullConstraint(childrelid, colname);
14286 if (!HeapTupleIsValid(tuple))
14287 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14288 colname, RelationGetRelid(childrel));
14289 }
14290 else
14291 {
14292 SysScanDesc scan;
14293 ScanKeyData skey[3];
14294
14295 ScanKeyInit(&skey[0],
14296 Anum_pg_constraint_conrelid,
14297 BTEqualStrategyNumber, F_OIDEQ,
14298 ObjectIdGetDatum(childrelid));
14299 ScanKeyInit(&skey[1],
14300 Anum_pg_constraint_contypid,
14301 BTEqualStrategyNumber, F_OIDEQ,
14303 ScanKeyInit(&skey[2],
14304 Anum_pg_constraint_conname,
14305 BTEqualStrategyNumber, F_NAMEEQ,
14306 CStringGetDatum(constrName));
14307 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14308 true, NULL, 3, skey);
14309 /* There can only be one, so no need to loop */
14310 tuple = systable_getnext(scan);
14311 if (!HeapTupleIsValid(tuple))
14312 ereport(ERROR,
14313 (errcode(ERRCODE_UNDEFINED_OBJECT),
14314 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14315 constrName,
14316 RelationGetRelationName(childrel))));
14317 tuple = heap_copytuple(tuple);
14318 systable_endscan(scan);
14319 }
14320
14321 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14322
14323 /* Right now only CHECK and not-null constraints can be inherited */
14324 if (childcon->contype != CONSTRAINT_CHECK &&
14325 childcon->contype != CONSTRAINT_NOTNULL)
14326 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14327
14328 if (childcon->coninhcount <= 0) /* shouldn't happen */
14329 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14330 childrelid, NameStr(childcon->conname));
14331
14332 if (recurse)
14333 {
14334 /*
14335 * If the child constraint has other definition sources, just
14336 * decrement its inheritance count; if not, recurse to delete it.
14337 */
14338 if (childcon->coninhcount == 1 && !childcon->conislocal)
14339 {
14340 /* Time to delete this child constraint, too */
14341 dropconstraint_internal(childrel, tuple, behavior,
14342 recurse, true, missing_ok,
14343 lockmode);
14344 }
14345 else
14346 {
14347 /* Child constraint must survive my deletion */
14348 childcon->coninhcount--;
14349 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14350
14351 /* Make update visible */
14353 }
14354 }
14355 else
14356 {
14357 /*
14358 * If we were told to drop ONLY in this table (no recursion) and
14359 * there are no further parents for this constraint, we need to
14360 * mark the inheritors' constraints as locally defined rather than
14361 * inherited.
14362 */
14363 childcon->coninhcount--;
14364 if (childcon->coninhcount == 0)
14365 childcon->conislocal = true;
14366
14367 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14368
14369 /* Make update visible */
14371 }
14372
14373 heap_freetuple(tuple);
14374
14375 table_close(childrel, NoLock);
14376 }
14377
14379
14380 return conobj;
14381}
14382
14383/*
14384 * ALTER COLUMN TYPE
14385 *
14386 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14387 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14388 * transformed (and must be, because we rely on some transformed fields).
14389 *
14390 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14391 * table will be done "in parallel" during phase 3, so all the USING
14392 * expressions should be parsed assuming the original column types. Also,
14393 * this allows a USING expression to refer to a field that will be dropped.
14394 *
14395 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14396 * the first two execution steps in phase 2; they must not see the effects
14397 * of any other subcommand types, since the USING expressions are parsed
14398 * against the unmodified table's state.
14399 */
14400static void
14402 AlteredTableInfo *tab, Relation rel,
14403 bool recurse, bool recursing,
14404 AlterTableCmd *cmd, LOCKMODE lockmode,
14405 AlterTableUtilityContext *context)
14406{
14407 char *colName = cmd->name;
14408 ColumnDef *def = (ColumnDef *) cmd->def;
14409 TypeName *typeName = def->typeName;
14410 Node *transform = def->cooked_default;
14411 HeapTuple tuple;
14412 Form_pg_attribute attTup;
14414 Oid targettype;
14415 int32 targettypmod;
14416 Oid targetcollid;
14418 ParseState *pstate = make_parsestate(NULL);
14419 AclResult aclresult;
14420 bool is_expr;
14421
14422 pstate->p_sourcetext = context->queryString;
14423
14424 if (rel->rd_rel->reloftype && !recursing)
14425 ereport(ERROR,
14426 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14427 errmsg("cannot alter column type of typed table"),
14428 parser_errposition(pstate, def->location)));
14429
14430 /* lookup the attribute so we can check inheritance status */
14431 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14432 if (!HeapTupleIsValid(tuple))
14433 ereport(ERROR,
14434 (errcode(ERRCODE_UNDEFINED_COLUMN),
14435 errmsg("column \"%s\" of relation \"%s\" does not exist",
14436 colName, RelationGetRelationName(rel)),
14437 parser_errposition(pstate, def->location)));
14438 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14439 attnum = attTup->attnum;
14440
14441 /* Can't alter a system attribute */
14442 if (attnum <= 0)
14443 ereport(ERROR,
14444 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14445 errmsg("cannot alter system column \"%s\"", colName),
14446 parser_errposition(pstate, def->location)));
14447
14448 /*
14449 * Cannot specify USING when altering type of a generated column, because
14450 * that would violate the generation expression.
14451 */
14452 if (attTup->attgenerated && def->cooked_default)
14453 ereport(ERROR,
14454 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14455 errmsg("cannot specify USING when altering type of generated column"),
14456 errdetail("Column \"%s\" is a generated column.", colName),
14457 parser_errposition(pstate, def->location)));
14458
14459 /*
14460 * Don't alter inherited columns. At outer level, there had better not be
14461 * any inherited definition; when recursing, we assume this was checked at
14462 * the parent level (see below).
14463 */
14464 if (attTup->attinhcount > 0 && !recursing)
14465 ereport(ERROR,
14466 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14467 errmsg("cannot alter inherited column \"%s\"", colName),
14468 parser_errposition(pstate, def->location)));
14469
14470 /* Don't alter columns used in the partition key */
14471 if (has_partition_attrs(rel,
14473 &is_expr))
14474 ereport(ERROR,
14475 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14476 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14477 colName, RelationGetRelationName(rel)),
14478 parser_errposition(pstate, def->location)));
14479
14480 /* Look up the target type */
14481 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14482
14483 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14484 if (aclresult != ACLCHECK_OK)
14485 aclcheck_error_type(aclresult, targettype);
14486
14487 /* And the collation */
14488 targetcollid = GetColumnDefCollation(pstate, def, targettype);
14489
14490 /* make sure datatype is legal for a column */
14491 CheckAttributeType(colName, targettype, targetcollid,
14492 list_make1_oid(rel->rd_rel->reltype),
14493 (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14494
14495 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14496 {
14497 /* do nothing */
14498 }
14499 else if (tab->relkind == RELKIND_RELATION ||
14500 tab->relkind == RELKIND_PARTITIONED_TABLE)
14501 {
14502 /*
14503 * Set up an expression to transform the old data value to the new
14504 * type. If a USING option was given, use the expression as
14505 * transformed by transformAlterTableStmt, else just take the old
14506 * value and try to coerce it. We do this first so that type
14507 * incompatibility can be detected before we waste effort, and because
14508 * we need the expression to be parsed against the original table row
14509 * type.
14510 */
14511 if (!transform)
14512 {
14513 transform = (Node *) makeVar(1, attnum,
14514 attTup->atttypid, attTup->atttypmod,
14515 attTup->attcollation,
14516 0);
14517 }
14518
14519 transform = coerce_to_target_type(pstate,
14520 transform, exprType(transform),
14521 targettype, targettypmod,
14524 -1);
14525 if (transform == NULL)
14526 {
14527 /* error text depends on whether USING was specified or not */
14528 if (def->cooked_default != NULL)
14529 ereport(ERROR,
14530 (errcode(ERRCODE_DATATYPE_MISMATCH),
14531 errmsg("result of USING clause for column \"%s\""
14532 " cannot be cast automatically to type %s",
14533 colName, format_type_be(targettype)),
14534 errhint("You might need to add an explicit cast.")));
14535 else
14536 ereport(ERROR,
14537 (errcode(ERRCODE_DATATYPE_MISMATCH),
14538 errmsg("column \"%s\" cannot be cast automatically to type %s",
14539 colName, format_type_be(targettype)),
14540 !attTup->attgenerated ?
14541 /* translator: USING is SQL, don't translate it */
14542 errhint("You might need to specify \"USING %s::%s\".",
14543 quote_identifier(colName),
14544 format_type_with_typemod(targettype,
14545 targettypmod)) : 0));
14546 }
14547
14548 /* Fix collations after all else */
14549 assign_expr_collations(pstate, transform);
14550
14551 /* Expand virtual generated columns in the expr. */
14552 transform = expand_generated_columns_in_expr(transform, rel, 1);
14553
14554 /* Plan the expr now so we can accurately assess the need to rewrite. */
14555 transform = (Node *) expression_planner((Expr *) transform);
14556
14557 /*
14558 * Add a work queue item to make ATRewriteTable update the column
14559 * contents.
14560 */
14562 newval->attnum = attnum;
14563 newval->expr = (Expr *) transform;
14564 newval->is_generated = false;
14565
14566 tab->newvals = lappend(tab->newvals, newval);
14567 if (ATColumnChangeRequiresRewrite(transform, attnum))
14569 }
14570 else if (transform)
14571 ereport(ERROR,
14572 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14573 errmsg("\"%s\" is not a table",
14575
14576 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14577 {
14578 /*
14579 * For relations or columns without storage, do this check now.
14580 * Regular tables will check it later when the table is being
14581 * rewritten.
14582 */
14583 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14584 }
14585
14586 ReleaseSysCache(tuple);
14587
14588 /*
14589 * Recurse manually by queueing a new command for each child, if
14590 * necessary. We cannot apply ATSimpleRecursion here because we need to
14591 * remap attribute numbers in the USING expression, if any.
14592 *
14593 * If we are told not to recurse, there had better not be any child
14594 * tables; else the alter would put them out of step.
14595 */
14596 if (recurse)
14597 {
14598 Oid relid = RelationGetRelid(rel);
14599 List *child_oids,
14600 *child_numparents;
14601 ListCell *lo,
14602 *li;
14603
14604 child_oids = find_all_inheritors(relid, lockmode,
14605 &child_numparents);
14606
14607 /*
14608 * find_all_inheritors does the recursive search of the inheritance
14609 * hierarchy, so all we have to do is process all of the relids in the
14610 * list that it returns.
14611 */
14612 forboth(lo, child_oids, li, child_numparents)
14613 {
14614 Oid childrelid = lfirst_oid(lo);
14615 int numparents = lfirst_int(li);
14616 Relation childrel;
14617 HeapTuple childtuple;
14618 Form_pg_attribute childattTup;
14619
14620 if (childrelid == relid)
14621 continue;
14622
14623 /* find_all_inheritors already got lock */
14624 childrel = relation_open(childrelid, NoLock);
14625 CheckAlterTableIsSafe(childrel);
14626
14627 /*
14628 * Verify that the child doesn't have any inherited definitions of
14629 * this column that came from outside this inheritance hierarchy.
14630 * (renameatt makes a similar test, though in a different way
14631 * because of its different recursion mechanism.)
14632 */
14633 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14634 colName);
14635 if (!HeapTupleIsValid(childtuple))
14636 ereport(ERROR,
14637 (errcode(ERRCODE_UNDEFINED_COLUMN),
14638 errmsg("column \"%s\" of relation \"%s\" does not exist",
14639 colName, RelationGetRelationName(childrel))));
14640 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14641
14642 if (childattTup->attinhcount > numparents)
14643 ereport(ERROR,
14644 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14645 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14646 colName, RelationGetRelationName(childrel))));
14647
14648 ReleaseSysCache(childtuple);
14649
14650 /*
14651 * Remap the attribute numbers. If no USING expression was
14652 * specified, there is no need for this step.
14653 */
14654 if (def->cooked_default)
14655 {
14656 AttrMap *attmap;
14657 bool found_whole_row;
14658
14659 /* create a copy to scribble on */
14660 cmd = copyObject(cmd);
14661
14662 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14663 RelationGetDescr(rel),
14664 false);
14665 ((ColumnDef *) cmd->def)->cooked_default =
14667 1, 0,
14668 attmap,
14669 InvalidOid, &found_whole_row);
14670 if (found_whole_row)
14671 ereport(ERROR,
14672 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14673 errmsg("cannot convert whole-row table reference"),
14674 errdetail("USING expression contains a whole-row table reference.")));
14675 pfree(attmap);
14676 }
14677 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14678 relation_close(childrel, NoLock);
14679 }
14680 }
14681 else if (!recursing &&
14683 ereport(ERROR,
14684 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14685 errmsg("type of inherited column \"%s\" must be changed in child tables too",
14686 colName)));
14687
14688 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14689 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14690}
14691
14692/*
14693 * When the data type of a column is changed, a rewrite might not be required
14694 * if the new type is sufficiently identical to the old one, and the USING
14695 * clause isn't trying to insert some other value. It's safe to skip the
14696 * rewrite in these cases:
14697 *
14698 * - the old type is binary coercible to the new type
14699 * - the new type is an unconstrained domain over the old type
14700 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14701 *
14702 * In the case of a constrained domain, we could get by with scanning the
14703 * table and checking the constraint rather than actually rewriting it, but we
14704 * don't currently try to do that.
14705 */
14706static bool
14708{
14709 Assert(expr != NULL);
14710
14711 for (;;)
14712 {
14713 /* only one varno, so no need to check that */
14714 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14715 return false;
14716 else if (IsA(expr, RelabelType))
14717 expr = (Node *) ((RelabelType *) expr)->arg;
14718 else if (IsA(expr, CoerceToDomain))
14719 {
14720 CoerceToDomain *d = (CoerceToDomain *) expr;
14721
14723 return true;
14724 expr = (Node *) d->arg;
14725 }
14726 else if (IsA(expr, FuncExpr))
14727 {
14728 FuncExpr *f = (FuncExpr *) expr;
14729
14730 switch (f->funcid)
14731 {
14732 case F_TIMESTAMPTZ_TIMESTAMP:
14733 case F_TIMESTAMP_TIMESTAMPTZ:
14735 return true;
14736 else
14737 expr = linitial(f->args);
14738 break;
14739 default:
14740 return true;
14741 }
14742 }
14743 else
14744 return true;
14745 }
14746}
14747
14748/*
14749 * ALTER COLUMN .. SET DATA TYPE
14750 *
14751 * Return the address of the modified column.
14752 */
14753static ObjectAddress
14755 AlterTableCmd *cmd, LOCKMODE lockmode)
14756{
14757 char *colName = cmd->name;
14758 ColumnDef *def = (ColumnDef *) cmd->def;
14759 TypeName *typeName = def->typeName;
14760 HeapTuple heapTup;
14761 Form_pg_attribute attTup,
14762 attOldTup;
14764 HeapTuple typeTuple;
14765 Form_pg_type tform;
14766 Oid targettype;
14767 int32 targettypmod;
14768 Oid targetcollid;
14769 Node *defaultexpr;
14770 Relation attrelation;
14771 Relation depRel;
14772 ScanKeyData key[3];
14773 SysScanDesc scan;
14774 HeapTuple depTup;
14775 ObjectAddress address;
14776
14777 /*
14778 * Clear all the missing values if we're rewriting the table, since this
14779 * renders them pointless.
14780 */
14781 if (tab->rewrite)
14782 {
14783 Relation newrel;
14784
14785 newrel = table_open(RelationGetRelid(rel), NoLock);
14786 RelationClearMissing(newrel);
14787 relation_close(newrel, NoLock);
14788 /* make sure we don't conflict with later attribute modifications */
14790 }
14791
14792 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14793
14794 /* Look up the target column */
14795 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14796 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14797 ereport(ERROR,
14798 (errcode(ERRCODE_UNDEFINED_COLUMN),
14799 errmsg("column \"%s\" of relation \"%s\" does not exist",
14800 colName, RelationGetRelationName(rel))));
14801 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14802 attnum = attTup->attnum;
14803 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14804
14805 /* Check for multiple ALTER TYPE on same column --- can't cope */
14806 if (attTup->atttypid != attOldTup->atttypid ||
14807 attTup->atttypmod != attOldTup->atttypmod)
14808 ereport(ERROR,
14809 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14810 errmsg("cannot alter type of column \"%s\" twice",
14811 colName)));
14812
14813 /* Look up the target type (should not fail, since prep found it) */
14814 typeTuple = typenameType(NULL, typeName, &targettypmod);
14815 tform = (Form_pg_type) GETSTRUCT(typeTuple);
14816 targettype = tform->oid;
14817 /* And the collation */
14818 targetcollid = GetColumnDefCollation(NULL, def, targettype);
14819
14820 /*
14821 * If there is a default expression for the column, get it and ensure we
14822 * can coerce it to the new datatype. (We must do this before changing
14823 * the column type, because build_column_default itself will try to
14824 * coerce, and will not issue the error message we want if it fails.)
14825 *
14826 * We remove any implicit coercion steps at the top level of the old
14827 * default expression; this has been agreed to satisfy the principle of
14828 * least surprise. (The conversion to the new column type should act like
14829 * it started from what the user sees as the stored expression, and the
14830 * implicit coercions aren't going to be shown.)
14831 */
14832 if (attTup->atthasdef)
14833 {
14834 defaultexpr = build_column_default(rel, attnum);
14835 Assert(defaultexpr);
14836 defaultexpr = strip_implicit_coercions(defaultexpr);
14837 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14838 defaultexpr, exprType(defaultexpr),
14839 targettype, targettypmod,
14842 -1);
14843 if (defaultexpr == NULL)
14844 {
14845 if (attTup->attgenerated)
14846 ereport(ERROR,
14847 (errcode(ERRCODE_DATATYPE_MISMATCH),
14848 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14849 colName, format_type_be(targettype))));
14850 else
14851 ereport(ERROR,
14852 (errcode(ERRCODE_DATATYPE_MISMATCH),
14853 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14854 colName, format_type_be(targettype))));
14855 }
14856 }
14857 else
14858 defaultexpr = NULL;
14859
14860 /*
14861 * Find everything that depends on the column (constraints, indexes, etc),
14862 * and record enough information to let us recreate the objects.
14863 *
14864 * The actual recreation does not happen here, but only after we have
14865 * performed all the individual ALTER TYPE operations. We have to save
14866 * the info before executing ALTER TYPE, though, else the deparser will
14867 * get confused.
14868 */
14870
14871 /*
14872 * Now scan for dependencies of this column on other things. The only
14873 * things we should find are the dependency on the column datatype and
14874 * possibly a collation dependency. Those can be removed.
14875 */
14876 depRel = table_open(DependRelationId, RowExclusiveLock);
14877
14878 ScanKeyInit(&key[0],
14879 Anum_pg_depend_classid,
14880 BTEqualStrategyNumber, F_OIDEQ,
14881 ObjectIdGetDatum(RelationRelationId));
14882 ScanKeyInit(&key[1],
14883 Anum_pg_depend_objid,
14884 BTEqualStrategyNumber, F_OIDEQ,
14886 ScanKeyInit(&key[2],
14887 Anum_pg_depend_objsubid,
14888 BTEqualStrategyNumber, F_INT4EQ,
14890
14891 scan = systable_beginscan(depRel, DependDependerIndexId, true,
14892 NULL, 3, key);
14893
14894 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14895 {
14896 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14897 ObjectAddress foundObject;
14898
14899 foundObject.classId = foundDep->refclassid;
14900 foundObject.objectId = foundDep->refobjid;
14901 foundObject.objectSubId = foundDep->refobjsubid;
14902
14903 if (foundDep->deptype != DEPENDENCY_NORMAL)
14904 elog(ERROR, "found unexpected dependency type '%c'",
14905 foundDep->deptype);
14906 if (!(foundDep->refclassid == TypeRelationId &&
14907 foundDep->refobjid == attTup->atttypid) &&
14908 !(foundDep->refclassid == CollationRelationId &&
14909 foundDep->refobjid == attTup->attcollation))
14910 elog(ERROR, "found unexpected dependency for column: %s",
14911 getObjectDescription(&foundObject, false));
14912
14913 CatalogTupleDelete(depRel, &depTup->t_self);
14914 }
14915
14916 systable_endscan(scan);
14917
14919
14920 /*
14921 * Here we go --- change the recorded column type and collation. (Note
14922 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14923 * fix up the missing value if any.
14924 */
14925 if (attTup->atthasmissing)
14926 {
14927 Datum missingval;
14928 bool missingNull;
14929
14930 /* if rewrite is true the missing value should already be cleared */
14931 Assert(tab->rewrite == 0);
14932
14933 /* Get the missing value datum */
14934 missingval = heap_getattr(heapTup,
14935 Anum_pg_attribute_attmissingval,
14936 attrelation->rd_att,
14937 &missingNull);
14938
14939 /* if it's a null array there is nothing to do */
14940
14941 if (!missingNull)
14942 {
14943 /*
14944 * Get the datum out of the array and repack it in a new array
14945 * built with the new type data. We assume that since the table
14946 * doesn't need rewriting, the actual Datum doesn't need to be
14947 * changed, only the array metadata.
14948 */
14949
14950 int one = 1;
14951 bool isNull;
14952 Datum valuesAtt[Natts_pg_attribute] = {0};
14953 bool nullsAtt[Natts_pg_attribute] = {0};
14954 bool replacesAtt[Natts_pg_attribute] = {0};
14955 HeapTuple newTup;
14956
14957 missingval = array_get_element(missingval,
14958 1,
14959 &one,
14960 0,
14961 attTup->attlen,
14962 attTup->attbyval,
14963 attTup->attalign,
14964 &isNull);
14965 missingval = PointerGetDatum(construct_array(&missingval,
14966 1,
14967 targettype,
14968 tform->typlen,
14969 tform->typbyval,
14970 tform->typalign));
14971
14972 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14973 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14974 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14975
14976 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14977 valuesAtt, nullsAtt, replacesAtt);
14978 heap_freetuple(heapTup);
14979 heapTup = newTup;
14980 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14981 }
14982 }
14983
14984 attTup->atttypid = targettype;
14985 attTup->atttypmod = targettypmod;
14986 attTup->attcollation = targetcollid;
14987 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14988 ereport(ERROR,
14989 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14990 errmsg("too many array dimensions"));
14991 attTup->attndims = list_length(typeName->arrayBounds);
14992 attTup->attlen = tform->typlen;
14993 attTup->attbyval = tform->typbyval;
14994 attTup->attalign = tform->typalign;
14995 attTup->attstorage = tform->typstorage;
14996 attTup->attcompression = InvalidCompressionMethod;
14997
14998 ReleaseSysCache(typeTuple);
14999
15000 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
15001
15002 table_close(attrelation, RowExclusiveLock);
15003
15004 /* Install dependencies on new datatype and collation */
15007
15008 /*
15009 * Drop any pg_statistic entry for the column, since it's now wrong type
15010 */
15012
15013 InvokeObjectPostAlterHook(RelationRelationId,
15014 RelationGetRelid(rel), attnum);
15015
15016 /*
15017 * Update the default, if present, by brute force --- remove and re-add
15018 * the default. Probably unsafe to take shortcuts, since the new version
15019 * may well have additional dependencies. (It's okay to do this now,
15020 * rather than after other ALTER TYPE commands, since the default won't
15021 * depend on other column types.)
15022 */
15023 if (defaultexpr)
15024 {
15025 /*
15026 * If it's a GENERATED default, drop its dependency records, in
15027 * particular its INTERNAL dependency on the column, which would
15028 * otherwise cause dependency.c to refuse to perform the deletion.
15029 */
15030 if (attTup->attgenerated)
15031 {
15032 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
15033
15034 if (!OidIsValid(attrdefoid))
15035 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15036 RelationGetRelid(rel), attnum);
15037 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
15038 }
15039
15040 /*
15041 * Make updates-so-far visible, particularly the new pg_attribute row
15042 * which will be updated again.
15043 */
15045
15046 /*
15047 * We use RESTRICT here for safety, but at present we do not expect
15048 * anything to depend on the default.
15049 */
15051 true);
15052
15053 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15054 }
15055
15056 ObjectAddressSubSet(address, RelationRelationId,
15057 RelationGetRelid(rel), attnum);
15058
15059 /* Cleanup */
15060 heap_freetuple(heapTup);
15061
15062 return address;
15063}
15064
15065/*
15066 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
15067 * that depends on the column (constraints, indexes, etc), and record enough
15068 * information to let us recreate the objects.
15069 */
15070static void
15072 Relation rel, AttrNumber attnum, const char *colName)
15073{
15074 Relation depRel;
15075 ScanKeyData key[3];
15076 SysScanDesc scan;
15077 HeapTuple depTup;
15078
15079 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15080
15081 depRel = table_open(DependRelationId, RowExclusiveLock);
15082
15083 ScanKeyInit(&key[0],
15084 Anum_pg_depend_refclassid,
15085 BTEqualStrategyNumber, F_OIDEQ,
15086 ObjectIdGetDatum(RelationRelationId));
15087 ScanKeyInit(&key[1],
15088 Anum_pg_depend_refobjid,
15089 BTEqualStrategyNumber, F_OIDEQ,
15091 ScanKeyInit(&key[2],
15092 Anum_pg_depend_refobjsubid,
15093 BTEqualStrategyNumber, F_INT4EQ,
15095
15096 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15097 NULL, 3, key);
15098
15099 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15100 {
15101 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15102 ObjectAddress foundObject;
15103
15104 foundObject.classId = foundDep->classid;
15105 foundObject.objectId = foundDep->objid;
15106 foundObject.objectSubId = foundDep->objsubid;
15107
15108 switch (foundObject.classId)
15109 {
15110 case RelationRelationId:
15111 {
15112 char relKind = get_rel_relkind(foundObject.objectId);
15113
15114 if (relKind == RELKIND_INDEX ||
15115 relKind == RELKIND_PARTITIONED_INDEX)
15116 {
15117 Assert(foundObject.objectSubId == 0);
15118 RememberIndexForRebuilding(foundObject.objectId, tab);
15119 }
15120 else if (relKind == RELKIND_SEQUENCE)
15121 {
15122 /*
15123 * This must be a SERIAL column's sequence. We need
15124 * not do anything to it.
15125 */
15126 Assert(foundObject.objectSubId == 0);
15127 }
15128 else
15129 {
15130 /* Not expecting any other direct dependencies... */
15131 elog(ERROR, "unexpected object depending on column: %s",
15132 getObjectDescription(&foundObject, false));
15133 }
15134 break;
15135 }
15136
15137 case ConstraintRelationId:
15138 Assert(foundObject.objectSubId == 0);
15139 RememberConstraintForRebuilding(foundObject.objectId, tab);
15140 break;
15141
15142 case ProcedureRelationId:
15143
15144 /*
15145 * A new-style SQL function can depend on a column, if that
15146 * column is referenced in the parsed function body. Ideally
15147 * we'd automatically update the function by deparsing and
15148 * reparsing it, but that's risky and might well fail anyhow.
15149 * FIXME someday.
15150 *
15151 * This is only a problem for AT_AlterColumnType, not
15152 * AT_SetExpression.
15153 */
15154 if (subtype == AT_AlterColumnType)
15155 ereport(ERROR,
15156 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15157 errmsg("cannot alter type of a column used by a function or procedure"),
15158 errdetail("%s depends on column \"%s\"",
15159 getObjectDescription(&foundObject, false),
15160 colName)));
15161 break;
15162
15163 case RewriteRelationId:
15164
15165 /*
15166 * View/rule bodies have pretty much the same issues as
15167 * function bodies. FIXME someday.
15168 */
15169 if (subtype == AT_AlterColumnType)
15170 ereport(ERROR,
15171 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15172 errmsg("cannot alter type of a column used by a view or rule"),
15173 errdetail("%s depends on column \"%s\"",
15174 getObjectDescription(&foundObject, false),
15175 colName)));
15176 break;
15177
15178 case TriggerRelationId:
15179
15180 /*
15181 * A trigger can depend on a column because the column is
15182 * specified as an update target, or because the column is
15183 * used in the trigger's WHEN condition. The first case would
15184 * not require any extra work, but the second case would
15185 * require updating the WHEN expression, which has the same
15186 * issues as above. Since we can't easily tell which case
15187 * applies, we punt for both. FIXME someday.
15188 */
15189 if (subtype == AT_AlterColumnType)
15190 ereport(ERROR,
15191 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15192 errmsg("cannot alter type of a column used in a trigger definition"),
15193 errdetail("%s depends on column \"%s\"",
15194 getObjectDescription(&foundObject, false),
15195 colName)));
15196 break;
15197
15198 case PolicyRelationId:
15199
15200 /*
15201 * A policy can depend on a column because the column is
15202 * specified in the policy's USING or WITH CHECK qual
15203 * expressions. It might be possible to rewrite and recheck
15204 * the policy expression, but punt for now. It's certainly
15205 * easy enough to remove and recreate the policy; still, FIXME
15206 * someday.
15207 */
15208 if (subtype == AT_AlterColumnType)
15209 ereport(ERROR,
15210 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15211 errmsg("cannot alter type of a column used in a policy definition"),
15212 errdetail("%s depends on column \"%s\"",
15213 getObjectDescription(&foundObject, false),
15214 colName)));
15215 break;
15216
15217 case AttrDefaultRelationId:
15218 {
15220
15221 if (col.objectId == RelationGetRelid(rel) &&
15222 col.objectSubId == attnum)
15223 {
15224 /*
15225 * Ignore the column's own default expression. The
15226 * caller deals with it.
15227 */
15228 }
15229 else
15230 {
15231 /*
15232 * This must be a reference from the expression of a
15233 * generated column elsewhere in the same table.
15234 * Changing the type/generated expression of a column
15235 * that is used by a generated column is not allowed
15236 * by SQL standard, so just punt for now. It might be
15237 * doable with some thinking and effort.
15238 */
15239 if (subtype == AT_AlterColumnType)
15240 ereport(ERROR,
15241 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15242 errmsg("cannot alter type of a column used by a generated column"),
15243 errdetail("Column \"%s\" is used by generated column \"%s\".",
15244 colName,
15246 col.objectSubId,
15247 false))));
15248 }
15249 break;
15250 }
15251
15252 case StatisticExtRelationId:
15253
15254 /*
15255 * Give the extended-stats machinery a chance to fix anything
15256 * that this column type change would break.
15257 */
15258 RememberStatisticsForRebuilding(foundObject.objectId, tab);
15259 break;
15260
15261 case PublicationRelRelationId:
15262
15263 /*
15264 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15265 * clause. Same issues as above. FIXME someday.
15266 */
15267 if (subtype == AT_AlterColumnType)
15268 ereport(ERROR,
15269 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15270 errmsg("cannot alter type of a column used by a publication WHERE clause"),
15271 errdetail("%s depends on column \"%s\"",
15272 getObjectDescription(&foundObject, false),
15273 colName)));
15274 break;
15275
15276 default:
15277
15278 /*
15279 * We don't expect any other sorts of objects to depend on a
15280 * column.
15281 */
15282 elog(ERROR, "unexpected object depending on column: %s",
15283 getObjectDescription(&foundObject, false));
15284 break;
15285 }
15286 }
15287
15288 systable_endscan(scan);
15289 table_close(depRel, NoLock);
15290}
15291
15292/*
15293 * Subroutine for ATExecAlterColumnType: remember that a replica identity
15294 * needs to be reset.
15295 */
15296static void
15298{
15299 if (!get_index_isreplident(indoid))
15300 return;
15301
15302 if (tab->replicaIdentityIndex)
15303 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15304
15305 tab->replicaIdentityIndex = get_rel_name(indoid);
15306}
15307
15308/*
15309 * Subroutine for ATExecAlterColumnType: remember any clustered index.
15310 */
15311static void
15313{
15314 if (!get_index_isclustered(indoid))
15315 return;
15316
15317 if (tab->clusterOnIndex)
15318 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15319
15320 tab->clusterOnIndex = get_rel_name(indoid);
15321}
15322
15323/*
15324 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15325 * to be rebuilt (which we might already know).
15326 */
15327static void
15329{
15330 /*
15331 * This de-duplication check is critical for two independent reasons: we
15332 * mustn't try to recreate the same constraint twice, and if a constraint
15333 * depends on more than one column whose type is to be altered, we must
15334 * capture its definition string before applying any of the column type
15335 * changes. ruleutils.c will get confused if we ask again later.
15336 */
15337 if (!list_member_oid(tab->changedConstraintOids, conoid))
15338 {
15339 /* OK, capture the constraint's existing definition string */
15340 char *defstring = pg_get_constraintdef_command(conoid);
15341 Oid indoid;
15342
15343 /*
15344 * It is critical to create not-null constraints ahead of primary key
15345 * indexes; otherwise, the not-null constraint would be created by the
15346 * primary key, and the constraint name would be wrong.
15347 */
15348 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15349 {
15350 tab->changedConstraintOids = lcons_oid(conoid,
15352 tab->changedConstraintDefs = lcons(defstring,
15354 }
15355 else
15356 {
15357
15359 conoid);
15361 defstring);
15362 }
15363
15364 /*
15365 * For the index of a constraint, if any, remember if it is used for
15366 * the table's replica identity or if it is a clustered index, so that
15367 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15368 * those properties.
15369 */
15370 indoid = get_constraint_index(conoid);
15371 if (OidIsValid(indoid))
15372 {
15374 RememberClusterOnForRebuilding(indoid, tab);
15375 }
15376 }
15377}
15378
15379/*
15380 * Subroutine for ATExecAlterColumnType: remember that an index needs
15381 * to be rebuilt (which we might already know).
15382 */
15383static void
15385{
15386 /*
15387 * This de-duplication check is critical for two independent reasons: we
15388 * mustn't try to recreate the same index twice, and if an index depends
15389 * on more than one column whose type is to be altered, we must capture
15390 * its definition string before applying any of the column type changes.
15391 * ruleutils.c will get confused if we ask again later.
15392 */
15393 if (!list_member_oid(tab->changedIndexOids, indoid))
15394 {
15395 /*
15396 * Before adding it as an index-to-rebuild, we'd better see if it
15397 * belongs to a constraint, and if so rebuild the constraint instead.
15398 * Typically this check fails, because constraint indexes normally
15399 * have only dependencies on their constraint. But it's possible for
15400 * such an index to also have direct dependencies on table columns,
15401 * for example with a partial exclusion constraint.
15402 */
15403 Oid conoid = get_index_constraint(indoid);
15404
15405 if (OidIsValid(conoid))
15406 {
15408 }
15409 else
15410 {
15411 /* OK, capture the index's existing definition string */
15412 char *defstring = pg_get_indexdef_string(indoid);
15413
15415 indoid);
15417 defstring);
15418
15419 /*
15420 * Remember if this index is used for the table's replica identity
15421 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15422 * can queue up commands necessary to restore those properties.
15423 */
15425 RememberClusterOnForRebuilding(indoid, tab);
15426 }
15427 }
15428}
15429
15430/*
15431 * Subroutine for ATExecAlterColumnType: remember that a statistics object
15432 * needs to be rebuilt (which we might already know).
15433 */
15434static void
15436{
15437 /*
15438 * This de-duplication check is critical for two independent reasons: we
15439 * mustn't try to recreate the same statistics object twice, and if the
15440 * statistics object depends on more than one column whose type is to be
15441 * altered, we must capture its definition string before applying any of
15442 * the type changes. ruleutils.c will get confused if we ask again later.
15443 */
15444 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15445 {
15446 /* OK, capture the statistics object's existing definition string */
15447 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15448
15450 stxoid);
15452 defstring);
15453 }
15454}
15455
15456/*
15457 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15458 * operations for a particular relation. We have to drop and recreate all the
15459 * indexes and constraints that depend on the altered columns. We do the
15460 * actual dropping here, but re-creation is managed by adding work queue
15461 * entries to do those steps later.
15462 */
15463static void
15465{
15466 ObjectAddress obj;
15467 ObjectAddresses *objects;
15468 ListCell *def_item;
15469 ListCell *oid_item;
15470
15471 /*
15472 * Collect all the constraints and indexes to drop so we can process them
15473 * in a single call. That way we don't have to worry about dependencies
15474 * among them.
15475 */
15476 objects = new_object_addresses();
15477
15478 /*
15479 * Re-parse the index and constraint definitions, and attach them to the
15480 * appropriate work queue entries. We do this before dropping because in
15481 * the case of a constraint on another table, we might not yet have
15482 * exclusive lock on the table the constraint is attached to, and we need
15483 * to get that before reparsing/dropping. (That's possible at least for
15484 * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15485 * requires a dependency on the target table's composite type in the other
15486 * table's constraint expressions.)
15487 *
15488 * We can't rely on the output of deparsing to tell us which relation to
15489 * operate on, because concurrent activity might have made the name
15490 * resolve differently. Instead, we've got to use the OID of the
15491 * constraint or index we're processing to figure out which relation to
15492 * operate on.
15493 */
15494 forboth(oid_item, tab->changedConstraintOids,
15495 def_item, tab->changedConstraintDefs)
15496 {
15497 Oid oldId = lfirst_oid(oid_item);
15498 HeapTuple tup;
15500 Oid relid;
15501 Oid confrelid;
15502 bool conislocal;
15503
15504 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15505 if (!HeapTupleIsValid(tup)) /* should not happen */
15506 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15507 con = (Form_pg_constraint) GETSTRUCT(tup);
15508 if (OidIsValid(con->conrelid))
15509 relid = con->conrelid;
15510 else
15511 {
15512 /* must be a domain constraint */
15513 relid = get_typ_typrelid(getBaseType(con->contypid));
15514 if (!OidIsValid(relid))
15515 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15516 }
15517 confrelid = con->confrelid;
15518 conislocal = con->conislocal;
15519 ReleaseSysCache(tup);
15520
15521 ObjectAddressSet(obj, ConstraintRelationId, oldId);
15522 add_exact_object_address(&obj, objects);
15523
15524 /*
15525 * If the constraint is inherited (only), we don't want to inject a
15526 * new definition here; it'll get recreated when
15527 * ATAddCheckNNConstraint recurses from adding the parent table's
15528 * constraint. But we had to carry the info this far so that we can
15529 * drop the constraint below.
15530 */
15531 if (!conislocal)
15532 continue;
15533
15534 /*
15535 * When rebuilding another table's constraint that references the
15536 * table we're modifying, we might not yet have any lock on the other
15537 * table, so get one now. We'll need AccessExclusiveLock for the DROP
15538 * CONSTRAINT step, so there's no value in asking for anything weaker.
15539 */
15540 if (relid != tab->relid)
15542
15543 ATPostAlterTypeParse(oldId, relid, confrelid,
15544 (char *) lfirst(def_item),
15545 wqueue, lockmode, tab->rewrite);
15546 }
15547 forboth(oid_item, tab->changedIndexOids,
15548 def_item, tab->changedIndexDefs)
15549 {
15550 Oid oldId = lfirst_oid(oid_item);
15551 Oid relid;
15552
15553 relid = IndexGetRelation(oldId, false);
15554
15555 /*
15556 * As above, make sure we have lock on the index's table if it's not
15557 * the same table.
15558 */
15559 if (relid != tab->relid)
15561
15562 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15563 (char *) lfirst(def_item),
15564 wqueue, lockmode, tab->rewrite);
15565
15566 ObjectAddressSet(obj, RelationRelationId, oldId);
15567 add_exact_object_address(&obj, objects);
15568 }
15569
15570 /* add dependencies for new statistics */
15571 forboth(oid_item, tab->changedStatisticsOids,
15572 def_item, tab->changedStatisticsDefs)
15573 {
15574 Oid oldId = lfirst_oid(oid_item);
15575 Oid relid;
15576
15577 relid = StatisticsGetRelation(oldId, false);
15578
15579 /*
15580 * As above, make sure we have lock on the statistics object's table
15581 * if it's not the same table. However, we take
15582 * ShareUpdateExclusiveLock here, aligning with the lock level used in
15583 * CreateStatistics and RemoveStatisticsById.
15584 *
15585 * CAUTION: this should be done after all cases that grab
15586 * AccessExclusiveLock, else we risk causing deadlock due to needing
15587 * to promote our table lock.
15588 */
15589 if (relid != tab->relid)
15591
15592 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15593 (char *) lfirst(def_item),
15594 wqueue, lockmode, tab->rewrite);
15595
15596 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15597 add_exact_object_address(&obj, objects);
15598 }
15599
15600 /*
15601 * Queue up command to restore replica identity index marking
15602 */
15603 if (tab->replicaIdentityIndex)
15604 {
15607
15608 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15609 subcmd->name = tab->replicaIdentityIndex;
15611 cmd->def = (Node *) subcmd;
15612
15613 /* do it after indexes and constraints */
15614 tab->subcmds[AT_PASS_OLD_CONSTR] =
15615 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15616 }
15617
15618 /*
15619 * Queue up command to restore marking of index used for cluster.
15620 */
15621 if (tab->clusterOnIndex)
15622 {
15624
15625 cmd->subtype = AT_ClusterOn;
15626 cmd->name = tab->clusterOnIndex;
15627
15628 /* do it after indexes and constraints */
15629 tab->subcmds[AT_PASS_OLD_CONSTR] =
15630 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15631 }
15632
15633 /*
15634 * It should be okay to use DROP_RESTRICT here, since nothing else should
15635 * be depending on these objects.
15636 */
15638
15639 free_object_addresses(objects);
15640
15641 /*
15642 * The objects will get recreated during subsequent passes over the work
15643 * queue.
15644 */
15645}
15646
15647/*
15648 * Parse the previously-saved definition string for a constraint, index or
15649 * statistics object against the newly-established column data type(s), and
15650 * queue up the resulting command parsetrees for execution.
15651 *
15652 * This might fail if, for example, you have a WHERE clause that uses an
15653 * operator that's not available for the new column type.
15654 */
15655static void
15656ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15657 List **wqueue, LOCKMODE lockmode, bool rewrite)
15658{
15659 List *raw_parsetree_list;
15660 List *querytree_list;
15661 ListCell *list_item;
15662 Relation rel;
15663
15664 /*
15665 * We expect that we will get only ALTER TABLE and CREATE INDEX
15666 * statements. Hence, there is no need to pass them through
15667 * parse_analyze_*() or the rewriter, but instead we need to pass them
15668 * through parse_utilcmd.c to make them ready for execution.
15669 */
15670 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15671 querytree_list = NIL;
15672 foreach(list_item, raw_parsetree_list)
15673 {
15674 RawStmt *rs = lfirst_node(RawStmt, list_item);
15675 Node *stmt = rs->stmt;
15676
15677 if (IsA(stmt, IndexStmt))
15678 querytree_list = lappend(querytree_list,
15679 transformIndexStmt(oldRelId,
15680 (IndexStmt *) stmt,
15681 cmd));
15682 else if (IsA(stmt, AlterTableStmt))
15683 {
15684 List *beforeStmts;
15685 List *afterStmts;
15686
15687 stmt = (Node *) transformAlterTableStmt(oldRelId,
15688 (AlterTableStmt *) stmt,
15689 cmd,
15690 &beforeStmts,
15691 &afterStmts);
15692 querytree_list = list_concat(querytree_list, beforeStmts);
15693 querytree_list = lappend(querytree_list, stmt);
15694 querytree_list = list_concat(querytree_list, afterStmts);
15695 }
15696 else if (IsA(stmt, CreateStatsStmt))
15697 querytree_list = lappend(querytree_list,
15698 transformStatsStmt(oldRelId,
15700 cmd));
15701 else
15702 querytree_list = lappend(querytree_list, stmt);
15703 }
15704
15705 /* Caller should already have acquired whatever lock we need. */
15706 rel = relation_open(oldRelId, NoLock);
15707
15708 /*
15709 * Attach each generated command to the proper place in the work queue.
15710 * Note this could result in creation of entirely new work-queue entries.
15711 *
15712 * Also note that we have to tweak the command subtypes, because it turns
15713 * out that re-creation of indexes and constraints has to act a bit
15714 * differently from initial creation.
15715 */
15716 foreach(list_item, querytree_list)
15717 {
15718 Node *stm = (Node *) lfirst(list_item);
15719 AlteredTableInfo *tab;
15720
15721 tab = ATGetQueueEntry(wqueue, rel);
15722
15723 if (IsA(stm, IndexStmt))
15724 {
15725 IndexStmt *stmt = (IndexStmt *) stm;
15726 AlterTableCmd *newcmd;
15727
15728 if (!rewrite)
15729 TryReuseIndex(oldId, stmt);
15730 stmt->reset_default_tblspc = true;
15731 /* keep the index's comment */
15732 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15733
15734 newcmd = makeNode(AlterTableCmd);
15735 newcmd->subtype = AT_ReAddIndex;
15736 newcmd->def = (Node *) stmt;
15738 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15739 }
15740 else if (IsA(stm, AlterTableStmt))
15741 {
15743 ListCell *lcmd;
15744
15745 foreach(lcmd, stmt->cmds)
15746 {
15748
15749 if (cmd->subtype == AT_AddIndex)
15750 {
15751 IndexStmt *indstmt;
15752 Oid indoid;
15753
15754 indstmt = castNode(IndexStmt, cmd->def);
15755 indoid = get_constraint_index(oldId);
15756
15757 if (!rewrite)
15758 TryReuseIndex(indoid, indstmt);
15759 /* keep any comment on the index */
15760 indstmt->idxcomment = GetComment(indoid,
15761 RelationRelationId, 0);
15762 indstmt->reset_default_tblspc = true;
15763
15764 cmd->subtype = AT_ReAddIndex;
15766 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15767
15768 /* recreate any comment on the constraint */
15771 oldId,
15772 rel,
15773 NIL,
15774 indstmt->idxname);
15775 }
15776 else if (cmd->subtype == AT_AddConstraint)
15777 {
15778 Constraint *con = castNode(Constraint, cmd->def);
15779
15780 con->old_pktable_oid = refRelId;
15781 /* rewriting neither side of a FK */
15782 if (con->contype == CONSTR_FOREIGN &&
15783 !rewrite && tab->rewrite == 0)
15784 TryReuseForeignKey(oldId, con);
15785 con->reset_default_tblspc = true;
15789
15790 /*
15791 * Recreate any comment on the constraint. If we have
15792 * recreated a primary key, then transformTableConstraint
15793 * has added an unnamed not-null constraint here; skip
15794 * this in that case.
15795 */
15796 if (con->conname)
15799 oldId,
15800 rel,
15801 NIL,
15802 con->conname);
15803 else
15804 Assert(con->contype == CONSTR_NOTNULL);
15805 }
15806 else
15807 elog(ERROR, "unexpected statement subtype: %d",
15808 (int) cmd->subtype);
15809 }
15810 }
15811 else if (IsA(stm, AlterDomainStmt))
15812 {
15814
15815 if (stmt->subtype == AD_AddConstraint)
15816 {
15817 Constraint *con = castNode(Constraint, stmt->def);
15819
15821 cmd->def = (Node *) stmt;
15824
15825 /* recreate any comment on the constraint */
15828 oldId,
15829 NULL,
15830 stmt->typeName,
15831 con->conname);
15832 }
15833 else
15834 elog(ERROR, "unexpected statement subtype: %d",
15835 (int) stmt->subtype);
15836 }
15837 else if (IsA(stm, CreateStatsStmt))
15838 {
15840 AlterTableCmd *newcmd;
15841
15842 /* keep the statistics object's comment */
15843 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15844
15845 newcmd = makeNode(AlterTableCmd);
15846 newcmd->subtype = AT_ReAddStatistics;
15847 newcmd->def = (Node *) stmt;
15848 tab->subcmds[AT_PASS_MISC] =
15849 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15850 }
15851 else
15852 elog(ERROR, "unexpected statement type: %d",
15853 (int) nodeTag(stm));
15854 }
15855
15856 relation_close(rel, NoLock);
15857}
15858
15859/*
15860 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15861 * for a table or domain constraint that is being rebuilt.
15862 *
15863 * objid is the OID of the constraint.
15864 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15865 * as a string list) for a domain constraint.
15866 * (We could dig that info, as well as the conname, out of the pg_constraint
15867 * entry; but callers already have them so might as well pass them.)
15868 */
15869static void
15871 Relation rel, List *domname,
15872 const char *conname)
15873{
15874 CommentStmt *cmd;
15875 char *comment_str;
15876 AlterTableCmd *newcmd;
15877
15878 /* Look for comment for object wanted, and leave if none */
15879 comment_str = GetComment(objid, ConstraintRelationId, 0);
15880 if (comment_str == NULL)
15881 return;
15882
15883 /* Build CommentStmt node, copying all input data for safety */
15884 cmd = makeNode(CommentStmt);
15885 if (rel)
15886 {
15888 cmd->object = (Node *)
15891 makeString(pstrdup(conname)));
15892 }
15893 else
15894 {
15896 cmd->object = (Node *)
15898 makeString(pstrdup(conname)));
15899 }
15900 cmd->comment = comment_str;
15901
15902 /* Append it to list of commands */
15903 newcmd = makeNode(AlterTableCmd);
15904 newcmd->subtype = AT_ReAddComment;
15905 newcmd->def = (Node *) cmd;
15906 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15907}
15908
15909/*
15910 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15911 * for the real analysis, then mutates the IndexStmt based on that verdict.
15912 */
15913static void
15915{
15916 if (CheckIndexCompatible(oldId,
15917 stmt->accessMethod,
15918 stmt->indexParams,
15919 stmt->excludeOpNames,
15920 stmt->iswithoutoverlaps))
15921 {
15922 Relation irel = index_open(oldId, NoLock);
15923
15924 /* If it's a partitioned index, there is no storage to share. */
15925 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15926 {
15927 stmt->oldNumber = irel->rd_locator.relNumber;
15928 stmt->oldCreateSubid = irel->rd_createSubid;
15929 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15930 }
15931 index_close(irel, NoLock);
15932 }
15933}
15934
15935/*
15936 * Subroutine for ATPostAlterTypeParse().
15937 *
15938 * Stash the old P-F equality operator into the Constraint node, for possible
15939 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15940 * this constraint can be skipped.
15941 */
15942static void
15944{
15945 HeapTuple tup;
15946 Datum adatum;
15947 ArrayType *arr;
15948 Oid *rawarr;
15949 int numkeys;
15950 int i;
15951
15952 Assert(con->contype == CONSTR_FOREIGN);
15953 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15954
15955 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15956 if (!HeapTupleIsValid(tup)) /* should not happen */
15957 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15958
15959 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15960 Anum_pg_constraint_conpfeqop);
15961 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15962 numkeys = ARR_DIMS(arr)[0];
15963 /* test follows the one in ri_FetchConstraintInfo() */
15964 if (ARR_NDIM(arr) != 1 ||
15965 ARR_HASNULL(arr) ||
15966 ARR_ELEMTYPE(arr) != OIDOID)
15967 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15968 rawarr = (Oid *) ARR_DATA_PTR(arr);
15969
15970 /* stash a List of the operator Oids in our Constraint node */
15971 for (i = 0; i < numkeys; i++)
15972 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15973
15974 ReleaseSysCache(tup);
15975}
15976
15977/*
15978 * ALTER COLUMN .. OPTIONS ( ... )
15979 *
15980 * Returns the address of the modified column
15981 */
15982static ObjectAddress
15984 const char *colName,
15985 List *options,
15986 LOCKMODE lockmode)
15987{
15988 Relation ftrel;
15989 Relation attrel;
15990 ForeignServer *server;
15991 ForeignDataWrapper *fdw;
15992 HeapTuple tuple;
15993 HeapTuple newtuple;
15994 bool isnull;
15995 Datum repl_val[Natts_pg_attribute];
15996 bool repl_null[Natts_pg_attribute];
15997 bool repl_repl[Natts_pg_attribute];
15998 Datum datum;
15999 Form_pg_foreign_table fttableform;
16000 Form_pg_attribute atttableform;
16002 ObjectAddress address;
16003
16004 if (options == NIL)
16005 return InvalidObjectAddress;
16006
16007 /* First, determine FDW validator associated to the foreign table. */
16008 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
16009 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
16010 if (!HeapTupleIsValid(tuple))
16011 ereport(ERROR,
16012 (errcode(ERRCODE_UNDEFINED_OBJECT),
16013 errmsg("foreign table \"%s\" does not exist",
16015 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16016 server = GetForeignServer(fttableform->ftserver);
16017 fdw = GetForeignDataWrapper(server->fdwid);
16018
16020 ReleaseSysCache(tuple);
16021
16022 attrel = table_open(AttributeRelationId, RowExclusiveLock);
16023 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
16024 if (!HeapTupleIsValid(tuple))
16025 ereport(ERROR,
16026 (errcode(ERRCODE_UNDEFINED_COLUMN),
16027 errmsg("column \"%s\" of relation \"%s\" does not exist",
16028 colName, RelationGetRelationName(rel))));
16029
16030 /* Prevent them from altering a system attribute */
16031 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16032 attnum = atttableform->attnum;
16033 if (attnum <= 0)
16034 ereport(ERROR,
16035 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16036 errmsg("cannot alter system column \"%s\"", colName)));
16037
16038
16039 /* Initialize buffers for new tuple values */
16040 memset(repl_val, 0, sizeof(repl_val));
16041 memset(repl_null, false, sizeof(repl_null));
16042 memset(repl_repl, false, sizeof(repl_repl));
16043
16044 /* Extract the current options */
16045 datum = SysCacheGetAttr(ATTNAME,
16046 tuple,
16047 Anum_pg_attribute_attfdwoptions,
16048 &isnull);
16049 if (isnull)
16050 datum = PointerGetDatum(NULL);
16051
16052 /* Transform the options */
16053 datum = transformGenericOptions(AttributeRelationId,
16054 datum,
16055 options,
16056 fdw->fdwvalidator);
16057
16058 if (DatumGetPointer(datum) != NULL)
16059 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16060 else
16061 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16062
16063 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16064
16065 /* Everything looks good - update the tuple */
16066
16067 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16068 repl_val, repl_null, repl_repl);
16069
16070 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16071
16072 InvokeObjectPostAlterHook(RelationRelationId,
16073 RelationGetRelid(rel),
16074 atttableform->attnum);
16075 ObjectAddressSubSet(address, RelationRelationId,
16076 RelationGetRelid(rel), attnum);
16077
16078 ReleaseSysCache(tuple);
16079
16081
16082 heap_freetuple(newtuple);
16083
16084 return address;
16085}
16086
16087/*
16088 * ALTER TABLE OWNER
16089 *
16090 * recursing is true if we are recursing from a table to its indexes,
16091 * sequences, or toast table. We don't allow the ownership of those things to
16092 * be changed separately from the parent table. Also, we can skip permission
16093 * checks (this is necessary not just an optimization, else we'd fail to
16094 * handle toast tables properly).
16095 *
16096 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
16097 * free-standing composite type.
16098 */
16099void
16100ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
16101{
16102 Relation target_rel;
16103 Relation class_rel;
16104 HeapTuple tuple;
16105 Form_pg_class tuple_class;
16106
16107 /*
16108 * Get exclusive lock till end of transaction on the target table. Use
16109 * relation_open so that we can work on indexes and sequences.
16110 */
16111 target_rel = relation_open(relationOid, lockmode);
16112
16113 /* Get its pg_class tuple, too */
16114 class_rel = table_open(RelationRelationId, RowExclusiveLock);
16115
16116 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16117 if (!HeapTupleIsValid(tuple))
16118 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16119 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16120
16121 /* Can we change the ownership of this tuple? */
16122 switch (tuple_class->relkind)
16123 {
16124 case RELKIND_RELATION:
16125 case RELKIND_VIEW:
16126 case RELKIND_MATVIEW:
16127 case RELKIND_FOREIGN_TABLE:
16128 case RELKIND_PARTITIONED_TABLE:
16129 /* ok to change owner */
16130 break;
16131 case RELKIND_INDEX:
16132 if (!recursing)
16133 {
16134 /*
16135 * Because ALTER INDEX OWNER used to be allowed, and in fact
16136 * is generated by old versions of pg_dump, we give a warning
16137 * and do nothing rather than erroring out. Also, to avoid
16138 * unnecessary chatter while restoring those old dumps, say
16139 * nothing at all if the command would be a no-op anyway.
16140 */
16141 if (tuple_class->relowner != newOwnerId)
16143 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16144 errmsg("cannot change owner of index \"%s\"",
16145 NameStr(tuple_class->relname)),
16146 errhint("Change the ownership of the index's table instead.")));
16147 /* quick hack to exit via the no-op path */
16148 newOwnerId = tuple_class->relowner;
16149 }
16150 break;
16151 case RELKIND_PARTITIONED_INDEX:
16152 if (recursing)
16153 break;
16154 ereport(ERROR,
16155 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16156 errmsg("cannot change owner of index \"%s\"",
16157 NameStr(tuple_class->relname)),
16158 errhint("Change the ownership of the index's table instead.")));
16159 break;
16160 case RELKIND_SEQUENCE:
16161 if (!recursing &&
16162 tuple_class->relowner != newOwnerId)
16163 {
16164 /* if it's an owned sequence, disallow changing it by itself */
16165 Oid tableId;
16166 int32 colId;
16167
16168 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16169 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16170 ereport(ERROR,
16171 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16172 errmsg("cannot change owner of sequence \"%s\"",
16173 NameStr(tuple_class->relname)),
16174 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16175 NameStr(tuple_class->relname),
16176 get_rel_name(tableId))));
16177 }
16178 break;
16179 case RELKIND_COMPOSITE_TYPE:
16180 if (recursing)
16181 break;
16182 ereport(ERROR,
16183 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16184 errmsg("\"%s\" is a composite type",
16185 NameStr(tuple_class->relname)),
16186 /* translator: %s is an SQL ALTER command */
16187 errhint("Use %s instead.",
16188 "ALTER TYPE")));
16189 break;
16190 case RELKIND_TOASTVALUE:
16191 if (recursing)
16192 break;
16193 /* FALL THRU */
16194 default:
16195 ereport(ERROR,
16196 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16197 errmsg("cannot change owner of relation \"%s\"",
16198 NameStr(tuple_class->relname)),
16199 errdetail_relkind_not_supported(tuple_class->relkind)));
16200 }
16201
16202 /*
16203 * If the new owner is the same as the existing owner, consider the
16204 * command to have succeeded. This is for dump restoration purposes.
16205 */
16206 if (tuple_class->relowner != newOwnerId)
16207 {
16208 Datum repl_val[Natts_pg_class];
16209 bool repl_null[Natts_pg_class];
16210 bool repl_repl[Natts_pg_class];
16211 Acl *newAcl;
16212 Datum aclDatum;
16213 bool isNull;
16214 HeapTuple newtuple;
16215
16216 /* skip permission checks when recursing to index or toast table */
16217 if (!recursing)
16218 {
16219 /* Superusers can always do it */
16220 if (!superuser())
16221 {
16222 Oid namespaceOid = tuple_class->relnamespace;
16223 AclResult aclresult;
16224
16225 /* Otherwise, must be owner of the existing object */
16226 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16228 RelationGetRelationName(target_rel));
16229
16230 /* Must be able to become new owner */
16231 check_can_set_role(GetUserId(), newOwnerId);
16232
16233 /* New owner must have CREATE privilege on namespace */
16234 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16235 ACL_CREATE);
16236 if (aclresult != ACLCHECK_OK)
16237 aclcheck_error(aclresult, OBJECT_SCHEMA,
16238 get_namespace_name(namespaceOid));
16239 }
16240 }
16241
16242 memset(repl_null, false, sizeof(repl_null));
16243 memset(repl_repl, false, sizeof(repl_repl));
16244
16245 repl_repl[Anum_pg_class_relowner - 1] = true;
16246 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16247
16248 /*
16249 * Determine the modified ACL for the new owner. This is only
16250 * necessary when the ACL is non-null.
16251 */
16252 aclDatum = SysCacheGetAttr(RELOID, tuple,
16253 Anum_pg_class_relacl,
16254 &isNull);
16255 if (!isNull)
16256 {
16257 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16258 tuple_class->relowner, newOwnerId);
16259 repl_repl[Anum_pg_class_relacl - 1] = true;
16260 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16261 }
16262
16263 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16264
16265 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16266
16267 heap_freetuple(newtuple);
16268
16269 /*
16270 * We must similarly update any per-column ACLs to reflect the new
16271 * owner; for neatness reasons that's split out as a subroutine.
16272 */
16273 change_owner_fix_column_acls(relationOid,
16274 tuple_class->relowner,
16275 newOwnerId);
16276
16277 /*
16278 * Update owner dependency reference, if any. A composite type has
16279 * none, because it's tracked for the pg_type entry instead of here;
16280 * indexes and TOAST tables don't have their own entries either.
16281 */
16282 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16283 tuple_class->relkind != RELKIND_INDEX &&
16284 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16285 tuple_class->relkind != RELKIND_TOASTVALUE)
16286 changeDependencyOnOwner(RelationRelationId, relationOid,
16287 newOwnerId);
16288
16289 /*
16290 * Also change the ownership of the table's row type, if it has one
16291 */
16292 if (OidIsValid(tuple_class->reltype))
16293 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16294
16295 /*
16296 * If we are operating on a table or materialized view, also change
16297 * the ownership of any indexes and sequences that belong to the
16298 * relation, as well as its toast table (if it has one).
16299 */
16300 if (tuple_class->relkind == RELKIND_RELATION ||
16301 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16302 tuple_class->relkind == RELKIND_MATVIEW ||
16303 tuple_class->relkind == RELKIND_TOASTVALUE)
16304 {
16305 List *index_oid_list;
16306 ListCell *i;
16307
16308 /* Find all the indexes belonging to this relation */
16309 index_oid_list = RelationGetIndexList(target_rel);
16310
16311 /* For each index, recursively change its ownership */
16312 foreach(i, index_oid_list)
16313 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16314
16315 list_free(index_oid_list);
16316 }
16317
16318 /* If it has a toast table, recurse to change its ownership */
16319 if (tuple_class->reltoastrelid != InvalidOid)
16320 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16321 true, lockmode);
16322
16323 /* If it has dependent sequences, recurse to change them too */
16324 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16325 }
16326
16327 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16328
16329 ReleaseSysCache(tuple);
16330 table_close(class_rel, RowExclusiveLock);
16331 relation_close(target_rel, NoLock);
16332}
16333
16334/*
16335 * change_owner_fix_column_acls
16336 *
16337 * Helper function for ATExecChangeOwner. Scan the columns of the table
16338 * and fix any non-null column ACLs to reflect the new owner.
16339 */
16340static void
16341change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16342{
16343 Relation attRelation;
16344 SysScanDesc scan;
16345 ScanKeyData key[1];
16346 HeapTuple attributeTuple;
16347
16348 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16349 ScanKeyInit(&key[0],
16350 Anum_pg_attribute_attrelid,
16351 BTEqualStrategyNumber, F_OIDEQ,
16352 ObjectIdGetDatum(relationOid));
16353 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16354 true, NULL, 1, key);
16355 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16356 {
16357 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16358 Datum repl_val[Natts_pg_attribute];
16359 bool repl_null[Natts_pg_attribute];
16360 bool repl_repl[Natts_pg_attribute];
16361 Acl *newAcl;
16362 Datum aclDatum;
16363 bool isNull;
16364 HeapTuple newtuple;
16365
16366 /* Ignore dropped columns */
16367 if (att->attisdropped)
16368 continue;
16369
16370 aclDatum = heap_getattr(attributeTuple,
16371 Anum_pg_attribute_attacl,
16372 RelationGetDescr(attRelation),
16373 &isNull);
16374 /* Null ACLs do not require changes */
16375 if (isNull)
16376 continue;
16377
16378 memset(repl_null, false, sizeof(repl_null));
16379 memset(repl_repl, false, sizeof(repl_repl));
16380
16381 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16382 oldOwnerId, newOwnerId);
16383 repl_repl[Anum_pg_attribute_attacl - 1] = true;
16384 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16385
16386 newtuple = heap_modify_tuple(attributeTuple,
16387 RelationGetDescr(attRelation),
16388 repl_val, repl_null, repl_repl);
16389
16390 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16391
16392 heap_freetuple(newtuple);
16393 }
16394 systable_endscan(scan);
16395 table_close(attRelation, RowExclusiveLock);
16396}
16397
16398/*
16399 * change_owner_recurse_to_sequences
16400 *
16401 * Helper function for ATExecChangeOwner. Examines pg_depend searching
16402 * for sequences that are dependent on serial columns, and changes their
16403 * ownership.
16404 */
16405static void
16406change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16407{
16408 Relation depRel;
16409 SysScanDesc scan;
16410 ScanKeyData key[2];
16411 HeapTuple tup;
16412
16413 /*
16414 * SERIAL sequences are those having an auto dependency on one of the
16415 * table's columns (we don't care *which* column, exactly).
16416 */
16417 depRel = table_open(DependRelationId, AccessShareLock);
16418
16419 ScanKeyInit(&key[0],
16420 Anum_pg_depend_refclassid,
16421 BTEqualStrategyNumber, F_OIDEQ,
16422 ObjectIdGetDatum(RelationRelationId));
16423 ScanKeyInit(&key[1],
16424 Anum_pg_depend_refobjid,
16425 BTEqualStrategyNumber, F_OIDEQ,
16426 ObjectIdGetDatum(relationOid));
16427 /* we leave refobjsubid unspecified */
16428
16429 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16430 NULL, 2, key);
16431
16432 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16433 {
16434 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16435 Relation seqRel;
16436
16437 /* skip dependencies other than auto dependencies on columns */
16438 if (depForm->refobjsubid == 0 ||
16439 depForm->classid != RelationRelationId ||
16440 depForm->objsubid != 0 ||
16441 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16442 continue;
16443
16444 /* Use relation_open just in case it's an index */
16445 seqRel = relation_open(depForm->objid, lockmode);
16446
16447 /* skip non-sequence relations */
16448 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16449 {
16450 /* No need to keep the lock */
16451 relation_close(seqRel, lockmode);
16452 continue;
16453 }
16454
16455 /* We don't need to close the sequence while we alter it. */
16456 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16457
16458 /* Now we can close it. Keep the lock till end of transaction. */
16459 relation_close(seqRel, NoLock);
16460 }
16461
16462 systable_endscan(scan);
16463
16465}
16466
16467/*
16468 * ALTER TABLE CLUSTER ON
16469 *
16470 * The only thing we have to do is to change the indisclustered bits.
16471 *
16472 * Return the address of the new clustering index.
16473 */
16474static ObjectAddress
16475ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16476{
16477 Oid indexOid;
16478 ObjectAddress address;
16479
16480 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16481
16482 if (!OidIsValid(indexOid))
16483 ereport(ERROR,
16484 (errcode(ERRCODE_UNDEFINED_OBJECT),
16485 errmsg("index \"%s\" for table \"%s\" does not exist",
16486 indexName, RelationGetRelationName(rel))));
16487
16488 /* Check index is valid to cluster on */
16489 check_index_is_clusterable(rel, indexOid, lockmode);
16490
16491 /* And do the work */
16492 mark_index_clustered(rel, indexOid, false);
16493
16494 ObjectAddressSet(address,
16495 RelationRelationId, indexOid);
16496
16497 return address;
16498}
16499
16500/*
16501 * ALTER TABLE SET WITHOUT CLUSTER
16502 *
16503 * We have to find any indexes on the table that have indisclustered bit
16504 * set and turn it off.
16505 */
16506static void
16508{
16509 mark_index_clustered(rel, InvalidOid, false);
16510}
16511
16512/*
16513 * Preparation phase for SET ACCESS METHOD
16514 *
16515 * Check that the access method exists and determine whether a change is
16516 * actually needed.
16517 */
16518static void
16520{
16521 Oid amoid;
16522
16523 /*
16524 * Look up the access method name and check that it differs from the
16525 * table's current AM. If DEFAULT was specified for a partitioned table
16526 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16527 */
16528 if (amname != NULL)
16529 amoid = get_table_am_oid(amname, false);
16530 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16531 amoid = InvalidOid;
16532 else
16534
16535 /* if it's a match, phase 3 doesn't need to do anything */
16536 if (rel->rd_rel->relam == amoid)
16537 return;
16538
16539 /* Save info for Phase 3 to do the real work */
16541 tab->newAccessMethod = amoid;
16542 tab->chgAccessMethod = true;
16543}
16544
16545/*
16546 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16547 * storage that have an interest in preserving AM.
16548 *
16549 * Since these have no storage, setting the access method is a catalog only
16550 * operation.
16551 */
16552static void
16554{
16555 Relation pg_class;
16556 Oid oldAccessMethodId;
16557 HeapTuple tuple;
16558 Form_pg_class rd_rel;
16559 Oid reloid = RelationGetRelid(rel);
16560
16561 /*
16562 * Shouldn't be called on relations having storage; these are processed in
16563 * phase 3.
16564 */
16565 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16566
16567 /* Get a modifiable copy of the relation's pg_class row. */
16568 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16569
16570 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16571 if (!HeapTupleIsValid(tuple))
16572 elog(ERROR, "cache lookup failed for relation %u", reloid);
16573 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16574
16575 /* Update the pg_class row. */
16576 oldAccessMethodId = rd_rel->relam;
16577 rd_rel->relam = newAccessMethodId;
16578
16579 /* Leave if no update required */
16580 if (rd_rel->relam == oldAccessMethodId)
16581 {
16582 heap_freetuple(tuple);
16583 table_close(pg_class, RowExclusiveLock);
16584 return;
16585 }
16586
16587 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16588
16589 /*
16590 * Update the dependency on the new access method. No dependency is added
16591 * if the new access method is InvalidOid (default case). Be very careful
16592 * that this has to compare the previous value stored in pg_class with the
16593 * new one.
16594 */
16595 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16596 {
16597 ObjectAddress relobj,
16598 referenced;
16599
16600 /*
16601 * New access method is defined and there was no dependency
16602 * previously, so record a new one.
16603 */
16604 ObjectAddressSet(relobj, RelationRelationId, reloid);
16605 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16606 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16607 }
16608 else if (OidIsValid(oldAccessMethodId) &&
16609 !OidIsValid(rd_rel->relam))
16610 {
16611 /*
16612 * There was an access method defined, and no new one, so just remove
16613 * the existing dependency.
16614 */
16615 deleteDependencyRecordsForClass(RelationRelationId, reloid,
16616 AccessMethodRelationId,
16618 }
16619 else
16620 {
16621 Assert(OidIsValid(oldAccessMethodId) &&
16622 OidIsValid(rd_rel->relam));
16623
16624 /* Both are valid, so update the dependency */
16625 changeDependencyFor(RelationRelationId, reloid,
16626 AccessMethodRelationId,
16627 oldAccessMethodId, rd_rel->relam);
16628 }
16629
16630 /* make the relam and dependency changes visible */
16632
16633 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16634
16635 heap_freetuple(tuple);
16636 table_close(pg_class, RowExclusiveLock);
16637}
16638
16639/*
16640 * ALTER TABLE SET TABLESPACE
16641 */
16642static void
16643ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16644{
16645 Oid tablespaceId;
16646
16647 /* Check that the tablespace exists */
16648 tablespaceId = get_tablespace_oid(tablespacename, false);
16649
16650 /* Check permissions except when moving to database's default */
16651 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16652 {
16653 AclResult aclresult;
16654
16655 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16656 if (aclresult != ACLCHECK_OK)
16657 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16658 }
16659
16660 /* Save info for Phase 3 to do the real work */
16661 if (OidIsValid(tab->newTableSpace))
16662 ereport(ERROR,
16663 (errcode(ERRCODE_SYNTAX_ERROR),
16664 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16665
16666 tab->newTableSpace = tablespaceId;
16667}
16668
16669/*
16670 * Set, reset, or replace reloptions.
16671 */
16672static void
16674 LOCKMODE lockmode)
16675{
16676 Oid relid;
16677 Relation pgclass;
16678 HeapTuple tuple;
16679 HeapTuple newtuple;
16680 Datum datum;
16681 Datum newOptions;
16682 Datum repl_val[Natts_pg_class];
16683 bool repl_null[Natts_pg_class];
16684 bool repl_repl[Natts_pg_class];
16685 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16686
16687 if (defList == NIL && operation != AT_ReplaceRelOptions)
16688 return; /* nothing to do */
16689
16690 pgclass = table_open(RelationRelationId, RowExclusiveLock);
16691
16692 /* Fetch heap tuple */
16693 relid = RelationGetRelid(rel);
16694 tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16695 if (!HeapTupleIsValid(tuple))
16696 elog(ERROR, "cache lookup failed for relation %u", relid);
16697
16698 if (operation == AT_ReplaceRelOptions)
16699 {
16700 /*
16701 * If we're supposed to replace the reloptions list, we just pretend
16702 * there were none before.
16703 */
16704 datum = (Datum) 0;
16705 }
16706 else
16707 {
16708 bool isnull;
16709
16710 /* Get the old reloptions */
16711 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16712 &isnull);
16713 if (isnull)
16714 datum = (Datum) 0;
16715 }
16716
16717 /* Generate new proposed reloptions (text array) */
16718 newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16719 operation == AT_ResetRelOptions);
16720
16721 /* Validate */
16722 switch (rel->rd_rel->relkind)
16723 {
16724 case RELKIND_RELATION:
16725 case RELKIND_MATVIEW:
16726 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16727 break;
16728 case RELKIND_PARTITIONED_TABLE:
16729 (void) partitioned_table_reloptions(newOptions, true);
16730 break;
16731 case RELKIND_VIEW:
16732 (void) view_reloptions(newOptions, true);
16733 break;
16734 case RELKIND_INDEX:
16735 case RELKIND_PARTITIONED_INDEX:
16736 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16737 break;
16738 case RELKIND_TOASTVALUE:
16739 /* fall through to error -- shouldn't ever get here */
16740 default:
16741 ereport(ERROR,
16742 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16743 errmsg("cannot set options for relation \"%s\"",
16745 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16746 break;
16747 }
16748
16749 /* Special-case validation of view options */
16750 if (rel->rd_rel->relkind == RELKIND_VIEW)
16751 {
16752 Query *view_query = get_view_query(rel);
16753 List *view_options = untransformRelOptions(newOptions);
16754 ListCell *cell;
16755 bool check_option = false;
16756
16757 foreach(cell, view_options)
16758 {
16759 DefElem *defel = (DefElem *) lfirst(cell);
16760
16761 if (strcmp(defel->defname, "check_option") == 0)
16762 check_option = true;
16763 }
16764
16765 /*
16766 * If the check option is specified, look to see if the view is
16767 * actually auto-updatable or not.
16768 */
16769 if (check_option)
16770 {
16771 const char *view_updatable_error =
16772 view_query_is_auto_updatable(view_query, true);
16773
16774 if (view_updatable_error)
16775 ereport(ERROR,
16776 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16777 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16778 errhint("%s", _(view_updatable_error))));
16779 }
16780 }
16781
16782 /*
16783 * All we need do here is update the pg_class row; the new options will be
16784 * propagated into relcaches during post-commit cache inval.
16785 */
16786 memset(repl_val, 0, sizeof(repl_val));
16787 memset(repl_null, false, sizeof(repl_null));
16788 memset(repl_repl, false, sizeof(repl_repl));
16789
16790 if (newOptions != (Datum) 0)
16791 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16792 else
16793 repl_null[Anum_pg_class_reloptions - 1] = true;
16794
16795 repl_repl[Anum_pg_class_reloptions - 1] = true;
16796
16797 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16798 repl_val, repl_null, repl_repl);
16799
16800 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16801 UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16802
16803 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16804
16805 heap_freetuple(newtuple);
16806
16807 ReleaseSysCache(tuple);
16808
16809 /* repeat the whole exercise for the toast table, if there's one */
16810 if (OidIsValid(rel->rd_rel->reltoastrelid))
16811 {
16812 Relation toastrel;
16813 Oid toastid = rel->rd_rel->reltoastrelid;
16814
16815 toastrel = table_open(toastid, lockmode);
16816
16817 /* Fetch heap tuple */
16818 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16819 if (!HeapTupleIsValid(tuple))
16820 elog(ERROR, "cache lookup failed for relation %u", toastid);
16821
16822 if (operation == AT_ReplaceRelOptions)
16823 {
16824 /*
16825 * If we're supposed to replace the reloptions list, we just
16826 * pretend there were none before.
16827 */
16828 datum = (Datum) 0;
16829 }
16830 else
16831 {
16832 bool isnull;
16833
16834 /* Get the old reloptions */
16835 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16836 &isnull);
16837 if (isnull)
16838 datum = (Datum) 0;
16839 }
16840
16841 newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16842 false, operation == AT_ResetRelOptions);
16843
16844 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16845
16846 memset(repl_val, 0, sizeof(repl_val));
16847 memset(repl_null, false, sizeof(repl_null));
16848 memset(repl_repl, false, sizeof(repl_repl));
16849
16850 if (newOptions != (Datum) 0)
16851 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16852 else
16853 repl_null[Anum_pg_class_reloptions - 1] = true;
16854
16855 repl_repl[Anum_pg_class_reloptions - 1] = true;
16856
16857 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16858 repl_val, repl_null, repl_repl);
16859
16860 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16861
16862 InvokeObjectPostAlterHookArg(RelationRelationId,
16863 RelationGetRelid(toastrel), 0,
16864 InvalidOid, true);
16865
16866 heap_freetuple(newtuple);
16867
16868 ReleaseSysCache(tuple);
16869
16870 table_close(toastrel, NoLock);
16871 }
16872
16873 table_close(pgclass, RowExclusiveLock);
16874}
16875
16876/*
16877 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16878 * rewriting to be done, so we just want to copy the data as fast as possible.
16879 */
16880static void
16881ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16882{
16883 Relation rel;
16884 Oid reltoastrelid;
16885 RelFileNumber newrelfilenumber;
16886 RelFileLocator newrlocator;
16887 List *reltoastidxids = NIL;
16888 ListCell *lc;
16889
16890 /*
16891 * Need lock here in case we are recursing to toast table or index
16892 */
16893 rel = relation_open(tableOid, lockmode);
16894
16895 /* Check first if relation can be moved to new tablespace */
16896 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16897 {
16898 InvokeObjectPostAlterHook(RelationRelationId,
16899 RelationGetRelid(rel), 0);
16900 relation_close(rel, NoLock);
16901 return;
16902 }
16903
16904 reltoastrelid = rel->rd_rel->reltoastrelid;
16905 /* Fetch the list of indexes on toast relation if necessary */
16906 if (OidIsValid(reltoastrelid))
16907 {
16908 Relation toastRel = relation_open(reltoastrelid, lockmode);
16909
16910 reltoastidxids = RelationGetIndexList(toastRel);
16911 relation_close(toastRel, lockmode);
16912 }
16913
16914 /*
16915 * Relfilenumbers are not unique in databases across tablespaces, so we
16916 * need to allocate a new one in the new tablespace.
16917 */
16918 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16919 rel->rd_rel->relpersistence);
16920
16921 /* Open old and new relation */
16922 newrlocator = rel->rd_locator;
16923 newrlocator.relNumber = newrelfilenumber;
16924 newrlocator.spcOid = newTableSpace;
16925
16926 /* hand off to AM to actually create new rel storage and copy the data */
16927 if (rel->rd_rel->relkind == RELKIND_INDEX)
16928 {
16929 index_copy_data(rel, newrlocator);
16930 }
16931 else
16932 {
16933 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16934 table_relation_copy_data(rel, &newrlocator);
16935 }
16936
16937 /*
16938 * Update the pg_class row.
16939 *
16940 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16941 * executed on pg_class or its indexes (the above copy wouldn't contain
16942 * the updated pg_class entry), but that's forbidden with
16943 * CheckRelationTableSpaceMove().
16944 */
16945 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16946
16947 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16948
16950
16951 relation_close(rel, NoLock);
16952
16953 /* Make sure the reltablespace change is visible */
16955
16956 /* Move associated toast relation and/or indexes, too */
16957 if (OidIsValid(reltoastrelid))
16958 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16959 foreach(lc, reltoastidxids)
16960 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16961
16962 /* Clean up */
16963 list_free(reltoastidxids);
16964}
16965
16966/*
16967 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16968 * storage that have an interest in preserving tablespace.
16969 *
16970 * Since these have no storage the tablespace can be updated with a simple
16971 * metadata only operation to update the tablespace.
16972 */
16973static void
16975{
16976 /*
16977 * Shouldn't be called on relations having storage; these are processed in
16978 * phase 3.
16979 */
16980 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16981
16982 /* check if relation can be moved to its new tablespace */
16983 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16984 {
16985 InvokeObjectPostAlterHook(RelationRelationId,
16986 RelationGetRelid(rel),
16987 0);
16988 return;
16989 }
16990
16991 /* Update can be done, so change reltablespace */
16992 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16993
16994 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16995
16996 /* Make sure the reltablespace change is visible */
16998}
16999
17000/*
17001 * Alter Table ALL ... SET TABLESPACE
17002 *
17003 * Allows a user to move all objects of some type in a given tablespace in the
17004 * current database to another tablespace. Objects can be chosen based on the
17005 * owner of the object also, to allow users to move only their objects.
17006 * The user must have CREATE rights on the new tablespace, as usual. The main
17007 * permissions handling is done by the lower-level table move function.
17008 *
17009 * All to-be-moved objects are locked first. If NOWAIT is specified and the
17010 * lock can't be acquired then we ereport(ERROR).
17011 */
17012Oid
17014{
17015 List *relations = NIL;
17016 ListCell *l;
17017 ScanKeyData key[1];
17018 Relation rel;
17019 TableScanDesc scan;
17020 HeapTuple tuple;
17021 Oid orig_tablespaceoid;
17022 Oid new_tablespaceoid;
17023 List *role_oids = roleSpecsToIds(stmt->roles);
17024
17025 /* Ensure we were not asked to move something we can't */
17026 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17027 stmt->objtype != OBJECT_MATVIEW)
17028 ereport(ERROR,
17029 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17030 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17031
17032 /* Get the orig and new tablespace OIDs */
17033 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17034 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17035
17036 /* Can't move shared relations in to or out of pg_global */
17037 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17038 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
17039 new_tablespaceoid == GLOBALTABLESPACE_OID)
17040 ereport(ERROR,
17041 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17042 errmsg("cannot move relations in to or out of pg_global tablespace")));
17043
17044 /*
17045 * Must have CREATE rights on the new tablespace, unless it is the
17046 * database default tablespace (which all users implicitly have CREATE
17047 * rights on).
17048 */
17049 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
17050 {
17051 AclResult aclresult;
17052
17053 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
17054 ACL_CREATE);
17055 if (aclresult != ACLCHECK_OK)
17057 get_tablespace_name(new_tablespaceoid));
17058 }
17059
17060 /*
17061 * Now that the checks are done, check if we should set either to
17062 * InvalidOid because it is our database's default tablespace.
17063 */
17064 if (orig_tablespaceoid == MyDatabaseTableSpace)
17065 orig_tablespaceoid = InvalidOid;
17066
17067 if (new_tablespaceoid == MyDatabaseTableSpace)
17068 new_tablespaceoid = InvalidOid;
17069
17070 /* no-op */
17071 if (orig_tablespaceoid == new_tablespaceoid)
17072 return new_tablespaceoid;
17073
17074 /*
17075 * Walk the list of objects in the tablespace and move them. This will
17076 * only find objects in our database, of course.
17077 */
17078 ScanKeyInit(&key[0],
17079 Anum_pg_class_reltablespace,
17080 BTEqualStrategyNumber, F_OIDEQ,
17081 ObjectIdGetDatum(orig_tablespaceoid));
17082
17083 rel = table_open(RelationRelationId, AccessShareLock);
17084 scan = table_beginscan_catalog(rel, 1, key);
17085 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17086 {
17087 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17088 Oid relOid = relForm->oid;
17089
17090 /*
17091 * Do not move objects in pg_catalog as part of this, if an admin
17092 * really wishes to do so, they can issue the individual ALTER
17093 * commands directly.
17094 *
17095 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17096 * (TOAST will be moved with the main table).
17097 */
17098 if (IsCatalogNamespace(relForm->relnamespace) ||
17099 relForm->relisshared ||
17100 isAnyTempNamespace(relForm->relnamespace) ||
17101 IsToastNamespace(relForm->relnamespace))
17102 continue;
17103
17104 /* Only move the object type requested */
17105 if ((stmt->objtype == OBJECT_TABLE &&
17106 relForm->relkind != RELKIND_RELATION &&
17107 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17108 (stmt->objtype == OBJECT_INDEX &&
17109 relForm->relkind != RELKIND_INDEX &&
17110 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17111 (stmt->objtype == OBJECT_MATVIEW &&
17112 relForm->relkind != RELKIND_MATVIEW))
17113 continue;
17114
17115 /* Check if we are only moving objects owned by certain roles */
17116 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17117 continue;
17118
17119 /*
17120 * Handle permissions-checking here since we are locking the tables
17121 * and also to avoid doing a bunch of work only to fail part-way. Note
17122 * that permissions will also be checked by AlterTableInternal().
17123 *
17124 * Caller must be considered an owner on the table to move it.
17125 */
17126 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17128 NameStr(relForm->relname));
17129
17130 if (stmt->nowait &&
17132 ereport(ERROR,
17133 (errcode(ERRCODE_OBJECT_IN_USE),
17134 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17135 get_namespace_name(relForm->relnamespace),
17136 NameStr(relForm->relname))));
17137 else
17139
17140 /* Add to our list of objects to move */
17141 relations = lappend_oid(relations, relOid);
17142 }
17143
17144 table_endscan(scan);
17146
17147 if (relations == NIL)
17149 (errcode(ERRCODE_NO_DATA_FOUND),
17150 errmsg("no matching relations in tablespace \"%s\" found",
17151 orig_tablespaceoid == InvalidOid ? "(database default)" :
17152 get_tablespace_name(orig_tablespaceoid))));
17153
17154 /* Everything is locked, loop through and move all of the relations. */
17155 foreach(l, relations)
17156 {
17157 List *cmds = NIL;
17159
17161 cmd->name = stmt->new_tablespacename;
17162
17163 cmds = lappend(cmds, cmd);
17164
17166 /* OID is set by AlterTableInternal */
17167 AlterTableInternal(lfirst_oid(l), cmds, false);
17169 }
17170
17171 return new_tablespaceoid;
17172}
17173
17174static void
17176{
17177 SMgrRelation dstrel;
17178
17179 /*
17180 * Since we copy the file directly without looking at the shared buffers,
17181 * we'd better first flush out any pages of the source relation that are
17182 * in shared buffers. We assume no new changes will be made while we are
17183 * holding exclusive lock on the rel.
17184 */
17186
17187 /*
17188 * Create and copy all forks of the relation, and schedule unlinking of
17189 * old physical files.
17190 *
17191 * NOTE: any conflict in relfilenumber value will be caught in
17192 * RelationCreateStorage().
17193 */
17194 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17195
17196 /* copy main fork */
17198 rel->rd_rel->relpersistence);
17199
17200 /* copy those extra forks that exist */
17201 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17202 forkNum <= MAX_FORKNUM; forkNum++)
17203 {
17204 if (smgrexists(RelationGetSmgr(rel), forkNum))
17205 {
17206 smgrcreate(dstrel, forkNum, false);
17207
17208 /*
17209 * WAL log creation if the relation is persistent, or this is the
17210 * init fork of an unlogged relation.
17211 */
17212 if (RelationIsPermanent(rel) ||
17213 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17214 forkNum == INIT_FORKNUM))
17215 log_smgrcreate(&newrlocator, forkNum);
17216 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17217 rel->rd_rel->relpersistence);
17218 }
17219 }
17220
17221 /* drop old relation, and close new one */
17223 smgrclose(dstrel);
17224}
17225
17226/*
17227 * ALTER TABLE ENABLE/DISABLE TRIGGER
17228 *
17229 * We just pass this off to trigger.c.
17230 */
17231static void
17232ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17233 char fires_when, bool skip_system, bool recurse,
17234 LOCKMODE lockmode)
17235{
17236 EnableDisableTrigger(rel, trigname, InvalidOid,
17237 fires_when, skip_system, recurse,
17238 lockmode);
17239
17240 InvokeObjectPostAlterHook(RelationRelationId,
17241 RelationGetRelid(rel), 0);
17242}
17243
17244/*
17245 * ALTER TABLE ENABLE/DISABLE RULE
17246 *
17247 * We just pass this off to rewriteDefine.c.
17248 */
17249static void
17250ATExecEnableDisableRule(Relation rel, const char *rulename,
17251 char fires_when, LOCKMODE lockmode)
17252{
17253 EnableDisableRule(rel, rulename, fires_when);
17254
17255 InvokeObjectPostAlterHook(RelationRelationId,
17256 RelationGetRelid(rel), 0);
17257}
17258
17259/*
17260 * ALTER TABLE INHERIT
17261 *
17262 * Add a parent to the child's parents. This verifies that all the columns and
17263 * check constraints of the parent appear in the child and that they have the
17264 * same data types and expressions.
17265 */
17266static void
17268{
17269 if (child_rel->rd_rel->reloftype)
17270 ereport(ERROR,
17271 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17272 errmsg("cannot change inheritance of typed table")));
17273
17274 if (child_rel->rd_rel->relispartition)
17275 ereport(ERROR,
17276 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17277 errmsg("cannot change inheritance of a partition")));
17278
17279 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17280 ereport(ERROR,
17281 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17282 errmsg("cannot change inheritance of partitioned table")));
17283}
17284
17285/*
17286 * Return the address of the new parent relation.
17287 */
17288static ObjectAddress
17289ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17290{
17291 Relation parent_rel;
17292 List *children;
17293 ObjectAddress address;
17294 const char *trigger_name;
17295
17296 /*
17297 * A self-exclusive lock is needed here. See the similar case in
17298 * MergeAttributes() for a full explanation.
17299 */
17300 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17301
17302 /*
17303 * Must be owner of both parent and child -- child was checked by
17304 * ATSimplePermissions call in ATPrepCmd
17305 */
17308
17309 /* Permanent rels cannot inherit from temporary ones */
17310 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17311 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17312 ereport(ERROR,
17313 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17314 errmsg("cannot inherit from temporary relation \"%s\"",
17315 RelationGetRelationName(parent_rel))));
17316
17317 /* If parent rel is temp, it must belong to this session */
17318 if (RELATION_IS_OTHER_TEMP(parent_rel))
17319 ereport(ERROR,
17320 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17321 errmsg("cannot inherit from temporary relation of another session")));
17322
17323 /* Ditto for the child */
17324 if (RELATION_IS_OTHER_TEMP(child_rel))
17325 ereport(ERROR,
17326 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17327 errmsg("cannot inherit to temporary relation of another session")));
17328
17329 /* Prevent partitioned tables from becoming inheritance parents */
17330 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17331 ereport(ERROR,
17332 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17333 errmsg("cannot inherit from partitioned table \"%s\"",
17334 parent->relname)));
17335
17336 /* Likewise for partitions */
17337 if (parent_rel->rd_rel->relispartition)
17338 ereport(ERROR,
17339 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17340 errmsg("cannot inherit from a partition")));
17341
17342 /*
17343 * Prevent circularity by seeing if proposed parent inherits from child.
17344 * (In particular, this disallows making a rel inherit from itself.)
17345 *
17346 * This is not completely bulletproof because of race conditions: in
17347 * multi-level inheritance trees, someone else could concurrently be
17348 * making another inheritance link that closes the loop but does not join
17349 * either of the rels we have locked. Preventing that seems to require
17350 * exclusive locks on the entire inheritance tree, which is a cure worse
17351 * than the disease. find_all_inheritors() will cope with circularity
17352 * anyway, so don't sweat it too much.
17353 *
17354 * We use weakest lock we can on child's children, namely AccessShareLock.
17355 */
17356 children = find_all_inheritors(RelationGetRelid(child_rel),
17357 AccessShareLock, NULL);
17358
17359 if (list_member_oid(children, RelationGetRelid(parent_rel)))
17360 ereport(ERROR,
17361 (errcode(ERRCODE_DUPLICATE_TABLE),
17362 errmsg("circular inheritance not allowed"),
17363 errdetail("\"%s\" is already a child of \"%s\".",
17364 parent->relname,
17365 RelationGetRelationName(child_rel))));
17366
17367 /*
17368 * If child_rel has row-level triggers with transition tables, we
17369 * currently don't allow it to become an inheritance child. See also
17370 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17371 */
17372 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17373 if (trigger_name != NULL)
17374 ereport(ERROR,
17375 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17376 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17377 trigger_name, RelationGetRelationName(child_rel)),
17378 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17379
17380 /* OK to create inheritance */
17381 CreateInheritance(child_rel, parent_rel, false);
17382
17383 ObjectAddressSet(address, RelationRelationId,
17384 RelationGetRelid(parent_rel));
17385
17386 /* keep our lock on the parent relation until commit */
17387 table_close(parent_rel, NoLock);
17388
17389 return address;
17390}
17391
17392/*
17393 * CreateInheritance
17394 * Catalog manipulation portion of creating inheritance between a child
17395 * table and a parent table.
17396 *
17397 * Common to ATExecAddInherit() and ATExecAttachPartition().
17398 */
17399static void
17400CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17401{
17402 Relation catalogRelation;
17403 SysScanDesc scan;
17405 HeapTuple inheritsTuple;
17406 int32 inhseqno;
17407
17408 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17409 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17410
17411 /*
17412 * Check for duplicates in the list of parents, and determine the highest
17413 * inhseqno already present; we'll use the next one for the new parent.
17414 * Also, if proposed child is a partition, it cannot already be
17415 * inheriting.
17416 *
17417 * Note: we do not reject the case where the child already inherits from
17418 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17419 */
17421 Anum_pg_inherits_inhrelid,
17422 BTEqualStrategyNumber, F_OIDEQ,
17424 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17425 true, NULL, 1, &key);
17426
17427 /* inhseqno sequences start at 1 */
17428 inhseqno = 0;
17429 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17430 {
17431 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17432
17433 if (inh->inhparent == RelationGetRelid(parent_rel))
17434 ereport(ERROR,
17435 (errcode(ERRCODE_DUPLICATE_TABLE),
17436 errmsg("relation \"%s\" would be inherited from more than once",
17437 RelationGetRelationName(parent_rel))));
17438
17439 if (inh->inhseqno > inhseqno)
17440 inhseqno = inh->inhseqno;
17441 }
17442 systable_endscan(scan);
17443
17444 /* Match up the columns and bump attinhcount as needed */
17445 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17446
17447 /* Match up the constraints and bump coninhcount as needed */
17448 MergeConstraintsIntoExisting(child_rel, parent_rel);
17449
17450 /*
17451 * OK, it looks valid. Make the catalog entries that show inheritance.
17452 */
17454 RelationGetRelid(parent_rel),
17455 inhseqno + 1,
17456 catalogRelation,
17457 parent_rel->rd_rel->relkind ==
17458 RELKIND_PARTITIONED_TABLE);
17459
17460 /* Now we're done with pg_inherits */
17461 table_close(catalogRelation, RowExclusiveLock);
17462}
17463
17464/*
17465 * Obtain the source-text form of the constraint expression for a check
17466 * constraint, given its pg_constraint tuple
17467 */
17468static char *
17470{
17472 bool isnull;
17473 Datum attr;
17474 Datum expr;
17475
17476 con = (Form_pg_constraint) GETSTRUCT(contup);
17477 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17478 if (isnull)
17479 elog(ERROR, "null conbin for constraint %u", con->oid);
17480
17481 expr = DirectFunctionCall2(pg_get_expr, attr,
17482 ObjectIdGetDatum(con->conrelid));
17483 return TextDatumGetCString(expr);
17484}
17485
17486/*
17487 * Determine whether two check constraints are functionally equivalent
17488 *
17489 * The test we apply is to see whether they reverse-compile to the same
17490 * source string. This insulates us from issues like whether attributes
17491 * have the same physical column numbers in parent and child relations.
17492 *
17493 * Note that we ignore enforceability as there are cases where constraints
17494 * with differing enforceability are allowed.
17495 */
17496static bool
17498{
17501
17502 if (acon->condeferrable != bcon->condeferrable ||
17503 acon->condeferred != bcon->condeferred ||
17504 strcmp(decompile_conbin(a, tupleDesc),
17505 decompile_conbin(b, tupleDesc)) != 0)
17506 return false;
17507 else
17508 return true;
17509}
17510
17511/*
17512 * Check columns in child table match up with columns in parent, and increment
17513 * their attinhcount.
17514 *
17515 * Called by CreateInheritance
17516 *
17517 * Currently all parent columns must be found in child. Missing columns are an
17518 * error. One day we might consider creating new columns like CREATE TABLE
17519 * does. However, that is widely unpopular --- in the common use case of
17520 * partitioned tables it's a foot-gun.
17521 *
17522 * The data type must match exactly. If the parent column is NOT NULL then
17523 * the child must be as well. Defaults are not compared, however.
17524 */
17525static void
17526MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17527{
17528 Relation attrrel;
17529 TupleDesc parent_desc;
17530
17531 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17532 parent_desc = RelationGetDescr(parent_rel);
17533
17534 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17535 {
17536 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17537 char *parent_attname = NameStr(parent_att->attname);
17538 HeapTuple tuple;
17539
17540 /* Ignore dropped columns in the parent. */
17541 if (parent_att->attisdropped)
17542 continue;
17543
17544 /* Find same column in child (matching on column name). */
17545 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17546 if (HeapTupleIsValid(tuple))
17547 {
17548 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17549
17550 if (parent_att->atttypid != child_att->atttypid ||
17551 parent_att->atttypmod != child_att->atttypmod)
17552 ereport(ERROR,
17553 (errcode(ERRCODE_DATATYPE_MISMATCH),
17554 errmsg("child table \"%s\" has different type for column \"%s\"",
17555 RelationGetRelationName(child_rel), parent_attname)));
17556
17557 if (parent_att->attcollation != child_att->attcollation)
17558 ereport(ERROR,
17559 (errcode(ERRCODE_COLLATION_MISMATCH),
17560 errmsg("child table \"%s\" has different collation for column \"%s\"",
17561 RelationGetRelationName(child_rel), parent_attname)));
17562
17563 /*
17564 * If the parent has a not-null constraint that's not NO INHERIT,
17565 * make sure the child has one too.
17566 *
17567 * Other constraints are checked elsewhere.
17568 */
17569 if (parent_att->attnotnull && !child_att->attnotnull)
17570 {
17571 HeapTuple contup;
17572
17573 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17574 parent_att->attnum);
17575 if (HeapTupleIsValid(contup) &&
17576 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17577 ereport(ERROR,
17578 errcode(ERRCODE_DATATYPE_MISMATCH),
17579 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17580 parent_attname, RelationGetRelationName(child_rel)));
17581 }
17582
17583 /*
17584 * Child column must be generated if and only if parent column is.
17585 */
17586 if (parent_att->attgenerated && !child_att->attgenerated)
17587 ereport(ERROR,
17588 (errcode(ERRCODE_DATATYPE_MISMATCH),
17589 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17590 if (child_att->attgenerated && !parent_att->attgenerated)
17591 ereport(ERROR,
17592 (errcode(ERRCODE_DATATYPE_MISMATCH),
17593 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17594
17595 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17596 ereport(ERROR,
17597 (errcode(ERRCODE_DATATYPE_MISMATCH),
17598 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17599 errdetail("Parent column is %s, child column is %s.",
17600 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17601 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17602
17603 /*
17604 * Regular inheritance children are independent enough not to
17605 * inherit identity columns. But partitions are integral part of
17606 * a partitioned table and inherit identity column.
17607 */
17608 if (ispartition)
17609 child_att->attidentity = parent_att->attidentity;
17610
17611 /*
17612 * OK, bump the child column's inheritance count. (If we fail
17613 * later on, this change will just roll back.)
17614 */
17615 if (pg_add_s16_overflow(child_att->attinhcount, 1,
17616 &child_att->attinhcount))
17617 ereport(ERROR,
17618 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17619 errmsg("too many inheritance parents"));
17620
17621 /*
17622 * In case of partitions, we must enforce that value of attislocal
17623 * is same in all partitions. (Note: there are only inherited
17624 * attributes in partitions)
17625 */
17626 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17627 {
17628 Assert(child_att->attinhcount == 1);
17629 child_att->attislocal = false;
17630 }
17631
17632 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17633 heap_freetuple(tuple);
17634 }
17635 else
17636 {
17637 ereport(ERROR,
17638 (errcode(ERRCODE_DATATYPE_MISMATCH),
17639 errmsg("child table is missing column \"%s\"", parent_attname)));
17640 }
17641 }
17642
17643 table_close(attrrel, RowExclusiveLock);
17644}
17645
17646/*
17647 * Check constraints in child table match up with constraints in parent,
17648 * and increment their coninhcount.
17649 *
17650 * Constraints that are marked ONLY in the parent are ignored.
17651 *
17652 * Called by CreateInheritance
17653 *
17654 * Currently all constraints in parent must be present in the child. One day we
17655 * may consider adding new constraints like CREATE TABLE does.
17656 *
17657 * XXX This is O(N^2) which may be an issue with tables with hundreds of
17658 * constraints. As long as tables have more like 10 constraints it shouldn't be
17659 * a problem though. Even 100 constraints ought not be the end of the world.
17660 *
17661 * XXX See MergeWithExistingConstraint too if you change this code.
17662 */
17663static void
17665{
17666 Relation constraintrel;
17667 SysScanDesc parent_scan;
17668 ScanKeyData parent_key;
17669 HeapTuple parent_tuple;
17670 Oid parent_relid = RelationGetRelid(parent_rel);
17671 AttrMap *attmap;
17672
17673 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17674
17675 /* Outer loop scans through the parent's constraint definitions */
17676 ScanKeyInit(&parent_key,
17677 Anum_pg_constraint_conrelid,
17678 BTEqualStrategyNumber, F_OIDEQ,
17679 ObjectIdGetDatum(parent_relid));
17680 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17681 true, NULL, 1, &parent_key);
17682
17683 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17684 RelationGetDescr(child_rel),
17685 true);
17686
17687 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17688 {
17689 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17690 SysScanDesc child_scan;
17691 ScanKeyData child_key;
17692 HeapTuple child_tuple;
17693 AttrNumber parent_attno;
17694 bool found = false;
17695
17696 if (parent_con->contype != CONSTRAINT_CHECK &&
17697 parent_con->contype != CONSTRAINT_NOTNULL)
17698 continue;
17699
17700 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17701 if (parent_con->connoinherit)
17702 continue;
17703
17704 if (parent_con->contype == CONSTRAINT_NOTNULL)
17705 parent_attno = extractNotNullColumn(parent_tuple);
17706 else
17707 parent_attno = InvalidAttrNumber;
17708
17709 /* Search for a child constraint matching this one */
17710 ScanKeyInit(&child_key,
17711 Anum_pg_constraint_conrelid,
17712 BTEqualStrategyNumber, F_OIDEQ,
17714 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17715 true, NULL, 1, &child_key);
17716
17717 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17718 {
17719 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17720 HeapTuple child_copy;
17721
17722 if (child_con->contype != parent_con->contype)
17723 continue;
17724
17725 /*
17726 * CHECK constraint are matched by constraint name, NOT NULL ones
17727 * by attribute number.
17728 */
17729 if (child_con->contype == CONSTRAINT_CHECK)
17730 {
17731 if (strcmp(NameStr(parent_con->conname),
17732 NameStr(child_con->conname)) != 0)
17733 continue;
17734 }
17735 else if (child_con->contype == CONSTRAINT_NOTNULL)
17736 {
17737 Form_pg_attribute parent_attr;
17738 Form_pg_attribute child_attr;
17739 AttrNumber child_attno;
17740
17741 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17742 child_attno = extractNotNullColumn(child_tuple);
17743 if (parent_attno != attmap->attnums[child_attno - 1])
17744 continue;
17745
17746 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17747 /* there shouldn't be constraints on dropped columns */
17748 if (parent_attr->attisdropped || child_attr->attisdropped)
17749 elog(ERROR, "found not-null constraint on dropped columns");
17750 }
17751
17752 if (child_con->contype == CONSTRAINT_CHECK &&
17753 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17754 ereport(ERROR,
17755 (errcode(ERRCODE_DATATYPE_MISMATCH),
17756 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17757 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17758
17759 /*
17760 * If the child constraint is "no inherit" then cannot merge
17761 */
17762 if (child_con->connoinherit)
17763 ereport(ERROR,
17764 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17765 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17766 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17767
17768 /*
17769 * If the child constraint is "not valid" then cannot merge with a
17770 * valid parent constraint
17771 */
17772 if (parent_con->convalidated && child_con->conenforced &&
17773 !child_con->convalidated)
17774 ereport(ERROR,
17775 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17776 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17777 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17778
17779 /*
17780 * A NOT ENFORCED child constraint cannot be merged with an
17781 * ENFORCED parent constraint. However, the reverse is allowed,
17782 * where the child constraint is ENFORCED.
17783 */
17784 if (parent_con->conenforced && !child_con->conenforced)
17785 ereport(ERROR,
17786 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17787 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17788 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17789
17790 /*
17791 * OK, bump the child constraint's inheritance count. (If we fail
17792 * later on, this change will just roll back.)
17793 */
17794 child_copy = heap_copytuple(child_tuple);
17795 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17796
17797 if (pg_add_s16_overflow(child_con->coninhcount, 1,
17798 &child_con->coninhcount))
17799 ereport(ERROR,
17800 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17801 errmsg("too many inheritance parents"));
17802
17803 /*
17804 * In case of partitions, an inherited constraint must be
17805 * inherited only once since it cannot have multiple parents and
17806 * it is never considered local.
17807 */
17808 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17809 {
17810 Assert(child_con->coninhcount == 1);
17811 child_con->conislocal = false;
17812 }
17813
17814 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17815 heap_freetuple(child_copy);
17816
17817 found = true;
17818 break;
17819 }
17820
17821 systable_endscan(child_scan);
17822
17823 if (!found)
17824 {
17825 if (parent_con->contype == CONSTRAINT_NOTNULL)
17826 ereport(ERROR,
17827 errcode(ERRCODE_DATATYPE_MISMATCH),
17828 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17829 get_attname(parent_relid,
17830 extractNotNullColumn(parent_tuple),
17831 false),
17832 RelationGetRelationName(child_rel)));
17833
17834 ereport(ERROR,
17835 (errcode(ERRCODE_DATATYPE_MISMATCH),
17836 errmsg("child table is missing constraint \"%s\"",
17837 NameStr(parent_con->conname))));
17838 }
17839 }
17840
17841 systable_endscan(parent_scan);
17842 table_close(constraintrel, RowExclusiveLock);
17843}
17844
17845/*
17846 * ALTER TABLE NO INHERIT
17847 *
17848 * Return value is the address of the relation that is no longer parent.
17849 */
17850static ObjectAddress
17852{
17853 ObjectAddress address;
17854 Relation parent_rel;
17855
17856 if (rel->rd_rel->relispartition)
17857 ereport(ERROR,
17858 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17859 errmsg("cannot change inheritance of a partition")));
17860
17861 /*
17862 * AccessShareLock on the parent is probably enough, seeing that DROP
17863 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17864 * be inspecting the parent's schema.
17865 */
17866 parent_rel = table_openrv(parent, AccessShareLock);
17867
17868 /*
17869 * We don't bother to check ownership of the parent table --- ownership of
17870 * the child is presumed enough rights.
17871 */
17872
17873 /* Off to RemoveInheritance() where most of the work happens */
17874 RemoveInheritance(rel, parent_rel, false);
17875
17876 ObjectAddressSet(address, RelationRelationId,
17877 RelationGetRelid(parent_rel));
17878
17879 /* keep our lock on the parent relation until commit */
17880 table_close(parent_rel, NoLock);
17881
17882 return address;
17883}
17884
17885/*
17886 * MarkInheritDetached
17887 *
17888 * Set inhdetachpending for a partition, for ATExecDetachPartition
17889 * in concurrent mode. While at it, verify that no other partition is
17890 * already pending detach.
17891 */
17892static void
17894{
17895 Relation catalogRelation;
17896 SysScanDesc scan;
17898 HeapTuple inheritsTuple;
17899 bool found = false;
17900
17901 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17902
17903 /*
17904 * Find pg_inherits entries by inhparent. (We need to scan them all in
17905 * order to verify that no other partition is pending detach.)
17906 */
17907 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17909 Anum_pg_inherits_inhparent,
17910 BTEqualStrategyNumber, F_OIDEQ,
17911 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17912 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17913 true, NULL, 1, &key);
17914
17915 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17916 {
17917 Form_pg_inherits inhForm;
17918
17919 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17920 if (inhForm->inhdetachpending)
17921 ereport(ERROR,
17922 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17923 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17924 get_rel_name(inhForm->inhrelid),
17925 get_namespace_name(parent_rel->rd_rel->relnamespace),
17926 RelationGetRelationName(parent_rel)),
17927 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17928
17929 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17930 {
17931 HeapTuple newtup;
17932
17933 newtup = heap_copytuple(inheritsTuple);
17934 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17935
17936 CatalogTupleUpdate(catalogRelation,
17937 &inheritsTuple->t_self,
17938 newtup);
17939 found = true;
17940 heap_freetuple(newtup);
17941 /* keep looking, to ensure we catch others pending detach */
17942 }
17943 }
17944
17945 /* Done */
17946 systable_endscan(scan);
17947 table_close(catalogRelation, RowExclusiveLock);
17948
17949 if (!found)
17950 ereport(ERROR,
17952 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17953 RelationGetRelationName(child_rel),
17954 RelationGetRelationName(parent_rel))));
17955}
17956
17957/*
17958 * RemoveInheritance
17959 *
17960 * Drop a parent from the child's parents. This just adjusts the attinhcount
17961 * and attislocal of the columns and removes the pg_inherit and pg_depend
17962 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17963 *
17964 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17965 * up attislocal stays true, which means if a child is ever removed from a
17966 * parent then its columns will never be automatically dropped which may
17967 * surprise. But at least we'll never surprise by dropping columns someone
17968 * isn't expecting to be dropped which would actually mean data loss.
17969 *
17970 * coninhcount and conislocal for inherited constraints are adjusted in
17971 * exactly the same way.
17972 *
17973 * Common to ATExecDropInherit() and ATExecDetachPartition().
17974 */
17975static void
17976RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17977{
17978 Relation catalogRelation;
17979 SysScanDesc scan;
17980 ScanKeyData key[3];
17981 HeapTuple attributeTuple,
17982 constraintTuple;
17983 AttrMap *attmap;
17984 List *connames;
17985 List *nncolumns;
17986 bool found;
17987 bool is_partitioning;
17988
17989 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17990
17991 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17992 RelationGetRelid(parent_rel),
17993 expect_detached,
17994 RelationGetRelationName(child_rel));
17995 if (!found)
17996 {
17997 if (is_partitioning)
17998 ereport(ERROR,
18000 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18001 RelationGetRelationName(child_rel),
18002 RelationGetRelationName(parent_rel))));
18003 else
18004 ereport(ERROR,
18006 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18007 RelationGetRelationName(parent_rel),
18008 RelationGetRelationName(child_rel))));
18009 }
18010
18011 /*
18012 * Search through child columns looking for ones matching parent rel
18013 */
18014 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
18015 ScanKeyInit(&key[0],
18016 Anum_pg_attribute_attrelid,
18017 BTEqualStrategyNumber, F_OIDEQ,
18019 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
18020 true, NULL, 1, key);
18021 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
18022 {
18023 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
18024
18025 /* Ignore if dropped or not inherited */
18026 if (att->attisdropped)
18027 continue;
18028 if (att->attinhcount <= 0)
18029 continue;
18030
18032 NameStr(att->attname)))
18033 {
18034 /* Decrement inhcount and possibly set islocal to true */
18035 HeapTuple copyTuple = heap_copytuple(attributeTuple);
18036 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
18037
18038 copy_att->attinhcount--;
18039 if (copy_att->attinhcount == 0)
18040 copy_att->attislocal = true;
18041
18042 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18043 heap_freetuple(copyTuple);
18044 }
18045 }
18046 systable_endscan(scan);
18047 table_close(catalogRelation, RowExclusiveLock);
18048
18049 /*
18050 * Likewise, find inherited check and not-null constraints and disinherit
18051 * them. To do this, we first need a list of the names of the parent's
18052 * check constraints. (We cheat a bit by only checking for name matches,
18053 * assuming that the expressions will match.)
18054 *
18055 * For NOT NULL columns, we store column numbers to match, mapping them in
18056 * to the child rel's attribute numbers.
18057 */
18058 attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
18059 RelationGetDescr(parent_rel),
18060 false);
18061
18062 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
18063 ScanKeyInit(&key[0],
18064 Anum_pg_constraint_conrelid,
18065 BTEqualStrategyNumber, F_OIDEQ,
18066 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18067 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18068 true, NULL, 1, key);
18069
18070 connames = NIL;
18071 nncolumns = NIL;
18072
18073 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18074 {
18075 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18076
18077 if (con->connoinherit)
18078 continue;
18079
18080 if (con->contype == CONSTRAINT_CHECK)
18081 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18082 if (con->contype == CONSTRAINT_NOTNULL)
18083 {
18084 AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
18085
18086 nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18087 }
18088 }
18089
18090 systable_endscan(scan);
18091
18092 /* Now scan the child's constraints to find matches */
18093 ScanKeyInit(&key[0],
18094 Anum_pg_constraint_conrelid,
18095 BTEqualStrategyNumber, F_OIDEQ,
18097 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18098 true, NULL, 1, key);
18099
18100 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18101 {
18102 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18103 bool match = false;
18104
18105 /*
18106 * Match CHECK constraints by name, not-null constraints by column
18107 * number, and ignore all others.
18108 */
18109 if (con->contype == CONSTRAINT_CHECK)
18110 {
18111 foreach_ptr(char, chkname, connames)
18112 {
18113 if (con->contype == CONSTRAINT_CHECK &&
18114 strcmp(NameStr(con->conname), chkname) == 0)
18115 {
18116 match = true;
18117 connames = foreach_delete_current(connames, chkname);
18118 break;
18119 }
18120 }
18121 }
18122 else if (con->contype == CONSTRAINT_NOTNULL)
18123 {
18124 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18125
18126 foreach_int(prevattno, nncolumns)
18127 {
18128 if (prevattno == child_attno)
18129 {
18130 match = true;
18131 nncolumns = foreach_delete_current(nncolumns, prevattno);
18132 break;
18133 }
18134 }
18135 }
18136 else
18137 continue;
18138
18139 if (match)
18140 {
18141 /* Decrement inhcount and possibly set islocal to true */
18142 HeapTuple copyTuple = heap_copytuple(constraintTuple);
18143 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18144
18145 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18146 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18147 RelationGetRelid(child_rel), NameStr(copy_con->conname));
18148
18149 copy_con->coninhcount--;
18150 if (copy_con->coninhcount == 0)
18151 copy_con->conislocal = true;
18152
18153 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18154 heap_freetuple(copyTuple);
18155 }
18156 }
18157
18158 /* We should have matched all constraints */
18159 if (connames != NIL || nncolumns != NIL)
18160 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18161 list_length(connames) + list_length(nncolumns),
18162 RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18163
18164 systable_endscan(scan);
18165 table_close(catalogRelation, RowExclusiveLock);
18166
18168 RelationRelationId,
18169 RelationGetRelid(parent_rel),
18170 child_dependency_type(is_partitioning));
18171
18172 /*
18173 * Post alter hook of this inherits. Since object_access_hook doesn't take
18174 * multiple object identifiers, we relay oid of parent relation using
18175 * auxiliary_id argument.
18176 */
18177 InvokeObjectPostAlterHookArg(InheritsRelationId,
18178 RelationGetRelid(child_rel), 0,
18179 RelationGetRelid(parent_rel), false);
18180}
18181
18182/*
18183 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18184 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18185 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18186 * be TypeRelationId). There's no convenient way to do this, so go trawling
18187 * through pg_depend.
18188 */
18189static void
18190drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18191 DependencyType deptype)
18192{
18193 Relation catalogRelation;
18194 SysScanDesc scan;
18195 ScanKeyData key[3];
18196 HeapTuple depTuple;
18197
18198 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18199
18200 ScanKeyInit(&key[0],
18201 Anum_pg_depend_classid,
18202 BTEqualStrategyNumber, F_OIDEQ,
18203 ObjectIdGetDatum(RelationRelationId));
18204 ScanKeyInit(&key[1],
18205 Anum_pg_depend_objid,
18206 BTEqualStrategyNumber, F_OIDEQ,
18207 ObjectIdGetDatum(relid));
18208 ScanKeyInit(&key[2],
18209 Anum_pg_depend_objsubid,
18210 BTEqualStrategyNumber, F_INT4EQ,
18211 Int32GetDatum(0));
18212
18213 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18214 NULL, 3, key);
18215
18216 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18217 {
18218 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18219
18220 if (dep->refclassid == refclassid &&
18221 dep->refobjid == refobjid &&
18222 dep->refobjsubid == 0 &&
18223 dep->deptype == deptype)
18224 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18225 }
18226
18227 systable_endscan(scan);
18228 table_close(catalogRelation, RowExclusiveLock);
18229}
18230
18231/*
18232 * ALTER TABLE OF
18233 *
18234 * Attach a table to a composite type, as though it had been created with CREATE
18235 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18236 * subject table must not have inheritance parents. These restrictions ensure
18237 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18238 *
18239 * The address of the type is returned.
18240 */
18241static ObjectAddress
18242ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18243{
18244 Oid relid = RelationGetRelid(rel);
18245 Type typetuple;
18246 Form_pg_type typeform;
18247 Oid typeid;
18248 Relation inheritsRelation,
18249 relationRelation;
18250 SysScanDesc scan;
18252 AttrNumber table_attno,
18253 type_attno;
18254 TupleDesc typeTupleDesc,
18255 tableTupleDesc;
18256 ObjectAddress tableobj,
18257 typeobj;
18258 HeapTuple classtuple;
18259
18260 /* Validate the type. */
18261 typetuple = typenameType(NULL, ofTypename, NULL);
18262 check_of_type(typetuple);
18263 typeform = (Form_pg_type) GETSTRUCT(typetuple);
18264 typeid = typeform->oid;
18265
18266 /* Fail if the table has any inheritance parents. */
18267 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18269 Anum_pg_inherits_inhrelid,
18270 BTEqualStrategyNumber, F_OIDEQ,
18271 ObjectIdGetDatum(relid));
18272 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18273 true, NULL, 1, &key);
18275 ereport(ERROR,
18276 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18277 errmsg("typed tables cannot inherit")));
18278 systable_endscan(scan);
18279 table_close(inheritsRelation, AccessShareLock);
18280
18281 /*
18282 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18283 * require that the order also match. However, attnotnull need not match.
18284 */
18285 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18286 tableTupleDesc = RelationGetDescr(rel);
18287 table_attno = 1;
18288 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18289 {
18290 Form_pg_attribute type_attr,
18291 table_attr;
18292 const char *type_attname,
18293 *table_attname;
18294
18295 /* Get the next non-dropped type attribute. */
18296 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18297 if (type_attr->attisdropped)
18298 continue;
18299 type_attname = NameStr(type_attr->attname);
18300
18301 /* Get the next non-dropped table attribute. */
18302 do
18303 {
18304 if (table_attno > tableTupleDesc->natts)
18305 ereport(ERROR,
18306 (errcode(ERRCODE_DATATYPE_MISMATCH),
18307 errmsg("table is missing column \"%s\"",
18308 type_attname)));
18309 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18310 table_attno++;
18311 } while (table_attr->attisdropped);
18312 table_attname = NameStr(table_attr->attname);
18313
18314 /* Compare name. */
18315 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18316 ereport(ERROR,
18317 (errcode(ERRCODE_DATATYPE_MISMATCH),
18318 errmsg("table has column \"%s\" where type requires \"%s\"",
18319 table_attname, type_attname)));
18320
18321 /* Compare type. */
18322 if (table_attr->atttypid != type_attr->atttypid ||
18323 table_attr->atttypmod != type_attr->atttypmod ||
18324 table_attr->attcollation != type_attr->attcollation)
18325 ereport(ERROR,
18326 (errcode(ERRCODE_DATATYPE_MISMATCH),
18327 errmsg("table \"%s\" has different type for column \"%s\"",
18328 RelationGetRelationName(rel), type_attname)));
18329 }
18330 ReleaseTupleDesc(typeTupleDesc);
18331
18332 /* Any remaining columns at the end of the table had better be dropped. */
18333 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18334 {
18335 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18336 table_attno - 1);
18337
18338 if (!table_attr->attisdropped)
18339 ereport(ERROR,
18340 (errcode(ERRCODE_DATATYPE_MISMATCH),
18341 errmsg("table has extra column \"%s\"",
18342 NameStr(table_attr->attname))));
18343 }
18344
18345 /* If the table was already typed, drop the existing dependency. */
18346 if (rel->rd_rel->reloftype)
18347 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18349
18350 /* Record a dependency on the new type. */
18351 tableobj.classId = RelationRelationId;
18352 tableobj.objectId = relid;
18353 tableobj.objectSubId = 0;
18354 typeobj.classId = TypeRelationId;
18355 typeobj.objectId = typeid;
18356 typeobj.objectSubId = 0;
18357 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18358
18359 /* Update pg_class.reloftype */
18360 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18361 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18362 if (!HeapTupleIsValid(classtuple))
18363 elog(ERROR, "cache lookup failed for relation %u", relid);
18364 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18365 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18366
18367 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18368
18369 heap_freetuple(classtuple);
18370 table_close(relationRelation, RowExclusiveLock);
18371
18372 ReleaseSysCache(typetuple);
18373
18374 return typeobj;
18375}
18376
18377/*
18378 * ALTER TABLE NOT OF
18379 *
18380 * Detach a typed table from its originating type. Just clear reloftype and
18381 * remove the dependency.
18382 */
18383static void
18385{
18386 Oid relid = RelationGetRelid(rel);
18387 Relation relationRelation;
18388 HeapTuple tuple;
18389
18390 if (!OidIsValid(rel->rd_rel->reloftype))
18391 ereport(ERROR,
18392 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18393 errmsg("\"%s\" is not a typed table",
18395
18396 /*
18397 * We don't bother to check ownership of the type --- ownership of the
18398 * table is presumed enough rights. No lock required on the type, either.
18399 */
18400
18401 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18403
18404 /* Clear pg_class.reloftype */
18405 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18406 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18407 if (!HeapTupleIsValid(tuple))
18408 elog(ERROR, "cache lookup failed for relation %u", relid);
18409 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18410 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18411
18412 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18413
18414 heap_freetuple(tuple);
18415 table_close(relationRelation, RowExclusiveLock);
18416}
18417
18418/*
18419 * relation_mark_replica_identity: Update a table's replica identity
18420 *
18421 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18422 * index. Otherwise, it must be InvalidOid.
18423 *
18424 * Caller had better hold an exclusive lock on the relation, as the results
18425 * of running two of these concurrently wouldn't be pretty.
18426 */
18427static void
18428relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18429 bool is_internal)
18430{
18431 Relation pg_index;
18432 Relation pg_class;
18433 HeapTuple pg_class_tuple;
18434 HeapTuple pg_index_tuple;
18435 Form_pg_class pg_class_form;
18436 Form_pg_index pg_index_form;
18437 ListCell *index;
18438
18439 /*
18440 * Check whether relreplident has changed, and update it if so.
18441 */
18442 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18443 pg_class_tuple = SearchSysCacheCopy1(RELOID,
18445 if (!HeapTupleIsValid(pg_class_tuple))
18446 elog(ERROR, "cache lookup failed for relation \"%s\"",
18448 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18449 if (pg_class_form->relreplident != ri_type)
18450 {
18451 pg_class_form->relreplident = ri_type;
18452 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18453 }
18454 table_close(pg_class, RowExclusiveLock);
18455 heap_freetuple(pg_class_tuple);
18456
18457 /*
18458 * Update the per-index indisreplident flags correctly.
18459 */
18460 pg_index = table_open(IndexRelationId, RowExclusiveLock);
18461 foreach(index, RelationGetIndexList(rel))
18462 {
18463 Oid thisIndexOid = lfirst_oid(index);
18464 bool dirty = false;
18465
18466 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18467 ObjectIdGetDatum(thisIndexOid));
18468 if (!HeapTupleIsValid(pg_index_tuple))
18469 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18470 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18471
18472 if (thisIndexOid == indexOid)
18473 {
18474 /* Set the bit if not already set. */
18475 if (!pg_index_form->indisreplident)
18476 {
18477 dirty = true;
18478 pg_index_form->indisreplident = true;
18479 }
18480 }
18481 else
18482 {
18483 /* Unset the bit if set. */
18484 if (pg_index_form->indisreplident)
18485 {
18486 dirty = true;
18487 pg_index_form->indisreplident = false;
18488 }
18489 }
18490
18491 if (dirty)
18492 {
18493 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18494 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18495 InvalidOid, is_internal);
18496
18497 /*
18498 * Invalidate the relcache for the table, so that after we commit
18499 * all sessions will refresh the table's replica identity index
18500 * before attempting any UPDATE or DELETE on the table. (If we
18501 * changed the table's pg_class row above, then a relcache inval
18502 * is already queued due to that; but we might not have.)
18503 */
18505 }
18506 heap_freetuple(pg_index_tuple);
18507 }
18508
18509 table_close(pg_index, RowExclusiveLock);
18510}
18511
18512/*
18513 * ALTER TABLE <name> REPLICA IDENTITY ...
18514 */
18515static void
18517{
18518 Oid indexOid;
18519 Relation indexRel;
18520 int key;
18521
18522 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18523 {
18524 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18525 return;
18526 }
18527 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18528 {
18529 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18530 return;
18531 }
18532 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18533 {
18534 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18535 return;
18536 }
18537 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18538 {
18539 /* fallthrough */ ;
18540 }
18541 else
18542 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18543
18544 /* Check that the index exists */
18545 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18546 if (!OidIsValid(indexOid))
18547 ereport(ERROR,
18548 (errcode(ERRCODE_UNDEFINED_OBJECT),
18549 errmsg("index \"%s\" for table \"%s\" does not exist",
18550 stmt->name, RelationGetRelationName(rel))));
18551
18552 indexRel = index_open(indexOid, ShareLock);
18553
18554 /* Check that the index is on the relation we're altering. */
18555 if (indexRel->rd_index == NULL ||
18556 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18557 ereport(ERROR,
18558 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18559 errmsg("\"%s\" is not an index for table \"%s\"",
18560 RelationGetRelationName(indexRel),
18562
18563 /*
18564 * The AM must support uniqueness, and the index must in fact be unique.
18565 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18566 * exclusion), we can use that too.
18567 */
18568 if ((!indexRel->rd_indam->amcanunique ||
18569 !indexRel->rd_index->indisunique) &&
18570 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18571 ereport(ERROR,
18572 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18573 errmsg("cannot use non-unique index \"%s\" as replica identity",
18574 RelationGetRelationName(indexRel))));
18575 /* Deferred indexes are not guaranteed to be always unique. */
18576 if (!indexRel->rd_index->indimmediate)
18577 ereport(ERROR,
18578 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18579 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18580 RelationGetRelationName(indexRel))));
18581 /* Expression indexes aren't supported. */
18582 if (RelationGetIndexExpressions(indexRel) != NIL)
18583 ereport(ERROR,
18584 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18585 errmsg("cannot use expression index \"%s\" as replica identity",
18586 RelationGetRelationName(indexRel))));
18587 /* Predicate indexes aren't supported. */
18588 if (RelationGetIndexPredicate(indexRel) != NIL)
18589 ereport(ERROR,
18590 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18591 errmsg("cannot use partial index \"%s\" as replica identity",
18592 RelationGetRelationName(indexRel))));
18593
18594 /* Check index for nullable columns. */
18595 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18596 {
18597 int16 attno = indexRel->rd_index->indkey.values[key];
18598 Form_pg_attribute attr;
18599
18600 /*
18601 * Reject any other system columns. (Going forward, we'll disallow
18602 * indexes containing such columns in the first place, but they might
18603 * exist in older branches.)
18604 */
18605 if (attno <= 0)
18606 ereport(ERROR,
18607 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18608 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18609 RelationGetRelationName(indexRel), attno)));
18610
18611 attr = TupleDescAttr(rel->rd_att, attno - 1);
18612 if (!attr->attnotnull)
18613 ereport(ERROR,
18614 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18615 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18616 RelationGetRelationName(indexRel),
18617 NameStr(attr->attname))));
18618 }
18619
18620 /* This index is suitable for use as a replica identity. Mark it. */
18621 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18622
18623 index_close(indexRel, NoLock);
18624}
18625
18626/*
18627 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18628 */
18629static void
18631{
18632 Relation pg_class;
18633 Oid relid;
18634 HeapTuple tuple;
18635
18636 relid = RelationGetRelid(rel);
18637
18638 /* Pull the record for this relation and update it */
18639 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18640
18641 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18642
18643 if (!HeapTupleIsValid(tuple))
18644 elog(ERROR, "cache lookup failed for relation %u", relid);
18645
18646 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18647 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18648
18649 InvokeObjectPostAlterHook(RelationRelationId,
18650 RelationGetRelid(rel), 0);
18651
18652 table_close(pg_class, RowExclusiveLock);
18653 heap_freetuple(tuple);
18654}
18655
18656/*
18657 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18658 */
18659static void
18661{
18662 Relation pg_class;
18663 Oid relid;
18664 HeapTuple tuple;
18665
18666 relid = RelationGetRelid(rel);
18667
18668 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18669
18670 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18671
18672 if (!HeapTupleIsValid(tuple))
18673 elog(ERROR, "cache lookup failed for relation %u", relid);
18674
18675 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18676 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18677
18678 InvokeObjectPostAlterHook(RelationRelationId,
18679 RelationGetRelid(rel), 0);
18680
18681 table_close(pg_class, RowExclusiveLock);
18682 heap_freetuple(tuple);
18683}
18684
18685/*
18686 * ALTER FOREIGN TABLE <name> OPTIONS (...)
18687 */
18688static void
18690{
18691 Relation ftrel;
18692 ForeignServer *server;
18693 ForeignDataWrapper *fdw;
18694 HeapTuple tuple;
18695 bool isnull;
18696 Datum repl_val[Natts_pg_foreign_table];
18697 bool repl_null[Natts_pg_foreign_table];
18698 bool repl_repl[Natts_pg_foreign_table];
18699 Datum datum;
18700 Form_pg_foreign_table tableform;
18701
18702 if (options == NIL)
18703 return;
18704
18705 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18706
18707 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18708 ObjectIdGetDatum(rel->rd_id));
18709 if (!HeapTupleIsValid(tuple))
18710 ereport(ERROR,
18711 (errcode(ERRCODE_UNDEFINED_OBJECT),
18712 errmsg("foreign table \"%s\" does not exist",
18714 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18715 server = GetForeignServer(tableform->ftserver);
18716 fdw = GetForeignDataWrapper(server->fdwid);
18717
18718 memset(repl_val, 0, sizeof(repl_val));
18719 memset(repl_null, false, sizeof(repl_null));
18720 memset(repl_repl, false, sizeof(repl_repl));
18721
18722 /* Extract the current options */
18723 datum = SysCacheGetAttr(FOREIGNTABLEREL,
18724 tuple,
18725 Anum_pg_foreign_table_ftoptions,
18726 &isnull);
18727 if (isnull)
18728 datum = PointerGetDatum(NULL);
18729
18730 /* Transform the options */
18731 datum = transformGenericOptions(ForeignTableRelationId,
18732 datum,
18733 options,
18734 fdw->fdwvalidator);
18735
18736 if (DatumGetPointer(datum) != NULL)
18737 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18738 else
18739 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18740
18741 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18742
18743 /* Everything looks good - update the tuple */
18744
18745 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18746 repl_val, repl_null, repl_repl);
18747
18748 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18749
18750 /*
18751 * Invalidate relcache so that all sessions will refresh any cached plans
18752 * that might depend on the old options.
18753 */
18755
18756 InvokeObjectPostAlterHook(ForeignTableRelationId,
18757 RelationGetRelid(rel), 0);
18758
18760
18761 heap_freetuple(tuple);
18762}
18763
18764/*
18765 * ALTER TABLE ALTER COLUMN SET COMPRESSION
18766 *
18767 * Return value is the address of the modified column
18768 */
18769static ObjectAddress
18771 const char *column,
18772 Node *newValue,
18773 LOCKMODE lockmode)
18774{
18775 Relation attrel;
18776 HeapTuple tuple;
18777 Form_pg_attribute atttableform;
18779 char *compression;
18780 char cmethod;
18781 ObjectAddress address;
18782
18783 compression = strVal(newValue);
18784
18785 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18786
18787 /* copy the cache entry so we can scribble on it below */
18788 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18789 if (!HeapTupleIsValid(tuple))
18790 ereport(ERROR,
18791 (errcode(ERRCODE_UNDEFINED_COLUMN),
18792 errmsg("column \"%s\" of relation \"%s\" does not exist",
18793 column, RelationGetRelationName(rel))));
18794
18795 /* prevent them from altering a system attribute */
18796 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18797 attnum = atttableform->attnum;
18798 if (attnum <= 0)
18799 ereport(ERROR,
18800 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18801 errmsg("cannot alter system column \"%s\"", column)));
18802
18803 /*
18804 * Check that column type is compressible, then get the attribute
18805 * compression method code
18806 */
18807 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18808
18809 /* update pg_attribute entry */
18810 atttableform->attcompression = cmethod;
18811 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18812
18813 InvokeObjectPostAlterHook(RelationRelationId,
18814 RelationGetRelid(rel),
18815 attnum);
18816
18817 /*
18818 * Apply the change to indexes as well (only for simple index columns,
18819 * matching behavior of index.c ConstructTupleDescriptor()).
18820 */
18821 SetIndexStorageProperties(rel, attrel, attnum,
18822 false, 0,
18823 true, cmethod,
18824 lockmode);
18825
18826 heap_freetuple(tuple);
18827
18829
18830 /* make changes visible */
18832
18833 ObjectAddressSubSet(address, RelationRelationId,
18834 RelationGetRelid(rel), attnum);
18835 return address;
18836}
18837
18838
18839/*
18840 * Preparation phase for SET LOGGED/UNLOGGED
18841 *
18842 * This verifies that we're not trying to change a temp table. Also,
18843 * existing foreign key constraints are checked to avoid ending up with
18844 * permanent tables referencing unlogged tables.
18845 */
18846static void
18848{
18849 Relation pg_constraint;
18850 HeapTuple tuple;
18851 SysScanDesc scan;
18852 ScanKeyData skey[1];
18853
18854 /*
18855 * Disallow changing status for a temp table. Also verify whether we can
18856 * get away with doing nothing; in such cases we don't need to run the
18857 * checks below, either.
18858 */
18859 switch (rel->rd_rel->relpersistence)
18860 {
18861 case RELPERSISTENCE_TEMP:
18862 ereport(ERROR,
18863 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18864 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18866 errtable(rel)));
18867 break;
18868 case RELPERSISTENCE_PERMANENT:
18869 if (toLogged)
18870 /* nothing to do */
18871 return;
18872 break;
18873 case RELPERSISTENCE_UNLOGGED:
18874 if (!toLogged)
18875 /* nothing to do */
18876 return;
18877 break;
18878 }
18879
18880 /*
18881 * Check that the table is not part of any publication when changing to
18882 * UNLOGGED, as UNLOGGED tables can't be published.
18883 */
18884 if (!toLogged &&
18886 ereport(ERROR,
18887 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18888 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18890 errdetail("Unlogged relations cannot be replicated.")));
18891
18892 /*
18893 * Check existing foreign key constraints to preserve the invariant that
18894 * permanent tables cannot reference unlogged ones. Self-referencing
18895 * foreign keys can safely be ignored.
18896 */
18897 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18898
18899 /*
18900 * Scan conrelid if changing to permanent, else confrelid. This also
18901 * determines whether a useful index exists.
18902 */
18903 ScanKeyInit(&skey[0],
18904 toLogged ? Anum_pg_constraint_conrelid :
18905 Anum_pg_constraint_confrelid,
18906 BTEqualStrategyNumber, F_OIDEQ,
18908 scan = systable_beginscan(pg_constraint,
18909 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18910 true, NULL, 1, skey);
18911
18912 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18913 {
18915
18916 if (con->contype == CONSTRAINT_FOREIGN)
18917 {
18918 Oid foreignrelid;
18919 Relation foreignrel;
18920
18921 /* the opposite end of what we used as scankey */
18922 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18923
18924 /* ignore if self-referencing */
18925 if (RelationGetRelid(rel) == foreignrelid)
18926 continue;
18927
18928 foreignrel = relation_open(foreignrelid, AccessShareLock);
18929
18930 if (toLogged)
18931 {
18932 if (!RelationIsPermanent(foreignrel))
18933 ereport(ERROR,
18934 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18935 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18937 RelationGetRelationName(foreignrel)),
18938 errtableconstraint(rel, NameStr(con->conname))));
18939 }
18940 else
18941 {
18942 if (RelationIsPermanent(foreignrel))
18943 ereport(ERROR,
18944 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18945 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18947 RelationGetRelationName(foreignrel)),
18948 errtableconstraint(rel, NameStr(con->conname))));
18949 }
18950
18951 relation_close(foreignrel, AccessShareLock);
18952 }
18953 }
18954
18955 systable_endscan(scan);
18956
18957 table_close(pg_constraint, AccessShareLock);
18958
18959 /* force rewrite if necessary; see comment in ATRewriteTables */
18961 if (toLogged)
18962 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18963 else
18964 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18965 tab->chgPersistence = true;
18966}
18967
18968/*
18969 * Execute ALTER TABLE SET SCHEMA
18970 */
18973{
18974 Relation rel;
18975 Oid relid;
18976 Oid oldNspOid;
18977 Oid nspOid;
18978 RangeVar *newrv;
18979 ObjectAddresses *objsMoved;
18980 ObjectAddress myself;
18981
18983 stmt->missing_ok ? RVR_MISSING_OK : 0,
18985 stmt);
18986
18987 if (!OidIsValid(relid))
18988 {
18990 (errmsg("relation \"%s\" does not exist, skipping",
18991 stmt->relation->relname)));
18992 return InvalidObjectAddress;
18993 }
18994
18995 rel = relation_open(relid, NoLock);
18996
18997 oldNspOid = RelationGetNamespace(rel);
18998
18999 /* If it's an owned sequence, disallow moving it by itself. */
19000 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19001 {
19002 Oid tableId;
19003 int32 colId;
19004
19005 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19006 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
19007 ereport(ERROR,
19008 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19009 errmsg("cannot move an owned sequence into another schema"),
19010 errdetail("Sequence \"%s\" is linked to table \"%s\".",
19012 get_rel_name(tableId))));
19013 }
19014
19015 /* Get and lock schema OID and check its permissions. */
19016 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19017 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
19018
19019 /* common checks on switching namespaces */
19020 CheckSetNamespace(oldNspOid, nspOid);
19021
19022 objsMoved = new_object_addresses();
19023 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
19024 free_object_addresses(objsMoved);
19025
19026 ObjectAddressSet(myself, RelationRelationId, relid);
19027
19028 if (oldschema)
19029 *oldschema = oldNspOid;
19030
19031 /* close rel, but keep lock until commit */
19032 relation_close(rel, NoLock);
19033
19034 return myself;
19035}
19036
19037/*
19038 * The guts of relocating a table or materialized view to another namespace:
19039 * besides moving the relation itself, its dependent objects are relocated to
19040 * the new schema.
19041 */
19042void
19044 ObjectAddresses *objsMoved)
19045{
19046 Relation classRel;
19047
19048 Assert(objsMoved != NULL);
19049
19050 /* OK, modify the pg_class row and pg_depend entry */
19051 classRel = table_open(RelationRelationId, RowExclusiveLock);
19052
19053 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
19054 nspOid, true, objsMoved);
19055
19056 /* Fix the table's row type too, if it has one */
19057 if (OidIsValid(rel->rd_rel->reltype))
19058 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
19059 false, /* isImplicitArray */
19060 false, /* ignoreDependent */
19061 false, /* errorOnTableType */
19062 objsMoved);
19063
19064 /* Fix other dependent stuff */
19065 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19066 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19067 objsMoved, AccessExclusiveLock);
19068 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19069 false, objsMoved);
19070
19071 table_close(classRel, RowExclusiveLock);
19072}
19073
19074/*
19075 * The guts of relocating a relation to another namespace: fix the pg_class
19076 * entry, and the pg_depend entry if any. Caller must already have
19077 * opened and write-locked pg_class.
19078 */
19079void
19081 Oid oldNspOid, Oid newNspOid,
19082 bool hasDependEntry,
19083 ObjectAddresses *objsMoved)
19084{
19085 HeapTuple classTup;
19086 Form_pg_class classForm;
19087 ObjectAddress thisobj;
19088 bool already_done = false;
19089
19090 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19091 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19092 if (!HeapTupleIsValid(classTup))
19093 elog(ERROR, "cache lookup failed for relation %u", relOid);
19094 classForm = (Form_pg_class) GETSTRUCT(classTup);
19095
19096 Assert(classForm->relnamespace == oldNspOid);
19097
19098 thisobj.classId = RelationRelationId;
19099 thisobj.objectId = relOid;
19100 thisobj.objectSubId = 0;
19101
19102 /*
19103 * If the object has already been moved, don't move it again. If it's
19104 * already in the right place, don't move it, but still fire the object
19105 * access hook.
19106 */
19107 already_done = object_address_present(&thisobj, objsMoved);
19108 if (!already_done && oldNspOid != newNspOid)
19109 {
19110 ItemPointerData otid = classTup->t_self;
19111
19112 /* check for duplicate name (more friendly than unique-index failure) */
19113 if (get_relname_relid(NameStr(classForm->relname),
19114 newNspOid) != InvalidOid)
19115 ereport(ERROR,
19116 (errcode(ERRCODE_DUPLICATE_TABLE),
19117 errmsg("relation \"%s\" already exists in schema \"%s\"",
19118 NameStr(classForm->relname),
19119 get_namespace_name(newNspOid))));
19120
19121 /* classTup is a copy, so OK to scribble on */
19122 classForm->relnamespace = newNspOid;
19123
19124 CatalogTupleUpdate(classRel, &otid, classTup);
19125 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19126
19127
19128 /* Update dependency on schema if caller said so */
19129 if (hasDependEntry &&
19130 changeDependencyFor(RelationRelationId,
19131 relOid,
19132 NamespaceRelationId,
19133 oldNspOid,
19134 newNspOid) != 1)
19135 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19136 NameStr(classForm->relname));
19137 }
19138 else
19139 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19140 if (!already_done)
19141 {
19142 add_exact_object_address(&thisobj, objsMoved);
19143
19144 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19145 }
19146
19147 heap_freetuple(classTup);
19148}
19149
19150/*
19151 * Move all indexes for the specified relation to another namespace.
19152 *
19153 * Note: we assume adequate permission checking was done by the caller,
19154 * and that the caller has a suitable lock on the owning relation.
19155 */
19156static void
19158 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19159{
19160 List *indexList;
19161 ListCell *l;
19162
19163 indexList = RelationGetIndexList(rel);
19164
19165 foreach(l, indexList)
19166 {
19167 Oid indexOid = lfirst_oid(l);
19168 ObjectAddress thisobj;
19169
19170 thisobj.classId = RelationRelationId;
19171 thisobj.objectId = indexOid;
19172 thisobj.objectSubId = 0;
19173
19174 /*
19175 * Note: currently, the index will not have its own dependency on the
19176 * namespace, so we don't need to do changeDependencyFor(). There's no
19177 * row type in pg_type, either.
19178 *
19179 * XXX this objsMoved test may be pointless -- surely we have a single
19180 * dependency link from a relation to each index?
19181 */
19182 if (!object_address_present(&thisobj, objsMoved))
19183 {
19184 AlterRelationNamespaceInternal(classRel, indexOid,
19185 oldNspOid, newNspOid,
19186 false, objsMoved);
19187 add_exact_object_address(&thisobj, objsMoved);
19188 }
19189 }
19190
19191 list_free(indexList);
19192}
19193
19194/*
19195 * Move all identity and SERIAL-column sequences of the specified relation to another
19196 * namespace.
19197 *
19198 * Note: we assume adequate permission checking was done by the caller,
19199 * and that the caller has a suitable lock on the owning relation.
19200 */
19201static void
19203 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19204 LOCKMODE lockmode)
19205{
19206 Relation depRel;
19207 SysScanDesc scan;
19208 ScanKeyData key[2];
19209 HeapTuple tup;
19210
19211 /*
19212 * SERIAL sequences are those having an auto dependency on one of the
19213 * table's columns (we don't care *which* column, exactly).
19214 */
19215 depRel = table_open(DependRelationId, AccessShareLock);
19216
19217 ScanKeyInit(&key[0],
19218 Anum_pg_depend_refclassid,
19219 BTEqualStrategyNumber, F_OIDEQ,
19220 ObjectIdGetDatum(RelationRelationId));
19221 ScanKeyInit(&key[1],
19222 Anum_pg_depend_refobjid,
19223 BTEqualStrategyNumber, F_OIDEQ,
19225 /* we leave refobjsubid unspecified */
19226
19227 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19228 NULL, 2, key);
19229
19230 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19231 {
19232 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19233 Relation seqRel;
19234
19235 /* skip dependencies other than auto dependencies on columns */
19236 if (depForm->refobjsubid == 0 ||
19237 depForm->classid != RelationRelationId ||
19238 depForm->objsubid != 0 ||
19239 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19240 continue;
19241
19242 /* Use relation_open just in case it's an index */
19243 seqRel = relation_open(depForm->objid, lockmode);
19244
19245 /* skip non-sequence relations */
19246 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19247 {
19248 /* No need to keep the lock */
19249 relation_close(seqRel, lockmode);
19250 continue;
19251 }
19252
19253 /* Fix the pg_class and pg_depend entries */
19254 AlterRelationNamespaceInternal(classRel, depForm->objid,
19255 oldNspOid, newNspOid,
19256 true, objsMoved);
19257
19258 /*
19259 * Sequences used to have entries in pg_type, but no longer do. If we
19260 * ever re-instate that, we'll need to move the pg_type entry to the
19261 * new namespace, too (using AlterTypeNamespaceInternal).
19262 */
19263 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19264
19265 /* Now we can close it. Keep the lock till end of transaction. */
19266 relation_close(seqRel, NoLock);
19267 }
19268
19269 systable_endscan(scan);
19270
19272}
19273
19274
19275/*
19276 * This code supports
19277 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19278 *
19279 * Because we only support this for TEMP tables, it's sufficient to remember
19280 * the state in a backend-local data structure.
19281 */
19282
19283/*
19284 * Register a newly-created relation's ON COMMIT action.
19285 */
19286void
19288{
19289 OnCommitItem *oc;
19290 MemoryContext oldcxt;
19291
19292 /*
19293 * We needn't bother registering the relation unless there is an ON COMMIT
19294 * action we need to take.
19295 */
19297 return;
19298
19300
19302 oc->relid = relid;
19303 oc->oncommit = action;
19306
19307 /*
19308 * We use lcons() here so that ON COMMIT actions are processed in reverse
19309 * order of registration. That might not be essential but it seems
19310 * reasonable.
19311 */
19313
19314 MemoryContextSwitchTo(oldcxt);
19315}
19316
19317/*
19318 * Unregister any ON COMMIT action when a relation is deleted.
19319 *
19320 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19321 */
19322void
19324{
19325 ListCell *l;
19326
19327 foreach(l, on_commits)
19328 {
19329 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19330
19331 if (oc->relid == relid)
19332 {
19334 break;
19335 }
19336 }
19337}
19338
19339/*
19340 * Perform ON COMMIT actions.
19341 *
19342 * This is invoked just before actually committing, since it's possible
19343 * to encounter errors.
19344 */
19345void
19347{
19348 ListCell *l;
19349 List *oids_to_truncate = NIL;
19350 List *oids_to_drop = NIL;
19351
19352 foreach(l, on_commits)
19353 {
19354 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19355
19356 /* Ignore entry if already dropped in this xact */
19358 continue;
19359
19360 switch (oc->oncommit)
19361 {
19362 case ONCOMMIT_NOOP:
19364 /* Do nothing (there shouldn't be such entries, actually) */
19365 break;
19367
19368 /*
19369 * If this transaction hasn't accessed any temporary
19370 * relations, we can skip truncating ON COMMIT DELETE ROWS
19371 * tables, as they must still be empty.
19372 */
19374 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19375 break;
19376 case ONCOMMIT_DROP:
19377 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19378 break;
19379 }
19380 }
19381
19382 /*
19383 * Truncate relations before dropping so that all dependencies between
19384 * relations are removed after they are worked on. Doing it like this
19385 * might be a waste as it is possible that a relation being truncated will
19386 * be dropped anyway due to its parent being dropped, but this makes the
19387 * code more robust because of not having to re-check that the relation
19388 * exists at truncation time.
19389 */
19390 if (oids_to_truncate != NIL)
19391 heap_truncate(oids_to_truncate);
19392
19393 if (oids_to_drop != NIL)
19394 {
19395 ObjectAddresses *targetObjects = new_object_addresses();
19396
19397 foreach(l, oids_to_drop)
19398 {
19399 ObjectAddress object;
19400
19401 object.classId = RelationRelationId;
19402 object.objectId = lfirst_oid(l);
19403 object.objectSubId = 0;
19404
19405 Assert(!object_address_present(&object, targetObjects));
19406
19407 add_exact_object_address(&object, targetObjects);
19408 }
19409
19410 /*
19411 * Object deletion might involve toast table access (to clean up
19412 * toasted catalog entries), so ensure we have a valid snapshot.
19413 */
19415
19416 /*
19417 * Since this is an automatic drop, rather than one directly initiated
19418 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19419 */
19422
19424
19425#ifdef USE_ASSERT_CHECKING
19426
19427 /*
19428 * Note that table deletion will call remove_on_commit_action, so the
19429 * entry should get marked as deleted.
19430 */
19431 foreach(l, on_commits)
19432 {
19433 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19434
19435 if (oc->oncommit != ONCOMMIT_DROP)
19436 continue;
19437
19439 }
19440#endif
19441 }
19442}
19443
19444/*
19445 * Post-commit or post-abort cleanup for ON COMMIT management.
19446 *
19447 * All we do here is remove no-longer-needed OnCommitItem entries.
19448 *
19449 * During commit, remove entries that were deleted during this transaction;
19450 * during abort, remove those created during this transaction.
19451 */
19452void
19454{
19455 ListCell *cur_item;
19456
19457 foreach(cur_item, on_commits)
19458 {
19459 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19460
19461 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19463 {
19464 /* cur_item must be removed */
19466 pfree(oc);
19467 }
19468 else
19469 {
19470 /* cur_item must be preserved */
19473 }
19474 }
19475}
19476
19477/*
19478 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19479 *
19480 * During subabort, we can immediately remove entries created during this
19481 * subtransaction. During subcommit, just relabel entries marked during
19482 * this subtransaction as being the parent's responsibility.
19483 */
19484void
19486 SubTransactionId parentSubid)
19487{
19488 ListCell *cur_item;
19489
19490 foreach(cur_item, on_commits)
19491 {
19492 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19493
19494 if (!isCommit && oc->creating_subid == mySubid)
19495 {
19496 /* cur_item must be removed */
19498 pfree(oc);
19499 }
19500 else
19501 {
19502 /* cur_item must be preserved */
19503 if (oc->creating_subid == mySubid)
19504 oc->creating_subid = parentSubid;
19505 if (oc->deleting_subid == mySubid)
19506 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19507 }
19508 }
19509}
19510
19511/*
19512 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19513 * the relation to be locked only if (1) it's a plain or partitioned table,
19514 * materialized view, or TOAST table and (2) the current user is the owner (or
19515 * the superuser) or has been granted MAINTAIN. This meets the
19516 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19517 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19518 */
19519void
19521 Oid relId, Oid oldRelId, void *arg)
19522{
19523 char relkind;
19524 AclResult aclresult;
19525
19526 /* Nothing to do if the relation was not found. */
19527 if (!OidIsValid(relId))
19528 return;
19529
19530 /*
19531 * If the relation does exist, check whether it's an index. But note that
19532 * the relation might have been dropped between the time we did the name
19533 * lookup and now. In that case, there's nothing to do.
19534 */
19535 relkind = get_rel_relkind(relId);
19536 if (!relkind)
19537 return;
19538 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19539 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19540 ereport(ERROR,
19541 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19542 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19543
19544 /* Check permissions */
19545 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19546 if (aclresult != ACLCHECK_OK)
19547 aclcheck_error(aclresult,
19549 relation->relname);
19550}
19551
19552/*
19553 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19554 */
19555static void
19557 Oid relId, Oid oldRelId, void *arg)
19558{
19559 HeapTuple tuple;
19560
19561 /* Nothing to do if the relation was not found. */
19562 if (!OidIsValid(relId))
19563 return;
19564
19565 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19566 if (!HeapTupleIsValid(tuple)) /* should not happen */
19567 elog(ERROR, "cache lookup failed for relation %u", relId);
19568
19571
19572 ReleaseSysCache(tuple);
19573}
19574
19575/*
19576 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19577 * the owner of the relation, or superuser.
19578 */
19579void
19581 Oid relId, Oid oldRelId, void *arg)
19582{
19583 HeapTuple tuple;
19584
19585 /* Nothing to do if the relation was not found. */
19586 if (!OidIsValid(relId))
19587 return;
19588
19589 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19590 if (!HeapTupleIsValid(tuple)) /* should not happen */
19591 elog(ERROR, "cache lookup failed for relation %u", relId);
19592
19593 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19595 relation->relname);
19596
19597 if (!allowSystemTableMods &&
19598 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19599 ereport(ERROR,
19600 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19601 errmsg("permission denied: \"%s\" is a system catalog",
19602 relation->relname)));
19603
19604 ReleaseSysCache(tuple);
19605}
19606
19607/*
19608 * Common RangeVarGetRelid callback for rename, set schema, and alter table
19609 * processing.
19610 */
19611static void
19613 void *arg)
19614{
19615 Node *stmt = (Node *) arg;
19616 ObjectType reltype;
19617 HeapTuple tuple;
19618 Form_pg_class classform;
19619 AclResult aclresult;
19620 char relkind;
19621
19622 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19623 if (!HeapTupleIsValid(tuple))
19624 return; /* concurrently dropped */
19625 classform = (Form_pg_class) GETSTRUCT(tuple);
19626 relkind = classform->relkind;
19627
19628 /* Must own relation. */
19629 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19631
19632 /* No system table modifications unless explicitly allowed. */
19633 if (!allowSystemTableMods && IsSystemClass(relid, classform))
19634 ereport(ERROR,
19635 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19636 errmsg("permission denied: \"%s\" is a system catalog",
19637 rv->relname)));
19638
19639 /*
19640 * Extract the specified relation type from the statement parse tree.
19641 *
19642 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19643 * have CREATE rights on the containing namespace.
19644 */
19645 if (IsA(stmt, RenameStmt))
19646 {
19647 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19649 if (aclresult != ACLCHECK_OK)
19650 aclcheck_error(aclresult, OBJECT_SCHEMA,
19651 get_namespace_name(classform->relnamespace));
19652 reltype = ((RenameStmt *) stmt)->renameType;
19653 }
19654 else if (IsA(stmt, AlterObjectSchemaStmt))
19655 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19656
19657 else if (IsA(stmt, AlterTableStmt))
19658 reltype = ((AlterTableStmt *) stmt)->objtype;
19659 else
19660 {
19661 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19662 reltype = OBJECT_TABLE; /* placate compiler */
19663 }
19664
19665 /*
19666 * For compatibility with prior releases, we allow ALTER TABLE to be used
19667 * with most other types of relations (but not composite types). We allow
19668 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19669 * otherwise. Otherwise, the user must select the correct form of the
19670 * command for the relation at issue.
19671 */
19672 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19673 ereport(ERROR,
19674 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19675 errmsg("\"%s\" is not a sequence", rv->relname)));
19676
19677 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19678 ereport(ERROR,
19679 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19680 errmsg("\"%s\" is not a view", rv->relname)));
19681
19682 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19683 ereport(ERROR,
19684 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19685 errmsg("\"%s\" is not a materialized view", rv->relname)));
19686
19687 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19688 ereport(ERROR,
19689 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19690 errmsg("\"%s\" is not a foreign table", rv->relname)));
19691
19692 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19693 ereport(ERROR,
19694 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19695 errmsg("\"%s\" is not a composite type", rv->relname)));
19696
19697 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19698 relkind != RELKIND_PARTITIONED_INDEX
19699 && !IsA(stmt, RenameStmt))
19700 ereport(ERROR,
19701 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19702 errmsg("\"%s\" is not an index", rv->relname)));
19703
19704 /*
19705 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19706 * TYPE for that.
19707 */
19708 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19709 ereport(ERROR,
19710 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19711 errmsg("\"%s\" is a composite type", rv->relname),
19712 /* translator: %s is an SQL ALTER command */
19713 errhint("Use %s instead.",
19714 "ALTER TYPE")));
19715
19716 /*
19717 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19718 * to a different schema, such as indexes and TOAST tables.
19719 */
19721 {
19722 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19723 ereport(ERROR,
19724 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19725 errmsg("cannot change schema of index \"%s\"",
19726 rv->relname),
19727 errhint("Change the schema of the table instead.")));
19728 else if (relkind == RELKIND_COMPOSITE_TYPE)
19729 ereport(ERROR,
19730 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19731 errmsg("cannot change schema of composite type \"%s\"",
19732 rv->relname),
19733 /* translator: %s is an SQL ALTER command */
19734 errhint("Use %s instead.",
19735 "ALTER TYPE")));
19736 else if (relkind == RELKIND_TOASTVALUE)
19737 ereport(ERROR,
19738 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19739 errmsg("cannot change schema of TOAST table \"%s\"",
19740 rv->relname),
19741 errhint("Change the schema of the table instead.")));
19742 }
19743
19744 ReleaseSysCache(tuple);
19745}
19746
19747/*
19748 * Transform any expressions present in the partition key
19749 *
19750 * Returns a transformed PartitionSpec.
19751 */
19752static PartitionSpec *
19754{
19755 PartitionSpec *newspec;
19756 ParseState *pstate;
19757 ParseNamespaceItem *nsitem;
19758 ListCell *l;
19759
19760 newspec = makeNode(PartitionSpec);
19761
19762 newspec->strategy = partspec->strategy;
19763 newspec->partParams = NIL;
19764 newspec->location = partspec->location;
19765
19766 /* Check valid number of columns for strategy */
19767 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19768 list_length(partspec->partParams) != 1)
19769 ereport(ERROR,
19770 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19771 errmsg("cannot use \"list\" partition strategy with more than one column")));
19772
19773 /*
19774 * Create a dummy ParseState and insert the target relation as its sole
19775 * rangetable entry. We need a ParseState for transformExpr.
19776 */
19777 pstate = make_parsestate(NULL);
19778 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19779 NULL, false, true);
19780 addNSItemToQuery(pstate, nsitem, true, true, true);
19781
19782 /* take care of any partition expressions */
19783 foreach(l, partspec->partParams)
19784 {
19786
19787 if (pelem->expr)
19788 {
19789 /* Copy, to avoid scribbling on the input */
19790 pelem = copyObject(pelem);
19791
19792 /* Now do parse transformation of the expression */
19793 pelem->expr = transformExpr(pstate, pelem->expr,
19795
19796 /* we have to fix its collations too */
19797 assign_expr_collations(pstate, pelem->expr);
19798 }
19799
19800 newspec->partParams = lappend(newspec->partParams, pelem);
19801 }
19802
19803 return newspec;
19804}
19805
19806/*
19807 * Compute per-partition-column information from a list of PartitionElems.
19808 * Expressions in the PartitionElems must be parse-analyzed already.
19809 */
19810static void
19811ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19812 List **partexprs, Oid *partopclass, Oid *partcollation,
19813 PartitionStrategy strategy)
19814{
19815 int attn;
19816 ListCell *lc;
19817 Oid am_oid;
19818
19819 attn = 0;
19820 foreach(lc, partParams)
19821 {
19823 Oid atttype;
19824 Oid attcollation;
19825
19826 if (pelem->name != NULL)
19827 {
19828 /* Simple attribute reference */
19829 HeapTuple atttuple;
19830 Form_pg_attribute attform;
19831
19833 pelem->name);
19834 if (!HeapTupleIsValid(atttuple))
19835 ereport(ERROR,
19836 (errcode(ERRCODE_UNDEFINED_COLUMN),
19837 errmsg("column \"%s\" named in partition key does not exist",
19838 pelem->name),
19839 parser_errposition(pstate, pelem->location)));
19840 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19841
19842 if (attform->attnum <= 0)
19843 ereport(ERROR,
19844 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19845 errmsg("cannot use system column \"%s\" in partition key",
19846 pelem->name),
19847 parser_errposition(pstate, pelem->location)));
19848
19849 /*
19850 * Stored generated columns cannot work: They are computed after
19851 * BEFORE triggers, but partition routing is done before all
19852 * triggers. Maybe virtual generated columns could be made to
19853 * work, but then they would need to be handled as an expression
19854 * below.
19855 */
19856 if (attform->attgenerated)
19857 ereport(ERROR,
19858 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19859 errmsg("cannot use generated column in partition key"),
19860 errdetail("Column \"%s\" is a generated column.",
19861 pelem->name),
19862 parser_errposition(pstate, pelem->location)));
19863
19864 partattrs[attn] = attform->attnum;
19865 atttype = attform->atttypid;
19866 attcollation = attform->attcollation;
19867 ReleaseSysCache(atttuple);
19868 }
19869 else
19870 {
19871 /* Expression */
19872 Node *expr = pelem->expr;
19873 char partattname[16];
19874 Bitmapset *expr_attrs = NULL;
19875 int i;
19876
19877 Assert(expr != NULL);
19878 atttype = exprType(expr);
19879 attcollation = exprCollation(expr);
19880
19881 /*
19882 * The expression must be of a storable type (e.g., not RECORD).
19883 * The test is the same as for whether a table column is of a safe
19884 * type (which is why we needn't check for the non-expression
19885 * case).
19886 */
19887 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19888 CheckAttributeType(partattname,
19889 atttype, attcollation,
19891
19892 /*
19893 * Strip any top-level COLLATE clause. This ensures that we treat
19894 * "x COLLATE y" and "(x COLLATE y)" alike.
19895 */
19896 while (IsA(expr, CollateExpr))
19897 expr = (Node *) ((CollateExpr *) expr)->arg;
19898
19899 /*
19900 * Examine all the columns in the partition key expression. When
19901 * the whole-row reference is present, examine all the columns of
19902 * the partitioned table.
19903 */
19904 pull_varattnos(expr, 1, &expr_attrs);
19906 {
19907 expr_attrs = bms_add_range(expr_attrs,
19910 expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
19911 }
19912
19913 i = -1;
19914 while ((i = bms_next_member(expr_attrs, i)) >= 0)
19915 {
19917
19918 Assert(attno != 0);
19919
19920 /*
19921 * Cannot allow system column references, since that would
19922 * make partition routing impossible: their values won't be
19923 * known yet when we need to do that.
19924 */
19925 if (attno < 0)
19926 ereport(ERROR,
19927 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19928 errmsg("partition key expressions cannot contain system column references")));
19929
19930 /*
19931 * Stored generated columns cannot work: They are computed
19932 * after BEFORE triggers, but partition routing is done before
19933 * all triggers. Virtual generated columns could probably
19934 * work, but it would require more work elsewhere (for example
19935 * SET EXPRESSION would need to check whether the column is
19936 * used in partition keys). Seems safer to prohibit for now.
19937 */
19938 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19939 ereport(ERROR,
19940 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19941 errmsg("cannot use generated column in partition key"),
19942 errdetail("Column \"%s\" is a generated column.",
19943 get_attname(RelationGetRelid(rel), attno, false)),
19944 parser_errposition(pstate, pelem->location)));
19945 }
19946
19947 if (IsA(expr, Var) &&
19948 ((Var *) expr)->varattno > 0)
19949 {
19950
19951 /*
19952 * User wrote "(column)" or "(column COLLATE something)".
19953 * Treat it like simple attribute anyway.
19954 */
19955 partattrs[attn] = ((Var *) expr)->varattno;
19956 }
19957 else
19958 {
19959 partattrs[attn] = 0; /* marks the column as expression */
19960 *partexprs = lappend(*partexprs, expr);
19961
19962 /*
19963 * transformPartitionSpec() should have already rejected
19964 * subqueries, aggregates, window functions, and SRFs, based
19965 * on the EXPR_KIND_ for partition expressions.
19966 */
19967
19968 /*
19969 * Preprocess the expression before checking for mutability.
19970 * This is essential for the reasons described in
19971 * contain_mutable_functions_after_planning. However, we call
19972 * expression_planner for ourselves rather than using that
19973 * function, because if constant-folding reduces the
19974 * expression to a constant, we'd like to know that so we can
19975 * complain below.
19976 *
19977 * Like contain_mutable_functions_after_planning, assume that
19978 * expression_planner won't scribble on its input, so this
19979 * won't affect the partexprs entry we saved above.
19980 */
19981 expr = (Node *) expression_planner((Expr *) expr);
19982
19983 /*
19984 * Partition expressions cannot contain mutable functions,
19985 * because a given row must always map to the same partition
19986 * as long as there is no change in the partition boundary
19987 * structure.
19988 */
19989 if (contain_mutable_functions(expr))
19990 ereport(ERROR,
19991 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19992 errmsg("functions in partition key expression must be marked IMMUTABLE")));
19993
19994 /*
19995 * While it is not exactly *wrong* for a partition expression
19996 * to be a constant, it seems better to reject such keys.
19997 */
19998 if (IsA(expr, Const))
19999 ereport(ERROR,
20000 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20001 errmsg("cannot use constant expression as partition key")));
20002 }
20003 }
20004
20005 /*
20006 * Apply collation override if any
20007 */
20008 if (pelem->collation)
20009 attcollation = get_collation_oid(pelem->collation, false);
20010
20011 /*
20012 * Check we have a collation iff it's a collatable type. The only
20013 * expected failures here are (1) COLLATE applied to a noncollatable
20014 * type, or (2) partition expression had an unresolved collation. But
20015 * we might as well code this to be a complete consistency check.
20016 */
20017 if (type_is_collatable(atttype))
20018 {
20019 if (!OidIsValid(attcollation))
20020 ereport(ERROR,
20021 (errcode(ERRCODE_INDETERMINATE_COLLATION),
20022 errmsg("could not determine which collation to use for partition expression"),
20023 errhint("Use the COLLATE clause to set the collation explicitly.")));
20024 }
20025 else
20026 {
20027 if (OidIsValid(attcollation))
20028 ereport(ERROR,
20029 (errcode(ERRCODE_DATATYPE_MISMATCH),
20030 errmsg("collations are not supported by type %s",
20031 format_type_be(atttype))));
20032 }
20033
20034 partcollation[attn] = attcollation;
20035
20036 /*
20037 * Identify the appropriate operator class. For list and range
20038 * partitioning, we use a btree operator class; hash partitioning uses
20039 * a hash operator class.
20040 */
20041 if (strategy == PARTITION_STRATEGY_HASH)
20042 am_oid = HASH_AM_OID;
20043 else
20044 am_oid = BTREE_AM_OID;
20045
20046 if (!pelem->opclass)
20047 {
20048 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20049
20050 if (!OidIsValid(partopclass[attn]))
20051 {
20052 if (strategy == PARTITION_STRATEGY_HASH)
20053 ereport(ERROR,
20054 (errcode(ERRCODE_UNDEFINED_OBJECT),
20055 errmsg("data type %s has no default operator class for access method \"%s\"",
20056 format_type_be(atttype), "hash"),
20057 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20058 else
20059 ereport(ERROR,
20060 (errcode(ERRCODE_UNDEFINED_OBJECT),
20061 errmsg("data type %s has no default operator class for access method \"%s\"",
20062 format_type_be(atttype), "btree"),
20063 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20064 }
20065 }
20066 else
20067 partopclass[attn] = ResolveOpClass(pelem->opclass,
20068 atttype,
20069 am_oid == HASH_AM_OID ? "hash" : "btree",
20070 am_oid);
20071
20072 attn++;
20073 }
20074}
20075
20076/*
20077 * PartConstraintImpliedByRelConstraint
20078 * Do scanrel's existing constraints imply the partition constraint?
20079 *
20080 * "Existing constraints" include its check constraints and column-level
20081 * not-null constraints. partConstraint describes the partition constraint,
20082 * in implicit-AND form.
20083 */
20084bool
20086 List *partConstraint)
20087{
20088 List *existConstraint = NIL;
20089 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20090 int i;
20091
20092 if (constr && constr->has_not_null)
20093 {
20094 int natts = scanrel->rd_att->natts;
20095
20096 for (i = 1; i <= natts; i++)
20097 {
20098 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20099
20100 /* invalid not-null constraint must be ignored here */
20101 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20102 {
20103 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20104 NullTest *ntest = makeNode(NullTest);
20105
20106 ntest->arg = (Expr *) makeVar(1,
20107 i,
20108 wholeatt->atttypid,
20109 wholeatt->atttypmod,
20110 wholeatt->attcollation,
20111 0);
20112 ntest->nulltesttype = IS_NOT_NULL;
20113
20114 /*
20115 * argisrow=false is correct even for a composite column,
20116 * because attnotnull does not represent a SQL-spec IS NOT
20117 * NULL test in such a case, just IS DISTINCT FROM NULL.
20118 */
20119 ntest->argisrow = false;
20120 ntest->location = -1;
20121 existConstraint = lappend(existConstraint, ntest);
20122 }
20123 }
20124 }
20125
20126 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20127}
20128
20129/*
20130 * ConstraintImpliedByRelConstraint
20131 * Do scanrel's existing constraints imply the given constraint?
20132 *
20133 * testConstraint is the constraint to validate. provenConstraint is a
20134 * caller-provided list of conditions which this function may assume
20135 * to be true. Both provenConstraint and testConstraint must be in
20136 * implicit-AND form, must only contain immutable clauses, and must
20137 * contain only Vars with varno = 1.
20138 */
20139bool
20140ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20141{
20142 List *existConstraint = list_copy(provenConstraint);
20143 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20144 int num_check,
20145 i;
20146
20147 num_check = (constr != NULL) ? constr->num_check : 0;
20148 for (i = 0; i < num_check; i++)
20149 {
20150 Node *cexpr;
20151
20152 /*
20153 * If this constraint hasn't been fully validated yet, we must ignore
20154 * it here.
20155 */
20156 if (!constr->check[i].ccvalid)
20157 continue;
20158
20159 /*
20160 * NOT ENFORCED constraints are always marked as invalid, which should
20161 * have been ignored.
20162 */
20163 Assert(constr->check[i].ccenforced);
20164
20165 cexpr = stringToNode(constr->check[i].ccbin);
20166
20167 /*
20168 * Run each expression through const-simplification and
20169 * canonicalization. It is necessary, because we will be comparing it
20170 * to similarly-processed partition constraint expressions, and may
20171 * fail to detect valid matches without this.
20172 */
20173 cexpr = eval_const_expressions(NULL, cexpr);
20174 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20175
20176 existConstraint = list_concat(existConstraint,
20177 make_ands_implicit((Expr *) cexpr));
20178 }
20179
20180 /*
20181 * Try to make the proof. Since we are comparing CHECK constraints, we
20182 * need to use weak implication, i.e., we assume existConstraint is
20183 * not-false and try to prove the same for testConstraint.
20184 *
20185 * Note that predicate_implied_by assumes its first argument is known
20186 * immutable. That should always be true for both NOT NULL and partition
20187 * constraints, so we don't test it here.
20188 */
20189 return predicate_implied_by(testConstraint, existConstraint, true);
20190}
20191
20192/*
20193 * QueuePartitionConstraintValidation
20194 *
20195 * Add an entry to wqueue to have the given partition constraint validated by
20196 * Phase 3, for the given relation, and all its children.
20197 *
20198 * We first verify whether the given constraint is implied by pre-existing
20199 * relation constraints; if it is, there's no need to scan the table to
20200 * validate, so don't queue in that case.
20201 */
20202static void
20204 List *partConstraint,
20205 bool validate_default)
20206{
20207 /*
20208 * Based on the table's existing constraints, determine whether or not we
20209 * may skip scanning the table.
20210 */
20211 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20212 {
20213 if (!validate_default)
20215 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20216 RelationGetRelationName(scanrel))));
20217 else
20219 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20220 RelationGetRelationName(scanrel))));
20221 return;
20222 }
20223
20224 /*
20225 * Constraints proved insufficient. For plain relations, queue a
20226 * validation item now; for partitioned tables, recurse to process each
20227 * partition.
20228 */
20229 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20230 {
20231 AlteredTableInfo *tab;
20232
20233 /* Grab a work queue entry. */
20234 tab = ATGetQueueEntry(wqueue, scanrel);
20235 Assert(tab->partition_constraint == NULL);
20236 tab->partition_constraint = (Expr *) linitial(partConstraint);
20237 tab->validate_default = validate_default;
20238 }
20239 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20240 {
20241 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20242 int i;
20243
20244 for (i = 0; i < partdesc->nparts; i++)
20245 {
20246 Relation part_rel;
20247 List *thisPartConstraint;
20248
20249 /*
20250 * This is the minimum lock we need to prevent deadlocks.
20251 */
20252 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20253
20254 /*
20255 * Adjust the constraint for scanrel so that it matches this
20256 * partition's attribute numbers.
20257 */
20258 thisPartConstraint =
20259 map_partition_varattnos(partConstraint, 1,
20260 part_rel, scanrel);
20261
20262 QueuePartitionConstraintValidation(wqueue, part_rel,
20263 thisPartConstraint,
20264 validate_default);
20265 table_close(part_rel, NoLock); /* keep lock till commit */
20266 }
20267 }
20268}
20269
20270/*
20271 * attachPartitionTable: attach a new partition to the partitioned table
20272 *
20273 * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
20274 * of an ALTER TABLE sequence.
20275 * rel: partitioned relation;
20276 * attachrel: relation of attached partition;
20277 * bound: bounds of attached relation.
20278 */
20279static void
20281{
20282 /*
20283 * Create an inheritance; the relevant checks are performed inside the
20284 * function.
20285 */
20286 CreateInheritance(attachrel, rel, true);
20287
20288 /* Update the pg_class entry. */
20289 StorePartitionBound(attachrel, rel, bound);
20290
20291 /* Ensure there exists a correct set of indexes in the partition. */
20292 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20293
20294 /* and triggers */
20295 CloneRowTriggersToPartition(rel, attachrel);
20296
20297 /*
20298 * Clone foreign key constraints. Callee is responsible for setting up
20299 * for phase 3 constraint verification.
20300 */
20301 CloneForeignKeyConstraints(wqueue, rel, attachrel);
20302}
20303
20304/*
20305 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20306 *
20307 * Return the address of the newly attached partition.
20308 */
20309static ObjectAddress
20311 AlterTableUtilityContext *context)
20312{
20313 Relation attachrel,
20314 catalog;
20315 List *attachrel_children;
20316 List *partConstraint;
20317 SysScanDesc scan;
20318 ScanKeyData skey;
20319 AttrNumber attno;
20320 int natts;
20321 TupleDesc tupleDesc;
20322 ObjectAddress address;
20323 const char *trigger_name;
20324 Oid defaultPartOid;
20325 List *partBoundConstraint;
20326 ParseState *pstate = make_parsestate(NULL);
20327
20328 pstate->p_sourcetext = context->queryString;
20329
20330 /*
20331 * We must lock the default partition if one exists, because attaching a
20332 * new partition will change its partition constraint.
20333 */
20334 defaultPartOid =
20336 if (OidIsValid(defaultPartOid))
20337 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20338
20339 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20340
20341 /*
20342 * XXX I think it'd be a good idea to grab locks on all tables referenced
20343 * by FKs at this point also.
20344 */
20345
20346 /*
20347 * Must be owner of both parent and source table -- parent was checked by
20348 * ATSimplePermissions call in ATPrepCmd
20349 */
20352
20353 /* A partition can only have one parent */
20354 if (attachrel->rd_rel->relispartition)
20355 ereport(ERROR,
20356 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20357 errmsg("\"%s\" is already a partition",
20358 RelationGetRelationName(attachrel))));
20359
20360 if (OidIsValid(attachrel->rd_rel->reloftype))
20361 ereport(ERROR,
20362 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20363 errmsg("cannot attach a typed table as partition")));
20364
20365 /*
20366 * Table being attached should not already be part of inheritance; either
20367 * as a child table...
20368 */
20369 catalog = table_open(InheritsRelationId, AccessShareLock);
20370 ScanKeyInit(&skey,
20371 Anum_pg_inherits_inhrelid,
20372 BTEqualStrategyNumber, F_OIDEQ,
20374 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20375 NULL, 1, &skey);
20377 ereport(ERROR,
20378 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20379 errmsg("cannot attach inheritance child as partition")));
20380 systable_endscan(scan);
20381
20382 /* ...or as a parent table (except the case when it is partitioned) */
20383 ScanKeyInit(&skey,
20384 Anum_pg_inherits_inhparent,
20385 BTEqualStrategyNumber, F_OIDEQ,
20387 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20388 1, &skey);
20390 attachrel->rd_rel->relkind == RELKIND_RELATION)
20391 ereport(ERROR,
20392 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20393 errmsg("cannot attach inheritance parent as partition")));
20394 systable_endscan(scan);
20395 table_close(catalog, AccessShareLock);
20396
20397 /*
20398 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20399 * particular, this disallows making a rel a partition of itself.)
20400 *
20401 * We do that by checking if rel is a member of the list of attachrel's
20402 * partitions provided the latter is partitioned at all. We want to avoid
20403 * having to construct this list again, so we request the strongest lock
20404 * on all partitions. We need the strongest lock, because we may decide
20405 * to scan them if we find out that the table being attached (or its leaf
20406 * partitions) may contain rows that violate the partition constraint. If
20407 * the table has a constraint that would prevent such rows, which by
20408 * definition is present in all the partitions, we need not scan the
20409 * table, nor its partitions. But we cannot risk a deadlock by taking a
20410 * weaker lock now and the stronger one only when needed.
20411 */
20412 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20413 AccessExclusiveLock, NULL);
20414 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20415 ereport(ERROR,
20416 (errcode(ERRCODE_DUPLICATE_TABLE),
20417 errmsg("circular inheritance not allowed"),
20418 errdetail("\"%s\" is already a child of \"%s\".",
20420 RelationGetRelationName(attachrel))));
20421
20422 /* If the parent is permanent, so must be all of its partitions. */
20423 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20424 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20425 ereport(ERROR,
20426 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20427 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20429
20430 /* Temp parent cannot have a partition that is itself not a temp */
20431 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20432 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20433 ereport(ERROR,
20434 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20435 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20437
20438 /* If the parent is temp, it must belong to this session */
20439 if (RELATION_IS_OTHER_TEMP(rel))
20440 ereport(ERROR,
20441 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20442 errmsg("cannot attach as partition of temporary relation of another session")));
20443
20444 /* Ditto for the partition */
20445 if (RELATION_IS_OTHER_TEMP(attachrel))
20446 ereport(ERROR,
20447 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20448 errmsg("cannot attach temporary relation of another session as partition")));
20449
20450 /*
20451 * Check if attachrel has any identity columns or any columns that aren't
20452 * in the parent.
20453 */
20454 tupleDesc = RelationGetDescr(attachrel);
20455 natts = tupleDesc->natts;
20456 for (attno = 1; attno <= natts; attno++)
20457 {
20458 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20459 char *attributeName = NameStr(attribute->attname);
20460
20461 /* Ignore dropped */
20462 if (attribute->attisdropped)
20463 continue;
20464
20465 if (attribute->attidentity)
20466 ereport(ERROR,
20467 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20468 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20469 RelationGetRelationName(attachrel), attributeName),
20470 errdetail("The new partition may not contain an identity column."));
20471
20472 /* Try to find the column in parent (matching on column name) */
20473 if (!SearchSysCacheExists2(ATTNAME,
20475 CStringGetDatum(attributeName)))
20476 ereport(ERROR,
20477 (errcode(ERRCODE_DATATYPE_MISMATCH),
20478 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20479 RelationGetRelationName(attachrel), attributeName,
20481 errdetail("The new partition may contain only the columns present in parent.")));
20482 }
20483
20484 /*
20485 * If child_rel has row-level triggers with transition tables, we
20486 * currently don't allow it to become a partition. See also prohibitions
20487 * in ATExecAddInherit() and CreateTrigger().
20488 */
20489 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20490 if (trigger_name != NULL)
20491 ereport(ERROR,
20492 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20493 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20494 trigger_name, RelationGetRelationName(attachrel)),
20495 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20496
20497 /*
20498 * Check that the new partition's bound is valid and does not overlap any
20499 * of existing partitions of the parent - note that it does not return on
20500 * error.
20501 */
20503 cmd->bound, pstate);
20504
20505 attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
20506
20507 /*
20508 * Generate a partition constraint from the partition bound specification.
20509 * If the parent itself is a partition, make sure to include its
20510 * constraint as well.
20511 */
20512 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20513
20514 /*
20515 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20516 * since it's needed later to construct the constraint expression for
20517 * validating against the default partition, if any.
20518 */
20519 partConstraint = list_concat_copy(partBoundConstraint,
20521
20522 /* Skip validation if there are no constraints to validate. */
20523 if (partConstraint)
20524 {
20525 /*
20526 * Run the partition quals through const-simplification similar to
20527 * check constraints. We skip canonicalize_qual, though, because
20528 * partition quals should be in canonical form already.
20529 */
20530 partConstraint =
20532 (Node *) partConstraint);
20533
20534 /* XXX this sure looks wrong */
20535 partConstraint = list_make1(make_ands_explicit(partConstraint));
20536
20537 /*
20538 * Adjust the generated constraint to match this partition's attribute
20539 * numbers.
20540 */
20541 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20542 rel);
20543
20544 /* Validate partition constraints against the table being attached. */
20545 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20546 false);
20547 }
20548
20549 /*
20550 * If we're attaching a partition other than the default partition and a
20551 * default one exists, then that partition's partition constraint changes,
20552 * so add an entry to the work queue to validate it, too. (We must not do
20553 * this when the partition being attached is the default one; we already
20554 * did it above!)
20555 */
20556 if (OidIsValid(defaultPartOid))
20557 {
20558 Relation defaultrel;
20559 List *defPartConstraint;
20560
20561 Assert(!cmd->bound->is_default);
20562
20563 /* we already hold a lock on the default partition */
20564 defaultrel = table_open(defaultPartOid, NoLock);
20565 defPartConstraint =
20566 get_proposed_default_constraint(partBoundConstraint);
20567
20568 /*
20569 * Map the Vars in the constraint expression from rel's attnos to
20570 * defaultrel's.
20571 */
20572 defPartConstraint =
20573 map_partition_varattnos(defPartConstraint,
20574 1, defaultrel, rel);
20575 QueuePartitionConstraintValidation(wqueue, defaultrel,
20576 defPartConstraint, true);
20577
20578 /* keep our lock until commit. */
20579 table_close(defaultrel, NoLock);
20580 }
20581
20582 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20583
20584 /*
20585 * If the partition we just attached is partitioned itself, invalidate
20586 * relcache for all descendent partitions too to ensure that their
20587 * rd_partcheck expression trees are rebuilt; partitions already locked at
20588 * the beginning of this function.
20589 */
20590 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20591 {
20592 ListCell *l;
20593
20594 foreach(l, attachrel_children)
20595 {
20597 }
20598 }
20599
20600 /* keep our lock until commit */
20601 table_close(attachrel, NoLock);
20602
20603 return address;
20604}
20605
20606/*
20607 * AttachPartitionEnsureIndexes
20608 * subroutine for ATExecAttachPartition to create/match indexes
20609 *
20610 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20611 * PARTITION: every partition must have an index attached to each index on the
20612 * partitioned table.
20613 */
20614static void
20616{
20617 List *idxes;
20618 List *attachRelIdxs;
20619 Relation *attachrelIdxRels;
20620 IndexInfo **attachInfos;
20621 ListCell *cell;
20622 MemoryContext cxt;
20623 MemoryContext oldcxt;
20624
20626 "AttachPartitionEnsureIndexes",
20628 oldcxt = MemoryContextSwitchTo(cxt);
20629
20630 idxes = RelationGetIndexList(rel);
20631 attachRelIdxs = RelationGetIndexList(attachrel);
20632 attachrelIdxRels = palloc_array(Relation, list_length(attachRelIdxs));
20633 attachInfos = palloc_array(IndexInfo *, list_length(attachRelIdxs));
20634
20635 /* Build arrays of all existing indexes and their IndexInfos */
20636 foreach_oid(cldIdxId, attachRelIdxs)
20637 {
20638 int i = foreach_current_index(cldIdxId);
20639
20640 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20641 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20642 }
20643
20644 /*
20645 * If we're attaching a foreign table, we must fail if any of the indexes
20646 * is a constraint index; otherwise, there's nothing to do here. Do this
20647 * before starting work, to avoid wasting the effort of building a few
20648 * non-unique indexes before coming across a unique one.
20649 */
20650 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20651 {
20652 foreach(cell, idxes)
20653 {
20654 Oid idx = lfirst_oid(cell);
20656
20657 if (idxRel->rd_index->indisunique ||
20658 idxRel->rd_index->indisprimary)
20659 ereport(ERROR,
20660 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20661 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20662 RelationGetRelationName(attachrel),
20664 errdetail("Partitioned table \"%s\" contains unique indexes.",
20667 }
20668
20669 goto out;
20670 }
20671
20672 /*
20673 * For each index on the partitioned table, find a matching one in the
20674 * partition-to-be; if one is not found, create one.
20675 */
20676 foreach(cell, idxes)
20677 {
20678 Oid idx = lfirst_oid(cell);
20680 IndexInfo *info;
20681 AttrMap *attmap;
20682 bool found = false;
20683 Oid constraintOid;
20684
20685 /*
20686 * Ignore indexes in the partitioned table other than partitioned
20687 * indexes.
20688 */
20689 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20690 {
20692 continue;
20693 }
20694
20695 /* construct an indexinfo to compare existing indexes against */
20696 info = BuildIndexInfo(idxRel);
20697 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20698 RelationGetDescr(rel),
20699 false);
20701
20702 /*
20703 * Scan the list of existing indexes in the partition-to-be, and mark
20704 * the first matching, valid, unattached one we find, if any, as
20705 * partition of the parent index. If we find one, we're done.
20706 */
20707 for (int i = 0; i < list_length(attachRelIdxs); i++)
20708 {
20709 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20710 Oid cldConstrOid = InvalidOid;
20711
20712 /* does this index have a parent? if so, can't use it */
20713 if (attachrelIdxRels[i]->rd_rel->relispartition)
20714 continue;
20715
20716 /* If this index is invalid, can't use it */
20717 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20718 continue;
20719
20720 if (CompareIndexInfo(attachInfos[i], info,
20721 attachrelIdxRels[i]->rd_indcollation,
20722 idxRel->rd_indcollation,
20723 attachrelIdxRels[i]->rd_opfamily,
20724 idxRel->rd_opfamily,
20725 attmap))
20726 {
20727 /*
20728 * If this index is being created in the parent because of a
20729 * constraint, then the child needs to have a constraint also,
20730 * so look for one. If there is no such constraint, this
20731 * index is no good, so keep looking.
20732 */
20733 if (OidIsValid(constraintOid))
20734 {
20735 cldConstrOid =
20737 cldIdxId);
20738 /* no dice */
20739 if (!OidIsValid(cldConstrOid))
20740 continue;
20741
20742 /* Ensure they're both the same type of constraint */
20743 if (get_constraint_type(constraintOid) !=
20744 get_constraint_type(cldConstrOid))
20745 continue;
20746 }
20747
20748 /* bingo. */
20749 IndexSetParentIndex(attachrelIdxRels[i], idx);
20750 if (OidIsValid(constraintOid))
20751 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20752 RelationGetRelid(attachrel));
20753 found = true;
20754
20756 break;
20757 }
20758 }
20759
20760 /*
20761 * If no suitable index was found in the partition-to-be, create one
20762 * now. Note that if this is a PK, not-null constraints must already
20763 * exist.
20764 */
20765 if (!found)
20766 {
20767 IndexStmt *stmt;
20768 Oid conOid;
20769
20771 idxRel, attmap,
20772 &conOid);
20774 RelationGetRelid(idxRel),
20775 conOid,
20776 -1,
20777 true, false, false, false, false);
20778 }
20779
20781 }
20782
20783out:
20784 /* Clean up. */
20785 for (int i = 0; i < list_length(attachRelIdxs); i++)
20786 index_close(attachrelIdxRels[i], AccessShareLock);
20787 MemoryContextSwitchTo(oldcxt);
20789}
20790
20791/*
20792 * CloneRowTriggersToPartition
20793 * subroutine for ATExecAttachPartition/DefineRelation to create row
20794 * triggers on partitions
20795 */
20796static void
20798{
20799 Relation pg_trigger;
20801 SysScanDesc scan;
20802 HeapTuple tuple;
20803 MemoryContext perTupCxt;
20804
20805 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20806 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20807 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20808 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20809 true, NULL, 1, &key);
20810
20812 "clone trig", ALLOCSET_SMALL_SIZES);
20813
20814 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20815 {
20816 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20817 CreateTrigStmt *trigStmt;
20818 Node *qual = NULL;
20819 Datum value;
20820 bool isnull;
20821 List *cols = NIL;
20822 List *trigargs = NIL;
20823 MemoryContext oldcxt;
20824
20825 /*
20826 * Ignore statement-level triggers; those are not cloned.
20827 */
20828 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20829 continue;
20830
20831 /*
20832 * Don't clone internal triggers, because the constraint cloning code
20833 * will.
20834 */
20835 if (trigForm->tgisinternal)
20836 continue;
20837
20838 /*
20839 * Complain if we find an unexpected trigger type.
20840 */
20841 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20842 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20843 elog(ERROR, "unexpected trigger \"%s\" found",
20844 NameStr(trigForm->tgname));
20845
20846 /* Use short-lived context for CREATE TRIGGER */
20847 oldcxt = MemoryContextSwitchTo(perTupCxt);
20848
20849 /*
20850 * If there is a WHEN clause, generate a 'cooked' version of it that's
20851 * appropriate for the partition.
20852 */
20853 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20854 RelationGetDescr(pg_trigger), &isnull);
20855 if (!isnull)
20856 {
20858 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20859 partition, parent);
20860 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20861 partition, parent);
20862 }
20863
20864 /*
20865 * If there is a column list, transform it to a list of column names.
20866 * Note we don't need to map this list in any way ...
20867 */
20868 if (trigForm->tgattr.dim1 > 0)
20869 {
20870 int i;
20871
20872 for (i = 0; i < trigForm->tgattr.dim1; i++)
20873 {
20875
20876 col = TupleDescAttr(parent->rd_att,
20877 trigForm->tgattr.values[i] - 1);
20878 cols = lappend(cols,
20879 makeString(pstrdup(NameStr(col->attname))));
20880 }
20881 }
20882
20883 /* Reconstruct trigger arguments list. */
20884 if (trigForm->tgnargs > 0)
20885 {
20886 char *p;
20887
20888 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20889 RelationGetDescr(pg_trigger), &isnull);
20890 if (isnull)
20891 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20892 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20893
20894 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20895
20896 for (int i = 0; i < trigForm->tgnargs; i++)
20897 {
20898 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20899 p += strlen(p) + 1;
20900 }
20901 }
20902
20903 trigStmt = makeNode(CreateTrigStmt);
20904 trigStmt->replace = false;
20905 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20906 trigStmt->trigname = NameStr(trigForm->tgname);
20907 trigStmt->relation = NULL;
20908 trigStmt->funcname = NULL; /* passed separately */
20909 trigStmt->args = trigargs;
20910 trigStmt->row = true;
20911 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20912 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20913 trigStmt->columns = cols;
20914 trigStmt->whenClause = NULL; /* passed separately */
20915 trigStmt->transitionRels = NIL; /* not supported at present */
20916 trigStmt->deferrable = trigForm->tgdeferrable;
20917 trigStmt->initdeferred = trigForm->tginitdeferred;
20918 trigStmt->constrrel = NULL; /* passed separately */
20919
20920 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20921 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20922 trigForm->tgfoid, trigForm->oid, qual,
20923 false, true, trigForm->tgenabled);
20924
20925 MemoryContextSwitchTo(oldcxt);
20926 MemoryContextReset(perTupCxt);
20927 }
20928
20929 MemoryContextDelete(perTupCxt);
20930
20931 systable_endscan(scan);
20932 table_close(pg_trigger, RowExclusiveLock);
20933}
20934
20935/*
20936 * ALTER TABLE DETACH PARTITION
20937 *
20938 * Return the address of the relation that is no longer a partition of rel.
20939 *
20940 * If concurrent mode is requested, we run in two transactions. A side-
20941 * effect is that this command cannot run in a multi-part ALTER TABLE.
20942 * Currently, that's enforced by the grammar.
20943 *
20944 * The strategy for concurrency is to first modify the partition's
20945 * pg_inherit catalog row to make it visible to everyone that the
20946 * partition is detached, lock the partition against writes, and commit
20947 * the transaction; anyone who requests the partition descriptor from
20948 * that point onwards has to ignore such a partition. In a second
20949 * transaction, we wait until all transactions that could have seen the
20950 * partition as attached are gone, then we remove the rest of partition
20951 * metadata (pg_inherits and pg_class.relpartbounds).
20952 */
20953static ObjectAddress
20955 RangeVar *name, bool concurrent)
20956{
20957 Relation partRel;
20958 ObjectAddress address;
20959 Oid defaultPartOid;
20960
20961 /*
20962 * We must lock the default partition, because detaching this partition
20963 * will change its partition constraint.
20964 */
20965 defaultPartOid =
20967 if (OidIsValid(defaultPartOid))
20968 {
20969 /*
20970 * Concurrent detaching when a default partition exists is not
20971 * supported. The main problem is that the default partition
20972 * constraint would change. And there's a definitional problem: what
20973 * should happen to the tuples that are being inserted that belong to
20974 * the partition being detached? Putting them on the partition being
20975 * detached would be wrong, since they'd become "lost" after the
20976 * detaching completes but we cannot put them in the default partition
20977 * either until we alter its partition constraint.
20978 *
20979 * I think we could solve this problem if we effected the constraint
20980 * change before committing the first transaction. But the lock would
20981 * have to remain AEL and it would cause concurrent query planning to
20982 * be blocked, so changing it that way would be even worse.
20983 */
20984 if (concurrent)
20985 ereport(ERROR,
20986 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20987 errmsg("cannot detach partitions concurrently when a default partition exists")));
20988 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20989 }
20990
20991 /*
20992 * In concurrent mode, the partition is locked with share-update-exclusive
20993 * in the first transaction. This allows concurrent transactions to be
20994 * doing DML to the partition.
20995 */
20996 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20998
20999 /*
21000 * Check inheritance conditions and either delete the pg_inherits row (in
21001 * non-concurrent mode) or just set the inhdetachpending flag.
21002 */
21003 if (!concurrent)
21004 RemoveInheritance(partRel, rel, false);
21005 else
21006 MarkInheritDetached(partRel, rel);
21007
21008 /*
21009 * Ensure that foreign keys still hold after this detach. This keeps
21010 * locks on the referencing tables, which prevents concurrent transactions
21011 * from adding rows that we wouldn't see. For this to work in concurrent
21012 * mode, it is critical that the partition appears as no longer attached
21013 * for the RI queries as soon as the first transaction commits.
21014 */
21016
21017 /*
21018 * Concurrent mode has to work harder; first we add a new constraint to
21019 * the partition that matches the partition constraint. Then we close our
21020 * existing transaction, and in a new one wait for all processes to catch
21021 * up on the catalog updates we've done so far; at that point we can
21022 * complete the operation.
21023 */
21024 if (concurrent)
21025 {
21026 Oid partrelid,
21027 parentrelid;
21028 LOCKTAG tag;
21029 char *parentrelname;
21030 char *partrelname;
21031
21032 /*
21033 * We're almost done now; the only traces that remain are the
21034 * pg_inherits tuple and the partition's relpartbounds. Before we can
21035 * remove those, we need to wait until all transactions that know that
21036 * this is a partition are gone.
21037 */
21038
21039 /*
21040 * Remember relation OIDs to re-acquire them later; and relation names
21041 * too, for error messages if something is dropped in between.
21042 */
21043 partrelid = RelationGetRelid(partRel);
21044 parentrelid = RelationGetRelid(rel);
21045 parentrelname = MemoryContextStrdup(PortalContext,
21047 partrelname = MemoryContextStrdup(PortalContext,
21048 RelationGetRelationName(partRel));
21049
21050 /* Invalidate relcache entries for the parent -- must be before close */
21052
21053 table_close(partRel, NoLock);
21054 table_close(rel, NoLock);
21055 tab->rel = NULL;
21056
21057 /* Make updated catalog entry visible */
21060
21062
21063 /*
21064 * Now wait. This ensures that all queries that were planned
21065 * including the partition are finished before we remove the rest of
21066 * catalog entries. We don't need or indeed want to acquire this
21067 * lock, though -- that would block later queries.
21068 *
21069 * We don't need to concern ourselves with waiting for a lock on the
21070 * partition itself, since we will acquire AccessExclusiveLock below.
21071 */
21072 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
21074
21075 /*
21076 * Now acquire locks in both relations again. Note they may have been
21077 * removed in the meantime, so care is required.
21078 */
21079 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
21080 partRel = try_relation_open(partrelid, AccessExclusiveLock);
21081
21082 /* If the relations aren't there, something bad happened; bail out */
21083 if (rel == NULL)
21084 {
21085 if (partRel != NULL) /* shouldn't happen */
21086 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21087 partrelname);
21088 ereport(ERROR,
21089 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21090 errmsg("partitioned table \"%s\" was removed concurrently",
21091 parentrelname)));
21092 }
21093 if (partRel == NULL)
21094 ereport(ERROR,
21095 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21096 errmsg("partition \"%s\" was removed concurrently", partrelname)));
21097
21098 tab->rel = rel;
21099 }
21100
21101 /*
21102 * Detaching the partition might involve TOAST table access, so ensure we
21103 * have a valid snapshot.
21104 */
21106
21107 /* Do the final part of detaching */
21108 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21109
21111
21112 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21113
21114 /* keep our lock until commit */
21115 table_close(partRel, NoLock);
21116
21117 return address;
21118}
21119
21120/*
21121 * Second part of ALTER TABLE .. DETACH.
21122 *
21123 * This is separate so that it can be run independently when the second
21124 * transaction of the concurrent algorithm fails (crash or abort).
21125 */
21126static void
21127DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
21128 Oid defaultPartOid)
21129{
21130 Relation classRel;
21131 List *fks;
21132 ListCell *cell;
21133 List *indexes;
21134 Datum new_val[Natts_pg_class];
21135 bool new_null[Natts_pg_class],
21136 new_repl[Natts_pg_class];
21137 HeapTuple tuple,
21138 newtuple;
21139 Relation trigrel = NULL;
21140 List *fkoids = NIL;
21141
21142 if (concurrent)
21143 {
21144 /*
21145 * We can remove the pg_inherits row now. (In the non-concurrent case,
21146 * this was already done).
21147 */
21148 RemoveInheritance(partRel, rel, true);
21149 }
21150
21151 /* Drop any triggers that were cloned on creation/attach. */
21153
21154 /*
21155 * Detach any foreign keys that are inherited. This includes creating
21156 * additional action triggers.
21157 */
21158 fks = copyObject(RelationGetFKeyList(partRel));
21159 if (fks != NIL)
21160 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21161
21162 /*
21163 * It's possible that the partition being detached has a foreign key that
21164 * references a partitioned table. When that happens, there are multiple
21165 * pg_constraint rows for the partition: one points to the partitioned
21166 * table itself, while the others point to each of its partitions. Only
21167 * the topmost one is to be considered here; the child constraints must be
21168 * left alone, because conceptually those aren't coming from our parent
21169 * partitioned table, but from this partition itself.
21170 *
21171 * We implement this by collecting all the constraint OIDs in a first scan
21172 * of the FK array, and skipping in the loop below those constraints whose
21173 * parents are listed here.
21174 */
21176 fkoids = lappend_oid(fkoids, fk->conoid);
21177
21178 foreach(cell, fks)
21179 {
21180 ForeignKeyCacheInfo *fk = lfirst(cell);
21181 HeapTuple contup;
21182 Form_pg_constraint conform;
21183
21184 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21185 if (!HeapTupleIsValid(contup))
21186 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21187 conform = (Form_pg_constraint) GETSTRUCT(contup);
21188
21189 /*
21190 * Consider only inherited foreign keys, and only if their parents
21191 * aren't in the list.
21192 */
21193 if (conform->contype != CONSTRAINT_FOREIGN ||
21194 !OidIsValid(conform->conparentid) ||
21195 list_member_oid(fkoids, conform->conparentid))
21196 {
21197 ReleaseSysCache(contup);
21198 continue;
21199 }
21200
21201 /*
21202 * The constraint on this table must be marked no longer a child of
21203 * the parent's constraint, as do its check triggers.
21204 */
21206
21207 /*
21208 * Also, look up the partition's "check" triggers corresponding to the
21209 * ENFORCED constraint being detached and detach them from the parent
21210 * triggers. NOT ENFORCED constraints do not have these triggers;
21211 * therefore, this step is not needed.
21212 */
21213 if (fk->conenforced)
21214 {
21215 Oid insertTriggerOid,
21216 updateTriggerOid;
21217
21219 fk->conoid, fk->confrelid, fk->conrelid,
21220 &insertTriggerOid, &updateTriggerOid);
21221 Assert(OidIsValid(insertTriggerOid));
21222 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21223 RelationGetRelid(partRel));
21224 Assert(OidIsValid(updateTriggerOid));
21225 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21226 RelationGetRelid(partRel));
21227 }
21228
21229 /*
21230 * Lastly, create the action triggers on the referenced table, using
21231 * addFkRecurseReferenced, which requires some elaborate setup (so put
21232 * it in a separate block). While at it, if the table is partitioned,
21233 * that function will recurse to create the pg_constraint rows and
21234 * action triggers for each partition.
21235 *
21236 * Note there's no need to do addFkConstraint() here, because the
21237 * pg_constraint row already exists.
21238 */
21239 {
21240 Constraint *fkconstraint;
21241 int numfks;
21242 AttrNumber conkey[INDEX_MAX_KEYS];
21243 AttrNumber confkey[INDEX_MAX_KEYS];
21244 Oid conpfeqop[INDEX_MAX_KEYS];
21245 Oid conppeqop[INDEX_MAX_KEYS];
21246 Oid conffeqop[INDEX_MAX_KEYS];
21247 int numfkdelsetcols;
21248 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21249 Relation refdRel;
21250
21252 &numfks,
21253 conkey,
21254 confkey,
21255 conpfeqop,
21256 conppeqop,
21257 conffeqop,
21258 &numfkdelsetcols,
21259 confdelsetcols);
21260
21261 /* Create a synthetic node we'll use throughout */
21262 fkconstraint = makeNode(Constraint);
21263 fkconstraint->contype = CONSTRAINT_FOREIGN;
21264 fkconstraint->conname = pstrdup(NameStr(conform->conname));
21265 fkconstraint->deferrable = conform->condeferrable;
21266 fkconstraint->initdeferred = conform->condeferred;
21267 fkconstraint->is_enforced = conform->conenforced;
21268 fkconstraint->skip_validation = true;
21269 fkconstraint->initially_valid = conform->convalidated;
21270 /* a few irrelevant fields omitted here */
21271 fkconstraint->pktable = NULL;
21272 fkconstraint->fk_attrs = NIL;
21273 fkconstraint->pk_attrs = NIL;
21274 fkconstraint->fk_matchtype = conform->confmatchtype;
21275 fkconstraint->fk_upd_action = conform->confupdtype;
21276 fkconstraint->fk_del_action = conform->confdeltype;
21277 fkconstraint->fk_del_set_cols = NIL;
21278 fkconstraint->old_conpfeqop = NIL;
21279 fkconstraint->old_pktable_oid = InvalidOid;
21280 fkconstraint->location = -1;
21281
21282 /* set up colnames, used to generate the constraint name */
21283 for (int i = 0; i < numfks; i++)
21284 {
21286
21287 att = TupleDescAttr(RelationGetDescr(partRel),
21288 conkey[i] - 1);
21289
21290 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21291 makeString(NameStr(att->attname)));
21292 }
21293
21295
21296 addFkRecurseReferenced(fkconstraint, partRel,
21297 refdRel,
21298 conform->conindid,
21299 fk->conoid,
21300 numfks,
21301 confkey,
21302 conkey,
21303 conpfeqop,
21304 conppeqop,
21305 conffeqop,
21306 numfkdelsetcols,
21307 confdelsetcols,
21308 true,
21310 conform->conperiod);
21311 table_close(refdRel, NoLock); /* keep lock till end of xact */
21312 }
21313
21314 ReleaseSysCache(contup);
21315 }
21316 list_free_deep(fks);
21317 if (trigrel)
21318 table_close(trigrel, RowExclusiveLock);
21319
21320 /*
21321 * Any sub-constraints that are in the referenced-side of a larger
21322 * constraint have to be removed. This partition is no longer part of the
21323 * key space of the constraint.
21324 */
21325 foreach(cell, GetParentedForeignKeyRefs(partRel))
21326 {
21327 Oid constrOid = lfirst_oid(cell);
21328 ObjectAddress constraint;
21329
21331 deleteDependencyRecordsForClass(ConstraintRelationId,
21332 constrOid,
21333 ConstraintRelationId,
21336
21337 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21338 performDeletion(&constraint, DROP_RESTRICT, 0);
21339 }
21340
21341 /* Now we can detach indexes */
21342 indexes = RelationGetIndexList(partRel);
21343 foreach(cell, indexes)
21344 {
21345 Oid idxid = lfirst_oid(cell);
21346 Oid parentidx;
21347 Relation idx;
21348 Oid constrOid;
21349 Oid parentConstrOid;
21350
21351 if (!has_superclass(idxid))
21352 continue;
21353
21354 parentidx = get_partition_parent(idxid, false);
21355 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21356
21359
21360 /*
21361 * If there's a constraint associated with the index, detach it too.
21362 * Careful: it is possible for a constraint index in a partition to be
21363 * the child of a non-constraint index, so verify whether the parent
21364 * index does actually have a constraint.
21365 */
21367 idxid);
21369 parentidx);
21370 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21372
21374 }
21375
21376 /* Update pg_class tuple */
21377 classRel = table_open(RelationRelationId, RowExclusiveLock);
21378 tuple = SearchSysCacheCopy1(RELOID,
21380 if (!HeapTupleIsValid(tuple))
21381 elog(ERROR, "cache lookup failed for relation %u",
21382 RelationGetRelid(partRel));
21383 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21384
21385 /* Clear relpartbound and reset relispartition */
21386 memset(new_val, 0, sizeof(new_val));
21387 memset(new_null, false, sizeof(new_null));
21388 memset(new_repl, false, sizeof(new_repl));
21389 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21390 new_null[Anum_pg_class_relpartbound - 1] = true;
21391 new_repl[Anum_pg_class_relpartbound - 1] = true;
21392 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21393 new_val, new_null, new_repl);
21394
21395 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21396 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21397 heap_freetuple(newtuple);
21398 table_close(classRel, RowExclusiveLock);
21399
21400 /*
21401 * Drop identity property from all identity columns of partition.
21402 */
21403 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21404 {
21405 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21406
21407 if (!attr->attisdropped && attr->attidentity)
21408 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21409 AccessExclusiveLock, true, true);
21410 }
21411
21412 if (OidIsValid(defaultPartOid))
21413 {
21414 /*
21415 * If the relation being detached is the default partition itself,
21416 * remove it from the parent's pg_partitioned_table entry.
21417 *
21418 * If not, we must invalidate default partition's relcache entry, as
21419 * in StorePartitionBound: its partition constraint depends on every
21420 * other partition's partition constraint.
21421 */
21422 if (RelationGetRelid(partRel) == defaultPartOid)
21424 else
21425 CacheInvalidateRelcacheByRelid(defaultPartOid);
21426 }
21427
21428 /*
21429 * Invalidate the parent's relcache so that the partition is no longer
21430 * included in its partition descriptor.
21431 */
21433
21434 /*
21435 * If the partition we just detached is partitioned itself, invalidate
21436 * relcache for all descendent partitions too to ensure that their
21437 * rd_partcheck expression trees are rebuilt; must lock partitions before
21438 * doing so, using the same lockmode as what partRel has been locked with
21439 * by the caller.
21440 */
21441 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21442 {
21443 List *children;
21444
21445 children = find_all_inheritors(RelationGetRelid(partRel),
21446 AccessExclusiveLock, NULL);
21447 foreach(cell, children)
21448 {
21450 }
21451 }
21452}
21453
21454/*
21455 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21456 *
21457 * To use when a DETACH PARTITION command previously did not run to
21458 * completion; this completes the detaching process.
21459 */
21460static ObjectAddress
21462{
21463 Relation partRel;
21464 ObjectAddress address;
21465 Snapshot snap = GetActiveSnapshot();
21466
21468
21469 /*
21470 * Wait until existing snapshots are gone. This is important if the
21471 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21472 * user could immediately run DETACH FINALIZE without actually waiting for
21473 * existing transactions. We must not complete the detach action until
21474 * all such queries are complete (otherwise we would present them with an
21475 * inconsistent view of catalogs).
21476 */
21477 WaitForOlderSnapshots(snap->xmin, false);
21478
21479 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21480
21481 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21482
21483 table_close(partRel, NoLock);
21484
21485 return address;
21486}
21487
21488/*
21489 * DropClonedTriggersFromPartition
21490 * subroutine for ATExecDetachPartition to remove any triggers that were
21491 * cloned to the partition when it was created-as-partition or attached.
21492 * This undoes what CloneRowTriggersToPartition did.
21493 */
21494static void
21496{
21497 ScanKeyData skey;
21498 SysScanDesc scan;
21499 HeapTuple trigtup;
21500 Relation tgrel;
21501 ObjectAddresses *objects;
21502
21503 objects = new_object_addresses();
21504
21505 /*
21506 * Scan pg_trigger to search for all triggers on this rel.
21507 */
21508 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21509 F_OIDEQ, ObjectIdGetDatum(partitionId));
21510 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21511 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21512 true, NULL, 1, &skey);
21513 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21514 {
21515 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21516 ObjectAddress trig;
21517
21518 /* Ignore triggers that weren't cloned */
21519 if (!OidIsValid(pg_trigger->tgparentid))
21520 continue;
21521
21522 /*
21523 * Ignore internal triggers that are implementation objects of foreign
21524 * keys, because these will be detached when the foreign keys
21525 * themselves are.
21526 */
21527 if (OidIsValid(pg_trigger->tgconstrrelid))
21528 continue;
21529
21530 /*
21531 * This is ugly, but necessary: remove the dependency markings on the
21532 * trigger so that it can be removed.
21533 */
21534 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21535 TriggerRelationId,
21537 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21538 RelationRelationId,
21540
21541 /* remember this trigger to remove it below */
21542 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21543 add_exact_object_address(&trig, objects);
21544 }
21545
21546 /* make the dependency removal visible to the deletion below */
21549
21550 /* done */
21551 free_object_addresses(objects);
21552 systable_endscan(scan);
21554}
21555
21556/*
21557 * Before acquiring lock on an index, acquire the same lock on the owning
21558 * table.
21559 */
21561{
21565};
21566
21567static void
21568RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21569 void *arg)
21570{
21572 Form_pg_class classform;
21573 HeapTuple tuple;
21574
21575 state = (struct AttachIndexCallbackState *) arg;
21576
21577 if (!state->lockedParentTbl)
21578 {
21579 LockRelationOid(state->parentTblOid, AccessShareLock);
21580 state->lockedParentTbl = true;
21581 }
21582
21583 /*
21584 * If we previously locked some other heap, and the name we're looking up
21585 * no longer refers to an index on that relation, release the now-useless
21586 * lock. XXX maybe we should do *after* we verify whether the index does
21587 * not actually belong to the same relation ...
21588 */
21589 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21590 {
21591 UnlockRelationOid(state->partitionOid, AccessShareLock);
21592 state->partitionOid = InvalidOid;
21593 }
21594
21595 /* Didn't find a relation, so no need for locking or permission checks. */
21596 if (!OidIsValid(relOid))
21597 return;
21598
21599 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21600 if (!HeapTupleIsValid(tuple))
21601 return; /* concurrently dropped, so nothing to do */
21602 classform = (Form_pg_class) GETSTRUCT(tuple);
21603 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21604 classform->relkind != RELKIND_INDEX)
21605 ereport(ERROR,
21606 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21607 errmsg("\"%s\" is not an index", rv->relname)));
21608 ReleaseSysCache(tuple);
21609
21610 /*
21611 * Since we need only examine the heap's tupledesc, an access share lock
21612 * on it (preventing any DDL) is sufficient.
21613 */
21614 state->partitionOid = IndexGetRelation(relOid, false);
21615 LockRelationOid(state->partitionOid, AccessShareLock);
21616}
21617
21618/*
21619 * ALTER INDEX i1 ATTACH PARTITION i2
21620 */
21621static ObjectAddress
21623{
21624 Relation partIdx;
21625 Relation partTbl;
21626 Relation parentTbl;
21627 ObjectAddress address;
21628 Oid partIdxId;
21629 Oid currParent;
21631
21632 /*
21633 * We need to obtain lock on the index 'name' to modify it, but we also
21634 * need to read its owning table's tuple descriptor -- so we need to lock
21635 * both. To avoid deadlocks, obtain lock on the table before doing so on
21636 * the index. Furthermore, we need to examine the parent table of the
21637 * partition, so lock that one too.
21638 */
21639 state.partitionOid = InvalidOid;
21640 state.parentTblOid = parentIdx->rd_index->indrelid;
21641 state.lockedParentTbl = false;
21642 partIdxId =
21645 &state);
21646 /* Not there? */
21647 if (!OidIsValid(partIdxId))
21648 ereport(ERROR,
21649 (errcode(ERRCODE_UNDEFINED_OBJECT),
21650 errmsg("index \"%s\" does not exist", name->relname)));
21651
21652 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21653 partIdx = relation_open(partIdxId, AccessExclusiveLock);
21654
21655 /* we already hold locks on both tables, so this is safe: */
21656 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21657 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21658
21659 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21660
21661 /* Silently do nothing if already in the right state */
21662 currParent = partIdx->rd_rel->relispartition ?
21663 get_partition_parent(partIdxId, false) : InvalidOid;
21664 if (currParent != RelationGetRelid(parentIdx))
21665 {
21666 IndexInfo *childInfo;
21667 IndexInfo *parentInfo;
21668 AttrMap *attmap;
21669 bool found;
21670 int i;
21671 PartitionDesc partDesc;
21672 Oid constraintOid,
21673 cldConstrId = InvalidOid;
21674
21675 /*
21676 * If this partition already has an index attached, refuse the
21677 * operation.
21678 */
21679 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21680
21681 if (OidIsValid(currParent))
21682 ereport(ERROR,
21683 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21684 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21685 RelationGetRelationName(partIdx),
21686 RelationGetRelationName(parentIdx)),
21687 errdetail("Index \"%s\" is already attached to another index.",
21688 RelationGetRelationName(partIdx))));
21689
21690 /* Make sure it indexes a partition of the other index's table */
21691 partDesc = RelationGetPartitionDesc(parentTbl, true);
21692 found = false;
21693 for (i = 0; i < partDesc->nparts; i++)
21694 {
21695 if (partDesc->oids[i] == state.partitionOid)
21696 {
21697 found = true;
21698 break;
21699 }
21700 }
21701 if (!found)
21702 ereport(ERROR,
21703 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21704 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21705 RelationGetRelationName(partIdx),
21706 RelationGetRelationName(parentIdx)),
21707 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21708 RelationGetRelationName(partIdx),
21709 RelationGetRelationName(parentTbl))));
21710
21711 /* Ensure the indexes are compatible */
21712 childInfo = BuildIndexInfo(partIdx);
21713 parentInfo = BuildIndexInfo(parentIdx);
21714 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21715 RelationGetDescr(parentTbl),
21716 false);
21717 if (!CompareIndexInfo(childInfo, parentInfo,
21718 partIdx->rd_indcollation,
21719 parentIdx->rd_indcollation,
21720 partIdx->rd_opfamily,
21721 parentIdx->rd_opfamily,
21722 attmap))
21723 ereport(ERROR,
21724 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21725 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21726 RelationGetRelationName(partIdx),
21727 RelationGetRelationName(parentIdx)),
21728 errdetail("The index definitions do not match.")));
21729
21730 /*
21731 * If there is a constraint in the parent, make sure there is one in
21732 * the child too.
21733 */
21734 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21735 RelationGetRelid(parentIdx));
21736
21737 if (OidIsValid(constraintOid))
21738 {
21740 partIdxId);
21741 if (!OidIsValid(cldConstrId))
21742 ereport(ERROR,
21743 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21744 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21745 RelationGetRelationName(partIdx),
21746 RelationGetRelationName(parentIdx)),
21747 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21748 RelationGetRelationName(parentIdx),
21749 RelationGetRelationName(parentTbl),
21750 RelationGetRelationName(partIdx))));
21751 }
21752
21753 /*
21754 * If it's a primary key, make sure the columns in the partition are
21755 * NOT NULL.
21756 */
21757 if (parentIdx->rd_index->indisprimary)
21758 verifyPartitionIndexNotNull(childInfo, partTbl);
21759
21760 /* All good -- do it */
21761 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21762 if (OidIsValid(constraintOid))
21763 ConstraintSetParentConstraint(cldConstrId, constraintOid,
21764 RelationGetRelid(partTbl));
21765
21766 free_attrmap(attmap);
21767
21768 validatePartitionedIndex(parentIdx, parentTbl);
21769 }
21770
21771 relation_close(parentTbl, AccessShareLock);
21772 /* keep these locks till commit */
21773 relation_close(partTbl, NoLock);
21774 relation_close(partIdx, NoLock);
21775
21776 return address;
21777}
21778
21779/*
21780 * Verify whether the given partition already contains an index attached
21781 * to the given partitioned index. If so, raise an error.
21782 */
21783static void
21784refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21785{
21786 Oid existingIdx;
21787
21788 existingIdx = index_get_partition(partitionTbl,
21789 RelationGetRelid(parentIdx));
21790 if (OidIsValid(existingIdx))
21791 ereport(ERROR,
21792 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21793 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21794 RelationGetRelationName(partIdx),
21795 RelationGetRelationName(parentIdx)),
21796 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21797 get_rel_name(existingIdx),
21798 RelationGetRelationName(partitionTbl))));
21799}
21800
21801/*
21802 * Verify whether the set of attached partition indexes to a parent index on
21803 * a partitioned table is complete. If it is, mark the parent index valid.
21804 *
21805 * This should be called each time a partition index is attached.
21806 */
21807static void
21809{
21810 Relation inheritsRel;
21811 SysScanDesc scan;
21813 int tuples = 0;
21814 HeapTuple inhTup;
21815 bool updated = false;
21816
21817 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21818
21819 /*
21820 * Scan pg_inherits for this parent index. Count each valid index we find
21821 * (verifying the pg_index entry for each), and if we reach the total
21822 * amount we expect, we can mark this parent index as valid.
21823 */
21824 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21825 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21826 BTEqualStrategyNumber, F_OIDEQ,
21828 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21829 NULL, 1, &key);
21830 while ((inhTup = systable_getnext(scan)) != NULL)
21831 {
21832 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21833 HeapTuple indTup;
21834 Form_pg_index indexForm;
21835
21836 indTup = SearchSysCache1(INDEXRELID,
21837 ObjectIdGetDatum(inhForm->inhrelid));
21838 if (!HeapTupleIsValid(indTup))
21839 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21840 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21841 if (indexForm->indisvalid)
21842 tuples += 1;
21843 ReleaseSysCache(indTup);
21844 }
21845
21846 /* Done with pg_inherits */
21847 systable_endscan(scan);
21848 table_close(inheritsRel, AccessShareLock);
21849
21850 /*
21851 * If we found as many inherited indexes as the partitioned table has
21852 * partitions, we're good; update pg_index to set indisvalid.
21853 */
21854 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21855 {
21856 Relation idxRel;
21857 HeapTuple indTup;
21858 Form_pg_index indexForm;
21859
21860 idxRel = table_open(IndexRelationId, RowExclusiveLock);
21861 indTup = SearchSysCacheCopy1(INDEXRELID,
21863 if (!HeapTupleIsValid(indTup))
21864 elog(ERROR, "cache lookup failed for index %u",
21865 RelationGetRelid(partedIdx));
21866 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21867
21868 indexForm->indisvalid = true;
21869 updated = true;
21870
21871 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21872
21874 heap_freetuple(indTup);
21875 }
21876
21877 /*
21878 * If this index is in turn a partition of a larger index, validating it
21879 * might cause the parent to become valid also. Try that.
21880 */
21881 if (updated && partedIdx->rd_rel->relispartition)
21882 {
21883 Oid parentIdxId,
21884 parentTblId;
21885 Relation parentIdx,
21886 parentTbl;
21887
21888 /* make sure we see the validation we just did */
21890
21891 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21892 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21893 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21894 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21895 Assert(!parentIdx->rd_index->indisvalid);
21896
21897 validatePartitionedIndex(parentIdx, parentTbl);
21898
21901 }
21902}
21903
21904/*
21905 * When attaching an index as a partition of a partitioned index which is a
21906 * primary key, verify that all the columns in the partition are marked NOT
21907 * NULL.
21908 */
21909static void
21911{
21912 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21913 {
21915 iinfo->ii_IndexAttrNumbers[i] - 1);
21916
21917 if (!att->attnotnull)
21918 ereport(ERROR,
21919 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21920 errmsg("invalid primary key definition"),
21921 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21922 NameStr(att->attname),
21923 RelationGetRelationName(partition)));
21924 }
21925}
21926
21927/*
21928 * Return an OID list of constraints that reference the given relation
21929 * that are marked as having a parent constraints.
21930 */
21931static List *
21933{
21934 Relation pg_constraint;
21935 HeapTuple tuple;
21936 SysScanDesc scan;
21937 ScanKeyData key[2];
21938 List *constraints = NIL;
21939
21940 /*
21941 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21942 * scan.
21943 */
21944 if (RelationGetIndexList(partition) == NIL ||
21947 return NIL;
21948
21949 /* Search for constraints referencing this table */
21950 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21951 ScanKeyInit(&key[0],
21952 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21953 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21954 ScanKeyInit(&key[1],
21955 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21956 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21957
21958 /* XXX This is a seqscan, as we don't have a usable index */
21959 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21960 while ((tuple = systable_getnext(scan)) != NULL)
21961 {
21962 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21963
21964 /*
21965 * We only need to process constraints that are part of larger ones.
21966 */
21967 if (!OidIsValid(constrForm->conparentid))
21968 continue;
21969
21970 constraints = lappend_oid(constraints, constrForm->oid);
21971 }
21972
21973 systable_endscan(scan);
21974 table_close(pg_constraint, AccessShareLock);
21975
21976 return constraints;
21977}
21978
21979/*
21980 * During DETACH PARTITION, verify that any foreign keys pointing to the
21981 * partitioned table would not become invalid. An error is raised if any
21982 * referenced values exist.
21983 */
21984static void
21986{
21987 List *constraints;
21988 ListCell *cell;
21989
21990 constraints = GetParentedForeignKeyRefs(partition);
21991
21992 foreach(cell, constraints)
21993 {
21994 Oid constrOid = lfirst_oid(cell);
21995 HeapTuple tuple;
21996 Form_pg_constraint constrForm;
21997 Relation rel;
21998 Trigger trig = {0};
21999
22000 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
22001 if (!HeapTupleIsValid(tuple))
22002 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22003 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
22004
22005 Assert(OidIsValid(constrForm->conparentid));
22006 Assert(constrForm->confrelid == RelationGetRelid(partition));
22007
22008 /* prevent data changes into the referencing table until commit */
22009 rel = table_open(constrForm->conrelid, ShareLock);
22010
22011 trig.tgoid = InvalidOid;
22012 trig.tgname = NameStr(constrForm->conname);
22014 trig.tgisinternal = true;
22015 trig.tgconstrrelid = RelationGetRelid(partition);
22016 trig.tgconstrindid = constrForm->conindid;
22017 trig.tgconstraint = constrForm->oid;
22018 trig.tgdeferrable = false;
22019 trig.tginitdeferred = false;
22020 /* we needn't fill in remaining fields */
22021
22022 RI_PartitionRemove_Check(&trig, rel, partition);
22023
22024 ReleaseSysCache(tuple);
22025
22026 table_close(rel, NoLock);
22027 }
22028}
22029
22030/*
22031 * resolve column compression specification to compression method.
22032 */
22033static char
22034GetAttributeCompression(Oid atttypid, const char *compression)
22035{
22036 char cmethod;
22037
22038 if (compression == NULL || strcmp(compression, "default") == 0)
22040
22041 /*
22042 * To specify a nondefault method, the column data type must be toastable.
22043 * Note this says nothing about whether the column's attstorage setting
22044 * permits compression; we intentionally allow attstorage and
22045 * attcompression to be independent. But with a non-toastable type,
22046 * attstorage could not be set to a value that would permit compression.
22047 *
22048 * We don't actually need to enforce this, since nothing bad would happen
22049 * if attcompression were non-default; it would never be consulted. But
22050 * it seems more user-friendly to complain about a certainly-useless
22051 * attempt to set the property.
22052 */
22053 if (!TypeIsToastable(atttypid))
22054 ereport(ERROR,
22055 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22056 errmsg("column data type %s does not support compression",
22057 format_type_be(atttypid))));
22058
22059 cmethod = CompressionNameToMethod(compression);
22060 if (!CompressionMethodIsValid(cmethod))
22061 ereport(ERROR,
22062 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22063 errmsg("invalid compression method \"%s\"", compression)));
22064
22065 return cmethod;
22066}
22067
22068/*
22069 * resolve column storage specification
22070 */
22071static char
22072GetAttributeStorage(Oid atttypid, const char *storagemode)
22073{
22074 char cstorage = 0;
22075
22076 if (pg_strcasecmp(storagemode, "plain") == 0)
22077 cstorage = TYPSTORAGE_PLAIN;
22078 else if (pg_strcasecmp(storagemode, "external") == 0)
22079 cstorage = TYPSTORAGE_EXTERNAL;
22080 else if (pg_strcasecmp(storagemode, "extended") == 0)
22081 cstorage = TYPSTORAGE_EXTENDED;
22082 else if (pg_strcasecmp(storagemode, "main") == 0)
22083 cstorage = TYPSTORAGE_MAIN;
22084 else if (pg_strcasecmp(storagemode, "default") == 0)
22085 cstorage = get_typstorage(atttypid);
22086 else
22087 ereport(ERROR,
22088 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22089 errmsg("invalid storage type \"%s\"",
22090 storagemode)));
22091
22092 /*
22093 * safety check: do not allow toasted storage modes unless column datatype
22094 * is TOAST-aware.
22095 */
22096 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22097 ereport(ERROR,
22098 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22099 errmsg("column data type %s can only have storage PLAIN",
22100 format_type_be(atttypid))));
22101
22102 return cstorage;
22103}
22104
22105/*
22106 * buildExpressionExecutionStates: build the needed expression execution states
22107 * for new partition (newPartRel) checks and initialize expressions for
22108 * generated columns. All expressions should be created in "tab"
22109 * (AlteredTableInfo structure).
22110 */
22111static void
22113{
22114 /*
22115 * Build the needed expression execution states. Here, we expect only NOT
22116 * NULL and CHECK constraint.
22117 */
22119 {
22120 switch (con->contype)
22121 {
22122 case CONSTR_CHECK:
22123
22124 /*
22125 * We already expanded virtual expression in
22126 * createTableConstraints.
22127 */
22128 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22129 break;
22130 case CONSTR_NOTNULL:
22131 /* Nothing to do here. */
22132 break;
22133 default:
22134 elog(ERROR, "unrecognized constraint type: %d",
22135 (int) con->contype);
22136 }
22137 }
22138
22139 /* Expression already planned in createTableConstraints */
22141 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22142}
22143
22144/*
22145 * evaluateGeneratedExpressionsAndCheckConstraints: evaluate any generated
22146 * expressions for "tab" (AlteredTableInfo structure) whose inputs come from
22147 * the new tuple (insertslot) of the new partition (newPartRel).
22148 */
22149static void
22151 Relation newPartRel,
22152 TupleTableSlot *insertslot,
22153 ExprContext *econtext)
22154{
22155 econtext->ecxt_scantuple = insertslot;
22156
22158 {
22159 if (!ex->is_generated)
22160 continue;
22161
22162 insertslot->tts_values[ex->attnum - 1]
22163 = ExecEvalExpr(ex->exprstate,
22164 econtext,
22165 &insertslot->tts_isnull[ex->attnum - 1]);
22166 }
22167
22169 {
22170 switch (con->contype)
22171 {
22172 case CONSTR_CHECK:
22173 if (!ExecCheck(con->qualstate, econtext))
22174 ereport(ERROR,
22175 errcode(ERRCODE_CHECK_VIOLATION),
22176 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22177 con->name, RelationGetRelationName(newPartRel)),
22178 errtableconstraint(newPartRel, con->name));
22179 break;
22180 case CONSTR_NOTNULL:
22181 case CONSTR_FOREIGN:
22182 /* Nothing to do here */
22183 break;
22184 default:
22185 elog(ERROR, "unrecognized constraint type: %d",
22186 (int) con->contype);
22187 }
22188 }
22189}
22190
22191/*
22192 * getAttributesList: build a list of columns (ColumnDef) based on parent_rel
22193 */
22194static List *
22196{
22197 AttrNumber parent_attno;
22198 TupleDesc modelDesc;
22199 List *colList = NIL;
22200
22201 modelDesc = RelationGetDescr(parent_rel);
22202
22203 for (parent_attno = 1; parent_attno <= modelDesc->natts;
22204 parent_attno++)
22205 {
22206 Form_pg_attribute attribute = TupleDescAttr(modelDesc,
22207 parent_attno - 1);
22208 ColumnDef *def;
22209
22210 /* Ignore dropped columns in the parent. */
22211 if (attribute->attisdropped)
22212 continue;
22213
22214 def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22215 attribute->atttypmod, attribute->attcollation);
22216
22217 def->is_not_null = attribute->attnotnull;
22218
22219 /* Copy identity. */
22220 def->identity = attribute->attidentity;
22221
22222 /* Copy attgenerated. */
22223 def->generated = attribute->attgenerated;
22224
22225 def->storage = attribute->attstorage;
22226
22227 /* Likewise, copy compression. */
22228 if (CompressionMethodIsValid(attribute->attcompression))
22229 def->compression =
22230 pstrdup(GetCompressionMethodName(attribute->attcompression));
22231 else
22232 def->compression = NULL;
22233
22234 /* Add to column list. */
22235 colList = lappend(colList, def);
22236 }
22237
22238 return colList;
22239}
22240
22241/*
22242 * createTableConstraints:
22243 * create check constraints, default values, and generated values for newRel
22244 * based on parent_rel. tab is pending-work queue for newRel, we may need it in
22245 * MergePartitionsMoveRows.
22246 */
22247static void
22249 Relation parent_rel, Relation newRel)
22250{
22251 TupleDesc tupleDesc;
22252 TupleConstr *constr;
22253 AttrMap *attmap;
22254 AttrNumber parent_attno;
22255 int ccnum;
22256 List *constraints = NIL;
22257 List *cookedConstraints = NIL;
22258
22259 tupleDesc = RelationGetDescr(parent_rel);
22260 constr = tupleDesc->constr;
22261
22262 if (!constr)
22263 return;
22264
22265 /*
22266 * Construct a map from the parent relation's attnos to the child rel's.
22267 * This re-checks type match, etc, although it shouldn't be possible to
22268 * have a failure since both tables are locked.
22269 */
22270 attmap = build_attrmap_by_name(RelationGetDescr(newRel),
22271 tupleDesc,
22272 false);
22273
22274 /* Cycle for default values. */
22275 for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
22276 {
22277 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
22278 parent_attno - 1);
22279
22280 /* Ignore dropped columns in the parent. */
22281 if (attribute->attisdropped)
22282 continue;
22283
22284 /* Copy the default, if present, and it should be copied. */
22285 if (attribute->atthasdef)
22286 {
22287 Node *this_default = NULL;
22288 bool found_whole_row;
22289 AttrNumber num;
22290 Node *def;
22292
22293 if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
22294 this_default = build_generation_expression(parent_rel, attribute->attnum);
22295 else
22296 {
22297 this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
22298 if (this_default == NULL)
22299 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
22300 attribute->attnum, RelationGetRelationName(parent_rel));
22301 }
22302
22303 num = attmap->attnums[parent_attno - 1];
22304 def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
22305
22306 if (found_whole_row && attribute->attgenerated != '\0')
22307 elog(ERROR, "cannot convert whole-row table reference");
22308
22309 /* Add a pre-cooked default expression. */
22310 StoreAttrDefault(newRel, num, def, true);
22311
22312 /*
22313 * Stored generated column expressions in parent_rel might
22314 * reference the tableoid. newRel, parent_rel tableoid clear is
22315 * not the same. If so, these stored generated columns require
22316 * recomputation for newRel within MergePartitionsMoveRows.
22317 */
22318 if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
22319 {
22321 newval->attnum = num;
22322 newval->expr = expression_planner((Expr *) def);
22323 newval->is_generated = (attribute->attgenerated != '\0');
22324 tab->newvals = lappend(tab->newvals, newval);
22325 }
22326 }
22327 }
22328
22329 /* Cycle for CHECK constraints. */
22330 for (ccnum = 0; ccnum < constr->num_check; ccnum++)
22331 {
22332 char *ccname = constr->check[ccnum].ccname;
22333 char *ccbin = constr->check[ccnum].ccbin;
22334 bool ccenforced = constr->check[ccnum].ccenforced;
22335 bool ccnoinherit = constr->check[ccnum].ccnoinherit;
22336 bool ccvalid = constr->check[ccnum].ccvalid;
22337 Node *ccbin_node;
22338 bool found_whole_row;
22339 Constraint *constr;
22340
22341 /*
22342 * The partitioned table can not have a NO INHERIT check constraint
22343 * (see StoreRelCheck function for details).
22344 */
22345 Assert(!ccnoinherit);
22346
22347 ccbin_node = map_variable_attnos(stringToNode(ccbin),
22348 1, 0,
22349 attmap,
22350 InvalidOid, &found_whole_row);
22351
22352 /*
22353 * For the moment we have to reject whole-row variables (as for CREATE
22354 * TABLE LIKE and inheritances).
22355 */
22356 if (found_whole_row)
22357 elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
22358 ccname,
22359 RelationGetRelationName(parent_rel));
22360
22361 constr = makeNode(Constraint);
22362 constr->contype = CONSTR_CHECK;
22363 constr->conname = pstrdup(ccname);
22364 constr->deferrable = false;
22365 constr->initdeferred = false;
22366 constr->is_enforced = ccenforced;
22367 constr->skip_validation = !ccvalid;
22368 constr->initially_valid = ccvalid;
22369 constr->is_no_inherit = ccnoinherit;
22370 constr->raw_expr = NULL;
22371 constr->cooked_expr = nodeToString(ccbin_node);
22372 constr->location = -1;
22373 constraints = lappend(constraints, constr);
22374 }
22375
22376 /* Install all CHECK constraints. */
22377 cookedConstraints = AddRelationNewConstraints(newRel, NIL, constraints,
22378 false, true, true, NULL);
22379
22380 /* Make the additional catalog changes visible. */
22382
22383 /*
22384 * parent_rel check constraint expression may reference tableoid, so later
22385 * in MergePartitionsMoveRows, we need to evaluate the check constraint
22386 * again for the newRel. We can check whether the check constraint
22387 * contains a tableoid reference via pull_varattnos.
22388 */
22389 foreach_ptr(CookedConstraint, ccon, cookedConstraints)
22390 {
22391 if (!ccon->skip_validation)
22392 {
22393 Node *qual;
22394 Bitmapset *attnums = NULL;
22395
22396 Assert(ccon->contype == CONSTR_CHECK);
22397 qual = expand_generated_columns_in_expr(ccon->expr, newRel, 1);
22398 pull_varattnos(qual, 1, &attnums);
22399
22400 /*
22401 * Add a check only if it contains a tableoid
22402 * (TableOidAttributeNumber).
22403 */
22405 attnums))
22406 {
22407 NewConstraint *newcon;
22408
22409 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
22410 newcon->name = ccon->name;
22411 newcon->contype = CONSTR_CHECK;
22412 newcon->qual = qual;
22413
22414 tab->constraints = lappend(tab->constraints, newcon);
22415 }
22416 }
22417 }
22418
22419 /* Don't need the cookedConstraints anymore. */
22420 list_free_deep(cookedConstraints);
22421
22422 /* Reproduce not-null constraints. */
22423 if (constr->has_not_null)
22424 {
22425 List *nnconstraints;
22426
22427 /*
22428 * The "include_noinh" argument is false because a partitioned table
22429 * can't have NO INHERIT constraint.
22430 */
22431 nnconstraints = RelationGetNotNullConstraints(RelationGetRelid(parent_rel),
22432 false, false);
22433
22434 Assert(list_length(nnconstraints) > 0);
22435
22436 /*
22437 * We already set pg_attribute.attnotnull in createPartitionTable. No
22438 * need call set_attnotnull again.
22439 */
22440 AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, true, NULL);
22441 }
22442}
22443
22444/*
22445 * createPartitionTable:
22446 *
22447 * Create a new partition (newPartName) for the partitioned table (parent_rel).
22448 * ownerId is determined by the partition on which the operation is performed,
22449 * so it is passed separately. The new partition will inherit the access method
22450 * and persistence type from the parent table.
22451 *
22452 * Returns the created relation (locked in AccessExclusiveLock mode).
22453 */
22454static Relation
22455createPartitionTable(List **wqueue, RangeVar *newPartName,
22456 Relation parent_rel, Oid ownerId)
22457{
22458 Relation newRel;
22459 Oid newRelId;
22460 Oid existingRelid;
22462 List *colList = NIL;
22463 Oid relamId;
22464 Oid namespaceId;
22465 AlteredTableInfo *new_partrel_tab;
22466 Form_pg_class parent_relform = parent_rel->rd_rel;
22467
22468 /* If the existing rel is temp, it must belong to this session. */
22469 if (RELATION_IS_OTHER_TEMP(parent_rel))
22470 ereport(ERROR,
22471 errcode(ERRCODE_WRONG_OBJECT_TYPE),
22472 errmsg("cannot create as partition of temporary relation of another session"));
22473
22474 /* Look up inheritance ancestors and generate the relation schema. */
22475 colList = getAttributesList(parent_rel);
22476
22477 /* Create a tuple descriptor from the relation schema. */
22479
22480 /* Look up the access method for the new relation. */
22481 relamId = (parent_relform->relam != InvalidOid) ? parent_relform->relam : HEAP_TABLE_AM_OID;
22482
22483 /* Look up the namespace in which we are supposed to create the relation. */
22484 namespaceId =
22485 RangeVarGetAndCheckCreationNamespace(newPartName, NoLock, &existingRelid);
22486 if (OidIsValid(existingRelid))
22487 ereport(ERROR,
22488 errcode(ERRCODE_DUPLICATE_TABLE),
22489 errmsg("relation \"%s\" already exists", newPartName->relname));
22490
22491 /*
22492 * We intended to create the partition with the same persistence as the
22493 * parent table, but we still need to recheck because that might be
22494 * affected by the search_path. If the parent is permanent, so must be
22495 * all of its partitions.
22496 */
22497 if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22498 newPartName->relpersistence == RELPERSISTENCE_TEMP)
22499 ereport(ERROR,
22500 errcode(ERRCODE_WRONG_OBJECT_TYPE),
22501 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22502 RelationGetRelationName(parent_rel)));
22503
22504 /* Permanent rels cannot be partitions belonging to a temporary parent. */
22505 if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22506 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22507 ereport(ERROR,
22508 errcode(ERRCODE_WRONG_OBJECT_TYPE),
22509 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
22510 RelationGetRelationName(parent_rel)));
22511
22512 /* Create the relation. */
22513 newRelId = heap_create_with_catalog(newPartName->relname,
22514 namespaceId,
22515 parent_relform->reltablespace,
22516 InvalidOid,
22517 InvalidOid,
22518 InvalidOid,
22519 ownerId,
22520 relamId,
22521 descriptor,
22522 NIL,
22523 RELKIND_RELATION,
22524 newPartName->relpersistence,
22525 false,
22526 false,
22528 (Datum) 0,
22529 true,
22531 true,
22532 InvalidOid,
22533 NULL);
22534
22535 /*
22536 * We must bump the command counter to make the newly-created relation
22537 * tuple visible for opening.
22538 */
22540
22541 /*
22542 * Open the new partition with no lock, because we already have an
22543 * AccessExclusiveLock placed there after creation.
22544 */
22545 newRel = table_open(newRelId, NoLock);
22546
22547 /* Find or create a work queue entry for the newly created table. */
22548 new_partrel_tab = ATGetQueueEntry(wqueue, newRel);
22549
22550 /* Create constraints, default values, and generated values. */
22551 createTableConstraints(wqueue, new_partrel_tab, parent_rel, newRel);
22552
22553 /*
22554 * Need to call CommandCounterIncrement, so a fresh relcache entry has
22555 * newly installed constraint info.
22556 */
22558
22559 return newRel;
22560}
22561
22562/*
22563 * MergePartitionsMoveRows: scan partitions to be merged (mergingPartitions)
22564 * of the partitioned table and move rows into the new partition
22565 * (newPartRel). We also verify check constraints against these rows.
22566 */
22567static void
22568MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
22569{
22570 CommandId mycid;
22571 EState *estate;
22572 AlteredTableInfo *tab;
22573 ListCell *ltab;
22574
22575 /* The FSM is empty, so don't bother using it. */
22576 int ti_options = TABLE_INSERT_SKIP_FSM;
22577 BulkInsertState bistate; /* state of bulk inserts for partition */
22578 TupleTableSlot *dstslot;
22579
22580 /* Find the work queue entry for the new partition table: newPartRel. */
22581 tab = ATGetQueueEntry(wqueue, newPartRel);
22582
22583 /* Generate the constraint and default execution states. */
22584 estate = CreateExecutorState();
22585
22586 buildExpressionExecutionStates(tab, newPartRel, estate);
22587
22588 mycid = GetCurrentCommandId(true);
22589
22590 /* Prepare a BulkInsertState for table_tuple_insert. */
22591 bistate = GetBulkInsertState();
22592
22593 /* Create the necessary tuple slot. */
22594 dstslot = table_slot_create(newPartRel, NULL);
22595
22596 foreach_oid(merging_oid, mergingPartitions)
22597 {
22598 ExprContext *econtext;
22599 TupleTableSlot *srcslot;
22600 TupleConversionMap *tuple_map;
22601 TableScanDesc scan;
22602 MemoryContext oldCxt;
22603 Snapshot snapshot;
22604 Relation mergingPartition;
22605
22606 econtext = GetPerTupleExprContext(estate);
22607
22608 /*
22609 * Partition is already locked in the transformPartitionCmdForMerge
22610 * function.
22611 */
22612 mergingPartition = table_open(merging_oid, NoLock);
22613
22614 /* Create a source tuple slot for the partition being merged. */
22615 srcslot = table_slot_create(mergingPartition, NULL);
22616
22617 /*
22618 * Map computing for moving attributes of the merged partition to the
22619 * new partition.
22620 */
22621 tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
22622 RelationGetDescr(newPartRel));
22623
22624 /* Scan through the rows. */
22625 snapshot = RegisterSnapshot(GetLatestSnapshot());
22626 scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
22627
22628 /*
22629 * Switch to per-tuple memory context and reset it for each tuple
22630 * produced, so we don't leak memory.
22631 */
22633
22634 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
22635 {
22636 TupleTableSlot *insertslot;
22637
22639
22640 if (tuple_map)
22641 {
22642 /* Need to use a map to copy attributes. */
22643 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
22644 }
22645 else
22646 {
22647 slot_getallattrs(srcslot);
22648
22649 /* Copy attributes directly. */
22650 insertslot = dstslot;
22651
22652 ExecClearTuple(insertslot);
22653
22654 memcpy(insertslot->tts_values, srcslot->tts_values,
22655 sizeof(Datum) * srcslot->tts_nvalid);
22656 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
22657 sizeof(bool) * srcslot->tts_nvalid);
22658
22659 ExecStoreVirtualTuple(insertslot);
22660 }
22661
22662 /*
22663 * Constraints and GENERATED expressions might reference the
22664 * tableoid column, so fill tts_tableOid with the desired value.
22665 * (We must do this each time, because it gets overwritten with
22666 * newrel's OID during storing.)
22667 */
22668 insertslot->tts_tableOid = RelationGetRelid(newPartRel);
22669
22670 /*
22671 * Now, evaluate any generated expressions whose inputs come from
22672 * the new tuple. We assume these columns won't reference each
22673 * other, so that there's no ordering dependency.
22674 */
22676 insertslot, econtext);
22677
22678 /* Write the tuple out to the new relation. */
22679 table_tuple_insert(newPartRel, insertslot, mycid,
22680 ti_options, bistate);
22681
22682 ResetExprContext(econtext);
22683 }
22684
22685 MemoryContextSwitchTo(oldCxt);
22686 table_endscan(scan);
22687 UnregisterSnapshot(snapshot);
22688
22689 if (tuple_map)
22690 free_conversion_map(tuple_map);
22691
22693 table_close(mergingPartition, NoLock);
22694 }
22695
22696 FreeExecutorState(estate);
22698 FreeBulkInsertState(bistate);
22699
22700 table_finish_bulk_insert(newPartRel, ti_options);
22701
22702 /*
22703 * We don't need to process this newPartRel since we already processed it
22704 * here, so delete the ALTER TABLE queue for it.
22705 */
22706 foreach(ltab, *wqueue)
22707 {
22708 tab = (AlteredTableInfo *) lfirst(ltab);
22709 if (tab->relid == RelationGetRelid(newPartRel))
22710 {
22711 *wqueue = list_delete_cell(*wqueue, ltab);
22712 break;
22713 }
22714 }
22715}
22716
22717/*
22718 * detachPartitionTable: detach partition "child_rel" from partitioned table
22719 * "parent_rel" with default partition identifier "defaultPartOid"
22720 */
22721static void
22722detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
22723{
22724 /* Remove the pg_inherits row first. */
22725 RemoveInheritance(child_rel, parent_rel, false);
22726
22727 /*
22728 * Detaching the partition might involve TOAST table access, so ensure we
22729 * have a valid snapshot.
22730 */
22732
22733 /* Do the final part of detaching. */
22734 DetachPartitionFinalize(parent_rel, child_rel, false, defaultPartOid);
22735
22737}
22738
22739/*
22740 * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
22741 */
22742static void
22745{
22746 Relation newPartRel;
22747 List *mergingPartitions = NIL;
22748 Oid defaultPartOid;
22749 Oid existingRelid;
22750 Oid ownerId = InvalidOid;
22751 Oid save_userid;
22752 int save_sec_context;
22753 int save_nestlevel;
22754
22755 /*
22756 * Check ownership of merged partitions - partitions with different owners
22757 * cannot be merged. Also, collect the OIDs of these partitions during the
22758 * check.
22759 */
22761 {
22762 Relation mergingPartition;
22763
22764 /*
22765 * We are going to detach and remove this partition. We already took
22766 * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
22767 * NoLock is fine.
22768 */
22769 mergingPartition = table_openrv_extended(name, NoLock, false);
22770 Assert(CheckRelationLockedByMe(mergingPartition, AccessExclusiveLock, false));
22771
22772 if (OidIsValid(ownerId))
22773 {
22774 /* Do the partitions being merged have different owners? */
22775 if (ownerId != mergingPartition->rd_rel->relowner)
22776 ereport(ERROR,
22777 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22778 errmsg("partitions being merged have different owners"));
22779 }
22780 else
22781 ownerId = mergingPartition->rd_rel->relowner;
22782
22783 /* Store the next merging partition into the list. */
22784 mergingPartitions = lappend_oid(mergingPartitions,
22785 RelationGetRelid(mergingPartition));
22786
22787 table_close(mergingPartition, NoLock);
22788 }
22789
22790 /* Look up the existing relation by the new partition name. */
22791 RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, &existingRelid);
22792
22793 /*
22794 * Check if this name is already taken. This helps us to detect the
22795 * situation when one of the merging partitions has the same name as the
22796 * new partition. Otherwise, this would fail later on anyway, but
22797 * catching this here allows us to emit a nicer error message.
22798 */
22799 if (OidIsValid(existingRelid))
22800 {
22801 if (list_member_oid(mergingPartitions, existingRelid))
22802 {
22803 /*
22804 * The new partition has the same name as one of the merging
22805 * partitions.
22806 */
22807 char tmpRelName[NAMEDATALEN];
22808
22809 /* Generate a temporary name. */
22810 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
22811
22812 /*
22813 * Rename the existing partition with a temporary name, leaving it
22814 * free for the new partition. We don't need to care about this
22815 * in the future because we're going to eventually drop the
22816 * existing partition anyway.
22817 */
22818 RenameRelationInternal(existingRelid, tmpRelName, true, false);
22819
22820 /*
22821 * We must bump the command counter to make the new partition
22822 * tuple visible for rename.
22823 */
22825 }
22826 else
22827 {
22828 ereport(ERROR,
22829 errcode(ERRCODE_DUPLICATE_TABLE),
22830 errmsg("relation \"%s\" already exists", cmd->name->relname));
22831 }
22832 }
22833
22834 defaultPartOid =
22836
22837 /* Detach all merging partitions. */
22838 foreach_oid(mergingPartitionOid, mergingPartitions)
22839 {
22840 Relation child_rel;
22841
22842 child_rel = table_open(mergingPartitionOid, NoLock);
22843
22844 detachPartitionTable(rel, child_rel, defaultPartOid);
22845
22846 table_close(child_rel, NoLock);
22847 }
22848
22849 /*
22850 * Perform a preliminary check to determine whether it's safe to drop all
22851 * merging partitions before we actually do so later. After merging rows
22852 * into the new partitions via MergePartitionsMoveRows, all old partitions
22853 * need to be dropped. However, since the drop behavior is DROP_RESTRICT
22854 * and the merge process (MergePartitionsMoveRows) can be time-consuming,
22855 * performing an early check on the drop eligibility of old partitions is
22856 * preferable.
22857 */
22858 foreach_oid(mergingPartitionOid, mergingPartitions)
22859 {
22860 ObjectAddress object;
22861
22862 /* Get oid of the later to be dropped relation. */
22863 object.objectId = mergingPartitionOid;
22864 object.classId = RelationRelationId;
22865 object.objectSubId = 0;
22866
22868 }
22869
22870 /*
22871 * Create a table for the new partition, using the partitioned table as a
22872 * model.
22873 */
22874 Assert(OidIsValid(ownerId));
22875 newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
22876
22877 /*
22878 * Switch to the table owner's userid, so that any index functions are run
22879 * as that user. Also, lockdown security-restricted operations and
22880 * arrange to make GUC variable changes local to this command.
22881 *
22882 * Need to do it after determining the namespace in the
22883 * createPartitionTable() call.
22884 */
22885 GetUserIdAndSecContext(&save_userid, &save_sec_context);
22886 SetUserIdAndSecContext(ownerId,
22887 save_sec_context | SECURITY_RESTRICTED_OPERATION);
22888 save_nestlevel = NewGUCNestLevel();
22890
22891 /* Copy data from merged partitions to the new partition. */
22892 MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel);
22893
22894 /* Drop the current partitions before attaching the new one. */
22895 foreach_oid(mergingPartitionOid, mergingPartitions)
22896 {
22897 ObjectAddress object;
22898
22899 object.objectId = mergingPartitionOid;
22900 object.classId = RelationRelationId;
22901 object.objectSubId = 0;
22902
22903 performDeletion(&object, DROP_RESTRICT, 0);
22904 }
22905
22906 list_free(mergingPartitions);
22907
22908 /*
22909 * Attach a new partition to the partitioned table. wqueue = NULL:
22910 * verification for each cloned constraint is not needed.
22911 */
22912 attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
22913
22914 /* Keep the lock until commit. */
22915 table_close(newPartRel, NoLock);
22916
22917 /* Roll back any GUC changes executed by index functions. */
22918 AtEOXact_GUC(false, save_nestlevel);
22919
22920 /* Restore the userid and security context. */
22921 SetUserIdAndSecContext(save_userid, save_sec_context);
22922}
22923
22924/*
22925 * Struct with the context of the new partition for inserting rows from the
22926 * split partition.
22927 */
22929{
22930 ExprState *partqualstate; /* expression for checking a slot for a
22931 * partition (NULL for DEFAULT partition) */
22932 BulkInsertState bistate; /* state of bulk inserts for partition */
22933 TupleTableSlot *dstslot; /* slot for inserting row into partition */
22934 AlteredTableInfo *tab; /* structure with generated column expressions
22935 * and check constraint expressions. */
22936 Relation partRel; /* relation for partition */
22938
22939/*
22940 * createSplitPartitionContext: create context for partition and fill it
22941 */
22942static SplitPartitionContext *
22944{
22946
22948 pc->partRel = partRel;
22949
22950 /*
22951 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
22952 * don't bother using it.
22953 */
22955
22956 /* Create a destination tuple slot for the new partition. */
22957 pc->dstslot = table_slot_create(pc->partRel, NULL);
22958
22959 return pc;
22960}
22961
22962/*
22963 * deleteSplitPartitionContext: delete context for partition
22964 */
22965static void
22967{
22968 ListCell *ltab;
22969
22972
22973 table_finish_bulk_insert(pc->partRel, ti_options);
22974
22975 /*
22976 * We don't need to process this pc->partRel so delete the ALTER TABLE
22977 * queue of it.
22978 */
22979 foreach(ltab, *wqueue)
22980 {
22981 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
22982
22983 if (tab->relid == RelationGetRelid(pc->partRel))
22984 {
22985 *wqueue = list_delete_cell(*wqueue, ltab);
22986 break;
22987 }
22988 }
22989
22990 pfree(pc);
22991}
22992
22993/*
22994 * SplitPartitionMoveRows: scan split partition (splitRel) of partitioned table
22995 * (rel) and move rows into new partitions.
22996 *
22997 * New partitions description:
22998 * partlist: list of pointers to SinglePartitionSpec structures. It contains
22999 * the partition specification details for all new partitions.
23000 * newPartRels: list of Relations, new partitions created in
23001 * ATExecSplitPartition.
23002 */
23003static void
23005 List *partlist, List *newPartRels)
23006{
23007 /* The FSM is empty, so don't bother using it. */
23008 int ti_options = TABLE_INSERT_SKIP_FSM;
23009 CommandId mycid;
23010 EState *estate;
23011 ListCell *listptr,
23012 *listptr2;
23013 TupleTableSlot *srcslot;
23014 ExprContext *econtext;
23015 TableScanDesc scan;
23016 Snapshot snapshot;
23017 MemoryContext oldCxt;
23018 List *partContexts = NIL;
23019 TupleConversionMap *tuple_map;
23020 SplitPartitionContext *defaultPartCtx = NULL,
23021 *pc;
23022
23023 mycid = GetCurrentCommandId(true);
23024
23025 estate = CreateExecutorState();
23026
23027 forboth(listptr, partlist, listptr2, newPartRels)
23028 {
23030
23032
23033 /* Find the work queue entry for the new partition table: newPartRel. */
23034 pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
23035
23036 buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
23037
23038 if (sps->bound->is_default)
23039 {
23040 /*
23041 * We should not create a structure to check the partition
23042 * constraint for the new DEFAULT partition.
23043 */
23044 defaultPartCtx = pc;
23045 }
23046 else
23047 {
23048 List *partConstraint;
23049
23050 /* Build expression execution states for partition check quals. */
23051 partConstraint = get_qual_from_partbound(rel, sps->bound);
23052 partConstraint =
23054 (Node *) partConstraint);
23055 /* Make a boolean expression for ExecCheck(). */
23056 partConstraint = list_make1(make_ands_explicit(partConstraint));
23057
23058 /*
23059 * Map the vars in the constraint expression from rel's attnos to
23060 * splitRel's.
23061 */
23062 partConstraint = map_partition_varattnos(partConstraint,
23063 1, splitRel, rel);
23064
23065 pc->partqualstate =
23066 ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
23067 Assert(pc->partqualstate != NULL);
23068 }
23069
23070 /* Store partition context into a list. */
23071 partContexts = lappend(partContexts, pc);
23072 }
23073
23074 econtext = GetPerTupleExprContext(estate);
23075
23076 /* Create the necessary tuple slot. */
23077 srcslot = table_slot_create(splitRel, NULL);
23078
23079 /*
23080 * Map computing for moving attributes of the split partition to the new
23081 * partition (for the first new partition, but other new partitions can
23082 * use the same map).
23083 */
23084 pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
23085 tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
23086 RelationGetDescr(pc->partRel));
23087
23088 /* Scan through the rows. */
23089 snapshot = RegisterSnapshot(GetLatestSnapshot());
23090 scan = table_beginscan(splitRel, snapshot, 0, NULL);
23091
23092 /*
23093 * Switch to per-tuple memory context and reset it for each tuple
23094 * produced, so we don't leak memory.
23095 */
23097
23098 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
23099 {
23100 bool found = false;
23101 TupleTableSlot *insertslot;
23102
23104
23105 econtext->ecxt_scantuple = srcslot;
23106
23107 /* Search partition for the current slot, srcslot. */
23108 foreach(listptr, partContexts)
23109 {
23110 pc = (SplitPartitionContext *) lfirst(listptr);
23111
23112 /* skip DEFAULT partition */
23113 if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
23114 {
23115 found = true;
23116 break;
23117 }
23118 }
23119 if (!found)
23120 {
23121 /* Use the DEFAULT partition if it exists. */
23122 if (defaultPartCtx)
23123 pc = defaultPartCtx;
23124 else
23125 ereport(ERROR,
23126 errcode(ERRCODE_CHECK_VIOLATION),
23127 errmsg("can not find partition for split partition row"),
23128 errtable(splitRel));
23129 }
23130
23131 if (tuple_map)
23132 {
23133 /* Need to use a map to copy attributes. */
23134 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
23135 }
23136 else
23137 {
23138 /* Extract data from the old tuple. */
23139 slot_getallattrs(srcslot);
23140
23141 /* Copy attributes directly. */
23142 insertslot = pc->dstslot;
23143
23144 ExecClearTuple(insertslot);
23145
23146 memcpy(insertslot->tts_values, srcslot->tts_values,
23147 sizeof(Datum) * srcslot->tts_nvalid);
23148 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23149 sizeof(bool) * srcslot->tts_nvalid);
23150
23151 ExecStoreVirtualTuple(insertslot);
23152 }
23153
23154 /*
23155 * Constraints and GENERATED expressions might reference the tableoid
23156 * column, so fill tts_tableOid with the desired value. (We must do
23157 * this each time, because it gets overwritten with newrel's OID
23158 * during storing.)
23159 */
23160 insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
23161
23162 /*
23163 * Now, evaluate any generated expressions whose inputs come from the
23164 * new tuple. We assume these columns won't reference each other, so
23165 * that there's no ordering dependency.
23166 */
23168 insertslot, econtext);
23169
23170 /* Write the tuple out to the new relation. */
23171 table_tuple_insert(pc->partRel, insertslot, mycid,
23172 ti_options, pc->bistate);
23173
23174 ResetExprContext(econtext);
23175 }
23176
23177 MemoryContextSwitchTo(oldCxt);
23178
23179 table_endscan(scan);
23180 UnregisterSnapshot(snapshot);
23181
23182 if (tuple_map)
23183 free_conversion_map(tuple_map);
23184
23186
23187 FreeExecutorState(estate);
23188
23189 foreach_ptr(SplitPartitionContext, spc, partContexts)
23190 deleteSplitPartitionContext(spc, wqueue, ti_options);
23191}
23192
23193/*
23194 * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
23195 */
23196static void
23199{
23200 Relation splitRel;
23201 Oid splitRelOid;
23202 ListCell *listptr,
23203 *listptr2;
23204 bool isSameName = false;
23205 char tmpRelName[NAMEDATALEN];
23206 List *newPartRels = NIL;
23207 ObjectAddress object;
23208 Oid defaultPartOid;
23209 Oid save_userid;
23210 int save_sec_context;
23211 int save_nestlevel;
23212
23213 defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
23214
23215 /*
23216 * Partition is already locked in the transformPartitionCmdForSplit
23217 * function.
23218 */
23219 splitRel = table_openrv(cmd->name, NoLock);
23220
23221 splitRelOid = RelationGetRelid(splitRel);
23222
23223 /* Check descriptions of new partitions. */
23225 {
23226 Oid existingRelid;
23227
23228 /* Look up the existing relation by the new partition name. */
23229 RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, &existingRelid);
23230
23231 /*
23232 * This would fail later on anyway if the relation already exists. But
23233 * by catching it here, we can emit a nicer error message.
23234 */
23235 if (existingRelid == splitRelOid && !isSameName)
23236 /* One new partition can have the same name as a split partition. */
23237 isSameName = true;
23238 else if (OidIsValid(existingRelid))
23239 ereport(ERROR,
23240 errcode(ERRCODE_DUPLICATE_TABLE),
23241 errmsg("relation \"%s\" already exists", sps->name->relname));
23242 }
23243
23244 /* Detach the split partition. */
23245 detachPartitionTable(rel, splitRel, defaultPartOid);
23246
23247 /*
23248 * Perform a preliminary check to determine whether it's safe to drop the
23249 * split partition before we actually do so later. After merging rows into
23250 * the new partitions via SplitPartitionMoveRows, all old partitions need
23251 * to be dropped. However, since the drop behavior is DROP_RESTRICT and
23252 * the merge process (SplitPartitionMoveRows) can be time-consuming,
23253 * performing an early check on the drop eligibility of old partitions is
23254 * preferable.
23255 */
23256 object.objectId = splitRelOid;
23257 object.classId = RelationRelationId;
23258 object.objectSubId = 0;
23260
23261 /*
23262 * If a new partition has the same name as the split partition, then we
23263 * should rename the split partition to reuse its name.
23264 */
23265 if (isSameName)
23266 {
23267 /*
23268 * We must bump the command counter to make the split partition tuple
23269 * visible for renaming.
23270 */
23272 /* Rename partition. */
23273 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23274 RenameRelationInternal(splitRelOid, tmpRelName, true, false);
23275
23276 /*
23277 * We must bump the command counter to make the split partition tuple
23278 * visible after renaming.
23279 */
23281 }
23282
23283 /* Create new partitions (like a split partition), without indexes. */
23285 {
23286 Relation newPartRel;
23287
23288 newPartRel = createPartitionTable(wqueue, sps->name, rel,
23289 splitRel->rd_rel->relowner);
23290 newPartRels = lappend(newPartRels, newPartRel);
23291 }
23292
23293 /*
23294 * Switch to the table owner's userid, so that any index functions are run
23295 * as that user. Also, lockdown security-restricted operations and
23296 * arrange to make GUC variable changes local to this command.
23297 *
23298 * Need to do it after determining the namespace in the
23299 * createPartitionTable() call.
23300 */
23301 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23302 SetUserIdAndSecContext(splitRel->rd_rel->relowner,
23303 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23304 save_nestlevel = NewGUCNestLevel();
23306
23307 /* Copy data from the split partition to the new partitions. */
23308 SplitPartitionMoveRows(wqueue, rel, splitRel, cmd->partlist, newPartRels);
23309 /* Keep the lock until commit. */
23310 table_close(splitRel, NoLock);
23311
23312 /* Attach new partitions to the partitioned table. */
23313 forboth(listptr, cmd->partlist, listptr2, newPartRels)
23314 {
23316 Relation newPartRel = (Relation) lfirst(listptr2);
23317
23318 /*
23319 * wqueue = NULL: verification for each cloned constraint is not
23320 * needed.
23321 */
23322 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
23323 /* Keep the lock until commit. */
23324 table_close(newPartRel, NoLock);
23325 }
23326
23327 /* Drop the split partition. */
23328 object.classId = RelationRelationId;
23329 object.objectId = splitRelOid;
23330 object.objectSubId = 0;
23331 /* Probably DROP_CASCADE is not needed. */
23332 performDeletion(&object, DROP_RESTRICT, 0);
23333
23334 /* Roll back any GUC changes executed by index functions. */
23335 AtEOXact_GUC(false, save_nestlevel);
23336
23337 /* Restore the userid and security context. */
23338 SetUserIdAndSecContext(save_userid, save_sec_context);
23339}
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:4942
#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:1194
#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:1032
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:1361
BulkInsertState GetBulkInsertState(void)
Definition: heapam.c:2036
void FreeBulkInsertState(BulkInsertState bistate)
Definition: heapam.c:2053
#define XLOG_HEAP_TRUNCATE
Definition: heapam_xlog.h:36
#define XLH_TRUNCATE_RESTART_SEQS
Definition: heapam_xlog.h:127
#define SizeOfHeapTruncate
Definition: heapam_xlog.h:142
#define XLH_TRUNCATE_CASCADE
Definition: heapam_xlog.h:126
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition: heaptuple.c:1210
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:778
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:456
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:904
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
#define MaxHeapAttributeNumber
Definition: htup_details.h:48
#define stmt
Definition: indent_codes.h:59
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3583
bool CompareIndexInfo(const IndexInfo *info1, const IndexInfo *info2, const Oid *collations1, const Oid *collations2, const Oid *opfamilies1, const Oid *opfamilies2, const AttrMap *attmap)
Definition: index.c:2537
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition: index.c:3948
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2428
void index_check_primary_key(Relation heapRel, const IndexInfo *indexInfo, bool is_alter_table, const IndexStmt *stmt)
Definition: index.c:202
ObjectAddress index_constraint_create(Relation heapRelation, Oid indexRelationId, Oid parentConstraintId, const IndexInfo *indexInfo, const char *constraintName, char constraintType, bits16 constr_flags, bool allow_system_table_mods, bool is_internal)
Definition: index.c:1885
#define REINDEX_REL_PROCESS_TOAST
Definition: index.h:159
#define INDEX_CONSTR_CREATE_UPDATE_INDEX
Definition: index.h:94
#define INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
Definition: index.h:95
#define INDEX_CONSTR_CREATE_DEFERRABLE
Definition: index.h:92
#define INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
Definition: index.h:91
#define INDEX_CONSTR_CREATE_INIT_DEFERRED
Definition: index.h:93
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
ObjectAddress DefineIndex(Oid tableId, IndexStmt *stmt, Oid indexRelationId, Oid parentIndexId, Oid parentConstraintId, int total_parts, bool is_alter_table, bool check_rights, bool check_not_in_use, bool skip_build, bool quiet)
Definition: indexcmds.c:541
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4445
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2345
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition: indexcmds.c:177
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:434
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2260
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition: indexing.c:313
void CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
Definition: indexing.c:365
long val
Definition: informix.c:689
static struct @171 value
static bool pg_add_s16_overflow(int16 a, int16 b, int16 *result)
Definition: int.h:67
void AcceptInvalidationMessages(void)
Definition: inval.c:930
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1631
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1687
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1665
int b
Definition: isn.c:74
int a
Definition: isn.c:73
int j
Definition: isn.c:78
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
Datum is_valid(PG_FUNCTION_ARGS)
Definition: isn.c: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:2093
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:949
Oid get_constraint_index(Oid conoid)
Definition: lsyscache.c:1204
char get_typstorage(Oid typid)
Definition: lsyscache.c:2584
bool get_index_isreplident(Oid index_oid)
Definition: lsyscache.c:3720
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2168
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3221
char * get_collation_name(Oid colloid)
Definition: lsyscache.c:1126
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3766
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:1172
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:918
bool get_collation_isdeterministic(Oid colloid)
Definition: lsyscache.c:1145
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1418
Oid get_rel_relam(Oid relid)
Definition: lsyscache.c:2265
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3246
Oid get_rel_tablespace(Oid relid)
Definition: lsyscache.c:2219
Oid get_typ_typrelid(Oid typid)
Definition: lsyscache.c:2896
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2703
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2686
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3531
char get_constraint_type(Oid conoid)
Definition: lsyscache.c:1234
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:2050
#define TypeIsToastable(typid)
Definition: lsyscache.h:218
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:799
TypeName * makeTypeNameFromNameList(List *names)
Definition: makefuncs.c:531
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
ColumnDef * makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid)
Definition: makefuncs.c:565
Const * makeNullConst(Oid consttype, int32 consttypmod, Oid constcollid)
Definition: makefuncs.c:388
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:473
Constraint * makeNotNullConstraint(String *colname)
Definition: makefuncs.c:493
List * make_ands_implicit(Expr *clause)
Definition: makefuncs.c:810
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:1746
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:400
char * pstrdup(const char *in)
Definition: mcxt.c:1759
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc0(Size size)
Definition: mcxt.c:1395
void * palloc(Size size)
Definition: mcxt.c:1365
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
MemoryContext CacheMemoryContext
Definition: mcxt.c:169
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
MemoryContext PortalContext
Definition: mcxt.c:175
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
#define 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:2575
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2848
#define ACL_MAINTAIN
Definition: parsenodes.h:90
#define ACL_USAGE
Definition: parsenodes.h:84
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2851
PartitionStrategy
Definition: parsenodes.h:899
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:902
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:900
ConstrType
Definition: parsenodes.h:2826
@ CONSTR_FOREIGN
Definition: parsenodes.h:2837
@ CONSTR_UNIQUE
Definition: parsenodes.h:2835
@ CONSTR_DEFAULT
Definition: parsenodes.h:2830
@ CONSTR_NOTNULL
Definition: parsenodes.h:2829
@ CONSTR_CHECK
Definition: parsenodes.h:2833
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2836
@ CONSTR_PRIMARY
Definition: parsenodes.h:2834
DropBehavior
Definition: parsenodes.h:2423
@ DROP_CASCADE
Definition: parsenodes.h:2425
@ DROP_RESTRICT
Definition: parsenodes.h:2424
ObjectType
Definition: parsenodes.h:2350
@ OBJECT_MATVIEW
Definition: parsenodes.h:2374
@ OBJECT_SCHEMA
Definition: parsenodes.h:2387
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2369
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2393
@ OBJECT_INDEX
Definition: parsenodes.h:2371
@ OBJECT_SEQUENCE
Definition: parsenodes.h:2388
@ OBJECT_TABLE
Definition: parsenodes.h:2392
@ OBJECT_VIEW
Definition: parsenodes.h:2402
@ OBJECT_TYPE
Definition: parsenodes.h:2400
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2391
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2364
AlterTableType
Definition: parsenodes.h:2442
@ AT_AddIndexConstraint
Definition: parsenodes.h:2464
@ AT_MergePartitions
Definition: parsenodes.h:2506
@ AT_DropOf
Definition: parsenodes.h:2495
@ AT_SetOptions
Definition: parsenodes.h:2452
@ AT_DropIdentity
Definition: parsenodes.h:2509
@ AT_DisableTrigUser
Definition: parsenodes.h:2487
@ AT_DropNotNull
Definition: parsenodes.h:2447
@ AT_AddOf
Definition: parsenodes.h:2494
@ AT_ResetOptions
Definition: parsenodes.h:2453
@ AT_ReplicaIdentity
Definition: parsenodes.h:2496
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2479
@ AT_EnableRowSecurity
Definition: parsenodes.h:2497
@ AT_AddColumnToView
Definition: parsenodes.h:2444
@ AT_ResetRelOptions
Definition: parsenodes.h:2478
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2482
@ AT_DropOids
Definition: parsenodes.h:2474
@ AT_SetIdentity
Definition: parsenodes.h:2508
@ AT_ReAddStatistics
Definition: parsenodes.h:2510
@ AT_SetUnLogged
Definition: parsenodes.h:2473
@ AT_DisableTrig
Definition: parsenodes.h:2483
@ AT_SetCompression
Definition: parsenodes.h:2455
@ AT_DropExpression
Definition: parsenodes.h:2450
@ AT_AddIndex
Definition: parsenodes.h:2457
@ AT_EnableReplicaRule
Definition: parsenodes.h:2490
@ AT_ReAddIndex
Definition: parsenodes.h:2458
@ AT_DropConstraint
Definition: parsenodes.h:2465
@ AT_SetNotNull
Definition: parsenodes.h:2448
@ AT_ClusterOn
Definition: parsenodes.h:2470
@ AT_AddIdentity
Definition: parsenodes.h:2507
@ AT_ForceRowSecurity
Definition: parsenodes.h:2499
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2489
@ AT_SetAccessMethod
Definition: parsenodes.h:2475
@ AT_AlterColumnType
Definition: parsenodes.h:2467
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2504
@ AT_AddInherit
Definition: parsenodes.h:2492
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2461
@ AT_EnableTrig
Definition: parsenodes.h:2480
@ AT_DropColumn
Definition: parsenodes.h:2456
@ AT_ReAddComment
Definition: parsenodes.h:2466
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2468
@ AT_DisableTrigAll
Definition: parsenodes.h:2485
@ AT_EnableRule
Definition: parsenodes.h:2488
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2500
@ AT_DetachPartition
Definition: parsenodes.h:2503
@ AT_SetStatistics
Definition: parsenodes.h:2451
@ AT_AttachPartition
Definition: parsenodes.h:2502
@ AT_AddConstraint
Definition: parsenodes.h:2459
@ AT_DropInherit
Definition: parsenodes.h:2493
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2481
@ AT_SetLogged
Definition: parsenodes.h:2472
@ AT_SetStorage
Definition: parsenodes.h:2454
@ AT_DisableRule
Definition: parsenodes.h:2491
@ AT_DisableRowSecurity
Definition: parsenodes.h:2498
@ AT_SetRelOptions
Definition: parsenodes.h:2477
@ AT_ChangeOwner
Definition: parsenodes.h:2469
@ AT_EnableTrigUser
Definition: parsenodes.h:2486
@ AT_SetExpression
Definition: parsenodes.h:2449
@ AT_ReAddConstraint
Definition: parsenodes.h:2460
@ AT_SetTableSpace
Definition: parsenodes.h:2476
@ AT_GenericOptions
Definition: parsenodes.h:2501
@ AT_ColumnDefault
Definition: parsenodes.h:2445
@ AT_CookedColumnDefault
Definition: parsenodes.h:2446
@ AT_AlterConstraint
Definition: parsenodes.h:2462
@ AT_EnableTrigAll
Definition: parsenodes.h:2484
@ AT_SplitPartition
Definition: parsenodes.h:2505
@ AT_DropCluster
Definition: parsenodes.h:2471
@ AT_ValidateConstraint
Definition: parsenodes.h:2463
@ AT_AddColumn
Definition: parsenodes.h:2443
#define ACL_REFERENCES
Definition: parsenodes.h:81
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2849
#define ACL_TRUNCATE
Definition: parsenodes.h:80
#define ACL_CREATE
Definition: parsenodes.h:85
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2850
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2847
@ 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:6763
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:4836
Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
Definition: relcache.c:5047
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:6103
int errtablecol(Relation rel, int attnum)
Definition: relcache.c:6066
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5210
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition: relcache.c:5303
void RelationSetNewRelfilenumber(Relation relation, char persistence)
Definition: relcache.c:3773
List * RelationGetFKeyList(Relation relation)
Definition: relcache.c:4731
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5097
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3976
int errtable(Relation rel)
Definition: relcache.c:6049
@ INDEX_ATTR_BITMAP_KEY
Definition: relcache.h:69
@ INDEX_ATTR_BITMAP_PRIMARY_KEY
Definition: relcache.h:70
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition: relcache.h:71
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1360
bytea * view_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2045
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2101
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2031
Datum transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1176
LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList)
Definition: reloptions.c:2155
bytea * attribute_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2116
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
Definition: reloptions.c:2066
#define HEAP_RELOPT_NAMESPACES
Definition: reloptions.h:61
Oid RelFileNumber
Definition: relpath.h:25
ForkNumber
Definition: relpath.h:56
@ MAIN_FORKNUM
Definition: relpath.h:58
@ INIT_FORKNUM
Definition: relpath.h:61
#define MAX_FORKNUM
Definition: relpath.h:70
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
void EnableDisableRule(Relation rel, const char *rulename, char fires_when)
#define RULE_FIRES_ON_ORIGIN
Definition: rewriteDefine.h:21
#define RULE_FIRES_ON_REPLICA
Definition: rewriteDefine.h:23
#define RULE_FIRES_ALWAYS
Definition: rewriteDefine.h:22
#define RULE_DISABLED
Definition: rewriteDefine.h:24
Query * get_view_query(Relation view)
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
Node * build_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:2521
DropBehavior behavior
Definition: parsenodes.h:2524
AlterTableType subtype
Definition: parsenodes.h:2516
RangeVar * relation
Definition: parsenodes.h:2435
ObjectType objtype
Definition: parsenodes.h:2437
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:3390
ObjectType objtype
Definition: parsenodes.h:3388
Node * object
Definition: parsenodes.h:3389
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:2864
ParseLoc location
Definition: parsenodes.h:2905
bool reset_default_tblspc
Definition: parsenodes.h:2886
List * keys
Definition: parsenodes.h:2876
List * pk_attrs
Definition: parsenodes.h:2894
List * fk_del_set_cols
Definition: parsenodes.h:2900
bool fk_with_period
Definition: parsenodes.h:2895
ConstrType contype
Definition: parsenodes.h:2861
Oid old_pktable_oid
Definition: parsenodes.h:2902
bool is_no_inherit
Definition: parsenodes.h:2868
char fk_upd_action
Definition: parsenodes.h:2898
List * old_conpfeqop
Definition: parsenodes.h:2901
bool is_enforced
Definition: parsenodes.h:2865
char fk_matchtype
Definition: parsenodes.h:2897
bool pk_with_period
Definition: parsenodes.h:2896
char * cooked_expr
Definition: parsenodes.h:2871
bool initially_valid
Definition: parsenodes.h:2867
bool skip_validation
Definition: parsenodes.h:2866
bool deferrable
Definition: parsenodes.h:2863
Node * raw_expr
Definition: parsenodes.h:2869
char * conname
Definition: parsenodes.h:2862
RangeVar * pktable
Definition: parsenodes.h:2892
char fk_del_action
Definition: parsenodes.h:2899
List * fk_attrs
Definition: parsenodes.h:2893
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:3149
List * transitionRels
Definition: parsenodes.h:3151
RangeVar * constrrel
Definition: parsenodes.h:3155
RangeVar * relation
Definition: parsenodes.h:3140
char * defname
Definition: parsenodes.h:843
bool missing_ok
Definition: parsenodes.h:3365
List * objects
Definition: parsenodes.h:3362
ObjectType removeType
Definition: parsenodes.h:3363
bool concurrent
Definition: parsenodes.h:3366
DropBehavior behavior
Definition: parsenodes.h:3364
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:3539
char * idxname
Definition: parsenodes.h:3513
char * idxcomment
Definition: parsenodes.h:3523
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:992
List * partlist
Definition: parsenodes.h:1000
RangeVar * name
Definition: parsenodes.h:989
List * collation
Definition: parsenodes.h:893
ParseLoc location
Definition: parsenodes.h:895
List * opclass
Definition: parsenodes.h:894
List * partParams
Definition: parsenodes.h:914
ParseLoc location
Definition: parsenodes.h:915
PartitionStrategy strategy
Definition: parsenodes.h:913
char * relname
Definition: primnodes.h:83
bool inh
Definition: primnodes.h:86
char 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:2114
RelFileNumber relNumber
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
SubTransactionId rd_firstRelfilelocatorSubid
Definition: rel.h:106
int rd_refcnt
Definition: rel.h:59
TriggerDesc * trigdesc
Definition: rel.h:117
bool rd_islocaltemp
Definition: rel.h:61
TupleDesc rd_att
Definition: rel.h:112
Form_pg_index rd_index
Definition: rel.h:192
bool rd_isnailed
Definition: rel.h:62
Oid rd_id
Definition: rel.h:113
SubTransactionId rd_newRelfilelocatorSubid
Definition: rel.h:104
SubTransactionId rd_createSubid
Definition: rel.h:103
RelFileLocator rd_locator
Definition: rel.h:57
Oid * rd_opfamily
Definition: rel.h:207
Oid * rd_indcollation
Definition: rel.h:217
Form_pg_class rd_rel
Definition: rel.h:111
Relation ri_RelationDesc
Definition: execnodes.h:480
PartitionBoundSpec * bound
Definition: parsenodes.h:977
TransactionId xmin
Definition: snapshot.h:153
TupleTableSlot * dstslot
Definition: tablecmds.c:22933
BulkInsertState bistate
Definition: tablecmds.c:22932
ExprState * partqualstate
Definition: tablecmds.c:22930
AlteredTableInfo * tab
Definition: tablecmds.c:22934
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:13026
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition: tablecmds.c:3841
void ResetRelRewrite(Oid myrelid)
Definition: tablecmds.c:4360
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
Definition: tablecmds.c:10674
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17526
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17893
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode)
Definition: tablecmds.c:15983
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5310
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:12791
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17289
ObjectAddress RenameRelation(RenameStmt *stmt)
Definition: tablecmds.c:4203
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6586
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition: tablecmds.c:9606
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:18384
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12226
static ColumnDef * MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3413
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:11066
#define ATT_TABLE
Definition: tablecmds.c:329
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
Definition: tablecmds.c:20140
static const char * storage_name(char c)
Definition: tablecmds.c:2459
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8830
void AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
Definition: tablecmds.c:19485
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition: tablecmds.c:7727
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8154
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
Definition: tablecmds.c:15656
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16881
static void MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
Definition: tablecmds.c:22568
static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
Definition: tablecmds.c:18190
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
Definition: tablecmds.c:12031
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17976
#define AT_NUM_PASSES
Definition: tablecmds.c:167
void PreCommit_on_commit_actions(void)
Definition: tablecmds.c:19346
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14105
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
Definition: tablecmds.c:11949
struct SplitPartitionContext SplitPartitionContext
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8516
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:22034
static int findAttrByName(const char *attributeName, const List *columns)
Definition: tablecmds.c:3607
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15384
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
Definition: tablecmds.c:14707
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9898
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12936
void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4531
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15435
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
Definition: tablecmds.c:13664
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition: tablecmds.c:7745
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7770
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13513
#define ATT_SEQUENCE
Definition: tablecmds.c:336
void RemoveRelations(DropStmt *drop)
Definition: tablecmds.c:1533
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
Definition: tablecmds.c:16643
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6919
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15297
static List * GetParentedForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21932
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19080
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9824
ObjectAddress renameatt(RenameStmt *stmt)
Definition: tablecmds.c:4006
void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:16100
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:19612
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4446
static void DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
Definition: tablecmds.c:1506
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4605
struct ForeignTruncateInfo ForeignTruncateInfo
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:20954
static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
Definition: tablecmds.c:19202
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:3986
TupleDesc BuildDescForRelation(const List *columns)
Definition: tablecmds.c:1375
static void attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
Definition: tablecmds.c:20280
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:18660
static void createTableConstraints(List **wqueue, AlteredTableInfo *tab, Relation parent_rel, Relation newRel)
Definition: tablecmds.c:22248
void AtEOXact_on_commit_actions(bool isCommit)
Definition: tablecmds.c:19453
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:13693
struct OnCommitItem OnCommitItem
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4413
static Relation createPartitionTable(List **wqueue, RangeVar *newPartName, Relation parent_rel, Oid ownerId)
Definition: tablecmds.c:22455
static void SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)
Definition: tablecmds.c:23004
static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12643
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13885
static void evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab, Relation newPartRel, TupleTableSlot *insertslot, ExprContext *econtext)
Definition: tablecmds.c:22150
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:17232
static void AttachPartitionForeignKey(List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11818
static List * MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
Definition: tablecmds.c:3164
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition: tablecmds.c:3792
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:21568
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:6889
static void truncate_check_activity(Relation rel)
Definition: tablecmds.c:2436
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:21808
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
Definition: tablecmds.c:12882
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:10095
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15914
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12160
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:10750
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15312
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:9079
static List * getAttributesList(Relation parent_rel)
Definition: tablecmds.c:22195
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:12440
static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13248
static void detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
Definition: tablecmds.c:22722
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:9312
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11475
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9158
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4872
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3690
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:18630
void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName)
Definition: tablecmds.c:6964
static void truncate_check_perms(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2418
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2367
#define ATT_INDEX
Definition: tablecmds.c:332
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:16406
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3562
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
Definition: tablecmds.c:15071
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:8784
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12369
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
Definition: tablecmds.c:21910
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8239
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:4044
static void DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
Definition: tablecmds.c:1458
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
Definition: tablecmds.c:15464
static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:22743
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5735
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19043
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9940
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11274
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17664
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9284
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3644
static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3243
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6767
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6620
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:16673
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8399
ObjectAddress AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
Definition: tablecmds.c:18972
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15328
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:1856
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)
Definition: tablecmds.c:18428
#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:7221
static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, PartitionStrategy strategy)
Definition: tablecmds.c:19811
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:20797
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:20615
static void StoreCatalogInheritance(Oid relationId, List *supers, bool child_is_partition)
Definition: tablecmds.c:3518
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:18689
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15943
struct AlteredTableInfo AlteredTableInfo
Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:4472
static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12840
struct NewConstraint NewConstraint
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8934
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6844
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:21127
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15870
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11245
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:10928
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition: tablecmds.c:7674
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:7245
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:14754
static void validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, bool hasperiod)
Definition: tablecmds.c:13722
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17469
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13145
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3747
void remove_on_commit_action(Oid relid)
Definition: tablecmds.c:19323
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16507
#define ATT_PARTITIONED_INDEX
Definition: tablecmds.c:335
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17400
static const struct dropmsgstrings dropmsgstringarray[]
Definition: tablecmds.c:256
static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
Definition: tablecmds.c:18847
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8630
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:13822
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
Definition: tablecmds.c:12722
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9527
static void DropClonedTriggersFromPartition(Oid partitionId)
Definition: tablecmds.c:21495
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:20203
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:18242
static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:1697
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4560
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12099
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7171
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17851
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21985
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9732
#define ATT_VIEW
Definition: tablecmds.c:330
static SplitPartitionContext * createSplitPartitionContext(Relation partRel)
Definition: tablecmds.c:22943
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19157
#define ATT_PARTITIONED_TABLE
Definition: tablecmds.c:337
static void deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, int ti_options)
Definition: tablecmds.c:22966
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5384
static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12586
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:18516
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:14020
static void ATPrepAddInherit(Relation child_rel)
Definition: tablecmds.c:17267
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:21461
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9221
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20310
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:7122
Oid AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
Definition: tablecmds.c:17013
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:13410
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8268
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:16553
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14040
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9648
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:18770
static PartitionSpec * transformPartitionSpec(Relation rel, PartitionSpec *partspec)
Definition: tablecmds.c:19753
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4907
static void buildExpressionExecutionStates(AlteredTableInfo *tab, Relation newPartRel, EState *estate)
Definition: tablecmds.c:22112
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:17175
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:7941
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4267
static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
Definition: tablecmds.c:7868
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:11722
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16974
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:14401
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
Definition: tablecmds.c:13355
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:16341
#define ATT_COMPOSITE_TYPE
Definition: tablecmds.c:333
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:21622
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:20085
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19520
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:23197
static List * on_commits
Definition: tablecmds.c:132
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:17497
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9711
ObjectAddress RenameConstraint(RenameStmt *stmt)
Definition: tablecmds.c:4153
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5862
void register_on_commit_action(Oid relid, OnCommitAction action)
Definition: tablecmds.c:19287
static void RangeVarCallbackForTruncate(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19556
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:16475
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:22072
void RangeVarCallbackOwnsRelation(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19580
#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:21784
void ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
Definition: tablecmds.c:1980
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
Definition: tablecmds.c:6150
static List * MergeAttributes(List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
Definition: tablecmds.c:2544
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:17250
struct NewColumnValue NewColumnValue
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
Definition: tablecmds.c:16519
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:8117
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:1958
#define MAX_STATISTICS_TARGET
Definition: vacuum.h:329
String * makeString(char *str)
Definition: value.c:63
#define intVal(v)
Definition: value.h:79
#define strVal(v)
Definition: value.h:82
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:296
static char * VARDATA_ANY(const void *PTR)
Definition: varatt.h:486
const char * name
SubTransactionId GetCurrentSubTransactionId(void)
Definition: xact.c:792
void CommandCounterIncrement(void)
Definition: xact.c:1101
void StartTransactionCommand(void)
Definition: xact.c:3077
void CommitTransactionCommand(void)
Definition: xact.c:3175
int MyXactFlags
Definition: xact.c:137
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:830
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:103
#define XLogLogicalInfoActive()
Definition: xlog.h:126
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:154
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:478
void XLogRegisterData(const void *data, uint32 len)
Definition: xloginsert.c:368
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:460
void XLogBeginInsert(void)
Definition: xloginsert.c:152