Line data Source code
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"
27 : #include "access/toast_compression.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"
35 : #include "catalog/objectaccess.h"
36 : #include "catalog/partition.h"
37 : #include "catalog/pg_am.h"
38 : #include "catalog/pg_attrdef.h"
39 : #include "catalog/pg_collation.h"
40 : #include "catalog/pg_constraint.h"
41 : #include "catalog/pg_depend.h"
42 : #include "catalog/pg_foreign_table.h"
43 : #include "catalog/pg_inherits.h"
44 : #include "catalog/pg_largeobject.h"
45 : #include "catalog/pg_namespace.h"
46 : #include "catalog/pg_opclass.h"
47 : #include "catalog/pg_policy.h"
48 : #include "catalog/pg_proc.h"
49 : #include "catalog/pg_publication_rel.h"
50 : #include "catalog/pg_rewrite.h"
51 : #include "catalog/pg_statistic_ext.h"
52 : #include "catalog/pg_tablespace.h"
53 : #include "catalog/pg_trigger.h"
54 : #include "catalog/pg_type.h"
55 : #include "catalog/storage.h"
56 : #include "catalog/storage_xlog.h"
57 : #include "catalog/toasting.h"
58 : #include "commands/cluster.h"
59 : #include "commands/comment.h"
60 : #include "commands/defrem.h"
61 : #include "commands/event_trigger.h"
62 : #include "commands/sequence.h"
63 : #include "commands/tablecmds.h"
64 : #include "commands/tablespace.h"
65 : #include "commands/trigger.h"
66 : #include "commands/typecmds.h"
67 : #include "commands/user.h"
68 : #include "commands/vacuum.h"
69 : #include "common/int.h"
70 : #include "executor/executor.h"
71 : #include "foreign/fdwapi.h"
72 : #include "foreign/foreign.h"
73 : #include "miscadmin.h"
74 : #include "nodes/makefuncs.h"
75 : #include "nodes/nodeFuncs.h"
76 : #include "nodes/parsenodes.h"
77 : #include "optimizer/optimizer.h"
78 : #include "parser/parse_coerce.h"
79 : #include "parser/parse_collate.h"
80 : #include "parser/parse_expr.h"
81 : #include "parser/parse_relation.h"
82 : #include "parser/parse_type.h"
83 : #include "parser/parse_utilcmd.h"
84 : #include "parser/parser.h"
85 : #include "partitioning/partbounds.h"
86 : #include "partitioning/partdesc.h"
87 : #include "pgstat.h"
88 : #include "rewrite/rewriteDefine.h"
89 : #include "rewrite/rewriteHandler.h"
90 : #include "rewrite/rewriteManip.h"
91 : #include "storage/bufmgr.h"
92 : #include "storage/lmgr.h"
93 : #include "storage/lock.h"
94 : #include "storage/predicate.h"
95 : #include "storage/smgr.h"
96 : #include "tcop/utility.h"
97 : #include "utils/acl.h"
98 : #include "utils/builtins.h"
99 : #include "utils/fmgroids.h"
100 : #include "utils/inval.h"
101 : #include "utils/lsyscache.h"
102 : #include "utils/memutils.h"
103 : #include "utils/partcache.h"
104 : #include "utils/relcache.h"
105 : #include "utils/ruleutils.h"
106 : #include "utils/snapmgr.h"
107 : #include "utils/syscache.h"
108 : #include "utils/timestamp.h"
109 : #include "utils/typcache.h"
110 : #include "utils/usercontext.h"
111 :
112 : /*
113 : * ON COMMIT action list
114 : */
115 : typedef struct OnCommitItem
116 : {
117 : Oid relid; /* relid of relation */
118 : OnCommitAction oncommit; /* what to do at end of xact */
119 :
120 : /*
121 : * If this entry was created during the current transaction,
122 : * creating_subid is the ID of the creating subxact; if created in a prior
123 : * transaction, creating_subid is zero. If deleted during the current
124 : * transaction, deleting_subid is the ID of the deleting subxact; if no
125 : * deletion request is pending, deleting_subid is zero.
126 : */
127 : SubTransactionId creating_subid;
128 : SubTransactionId deleting_subid;
129 : } OnCommitItem;
130 :
131 : static List *on_commits = NIL;
132 :
133 :
134 : /*
135 : * State information for ALTER TABLE
136 : *
137 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
138 : * structs, one for each table modified by the operation (the named table
139 : * plus any child tables that are affected). We save lists of subcommands
140 : * to apply to this table (possibly modified by parse transformation steps);
141 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
142 : * necessary information is stored in the constraints and newvals lists.
143 : *
144 : * Phase 2 is divided into multiple passes; subcommands are executed in
145 : * a pass determined by subcommand type.
146 : */
147 :
148 : typedef enum AlterTablePass
149 : {
150 : AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
151 : AT_PASS_DROP, /* DROP (all flavors) */
152 : AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
153 : AT_PASS_ADD_COL, /* ADD COLUMN */
154 : AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
155 : AT_PASS_OLD_INDEX, /* re-add existing indexes */
156 : AT_PASS_OLD_CONSTR, /* re-add existing constraints */
157 : /* We could support a RENAME COLUMN pass here, but not currently used */
158 : AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
159 : AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
160 : AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
161 : AT_PASS_ADD_INDEX, /* ADD indexes */
162 : AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
163 : AT_PASS_MISC, /* other stuff */
164 : } AlterTablePass;
165 :
166 : #define AT_NUM_PASSES (AT_PASS_MISC + 1)
167 :
168 : typedef struct AlteredTableInfo
169 : {
170 : /* Information saved before any work commences: */
171 : Oid relid; /* Relation to work on */
172 : char relkind; /* Its relkind */
173 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
174 :
175 : /*
176 : * Transiently set during Phase 2, normally set to NULL.
177 : *
178 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
179 : * returns control. This can be exploited by ATExecCmd subroutines to
180 : * close/reopen across transaction boundaries.
181 : */
182 : Relation rel;
183 :
184 : /* Information saved by Phase 1 for Phase 2: */
185 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
186 : /* Information saved by Phases 1/2 for Phase 3: */
187 : List *constraints; /* List of NewConstraint */
188 : List *newvals; /* List of NewColumnValue */
189 : List *afterStmts; /* List of utility command parsetrees */
190 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
191 : int rewrite; /* Reason for forced rewrite, if any */
192 : bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
193 : Oid newAccessMethod; /* new access method; 0 means no change,
194 : * if above is true */
195 : Oid newTableSpace; /* new tablespace; 0 means no change */
196 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
197 : char newrelpersistence; /* if above is true */
198 : Expr *partition_constraint; /* for attach partition validation */
199 : /* true, if validating default due to some other attach/detach */
200 : bool validate_default;
201 : /* Objects to rebuild after completing ALTER TYPE operations */
202 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
203 : List *changedConstraintDefs; /* string definitions of same */
204 : List *changedIndexOids; /* OIDs of indexes to rebuild */
205 : List *changedIndexDefs; /* string definitions of same */
206 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
207 : char *clusterOnIndex; /* index to use for CLUSTER */
208 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
209 : List *changedStatisticsDefs; /* string definitions of same */
210 : } AlteredTableInfo;
211 :
212 : /* Struct describing one new constraint to check in Phase 3 scan */
213 : /* Note: new not-null constraints are handled elsewhere */
214 : typedef struct NewConstraint
215 : {
216 : char *name; /* Constraint name, or NULL if none */
217 : ConstrType contype; /* CHECK or FOREIGN */
218 : Oid refrelid; /* PK rel, if FOREIGN */
219 : Oid refindid; /* OID of PK's index, if FOREIGN */
220 : bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
221 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
222 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
223 : ExprState *qualstate; /* Execution state for CHECK expr */
224 : } NewConstraint;
225 :
226 : /*
227 : * Struct describing one new column value that needs to be computed during
228 : * Phase 3 copy (this could be either a new column with a non-null default, or
229 : * a column that we're changing the type of). Columns without such an entry
230 : * are just copied from the old table during ATRewriteTable. Note that the
231 : * expr is an expression over *old* table values, except when is_generated
232 : * is true; then it is an expression over columns of the *new* tuple.
233 : */
234 : typedef struct NewColumnValue
235 : {
236 : AttrNumber attnum; /* which column */
237 : Expr *expr; /* expression to compute */
238 : ExprState *exprstate; /* execution state */
239 : bool is_generated; /* is it a GENERATED expression? */
240 : } NewColumnValue;
241 :
242 : /*
243 : * Error-reporting support for RemoveRelations
244 : */
245 : struct dropmsgstrings
246 : {
247 : char kind;
248 : int nonexistent_code;
249 : const char *nonexistent_msg;
250 : const char *skipping_msg;
251 : const char *nota_msg;
252 : const char *drophint_msg;
253 : };
254 :
255 : static const struct dropmsgstrings dropmsgstringarray[] = {
256 : {RELKIND_RELATION,
257 : ERRCODE_UNDEFINED_TABLE,
258 : gettext_noop("table \"%s\" does not exist"),
259 : gettext_noop("table \"%s\" does not exist, skipping"),
260 : gettext_noop("\"%s\" is not a table"),
261 : gettext_noop("Use DROP TABLE to remove a table.")},
262 : {RELKIND_SEQUENCE,
263 : ERRCODE_UNDEFINED_TABLE,
264 : gettext_noop("sequence \"%s\" does not exist"),
265 : gettext_noop("sequence \"%s\" does not exist, skipping"),
266 : gettext_noop("\"%s\" is not a sequence"),
267 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
268 : {RELKIND_VIEW,
269 : ERRCODE_UNDEFINED_TABLE,
270 : gettext_noop("view \"%s\" does not exist"),
271 : gettext_noop("view \"%s\" does not exist, skipping"),
272 : gettext_noop("\"%s\" is not a view"),
273 : gettext_noop("Use DROP VIEW to remove a view.")},
274 : {RELKIND_MATVIEW,
275 : ERRCODE_UNDEFINED_TABLE,
276 : gettext_noop("materialized view \"%s\" does not exist"),
277 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
278 : gettext_noop("\"%s\" is not a materialized view"),
279 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
280 : {RELKIND_INDEX,
281 : ERRCODE_UNDEFINED_OBJECT,
282 : gettext_noop("index \"%s\" does not exist"),
283 : gettext_noop("index \"%s\" does not exist, skipping"),
284 : gettext_noop("\"%s\" is not an index"),
285 : gettext_noop("Use DROP INDEX to remove an index.")},
286 : {RELKIND_COMPOSITE_TYPE,
287 : ERRCODE_UNDEFINED_OBJECT,
288 : gettext_noop("type \"%s\" does not exist"),
289 : gettext_noop("type \"%s\" does not exist, skipping"),
290 : gettext_noop("\"%s\" is not a type"),
291 : gettext_noop("Use DROP TYPE to remove a type.")},
292 : {RELKIND_FOREIGN_TABLE,
293 : ERRCODE_UNDEFINED_OBJECT,
294 : gettext_noop("foreign table \"%s\" does not exist"),
295 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
296 : gettext_noop("\"%s\" is not a foreign table"),
297 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
298 : {RELKIND_PARTITIONED_TABLE,
299 : ERRCODE_UNDEFINED_TABLE,
300 : gettext_noop("table \"%s\" does not exist"),
301 : gettext_noop("table \"%s\" does not exist, skipping"),
302 : gettext_noop("\"%s\" is not a table"),
303 : gettext_noop("Use DROP TABLE to remove a table.")},
304 : {RELKIND_PARTITIONED_INDEX,
305 : ERRCODE_UNDEFINED_OBJECT,
306 : gettext_noop("index \"%s\" does not exist"),
307 : gettext_noop("index \"%s\" does not exist, skipping"),
308 : gettext_noop("\"%s\" is not an index"),
309 : gettext_noop("Use DROP INDEX to remove an index.")},
310 : {'\0', 0, NULL, NULL, NULL, NULL}
311 : };
312 :
313 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
314 : struct DropRelationCallbackState
315 : {
316 : /* These fields are set by RemoveRelations: */
317 : char expected_relkind;
318 : LOCKMODE heap_lockmode;
319 : /* These fields are state to track which subsidiary locks are held: */
320 : Oid heapOid;
321 : Oid partParentOid;
322 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
323 : char actual_relkind;
324 : char actual_relpersistence;
325 : };
326 :
327 : /* Alter table target-type flags for ATSimplePermissions */
328 : #define ATT_TABLE 0x0001
329 : #define ATT_VIEW 0x0002
330 : #define ATT_MATVIEW 0x0004
331 : #define ATT_INDEX 0x0008
332 : #define ATT_COMPOSITE_TYPE 0x0010
333 : #define ATT_FOREIGN_TABLE 0x0020
334 : #define ATT_PARTITIONED_INDEX 0x0040
335 : #define ATT_SEQUENCE 0x0080
336 : #define ATT_PARTITIONED_TABLE 0x0100
337 :
338 : /*
339 : * ForeignTruncateInfo
340 : *
341 : * Information related to truncation of foreign tables. This is used for
342 : * the elements in a hash table. It uses the server OID as lookup key,
343 : * and includes a per-server list of all foreign tables involved in the
344 : * truncation.
345 : */
346 : typedef struct ForeignTruncateInfo
347 : {
348 : Oid serverid;
349 : List *rels;
350 : } ForeignTruncateInfo;
351 :
352 : /* Partial or complete FK creation in addFkConstraint() */
353 : typedef enum addFkConstraintSides
354 : {
355 : addFkReferencedSide,
356 : addFkReferencingSide,
357 : addFkBothSides,
358 : } addFkConstraintSides;
359 :
360 : /*
361 : * Partition tables are expected to be dropped when the parent partitioned
362 : * table gets dropped. Hence for partitioning we use AUTO dependency.
363 : * Otherwise, for regular inheritance use NORMAL dependency.
364 : */
365 : #define child_dependency_type(child_is_partition) \
366 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
367 :
368 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
369 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
370 : static void truncate_check_activity(Relation rel);
371 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
372 : Oid relId, Oid oldRelId, void *arg);
373 : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
374 : bool is_partition, List **supconstr,
375 : List **supnotnulls);
376 : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
377 : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
378 : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
379 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
380 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
381 : static void StoreCatalogInheritance(Oid relationId, List *supers,
382 : bool child_is_partition);
383 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
384 : int32 seqNumber, Relation inhRelation,
385 : bool child_is_partition);
386 : static int findAttrByName(const char *attributeName, const List *columns);
387 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
388 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
389 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
390 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
391 : LOCKMODE lockmode);
392 : static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
393 : ATAlterConstraint *cmdcon,
394 : bool recurse, LOCKMODE lockmode);
395 : static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
396 : Relation tgrel, Relation rel, HeapTuple contuple,
397 : bool recurse, LOCKMODE lockmode);
398 : static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
399 : Relation conrel, Relation tgrel,
400 : Oid fkrelid, Oid pkrelid,
401 : HeapTuple contuple, LOCKMODE lockmode,
402 : Oid ReferencedParentDelTrigger,
403 : Oid ReferencedParentUpdTrigger,
404 : Oid ReferencingParentInsTrigger,
405 : Oid ReferencingParentUpdTrigger);
406 : static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
407 : Relation conrel, Relation tgrel, Relation rel,
408 : HeapTuple contuple, bool recurse,
409 : List **otherrelids, LOCKMODE lockmode);
410 : static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
411 : Relation conrel, Relation rel,
412 : HeapTuple contuple, LOCKMODE lockmode);
413 : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
414 : bool deferrable, bool initdeferred,
415 : List **otherrelids);
416 : static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
417 : Relation conrel, Relation tgrel,
418 : Oid fkrelid, Oid pkrelid,
419 : HeapTuple contuple, LOCKMODE lockmode,
420 : Oid ReferencedParentDelTrigger,
421 : Oid ReferencedParentUpdTrigger,
422 : Oid ReferencingParentInsTrigger,
423 : Oid ReferencingParentUpdTrigger);
424 : static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
425 : Relation conrel, Relation tgrel, Relation rel,
426 : HeapTuple contuple, bool recurse,
427 : List **otherrelids, LOCKMODE lockmode);
428 : static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
429 : HeapTuple contuple);
430 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
431 : Relation rel, char *constrName,
432 : bool recurse, bool recursing, LOCKMODE lockmode);
433 : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
434 : HeapTuple contuple, LOCKMODE lockmode);
435 : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
436 : char *constrName, HeapTuple contuple,
437 : bool recurse, bool recursing, LOCKMODE lockmode);
438 : static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
439 : HeapTuple contuple, bool recurse, bool recursing,
440 : LOCKMODE lockmode);
441 : static int transformColumnNameList(Oid relId, List *colList,
442 : int16 *attnums, Oid *atttypids, Oid *attcollids);
443 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
444 : List **attnamelist,
445 : int16 *attnums, Oid *atttypids, Oid *attcollids,
446 : Oid *opclasses, bool *pk_has_without_overlaps);
447 : static Oid transformFkeyCheckAttrs(Relation pkrel,
448 : int numattrs, int16 *attnums,
449 : bool with_period, Oid *opclasses,
450 : bool *pk_has_without_overlaps);
451 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
452 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
453 : Oid *funcid);
454 : static void validateForeignKeyConstraint(char *conname,
455 : Relation rel, Relation pkrel,
456 : Oid pkindOid, Oid constraintOid, bool hasperiod);
457 : static void CheckAlterTableIsSafe(Relation rel);
458 : static void ATController(AlterTableStmt *parsetree,
459 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
460 : AlterTableUtilityContext *context);
461 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
462 : bool recurse, bool recursing, LOCKMODE lockmode,
463 : AlterTableUtilityContext *context);
464 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
465 : AlterTableUtilityContext *context);
466 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
467 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
468 : AlterTableUtilityContext *context);
469 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
470 : Relation rel, AlterTableCmd *cmd,
471 : bool recurse, LOCKMODE lockmode,
472 : AlterTablePass cur_pass,
473 : AlterTableUtilityContext *context);
474 : static void ATRewriteTables(AlterTableStmt *parsetree,
475 : List **wqueue, LOCKMODE lockmode,
476 : AlterTableUtilityContext *context);
477 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
478 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
479 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
480 : static void ATSimpleRecursion(List **wqueue, Relation rel,
481 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
482 : AlterTableUtilityContext *context);
483 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
484 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
485 : LOCKMODE lockmode,
486 : AlterTableUtilityContext *context);
487 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
488 : DropBehavior behavior);
489 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
490 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
491 : AlterTableUtilityContext *context);
492 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
493 : Relation rel, AlterTableCmd **cmd,
494 : bool recurse, bool recursing,
495 : LOCKMODE lockmode, AlterTablePass cur_pass,
496 : AlterTableUtilityContext *context);
497 : static bool check_for_column_name_collision(Relation rel, const char *colname,
498 : bool if_not_exists);
499 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
500 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
501 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
502 : LOCKMODE lockmode);
503 : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
504 : bool is_valid, bool queue_validation);
505 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
506 : char *conName, char *colName,
507 : bool recurse, bool recursing,
508 : LOCKMODE lockmode);
509 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
510 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
511 : List *testConstraint, List *provenConstraint);
512 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
513 : Node *newDefault, LOCKMODE lockmode);
514 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
515 : Node *newDefault);
516 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
517 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
518 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
519 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
520 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
521 : bool recurse, bool recursing);
522 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
523 : Node *newExpr, LOCKMODE lockmode);
524 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
525 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
526 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
527 : Node *newValue, LOCKMODE lockmode);
528 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
529 : Node *options, bool isReset, LOCKMODE lockmode);
530 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
531 : Node *newValue, LOCKMODE lockmode);
532 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
533 : AlterTableCmd *cmd, LOCKMODE lockmode,
534 : AlterTableUtilityContext *context);
535 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
536 : DropBehavior behavior,
537 : bool recurse, bool recursing,
538 : bool missing_ok, LOCKMODE lockmode,
539 : ObjectAddresses *addrs);
540 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
541 : bool recurse, LOCKMODE lockmode,
542 : AlterTableUtilityContext *context);
543 : static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
544 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
545 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
546 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
547 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
548 : static ObjectAddress ATExecAddConstraint(List **wqueue,
549 : AlteredTableInfo *tab, Relation rel,
550 : Constraint *newConstraint, bool recurse, bool is_readd,
551 : LOCKMODE lockmode);
552 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
553 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
554 : IndexStmt *stmt, LOCKMODE lockmode);
555 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
556 : AlteredTableInfo *tab, Relation rel,
557 : Constraint *constr,
558 : bool recurse, bool recursing, bool is_readd,
559 : LOCKMODE lockmode);
560 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
561 : Relation rel, Constraint *fkconstraint,
562 : bool recurse, bool recursing,
563 : LOCKMODE lockmode);
564 : static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
565 : int numfksetcols, int16 *fksetcolsattnums,
566 : List *fksetcols);
567 : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
568 : char *constraintname,
569 : Constraint *fkconstraint, Relation rel,
570 : Relation pkrel, Oid indexOid,
571 : Oid parentConstr,
572 : int numfks, int16 *pkattnum, int16 *fkattnum,
573 : Oid *pfeqoperators, Oid *ppeqoperators,
574 : Oid *ffeqoperators, int numfkdelsetcols,
575 : int16 *fkdelsetcols, bool is_internal,
576 : bool with_period);
577 : static void addFkRecurseReferenced(Constraint *fkconstraint,
578 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
579 : int numfks, int16 *pkattnum, int16 *fkattnum,
580 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
581 : int numfkdelsetcols, int16 *fkdelsetcols,
582 : bool old_check_ok,
583 : Oid parentDelTrigger, Oid parentUpdTrigger,
584 : bool with_period);
585 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
586 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
587 : int numfks, int16 *pkattnum, int16 *fkattnum,
588 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
589 : int numfkdelsetcols, int16 *fkdelsetcols,
590 : bool old_check_ok, LOCKMODE lockmode,
591 : Oid parentInsTrigger, Oid parentUpdTrigger,
592 : bool with_period);
593 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
594 : Relation partitionRel);
595 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
596 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
597 : Relation partRel);
598 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
599 : Constraint *fkconstraint, Oid constraintOid,
600 : Oid indexOid,
601 : Oid parentInsTrigger, Oid parentUpdTrigger,
602 : Oid *insertTrigOid, Oid *updateTrigOid);
603 : static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
604 : Constraint *fkconstraint, Oid constraintOid,
605 : Oid indexOid,
606 : Oid parentDelTrigger, Oid parentUpdTrigger,
607 : Oid *deleteTrigOid, Oid *updateTrigOid);
608 : static bool tryAttachPartitionForeignKey(List **wqueue,
609 : ForeignKeyCacheInfo *fk,
610 : Relation partition,
611 : Oid parentConstrOid, int numfks,
612 : AttrNumber *mapped_conkey, AttrNumber *confkey,
613 : Oid *conpfeqop,
614 : Oid parentInsTrigger,
615 : Oid parentUpdTrigger,
616 : Relation trigrel);
617 : static void AttachPartitionForeignKey(List **wqueue, Relation partition,
618 : Oid partConstrOid, Oid parentConstrOid,
619 : Oid parentInsTrigger, Oid parentUpdTrigger,
620 : Relation trigrel);
621 : static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
622 : Oid conoid, Oid conrelid);
623 : static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
624 : Oid confrelid, Oid conrelid);
625 : static void GetForeignKeyActionTriggers(Relation trigrel,
626 : Oid conoid, Oid confrelid, Oid conrelid,
627 : Oid *deleteTriggerOid,
628 : Oid *updateTriggerOid);
629 : static void GetForeignKeyCheckTriggers(Relation trigrel,
630 : Oid conoid, Oid confrelid, Oid conrelid,
631 : Oid *insertTriggerOid,
632 : Oid *updateTriggerOid);
633 : static void ATExecDropConstraint(Relation rel, const char *constrName,
634 : DropBehavior behavior, bool recurse,
635 : bool missing_ok, LOCKMODE lockmode);
636 : static ObjectAddress dropconstraint_internal(Relation rel,
637 : HeapTuple constraintTup, DropBehavior behavior,
638 : bool recurse, bool recursing,
639 : bool missing_ok, LOCKMODE lockmode);
640 : static void ATPrepAlterColumnType(List **wqueue,
641 : AlteredTableInfo *tab, Relation rel,
642 : bool recurse, bool recursing,
643 : AlterTableCmd *cmd, LOCKMODE lockmode,
644 : AlterTableUtilityContext *context);
645 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
646 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
647 : AlterTableCmd *cmd, LOCKMODE lockmode);
648 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
649 : Relation rel, AttrNumber attnum, const char *colName);
650 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
651 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
652 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
653 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
654 : LOCKMODE lockmode);
655 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
656 : char *cmd, List **wqueue, LOCKMODE lockmode,
657 : bool rewrite);
658 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
659 : Oid objid, Relation rel, List *domname,
660 : const char *conname);
661 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
662 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
663 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
664 : List *options, LOCKMODE lockmode);
665 : static void change_owner_fix_column_acls(Oid relationOid,
666 : Oid oldOwnerId, Oid newOwnerId);
667 : static void change_owner_recurse_to_sequences(Oid relationOid,
668 : Oid newOwnerId, LOCKMODE lockmode);
669 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
670 : LOCKMODE lockmode);
671 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
672 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
673 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
674 : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
675 : bool toLogged);
676 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
677 : const char *tablespacename, LOCKMODE lockmode);
678 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
679 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
680 : static void ATExecSetRelOptions(Relation rel, List *defList,
681 : AlterTableType operation,
682 : LOCKMODE lockmode);
683 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
684 : char fires_when, bool skip_system, bool recurse,
685 : LOCKMODE lockmode);
686 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
687 : char fires_when, LOCKMODE lockmode);
688 : static void ATPrepAddInherit(Relation child_rel);
689 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
690 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
691 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
692 : DependencyType deptype);
693 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
694 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
695 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
696 : static void ATExecGenericOptions(Relation rel, List *options);
697 : static void ATExecSetRowSecurity(Relation rel, bool rls);
698 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
699 : static ObjectAddress ATExecSetCompression(Relation rel,
700 : const char *column, Node *newValue, LOCKMODE lockmode);
701 :
702 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
703 : static const char *storage_name(char c);
704 :
705 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
706 : Oid oldRelOid, void *arg);
707 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
708 : Oid oldrelid, void *arg);
709 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
710 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
711 : List **partexprs, Oid *partopclass, Oid *partcollation,
712 : PartitionStrategy strategy);
713 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
714 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
715 : bool expect_detached);
716 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
717 : PartitionCmd *cmd,
718 : AlterTableUtilityContext *context);
719 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
720 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
721 : List *partConstraint,
722 : bool validate_default);
723 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
724 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
725 : static void DropClonedTriggersFromPartition(Oid partitionId);
726 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
727 : Relation rel, RangeVar *name,
728 : bool concurrent);
729 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
730 : bool concurrent, Oid defaultPartOid);
731 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
732 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
733 : RangeVar *name);
734 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
735 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
736 : Relation partitionTbl);
737 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
738 : static List *GetParentedForeignKeyRefs(Relation partition);
739 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
740 : static char GetAttributeCompression(Oid atttypid, const char *compression);
741 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
742 :
743 :
744 : /* ----------------------------------------------------------------
745 : * DefineRelation
746 : * Creates a new relation.
747 : *
748 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
749 : * The other arguments are used to extend the behavior for other cases:
750 : * relkind: relkind to assign to the new relation
751 : * ownerId: if not InvalidOid, use this as the new relation's owner.
752 : * typaddress: if not null, it's set to the pg_type entry's address.
753 : * queryString: for error reporting
754 : *
755 : * Note that permissions checks are done against current user regardless of
756 : * ownerId. A nonzero ownerId is used when someone is creating a relation
757 : * "on behalf of" someone else, so we still want to see that the current user
758 : * has permissions to do it.
759 : *
760 : * If successful, returns the address of the new relation.
761 : * ----------------------------------------------------------------
762 : */
763 : ObjectAddress
764 63246 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
765 : ObjectAddress *typaddress, const char *queryString)
766 : {
767 : char relname[NAMEDATALEN];
768 : Oid namespaceId;
769 : Oid relationId;
770 : Oid tablespaceId;
771 : Relation rel;
772 : TupleDesc descriptor;
773 : List *inheritOids;
774 : List *old_constraints;
775 : List *old_notnulls;
776 : List *rawDefaults;
777 : List *cookedDefaults;
778 : List *nncols;
779 : Datum reloptions;
780 : ListCell *listptr;
781 : AttrNumber attnum;
782 : bool partitioned;
783 63246 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
784 : Oid ofTypeId;
785 : ObjectAddress address;
786 : LOCKMODE parentLockmode;
787 63246 : Oid accessMethodId = InvalidOid;
788 :
789 : /*
790 : * Truncate relname to appropriate length (probably a waste of time, as
791 : * parser should have done this already).
792 : */
793 63246 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
794 :
795 : /*
796 : * Check consistency of arguments
797 : */
798 63246 : if (stmt->oncommit != ONCOMMIT_NOOP
799 188 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
800 12 : ereport(ERROR,
801 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
802 : errmsg("ON COMMIT can only be used on temporary tables")));
803 :
804 63234 : if (stmt->partspec != NULL)
805 : {
806 5120 : if (relkind != RELKIND_RELATION)
807 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
808 :
809 5120 : relkind = RELKIND_PARTITIONED_TABLE;
810 5120 : partitioned = true;
811 : }
812 : else
813 58114 : partitioned = false;
814 :
815 63234 : if (relkind == RELKIND_PARTITIONED_TABLE &&
816 5120 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
817 6 : ereport(ERROR,
818 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
819 : errmsg("partitioned tables cannot be unlogged")));
820 :
821 : /*
822 : * Look up the namespace in which we are supposed to create the relation,
823 : * check we have permission to create there, lock it against concurrent
824 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
825 : * namespace is selected.
826 : */
827 : namespaceId =
828 63228 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
829 :
830 : /*
831 : * Security check: disallow creating temp tables from security-restricted
832 : * code. This is needed because calling code might not expect untrusted
833 : * tables to appear in pg_temp at the front of its search path.
834 : */
835 63228 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
836 3126 : && InSecurityRestrictedOperation())
837 0 : ereport(ERROR,
838 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
839 : errmsg("cannot create temporary table within security-restricted operation")));
840 :
841 : /*
842 : * Determine the lockmode to use when scanning parents. A self-exclusive
843 : * lock is needed here.
844 : *
845 : * For regular inheritance, if two backends attempt to add children to the
846 : * same parent simultaneously, and that parent has no pre-existing
847 : * children, then both will attempt to update the parent's relhassubclass
848 : * field, leading to a "tuple concurrently updated" error. Also, this
849 : * interlocks against a concurrent ANALYZE on the parent table, which
850 : * might otherwise be attempting to clear the parent's relhassubclass
851 : * field, if its previous children were recently dropped.
852 : *
853 : * If the child table is a partition, then we instead grab an exclusive
854 : * lock on the parent because its partition descriptor will be changed by
855 : * addition of the new partition.
856 : */
857 63228 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
858 : ShareUpdateExclusiveLock);
859 :
860 : /* Determine the list of OIDs of the parents. */
861 63228 : inheritOids = NIL;
862 73706 : foreach(listptr, stmt->inhRelations)
863 : {
864 10478 : RangeVar *rv = (RangeVar *) lfirst(listptr);
865 : Oid parentOid;
866 :
867 10478 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
868 :
869 : /*
870 : * Reject duplications in the list of parents.
871 : */
872 10478 : if (list_member_oid(inheritOids, parentOid))
873 0 : ereport(ERROR,
874 : (errcode(ERRCODE_DUPLICATE_TABLE),
875 : errmsg("relation \"%s\" would be inherited from more than once",
876 : get_rel_name(parentOid))));
877 :
878 10478 : inheritOids = lappend_oid(inheritOids, parentOid);
879 : }
880 :
881 : /*
882 : * Select tablespace to use: an explicitly indicated one, or (in the case
883 : * of a partitioned table) the parent's, if it has one.
884 : */
885 63228 : if (stmt->tablespacename)
886 : {
887 118 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
888 :
889 112 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
890 6 : ereport(ERROR,
891 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
892 : errmsg("cannot specify default tablespace for partitioned relations")));
893 : }
894 63110 : else if (stmt->partbound)
895 : {
896 : Assert(list_length(inheritOids) == 1);
897 7922 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
898 : }
899 : else
900 55188 : tablespaceId = InvalidOid;
901 :
902 : /* still nothing? use the default */
903 63216 : if (!OidIsValid(tablespaceId))
904 63088 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
905 : partitioned);
906 :
907 : /* Check permissions except when using database's default */
908 63210 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
909 : {
910 : AclResult aclresult;
911 :
912 146 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
913 : ACL_CREATE);
914 146 : if (aclresult != ACLCHECK_OK)
915 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
916 6 : get_tablespace_name(tablespaceId));
917 : }
918 :
919 : /* In all cases disallow placing user relations in pg_global */
920 63204 : if (tablespaceId == GLOBALTABLESPACE_OID)
921 18 : ereport(ERROR,
922 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
923 : errmsg("only shared relations can be placed in pg_global tablespace")));
924 :
925 : /* Identify user ID that will own the table */
926 63186 : if (!OidIsValid(ownerId))
927 62902 : ownerId = GetUserId();
928 :
929 : /*
930 : * Parse and validate reloptions, if any.
931 : */
932 63186 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
933 : true, false);
934 :
935 63168 : switch (relkind)
936 : {
937 16366 : case RELKIND_VIEW:
938 16366 : (void) view_reloptions(reloptions, true);
939 16348 : break;
940 5096 : case RELKIND_PARTITIONED_TABLE:
941 5096 : (void) partitioned_table_reloptions(reloptions, true);
942 5090 : break;
943 41706 : default:
944 41706 : (void) heap_reloptions(relkind, reloptions, true);
945 : }
946 :
947 63048 : if (stmt->ofTypename)
948 : {
949 : AclResult aclresult;
950 :
951 98 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
952 :
953 98 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
954 98 : if (aclresult != ACLCHECK_OK)
955 6 : aclcheck_error_type(aclresult, ofTypeId);
956 : }
957 : else
958 62950 : ofTypeId = InvalidOid;
959 :
960 : /*
961 : * Look up inheritance ancestors and generate relation schema, including
962 : * inherited attributes. (Note that stmt->tableElts is destructively
963 : * modified by MergeAttributes.)
964 : */
965 62802 : stmt->tableElts =
966 63042 : MergeAttributes(stmt->tableElts, inheritOids,
967 63042 : stmt->relation->relpersistence,
968 63042 : stmt->partbound != NULL,
969 : &old_constraints, &old_notnulls);
970 :
971 : /*
972 : * Create a tuple descriptor from the relation schema. Note that this
973 : * deals with column names, types, and in-descriptor NOT NULL flags, but
974 : * not default values, NOT NULL or CHECK constraints; we handle those
975 : * below.
976 : */
977 62802 : descriptor = BuildDescForRelation(stmt->tableElts);
978 :
979 : /*
980 : * Find columns with default values and prepare for insertion of the
981 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
982 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
983 : * while raw defaults go into a list of RawColumnDefault structs that will
984 : * be processed by AddRelationNewConstraints. (We can't deal with raw
985 : * expressions until we can do transformExpr.)
986 : */
987 62754 : rawDefaults = NIL;
988 62754 : cookedDefaults = NIL;
989 62754 : attnum = 0;
990 :
991 316986 : foreach(listptr, stmt->tableElts)
992 : {
993 254232 : ColumnDef *colDef = lfirst(listptr);
994 :
995 254232 : attnum++;
996 254232 : if (colDef->raw_default != NULL)
997 : {
998 : RawColumnDefault *rawEnt;
999 :
1000 : Assert(colDef->cooked_default == NULL);
1001 :
1002 3338 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
1003 3338 : rawEnt->attnum = attnum;
1004 3338 : rawEnt->raw_default = colDef->raw_default;
1005 3338 : rawEnt->generated = colDef->generated;
1006 3338 : rawDefaults = lappend(rawDefaults, rawEnt);
1007 : }
1008 250894 : else if (colDef->cooked_default != NULL)
1009 : {
1010 : CookedConstraint *cooked;
1011 :
1012 404 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
1013 404 : cooked->contype = CONSTR_DEFAULT;
1014 404 : cooked->conoid = InvalidOid; /* until created */
1015 404 : cooked->name = NULL;
1016 404 : cooked->attnum = attnum;
1017 404 : cooked->expr = colDef->cooked_default;
1018 404 : cooked->is_enforced = true;
1019 404 : cooked->skip_validation = false;
1020 404 : cooked->is_local = true; /* not used for defaults */
1021 404 : cooked->inhcount = 0; /* ditto */
1022 404 : cooked->is_no_inherit = false;
1023 404 : cookedDefaults = lappend(cookedDefaults, cooked);
1024 : }
1025 : }
1026 :
1027 : /*
1028 : * For relations with table AM and partitioned tables, select access
1029 : * method to use: an explicitly indicated one, or (in the case of a
1030 : * partitioned table) the parent's, if it has one.
1031 : */
1032 62754 : if (stmt->accessMethod != NULL)
1033 : {
1034 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1035 122 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1036 : }
1037 62632 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1038 : {
1039 39404 : if (stmt->partbound)
1040 : {
1041 : Assert(list_length(inheritOids) == 1);
1042 7740 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1043 : }
1044 :
1045 39404 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1046 34300 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1047 : }
1048 :
1049 : /*
1050 : * Create the relation. Inherited defaults and CHECK constraints are
1051 : * passed in for immediate handling --- since they don't need parsing,
1052 : * they can be stored immediately.
1053 : */
1054 62736 : relationId = heap_create_with_catalog(relname,
1055 : namespaceId,
1056 : tablespaceId,
1057 : InvalidOid,
1058 : InvalidOid,
1059 : ofTypeId,
1060 : ownerId,
1061 : accessMethodId,
1062 : descriptor,
1063 : list_concat(cookedDefaults,
1064 : old_constraints),
1065 : relkind,
1066 62736 : stmt->relation->relpersistence,
1067 : false,
1068 : false,
1069 : stmt->oncommit,
1070 : reloptions,
1071 : true,
1072 : allowSystemTableMods,
1073 : false,
1074 : InvalidOid,
1075 : typaddress);
1076 :
1077 : /*
1078 : * We must bump the command counter to make the newly-created relation
1079 : * tuple visible for opening.
1080 : */
1081 62694 : CommandCounterIncrement();
1082 :
1083 : /*
1084 : * Open the new relation and acquire exclusive lock on it. This isn't
1085 : * really necessary for locking out other backends (since they can't see
1086 : * the new rel anyway until we commit), but it keeps the lock manager from
1087 : * complaining about deadlock risks.
1088 : */
1089 62694 : rel = relation_open(relationId, AccessExclusiveLock);
1090 :
1091 : /*
1092 : * Now add any newly specified column default and generation expressions
1093 : * to the new relation. These are passed to us in the form of raw
1094 : * parsetrees; we need to transform them to executable expression trees
1095 : * before they can be added. The most convenient way to do that is to
1096 : * apply the parser's transformExpr routine, but transformExpr doesn't
1097 : * work unless we have a pre-existing relation. So, the transformation has
1098 : * to be postponed to this final step of CREATE TABLE.
1099 : *
1100 : * This needs to be before processing the partitioning clauses because
1101 : * those could refer to generated columns.
1102 : */
1103 62694 : if (rawDefaults)
1104 2820 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1105 : true, true, false, queryString);
1106 :
1107 : /*
1108 : * Make column generation expressions visible for use by partitioning.
1109 : */
1110 62532 : CommandCounterIncrement();
1111 :
1112 : /* Process and store partition bound, if any. */
1113 62532 : if (stmt->partbound)
1114 : {
1115 : PartitionBoundSpec *bound;
1116 : ParseState *pstate;
1117 7844 : Oid parentId = linitial_oid(inheritOids),
1118 : defaultPartOid;
1119 : Relation parent,
1120 7844 : defaultRel = NULL;
1121 : ParseNamespaceItem *nsitem;
1122 :
1123 : /* Already have strong enough lock on the parent */
1124 7844 : parent = table_open(parentId, NoLock);
1125 :
1126 : /*
1127 : * We are going to try to validate the partition bound specification
1128 : * against the partition key of parentRel, so it better have one.
1129 : */
1130 7844 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1131 18 : ereport(ERROR,
1132 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1133 : errmsg("\"%s\" is not partitioned",
1134 : RelationGetRelationName(parent))));
1135 :
1136 : /*
1137 : * The partition constraint of the default partition depends on the
1138 : * partition bounds of every other partition. It is possible that
1139 : * another backend might be about to execute a query on the default
1140 : * partition table, and that the query relies on previously cached
1141 : * default partition constraints. We must therefore take a table lock
1142 : * strong enough to prevent all queries on the default partition from
1143 : * proceeding until we commit and send out a shared-cache-inval notice
1144 : * that will make them update their index lists.
1145 : *
1146 : * Order of locking: The relation being added won't be visible to
1147 : * other backends until it is committed, hence here in
1148 : * DefineRelation() the order of locking the default partition and the
1149 : * relation being added does not matter. But at all other places we
1150 : * need to lock the default relation before we lock the relation being
1151 : * added or removed i.e. we should take the lock in same order at all
1152 : * the places such that lock parent, lock default partition and then
1153 : * lock the partition so as to avoid a deadlock.
1154 : */
1155 : defaultPartOid =
1156 7826 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1157 : true));
1158 7826 : if (OidIsValid(defaultPartOid))
1159 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1160 :
1161 : /* Transform the bound values */
1162 7826 : pstate = make_parsestate(NULL);
1163 7826 : pstate->p_sourcetext = queryString;
1164 :
1165 : /*
1166 : * Add an nsitem containing this relation, so that transformExpr
1167 : * called on partition bound expressions is able to report errors
1168 : * using a proper context.
1169 : */
1170 7826 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1171 : NULL, false, false);
1172 7826 : addNSItemToQuery(pstate, nsitem, false, true, true);
1173 :
1174 7826 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1175 :
1176 : /*
1177 : * Check first that the new partition's bound is valid and does not
1178 : * overlap with any of existing partitions of the parent.
1179 : */
1180 7622 : check_new_partition_bound(relname, parent, bound, pstate);
1181 :
1182 : /*
1183 : * If the default partition exists, its partition constraints will
1184 : * change after the addition of this new partition such that it won't
1185 : * allow any row that qualifies for this new partition. So, check that
1186 : * the existing data in the default partition satisfies the constraint
1187 : * as it will exist after adding this partition.
1188 : */
1189 7508 : if (OidIsValid(defaultPartOid))
1190 : {
1191 348 : check_default_partition_contents(parent, defaultRel, bound);
1192 : /* Keep the lock until commit. */
1193 330 : table_close(defaultRel, NoLock);
1194 : }
1195 :
1196 : /* Update the pg_class entry. */
1197 7490 : StorePartitionBound(rel, parent, bound);
1198 :
1199 7490 : table_close(parent, NoLock);
1200 : }
1201 :
1202 : /* Store inheritance information for new rel. */
1203 62178 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1204 :
1205 : /*
1206 : * Process the partitioning specification (if any) and store the partition
1207 : * key information into the catalog.
1208 : */
1209 62178 : if (partitioned)
1210 : {
1211 : ParseState *pstate;
1212 : int partnatts;
1213 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1214 : Oid partopclass[PARTITION_MAX_KEYS];
1215 : Oid partcollation[PARTITION_MAX_KEYS];
1216 5090 : List *partexprs = NIL;
1217 :
1218 5090 : pstate = make_parsestate(NULL);
1219 5090 : pstate->p_sourcetext = queryString;
1220 :
1221 5090 : partnatts = list_length(stmt->partspec->partParams);
1222 :
1223 : /* Protect fixed-size arrays here and in executor */
1224 5090 : if (partnatts > PARTITION_MAX_KEYS)
1225 0 : ereport(ERROR,
1226 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1227 : errmsg("cannot partition using more than %d columns",
1228 : PARTITION_MAX_KEYS)));
1229 :
1230 : /*
1231 : * We need to transform the raw parsetrees corresponding to partition
1232 : * expressions into executable expression trees. Like column defaults
1233 : * and CHECK constraints, we could not have done the transformation
1234 : * earlier.
1235 : */
1236 5090 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1237 :
1238 5060 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1239 : partattrs, &partexprs, partopclass,
1240 5060 : partcollation, stmt->partspec->strategy);
1241 :
1242 4964 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1243 : partexprs,
1244 : partopclass, partcollation);
1245 :
1246 : /* make it all visible */
1247 4964 : CommandCounterIncrement();
1248 : }
1249 :
1250 : /*
1251 : * If we're creating a partition, create now all the indexes, triggers,
1252 : * FKs defined in the parent.
1253 : *
1254 : * We can't do it earlier, because DefineIndex wants to know the partition
1255 : * key which we just stored.
1256 : */
1257 62052 : if (stmt->partbound)
1258 : {
1259 7484 : Oid parentId = linitial_oid(inheritOids);
1260 : Relation parent;
1261 : List *idxlist;
1262 : ListCell *cell;
1263 :
1264 : /* Already have strong enough lock on the parent */
1265 7484 : parent = table_open(parentId, NoLock);
1266 7484 : idxlist = RelationGetIndexList(parent);
1267 :
1268 : /*
1269 : * For each index in the parent table, create one in the partition
1270 : */
1271 8868 : foreach(cell, idxlist)
1272 : {
1273 1402 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1274 : AttrMap *attmap;
1275 : IndexStmt *idxstmt;
1276 : Oid constraintOid;
1277 :
1278 1402 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1279 : {
1280 36 : if (idxRel->rd_index->indisunique)
1281 12 : ereport(ERROR,
1282 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1283 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1284 : RelationGetRelationName(parent)),
1285 : errdetail("Table \"%s\" contains indexes that are unique.",
1286 : RelationGetRelationName(parent))));
1287 : else
1288 : {
1289 24 : index_close(idxRel, AccessShareLock);
1290 24 : continue;
1291 : }
1292 : }
1293 :
1294 1366 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1295 : RelationGetDescr(parent),
1296 : false);
1297 : idxstmt =
1298 1366 : generateClonedIndexStmt(NULL, idxRel,
1299 : attmap, &constraintOid);
1300 1366 : DefineIndex(RelationGetRelid(rel),
1301 : idxstmt,
1302 : InvalidOid,
1303 : RelationGetRelid(idxRel),
1304 : constraintOid,
1305 : -1,
1306 : false, false, false, false, false);
1307 :
1308 1360 : index_close(idxRel, AccessShareLock);
1309 : }
1310 :
1311 7466 : list_free(idxlist);
1312 :
1313 : /*
1314 : * If there are any row-level triggers, clone them to the new
1315 : * partition.
1316 : */
1317 7466 : if (parent->trigdesc != NULL)
1318 420 : CloneRowTriggersToPartition(parent, rel);
1319 :
1320 : /*
1321 : * And foreign keys too. Note that because we're freshly creating the
1322 : * table, there is no need to verify these new constraints.
1323 : */
1324 7466 : CloneForeignKeyConstraints(NULL, parent, rel);
1325 :
1326 7466 : table_close(parent, NoLock);
1327 : }
1328 :
1329 : /*
1330 : * Now add any newly specified CHECK constraints to the new relation. Same
1331 : * as for defaults above, but these need to come after partitioning is set
1332 : * up.
1333 : */
1334 62034 : if (stmt->constraints)
1335 778 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1336 : true, true, false, queryString);
1337 :
1338 : /*
1339 : * Finally, merge the not-null constraints that are declared directly with
1340 : * those that come from parent relations (making sure to count inheritance
1341 : * appropriately for each), create them, and set the attnotnull flag on
1342 : * columns that don't yet have it.
1343 : */
1344 62004 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1345 : old_notnulls);
1346 139280 : foreach_int(attrnum, nncols)
1347 15428 : set_attnotnull(NULL, rel, attrnum, true, false);
1348 :
1349 61926 : ObjectAddressSet(address, RelationRelationId, relationId);
1350 :
1351 : /*
1352 : * Clean up. We keep lock on new relation (although it shouldn't be
1353 : * visible to anyone else anyway, until commit).
1354 : */
1355 61926 : relation_close(rel, NoLock);
1356 :
1357 61926 : return address;
1358 : }
1359 :
1360 : /*
1361 : * BuildDescForRelation
1362 : *
1363 : * Given a list of ColumnDef nodes, build a TupleDesc.
1364 : *
1365 : * Note: This is only for the limited purpose of table and view creation. Not
1366 : * everything is filled in. A real tuple descriptor should be obtained from
1367 : * the relcache.
1368 : */
1369 : TupleDesc
1370 65764 : BuildDescForRelation(const List *columns)
1371 : {
1372 : int natts;
1373 : AttrNumber attnum;
1374 : ListCell *l;
1375 : TupleDesc desc;
1376 : char *attname;
1377 : Oid atttypid;
1378 : int32 atttypmod;
1379 : Oid attcollation;
1380 : int attdim;
1381 :
1382 : /*
1383 : * allocate a new tuple descriptor
1384 : */
1385 65764 : natts = list_length(columns);
1386 65764 : desc = CreateTemplateTupleDesc(natts);
1387 :
1388 65764 : attnum = 0;
1389 :
1390 323222 : foreach(l, columns)
1391 : {
1392 257518 : ColumnDef *entry = lfirst(l);
1393 : AclResult aclresult;
1394 : Form_pg_attribute att;
1395 :
1396 : /*
1397 : * for each entry in the list, get the name and type information from
1398 : * the list and have TupleDescInitEntry fill in the attribute
1399 : * information we need.
1400 : */
1401 257518 : attnum++;
1402 :
1403 257518 : attname = entry->colname;
1404 257518 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1405 :
1406 257518 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1407 257518 : if (aclresult != ACLCHECK_OK)
1408 42 : aclcheck_error_type(aclresult, atttypid);
1409 :
1410 257476 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1411 257476 : attdim = list_length(entry->typeName->arrayBounds);
1412 257476 : if (attdim > PG_INT16_MAX)
1413 0 : ereport(ERROR,
1414 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1415 : errmsg("too many array dimensions"));
1416 :
1417 257476 : if (entry->typeName->setof)
1418 0 : ereport(ERROR,
1419 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1420 : errmsg("column \"%s\" cannot be declared SETOF",
1421 : attname)));
1422 :
1423 257476 : TupleDescInitEntry(desc, attnum, attname,
1424 : atttypid, atttypmod, attdim);
1425 257476 : att = TupleDescAttr(desc, attnum - 1);
1426 :
1427 : /* Override TupleDescInitEntry's settings as requested */
1428 257476 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1429 :
1430 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1431 257476 : att->attnotnull = entry->is_not_null;
1432 257476 : att->attislocal = entry->is_local;
1433 257476 : att->attinhcount = entry->inhcount;
1434 257476 : att->attidentity = entry->identity;
1435 257476 : att->attgenerated = entry->generated;
1436 257476 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1437 257464 : if (entry->storage)
1438 20096 : att->attstorage = entry->storage;
1439 237368 : else if (entry->storage_name)
1440 20 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1441 :
1442 257458 : populate_compact_attribute(desc, attnum - 1);
1443 : }
1444 :
1445 65704 : return desc;
1446 : }
1447 :
1448 : /*
1449 : * Emit the right error or warning message for a "DROP" command issued on a
1450 : * non-existent relation
1451 : */
1452 : static void
1453 1080 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1454 : {
1455 : const struct dropmsgstrings *rentry;
1456 :
1457 1200 : if (rel->schemaname != NULL &&
1458 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1459 : {
1460 42 : if (!missing_ok)
1461 : {
1462 0 : ereport(ERROR,
1463 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1464 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1465 : }
1466 : else
1467 : {
1468 42 : ereport(NOTICE,
1469 : (errmsg("schema \"%s\" does not exist, skipping",
1470 : rel->schemaname)));
1471 : }
1472 42 : return;
1473 : }
1474 :
1475 1358 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1476 : {
1477 1358 : if (rentry->kind == rightkind)
1478 : {
1479 1038 : if (!missing_ok)
1480 : {
1481 138 : ereport(ERROR,
1482 : (errcode(rentry->nonexistent_code),
1483 : errmsg(rentry->nonexistent_msg, rel->relname)));
1484 : }
1485 : else
1486 : {
1487 900 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1488 900 : break;
1489 : }
1490 : }
1491 : }
1492 :
1493 : Assert(rentry->kind != '\0'); /* Should be impossible */
1494 : }
1495 :
1496 : /*
1497 : * Emit the right error message for a "DROP" command issued on a
1498 : * relation of the wrong type
1499 : */
1500 : static void
1501 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1502 : {
1503 : const struct dropmsgstrings *rentry;
1504 : const struct dropmsgstrings *wentry;
1505 :
1506 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1507 0 : if (rentry->kind == rightkind)
1508 0 : break;
1509 : Assert(rentry->kind != '\0');
1510 :
1511 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1512 0 : if (wentry->kind == wrongkind)
1513 0 : break;
1514 : /* wrongkind could be something we don't have in our table... */
1515 :
1516 0 : ereport(ERROR,
1517 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1518 : errmsg(rentry->nota_msg, relname),
1519 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1520 : }
1521 :
1522 : /*
1523 : * RemoveRelations
1524 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1525 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1526 : */
1527 : void
1528 17054 : RemoveRelations(DropStmt *drop)
1529 : {
1530 : ObjectAddresses *objects;
1531 : char relkind;
1532 : ListCell *cell;
1533 17054 : int flags = 0;
1534 17054 : LOCKMODE lockmode = AccessExclusiveLock;
1535 :
1536 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1537 17054 : if (drop->concurrent)
1538 : {
1539 : /*
1540 : * Note that for temporary relations this lock may get upgraded later
1541 : * on, but as no other session can access a temporary relation, this
1542 : * is actually fine.
1543 : */
1544 178 : lockmode = ShareUpdateExclusiveLock;
1545 : Assert(drop->removeType == OBJECT_INDEX);
1546 178 : if (list_length(drop->objects) != 1)
1547 6 : ereport(ERROR,
1548 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1549 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1550 172 : if (drop->behavior == DROP_CASCADE)
1551 0 : ereport(ERROR,
1552 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1553 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1554 : }
1555 :
1556 : /*
1557 : * First we identify all the relations, then we delete them in a single
1558 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1559 : * RESTRICT errors if one of the relations depends on another.
1560 : */
1561 :
1562 : /* Determine required relkind */
1563 17048 : switch (drop->removeType)
1564 : {
1565 14794 : case OBJECT_TABLE:
1566 14794 : relkind = RELKIND_RELATION;
1567 14794 : break;
1568 :
1569 860 : case OBJECT_INDEX:
1570 860 : relkind = RELKIND_INDEX;
1571 860 : break;
1572 :
1573 170 : case OBJECT_SEQUENCE:
1574 170 : relkind = RELKIND_SEQUENCE;
1575 170 : break;
1576 :
1577 936 : case OBJECT_VIEW:
1578 936 : relkind = RELKIND_VIEW;
1579 936 : break;
1580 :
1581 126 : case OBJECT_MATVIEW:
1582 126 : relkind = RELKIND_MATVIEW;
1583 126 : break;
1584 :
1585 162 : case OBJECT_FOREIGN_TABLE:
1586 162 : relkind = RELKIND_FOREIGN_TABLE;
1587 162 : break;
1588 :
1589 0 : default:
1590 0 : elog(ERROR, "unrecognized drop object type: %d",
1591 : (int) drop->removeType);
1592 : relkind = 0; /* keep compiler quiet */
1593 : break;
1594 : }
1595 :
1596 : /* Lock and validate each relation; build a list of object addresses */
1597 17048 : objects = new_object_addresses();
1598 :
1599 38020 : foreach(cell, drop->objects)
1600 : {
1601 21136 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1602 : Oid relOid;
1603 : ObjectAddress obj;
1604 : struct DropRelationCallbackState state;
1605 :
1606 : /*
1607 : * These next few steps are a great deal like relation_openrv, but we
1608 : * don't bother building a relcache entry since we don't need it.
1609 : *
1610 : * Check for shared-cache-inval messages before trying to access the
1611 : * relation. This is needed to cover the case where the name
1612 : * identifies a rel that has been dropped and recreated since the
1613 : * start of our transaction: if we don't flush the old syscache entry,
1614 : * then we'll latch onto that entry and suffer an error later.
1615 : */
1616 21136 : AcceptInvalidationMessages();
1617 :
1618 : /* Look up the appropriate relation using namespace search. */
1619 21136 : state.expected_relkind = relkind;
1620 42272 : state.heap_lockmode = drop->concurrent ?
1621 21136 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1622 : /* We must initialize these fields to show that no locks are held: */
1623 21136 : state.heapOid = InvalidOid;
1624 21136 : state.partParentOid = InvalidOid;
1625 :
1626 21136 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1627 : RangeVarCallbackForDropRelation,
1628 : &state);
1629 :
1630 : /* Not there? */
1631 21116 : if (!OidIsValid(relOid))
1632 : {
1633 1080 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1634 942 : continue;
1635 : }
1636 :
1637 : /*
1638 : * Decide if concurrent mode needs to be used here or not. The
1639 : * callback retrieved the rel's persistence for us.
1640 : */
1641 20036 : if (drop->concurrent &&
1642 166 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1643 : {
1644 : Assert(list_length(drop->objects) == 1 &&
1645 : drop->removeType == OBJECT_INDEX);
1646 148 : flags |= PERFORM_DELETION_CONCURRENTLY;
1647 : }
1648 :
1649 : /*
1650 : * Concurrent index drop cannot be used with partitioned indexes,
1651 : * either.
1652 : */
1653 20036 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1654 148 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1655 6 : ereport(ERROR,
1656 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1657 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1658 : rel->relname)));
1659 :
1660 : /*
1661 : * If we're told to drop a partitioned index, we must acquire lock on
1662 : * all the children of its parent partitioned table before proceeding.
1663 : * Otherwise we'd try to lock the child index partitions before their
1664 : * tables, leading to potential deadlock against other sessions that
1665 : * will lock those objects in the other order.
1666 : */
1667 20030 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1668 76 : (void) find_all_inheritors(state.heapOid,
1669 : state.heap_lockmode,
1670 : NULL);
1671 :
1672 : /* OK, we're ready to delete this one */
1673 20030 : obj.classId = RelationRelationId;
1674 20030 : obj.objectId = relOid;
1675 20030 : obj.objectSubId = 0;
1676 :
1677 20030 : add_exact_object_address(&obj, objects);
1678 : }
1679 :
1680 16884 : performMultipleDeletions(objects, drop->behavior, flags);
1681 :
1682 16742 : free_object_addresses(objects);
1683 16742 : }
1684 :
1685 : /*
1686 : * Before acquiring a table lock, check whether we have sufficient rights.
1687 : * In the case of DROP INDEX, also try to lock the table before the index.
1688 : * Also, if the table to be dropped is a partition, we try to lock the parent
1689 : * first.
1690 : */
1691 : static void
1692 21454 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1693 : void *arg)
1694 : {
1695 : HeapTuple tuple;
1696 : struct DropRelationCallbackState *state;
1697 : char expected_relkind;
1698 : bool is_partition;
1699 : Form_pg_class classform;
1700 : LOCKMODE heap_lockmode;
1701 21454 : bool invalid_system_index = false;
1702 :
1703 21454 : state = (struct DropRelationCallbackState *) arg;
1704 21454 : heap_lockmode = state->heap_lockmode;
1705 :
1706 : /*
1707 : * If we previously locked some other index's heap, and the name we're
1708 : * looking up no longer refers to that relation, release the now-useless
1709 : * lock.
1710 : */
1711 21454 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1712 : {
1713 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1714 0 : state->heapOid = InvalidOid;
1715 : }
1716 :
1717 : /*
1718 : * Similarly, if we previously locked some other partition's heap, and the
1719 : * name we're looking up no longer refers to that relation, release the
1720 : * now-useless lock.
1721 : */
1722 21454 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1723 : {
1724 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1725 0 : state->partParentOid = InvalidOid;
1726 : }
1727 :
1728 : /* Didn't find a relation, so no need for locking or permission checks. */
1729 21454 : if (!OidIsValid(relOid))
1730 1090 : return;
1731 :
1732 20364 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1733 20364 : if (!HeapTupleIsValid(tuple))
1734 0 : return; /* concurrently dropped, so nothing to do */
1735 20364 : classform = (Form_pg_class) GETSTRUCT(tuple);
1736 20364 : is_partition = classform->relispartition;
1737 :
1738 : /* Pass back some data to save lookups in RemoveRelations */
1739 20364 : state->actual_relkind = classform->relkind;
1740 20364 : state->actual_relpersistence = classform->relpersistence;
1741 :
1742 : /*
1743 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1744 : * but RemoveRelations() can only pass one relkind for a given relation.
1745 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1746 : * That means we must be careful before giving the wrong type error when
1747 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1748 : * exists with indexes.
1749 : */
1750 20364 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1751 2924 : expected_relkind = RELKIND_RELATION;
1752 17440 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1753 86 : expected_relkind = RELKIND_INDEX;
1754 : else
1755 17354 : expected_relkind = classform->relkind;
1756 :
1757 20364 : if (state->expected_relkind != expected_relkind)
1758 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1759 0 : state->expected_relkind);
1760 :
1761 : /* Allow DROP to either table owner or schema owner */
1762 20364 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1763 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1764 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1765 18 : get_relkind_objtype(classform->relkind),
1766 18 : rel->relname);
1767 :
1768 : /*
1769 : * Check the case of a system index that might have been invalidated by a
1770 : * failed concurrent process and allow its drop. For the time being, this
1771 : * only concerns indexes of toast relations that became invalid during a
1772 : * REINDEX CONCURRENTLY process.
1773 : */
1774 20346 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1775 : {
1776 : HeapTuple locTuple;
1777 : Form_pg_index indexform;
1778 : bool indisvalid;
1779 :
1780 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1781 0 : if (!HeapTupleIsValid(locTuple))
1782 : {
1783 0 : ReleaseSysCache(tuple);
1784 0 : return;
1785 : }
1786 :
1787 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1788 0 : indisvalid = indexform->indisvalid;
1789 0 : ReleaseSysCache(locTuple);
1790 :
1791 : /* Mark object as being an invalid index of system catalogs */
1792 0 : if (!indisvalid)
1793 0 : invalid_system_index = true;
1794 : }
1795 :
1796 : /* In the case of an invalid index, it is fine to bypass this check */
1797 20346 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1798 2 : ereport(ERROR,
1799 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1800 : errmsg("permission denied: \"%s\" is a system catalog",
1801 : rel->relname)));
1802 :
1803 20344 : ReleaseSysCache(tuple);
1804 :
1805 : /*
1806 : * In DROP INDEX, attempt to acquire lock on the parent table before
1807 : * locking the index. index_drop() will need this anyway, and since
1808 : * regular queries lock tables before their indexes, we risk deadlock if
1809 : * we do it the other way around. No error if we don't find a pg_index
1810 : * entry, though --- the relation may have been dropped. Note that this
1811 : * code will execute for either plain or partitioned indexes.
1812 : */
1813 20344 : if (expected_relkind == RELKIND_INDEX &&
1814 : relOid != oldRelOid)
1815 : {
1816 848 : state->heapOid = IndexGetRelation(relOid, true);
1817 848 : if (OidIsValid(state->heapOid))
1818 848 : LockRelationOid(state->heapOid, heap_lockmode);
1819 : }
1820 :
1821 : /*
1822 : * Similarly, if the relation is a partition, we must acquire lock on its
1823 : * parent before locking the partition. That's because queries lock the
1824 : * parent before its partitions, so we risk deadlock if we do it the other
1825 : * way around.
1826 : */
1827 20344 : if (is_partition && relOid != oldRelOid)
1828 : {
1829 612 : state->partParentOid = get_partition_parent(relOid, true);
1830 612 : if (OidIsValid(state->partParentOid))
1831 612 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1832 : }
1833 : }
1834 :
1835 : /*
1836 : * ExecuteTruncate
1837 : * Executes a TRUNCATE command.
1838 : *
1839 : * This is a multi-relation truncate. We first open and grab exclusive
1840 : * lock on all relations involved, checking permissions and otherwise
1841 : * verifying that the relation is OK for truncation. Note that if relations
1842 : * are foreign tables, at this stage, we have not yet checked that their
1843 : * foreign data in external data sources are OK for truncation. These are
1844 : * checked when foreign data are actually truncated later. In CASCADE mode,
1845 : * relations having FK references to the targeted relations are automatically
1846 : * added to the group; in RESTRICT mode, we check that all FK references are
1847 : * internal to the group that's being truncated. Finally all the relations
1848 : * are truncated and reindexed.
1849 : */
1850 : void
1851 2926 : ExecuteTruncate(TruncateStmt *stmt)
1852 : {
1853 2926 : List *rels = NIL;
1854 2926 : List *relids = NIL;
1855 2926 : List *relids_logged = NIL;
1856 : ListCell *cell;
1857 :
1858 : /*
1859 : * Open, exclusive-lock, and check all the explicitly-specified relations
1860 : */
1861 6064 : foreach(cell, stmt->relations)
1862 : {
1863 3194 : RangeVar *rv = lfirst(cell);
1864 : Relation rel;
1865 3194 : bool recurse = rv->inh;
1866 : Oid myrelid;
1867 3194 : LOCKMODE lockmode = AccessExclusiveLock;
1868 :
1869 3194 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1870 : 0, RangeVarCallbackForTruncate,
1871 : NULL);
1872 :
1873 : /* don't throw error for "TRUNCATE foo, foo" */
1874 3156 : if (list_member_oid(relids, myrelid))
1875 2 : continue;
1876 :
1877 : /* open the relation, we already hold a lock on it */
1878 3154 : rel = table_open(myrelid, NoLock);
1879 :
1880 : /*
1881 : * RangeVarGetRelidExtended() has done most checks with its callback,
1882 : * but other checks with the now-opened Relation remain.
1883 : */
1884 3154 : truncate_check_activity(rel);
1885 :
1886 3148 : rels = lappend(rels, rel);
1887 3148 : relids = lappend_oid(relids, myrelid);
1888 :
1889 : /* Log this relation only if needed for logical decoding */
1890 3148 : if (RelationIsLogicallyLogged(rel))
1891 68 : relids_logged = lappend_oid(relids_logged, myrelid);
1892 :
1893 3148 : if (recurse)
1894 : {
1895 : ListCell *child;
1896 : List *children;
1897 :
1898 1838 : children = find_all_inheritors(myrelid, lockmode, NULL);
1899 :
1900 5480 : foreach(child, children)
1901 : {
1902 3642 : Oid childrelid = lfirst_oid(child);
1903 :
1904 3642 : if (list_member_oid(relids, childrelid))
1905 1838 : continue;
1906 :
1907 : /* find_all_inheritors already got lock */
1908 1804 : rel = table_open(childrelid, NoLock);
1909 :
1910 : /*
1911 : * It is possible that the parent table has children that are
1912 : * temp tables of other backends. We cannot safely access
1913 : * such tables (because of buffering issues), and the best
1914 : * thing to do is to silently ignore them. Note that this
1915 : * check is the same as one of the checks done in
1916 : * truncate_check_activity() called below, still it is kept
1917 : * here for simplicity.
1918 : */
1919 1804 : if (RELATION_IS_OTHER_TEMP(rel))
1920 : {
1921 8 : table_close(rel, lockmode);
1922 8 : continue;
1923 : }
1924 :
1925 : /*
1926 : * Inherited TRUNCATE commands perform access permission
1927 : * checks on the parent table only. So we skip checking the
1928 : * children's permissions and don't call
1929 : * truncate_check_perms() here.
1930 : */
1931 1796 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1932 1796 : truncate_check_activity(rel);
1933 :
1934 1796 : rels = lappend(rels, rel);
1935 1796 : relids = lappend_oid(relids, childrelid);
1936 :
1937 : /* Log this relation only if needed for logical decoding */
1938 1796 : if (RelationIsLogicallyLogged(rel))
1939 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1940 : }
1941 : }
1942 1310 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1943 12 : ereport(ERROR,
1944 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1945 : errmsg("cannot truncate only a partitioned table"),
1946 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1947 : }
1948 :
1949 2870 : ExecuteTruncateGuts(rels, relids, relids_logged,
1950 2870 : stmt->behavior, stmt->restart_seqs, false);
1951 :
1952 : /* And close the rels */
1953 7566 : foreach(cell, rels)
1954 : {
1955 4778 : Relation rel = (Relation) lfirst(cell);
1956 :
1957 4778 : table_close(rel, NoLock);
1958 : }
1959 2788 : }
1960 :
1961 : /*
1962 : * ExecuteTruncateGuts
1963 : *
1964 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1965 : * command (see above) as well as replication subscribers that execute a
1966 : * replicated TRUNCATE action.
1967 : *
1968 : * explicit_rels is the list of Relations to truncate that the command
1969 : * specified. relids is the list of Oids corresponding to explicit_rels.
1970 : * relids_logged is the list of Oids (a subset of relids) that require
1971 : * WAL-logging. This is all a bit redundant, but the existing callers have
1972 : * this information handy in this form.
1973 : */
1974 : void
1975 2908 : ExecuteTruncateGuts(List *explicit_rels,
1976 : List *relids,
1977 : List *relids_logged,
1978 : DropBehavior behavior, bool restart_seqs,
1979 : bool run_as_table_owner)
1980 : {
1981 : List *rels;
1982 2908 : List *seq_relids = NIL;
1983 2908 : HTAB *ft_htab = NULL;
1984 : EState *estate;
1985 : ResultRelInfo *resultRelInfos;
1986 : ResultRelInfo *resultRelInfo;
1987 : SubTransactionId mySubid;
1988 : ListCell *cell;
1989 : Oid *logrelids;
1990 :
1991 : /*
1992 : * Check the explicitly-specified relations.
1993 : *
1994 : * In CASCADE mode, suck in all referencing relations as well. This
1995 : * requires multiple iterations to find indirectly-dependent relations. At
1996 : * each phase, we need to exclusive-lock new rels before looking for their
1997 : * dependencies, else we might miss something. Also, we check each rel as
1998 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1999 : * time on a rel we have no permissions for.
2000 : */
2001 2908 : rels = list_copy(explicit_rels);
2002 2908 : if (behavior == DROP_CASCADE)
2003 : {
2004 : for (;;)
2005 40 : {
2006 : List *newrelids;
2007 :
2008 80 : newrelids = heap_truncate_find_FKs(relids);
2009 80 : if (newrelids == NIL)
2010 40 : break; /* nothing else to add */
2011 :
2012 134 : foreach(cell, newrelids)
2013 : {
2014 94 : Oid relid = lfirst_oid(cell);
2015 : Relation rel;
2016 :
2017 94 : rel = table_open(relid, AccessExclusiveLock);
2018 94 : ereport(NOTICE,
2019 : (errmsg("truncate cascades to table \"%s\"",
2020 : RelationGetRelationName(rel))));
2021 94 : truncate_check_rel(relid, rel->rd_rel);
2022 94 : truncate_check_perms(relid, rel->rd_rel);
2023 94 : truncate_check_activity(rel);
2024 94 : rels = lappend(rels, rel);
2025 94 : relids = lappend_oid(relids, relid);
2026 :
2027 : /* Log this relation only if needed for logical decoding */
2028 94 : if (RelationIsLogicallyLogged(rel))
2029 0 : relids_logged = lappend_oid(relids_logged, relid);
2030 : }
2031 : }
2032 : }
2033 :
2034 : /*
2035 : * Check foreign key references. In CASCADE mode, this should be
2036 : * unnecessary since we just pulled in all the references; but as a
2037 : * cross-check, do it anyway if in an Assert-enabled build.
2038 : */
2039 : #ifdef USE_ASSERT_CHECKING
2040 : heap_truncate_check_FKs(rels, false);
2041 : #else
2042 2908 : if (behavior == DROP_RESTRICT)
2043 2868 : heap_truncate_check_FKs(rels, false);
2044 : #endif
2045 :
2046 : /*
2047 : * If we are asked to restart sequences, find all the sequences, lock them
2048 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2049 : * We want to do this early since it's pointless to do all the truncation
2050 : * work only to fail on sequence permissions.
2051 : */
2052 2834 : if (restart_seqs)
2053 : {
2054 52 : foreach(cell, rels)
2055 : {
2056 26 : Relation rel = (Relation) lfirst(cell);
2057 26 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2058 : ListCell *seqcell;
2059 :
2060 62 : foreach(seqcell, seqlist)
2061 : {
2062 36 : Oid seq_relid = lfirst_oid(seqcell);
2063 : Relation seq_rel;
2064 :
2065 36 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2066 :
2067 : /* This check must match AlterSequence! */
2068 36 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2069 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2070 0 : RelationGetRelationName(seq_rel));
2071 :
2072 36 : seq_relids = lappend_oid(seq_relids, seq_relid);
2073 :
2074 36 : relation_close(seq_rel, NoLock);
2075 : }
2076 : }
2077 : }
2078 :
2079 : /* Prepare to catch AFTER triggers. */
2080 2834 : AfterTriggerBeginQuery();
2081 :
2082 : /*
2083 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2084 : * each relation. We don't need to call ExecOpenIndices, though.
2085 : *
2086 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2087 : * though we don't have a range table and don't populate the
2088 : * es_result_relations array. That's a bit bogus, but it's enough to make
2089 : * ExecGetTriggerResultRel() find them.
2090 : */
2091 2834 : estate = CreateExecutorState();
2092 : resultRelInfos = (ResultRelInfo *)
2093 2834 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2094 2834 : resultRelInfo = resultRelInfos;
2095 7784 : foreach(cell, rels)
2096 : {
2097 4950 : Relation rel = (Relation) lfirst(cell);
2098 :
2099 4950 : InitResultRelInfo(resultRelInfo,
2100 : rel,
2101 : 0, /* dummy rangetable index */
2102 : NULL,
2103 : 0);
2104 4950 : estate->es_opened_result_relations =
2105 4950 : lappend(estate->es_opened_result_relations, resultRelInfo);
2106 4950 : resultRelInfo++;
2107 : }
2108 :
2109 : /*
2110 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2111 : * truncating (this is because one of them might throw an error). Also, if
2112 : * we were to allow them to prevent statement execution, that would need
2113 : * to be handled here.
2114 : */
2115 2834 : resultRelInfo = resultRelInfos;
2116 7784 : foreach(cell, rels)
2117 : {
2118 : UserContext ucxt;
2119 :
2120 4950 : if (run_as_table_owner)
2121 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2122 : &ucxt);
2123 4950 : ExecBSTruncateTriggers(estate, resultRelInfo);
2124 4950 : if (run_as_table_owner)
2125 70 : RestoreUserContext(&ucxt);
2126 4950 : resultRelInfo++;
2127 : }
2128 :
2129 : /*
2130 : * OK, truncate each table.
2131 : */
2132 2834 : mySubid = GetCurrentSubTransactionId();
2133 :
2134 7784 : foreach(cell, rels)
2135 : {
2136 4950 : Relation rel = (Relation) lfirst(cell);
2137 :
2138 : /* Skip partitioned tables as there is nothing to do */
2139 4950 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2140 704 : continue;
2141 :
2142 : /*
2143 : * Build the lists of foreign tables belonging to each foreign server
2144 : * and pass each list to the foreign data wrapper's callback function,
2145 : * so that each server can truncate its all foreign tables in bulk.
2146 : * Each list is saved as a single entry in a hash table that uses the
2147 : * server OID as lookup key.
2148 : */
2149 4246 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2150 : {
2151 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2152 : bool found;
2153 : ForeignTruncateInfo *ft_info;
2154 :
2155 : /* First time through, initialize hashtable for foreign tables */
2156 34 : if (!ft_htab)
2157 : {
2158 : HASHCTL hctl;
2159 :
2160 30 : memset(&hctl, 0, sizeof(HASHCTL));
2161 30 : hctl.keysize = sizeof(Oid);
2162 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2163 30 : hctl.hcxt = CurrentMemoryContext;
2164 :
2165 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2166 : 32, /* start small and extend */
2167 : &hctl,
2168 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2169 : }
2170 :
2171 : /* Find or create cached entry for the foreign table */
2172 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2173 34 : if (!found)
2174 30 : ft_info->rels = NIL;
2175 :
2176 : /*
2177 : * Save the foreign table in the entry of the server that the
2178 : * foreign table belongs to.
2179 : */
2180 34 : ft_info->rels = lappend(ft_info->rels, rel);
2181 34 : continue;
2182 : }
2183 :
2184 : /*
2185 : * Normally, we need a transaction-safe truncation here. However, if
2186 : * the table was either created in the current (sub)transaction or has
2187 : * a new relfilenumber in the current (sub)transaction, then we can
2188 : * just truncate it in-place, because a rollback would cause the whole
2189 : * table or the current physical file to be thrown away anyway.
2190 : */
2191 4212 : if (rel->rd_createSubid == mySubid ||
2192 4184 : rel->rd_newRelfilelocatorSubid == mySubid)
2193 : {
2194 : /* Immediate, non-rollbackable truncation is OK */
2195 92 : heap_truncate_one_rel(rel);
2196 : }
2197 : else
2198 : {
2199 : Oid heap_relid;
2200 : Oid toast_relid;
2201 4120 : ReindexParams reindex_params = {0};
2202 :
2203 : /*
2204 : * This effectively deletes all rows in the table, and may be done
2205 : * in a serializable transaction. In that case we must record a
2206 : * rw-conflict in to this transaction from each transaction
2207 : * holding a predicate lock on the table.
2208 : */
2209 4120 : CheckTableForSerializableConflictIn(rel);
2210 :
2211 : /*
2212 : * Need the full transaction-safe pushups.
2213 : *
2214 : * Create a new empty storage file for the relation, and assign it
2215 : * as the relfilenumber value. The old storage file is scheduled
2216 : * for deletion at commit.
2217 : */
2218 4120 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2219 :
2220 4120 : heap_relid = RelationGetRelid(rel);
2221 :
2222 : /*
2223 : * The same for the toast table, if any.
2224 : */
2225 4120 : toast_relid = rel->rd_rel->reltoastrelid;
2226 4120 : if (OidIsValid(toast_relid))
2227 : {
2228 2282 : Relation toastrel = relation_open(toast_relid,
2229 : AccessExclusiveLock);
2230 :
2231 2282 : RelationSetNewRelfilenumber(toastrel,
2232 2282 : toastrel->rd_rel->relpersistence);
2233 2282 : table_close(toastrel, NoLock);
2234 : }
2235 :
2236 : /*
2237 : * Reconstruct the indexes to match, and we're done.
2238 : */
2239 4120 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2240 : &reindex_params);
2241 : }
2242 :
2243 4212 : pgstat_count_truncate(rel);
2244 : }
2245 :
2246 : /* Now go through the hash table, and truncate foreign tables */
2247 2834 : if (ft_htab)
2248 : {
2249 : ForeignTruncateInfo *ft_info;
2250 : HASH_SEQ_STATUS seq;
2251 :
2252 30 : hash_seq_init(&seq, ft_htab);
2253 :
2254 30 : PG_TRY();
2255 : {
2256 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2257 : {
2258 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2259 :
2260 : /* truncate_check_rel() has checked that already */
2261 : Assert(routine->ExecForeignTruncate != NULL);
2262 :
2263 30 : routine->ExecForeignTruncate(ft_info->rels,
2264 : behavior,
2265 : restart_seqs);
2266 : }
2267 : }
2268 8 : PG_FINALLY();
2269 : {
2270 30 : hash_destroy(ft_htab);
2271 : }
2272 30 : PG_END_TRY();
2273 : }
2274 :
2275 : /*
2276 : * Restart owned sequences if we were asked to.
2277 : */
2278 2862 : foreach(cell, seq_relids)
2279 : {
2280 36 : Oid seq_relid = lfirst_oid(cell);
2281 :
2282 36 : ResetSequence(seq_relid);
2283 : }
2284 :
2285 : /*
2286 : * Write a WAL record to allow this set of actions to be logically
2287 : * decoded.
2288 : *
2289 : * Assemble an array of relids so we can write a single WAL record for the
2290 : * whole action.
2291 : */
2292 2826 : if (relids_logged != NIL)
2293 : {
2294 : xl_heap_truncate xlrec;
2295 54 : int i = 0;
2296 :
2297 : /* should only get here if wal_level >= logical */
2298 : Assert(XLogLogicalInfoActive());
2299 :
2300 54 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2301 144 : foreach(cell, relids_logged)
2302 90 : logrelids[i++] = lfirst_oid(cell);
2303 :
2304 54 : xlrec.dbId = MyDatabaseId;
2305 54 : xlrec.nrelids = list_length(relids_logged);
2306 54 : xlrec.flags = 0;
2307 54 : if (behavior == DROP_CASCADE)
2308 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2309 54 : if (restart_seqs)
2310 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2311 :
2312 54 : XLogBeginInsert();
2313 54 : XLogRegisterData(&xlrec, SizeOfHeapTruncate);
2314 54 : XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2315 :
2316 54 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2317 :
2318 54 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2319 : }
2320 :
2321 : /*
2322 : * Process all AFTER STATEMENT TRUNCATE triggers.
2323 : */
2324 2826 : resultRelInfo = resultRelInfos;
2325 7768 : foreach(cell, rels)
2326 : {
2327 : UserContext ucxt;
2328 :
2329 4942 : if (run_as_table_owner)
2330 70 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2331 : &ucxt);
2332 4942 : ExecASTruncateTriggers(estate, resultRelInfo);
2333 4942 : if (run_as_table_owner)
2334 70 : RestoreUserContext(&ucxt);
2335 4942 : resultRelInfo++;
2336 : }
2337 :
2338 : /* Handle queued AFTER triggers */
2339 2826 : AfterTriggerEndQuery(estate);
2340 :
2341 : /* We can clean up the EState now */
2342 2826 : FreeExecutorState(estate);
2343 :
2344 : /*
2345 : * Close any rels opened by CASCADE (can't do this while EState still
2346 : * holds refs)
2347 : */
2348 2826 : rels = list_difference_ptr(rels, explicit_rels);
2349 2920 : foreach(cell, rels)
2350 : {
2351 94 : Relation rel = (Relation) lfirst(cell);
2352 :
2353 94 : table_close(rel, NoLock);
2354 : }
2355 2826 : }
2356 :
2357 : /*
2358 : * Check that a given relation is safe to truncate. Subroutine for
2359 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2360 : */
2361 : static void
2362 5464 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2363 : {
2364 5464 : char *relname = NameStr(reltuple->relname);
2365 :
2366 : /*
2367 : * Only allow truncate on regular tables, foreign tables using foreign
2368 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2369 : * latter are only being included here for the following checks; no
2370 : * physical truncation will occur in their case.).
2371 : */
2372 5464 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2373 : {
2374 38 : Oid serverid = GetForeignServerIdByRelId(relid);
2375 38 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2376 :
2377 36 : if (!fdwroutine->ExecForeignTruncate)
2378 2 : ereport(ERROR,
2379 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2380 : errmsg("cannot truncate foreign table \"%s\"",
2381 : relname)));
2382 : }
2383 5426 : else if (reltuple->relkind != RELKIND_RELATION &&
2384 726 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2385 0 : ereport(ERROR,
2386 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2387 : errmsg("\"%s\" is not a table", relname)));
2388 :
2389 : /*
2390 : * Most system catalogs can't be truncated at all, or at least not unless
2391 : * allow_system_table_mods=on. As an exception, however, we allow
2392 : * pg_largeobject to be truncated as part of pg_upgrade, because we need
2393 : * to change its relfilenode to match the old cluster, and allowing a
2394 : * TRUNCATE command to be executed is the easiest way of doing that.
2395 : */
2396 5460 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2397 50 : && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2398 2 : ereport(ERROR,
2399 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2400 : errmsg("permission denied: \"%s\" is a system catalog",
2401 : relname)));
2402 :
2403 5458 : InvokeObjectTruncateHook(relid);
2404 5458 : }
2405 :
2406 : /*
2407 : * Check that current user has the permission to truncate given relation.
2408 : */
2409 : static void
2410 3662 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2411 : {
2412 3662 : char *relname = NameStr(reltuple->relname);
2413 : AclResult aclresult;
2414 :
2415 : /* Permissions checks */
2416 3662 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2417 3662 : if (aclresult != ACLCHECK_OK)
2418 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2419 : relname);
2420 3630 : }
2421 :
2422 : /*
2423 : * Set of extra sanity checks to check if a given relation is safe to
2424 : * truncate. This is split with truncate_check_rel() as
2425 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2426 : */
2427 : static void
2428 5044 : truncate_check_activity(Relation rel)
2429 : {
2430 : /*
2431 : * Don't allow truncate on temp tables of other backends ... their local
2432 : * buffer manager is not going to cope.
2433 : */
2434 5044 : if (RELATION_IS_OTHER_TEMP(rel))
2435 0 : ereport(ERROR,
2436 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2437 : errmsg("cannot truncate temporary tables of other sessions")));
2438 :
2439 : /*
2440 : * Also check for active uses of the relation in the current transaction,
2441 : * including open scans and pending AFTER trigger events.
2442 : */
2443 5044 : CheckTableNotInUse(rel, "TRUNCATE");
2444 5038 : }
2445 :
2446 : /*
2447 : * storage_name
2448 : * returns the name corresponding to a typstorage/attstorage enum value
2449 : */
2450 : static const char *
2451 24 : storage_name(char c)
2452 : {
2453 24 : switch (c)
2454 : {
2455 0 : case TYPSTORAGE_PLAIN:
2456 0 : return "PLAIN";
2457 0 : case TYPSTORAGE_EXTERNAL:
2458 0 : return "EXTERNAL";
2459 12 : case TYPSTORAGE_EXTENDED:
2460 12 : return "EXTENDED";
2461 12 : case TYPSTORAGE_MAIN:
2462 12 : return "MAIN";
2463 0 : default:
2464 0 : return "???";
2465 : }
2466 : }
2467 :
2468 : /*----------
2469 : * MergeAttributes
2470 : * Returns new schema given initial schema and superclasses.
2471 : *
2472 : * Input arguments:
2473 : * 'columns' is the column/attribute definition for the table. (It's a list
2474 : * of ColumnDef's.) It is destructively changed.
2475 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2476 : * 'relpersistence' is the persistence type of the table.
2477 : * 'is_partition' tells if the table is a partition.
2478 : *
2479 : * Output arguments:
2480 : * 'supconstr' receives a list of CookedConstraint representing
2481 : * CHECK constraints belonging to parent relations, updated as
2482 : * necessary to be valid for the child.
2483 : * 'supnotnulls' receives a list of CookedConstraint representing
2484 : * not-null constraints based on those from parent relations.
2485 : *
2486 : * Return value:
2487 : * Completed schema list.
2488 : *
2489 : * Notes:
2490 : * The order in which the attributes are inherited is very important.
2491 : * Intuitively, the inherited attributes should come first. If a table
2492 : * inherits from multiple parents, the order of those attributes are
2493 : * according to the order of the parents specified in CREATE TABLE.
2494 : *
2495 : * Here's an example:
2496 : *
2497 : * create table person (name text, age int4, location point);
2498 : * create table emp (salary int4, manager text) inherits(person);
2499 : * create table student (gpa float8) inherits (person);
2500 : * create table stud_emp (percent int4) inherits (emp, student);
2501 : *
2502 : * The order of the attributes of stud_emp is:
2503 : *
2504 : * person {1:name, 2:age, 3:location}
2505 : * / \
2506 : * {6:gpa} student emp {4:salary, 5:manager}
2507 : * \ /
2508 : * stud_emp {7:percent}
2509 : *
2510 : * If the same attribute name appears multiple times, then it appears
2511 : * in the result table in the proper location for its first appearance.
2512 : *
2513 : * Constraints (including not-null constraints) for the child table
2514 : * are the union of all relevant constraints, from both the child schema
2515 : * and parent tables. In addition, in legacy inheritance, each column that
2516 : * appears in a primary key in any of the parents also gets a NOT NULL
2517 : * constraint (partitioning doesn't need this, because the PK itself gets
2518 : * inherited.)
2519 : *
2520 : * The default value for a child column is defined as:
2521 : * (1) If the child schema specifies a default, that value is used.
2522 : * (2) If neither the child nor any parent specifies a default, then
2523 : * the column will not have a default.
2524 : * (3) If conflicting defaults are inherited from different parents
2525 : * (and not overridden by the child), an error is raised.
2526 : * (4) Otherwise the inherited default is used.
2527 : *
2528 : * Note that the default-value infrastructure is used for generated
2529 : * columns' expressions too, so most of the preceding paragraph applies
2530 : * to generation expressions too. We insist that a child column be
2531 : * generated if and only if its parent(s) are, but it need not have
2532 : * the same generation expression.
2533 : *----------
2534 : */
2535 : static List *
2536 63042 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2537 : bool is_partition, List **supconstr, List **supnotnulls)
2538 : {
2539 63042 : List *inh_columns = NIL;
2540 63042 : List *constraints = NIL;
2541 63042 : List *nnconstraints = NIL;
2542 63042 : bool have_bogus_defaults = false;
2543 : int child_attno;
2544 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2545 63042 : List *saved_columns = NIL;
2546 : ListCell *lc;
2547 :
2548 : /*
2549 : * Check for and reject tables with too many columns. We perform this
2550 : * check relatively early for two reasons: (a) we don't run the risk of
2551 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2552 : * okay if we're processing <= 1600 columns, but could take minutes to
2553 : * execute if the user attempts to create a table with hundreds of
2554 : * thousands of columns.
2555 : *
2556 : * Note that we also need to check that we do not exceed this figure after
2557 : * including columns from inherited relations.
2558 : */
2559 63042 : if (list_length(columns) > MaxHeapAttributeNumber)
2560 0 : ereport(ERROR,
2561 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2562 : errmsg("tables can have at most %d columns",
2563 : MaxHeapAttributeNumber)));
2564 :
2565 : /*
2566 : * Check for duplicate names in the explicit list of attributes.
2567 : *
2568 : * Although we might consider merging such entries in the same way that we
2569 : * handle name conflicts for inherited attributes, it seems to make more
2570 : * sense to assume such conflicts are errors.
2571 : *
2572 : * We don't use foreach() here because we have two nested loops over the
2573 : * columns list, with possible element deletions in the inner one. If we
2574 : * used foreach_delete_current() it could only fix up the state of one of
2575 : * the loops, so it seems cleaner to use looping over list indexes for
2576 : * both loops. Note that any deletion will happen beyond where the outer
2577 : * loop is, so its index never needs adjustment.
2578 : */
2579 298120 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2580 : {
2581 235102 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2582 :
2583 235102 : if (!is_partition && coldef->typeName == NULL)
2584 : {
2585 : /*
2586 : * Typed table column option that does not belong to a column from
2587 : * the type. This works because the columns from the type come
2588 : * first in the list. (We omit this check for partition column
2589 : * lists; those are processed separately below.)
2590 : */
2591 6 : ereport(ERROR,
2592 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2593 : errmsg("column \"%s\" does not exist",
2594 : coldef->colname)));
2595 : }
2596 :
2597 : /* restpos scans all entries beyond coldef; incr is in loop body */
2598 7743890 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2599 : {
2600 7508812 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2601 :
2602 7508812 : if (strcmp(coldef->colname, restdef->colname) == 0)
2603 : {
2604 58 : if (coldef->is_from_type)
2605 : {
2606 : /*
2607 : * merge the column options into the column from the type
2608 : */
2609 40 : coldef->is_not_null = restdef->is_not_null;
2610 40 : coldef->raw_default = restdef->raw_default;
2611 40 : coldef->cooked_default = restdef->cooked_default;
2612 40 : coldef->constraints = restdef->constraints;
2613 40 : coldef->is_from_type = false;
2614 40 : columns = list_delete_nth_cell(columns, restpos);
2615 : }
2616 : else
2617 18 : ereport(ERROR,
2618 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2619 : errmsg("column \"%s\" specified more than once",
2620 : coldef->colname)));
2621 : }
2622 : else
2623 7508754 : restpos++;
2624 : }
2625 : }
2626 :
2627 : /*
2628 : * In case of a partition, there are no new column definitions, only dummy
2629 : * ColumnDefs created for column constraints. Set them aside for now and
2630 : * process them at the end.
2631 : */
2632 63018 : if (is_partition)
2633 : {
2634 7910 : saved_columns = columns;
2635 7910 : columns = NIL;
2636 : }
2637 :
2638 : /*
2639 : * Scan the parents left-to-right, and merge their attributes to form a
2640 : * list of inherited columns (inh_columns).
2641 : */
2642 63018 : child_attno = 0;
2643 73388 : foreach(lc, supers)
2644 : {
2645 10454 : Oid parent = lfirst_oid(lc);
2646 : Relation relation;
2647 : TupleDesc tupleDesc;
2648 : TupleConstr *constr;
2649 : AttrMap *newattmap;
2650 : List *inherited_defaults;
2651 : List *cols_with_defaults;
2652 : List *nnconstrs;
2653 : ListCell *lc1;
2654 : ListCell *lc2;
2655 10454 : Bitmapset *nncols = NULL;
2656 :
2657 : /* caller already got lock */
2658 10454 : relation = table_open(parent, NoLock);
2659 :
2660 : /*
2661 : * Check for active uses of the parent partitioned table in the
2662 : * current transaction, such as being used in some manner by an
2663 : * enclosing command.
2664 : */
2665 10454 : if (is_partition)
2666 7910 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2667 :
2668 : /*
2669 : * We do not allow partitioned tables and partitions to participate in
2670 : * regular inheritance.
2671 : */
2672 10448 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2673 6 : ereport(ERROR,
2674 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2675 : errmsg("cannot inherit from partitioned table \"%s\"",
2676 : RelationGetRelationName(relation))));
2677 10442 : if (relation->rd_rel->relispartition && !is_partition)
2678 6 : ereport(ERROR,
2679 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2680 : errmsg("cannot inherit from partition \"%s\"",
2681 : RelationGetRelationName(relation))));
2682 :
2683 10436 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2684 7906 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2685 7886 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2686 0 : ereport(ERROR,
2687 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2688 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2689 : RelationGetRelationName(relation))));
2690 :
2691 : /*
2692 : * If the parent is permanent, so must be all of its partitions. Note
2693 : * that inheritance allows that case.
2694 : */
2695 10436 : if (is_partition &&
2696 7904 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2697 : relpersistence == RELPERSISTENCE_TEMP)
2698 6 : ereport(ERROR,
2699 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2700 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2701 : RelationGetRelationName(relation))));
2702 :
2703 : /* Permanent rels cannot inherit from temporary ones */
2704 10430 : if (relpersistence != RELPERSISTENCE_TEMP &&
2705 10076 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2706 24 : ereport(ERROR,
2707 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2708 : errmsg(!is_partition
2709 : ? "cannot inherit from temporary relation \"%s\""
2710 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2711 : RelationGetRelationName(relation))));
2712 :
2713 : /* If existing rel is temp, it must belong to this session */
2714 10406 : if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2715 300 : !relation->rd_islocaltemp)
2716 0 : ereport(ERROR,
2717 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2718 : errmsg(!is_partition
2719 : ? "cannot inherit from temporary relation of another session"
2720 : : "cannot create as partition of temporary relation of another session")));
2721 :
2722 : /*
2723 : * We should have an UNDER permission flag for this, but for now,
2724 : * demand that creator of a child table own the parent.
2725 : */
2726 10406 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2727 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2728 0 : RelationGetRelationName(relation));
2729 :
2730 10406 : tupleDesc = RelationGetDescr(relation);
2731 10406 : constr = tupleDesc->constr;
2732 :
2733 : /*
2734 : * newattmap->attnums[] will contain the child-table attribute numbers
2735 : * for the attributes of this parent table. (They are not the same
2736 : * for parents after the first one, nor if we have dropped columns.)
2737 : */
2738 10406 : newattmap = make_attrmap(tupleDesc->natts);
2739 :
2740 : /* We can't process inherited defaults until newattmap is complete. */
2741 10406 : inherited_defaults = cols_with_defaults = NIL;
2742 :
2743 : /*
2744 : * Request attnotnull on columns that have a not-null constraint
2745 : * that's not marked NO INHERIT (even if not valid).
2746 : */
2747 10406 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2748 : true, false);
2749 23184 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2750 2372 : nncols = bms_add_member(nncols, cc->attnum);
2751 :
2752 31296 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2753 20890 : parent_attno++)
2754 : {
2755 20926 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2756 : parent_attno - 1);
2757 20926 : char *attributeName = NameStr(attribute->attname);
2758 : int exist_attno;
2759 : ColumnDef *newdef;
2760 : ColumnDef *mergeddef;
2761 :
2762 : /*
2763 : * Ignore dropped columns in the parent.
2764 : */
2765 20926 : if (attribute->attisdropped)
2766 192 : continue; /* leave newattmap->attnums entry as zero */
2767 :
2768 : /*
2769 : * Create new column definition
2770 : */
2771 20734 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2772 : attribute->atttypmod, attribute->attcollation);
2773 20734 : newdef->storage = attribute->attstorage;
2774 20734 : newdef->generated = attribute->attgenerated;
2775 20734 : if (CompressionMethodIsValid(attribute->attcompression))
2776 32 : newdef->compression =
2777 32 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2778 :
2779 : /*
2780 : * Regular inheritance children are independent enough not to
2781 : * inherit identity columns. But partitions are integral part of
2782 : * a partitioned table and inherit identity column.
2783 : */
2784 20734 : if (is_partition)
2785 16030 : newdef->identity = attribute->attidentity;
2786 :
2787 : /*
2788 : * Does it match some previously considered column from another
2789 : * parent?
2790 : */
2791 20734 : exist_attno = findAttrByName(attributeName, inh_columns);
2792 20734 : if (exist_attno > 0)
2793 : {
2794 : /*
2795 : * Yes, try to merge the two column definitions.
2796 : */
2797 380 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2798 :
2799 344 : newattmap->attnums[parent_attno - 1] = exist_attno;
2800 :
2801 : /*
2802 : * Partitions have only one parent, so conflict should never
2803 : * occur.
2804 : */
2805 : Assert(!is_partition);
2806 : }
2807 : else
2808 : {
2809 : /*
2810 : * No, create a new inherited column
2811 : */
2812 20354 : newdef->inhcount = 1;
2813 20354 : newdef->is_local = false;
2814 20354 : inh_columns = lappend(inh_columns, newdef);
2815 :
2816 20354 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2817 20354 : mergeddef = newdef;
2818 : }
2819 :
2820 : /*
2821 : * mark attnotnull if parent has it
2822 : */
2823 20698 : if (bms_is_member(parent_attno, nncols))
2824 2372 : mergeddef->is_not_null = true;
2825 :
2826 : /*
2827 : * Locate default/generation expression if any
2828 : */
2829 20698 : if (attribute->atthasdef)
2830 : {
2831 : Node *this_default;
2832 :
2833 718 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2834 718 : if (this_default == NULL)
2835 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2836 : parent_attno, RelationGetRelationName(relation));
2837 :
2838 : /*
2839 : * If it's a GENERATED default, it might contain Vars that
2840 : * need to be mapped to the inherited column(s)' new numbers.
2841 : * We can't do that till newattmap is ready, so just remember
2842 : * all the inherited default expressions for the moment.
2843 : */
2844 718 : inherited_defaults = lappend(inherited_defaults, this_default);
2845 718 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2846 : }
2847 : }
2848 :
2849 : /*
2850 : * Now process any inherited default expressions, adjusting attnos
2851 : * using the completed newattmap map.
2852 : */
2853 11088 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2854 : {
2855 718 : Node *this_default = (Node *) lfirst(lc1);
2856 718 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2857 : bool found_whole_row;
2858 :
2859 : /* Adjust Vars to match new table's column numbering */
2860 718 : this_default = map_variable_attnos(this_default,
2861 : 1, 0,
2862 : newattmap,
2863 : InvalidOid, &found_whole_row);
2864 :
2865 : /*
2866 : * For the moment we have to reject whole-row variables. We could
2867 : * convert them, if we knew the new table's rowtype OID, but that
2868 : * hasn't been assigned yet. (A variable could only appear in a
2869 : * generation expression, so the error message is correct.)
2870 : */
2871 718 : if (found_whole_row)
2872 0 : ereport(ERROR,
2873 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2874 : errmsg("cannot convert whole-row table reference"),
2875 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2876 : def->colname,
2877 : RelationGetRelationName(relation))));
2878 :
2879 : /*
2880 : * If we already had a default from some prior parent, check to
2881 : * see if they are the same. If so, no problem; if not, mark the
2882 : * column as having a bogus default. Below, we will complain if
2883 : * the bogus default isn't overridden by the child columns.
2884 : */
2885 : Assert(def->raw_default == NULL);
2886 718 : if (def->cooked_default == NULL)
2887 672 : def->cooked_default = this_default;
2888 46 : else if (!equal(def->cooked_default, this_default))
2889 : {
2890 40 : def->cooked_default = &bogus_marker;
2891 40 : have_bogus_defaults = true;
2892 : }
2893 : }
2894 :
2895 : /*
2896 : * Now copy the CHECK constraints of this parent, adjusting attnos
2897 : * using the completed newattmap map. Identically named constraints
2898 : * are merged if possible, else we throw error.
2899 : */
2900 10370 : if (constr && constr->num_check > 0)
2901 : {
2902 342 : ConstrCheck *check = constr->check;
2903 :
2904 1060 : for (int i = 0; i < constr->num_check; i++)
2905 : {
2906 718 : char *name = check[i].ccname;
2907 : Node *expr;
2908 : bool found_whole_row;
2909 :
2910 : /* ignore if the constraint is non-inheritable */
2911 718 : if (check[i].ccnoinherit)
2912 56 : continue;
2913 :
2914 : /* Adjust Vars to match new table's column numbering */
2915 662 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2916 : 1, 0,
2917 : newattmap,
2918 : InvalidOid, &found_whole_row);
2919 :
2920 : /*
2921 : * For the moment we have to reject whole-row variables. We
2922 : * could convert them, if we knew the new table's rowtype OID,
2923 : * but that hasn't been assigned yet.
2924 : */
2925 662 : if (found_whole_row)
2926 0 : ereport(ERROR,
2927 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2928 : errmsg("cannot convert whole-row table reference"),
2929 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2930 : name,
2931 : RelationGetRelationName(relation))));
2932 :
2933 662 : constraints = MergeCheckConstraint(constraints, name, expr,
2934 662 : check[i].ccenforced);
2935 : }
2936 : }
2937 :
2938 : /*
2939 : * Also copy the not-null constraints from this parent. The
2940 : * attnotnull markings were already installed above.
2941 : */
2942 23112 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2943 : {
2944 : Assert(nn->contype == CONSTR_NOTNULL);
2945 :
2946 2372 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2947 :
2948 2372 : nnconstraints = lappend(nnconstraints, nn);
2949 : }
2950 :
2951 10370 : free_attrmap(newattmap);
2952 :
2953 : /*
2954 : * Close the parent rel, but keep our lock on it until xact commit.
2955 : * That will prevent someone else from deleting or ALTERing the parent
2956 : * before the child is committed.
2957 : */
2958 10370 : table_close(relation, NoLock);
2959 : }
2960 :
2961 : /*
2962 : * If we had no inherited attributes, the result columns are just the
2963 : * explicitly declared columns. Otherwise, we need to merge the declared
2964 : * columns into the inherited column list. Although, we never have any
2965 : * explicitly declared columns if the table is a partition.
2966 : */
2967 62934 : if (inh_columns != NIL)
2968 : {
2969 9932 : int newcol_attno = 0;
2970 :
2971 10998 : foreach(lc, columns)
2972 : {
2973 1144 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2974 1144 : char *attributeName = newdef->colname;
2975 : int exist_attno;
2976 :
2977 : /*
2978 : * Partitions have only one parent and have no column definitions
2979 : * of their own, so conflict should never occur.
2980 : */
2981 : Assert(!is_partition);
2982 :
2983 1144 : newcol_attno++;
2984 :
2985 : /*
2986 : * Does it match some inherited column?
2987 : */
2988 1144 : exist_attno = findAttrByName(attributeName, inh_columns);
2989 1144 : if (exist_attno > 0)
2990 : {
2991 : /*
2992 : * Yes, try to merge the two column definitions.
2993 : */
2994 418 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2995 : }
2996 : else
2997 : {
2998 : /*
2999 : * No, attach new column unchanged to result columns.
3000 : */
3001 726 : inh_columns = lappend(inh_columns, newdef);
3002 : }
3003 : }
3004 :
3005 9854 : columns = inh_columns;
3006 :
3007 : /*
3008 : * Check that we haven't exceeded the legal # of columns after merging
3009 : * in inherited columns.
3010 : */
3011 9854 : if (list_length(columns) > MaxHeapAttributeNumber)
3012 0 : ereport(ERROR,
3013 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
3014 : errmsg("tables can have at most %d columns",
3015 : MaxHeapAttributeNumber)));
3016 : }
3017 :
3018 : /*
3019 : * Now that we have the column definition list for a partition, we can
3020 : * check whether the columns referenced in the column constraint specs
3021 : * actually exist. Also, merge column defaults.
3022 : */
3023 62856 : if (is_partition)
3024 : {
3025 8086 : foreach(lc, saved_columns)
3026 : {
3027 242 : ColumnDef *restdef = lfirst(lc);
3028 242 : bool found = false;
3029 : ListCell *l;
3030 :
3031 900 : foreach(l, columns)
3032 : {
3033 694 : ColumnDef *coldef = lfirst(l);
3034 :
3035 694 : if (strcmp(coldef->colname, restdef->colname) == 0)
3036 : {
3037 242 : found = true;
3038 :
3039 : /*
3040 : * Check for conflicts related to generated columns.
3041 : *
3042 : * Same rules as above: generated-ness has to match the
3043 : * parent, but the contents of the generation expression
3044 : * can be different.
3045 : */
3046 242 : if (coldef->generated)
3047 : {
3048 134 : if (restdef->raw_default && !restdef->generated)
3049 12 : ereport(ERROR,
3050 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3051 : errmsg("column \"%s\" inherits from generated column but specifies default",
3052 : restdef->colname)));
3053 122 : if (restdef->identity)
3054 0 : ereport(ERROR,
3055 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3056 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3057 : restdef->colname)));
3058 : }
3059 : else
3060 : {
3061 108 : if (restdef->generated)
3062 12 : ereport(ERROR,
3063 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3064 : errmsg("child column \"%s\" specifies generation expression",
3065 : restdef->colname),
3066 : errhint("A child table column cannot be generated unless its parent column is.")));
3067 : }
3068 :
3069 218 : if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3070 12 : ereport(ERROR,
3071 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3072 : errmsg("column \"%s\" inherits from generated column of different kind",
3073 : restdef->colname),
3074 : errdetail("Parent column is %s, child column is %s.",
3075 : coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3076 : restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3077 :
3078 : /*
3079 : * Override the parent's default value for this column
3080 : * (coldef->cooked_default) with the partition's local
3081 : * definition (restdef->raw_default), if there's one. It
3082 : * should be physically impossible to get a cooked default
3083 : * in the local definition or a raw default in the
3084 : * inherited definition, but make sure they're nulls, for
3085 : * future-proofing.
3086 : */
3087 : Assert(restdef->cooked_default == NULL);
3088 : Assert(coldef->raw_default == NULL);
3089 206 : if (restdef->raw_default)
3090 : {
3091 134 : coldef->raw_default = restdef->raw_default;
3092 134 : coldef->cooked_default = NULL;
3093 : }
3094 : }
3095 : }
3096 :
3097 : /* complain for constraints on columns not in parent */
3098 206 : if (!found)
3099 0 : ereport(ERROR,
3100 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3101 : errmsg("column \"%s\" does not exist",
3102 : restdef->colname)));
3103 : }
3104 : }
3105 :
3106 : /*
3107 : * If we found any conflicting parent default values, check to make sure
3108 : * they were overridden by the child.
3109 : */
3110 62820 : if (have_bogus_defaults)
3111 : {
3112 106 : foreach(lc, columns)
3113 : {
3114 84 : ColumnDef *def = lfirst(lc);
3115 :
3116 84 : if (def->cooked_default == &bogus_marker)
3117 : {
3118 18 : if (def->generated)
3119 12 : ereport(ERROR,
3120 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3121 : errmsg("column \"%s\" inherits conflicting generation expressions",
3122 : def->colname),
3123 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3124 : else
3125 6 : ereport(ERROR,
3126 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3127 : errmsg("column \"%s\" inherits conflicting default values",
3128 : def->colname),
3129 : errhint("To resolve the conflict, specify a default explicitly.")));
3130 : }
3131 : }
3132 : }
3133 :
3134 62802 : *supconstr = constraints;
3135 62802 : *supnotnulls = nnconstraints;
3136 :
3137 62802 : return columns;
3138 : }
3139 :
3140 :
3141 : /*
3142 : * MergeCheckConstraint
3143 : * Try to merge an inherited CHECK constraint with previous ones
3144 : *
3145 : * If we inherit identically-named constraints from multiple parents, we must
3146 : * merge them, or throw an error if they don't have identical definitions.
3147 : *
3148 : * constraints is a list of CookedConstraint structs for previous constraints.
3149 : *
3150 : * If the new constraint matches an existing one, then the existing
3151 : * constraint's inheritance count is updated. If there is a conflict (same
3152 : * name but different expression), throw an error. If the constraint neither
3153 : * matches nor conflicts with an existing one, a new constraint is appended to
3154 : * the list.
3155 : */
3156 : static List *
3157 662 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3158 : {
3159 : ListCell *lc;
3160 : CookedConstraint *newcon;
3161 :
3162 2116 : foreach(lc, constraints)
3163 : {
3164 1604 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3165 :
3166 : Assert(ccon->contype == CONSTR_CHECK);
3167 :
3168 : /* Non-matching names never conflict */
3169 1604 : if (strcmp(ccon->name, name) != 0)
3170 1454 : continue;
3171 :
3172 150 : if (equal(expr, ccon->expr))
3173 : {
3174 : /* OK to merge constraint with existing */
3175 150 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3176 : &ccon->inhcount))
3177 0 : ereport(ERROR,
3178 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3179 : errmsg("too many inheritance parents"));
3180 :
3181 : /*
3182 : * When enforceability differs, the merged constraint should be
3183 : * marked as ENFORCED because one of the parents is ENFORCED.
3184 : */
3185 150 : if (!ccon->is_enforced && is_enforced)
3186 : {
3187 48 : ccon->is_enforced = true;
3188 48 : ccon->skip_validation = false;
3189 : }
3190 :
3191 150 : return constraints;
3192 : }
3193 :
3194 0 : ereport(ERROR,
3195 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3196 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3197 : name)));
3198 : }
3199 :
3200 : /*
3201 : * Constraint couldn't be merged with an existing one and also didn't
3202 : * conflict with an existing one, so add it as a new one to the list.
3203 : */
3204 512 : newcon = palloc0_object(CookedConstraint);
3205 512 : newcon->contype = CONSTR_CHECK;
3206 512 : newcon->name = pstrdup(name);
3207 512 : newcon->expr = expr;
3208 512 : newcon->inhcount = 1;
3209 512 : newcon->is_enforced = is_enforced;
3210 512 : newcon->skip_validation = !is_enforced;
3211 512 : return lappend(constraints, newcon);
3212 : }
3213 :
3214 : /*
3215 : * MergeChildAttribute
3216 : * Merge given child attribute definition into given inherited attribute.
3217 : *
3218 : * Input arguments:
3219 : * 'inh_columns' is the list of inherited ColumnDefs.
3220 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3221 : * 'newcol_attno' is the attribute number in child table's schema definition
3222 : * 'newdef' is the column/attribute definition from the child table.
3223 : *
3224 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3225 : * ColumnDef remains unchanged.
3226 : *
3227 : * Notes:
3228 : * - The attribute is merged according to the rules laid out in the prologue
3229 : * of MergeAttributes().
3230 : * - If matching inherited attribute exists but the child attribute can not be
3231 : * merged into it, the function throws respective errors.
3232 : * - A partition can not have its own column definitions. Hence this function
3233 : * is applicable only to a regular inheritance child.
3234 : */
3235 : static void
3236 418 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3237 : {
3238 418 : char *attributeName = newdef->colname;
3239 : ColumnDef *inhdef;
3240 : Oid inhtypeid,
3241 : newtypeid;
3242 : int32 inhtypmod,
3243 : newtypmod;
3244 : Oid inhcollid,
3245 : newcollid;
3246 :
3247 418 : if (exist_attno == newcol_attno)
3248 356 : ereport(NOTICE,
3249 : (errmsg("merging column \"%s\" with inherited definition",
3250 : attributeName)));
3251 : else
3252 62 : ereport(NOTICE,
3253 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3254 : errdetail("User-specified column moved to the position of the inherited column.")));
3255 :
3256 418 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3257 :
3258 : /*
3259 : * Must have the same type and typmod
3260 : */
3261 418 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3262 418 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3263 418 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3264 12 : ereport(ERROR,
3265 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3266 : errmsg("column \"%s\" has a type conflict",
3267 : attributeName),
3268 : errdetail("%s versus %s",
3269 : format_type_with_typemod(inhtypeid, inhtypmod),
3270 : format_type_with_typemod(newtypeid, newtypmod))));
3271 :
3272 : /*
3273 : * Must have the same collation
3274 : */
3275 406 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3276 406 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3277 406 : if (inhcollid != newcollid)
3278 6 : ereport(ERROR,
3279 : (errcode(ERRCODE_COLLATION_MISMATCH),
3280 : errmsg("column \"%s\" has a collation conflict",
3281 : attributeName),
3282 : errdetail("\"%s\" versus \"%s\"",
3283 : get_collation_name(inhcollid),
3284 : get_collation_name(newcollid))));
3285 :
3286 : /*
3287 : * Identity is never inherited by a regular inheritance child. Pick
3288 : * child's identity definition if there's one.
3289 : */
3290 400 : inhdef->identity = newdef->identity;
3291 :
3292 : /*
3293 : * Copy storage parameter
3294 : */
3295 400 : if (inhdef->storage == 0)
3296 0 : inhdef->storage = newdef->storage;
3297 400 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3298 6 : ereport(ERROR,
3299 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3300 : errmsg("column \"%s\" has a storage parameter conflict",
3301 : attributeName),
3302 : errdetail("%s versus %s",
3303 : storage_name(inhdef->storage),
3304 : storage_name(newdef->storage))));
3305 :
3306 : /*
3307 : * Copy compression parameter
3308 : */
3309 394 : if (inhdef->compression == NULL)
3310 388 : inhdef->compression = newdef->compression;
3311 6 : else if (newdef->compression != NULL)
3312 : {
3313 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3314 6 : ereport(ERROR,
3315 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3316 : errmsg("column \"%s\" has a compression method conflict",
3317 : attributeName),
3318 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3319 : }
3320 :
3321 : /*
3322 : * Merge of not-null constraints = OR 'em together
3323 : */
3324 388 : inhdef->is_not_null |= newdef->is_not_null;
3325 :
3326 : /*
3327 : * Check for conflicts related to generated columns.
3328 : *
3329 : * If the parent column is generated, the child column will be made a
3330 : * generated column if it isn't already. If it is a generated column,
3331 : * we'll take its generation expression in preference to the parent's. We
3332 : * must check that the child column doesn't specify a default value or
3333 : * identity, which matches the rules for a single column in
3334 : * parse_utilcmd.c.
3335 : *
3336 : * Conversely, if the parent column is not generated, the child column
3337 : * can't be either. (We used to allow that, but it results in being able
3338 : * to override the generation expression via UPDATEs through the parent.)
3339 : */
3340 388 : if (inhdef->generated)
3341 : {
3342 78 : if (newdef->raw_default && !newdef->generated)
3343 12 : ereport(ERROR,
3344 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3345 : errmsg("column \"%s\" inherits from generated column but specifies default",
3346 : inhdef->colname)));
3347 66 : if (newdef->identity)
3348 12 : ereport(ERROR,
3349 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3350 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3351 : inhdef->colname)));
3352 : }
3353 : else
3354 : {
3355 310 : if (newdef->generated)
3356 12 : ereport(ERROR,
3357 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3358 : errmsg("child column \"%s\" specifies generation expression",
3359 : inhdef->colname),
3360 : errhint("A child table column cannot be generated unless its parent column is.")));
3361 : }
3362 :
3363 352 : if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3364 12 : ereport(ERROR,
3365 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3366 : errmsg("column \"%s\" inherits from generated column of different kind",
3367 : inhdef->colname),
3368 : errdetail("Parent column is %s, child column is %s.",
3369 : inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3370 : newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3371 :
3372 : /*
3373 : * If new def has a default, override previous default
3374 : */
3375 340 : if (newdef->raw_default != NULL)
3376 : {
3377 38 : inhdef->raw_default = newdef->raw_default;
3378 38 : inhdef->cooked_default = newdef->cooked_default;
3379 : }
3380 :
3381 : /* Mark the column as locally defined */
3382 340 : inhdef->is_local = true;
3383 340 : }
3384 :
3385 : /*
3386 : * MergeInheritedAttribute
3387 : * Merge given parent attribute definition into specified attribute
3388 : * inherited from the previous parents.
3389 : *
3390 : * Input arguments:
3391 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3392 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3393 : * 'newdef' is the new parent column/attribute definition to be merged.
3394 : *
3395 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3396 : *
3397 : * Notes:
3398 : * - The attribute is merged according to the rules laid out in the prologue
3399 : * of MergeAttributes().
3400 : * - If matching inherited attribute exists but the new attribute can not be
3401 : * merged into it, the function throws respective errors.
3402 : * - A partition inherits from only a single parent. Hence this function is
3403 : * applicable only to a regular inheritance.
3404 : */
3405 : static ColumnDef *
3406 380 : MergeInheritedAttribute(List *inh_columns,
3407 : int exist_attno,
3408 : const ColumnDef *newdef)
3409 : {
3410 380 : char *attributeName = newdef->colname;
3411 : ColumnDef *prevdef;
3412 : Oid prevtypeid,
3413 : newtypeid;
3414 : int32 prevtypmod,
3415 : newtypmod;
3416 : Oid prevcollid,
3417 : newcollid;
3418 :
3419 380 : ereport(NOTICE,
3420 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3421 : attributeName)));
3422 380 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3423 :
3424 : /*
3425 : * Must have the same type and typmod
3426 : */
3427 380 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3428 380 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3429 380 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3430 0 : ereport(ERROR,
3431 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3432 : errmsg("inherited column \"%s\" has a type conflict",
3433 : attributeName),
3434 : errdetail("%s versus %s",
3435 : format_type_with_typemod(prevtypeid, prevtypmod),
3436 : format_type_with_typemod(newtypeid, newtypmod))));
3437 :
3438 : /*
3439 : * Must have the same collation
3440 : */
3441 380 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3442 380 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3443 380 : if (prevcollid != newcollid)
3444 0 : ereport(ERROR,
3445 : (errcode(ERRCODE_COLLATION_MISMATCH),
3446 : errmsg("inherited column \"%s\" has a collation conflict",
3447 : attributeName),
3448 : errdetail("\"%s\" versus \"%s\"",
3449 : get_collation_name(prevcollid),
3450 : get_collation_name(newcollid))));
3451 :
3452 : /*
3453 : * Copy/check storage parameter
3454 : */
3455 380 : if (prevdef->storage == 0)
3456 0 : prevdef->storage = newdef->storage;
3457 380 : else if (prevdef->storage != newdef->storage)
3458 6 : ereport(ERROR,
3459 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3460 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3461 : attributeName),
3462 : errdetail("%s versus %s",
3463 : storage_name(prevdef->storage),
3464 : storage_name(newdef->storage))));
3465 :
3466 : /*
3467 : * Copy/check compression parameter
3468 : */
3469 374 : if (prevdef->compression == NULL)
3470 360 : prevdef->compression = newdef->compression;
3471 14 : else if (newdef->compression != NULL)
3472 : {
3473 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3474 6 : ereport(ERROR,
3475 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3476 : errmsg("column \"%s\" has a compression method conflict",
3477 : attributeName),
3478 : errdetail("%s versus %s",
3479 : prevdef->compression, newdef->compression)));
3480 : }
3481 :
3482 : /*
3483 : * Check for GENERATED conflicts
3484 : */
3485 368 : if (prevdef->generated != newdef->generated)
3486 24 : ereport(ERROR,
3487 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3488 : errmsg("inherited column \"%s\" has a generation conflict",
3489 : attributeName)));
3490 :
3491 : /*
3492 : * Default and other constraints are handled by the caller.
3493 : */
3494 :
3495 344 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3496 : &prevdef->inhcount))
3497 0 : ereport(ERROR,
3498 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3499 : errmsg("too many inheritance parents"));
3500 :
3501 344 : return prevdef;
3502 : }
3503 :
3504 : /*
3505 : * StoreCatalogInheritance
3506 : * Updates the system catalogs with proper inheritance information.
3507 : *
3508 : * supers is a list of the OIDs of the new relation's direct ancestors.
3509 : */
3510 : static void
3511 62178 : StoreCatalogInheritance(Oid relationId, List *supers,
3512 : bool child_is_partition)
3513 : {
3514 : Relation relation;
3515 : int32 seqNumber;
3516 : ListCell *entry;
3517 :
3518 : /*
3519 : * sanity checks
3520 : */
3521 : Assert(OidIsValid(relationId));
3522 :
3523 62178 : if (supers == NIL)
3524 52684 : return;
3525 :
3526 : /*
3527 : * Store INHERITS information in pg_inherits using direct ancestors only.
3528 : * Also enter dependencies on the direct ancestors, and make sure they are
3529 : * marked with relhassubclass = true.
3530 : *
3531 : * (Once upon a time, both direct and indirect ancestors were found here
3532 : * and then entered into pg_ipl. Since that catalog doesn't exist
3533 : * anymore, there's no need to look for indirect ancestors.)
3534 : */
3535 9494 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3536 :
3537 9494 : seqNumber = 1;
3538 19324 : foreach(entry, supers)
3539 : {
3540 9830 : Oid parentOid = lfirst_oid(entry);
3541 :
3542 9830 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3543 : child_is_partition);
3544 9830 : seqNumber++;
3545 : }
3546 :
3547 9494 : table_close(relation, RowExclusiveLock);
3548 : }
3549 :
3550 : /*
3551 : * Make catalog entries showing relationId as being an inheritance child
3552 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3553 : */
3554 : static void
3555 12648 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3556 : int32 seqNumber, Relation inhRelation,
3557 : bool child_is_partition)
3558 : {
3559 : ObjectAddress childobject,
3560 : parentobject;
3561 :
3562 : /* store the pg_inherits row */
3563 12648 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3564 :
3565 : /*
3566 : * Store a dependency too
3567 : */
3568 12648 : parentobject.classId = RelationRelationId;
3569 12648 : parentobject.objectId = parentOid;
3570 12648 : parentobject.objectSubId = 0;
3571 12648 : childobject.classId = RelationRelationId;
3572 12648 : childobject.objectId = relationId;
3573 12648 : childobject.objectSubId = 0;
3574 :
3575 12648 : recordDependencyOn(&childobject, &parentobject,
3576 : child_dependency_type(child_is_partition));
3577 :
3578 : /*
3579 : * Post creation hook of this inheritance. Since object_access_hook
3580 : * doesn't take multiple object identifiers, we relay oid of parent
3581 : * relation using auxiliary_id argument.
3582 : */
3583 12648 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3584 : relationId, 0,
3585 : parentOid, false);
3586 :
3587 : /*
3588 : * Mark the parent as having subclasses.
3589 : */
3590 12648 : SetRelationHasSubclass(parentOid, true);
3591 12648 : }
3592 :
3593 : /*
3594 : * Look for an existing column entry with the given name.
3595 : *
3596 : * Returns the index (starting with 1) if attribute already exists in columns,
3597 : * 0 if it doesn't.
3598 : */
3599 : static int
3600 21878 : findAttrByName(const char *attributeName, const List *columns)
3601 : {
3602 : ListCell *lc;
3603 21878 : int i = 1;
3604 :
3605 39264 : foreach(lc, columns)
3606 : {
3607 18184 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3608 798 : return i;
3609 :
3610 17386 : i++;
3611 : }
3612 21080 : return 0;
3613 : }
3614 :
3615 :
3616 : /*
3617 : * SetRelationHasSubclass
3618 : * Set the value of the relation's relhassubclass field in pg_class.
3619 : *
3620 : * It's always safe to set this field to true, because all SQL commands are
3621 : * ready to see true and then find no children. On the other hand, commands
3622 : * generally assume zero children if this is false.
3623 : *
3624 : * Caller must hold any self-exclusive lock until end of transaction. If the
3625 : * new value is false, caller must have acquired that lock before reading the
3626 : * evidence that justified the false value. That way, it properly waits if
3627 : * another backend is simultaneously concluding no need to change the tuple
3628 : * (new and old values are true).
3629 : *
3630 : * NOTE: an important side-effect of this operation is that an SI invalidation
3631 : * message is sent out to all backends --- including me --- causing plans
3632 : * referencing the relation to be rebuilt with the new list of children.
3633 : * This must happen even if we find that no change is needed in the pg_class
3634 : * row.
3635 : */
3636 : void
3637 15758 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3638 : {
3639 : Relation relationRelation;
3640 : HeapTuple tuple;
3641 : Form_pg_class classtuple;
3642 :
3643 : Assert(CheckRelationOidLockedByMe(relationId,
3644 : ShareUpdateExclusiveLock, false) ||
3645 : CheckRelationOidLockedByMe(relationId,
3646 : ShareRowExclusiveLock, true));
3647 :
3648 : /*
3649 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3650 : */
3651 15758 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3652 15758 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3653 15758 : if (!HeapTupleIsValid(tuple))
3654 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3655 15758 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3656 :
3657 15758 : if (classtuple->relhassubclass != relhassubclass)
3658 : {
3659 7948 : classtuple->relhassubclass = relhassubclass;
3660 7948 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3661 : }
3662 : else
3663 : {
3664 : /* no need to change tuple, but force relcache rebuild anyway */
3665 7810 : CacheInvalidateRelcacheByTuple(tuple);
3666 : }
3667 :
3668 15758 : heap_freetuple(tuple);
3669 15758 : table_close(relationRelation, RowExclusiveLock);
3670 15758 : }
3671 :
3672 : /*
3673 : * CheckRelationTableSpaceMove
3674 : * Check if relation can be moved to new tablespace.
3675 : *
3676 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3677 : *
3678 : * Returns true if the relation can be moved to the new tablespace; raises
3679 : * an error if it is not possible to do the move; returns false if the move
3680 : * would have no effect.
3681 : */
3682 : bool
3683 228 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3684 : {
3685 : Oid oldTableSpaceId;
3686 :
3687 : /*
3688 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3689 : * stored as 0.
3690 : */
3691 228 : oldTableSpaceId = rel->rd_rel->reltablespace;
3692 228 : if (newTableSpaceId == oldTableSpaceId ||
3693 220 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3694 10 : return false;
3695 :
3696 : /*
3697 : * We cannot support moving mapped relations into different tablespaces.
3698 : * (In particular this eliminates all shared catalogs.)
3699 : */
3700 218 : if (RelationIsMapped(rel))
3701 0 : ereport(ERROR,
3702 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3703 : errmsg("cannot move system relation \"%s\"",
3704 : RelationGetRelationName(rel))));
3705 :
3706 : /* Cannot move a non-shared relation into pg_global */
3707 218 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3708 12 : ereport(ERROR,
3709 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3710 : errmsg("only shared relations can be placed in pg_global tablespace")));
3711 :
3712 : /*
3713 : * Do not allow moving temp tables of other backends ... their local
3714 : * buffer manager is not going to cope.
3715 : */
3716 206 : if (RELATION_IS_OTHER_TEMP(rel))
3717 0 : ereport(ERROR,
3718 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3719 : errmsg("cannot move temporary tables of other sessions")));
3720 :
3721 206 : return true;
3722 : }
3723 :
3724 : /*
3725 : * SetRelationTableSpace
3726 : * Set new reltablespace and relfilenumber in pg_class entry.
3727 : *
3728 : * newTableSpaceId is the new tablespace for the relation, and
3729 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3730 : * InvalidRelFileNumber, this field is not updated.
3731 : *
3732 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3733 : *
3734 : * The caller of this routine had better check if a relation can be
3735 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3736 : * first, and is responsible for making the change visible with
3737 : * CommandCounterIncrement().
3738 : */
3739 : void
3740 206 : SetRelationTableSpace(Relation rel,
3741 : Oid newTableSpaceId,
3742 : RelFileNumber newRelFilenumber)
3743 : {
3744 : Relation pg_class;
3745 : HeapTuple tuple;
3746 : ItemPointerData otid;
3747 : Form_pg_class rd_rel;
3748 206 : Oid reloid = RelationGetRelid(rel);
3749 :
3750 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3751 :
3752 : /* Get a modifiable copy of the relation's pg_class row. */
3753 206 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3754 :
3755 206 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3756 206 : if (!HeapTupleIsValid(tuple))
3757 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3758 206 : otid = tuple->t_self;
3759 206 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3760 :
3761 : /* Update the pg_class row. */
3762 412 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3763 206 : InvalidOid : newTableSpaceId;
3764 206 : if (RelFileNumberIsValid(newRelFilenumber))
3765 162 : rd_rel->relfilenode = newRelFilenumber;
3766 206 : CatalogTupleUpdate(pg_class, &otid, tuple);
3767 206 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3768 :
3769 : /*
3770 : * Record dependency on tablespace. This is only required for relations
3771 : * that have no physical storage.
3772 : */
3773 206 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3774 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3775 : rd_rel->reltablespace);
3776 :
3777 206 : heap_freetuple(tuple);
3778 206 : table_close(pg_class, RowExclusiveLock);
3779 206 : }
3780 :
3781 : /*
3782 : * renameatt_check - basic sanity checks before attribute rename
3783 : */
3784 : static void
3785 1028 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3786 : {
3787 1028 : char relkind = classform->relkind;
3788 :
3789 1028 : if (classform->reloftype && !recursing)
3790 6 : ereport(ERROR,
3791 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3792 : errmsg("cannot rename column of typed table")));
3793 :
3794 : /*
3795 : * Renaming the columns of sequences or toast tables doesn't actually
3796 : * break anything from the system's point of view, since internal
3797 : * references are by attnum. But it doesn't seem right to allow users to
3798 : * change names that are hardcoded into the system, hence the following
3799 : * restriction.
3800 : */
3801 1022 : if (relkind != RELKIND_RELATION &&
3802 86 : relkind != RELKIND_VIEW &&
3803 86 : relkind != RELKIND_MATVIEW &&
3804 38 : relkind != RELKIND_COMPOSITE_TYPE &&
3805 38 : relkind != RELKIND_INDEX &&
3806 38 : relkind != RELKIND_PARTITIONED_INDEX &&
3807 0 : relkind != RELKIND_FOREIGN_TABLE &&
3808 : relkind != RELKIND_PARTITIONED_TABLE)
3809 0 : ereport(ERROR,
3810 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3811 : errmsg("cannot rename columns of relation \"%s\"",
3812 : NameStr(classform->relname)),
3813 : errdetail_relkind_not_supported(relkind)));
3814 :
3815 : /*
3816 : * permissions checking. only the owner of a class can change its schema.
3817 : */
3818 1022 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3819 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3820 0 : NameStr(classform->relname));
3821 1022 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3822 2 : ereport(ERROR,
3823 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3824 : errmsg("permission denied: \"%s\" is a system catalog",
3825 : NameStr(classform->relname))));
3826 1020 : }
3827 :
3828 : /*
3829 : * renameatt_internal - workhorse for renameatt
3830 : *
3831 : * Return value is the attribute number in the 'myrelid' relation.
3832 : */
3833 : static AttrNumber
3834 552 : renameatt_internal(Oid myrelid,
3835 : const char *oldattname,
3836 : const char *newattname,
3837 : bool recurse,
3838 : bool recursing,
3839 : int expected_parents,
3840 : DropBehavior behavior)
3841 : {
3842 : Relation targetrelation;
3843 : Relation attrelation;
3844 : HeapTuple atttup;
3845 : Form_pg_attribute attform;
3846 : AttrNumber attnum;
3847 :
3848 : /*
3849 : * Grab an exclusive lock on the target table, which we will NOT release
3850 : * until end of transaction.
3851 : */
3852 552 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3853 552 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3854 :
3855 : /*
3856 : * if the 'recurse' flag is set then we are supposed to rename this
3857 : * attribute in all classes that inherit from 'relname' (as well as in
3858 : * 'relname').
3859 : *
3860 : * any permissions or problems with duplicate attributes will cause the
3861 : * whole transaction to abort, which is what we want -- all or nothing.
3862 : */
3863 552 : if (recurse)
3864 : {
3865 : List *child_oids,
3866 : *child_numparents;
3867 : ListCell *lo,
3868 : *li;
3869 :
3870 : /*
3871 : * we need the number of parents for each child so that the recursive
3872 : * calls to renameatt() can determine whether there are any parents
3873 : * outside the inheritance hierarchy being processed.
3874 : */
3875 248 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3876 : &child_numparents);
3877 :
3878 : /*
3879 : * find_all_inheritors does the recursive search of the inheritance
3880 : * hierarchy, so all we have to do is process all of the relids in the
3881 : * list that it returns.
3882 : */
3883 734 : forboth(lo, child_oids, li, child_numparents)
3884 : {
3885 516 : Oid childrelid = lfirst_oid(lo);
3886 516 : int numparents = lfirst_int(li);
3887 :
3888 516 : if (childrelid == myrelid)
3889 248 : continue;
3890 : /* note we need not recurse again */
3891 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3892 : }
3893 : }
3894 : else
3895 : {
3896 : /*
3897 : * If we are told not to recurse, there had better not be any child
3898 : * tables; else the rename would put them out of step.
3899 : *
3900 : * expected_parents will only be 0 if we are not already recursing.
3901 : */
3902 340 : if (expected_parents == 0 &&
3903 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3904 12 : ereport(ERROR,
3905 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3906 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3907 : oldattname)));
3908 : }
3909 :
3910 : /* rename attributes in typed tables of composite type */
3911 510 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3912 : {
3913 : List *child_oids;
3914 : ListCell *lo;
3915 :
3916 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3917 24 : RelationGetRelationName(targetrelation),
3918 : behavior);
3919 :
3920 24 : foreach(lo, child_oids)
3921 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3922 : }
3923 :
3924 504 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3925 :
3926 504 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3927 504 : if (!HeapTupleIsValid(atttup))
3928 24 : ereport(ERROR,
3929 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3930 : errmsg("column \"%s\" does not exist",
3931 : oldattname)));
3932 480 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3933 :
3934 480 : attnum = attform->attnum;
3935 480 : if (attnum <= 0)
3936 0 : ereport(ERROR,
3937 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3938 : errmsg("cannot rename system column \"%s\"",
3939 : oldattname)));
3940 :
3941 : /*
3942 : * if the attribute is inherited, forbid the renaming. if this is a
3943 : * top-level call to renameatt(), then expected_parents will be 0, so the
3944 : * effect of this code will be to prohibit the renaming if the attribute
3945 : * is inherited at all. if this is a recursive call to renameatt(),
3946 : * expected_parents will be the number of parents the current relation has
3947 : * within the inheritance hierarchy being processed, so we'll prohibit the
3948 : * renaming only if there are additional parents from elsewhere.
3949 : */
3950 480 : if (attform->attinhcount > expected_parents)
3951 30 : ereport(ERROR,
3952 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3953 : errmsg("cannot rename inherited column \"%s\"",
3954 : oldattname)));
3955 :
3956 : /* new name should not already exist */
3957 450 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3958 :
3959 : /* apply the update */
3960 438 : namestrcpy(&(attform->attname), newattname);
3961 :
3962 438 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3963 :
3964 438 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3965 :
3966 438 : heap_freetuple(atttup);
3967 :
3968 438 : table_close(attrelation, RowExclusiveLock);
3969 :
3970 438 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3971 :
3972 438 : return attnum;
3973 : }
3974 :
3975 : /*
3976 : * Perform permissions and integrity checks before acquiring a relation lock.
3977 : */
3978 : static void
3979 430 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3980 : void *arg)
3981 : {
3982 : HeapTuple tuple;
3983 : Form_pg_class form;
3984 :
3985 430 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3986 430 : if (!HeapTupleIsValid(tuple))
3987 38 : return; /* concurrently dropped */
3988 392 : form = (Form_pg_class) GETSTRUCT(tuple);
3989 392 : renameatt_check(relid, form, false);
3990 384 : ReleaseSysCache(tuple);
3991 : }
3992 :
3993 : /*
3994 : * renameatt - changes the name of an attribute in a relation
3995 : *
3996 : * The returned ObjectAddress is that of the renamed column.
3997 : */
3998 : ObjectAddress
3999 316 : renameatt(RenameStmt *stmt)
4000 : {
4001 : Oid relid;
4002 : AttrNumber attnum;
4003 : ObjectAddress address;
4004 :
4005 : /* lock level taken here should match renameatt_internal */
4006 316 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4007 316 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4008 : RangeVarCallbackForRenameAttribute,
4009 : NULL);
4010 :
4011 302 : if (!OidIsValid(relid))
4012 : {
4013 24 : ereport(NOTICE,
4014 : (errmsg("relation \"%s\" does not exist, skipping",
4015 : stmt->relation->relname)));
4016 24 : return InvalidObjectAddress;
4017 : }
4018 :
4019 : attnum =
4020 278 : renameatt_internal(relid,
4021 278 : stmt->subname, /* old att name */
4022 278 : stmt->newname, /* new att name */
4023 278 : stmt->relation->inh, /* recursive? */
4024 : false, /* recursing? */
4025 : 0, /* expected inhcount */
4026 : stmt->behavior);
4027 :
4028 194 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4029 :
4030 194 : return address;
4031 : }
4032 :
4033 : /*
4034 : * same logic as renameatt_internal
4035 : */
4036 : static ObjectAddress
4037 90 : rename_constraint_internal(Oid myrelid,
4038 : Oid mytypid,
4039 : const char *oldconname,
4040 : const char *newconname,
4041 : bool recurse,
4042 : bool recursing,
4043 : int expected_parents)
4044 : {
4045 90 : Relation targetrelation = NULL;
4046 : Oid constraintOid;
4047 : HeapTuple tuple;
4048 : Form_pg_constraint con;
4049 : ObjectAddress address;
4050 :
4051 : Assert(!myrelid || !mytypid);
4052 :
4053 90 : if (mytypid)
4054 : {
4055 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4056 : }
4057 : else
4058 : {
4059 84 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4060 :
4061 : /*
4062 : * don't tell it whether we're recursing; we allow changing typed
4063 : * tables here
4064 : */
4065 84 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4066 :
4067 84 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4068 : }
4069 :
4070 90 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4071 90 : if (!HeapTupleIsValid(tuple))
4072 0 : elog(ERROR, "cache lookup failed for constraint %u",
4073 : constraintOid);
4074 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4075 :
4076 90 : if (myrelid &&
4077 84 : (con->contype == CONSTRAINT_CHECK ||
4078 24 : con->contype == CONSTRAINT_NOTNULL) &&
4079 66 : !con->connoinherit)
4080 : {
4081 54 : if (recurse)
4082 : {
4083 : List *child_oids,
4084 : *child_numparents;
4085 : ListCell *lo,
4086 : *li;
4087 :
4088 36 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4089 : &child_numparents);
4090 :
4091 84 : forboth(lo, child_oids, li, child_numparents)
4092 : {
4093 48 : Oid childrelid = lfirst_oid(lo);
4094 48 : int numparents = lfirst_int(li);
4095 :
4096 48 : if (childrelid == myrelid)
4097 36 : continue;
4098 :
4099 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4100 : }
4101 : }
4102 : else
4103 : {
4104 24 : if (expected_parents == 0 &&
4105 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4106 6 : ereport(ERROR,
4107 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4108 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4109 : oldconname)));
4110 : }
4111 :
4112 48 : if (con->coninhcount > expected_parents)
4113 6 : ereport(ERROR,
4114 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4115 : errmsg("cannot rename inherited constraint \"%s\"",
4116 : oldconname)));
4117 : }
4118 :
4119 78 : if (con->conindid
4120 18 : && (con->contype == CONSTRAINT_PRIMARY
4121 6 : || con->contype == CONSTRAINT_UNIQUE
4122 0 : || con->contype == CONSTRAINT_EXCLUSION))
4123 : /* rename the index; this renames the constraint as well */
4124 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4125 : else
4126 60 : RenameConstraintById(constraintOid, newconname);
4127 :
4128 78 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4129 :
4130 78 : ReleaseSysCache(tuple);
4131 :
4132 78 : if (targetrelation)
4133 : {
4134 : /*
4135 : * Invalidate relcache so as others can see the new constraint name.
4136 : */
4137 72 : CacheInvalidateRelcache(targetrelation);
4138 :
4139 72 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4140 : }
4141 :
4142 78 : return address;
4143 : }
4144 :
4145 : ObjectAddress
4146 84 : RenameConstraint(RenameStmt *stmt)
4147 : {
4148 84 : Oid relid = InvalidOid;
4149 84 : Oid typid = InvalidOid;
4150 :
4151 84 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4152 : {
4153 : Relation rel;
4154 : HeapTuple tup;
4155 :
4156 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4157 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4158 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4159 6 : if (!HeapTupleIsValid(tup))
4160 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4161 6 : checkDomainOwner(tup);
4162 6 : ReleaseSysCache(tup);
4163 6 : table_close(rel, NoLock);
4164 : }
4165 : else
4166 : {
4167 : /* lock level taken here should match rename_constraint_internal */
4168 78 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4169 78 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4170 : RangeVarCallbackForRenameAttribute,
4171 : NULL);
4172 78 : if (!OidIsValid(relid))
4173 : {
4174 6 : ereport(NOTICE,
4175 : (errmsg("relation \"%s\" does not exist, skipping",
4176 : stmt->relation->relname)));
4177 6 : return InvalidObjectAddress;
4178 : }
4179 : }
4180 :
4181 : return
4182 78 : rename_constraint_internal(relid, typid,
4183 78 : stmt->subname,
4184 78 : stmt->newname,
4185 150 : (stmt->relation &&
4186 72 : stmt->relation->inh), /* recursive? */
4187 : false, /* recursing? */
4188 : 0 /* expected inhcount */ );
4189 : }
4190 :
4191 : /*
4192 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4193 : * RENAME
4194 : */
4195 : ObjectAddress
4196 510 : RenameRelation(RenameStmt *stmt)
4197 : {
4198 510 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4199 : Oid relid;
4200 : ObjectAddress address;
4201 :
4202 : /*
4203 : * Grab an exclusive lock on the target table, index, sequence, view,
4204 : * materialized view, or foreign table, which we will NOT release until
4205 : * end of transaction.
4206 : *
4207 : * Lock level used here should match RenameRelationInternal, to avoid lock
4208 : * escalation. However, because ALTER INDEX can be used with any relation
4209 : * type, we mustn't believe without verification.
4210 : */
4211 : for (;;)
4212 12 : {
4213 : LOCKMODE lockmode;
4214 : char relkind;
4215 : bool obj_is_index;
4216 :
4217 522 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4218 :
4219 522 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4220 522 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4221 : RangeVarCallbackForAlterRelation,
4222 : stmt);
4223 :
4224 472 : if (!OidIsValid(relid))
4225 : {
4226 18 : ereport(NOTICE,
4227 : (errmsg("relation \"%s\" does not exist, skipping",
4228 : stmt->relation->relname)));
4229 18 : return InvalidObjectAddress;
4230 : }
4231 :
4232 : /*
4233 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4234 : * to rename a table), but we might've used the wrong lock level. If
4235 : * that happens, retry with the correct lock level. We don't bother
4236 : * if we already acquired AccessExclusiveLock with an index, however.
4237 : */
4238 454 : relkind = get_rel_relkind(relid);
4239 454 : obj_is_index = (relkind == RELKIND_INDEX ||
4240 : relkind == RELKIND_PARTITIONED_INDEX);
4241 454 : if (obj_is_index || is_index_stmt == obj_is_index)
4242 : break;
4243 :
4244 12 : UnlockRelationOid(relid, lockmode);
4245 12 : is_index_stmt = obj_is_index;
4246 : }
4247 :
4248 : /* Do the work */
4249 442 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4250 :
4251 430 : ObjectAddressSet(address, RelationRelationId, relid);
4252 :
4253 430 : return address;
4254 : }
4255 :
4256 : /*
4257 : * RenameRelationInternal - change the name of a relation
4258 : */
4259 : void
4260 1702 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4261 : {
4262 : Relation targetrelation;
4263 : Relation relrelation; /* for RELATION relation */
4264 : ItemPointerData otid;
4265 : HeapTuple reltup;
4266 : Form_pg_class relform;
4267 : Oid namespaceId;
4268 :
4269 : /*
4270 : * Grab a lock on the target relation, which we will NOT release until end
4271 : * of transaction. We need at least a self-exclusive lock so that
4272 : * concurrent DDL doesn't overwrite the rename if they start updating
4273 : * while still seeing the old version. The lock also guards against
4274 : * triggering relcache reloads in concurrent sessions, which might not
4275 : * handle this information changing under them. For indexes, we can use a
4276 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4277 : * specially.
4278 : */
4279 1702 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4280 1702 : namespaceId = RelationGetNamespace(targetrelation);
4281 :
4282 : /*
4283 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4284 : */
4285 1702 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4286 :
4287 1702 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4288 1702 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4289 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4290 1702 : otid = reltup->t_self;
4291 1702 : relform = (Form_pg_class) GETSTRUCT(reltup);
4292 :
4293 1702 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4294 12 : ereport(ERROR,
4295 : (errcode(ERRCODE_DUPLICATE_TABLE),
4296 : errmsg("relation \"%s\" already exists",
4297 : newrelname)));
4298 :
4299 : /*
4300 : * RenameRelation is careful not to believe the caller's idea of the
4301 : * relation kind being handled. We don't have to worry about this, but
4302 : * let's not be totally oblivious to it. We can process an index as
4303 : * not-an-index, but not the other way around.
4304 : */
4305 : Assert(!is_index ||
4306 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4307 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4308 :
4309 : /*
4310 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4311 : * because it's a copy...)
4312 : */
4313 1690 : namestrcpy(&(relform->relname), newrelname);
4314 :
4315 1690 : CatalogTupleUpdate(relrelation, &otid, reltup);
4316 1690 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4317 :
4318 1690 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4319 : InvalidOid, is_internal);
4320 :
4321 1690 : heap_freetuple(reltup);
4322 1690 : table_close(relrelation, RowExclusiveLock);
4323 :
4324 : /*
4325 : * Also rename the associated type, if any.
4326 : */
4327 1690 : if (OidIsValid(targetrelation->rd_rel->reltype))
4328 124 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4329 : newrelname, namespaceId);
4330 :
4331 : /*
4332 : * Also rename the associated constraint, if any.
4333 : */
4334 1690 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4335 886 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4336 : {
4337 822 : Oid constraintId = get_index_constraint(myrelid);
4338 :
4339 822 : if (OidIsValid(constraintId))
4340 36 : RenameConstraintById(constraintId, newrelname);
4341 : }
4342 :
4343 : /*
4344 : * Close rel, but keep lock!
4345 : */
4346 1690 : relation_close(targetrelation, NoLock);
4347 1690 : }
4348 :
4349 : /*
4350 : * ResetRelRewrite - reset relrewrite
4351 : */
4352 : void
4353 610 : ResetRelRewrite(Oid myrelid)
4354 : {
4355 : Relation relrelation; /* for RELATION relation */
4356 : HeapTuple reltup;
4357 : Form_pg_class relform;
4358 :
4359 : /*
4360 : * Find relation's pg_class tuple.
4361 : */
4362 610 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4363 :
4364 610 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4365 610 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4366 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4367 610 : relform = (Form_pg_class) GETSTRUCT(reltup);
4368 :
4369 : /*
4370 : * Update pg_class tuple.
4371 : */
4372 610 : relform->relrewrite = InvalidOid;
4373 :
4374 610 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4375 :
4376 610 : heap_freetuple(reltup);
4377 610 : table_close(relrelation, RowExclusiveLock);
4378 610 : }
4379 :
4380 : /*
4381 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4382 : * any open reference to the target table besides the one just acquired by
4383 : * the calling command; this implies there's an open cursor or active plan.
4384 : * We need this check because our lock doesn't protect us against stomping
4385 : * on our own foot, only other people's feet!
4386 : *
4387 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4388 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4389 : * possibly be relaxed to only error out for certain types of alterations.
4390 : * But the use-case for allowing any of these things is not obvious, so we
4391 : * won't work hard at it for now.
4392 : *
4393 : * We also reject these commands if there are any pending AFTER trigger events
4394 : * for the rel. This is certainly necessary for the rewriting variants of
4395 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4396 : * events would try to fetch the wrong tuples. It might be overly cautious
4397 : * in other cases, but again it seems better to err on the side of paranoia.
4398 : *
4399 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4400 : * we are worried about active indexscans on the index. The trigger-event
4401 : * check can be skipped, since we are doing no damage to the parent table.
4402 : *
4403 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4404 : */
4405 : void
4406 171674 : CheckTableNotInUse(Relation rel, const char *stmt)
4407 : {
4408 : int expected_refcnt;
4409 :
4410 171674 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4411 171674 : if (rel->rd_refcnt != expected_refcnt)
4412 42 : ereport(ERROR,
4413 : (errcode(ERRCODE_OBJECT_IN_USE),
4414 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4415 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4416 : stmt, RelationGetRelationName(rel))));
4417 :
4418 171632 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4419 280308 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4420 139068 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4421 18 : ereport(ERROR,
4422 : (errcode(ERRCODE_OBJECT_IN_USE),
4423 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4424 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4425 : stmt, RelationGetRelationName(rel))));
4426 171614 : }
4427 :
4428 : /*
4429 : * CheckAlterTableIsSafe
4430 : * Verify that it's safe to allow ALTER TABLE on this relation.
4431 : *
4432 : * This consists of CheckTableNotInUse() plus a check that the relation
4433 : * isn't another session's temp table. We must split out the temp-table
4434 : * check because there are callers of CheckTableNotInUse() that don't want
4435 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4436 : * an orphaned temp schema.) Compare truncate_check_activity().
4437 : */
4438 : static void
4439 63072 : CheckAlterTableIsSafe(Relation rel)
4440 : {
4441 : /*
4442 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4443 : * manager is not going to cope if we need to change the table's contents.
4444 : * Even if we don't, there may be optimizations that assume temp tables
4445 : * aren't subject to such interference.
4446 : */
4447 63072 : if (RELATION_IS_OTHER_TEMP(rel))
4448 0 : ereport(ERROR,
4449 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4450 : errmsg("cannot alter temporary tables of other sessions")));
4451 :
4452 : /*
4453 : * Also check for active uses of the relation in the current transaction,
4454 : * including open scans and pending AFTER trigger events.
4455 : */
4456 63072 : CheckTableNotInUse(rel, "ALTER TABLE");
4457 63036 : }
4458 :
4459 : /*
4460 : * AlterTableLookupRelation
4461 : * Look up, and lock, the OID for the relation named by an alter table
4462 : * statement.
4463 : */
4464 : Oid
4465 34164 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4466 : {
4467 68240 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4468 34164 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4469 : RangeVarCallbackForAlterRelation,
4470 : stmt);
4471 : }
4472 :
4473 : /*
4474 : * AlterTable
4475 : * Execute ALTER TABLE, which can be a list of subcommands
4476 : *
4477 : * ALTER TABLE is performed in three phases:
4478 : * 1. Examine subcommands and perform pre-transformation checking.
4479 : * 2. Validate and transform subcommands, and update system catalogs.
4480 : * 3. Scan table(s) to check new constraints, and optionally recopy
4481 : * the data into new table(s).
4482 : * Phase 3 is not performed unless one or more of the subcommands requires
4483 : * it. The intention of this design is to allow multiple independent
4484 : * updates of the table schema to be performed with only one pass over the
4485 : * data.
4486 : *
4487 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4488 : * each table to be affected (there may be multiple affected tables if the
4489 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4490 : * validation of the subcommands. Because earlier subcommands may change
4491 : * the catalog state seen by later commands, there are limits to what can
4492 : * be done in this phase. Generally, this phase acquires table locks,
4493 : * checks permissions and relkind, and recurses to find child tables.
4494 : *
4495 : * ATRewriteCatalogs performs phase 2 for each affected table.
4496 : * Certain subcommands need to be performed before others to avoid
4497 : * unnecessary conflicts; for example, DROP COLUMN should come before
4498 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4499 : * lists, one for each logical "pass" of phase 2.
4500 : *
4501 : * ATRewriteTables performs phase 3 for those tables that need it.
4502 : *
4503 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4504 : * since phase 1 already does it. However, for certain subcommand types
4505 : * it is only possible to determine how to recurse at phase 2 time; for
4506 : * those cases, phase 1 sets the cmd->recurse flag.
4507 : *
4508 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4509 : * the whole operation; we don't have to do anything special to clean up.
4510 : *
4511 : * The caller must lock the relation, with an appropriate lock level
4512 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4513 : * or higher. We pass the lock level down
4514 : * so that we can apply it recursively to inherited tables. Note that the
4515 : * lock level we want as we recurse might well be higher than required for
4516 : * that specific subcommand. So we pass down the overall lock requirement,
4517 : * rather than reassess it at lower levels.
4518 : *
4519 : * The caller also provides a "context" which is to be passed back to
4520 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4521 : * Some of the fields therein, such as the relid, are used here as well.
4522 : */
4523 : void
4524 33938 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4525 : AlterTableUtilityContext *context)
4526 : {
4527 : Relation rel;
4528 :
4529 : /* Caller is required to provide an adequate lock. */
4530 33938 : rel = relation_open(context->relid, NoLock);
4531 :
4532 33938 : CheckAlterTableIsSafe(rel);
4533 :
4534 33920 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4535 30266 : }
4536 :
4537 : /*
4538 : * AlterTableInternal
4539 : *
4540 : * ALTER TABLE with target specified by OID
4541 : *
4542 : * We do not reject if the relation is already open, because it's quite
4543 : * likely that one or more layers of caller have it open. That means it
4544 : * is unsafe to use this entry point for alterations that could break
4545 : * existing query plans. On the assumption it's not used for such, we
4546 : * don't have to reject pending AFTER triggers, either.
4547 : *
4548 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4549 : * used for any subcommand types that require parse transformation or
4550 : * could generate subcommands that have to be passed to ProcessUtility.
4551 : */
4552 : void
4553 282 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4554 : {
4555 : Relation rel;
4556 282 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4557 :
4558 282 : rel = relation_open(relid, lockmode);
4559 :
4560 282 : EventTriggerAlterTableRelid(relid);
4561 :
4562 282 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4563 282 : }
4564 :
4565 : /*
4566 : * AlterTableGetLockLevel
4567 : *
4568 : * Sets the overall lock level required for the supplied list of subcommands.
4569 : * Policy for doing this set according to needs of AlterTable(), see
4570 : * comments there for overall explanation.
4571 : *
4572 : * Function is called before and after parsing, so it must give same
4573 : * answer each time it is called. Some subcommands are transformed
4574 : * into other subcommand types, so the transform must never be made to a
4575 : * lower lock level than previously assigned. All transforms are noted below.
4576 : *
4577 : * Since this is called before we lock the table we cannot use table metadata
4578 : * to influence the type of lock we acquire.
4579 : *
4580 : * There should be no lockmodes hardcoded into the subcommand functions. All
4581 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4582 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4583 : * and does not travel through this section of code and cannot be combined with
4584 : * any of the subcommands given here.
4585 : *
4586 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4587 : * so any changes that might affect SELECTs running on standbys need to use
4588 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4589 : * have a solution for that also.
4590 : *
4591 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4592 : * that takes a lock less than AccessExclusiveLock can change object definitions
4593 : * while pg_dump is running. Be careful to check that the appropriate data is
4594 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4595 : * otherwise we might end up with an inconsistent dump that can't restore.
4596 : */
4597 : LOCKMODE
4598 34446 : AlterTableGetLockLevel(List *cmds)
4599 : {
4600 : /*
4601 : * This only works if we read catalog tables using MVCC snapshots.
4602 : */
4603 : ListCell *lcmd;
4604 34446 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4605 :
4606 70064 : foreach(lcmd, cmds)
4607 : {
4608 35618 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4609 35618 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4610 :
4611 35618 : switch (cmd->subtype)
4612 : {
4613 : /*
4614 : * These subcommands rewrite the heap, so require full locks.
4615 : */
4616 3562 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4617 : * to SELECT */
4618 : case AT_SetAccessMethod: /* must rewrite heap */
4619 : case AT_SetTableSpace: /* must rewrite heap */
4620 : case AT_AlterColumnType: /* must rewrite heap */
4621 3562 : cmd_lockmode = AccessExclusiveLock;
4622 3562 : break;
4623 :
4624 : /*
4625 : * These subcommands may require addition of toast tables. If
4626 : * we add a toast table to a table currently being scanned, we
4627 : * might miss data added to the new toast table by concurrent
4628 : * insert transactions.
4629 : */
4630 224 : case AT_SetStorage: /* may add toast tables, see
4631 : * ATRewriteCatalogs() */
4632 224 : cmd_lockmode = AccessExclusiveLock;
4633 224 : break;
4634 :
4635 : /*
4636 : * Removing constraints can affect SELECTs that have been
4637 : * optimized assuming the constraint holds true. See also
4638 : * CloneFkReferenced.
4639 : */
4640 1110 : case AT_DropConstraint: /* as DROP INDEX */
4641 : case AT_DropNotNull: /* may change some SQL plans */
4642 1110 : cmd_lockmode = AccessExclusiveLock;
4643 1110 : break;
4644 :
4645 : /*
4646 : * Subcommands that may be visible to concurrent SELECTs
4647 : */
4648 1756 : case AT_DropColumn: /* change visible to SELECT */
4649 : case AT_AddColumnToView: /* CREATE VIEW */
4650 : case AT_DropOids: /* used to equiv to DropColumn */
4651 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4652 : case AT_EnableReplicaRule: /* may change SELECT rules */
4653 : case AT_EnableRule: /* may change SELECT rules */
4654 : case AT_DisableRule: /* may change SELECT rules */
4655 1756 : cmd_lockmode = AccessExclusiveLock;
4656 1756 : break;
4657 :
4658 : /*
4659 : * Changing owner may remove implicit SELECT privileges
4660 : */
4661 3574 : case AT_ChangeOwner: /* change visible to SELECT */
4662 3574 : cmd_lockmode = AccessExclusiveLock;
4663 3574 : break;
4664 :
4665 : /*
4666 : * Changing foreign table options may affect optimization.
4667 : */
4668 254 : case AT_GenericOptions:
4669 : case AT_AlterColumnGenericOptions:
4670 254 : cmd_lockmode = AccessExclusiveLock;
4671 254 : break;
4672 :
4673 : /*
4674 : * These subcommands affect write operations only.
4675 : */
4676 346 : case AT_EnableTrig:
4677 : case AT_EnableAlwaysTrig:
4678 : case AT_EnableReplicaTrig:
4679 : case AT_EnableTrigAll:
4680 : case AT_EnableTrigUser:
4681 : case AT_DisableTrig:
4682 : case AT_DisableTrigAll:
4683 : case AT_DisableTrigUser:
4684 346 : cmd_lockmode = ShareRowExclusiveLock;
4685 346 : break;
4686 :
4687 : /*
4688 : * These subcommands affect write operations only. XXX
4689 : * Theoretically, these could be ShareRowExclusiveLock.
4690 : */
4691 2934 : case AT_ColumnDefault:
4692 : case AT_CookedColumnDefault:
4693 : case AT_AlterConstraint:
4694 : case AT_AddIndex: /* from ADD CONSTRAINT */
4695 : case AT_AddIndexConstraint:
4696 : case AT_ReplicaIdentity:
4697 : case AT_SetNotNull:
4698 : case AT_EnableRowSecurity:
4699 : case AT_DisableRowSecurity:
4700 : case AT_ForceRowSecurity:
4701 : case AT_NoForceRowSecurity:
4702 : case AT_AddIdentity:
4703 : case AT_DropIdentity:
4704 : case AT_SetIdentity:
4705 : case AT_SetExpression:
4706 : case AT_DropExpression:
4707 : case AT_SetCompression:
4708 2934 : cmd_lockmode = AccessExclusiveLock;
4709 2934 : break;
4710 :
4711 15598 : case AT_AddConstraint:
4712 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4713 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4714 15598 : if (IsA(cmd->def, Constraint))
4715 : {
4716 15598 : Constraint *con = (Constraint *) cmd->def;
4717 :
4718 15598 : switch (con->contype)
4719 : {
4720 11956 : case CONSTR_EXCLUSION:
4721 : case CONSTR_PRIMARY:
4722 : case CONSTR_UNIQUE:
4723 :
4724 : /*
4725 : * Cases essentially the same as CREATE INDEX. We
4726 : * could reduce the lock strength to ShareLock if
4727 : * we can work out how to allow concurrent catalog
4728 : * updates. XXX Might be set down to
4729 : * ShareRowExclusiveLock but requires further
4730 : * analysis.
4731 : */
4732 11956 : cmd_lockmode = AccessExclusiveLock;
4733 11956 : break;
4734 2560 : case CONSTR_FOREIGN:
4735 :
4736 : /*
4737 : * We add triggers to both tables when we add a
4738 : * Foreign Key, so the lock level must be at least
4739 : * as strong as CREATE TRIGGER.
4740 : */
4741 2560 : cmd_lockmode = ShareRowExclusiveLock;
4742 2560 : break;
4743 :
4744 1082 : default:
4745 1082 : cmd_lockmode = AccessExclusiveLock;
4746 : }
4747 0 : }
4748 15598 : break;
4749 :
4750 : /*
4751 : * These subcommands affect inheritance behaviour. Queries
4752 : * started before us will continue to see the old inheritance
4753 : * behaviour, while queries started after we commit will see
4754 : * new behaviour. No need to prevent reads or writes to the
4755 : * subtable while we hook it up though. Changing the TupDesc
4756 : * may be a problem, so keep highest lock.
4757 : */
4758 522 : case AT_AddInherit:
4759 : case AT_DropInherit:
4760 522 : cmd_lockmode = AccessExclusiveLock;
4761 522 : break;
4762 :
4763 : /*
4764 : * These subcommands affect implicit row type conversion. They
4765 : * have affects similar to CREATE/DROP CAST on queries. don't
4766 : * provide for invalidating parse trees as a result of such
4767 : * changes, so we keep these at AccessExclusiveLock.
4768 : */
4769 72 : case AT_AddOf:
4770 : case AT_DropOf:
4771 72 : cmd_lockmode = AccessExclusiveLock;
4772 72 : break;
4773 :
4774 : /*
4775 : * Only used by CREATE OR REPLACE VIEW which must conflict
4776 : * with an SELECTs currently using the view.
4777 : */
4778 198 : case AT_ReplaceRelOptions:
4779 198 : cmd_lockmode = AccessExclusiveLock;
4780 198 : break;
4781 :
4782 : /*
4783 : * These subcommands affect general strategies for performance
4784 : * and maintenance, though don't change the semantic results
4785 : * from normal data reads and writes. Delaying an ALTER TABLE
4786 : * behind currently active writes only delays the point where
4787 : * the new strategy begins to take effect, so there is no
4788 : * benefit in waiting. In this case the minimum restriction
4789 : * applies: we don't currently allow concurrent catalog
4790 : * updates.
4791 : */
4792 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4793 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4794 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4795 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4796 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4797 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4798 234 : break;
4799 :
4800 112 : case AT_SetLogged:
4801 : case AT_SetUnLogged:
4802 112 : cmd_lockmode = AccessExclusiveLock;
4803 112 : break;
4804 :
4805 448 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4806 448 : cmd_lockmode = ShareUpdateExclusiveLock;
4807 448 : break;
4808 :
4809 : /*
4810 : * Rel options are more complex than first appears. Options
4811 : * are set here for tables, views and indexes; for historical
4812 : * reasons these can all be used with ALTER TABLE, so we can't
4813 : * decide between them using the basic grammar.
4814 : */
4815 764 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4816 : * getTables() */
4817 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4818 : * getTables() */
4819 764 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4820 764 : break;
4821 :
4822 3314 : case AT_AttachPartition:
4823 3314 : cmd_lockmode = ShareUpdateExclusiveLock;
4824 3314 : break;
4825 :
4826 576 : case AT_DetachPartition:
4827 576 : if (((PartitionCmd *) cmd->def)->concurrent)
4828 164 : cmd_lockmode = ShareUpdateExclusiveLock;
4829 : else
4830 412 : cmd_lockmode = AccessExclusiveLock;
4831 576 : break;
4832 :
4833 20 : case AT_DetachPartitionFinalize:
4834 20 : cmd_lockmode = ShareUpdateExclusiveLock;
4835 20 : break;
4836 :
4837 0 : default: /* oops */
4838 0 : elog(ERROR, "unrecognized alter table type: %d",
4839 : (int) cmd->subtype);
4840 : break;
4841 : }
4842 :
4843 : /*
4844 : * Take the greatest lockmode from any subcommand
4845 : */
4846 35618 : if (cmd_lockmode > lockmode)
4847 29690 : lockmode = cmd_lockmode;
4848 : }
4849 :
4850 34446 : return lockmode;
4851 : }
4852 :
4853 : /*
4854 : * ATController provides top level control over the phases.
4855 : *
4856 : * parsetree is passed in to allow it to be passed to event triggers
4857 : * when requested.
4858 : */
4859 : static void
4860 34202 : ATController(AlterTableStmt *parsetree,
4861 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4862 : AlterTableUtilityContext *context)
4863 : {
4864 34202 : List *wqueue = NIL;
4865 : ListCell *lcmd;
4866 :
4867 : /* Phase 1: preliminary examination of commands, create work queue */
4868 69170 : foreach(lcmd, cmds)
4869 : {
4870 35368 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4871 :
4872 35368 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4873 : }
4874 :
4875 : /* Close the relation, but keep lock until commit */
4876 33802 : relation_close(rel, NoLock);
4877 :
4878 : /* Phase 2: update system catalogs */
4879 33802 : ATRewriteCatalogs(&wqueue, lockmode, context);
4880 :
4881 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4882 30986 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4883 30548 : }
4884 :
4885 : /*
4886 : * ATPrepCmd
4887 : *
4888 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4889 : * recursion and permission checks.
4890 : *
4891 : * Caller must have acquired appropriate lock type on relation already.
4892 : * This lock should be held until commit.
4893 : */
4894 : static void
4895 36308 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4896 : bool recurse, bool recursing, LOCKMODE lockmode,
4897 : AlterTableUtilityContext *context)
4898 : {
4899 : AlteredTableInfo *tab;
4900 36308 : AlterTablePass pass = AT_PASS_UNSET;
4901 :
4902 : /* Find or create work queue entry for this table */
4903 36308 : tab = ATGetQueueEntry(wqueue, rel);
4904 :
4905 : /*
4906 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4907 : * partitions that are pending detach.
4908 : */
4909 36308 : if (rel->rd_rel->relispartition &&
4910 3056 : cmd->subtype != AT_DetachPartitionFinalize &&
4911 1528 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4912 2 : ereport(ERROR,
4913 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4914 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4915 : RelationGetRelationName(rel)),
4916 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4917 :
4918 : /*
4919 : * Copy the original subcommand for each table, so we can scribble on it.
4920 : * This avoids conflicts when different child tables need to make
4921 : * different parse transformations (for example, the same column may have
4922 : * different column numbers in different children).
4923 : */
4924 36306 : cmd = copyObject(cmd);
4925 :
4926 : /*
4927 : * Do permissions and relkind checking, recursion to child tables if
4928 : * needed, and any additional phase-1 processing needed. (But beware of
4929 : * adding any processing that looks at table details that another
4930 : * subcommand could change. In some cases we reject multiple subcommands
4931 : * that could try to change the same state in contrary ways.)
4932 : */
4933 36306 : switch (cmd->subtype)
4934 : {
4935 2158 : case AT_AddColumn: /* ADD COLUMN */
4936 2158 : ATSimplePermissions(cmd->subtype, rel,
4937 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4938 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4939 2158 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4940 : lockmode, context);
4941 : /* Recursion occurs during execution phase */
4942 2146 : pass = AT_PASS_ADD_COL;
4943 2146 : break;
4944 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4945 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4946 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4947 : lockmode, context);
4948 : /* Recursion occurs during execution phase */
4949 24 : pass = AT_PASS_ADD_COL;
4950 24 : break;
4951 648 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4952 :
4953 : /*
4954 : * We allow defaults on views so that INSERT into a view can have
4955 : * default-ish behavior. This works because the rewriter
4956 : * substitutes default values into INSERTs before it expands
4957 : * rules.
4958 : */
4959 648 : ATSimplePermissions(cmd->subtype, rel,
4960 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4961 : ATT_FOREIGN_TABLE);
4962 648 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4963 : /* No command-specific prep needed */
4964 648 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4965 648 : break;
4966 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4967 : /* This is currently used only in CREATE TABLE */
4968 : /* (so the permission check really isn't necessary) */
4969 80 : ATSimplePermissions(cmd->subtype, rel,
4970 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4971 : /* This command never recurses */
4972 80 : pass = AT_PASS_ADD_OTHERCONSTR;
4973 80 : break;
4974 210 : case AT_AddIdentity:
4975 210 : ATSimplePermissions(cmd->subtype, rel,
4976 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4977 : ATT_FOREIGN_TABLE);
4978 : /* Set up recursion for phase 2; no other prep needed */
4979 210 : if (recurse)
4980 204 : cmd->recurse = true;
4981 210 : pass = AT_PASS_ADD_OTHERCONSTR;
4982 210 : break;
4983 62 : case AT_SetIdentity:
4984 62 : ATSimplePermissions(cmd->subtype, rel,
4985 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4986 : ATT_FOREIGN_TABLE);
4987 : /* Set up recursion for phase 2; no other prep needed */
4988 62 : if (recurse)
4989 56 : cmd->recurse = true;
4990 : /* This should run after AddIdentity, so do it in MISC pass */
4991 62 : pass = AT_PASS_MISC;
4992 62 : break;
4993 56 : case AT_DropIdentity:
4994 56 : ATSimplePermissions(cmd->subtype, rel,
4995 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4996 : ATT_FOREIGN_TABLE);
4997 : /* Set up recursion for phase 2; no other prep needed */
4998 56 : if (recurse)
4999 50 : cmd->recurse = true;
5000 56 : pass = AT_PASS_DROP;
5001 56 : break;
5002 274 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5003 274 : ATSimplePermissions(cmd->subtype, rel,
5004 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5005 : /* Set up recursion for phase 2; no other prep needed */
5006 268 : if (recurse)
5007 250 : cmd->recurse = true;
5008 268 : pass = AT_PASS_DROP;
5009 268 : break;
5010 414 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5011 414 : ATSimplePermissions(cmd->subtype, rel,
5012 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5013 : /* Set up recursion for phase 2; no other prep needed */
5014 408 : if (recurse)
5015 384 : cmd->recurse = true;
5016 408 : pass = AT_PASS_COL_ATTRS;
5017 408 : break;
5018 180 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5019 180 : ATSimplePermissions(cmd->subtype, rel,
5020 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5021 180 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5022 180 : pass = AT_PASS_SET_EXPRESSION;
5023 180 : break;
5024 86 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5025 86 : ATSimplePermissions(cmd->subtype, rel,
5026 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5027 86 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5028 86 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5029 62 : pass = AT_PASS_DROP;
5030 62 : break;
5031 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5032 164 : ATSimplePermissions(cmd->subtype, rel,
5033 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
5034 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
5035 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5036 : /* No command-specific prep needed */
5037 164 : pass = AT_PASS_MISC;
5038 164 : break;
5039 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5040 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5041 44 : ATSimplePermissions(cmd->subtype, rel,
5042 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5043 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5044 : /* This command never recurses */
5045 32 : pass = AT_PASS_MISC;
5046 32 : break;
5047 246 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5048 246 : ATSimplePermissions(cmd->subtype, rel,
5049 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5050 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5051 246 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5052 : /* No command-specific prep needed */
5053 246 : pass = AT_PASS_MISC;
5054 246 : break;
5055 90 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5056 90 : ATSimplePermissions(cmd->subtype, rel,
5057 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5058 : /* This command never recurses */
5059 : /* No command-specific prep needed */
5060 90 : pass = AT_PASS_MISC;
5061 90 : break;
5062 1656 : case AT_DropColumn: /* DROP COLUMN */
5063 1656 : ATSimplePermissions(cmd->subtype, rel,
5064 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5065 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5066 1650 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5067 : lockmode, context);
5068 : /* Recursion occurs during execution phase */
5069 1638 : pass = AT_PASS_DROP;
5070 1638 : break;
5071 0 : case AT_AddIndex: /* ADD INDEX */
5072 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5073 : /* This command never recurses */
5074 : /* No command-specific prep needed */
5075 0 : pass = AT_PASS_ADD_INDEX;
5076 0 : break;
5077 16062 : case AT_AddConstraint: /* ADD CONSTRAINT */
5078 16062 : ATSimplePermissions(cmd->subtype, rel,
5079 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5080 16062 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5081 16032 : if (recurse)
5082 : {
5083 : /* recurses at exec time; lock descendants and set flag */
5084 15362 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5085 15362 : cmd->recurse = true;
5086 : }
5087 16032 : pass = AT_PASS_ADD_CONSTR;
5088 16032 : break;
5089 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5090 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5091 : /* This command never recurses */
5092 : /* No command-specific prep needed */
5093 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5094 0 : break;
5095 798 : case AT_DropConstraint: /* DROP CONSTRAINT */
5096 798 : ATSimplePermissions(cmd->subtype, rel,
5097 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5098 798 : ATCheckPartitionsNotInUse(rel, lockmode);
5099 : /* Other recursion occurs during execution phase */
5100 : /* No command-specific prep needed except saving recurse flag */
5101 792 : if (recurse)
5102 756 : cmd->recurse = true;
5103 792 : pass = AT_PASS_DROP;
5104 792 : break;
5105 1288 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5106 1288 : ATSimplePermissions(cmd->subtype, rel,
5107 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5108 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5109 : /* See comments for ATPrepAlterColumnType */
5110 1288 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5111 : AT_PASS_UNSET, context);
5112 : Assert(cmd != NULL);
5113 : /* Performs own recursion */
5114 1282 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5115 : lockmode, context);
5116 1090 : pass = AT_PASS_ALTER_TYPE;
5117 1090 : break;
5118 172 : case AT_AlterColumnGenericOptions:
5119 172 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5120 : /* This command never recurses */
5121 : /* No command-specific prep needed */
5122 172 : pass = AT_PASS_MISC;
5123 172 : break;
5124 3550 : case AT_ChangeOwner: /* ALTER OWNER */
5125 : /* This command never recurses */
5126 : /* No command-specific prep needed */
5127 3550 : pass = AT_PASS_MISC;
5128 3550 : break;
5129 64 : case AT_ClusterOn: /* CLUSTER ON */
5130 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5131 64 : ATSimplePermissions(cmd->subtype, rel,
5132 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5133 : /* These commands never recurse */
5134 : /* No command-specific prep needed */
5135 64 : pass = AT_PASS_MISC;
5136 64 : break;
5137 112 : case AT_SetLogged: /* SET LOGGED */
5138 : case AT_SetUnLogged: /* SET UNLOGGED */
5139 112 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5140 100 : if (tab->chgPersistence)
5141 0 : ereport(ERROR,
5142 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5143 : errmsg("cannot change persistence setting twice")));
5144 100 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5145 88 : pass = AT_PASS_MISC;
5146 88 : break;
5147 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5148 6 : ATSimplePermissions(cmd->subtype, rel,
5149 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5150 6 : pass = AT_PASS_DROP;
5151 6 : break;
5152 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5153 128 : ATSimplePermissions(cmd->subtype, rel,
5154 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5155 :
5156 : /* check if another access method change was already requested */
5157 128 : if (tab->chgAccessMethod)
5158 18 : ereport(ERROR,
5159 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5160 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5161 :
5162 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5163 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5164 110 : break;
5165 160 : case AT_SetTableSpace: /* SET TABLESPACE */
5166 160 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5167 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5168 : /* This command never recurses */
5169 160 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5170 160 : pass = AT_PASS_MISC; /* doesn't actually matter */
5171 160 : break;
5172 960 : case AT_SetRelOptions: /* SET (...) */
5173 : case AT_ResetRelOptions: /* RESET (...) */
5174 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5175 960 : ATSimplePermissions(cmd->subtype, rel,
5176 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5177 : ATT_MATVIEW | ATT_INDEX);
5178 : /* This command never recurses */
5179 : /* No command-specific prep needed */
5180 958 : pass = AT_PASS_MISC;
5181 958 : break;
5182 436 : case AT_AddInherit: /* INHERIT */
5183 436 : ATSimplePermissions(cmd->subtype, rel,
5184 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5185 : /* This command never recurses */
5186 436 : ATPrepAddInherit(rel);
5187 418 : pass = AT_PASS_MISC;
5188 418 : break;
5189 86 : case AT_DropInherit: /* NO INHERIT */
5190 86 : ATSimplePermissions(cmd->subtype, rel,
5191 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5192 : /* This command never recurses */
5193 : /* No command-specific prep needed */
5194 86 : pass = AT_PASS_MISC;
5195 86 : break;
5196 288 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5197 288 : ATSimplePermissions(cmd->subtype, rel,
5198 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5199 : /* Recursion occurs during execution phase */
5200 282 : if (recurse)
5201 282 : cmd->recurse = true;
5202 282 : pass = AT_PASS_MISC;
5203 282 : break;
5204 448 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5205 448 : ATSimplePermissions(cmd->subtype, rel,
5206 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5207 : /* Recursion occurs during execution phase */
5208 : /* No command-specific prep needed except saving recurse flag */
5209 448 : if (recurse)
5210 448 : cmd->recurse = true;
5211 448 : pass = AT_PASS_MISC;
5212 448 : break;
5213 490 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5214 490 : ATSimplePermissions(cmd->subtype, rel,
5215 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5216 490 : pass = AT_PASS_MISC;
5217 : /* This command never recurses */
5218 : /* No command-specific prep needed */
5219 490 : break;
5220 346 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5221 : case AT_EnableAlwaysTrig:
5222 : case AT_EnableReplicaTrig:
5223 : case AT_EnableTrigAll:
5224 : case AT_EnableTrigUser:
5225 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5226 : case AT_DisableTrigAll:
5227 : case AT_DisableTrigUser:
5228 346 : ATSimplePermissions(cmd->subtype, rel,
5229 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5230 : /* Set up recursion for phase 2; no other prep needed */
5231 346 : if (recurse)
5232 318 : cmd->recurse = true;
5233 346 : pass = AT_PASS_MISC;
5234 346 : break;
5235 564 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5236 : case AT_EnableAlwaysRule:
5237 : case AT_EnableReplicaRule:
5238 : case AT_DisableRule:
5239 : case AT_AddOf: /* OF */
5240 : case AT_DropOf: /* NOT OF */
5241 : case AT_EnableRowSecurity:
5242 : case AT_DisableRowSecurity:
5243 : case AT_ForceRowSecurity:
5244 : case AT_NoForceRowSecurity:
5245 564 : ATSimplePermissions(cmd->subtype, rel,
5246 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5247 : /* These commands never recurse */
5248 : /* No command-specific prep needed */
5249 564 : pass = AT_PASS_MISC;
5250 564 : break;
5251 58 : case AT_GenericOptions:
5252 58 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5253 : /* No command-specific prep needed */
5254 58 : pass = AT_PASS_MISC;
5255 58 : break;
5256 3302 : case AT_AttachPartition:
5257 3302 : ATSimplePermissions(cmd->subtype, rel,
5258 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5259 : /* No command-specific prep needed */
5260 3296 : pass = AT_PASS_MISC;
5261 3296 : break;
5262 576 : case AT_DetachPartition:
5263 576 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5264 : /* No command-specific prep needed */
5265 558 : pass = AT_PASS_MISC;
5266 558 : break;
5267 20 : case AT_DetachPartitionFinalize:
5268 20 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5269 : /* No command-specific prep needed */
5270 14 : pass = AT_PASS_MISC;
5271 14 : break;
5272 0 : default: /* oops */
5273 0 : elog(ERROR, "unrecognized alter table type: %d",
5274 : (int) cmd->subtype);
5275 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5276 : break;
5277 : }
5278 : Assert(pass > AT_PASS_UNSET);
5279 :
5280 : /* Add the subcommand to the appropriate list for phase 2 */
5281 35896 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5282 35896 : }
5283 :
5284 : /*
5285 : * ATRewriteCatalogs
5286 : *
5287 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5288 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5289 : * conflicts).
5290 : */
5291 : static void
5292 33802 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5293 : AlterTableUtilityContext *context)
5294 : {
5295 : ListCell *ltab;
5296 :
5297 : /*
5298 : * We process all the tables "in parallel", one pass at a time. This is
5299 : * needed because we may have to propagate work from one table to another
5300 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5301 : * re-adding of the foreign key constraint to the other table). Work can
5302 : * only be propagated into later passes, however.
5303 : */
5304 426890 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5305 : {
5306 : /* Go through each table that needs to be processed */
5307 805046 : foreach(ltab, *wqueue)
5308 : {
5309 411958 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5310 411958 : List *subcmds = tab->subcmds[pass];
5311 : ListCell *lcmd;
5312 :
5313 411958 : if (subcmds == NIL)
5314 354722 : continue;
5315 :
5316 : /*
5317 : * Open the relation and store it in tab. This allows subroutines
5318 : * close and reopen, if necessary. Appropriate lock was obtained
5319 : * by phase 1, needn't get it again.
5320 : */
5321 57236 : tab->rel = relation_open(tab->relid, NoLock);
5322 :
5323 115274 : foreach(lcmd, subcmds)
5324 60854 : ATExecCmd(wqueue, tab,
5325 60854 : lfirst_node(AlterTableCmd, lcmd),
5326 : lockmode, pass, context);
5327 :
5328 : /*
5329 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5330 : * (this is not done in ATExecAlterColumnType since it should be
5331 : * done only once if multiple columns of a table are altered).
5332 : */
5333 54420 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5334 1120 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5335 :
5336 54420 : if (tab->rel)
5337 : {
5338 54420 : relation_close(tab->rel, NoLock);
5339 54420 : tab->rel = NULL;
5340 : }
5341 : }
5342 : }
5343 :
5344 : /* Check to see if a toast table must be added. */
5345 66518 : foreach(ltab, *wqueue)
5346 : {
5347 35532 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5348 :
5349 : /*
5350 : * If the table is source table of ATTACH PARTITION command, we did
5351 : * not modify anything about it that will change its toasting
5352 : * requirement, so no need to check.
5353 : */
5354 35532 : if (((tab->relkind == RELKIND_RELATION ||
5355 6974 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5356 33344 : tab->partition_constraint == NULL) ||
5357 4590 : tab->relkind == RELKIND_MATVIEW)
5358 31010 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5359 : }
5360 30986 : }
5361 :
5362 : /*
5363 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5364 : */
5365 : static void
5366 60854 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5367 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5368 : AlterTableUtilityContext *context)
5369 : {
5370 60854 : ObjectAddress address = InvalidObjectAddress;
5371 60854 : Relation rel = tab->rel;
5372 :
5373 60854 : switch (cmd->subtype)
5374 : {
5375 2164 : case AT_AddColumn: /* ADD COLUMN */
5376 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5377 2164 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5378 2164 : cmd->recurse, false,
5379 : lockmode, cur_pass, context);
5380 2032 : break;
5381 612 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5382 612 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5383 546 : break;
5384 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5385 80 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5386 80 : break;
5387 210 : case AT_AddIdentity:
5388 210 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5389 : cur_pass, context);
5390 : Assert(cmd != NULL);
5391 198 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5392 150 : break;
5393 62 : case AT_SetIdentity:
5394 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5395 : cur_pass, context);
5396 : Assert(cmd != NULL);
5397 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5398 38 : break;
5399 56 : case AT_DropIdentity:
5400 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5401 38 : break;
5402 268 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5403 268 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5404 166 : break;
5405 408 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5406 408 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5407 408 : cmd->recurse, false, lockmode);
5408 378 : break;
5409 180 : case AT_SetExpression:
5410 180 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5411 150 : break;
5412 56 : case AT_DropExpression:
5413 56 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5414 32 : break;
5415 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5416 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5417 116 : break;
5418 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5419 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5420 26 : break;
5421 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5422 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5423 6 : break;
5424 246 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5425 246 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5426 234 : break;
5427 90 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5428 90 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5429 : lockmode);
5430 84 : break;
5431 1638 : case AT_DropColumn: /* DROP COLUMN */
5432 1638 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5433 1638 : cmd->behavior, cmd->recurse, false,
5434 1638 : cmd->missing_ok, lockmode,
5435 : NULL);
5436 1458 : break;
5437 1454 : case AT_AddIndex: /* ADD INDEX */
5438 1454 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5439 : lockmode);
5440 1284 : break;
5441 444 : case AT_ReAddIndex: /* ADD INDEX */
5442 444 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5443 : lockmode);
5444 444 : break;
5445 14 : case AT_ReAddStatistics: /* ADD STATISTICS */
5446 14 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5447 : true, lockmode);
5448 14 : break;
5449 28306 : case AT_AddConstraint: /* ADD CONSTRAINT */
5450 : /* Transform the command only during initial examination */
5451 28306 : if (cur_pass == AT_PASS_ADD_CONSTR)
5452 16002 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5453 16032 : cmd->recurse, lockmode,
5454 : cur_pass, context);
5455 : /* Depending on constraint type, might be no more work to do now */
5456 28276 : if (cmd != NULL)
5457 : address =
5458 12274 : ATExecAddConstraint(wqueue, tab, rel,
5459 12274 : (Constraint *) cmd->def,
5460 12274 : cmd->recurse, false, lockmode);
5461 27596 : break;
5462 326 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5463 : address =
5464 326 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5465 : true, true, lockmode);
5466 314 : break;
5467 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5468 : * constraint */
5469 : address =
5470 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5471 14 : ((AlterDomainStmt *) cmd->def)->def,
5472 : NULL);
5473 8 : break;
5474 78 : case AT_ReAddComment: /* Re-add existing comment */
5475 78 : address = CommentObject((CommentStmt *) cmd->def);
5476 78 : break;
5477 10420 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5478 10420 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5479 : lockmode);
5480 10408 : break;
5481 282 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5482 282 : address = ATExecAlterConstraint(wqueue, rel,
5483 282 : castNode(ATAlterConstraint, cmd->def),
5484 282 : cmd->recurse, lockmode);
5485 216 : break;
5486 448 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5487 448 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5488 : false, lockmode);
5489 442 : break;
5490 792 : case AT_DropConstraint: /* DROP CONSTRAINT */
5491 792 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5492 792 : cmd->recurse,
5493 792 : cmd->missing_ok, lockmode);
5494 582 : break;
5495 1054 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5496 : /* parse transformation was done earlier */
5497 1054 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5498 1012 : break;
5499 172 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5500 : address =
5501 172 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5502 172 : (List *) cmd->def, lockmode);
5503 166 : break;
5504 3550 : case AT_ChangeOwner: /* ALTER OWNER */
5505 3544 : ATExecChangeOwner(RelationGetRelid(rel),
5506 3550 : get_rolespec_oid(cmd->newowner, false),
5507 : false, lockmode);
5508 3532 : break;
5509 64 : case AT_ClusterOn: /* CLUSTER ON */
5510 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5511 58 : break;
5512 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5513 18 : ATExecDropCluster(rel, lockmode);
5514 12 : break;
5515 88 : case AT_SetLogged: /* SET LOGGED */
5516 : case AT_SetUnLogged: /* SET UNLOGGED */
5517 88 : break;
5518 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5519 : /* nothing to do here, oid columns don't exist anymore */
5520 6 : break;
5521 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5522 :
5523 : /*
5524 : * Only do this for partitioned tables, for which this is just a
5525 : * catalog change. Tables with storage are handled by Phase 3.
5526 : */
5527 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5528 50 : tab->chgAccessMethod)
5529 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5530 92 : break;
5531 160 : case AT_SetTableSpace: /* SET TABLESPACE */
5532 :
5533 : /*
5534 : * Only do this for partitioned tables and indexes, for which this
5535 : * is just a catalog change. Other relation types which have
5536 : * storage are handled by Phase 3.
5537 : */
5538 160 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5539 148 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5540 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5541 :
5542 154 : break;
5543 958 : case AT_SetRelOptions: /* SET (...) */
5544 : case AT_ResetRelOptions: /* RESET (...) */
5545 : case AT_ReplaceRelOptions: /* replace entire option list */
5546 958 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5547 906 : break;
5548 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5549 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5550 : TRIGGER_FIRES_ON_ORIGIN, false,
5551 122 : cmd->recurse,
5552 : lockmode);
5553 122 : break;
5554 44 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5555 44 : ATExecEnableDisableTrigger(rel, cmd->name,
5556 : TRIGGER_FIRES_ALWAYS, false,
5557 44 : cmd->recurse,
5558 : lockmode);
5559 44 : break;
5560 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5561 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5562 : TRIGGER_FIRES_ON_REPLICA, false,
5563 16 : cmd->recurse,
5564 : lockmode);
5565 16 : break;
5566 140 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5567 140 : ATExecEnableDisableTrigger(rel, cmd->name,
5568 : TRIGGER_DISABLED, false,
5569 140 : cmd->recurse,
5570 : lockmode);
5571 140 : break;
5572 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5573 0 : ATExecEnableDisableTrigger(rel, NULL,
5574 : TRIGGER_FIRES_ON_ORIGIN, false,
5575 0 : cmd->recurse,
5576 : lockmode);
5577 0 : break;
5578 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5579 12 : ATExecEnableDisableTrigger(rel, NULL,
5580 : TRIGGER_DISABLED, false,
5581 12 : cmd->recurse,
5582 : lockmode);
5583 12 : break;
5584 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5585 0 : ATExecEnableDisableTrigger(rel, NULL,
5586 : TRIGGER_FIRES_ON_ORIGIN, true,
5587 0 : cmd->recurse,
5588 : lockmode);
5589 0 : break;
5590 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5591 12 : ATExecEnableDisableTrigger(rel, NULL,
5592 : TRIGGER_DISABLED, true,
5593 12 : cmd->recurse,
5594 : lockmode);
5595 12 : break;
5596 :
5597 8 : case AT_EnableRule: /* ENABLE RULE name */
5598 8 : ATExecEnableDisableRule(rel, cmd->name,
5599 : RULE_FIRES_ON_ORIGIN, lockmode);
5600 8 : break;
5601 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5602 0 : ATExecEnableDisableRule(rel, cmd->name,
5603 : RULE_FIRES_ALWAYS, lockmode);
5604 0 : break;
5605 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5606 6 : ATExecEnableDisableRule(rel, cmd->name,
5607 : RULE_FIRES_ON_REPLICA, lockmode);
5608 6 : break;
5609 38 : case AT_DisableRule: /* DISABLE RULE name */
5610 38 : ATExecEnableDisableRule(rel, cmd->name,
5611 : RULE_DISABLED, lockmode);
5612 38 : break;
5613 :
5614 418 : case AT_AddInherit:
5615 418 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5616 298 : break;
5617 86 : case AT_DropInherit:
5618 86 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5619 80 : break;
5620 66 : case AT_AddOf:
5621 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5622 30 : break;
5623 6 : case AT_DropOf:
5624 6 : ATExecDropOf(rel, lockmode);
5625 6 : break;
5626 508 : case AT_ReplicaIdentity:
5627 508 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5628 460 : break;
5629 302 : case AT_EnableRowSecurity:
5630 302 : ATExecSetRowSecurity(rel, true);
5631 302 : break;
5632 10 : case AT_DisableRowSecurity:
5633 10 : ATExecSetRowSecurity(rel, false);
5634 10 : break;
5635 96 : case AT_ForceRowSecurity:
5636 96 : ATExecForceNoForceRowSecurity(rel, true);
5637 96 : break;
5638 32 : case AT_NoForceRowSecurity:
5639 32 : ATExecForceNoForceRowSecurity(rel, false);
5640 32 : break;
5641 58 : case AT_GenericOptions:
5642 58 : ATExecGenericOptions(rel, (List *) cmd->def);
5643 56 : break;
5644 3296 : case AT_AttachPartition:
5645 3296 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5646 : cur_pass, context);
5647 : Assert(cmd != NULL);
5648 3272 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5649 2784 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5650 : context);
5651 : else
5652 488 : address = ATExecAttachPartitionIdx(wqueue, rel,
5653 488 : ((PartitionCmd *) cmd->def)->name);
5654 2882 : break;
5655 558 : case AT_DetachPartition:
5656 558 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5657 : cur_pass, context);
5658 : Assert(cmd != NULL);
5659 : /* ATPrepCmd ensures it must be a table */
5660 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5661 558 : address = ATExecDetachPartition(wqueue, tab, rel,
5662 558 : ((PartitionCmd *) cmd->def)->name,
5663 558 : ((PartitionCmd *) cmd->def)->concurrent);
5664 428 : break;
5665 14 : case AT_DetachPartitionFinalize:
5666 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5667 14 : break;
5668 0 : default: /* oops */
5669 0 : elog(ERROR, "unrecognized alter table type: %d",
5670 : (int) cmd->subtype);
5671 : break;
5672 : }
5673 :
5674 : /*
5675 : * Report the subcommand to interested event triggers.
5676 : */
5677 58038 : if (cmd)
5678 42036 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5679 :
5680 : /*
5681 : * Bump the command counter to ensure the next subcommand in the sequence
5682 : * can see the changes so far
5683 : */
5684 58038 : CommandCounterIncrement();
5685 58038 : }
5686 :
5687 : /*
5688 : * ATParseTransformCmd: perform parse transformation for one subcommand
5689 : *
5690 : * Returns the transformed subcommand tree, if there is one, else NULL.
5691 : *
5692 : * The parser may hand back additional AlterTableCmd(s) and/or other
5693 : * utility statements, either before or after the original subcommand.
5694 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5695 : * AlteredTableInfo (they had better be for later passes than the current one).
5696 : * Utility statements that are supposed to happen before the AlterTableCmd
5697 : * are executed immediately. Those that are supposed to happen afterwards
5698 : * are added to the tab->afterStmts list to be done at the very end.
5699 : */
5700 : static AlterTableCmd *
5701 23490 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5702 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5703 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5704 : {
5705 23490 : AlterTableCmd *newcmd = NULL;
5706 23490 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5707 : List *beforeStmts;
5708 : List *afterStmts;
5709 : ListCell *lc;
5710 :
5711 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5712 23490 : atstmt->relation =
5713 23490 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5714 23490 : pstrdup(RelationGetRelationName(rel)),
5715 : -1);
5716 23490 : atstmt->relation->inh = recurse;
5717 23490 : atstmt->cmds = list_make1(cmd);
5718 23490 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5719 23490 : atstmt->missing_ok = false;
5720 :
5721 : /* Transform the AlterTableStmt */
5722 23490 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5723 : atstmt,
5724 : context->queryString,
5725 : &beforeStmts,
5726 : &afterStmts);
5727 :
5728 : /* Execute any statements that should happen before these subcommand(s) */
5729 23986 : foreach(lc, beforeStmts)
5730 : {
5731 574 : Node *stmt = (Node *) lfirst(lc);
5732 :
5733 574 : ProcessUtilityForAlterTable(stmt, context);
5734 562 : CommandCounterIncrement();
5735 : }
5736 :
5737 : /* Examine the transformed subcommands and schedule them appropriately */
5738 55006 : foreach(lc, atstmt->cmds)
5739 : {
5740 31594 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5741 : AlterTablePass pass;
5742 :
5743 : /*
5744 : * This switch need only cover the subcommand types that can be added
5745 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5746 : * executing the subcommand immediately, as a substitute for the
5747 : * original subcommand. (Note, however, that this does cause
5748 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5749 : * which is important for index and foreign key constraints.)
5750 : *
5751 : * We assume we needn't do any phase-1 checks for added subcommands.
5752 : */
5753 31594 : switch (cmd2->subtype)
5754 : {
5755 1478 : case AT_AddIndex:
5756 1478 : pass = AT_PASS_ADD_INDEX;
5757 1478 : break;
5758 10420 : case AT_AddIndexConstraint:
5759 10420 : pass = AT_PASS_ADD_INDEXCONSTR;
5760 10420 : break;
5761 12286 : case AT_AddConstraint:
5762 : /* Recursion occurs during execution phase */
5763 12286 : if (recurse)
5764 12212 : cmd2->recurse = true;
5765 12286 : switch (castNode(Constraint, cmd2->def)->contype)
5766 : {
5767 8812 : case CONSTR_NOTNULL:
5768 8812 : pass = AT_PASS_COL_ATTRS;
5769 8812 : break;
5770 0 : case CONSTR_PRIMARY:
5771 : case CONSTR_UNIQUE:
5772 : case CONSTR_EXCLUSION:
5773 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5774 0 : break;
5775 3474 : default:
5776 3474 : pass = AT_PASS_ADD_OTHERCONSTR;
5777 3474 : break;
5778 : }
5779 12286 : break;
5780 0 : case AT_AlterColumnGenericOptions:
5781 : /* This command never recurses */
5782 : /* No command-specific prep needed */
5783 0 : pass = AT_PASS_MISC;
5784 0 : break;
5785 7410 : default:
5786 7410 : pass = cur_pass;
5787 7410 : break;
5788 : }
5789 :
5790 31594 : if (pass < cur_pass)
5791 : {
5792 : /* Cannot schedule into a pass we already finished */
5793 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5794 : pass);
5795 : }
5796 31594 : else if (pass > cur_pass)
5797 : {
5798 : /* OK, queue it up for later */
5799 24184 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5800 : }
5801 : else
5802 : {
5803 : /*
5804 : * We should see at most one subcommand for the current pass,
5805 : * which is the transformed version of the original subcommand.
5806 : */
5807 7410 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5808 : {
5809 : /* Found the transformed version of our subcommand */
5810 7410 : newcmd = cmd2;
5811 : }
5812 : else
5813 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5814 : pass);
5815 : }
5816 : }
5817 :
5818 : /* Queue up any after-statements to happen at the end */
5819 23412 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5820 :
5821 23412 : return newcmd;
5822 : }
5823 :
5824 : /*
5825 : * ATRewriteTables: ALTER TABLE phase 3
5826 : */
5827 : static void
5828 30986 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5829 : AlterTableUtilityContext *context)
5830 : {
5831 : ListCell *ltab;
5832 :
5833 : /* Go through each table that needs to be checked or rewritten */
5834 66142 : foreach(ltab, *wqueue)
5835 : {
5836 35502 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5837 :
5838 : /* Relations without storage may be ignored here */
5839 35502 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5840 6586 : continue;
5841 :
5842 : /*
5843 : * If we change column data types, the operation has to be propagated
5844 : * to tables that use this table's rowtype as a column type.
5845 : * tab->newvals will also be non-NULL in the case where we're adding a
5846 : * column with a default. We choose to forbid that case as well,
5847 : * since composite types might eventually support defaults.
5848 : *
5849 : * (Eventually we'll probably need to check for composite type
5850 : * dependencies even when we're just scanning the table without a
5851 : * rewrite, but at the moment a composite type does not enforce any
5852 : * constraints, so it's not necessary/appropriate to enforce them just
5853 : * during ALTER.)
5854 : */
5855 28916 : if (tab->newvals != NIL || tab->rewrite > 0)
5856 : {
5857 : Relation rel;
5858 :
5859 1692 : rel = table_open(tab->relid, NoLock);
5860 1692 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5861 1674 : table_close(rel, NoLock);
5862 : }
5863 :
5864 : /*
5865 : * We only need to rewrite the table if at least one column needs to
5866 : * be recomputed, or we are changing its persistence or access method.
5867 : *
5868 : * There are two reasons for requiring a rewrite when changing
5869 : * persistence: on one hand, we need to ensure that the buffers
5870 : * belonging to each of the two relations are marked with or without
5871 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5872 : * and assigns a new relfilenumber, we automatically create or drop an
5873 : * init fork for the relation as appropriate.
5874 : */
5875 28898 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5876 928 : {
5877 : /* Build a temporary relation and copy data */
5878 : Relation OldHeap;
5879 : Oid OIDNewHeap;
5880 : Oid NewAccessMethod;
5881 : Oid NewTableSpace;
5882 : char persistence;
5883 :
5884 984 : OldHeap = table_open(tab->relid, NoLock);
5885 :
5886 : /*
5887 : * We don't support rewriting of system catalogs; there are too
5888 : * many corner cases and too little benefit. In particular this
5889 : * is certainly not going to work for mapped catalogs.
5890 : */
5891 984 : if (IsSystemRelation(OldHeap))
5892 0 : ereport(ERROR,
5893 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5894 : errmsg("cannot rewrite system relation \"%s\"",
5895 : RelationGetRelationName(OldHeap))));
5896 :
5897 984 : if (RelationIsUsedAsCatalogTable(OldHeap))
5898 2 : ereport(ERROR,
5899 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5900 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5901 : RelationGetRelationName(OldHeap))));
5902 :
5903 : /*
5904 : * Don't allow rewrite on temp tables of other backends ... their
5905 : * local buffer manager is not going to cope. (This is redundant
5906 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5907 : * check here too.)
5908 : */
5909 982 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5910 0 : ereport(ERROR,
5911 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5912 : errmsg("cannot rewrite temporary tables of other sessions")));
5913 :
5914 : /*
5915 : * Select destination tablespace (same as original unless user
5916 : * requested a change)
5917 : */
5918 982 : if (tab->newTableSpace)
5919 0 : NewTableSpace = tab->newTableSpace;
5920 : else
5921 982 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5922 :
5923 : /*
5924 : * Select destination access method (same as original unless user
5925 : * requested a change)
5926 : */
5927 982 : if (tab->chgAccessMethod)
5928 36 : NewAccessMethod = tab->newAccessMethod;
5929 : else
5930 946 : NewAccessMethod = OldHeap->rd_rel->relam;
5931 :
5932 : /*
5933 : * Select persistence of transient table (same as original unless
5934 : * user requested a change)
5935 : */
5936 982 : persistence = tab->chgPersistence ?
5937 930 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5938 :
5939 982 : table_close(OldHeap, NoLock);
5940 :
5941 : /*
5942 : * Fire off an Event Trigger now, before actually rewriting the
5943 : * table.
5944 : *
5945 : * We don't support Event Trigger for nested commands anywhere,
5946 : * here included, and parsetree is given NULL when coming from
5947 : * AlterTableInternal.
5948 : *
5949 : * And fire it only once.
5950 : */
5951 982 : if (parsetree)
5952 982 : EventTriggerTableRewrite((Node *) parsetree,
5953 : tab->relid,
5954 : tab->rewrite);
5955 :
5956 : /*
5957 : * Create transient table that will receive the modified data.
5958 : *
5959 : * Ensure it is marked correctly as logged or unlogged. We have
5960 : * to do this here so that buffers for the new relfilenumber will
5961 : * have the right persistence set, and at the same time ensure
5962 : * that the original filenumbers's buffers will get read in with
5963 : * the correct setting (i.e. the original one). Otherwise a
5964 : * rollback after the rewrite would possibly result with buffers
5965 : * for the original filenumbers having the wrong persistence
5966 : * setting.
5967 : *
5968 : * NB: This relies on swap_relation_files() also swapping the
5969 : * persistence. That wouldn't work for pg_class, but that can't be
5970 : * unlogged anyway.
5971 : */
5972 976 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5973 : persistence, lockmode);
5974 :
5975 : /*
5976 : * Copy the heap data into the new table with the desired
5977 : * modifications, and test the current data within the table
5978 : * against new constraints generated by ALTER TABLE commands.
5979 : */
5980 976 : ATRewriteTable(tab, OIDNewHeap);
5981 :
5982 : /*
5983 : * Swap the physical files of the old and new heaps, then rebuild
5984 : * indexes and discard the old heap. We can use RecentXmin for
5985 : * the table's new relfrozenxid because we rewrote all the tuples
5986 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5987 : * we never try to swap toast tables by content, since we have no
5988 : * interest in letting this code work on system catalogs.
5989 : */
5990 934 : finish_heap_swap(tab->relid, OIDNewHeap,
5991 : false, false, true,
5992 934 : !OidIsValid(tab->newTableSpace),
5993 : RecentXmin,
5994 : ReadNextMultiXactId(),
5995 : persistence);
5996 :
5997 928 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5998 : }
5999 27914 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6000 : {
6001 24 : if (tab->chgPersistence)
6002 24 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
6003 : }
6004 : else
6005 : {
6006 : /*
6007 : * If required, test the current data within the table against new
6008 : * constraints generated by ALTER TABLE commands, but don't
6009 : * rebuild data.
6010 : */
6011 27890 : if (tab->constraints != NIL || tab->verify_new_notnull ||
6012 25004 : tab->partition_constraint != NULL)
6013 5134 : ATRewriteTable(tab, InvalidOid);
6014 :
6015 : /*
6016 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
6017 : * just do a block-by-block copy.
6018 : */
6019 27618 : if (tab->newTableSpace)
6020 124 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6021 : }
6022 :
6023 : /*
6024 : * Also change persistence of owned sequences, so that it matches the
6025 : * table persistence.
6026 : */
6027 28570 : if (tab->chgPersistence)
6028 : {
6029 76 : List *seqlist = getOwnedSequences(tab->relid);
6030 : ListCell *lc;
6031 :
6032 124 : foreach(lc, seqlist)
6033 : {
6034 48 : Oid seq_relid = lfirst_oid(lc);
6035 :
6036 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
6037 : }
6038 : }
6039 : }
6040 :
6041 : /*
6042 : * Foreign key constraints are checked in a final pass, since (a) it's
6043 : * generally best to examine each one separately, and (b) it's at least
6044 : * theoretically possible that we have changed both relations of the
6045 : * foreign key, and we'd better have finished both rewrites before we try
6046 : * to read the tables.
6047 : */
6048 65538 : foreach(ltab, *wqueue)
6049 : {
6050 34990 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6051 34990 : Relation rel = NULL;
6052 : ListCell *lcon;
6053 :
6054 : /* Relations without storage may be ignored here too */
6055 34990 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6056 6482 : continue;
6057 :
6058 30342 : foreach(lcon, tab->constraints)
6059 : {
6060 1926 : NewConstraint *con = lfirst(lcon);
6061 :
6062 1926 : if (con->contype == CONSTR_FOREIGN)
6063 : {
6064 1212 : Constraint *fkconstraint = (Constraint *) con->qual;
6065 : Relation refrel;
6066 :
6067 1212 : if (rel == NULL)
6068 : {
6069 : /* Long since locked, no need for another */
6070 1194 : rel = table_open(tab->relid, NoLock);
6071 : }
6072 :
6073 1212 : refrel = table_open(con->refrelid, RowShareLock);
6074 :
6075 1212 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6076 : con->refindid,
6077 : con->conid,
6078 1212 : con->conwithperiod);
6079 :
6080 : /*
6081 : * No need to mark the constraint row as validated, we did
6082 : * that when we inserted the row earlier.
6083 : */
6084 :
6085 1120 : table_close(refrel, NoLock);
6086 : }
6087 : }
6088 :
6089 28416 : if (rel)
6090 1102 : table_close(rel, NoLock);
6091 : }
6092 :
6093 : /* Finally, run any afterStmts that were queued up */
6094 65402 : foreach(ltab, *wqueue)
6095 : {
6096 34854 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6097 : ListCell *lc;
6098 :
6099 34940 : foreach(lc, tab->afterStmts)
6100 : {
6101 86 : Node *stmt = (Node *) lfirst(lc);
6102 :
6103 86 : ProcessUtilityForAlterTable(stmt, context);
6104 86 : CommandCounterIncrement();
6105 : }
6106 : }
6107 30548 : }
6108 :
6109 : /*
6110 : * ATRewriteTable: scan or rewrite one table
6111 : *
6112 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6113 : * must already hold AccessExclusiveLock on it.
6114 : */
6115 : static void
6116 6110 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6117 : {
6118 : Relation oldrel;
6119 : Relation newrel;
6120 : TupleDesc oldTupDesc;
6121 : TupleDesc newTupDesc;
6122 6110 : bool needscan = false;
6123 : List *notnull_attrs;
6124 : List *notnull_virtual_attrs;
6125 : int i;
6126 : ListCell *l;
6127 : EState *estate;
6128 : CommandId mycid;
6129 : BulkInsertState bistate;
6130 : int ti_options;
6131 6110 : ExprState *partqualstate = NULL;
6132 :
6133 : /*
6134 : * Open the relation(s). We have surely already locked the existing
6135 : * table.
6136 : */
6137 6110 : oldrel = table_open(tab->relid, NoLock);
6138 6110 : oldTupDesc = tab->oldDesc;
6139 6110 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6140 :
6141 6110 : if (OidIsValid(OIDNewHeap))
6142 : {
6143 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6144 : false));
6145 976 : newrel = table_open(OIDNewHeap, NoLock);
6146 : }
6147 : else
6148 5134 : newrel = NULL;
6149 :
6150 : /*
6151 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6152 : * is empty, so don't bother using it.
6153 : */
6154 6110 : if (newrel)
6155 : {
6156 976 : mycid = GetCurrentCommandId(true);
6157 976 : bistate = GetBulkInsertState();
6158 976 : ti_options = TABLE_INSERT_SKIP_FSM;
6159 : }
6160 : else
6161 : {
6162 : /* keep compiler quiet about using these uninitialized */
6163 5134 : mycid = 0;
6164 5134 : bistate = NULL;
6165 5134 : ti_options = 0;
6166 : }
6167 :
6168 : /*
6169 : * Generate the constraint and default execution states
6170 : */
6171 :
6172 6110 : estate = CreateExecutorState();
6173 :
6174 : /* Build the needed expression execution states */
6175 8156 : foreach(l, tab->constraints)
6176 : {
6177 2046 : NewConstraint *con = lfirst(l);
6178 :
6179 2046 : switch (con->contype)
6180 : {
6181 828 : case CONSTR_CHECK:
6182 828 : needscan = true;
6183 828 : con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6184 828 : break;
6185 1218 : case CONSTR_FOREIGN:
6186 : /* Nothing to do here */
6187 1218 : break;
6188 0 : default:
6189 0 : elog(ERROR, "unrecognized constraint type: %d",
6190 : (int) con->contype);
6191 : }
6192 : }
6193 :
6194 : /* Build expression execution states for partition check quals */
6195 6110 : if (tab->partition_constraint)
6196 : {
6197 2396 : needscan = true;
6198 2396 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6199 : }
6200 :
6201 7148 : foreach(l, tab->newvals)
6202 : {
6203 1038 : NewColumnValue *ex = lfirst(l);
6204 :
6205 : /* expr already planned */
6206 1038 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6207 : }
6208 :
6209 6110 : notnull_attrs = notnull_virtual_attrs = NIL;
6210 6110 : if (newrel || tab->verify_new_notnull)
6211 : {
6212 : /*
6213 : * If we are rebuilding the tuples OR if we added any new but not
6214 : * verified not-null constraints, check all *valid* not-null
6215 : * constraints. This is a bit of overkill but it minimizes risk of
6216 : * bugs.
6217 : *
6218 : * notnull_attrs does *not* collect attribute numbers for valid
6219 : * not-null constraints over virtual generated columns; instead, they
6220 : * are collected in notnull_virtual_attrs for verification elsewhere.
6221 : */
6222 7434 : for (i = 0; i < newTupDesc->natts; i++)
6223 : {
6224 5442 : CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6225 :
6226 5442 : if (attr->attnullability == ATTNULLABLE_VALID &&
6227 2090 : !attr->attisdropped)
6228 : {
6229 2090 : Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6230 :
6231 2090 : if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6232 2000 : notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6233 : else
6234 90 : notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6235 90 : wholeatt->attnum);
6236 : }
6237 : }
6238 1992 : if (notnull_attrs || notnull_virtual_attrs)
6239 1528 : needscan = true;
6240 : }
6241 :
6242 6110 : if (newrel || needscan)
6243 : {
6244 : ExprContext *econtext;
6245 : TupleTableSlot *oldslot;
6246 : TupleTableSlot *newslot;
6247 : TableScanDesc scan;
6248 : MemoryContext oldCxt;
6249 5100 : List *dropped_attrs = NIL;
6250 : ListCell *lc;
6251 : Snapshot snapshot;
6252 5100 : ResultRelInfo *rInfo = NULL;
6253 :
6254 : /*
6255 : * When adding or changing a virtual generated column with a not-null
6256 : * constraint, we need to evaluate whether the generation expression
6257 : * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6258 : * prepare a dummy ResultRelInfo.
6259 : */
6260 5100 : if (notnull_virtual_attrs != NIL)
6261 : {
6262 : MemoryContext oldcontext;
6263 :
6264 : Assert(newTupDesc->constr->has_generated_virtual);
6265 : Assert(newTupDesc->constr->has_not_null);
6266 60 : oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6267 60 : rInfo = makeNode(ResultRelInfo);
6268 60 : InitResultRelInfo(rInfo,
6269 : oldrel,
6270 : 0, /* dummy rangetable index */
6271 : NULL,
6272 : estate->es_instrument);
6273 60 : MemoryContextSwitchTo(oldcontext);
6274 : }
6275 :
6276 5100 : if (newrel)
6277 976 : ereport(DEBUG1,
6278 : (errmsg_internal("rewriting table \"%s\"",
6279 : RelationGetRelationName(oldrel))));
6280 : else
6281 4124 : ereport(DEBUG1,
6282 : (errmsg_internal("verifying table \"%s\"",
6283 : RelationGetRelationName(oldrel))));
6284 :
6285 5100 : if (newrel)
6286 : {
6287 : /*
6288 : * All predicate locks on the tuples or pages are about to be made
6289 : * invalid, because we move tuples around. Promote them to
6290 : * relation locks.
6291 : */
6292 976 : TransferPredicateLocksToHeapRelation(oldrel);
6293 : }
6294 :
6295 5100 : econtext = GetPerTupleExprContext(estate);
6296 :
6297 : /*
6298 : * Create necessary tuple slots. When rewriting, two slots are needed,
6299 : * otherwise one suffices. In the case where one slot suffices, we
6300 : * need to use the new tuple descriptor, otherwise some constraints
6301 : * can't be evaluated. Note that even when the tuple layout is the
6302 : * same and no rewrite is required, the tupDescs might not be
6303 : * (consider ADD COLUMN without a default).
6304 : */
6305 5100 : if (tab->rewrite)
6306 : {
6307 : Assert(newrel != NULL);
6308 976 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6309 : table_slot_callbacks(oldrel));
6310 976 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6311 : table_slot_callbacks(newrel));
6312 :
6313 : /*
6314 : * Set all columns in the new slot to NULL initially, to ensure
6315 : * columns added as part of the rewrite are initialized to NULL.
6316 : * That is necessary as tab->newvals will not contain an
6317 : * expression for columns with a NULL default, e.g. when adding a
6318 : * column without a default together with a column with a default
6319 : * requiring an actual rewrite.
6320 : */
6321 976 : ExecStoreAllNullTuple(newslot);
6322 : }
6323 : else
6324 : {
6325 4124 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6326 : table_slot_callbacks(oldrel));
6327 4124 : newslot = NULL;
6328 : }
6329 :
6330 : /*
6331 : * Any attributes that are dropped according to the new tuple
6332 : * descriptor can be set to NULL. We precompute the list of dropped
6333 : * attributes to avoid needing to do so in the per-tuple loop.
6334 : */
6335 17986 : for (i = 0; i < newTupDesc->natts; i++)
6336 : {
6337 12886 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6338 790 : dropped_attrs = lappend_int(dropped_attrs, i);
6339 : }
6340 :
6341 : /*
6342 : * Scan through the rows, generating a new row if needed and then
6343 : * checking all the constraints.
6344 : */
6345 5100 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6346 5100 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6347 :
6348 : /*
6349 : * Switch to per-tuple memory context and reset it for each tuple
6350 : * produced, so we don't leak memory.
6351 : */
6352 5100 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6353 :
6354 769958 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6355 : {
6356 : TupleTableSlot *insertslot;
6357 :
6358 765172 : if (tab->rewrite > 0)
6359 : {
6360 : /* Extract data from old tuple */
6361 99822 : slot_getallattrs(oldslot);
6362 99822 : ExecClearTuple(newslot);
6363 :
6364 : /* copy attributes */
6365 99822 : memcpy(newslot->tts_values, oldslot->tts_values,
6366 99822 : sizeof(Datum) * oldslot->tts_nvalid);
6367 99822 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6368 99822 : sizeof(bool) * oldslot->tts_nvalid);
6369 :
6370 : /* Set dropped attributes to null in new tuple */
6371 99938 : foreach(lc, dropped_attrs)
6372 116 : newslot->tts_isnull[lfirst_int(lc)] = true;
6373 :
6374 : /*
6375 : * Constraints and GENERATED expressions might reference the
6376 : * tableoid column, so fill tts_tableOid with the desired
6377 : * value. (We must do this each time, because it gets
6378 : * overwritten with newrel's OID during storing.)
6379 : */
6380 99822 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6381 :
6382 : /*
6383 : * Process supplied expressions to replace selected columns.
6384 : *
6385 : * First, evaluate expressions whose inputs come from the old
6386 : * tuple.
6387 : */
6388 99822 : econtext->ecxt_scantuple = oldslot;
6389 :
6390 205596 : foreach(l, tab->newvals)
6391 : {
6392 105786 : NewColumnValue *ex = lfirst(l);
6393 :
6394 105786 : if (ex->is_generated)
6395 312 : continue;
6396 :
6397 105474 : newslot->tts_values[ex->attnum - 1]
6398 105462 : = ExecEvalExpr(ex->exprstate,
6399 : econtext,
6400 105474 : &newslot->tts_isnull[ex->attnum - 1]);
6401 : }
6402 :
6403 99810 : ExecStoreVirtualTuple(newslot);
6404 :
6405 : /*
6406 : * Now, evaluate any expressions whose inputs come from the
6407 : * new tuple. We assume these columns won't reference each
6408 : * other, so that there's no ordering dependency.
6409 : */
6410 99810 : econtext->ecxt_scantuple = newslot;
6411 :
6412 205584 : foreach(l, tab->newvals)
6413 : {
6414 105774 : NewColumnValue *ex = lfirst(l);
6415 :
6416 105774 : if (!ex->is_generated)
6417 105462 : continue;
6418 :
6419 312 : newslot->tts_values[ex->attnum - 1]
6420 312 : = ExecEvalExpr(ex->exprstate,
6421 : econtext,
6422 312 : &newslot->tts_isnull[ex->attnum - 1]);
6423 : }
6424 :
6425 99810 : insertslot = newslot;
6426 : }
6427 : else
6428 : {
6429 : /*
6430 : * If there's no rewrite, old and new table are guaranteed to
6431 : * have the same AM, so we can just use the old slot to verify
6432 : * new constraints etc.
6433 : */
6434 665350 : insertslot = oldslot;
6435 : }
6436 :
6437 : /* Now check any constraints on the possibly-changed tuple */
6438 765160 : econtext->ecxt_scantuple = insertslot;
6439 :
6440 4106578 : foreach_int(attn, notnull_attrs)
6441 : {
6442 2576462 : if (slot_attisnull(insertslot, attn))
6443 : {
6444 102 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6445 :
6446 102 : ereport(ERROR,
6447 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6448 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6449 : NameStr(attr->attname),
6450 : RelationGetRelationName(oldrel)),
6451 : errtablecol(oldrel, attn)));
6452 : }
6453 : }
6454 :
6455 765058 : if (notnull_virtual_attrs != NIL)
6456 : {
6457 : AttrNumber attnum;
6458 :
6459 84 : attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6460 : estate,
6461 : notnull_virtual_attrs);
6462 84 : if (attnum != InvalidAttrNumber)
6463 : {
6464 30 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6465 :
6466 30 : ereport(ERROR,
6467 : errcode(ERRCODE_NOT_NULL_VIOLATION),
6468 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6469 : NameStr(attr->attname),
6470 : RelationGetRelationName(oldrel)),
6471 : errtablecol(oldrel, attnum));
6472 : }
6473 : }
6474 :
6475 773186 : foreach(l, tab->constraints)
6476 : {
6477 8254 : NewConstraint *con = lfirst(l);
6478 :
6479 8254 : switch (con->contype)
6480 : {
6481 8148 : case CONSTR_CHECK:
6482 8148 : if (!ExecCheck(con->qualstate, econtext))
6483 96 : ereport(ERROR,
6484 : (errcode(ERRCODE_CHECK_VIOLATION),
6485 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6486 : con->name,
6487 : RelationGetRelationName(oldrel)),
6488 : errtableconstraint(oldrel, con->name)));
6489 8052 : break;
6490 106 : case CONSTR_NOTNULL:
6491 : case CONSTR_FOREIGN:
6492 : /* Nothing to do here */
6493 106 : break;
6494 0 : default:
6495 0 : elog(ERROR, "unrecognized constraint type: %d",
6496 : (int) con->contype);
6497 : }
6498 : }
6499 :
6500 764932 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6501 : {
6502 74 : if (tab->validate_default)
6503 26 : ereport(ERROR,
6504 : (errcode(ERRCODE_CHECK_VIOLATION),
6505 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6506 : RelationGetRelationName(oldrel)),
6507 : errtable(oldrel)));
6508 : else
6509 48 : ereport(ERROR,
6510 : (errcode(ERRCODE_CHECK_VIOLATION),
6511 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6512 : RelationGetRelationName(oldrel)),
6513 : errtable(oldrel)));
6514 : }
6515 :
6516 : /* Write the tuple out to the new relation */
6517 764858 : if (newrel)
6518 99780 : table_tuple_insert(newrel, insertslot, mycid,
6519 : ti_options, bistate);
6520 :
6521 764858 : ResetExprContext(econtext);
6522 :
6523 764858 : CHECK_FOR_INTERRUPTS();
6524 : }
6525 :
6526 4786 : MemoryContextSwitchTo(oldCxt);
6527 4786 : table_endscan(scan);
6528 4786 : UnregisterSnapshot(snapshot);
6529 :
6530 4786 : ExecDropSingleTupleTableSlot(oldslot);
6531 4786 : if (newslot)
6532 934 : ExecDropSingleTupleTableSlot(newslot);
6533 : }
6534 :
6535 5796 : FreeExecutorState(estate);
6536 :
6537 5796 : table_close(oldrel, NoLock);
6538 5796 : if (newrel)
6539 : {
6540 934 : FreeBulkInsertState(bistate);
6541 :
6542 934 : table_finish_bulk_insert(newrel, ti_options);
6543 :
6544 934 : table_close(newrel, NoLock);
6545 : }
6546 5796 : }
6547 :
6548 : /*
6549 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6550 : */
6551 : static AlteredTableInfo *
6552 43796 : ATGetQueueEntry(List **wqueue, Relation rel)
6553 : {
6554 43796 : Oid relid = RelationGetRelid(rel);
6555 : AlteredTableInfo *tab;
6556 : ListCell *ltab;
6557 :
6558 53908 : foreach(ltab, *wqueue)
6559 : {
6560 14964 : tab = (AlteredTableInfo *) lfirst(ltab);
6561 14964 : if (tab->relid == relid)
6562 4852 : return tab;
6563 : }
6564 :
6565 : /*
6566 : * Not there, so add it. Note that we make a copy of the relation's
6567 : * existing descriptor before anything interesting can happen to it.
6568 : */
6569 38944 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6570 38944 : tab->relid = relid;
6571 38944 : tab->rel = NULL; /* set later */
6572 38944 : tab->relkind = rel->rd_rel->relkind;
6573 38944 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6574 38944 : tab->newAccessMethod = InvalidOid;
6575 38944 : tab->chgAccessMethod = false;
6576 38944 : tab->newTableSpace = InvalidOid;
6577 38944 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6578 38944 : tab->chgPersistence = false;
6579 :
6580 38944 : *wqueue = lappend(*wqueue, tab);
6581 :
6582 38944 : return tab;
6583 : }
6584 :
6585 : static const char *
6586 80 : alter_table_type_to_string(AlterTableType cmdtype)
6587 : {
6588 80 : switch (cmdtype)
6589 : {
6590 0 : case AT_AddColumn:
6591 : case AT_AddColumnToView:
6592 0 : return "ADD COLUMN";
6593 0 : case AT_ColumnDefault:
6594 : case AT_CookedColumnDefault:
6595 0 : return "ALTER COLUMN ... SET DEFAULT";
6596 6 : case AT_DropNotNull:
6597 6 : return "ALTER COLUMN ... DROP NOT NULL";
6598 6 : case AT_SetNotNull:
6599 6 : return "ALTER COLUMN ... SET NOT NULL";
6600 0 : case AT_SetExpression:
6601 0 : return "ALTER COLUMN ... SET EXPRESSION";
6602 0 : case AT_DropExpression:
6603 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6604 0 : case AT_SetStatistics:
6605 0 : return "ALTER COLUMN ... SET STATISTICS";
6606 12 : case AT_SetOptions:
6607 12 : return "ALTER COLUMN ... SET";
6608 0 : case AT_ResetOptions:
6609 0 : return "ALTER COLUMN ... RESET";
6610 0 : case AT_SetStorage:
6611 0 : return "ALTER COLUMN ... SET STORAGE";
6612 0 : case AT_SetCompression:
6613 0 : return "ALTER COLUMN ... SET COMPRESSION";
6614 6 : case AT_DropColumn:
6615 6 : return "DROP COLUMN";
6616 0 : case AT_AddIndex:
6617 : case AT_ReAddIndex:
6618 0 : return NULL; /* not real grammar */
6619 0 : case AT_AddConstraint:
6620 : case AT_ReAddConstraint:
6621 : case AT_ReAddDomainConstraint:
6622 : case AT_AddIndexConstraint:
6623 0 : return "ADD CONSTRAINT";
6624 6 : case AT_AlterConstraint:
6625 6 : return "ALTER CONSTRAINT";
6626 0 : case AT_ValidateConstraint:
6627 0 : return "VALIDATE CONSTRAINT";
6628 0 : case AT_DropConstraint:
6629 0 : return "DROP CONSTRAINT";
6630 0 : case AT_ReAddComment:
6631 0 : return NULL; /* not real grammar */
6632 0 : case AT_AlterColumnType:
6633 0 : return "ALTER COLUMN ... SET DATA TYPE";
6634 0 : case AT_AlterColumnGenericOptions:
6635 0 : return "ALTER COLUMN ... OPTIONS";
6636 0 : case AT_ChangeOwner:
6637 0 : return "OWNER TO";
6638 0 : case AT_ClusterOn:
6639 0 : return "CLUSTER ON";
6640 0 : case AT_DropCluster:
6641 0 : return "SET WITHOUT CLUSTER";
6642 0 : case AT_SetAccessMethod:
6643 0 : return "SET ACCESS METHOD";
6644 6 : case AT_SetLogged:
6645 6 : return "SET LOGGED";
6646 6 : case AT_SetUnLogged:
6647 6 : return "SET UNLOGGED";
6648 0 : case AT_DropOids:
6649 0 : return "SET WITHOUT OIDS";
6650 0 : case AT_SetTableSpace:
6651 0 : return "SET TABLESPACE";
6652 2 : case AT_SetRelOptions:
6653 2 : return "SET";
6654 0 : case AT_ResetRelOptions:
6655 0 : return "RESET";
6656 0 : case AT_ReplaceRelOptions:
6657 0 : return NULL; /* not real grammar */
6658 0 : case AT_EnableTrig:
6659 0 : return "ENABLE TRIGGER";
6660 0 : case AT_EnableAlwaysTrig:
6661 0 : return "ENABLE ALWAYS TRIGGER";
6662 0 : case AT_EnableReplicaTrig:
6663 0 : return "ENABLE REPLICA TRIGGER";
6664 0 : case AT_DisableTrig:
6665 0 : return "DISABLE TRIGGER";
6666 0 : case AT_EnableTrigAll:
6667 0 : return "ENABLE TRIGGER ALL";
6668 0 : case AT_DisableTrigAll:
6669 0 : return "DISABLE TRIGGER ALL";
6670 0 : case AT_EnableTrigUser:
6671 0 : return "ENABLE TRIGGER USER";
6672 0 : case AT_DisableTrigUser:
6673 0 : return "DISABLE TRIGGER USER";
6674 0 : case AT_EnableRule:
6675 0 : return "ENABLE RULE";
6676 0 : case AT_EnableAlwaysRule:
6677 0 : return "ENABLE ALWAYS RULE";
6678 0 : case AT_EnableReplicaRule:
6679 0 : return "ENABLE REPLICA RULE";
6680 0 : case AT_DisableRule:
6681 0 : return "DISABLE RULE";
6682 0 : case AT_AddInherit:
6683 0 : return "INHERIT";
6684 0 : case AT_DropInherit:
6685 0 : return "NO INHERIT";
6686 0 : case AT_AddOf:
6687 0 : return "OF";
6688 0 : case AT_DropOf:
6689 0 : return "NOT OF";
6690 0 : case AT_ReplicaIdentity:
6691 0 : return "REPLICA IDENTITY";
6692 0 : case AT_EnableRowSecurity:
6693 0 : return "ENABLE ROW SECURITY";
6694 0 : case AT_DisableRowSecurity:
6695 0 : return "DISABLE ROW SECURITY";
6696 0 : case AT_ForceRowSecurity:
6697 0 : return "FORCE ROW SECURITY";
6698 0 : case AT_NoForceRowSecurity:
6699 0 : return "NO FORCE ROW SECURITY";
6700 0 : case AT_GenericOptions:
6701 0 : return "OPTIONS";
6702 6 : case AT_AttachPartition:
6703 6 : return "ATTACH PARTITION";
6704 18 : case AT_DetachPartition:
6705 18 : return "DETACH PARTITION";
6706 6 : case AT_DetachPartitionFinalize:
6707 6 : return "DETACH PARTITION ... FINALIZE";
6708 0 : case AT_AddIdentity:
6709 0 : return "ALTER COLUMN ... ADD IDENTITY";
6710 0 : case AT_SetIdentity:
6711 0 : return "ALTER COLUMN ... SET";
6712 0 : case AT_DropIdentity:
6713 0 : return "ALTER COLUMN ... DROP IDENTITY";
6714 0 : case AT_ReAddStatistics:
6715 0 : return NULL; /* not real grammar */
6716 : }
6717 :
6718 0 : return NULL;
6719 : }
6720 :
6721 : /*
6722 : * ATSimplePermissions
6723 : *
6724 : * - Ensure that it is a relation (or possibly a view)
6725 : * - Ensure this user is the owner
6726 : * - Ensure that it is not a system table
6727 : */
6728 : static void
6729 38542 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6730 : {
6731 : int actual_target;
6732 :
6733 38542 : switch (rel->rd_rel->relkind)
6734 : {
6735 30084 : case RELKIND_RELATION:
6736 30084 : actual_target = ATT_TABLE;
6737 30084 : break;
6738 6080 : case RELKIND_PARTITIONED_TABLE:
6739 6080 : actual_target = ATT_PARTITIONED_TABLE;
6740 6080 : break;
6741 406 : case RELKIND_VIEW:
6742 406 : actual_target = ATT_VIEW;
6743 406 : break;
6744 48 : case RELKIND_MATVIEW:
6745 48 : actual_target = ATT_MATVIEW;
6746 48 : break;
6747 228 : case RELKIND_INDEX:
6748 228 : actual_target = ATT_INDEX;
6749 228 : break;
6750 530 : case RELKIND_PARTITIONED_INDEX:
6751 530 : actual_target = ATT_PARTITIONED_INDEX;
6752 530 : break;
6753 214 : case RELKIND_COMPOSITE_TYPE:
6754 214 : actual_target = ATT_COMPOSITE_TYPE;
6755 214 : break;
6756 926 : case RELKIND_FOREIGN_TABLE:
6757 926 : actual_target = ATT_FOREIGN_TABLE;
6758 926 : break;
6759 24 : case RELKIND_SEQUENCE:
6760 24 : actual_target = ATT_SEQUENCE;
6761 24 : break;
6762 2 : default:
6763 2 : actual_target = 0;
6764 2 : break;
6765 : }
6766 :
6767 : /* Wrong target type? */
6768 38542 : if ((actual_target & allowed_targets) == 0)
6769 : {
6770 80 : const char *action_str = alter_table_type_to_string(cmdtype);
6771 :
6772 80 : if (action_str)
6773 80 : ereport(ERROR,
6774 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6775 : /* translator: %s is a group of some SQL keywords */
6776 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6777 : action_str, RelationGetRelationName(rel)),
6778 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6779 : else
6780 : /* internal error? */
6781 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6782 : RelationGetRelationName(rel));
6783 : }
6784 :
6785 : /* Permissions checks */
6786 38462 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6787 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6788 12 : RelationGetRelationName(rel));
6789 :
6790 38450 : if (!allowSystemTableMods && IsSystemRelation(rel))
6791 0 : ereport(ERROR,
6792 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6793 : errmsg("permission denied: \"%s\" is a system catalog",
6794 : RelationGetRelationName(rel))));
6795 38450 : }
6796 :
6797 : /*
6798 : * ATSimpleRecursion
6799 : *
6800 : * Simple table recursion sufficient for most ALTER TABLE operations.
6801 : * All direct and indirect children are processed in an unspecified order.
6802 : * Note that if a child inherits from the original table via multiple
6803 : * inheritance paths, it will be visited just once.
6804 : */
6805 : static void
6806 1324 : ATSimpleRecursion(List **wqueue, Relation rel,
6807 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6808 : AlterTableUtilityContext *context)
6809 : {
6810 : /*
6811 : * Propagate to children, if desired and if there are (or might be) any
6812 : * children.
6813 : */
6814 1324 : if (recurse && rel->rd_rel->relhassubclass)
6815 : {
6816 84 : Oid relid = RelationGetRelid(rel);
6817 : ListCell *child;
6818 : List *children;
6819 :
6820 84 : children = find_all_inheritors(relid, lockmode, NULL);
6821 :
6822 : /*
6823 : * find_all_inheritors does the recursive search of the inheritance
6824 : * hierarchy, so all we have to do is process all of the relids in the
6825 : * list that it returns.
6826 : */
6827 366 : foreach(child, children)
6828 : {
6829 282 : Oid childrelid = lfirst_oid(child);
6830 : Relation childrel;
6831 :
6832 282 : if (childrelid == relid)
6833 84 : continue;
6834 : /* find_all_inheritors already got lock */
6835 198 : childrel = relation_open(childrelid, NoLock);
6836 198 : CheckAlterTableIsSafe(childrel);
6837 198 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6838 198 : relation_close(childrel, NoLock);
6839 : }
6840 : }
6841 1324 : }
6842 :
6843 : /*
6844 : * Obtain list of partitions of the given table, locking them all at the given
6845 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6846 : *
6847 : * This function is a no-op if the given relation is not a partitioned table;
6848 : * in particular, nothing is done if it's a legacy inheritance parent.
6849 : */
6850 : static void
6851 798 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6852 : {
6853 798 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6854 : {
6855 : List *inh;
6856 : ListCell *cell;
6857 :
6858 176 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6859 : /* first element is the parent rel; must ignore it */
6860 574 : for_each_from(cell, inh, 1)
6861 : {
6862 : Relation childrel;
6863 :
6864 : /* find_all_inheritors already got lock */
6865 404 : childrel = table_open(lfirst_oid(cell), NoLock);
6866 404 : CheckAlterTableIsSafe(childrel);
6867 398 : table_close(childrel, NoLock);
6868 : }
6869 170 : list_free(inh);
6870 : }
6871 792 : }
6872 :
6873 : /*
6874 : * ATTypedTableRecursion
6875 : *
6876 : * Propagate ALTER TYPE operations to the typed tables of that type.
6877 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6878 : * recursion to inheritance children of the typed tables.
6879 : */
6880 : static void
6881 190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6882 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6883 : {
6884 : ListCell *child;
6885 : List *children;
6886 :
6887 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6888 :
6889 190 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6890 190 : RelationGetRelationName(rel),
6891 : cmd->behavior);
6892 :
6893 202 : foreach(child, children)
6894 : {
6895 30 : Oid childrelid = lfirst_oid(child);
6896 : Relation childrel;
6897 :
6898 30 : childrel = relation_open(childrelid, lockmode);
6899 30 : CheckAlterTableIsSafe(childrel);
6900 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6901 30 : relation_close(childrel, NoLock);
6902 : }
6903 172 : }
6904 :
6905 :
6906 : /*
6907 : * find_composite_type_dependencies
6908 : *
6909 : * Check to see if the type "typeOid" is being used as a column in some table
6910 : * (possibly nested several levels deep in composite types, arrays, etc!).
6911 : * Eventually, we'd like to propagate the check or rewrite operation
6912 : * into such tables, but for now, just error out if we find any.
6913 : *
6914 : * Caller should provide either the associated relation of a rowtype,
6915 : * or a type name (not both) for use in the error message, if any.
6916 : *
6917 : * Note that "typeOid" is not necessarily a composite type; it could also be
6918 : * another container type such as an array or range, or a domain over one of
6919 : * these things. The name of this function is therefore somewhat historical,
6920 : * but it's not worth changing.
6921 : *
6922 : * We assume that functions and views depending on the type are not reasons
6923 : * to reject the ALTER. (How safe is this really?)
6924 : */
6925 : void
6926 4440 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6927 : const char *origTypeName)
6928 : {
6929 : Relation depRel;
6930 : ScanKeyData key[2];
6931 : SysScanDesc depScan;
6932 : HeapTuple depTup;
6933 :
6934 : /* since this function recurses, it could be driven to stack overflow */
6935 4440 : check_stack_depth();
6936 :
6937 : /*
6938 : * We scan pg_depend to find those things that depend on the given type.
6939 : * (We assume we can ignore refobjsubid for a type.)
6940 : */
6941 4440 : depRel = table_open(DependRelationId, AccessShareLock);
6942 :
6943 4440 : ScanKeyInit(&key[0],
6944 : Anum_pg_depend_refclassid,
6945 : BTEqualStrategyNumber, F_OIDEQ,
6946 : ObjectIdGetDatum(TypeRelationId));
6947 4440 : ScanKeyInit(&key[1],
6948 : Anum_pg_depend_refobjid,
6949 : BTEqualStrategyNumber, F_OIDEQ,
6950 : ObjectIdGetDatum(typeOid));
6951 :
6952 4440 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6953 : NULL, 2, key);
6954 :
6955 6828 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6956 : {
6957 2508 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6958 : Relation rel;
6959 : TupleDesc tupleDesc;
6960 : Form_pg_attribute att;
6961 :
6962 : /* Check for directly dependent types */
6963 2508 : if (pg_depend->classid == TypeRelationId)
6964 : {
6965 : /*
6966 : * This must be an array, domain, or range containing the given
6967 : * type, so recursively check for uses of this type. Note that
6968 : * any error message will mention the original type not the
6969 : * container; this is intentional.
6970 : */
6971 2138 : find_composite_type_dependencies(pg_depend->objid,
6972 : origRelation, origTypeName);
6973 2114 : continue;
6974 : }
6975 :
6976 : /* Else, ignore dependees that aren't relations */
6977 370 : if (pg_depend->classid != RelationRelationId)
6978 122 : continue;
6979 :
6980 248 : rel = relation_open(pg_depend->objid, AccessShareLock);
6981 248 : tupleDesc = RelationGetDescr(rel);
6982 :
6983 : /*
6984 : * If objsubid identifies a specific column, refer to that in error
6985 : * messages. Otherwise, search to see if there's a user column of the
6986 : * type. (We assume system columns are never of interesting types.)
6987 : * The search is needed because an index containing an expression
6988 : * column of the target type will just be recorded as a whole-relation
6989 : * dependency. If we do not find a column of the type, the dependency
6990 : * must indicate that the type is transiently referenced in an index
6991 : * expression but not stored on disk, which we assume is OK, just as
6992 : * we do for references in views. (It could also be that the target
6993 : * type is embedded in some container type that is stored in an index
6994 : * column, but the previous recursion should catch such cases.)
6995 : */
6996 248 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6997 90 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6998 : else
6999 : {
7000 158 : att = NULL;
7001 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
7002 : {
7003 254 : att = TupleDescAttr(tupleDesc, attno - 1);
7004 254 : if (att->atttypid == typeOid && !att->attisdropped)
7005 6 : break;
7006 248 : att = NULL;
7007 : }
7008 158 : if (att == NULL)
7009 : {
7010 : /* No such column, so assume OK */
7011 152 : relation_close(rel, AccessShareLock);
7012 152 : continue;
7013 : }
7014 : }
7015 :
7016 : /*
7017 : * We definitely should reject if the relation has storage. If it's
7018 : * partitioned, then perhaps we don't have to reject: if there are
7019 : * partitions then we'll fail when we find one, else there is no
7020 : * stored data to worry about. However, it's possible that the type
7021 : * change would affect conclusions about whether the type is sortable
7022 : * or hashable and thus (if it's a partitioning column) break the
7023 : * partitioning rule. For now, reject for partitioned rels too.
7024 : */
7025 96 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7026 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7027 : {
7028 96 : if (origTypeName)
7029 30 : ereport(ERROR,
7030 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7031 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7032 : origTypeName,
7033 : RelationGetRelationName(rel),
7034 : NameStr(att->attname))));
7035 66 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7036 18 : ereport(ERROR,
7037 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7038 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7039 : RelationGetRelationName(origRelation),
7040 : RelationGetRelationName(rel),
7041 : NameStr(att->attname))));
7042 48 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7043 6 : ereport(ERROR,
7044 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7045 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7046 : RelationGetRelationName(origRelation),
7047 : RelationGetRelationName(rel),
7048 : NameStr(att->attname))));
7049 : else
7050 42 : ereport(ERROR,
7051 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7052 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7053 : RelationGetRelationName(origRelation),
7054 : RelationGetRelationName(rel),
7055 : NameStr(att->attname))));
7056 : }
7057 0 : else if (OidIsValid(rel->rd_rel->reltype))
7058 : {
7059 : /*
7060 : * A view or composite type itself isn't a problem, but we must
7061 : * recursively check for indirect dependencies via its rowtype.
7062 : */
7063 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
7064 : origRelation, origTypeName);
7065 : }
7066 :
7067 0 : relation_close(rel, AccessShareLock);
7068 : }
7069 :
7070 4320 : systable_endscan(depScan);
7071 :
7072 4320 : relation_close(depRel, AccessShareLock);
7073 4320 : }
7074 :
7075 :
7076 : /*
7077 : * find_typed_table_dependencies
7078 : *
7079 : * Check to see if a composite type is being used as the type of a
7080 : * typed table. Abort if any are found and behavior is RESTRICT.
7081 : * Else return the list of tables.
7082 : */
7083 : static List *
7084 214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7085 : {
7086 : Relation classRel;
7087 : ScanKeyData key[1];
7088 : TableScanDesc scan;
7089 : HeapTuple tuple;
7090 214 : List *result = NIL;
7091 :
7092 214 : classRel = table_open(RelationRelationId, AccessShareLock);
7093 :
7094 214 : ScanKeyInit(&key[0],
7095 : Anum_pg_class_reloftype,
7096 : BTEqualStrategyNumber, F_OIDEQ,
7097 : ObjectIdGetDatum(typeOid));
7098 :
7099 214 : scan = table_beginscan_catalog(classRel, 1, key);
7100 :
7101 250 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7102 : {
7103 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7104 :
7105 60 : if (behavior == DROP_RESTRICT)
7106 24 : ereport(ERROR,
7107 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7108 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7109 : typeName),
7110 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7111 : else
7112 36 : result = lappend_oid(result, classform->oid);
7113 : }
7114 :
7115 190 : table_endscan(scan);
7116 190 : table_close(classRel, AccessShareLock);
7117 :
7118 190 : return result;
7119 : }
7120 :
7121 :
7122 : /*
7123 : * check_of_type
7124 : *
7125 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7126 : * isn't suitable, throw an error. Currently, we require that the type
7127 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7128 : * would require handling a number of extra corner cases in the DDL commands.
7129 : * (Also, allowing domain-over-composite would open up a can of worms about
7130 : * whether and how the domain's constraints should apply to derived tables.)
7131 : */
7132 : void
7133 194 : check_of_type(HeapTuple typetuple)
7134 : {
7135 194 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7136 194 : bool typeOk = false;
7137 :
7138 194 : if (typ->typtype == TYPTYPE_COMPOSITE)
7139 : {
7140 : Relation typeRelation;
7141 :
7142 : Assert(OidIsValid(typ->typrelid));
7143 188 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7144 188 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7145 :
7146 : /*
7147 : * Close the parent rel, but keep our AccessShareLock on it until xact
7148 : * commit. That will prevent someone else from deleting or ALTERing
7149 : * the type before the typed table creation/conversion commits.
7150 : */
7151 188 : relation_close(typeRelation, NoLock);
7152 :
7153 188 : if (!typeOk)
7154 6 : ereport(ERROR,
7155 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7156 : errmsg("type %s is the row type of another table",
7157 : format_type_be(typ->oid)),
7158 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7159 : }
7160 : else
7161 6 : ereport(ERROR,
7162 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7163 : errmsg("type %s is not a composite type",
7164 : format_type_be(typ->oid))));
7165 182 : }
7166 :
7167 :
7168 : /*
7169 : * ALTER TABLE ADD COLUMN
7170 : *
7171 : * Adds an additional attribute to a relation making the assumption that
7172 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7173 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7174 : * AlterTableCmd's.
7175 : *
7176 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7177 : * have to decide at runtime whether to recurse or not depending on whether we
7178 : * actually add a column or merely merge with an existing column. (We can't
7179 : * check this in a static pre-pass because it won't handle multiple inheritance
7180 : * situations correctly.)
7181 : */
7182 : static void
7183 2182 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7184 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7185 : AlterTableUtilityContext *context)
7186 : {
7187 2182 : if (rel->rd_rel->reloftype && !recursing)
7188 6 : ereport(ERROR,
7189 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7190 : errmsg("cannot add column to typed table")));
7191 :
7192 2176 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7193 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7194 :
7195 2170 : if (recurse && !is_view)
7196 2070 : cmd->recurse = true;
7197 2170 : }
7198 :
7199 : /*
7200 : * Add a column to a table. The return value is the address of the
7201 : * new column in the parent relation.
7202 : *
7203 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7204 : * copy (but that happens only after we check for IF NOT EXISTS).
7205 : */
7206 : static ObjectAddress
7207 2902 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7208 : AlterTableCmd **cmd, bool recurse, bool recursing,
7209 : LOCKMODE lockmode, AlterTablePass cur_pass,
7210 : AlterTableUtilityContext *context)
7211 : {
7212 2902 : Oid myrelid = RelationGetRelid(rel);
7213 2902 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7214 2902 : bool if_not_exists = (*cmd)->missing_ok;
7215 : Relation pgclass,
7216 : attrdesc;
7217 : HeapTuple reltup;
7218 : Form_pg_class relform;
7219 : Form_pg_attribute attribute;
7220 : int newattnum;
7221 : char relkind;
7222 : Expr *defval;
7223 : List *children;
7224 : ListCell *child;
7225 : AlterTableCmd *childcmd;
7226 : ObjectAddress address;
7227 : TupleDesc tupdesc;
7228 :
7229 : /* since this function recurses, it could be driven to stack overflow */
7230 2902 : check_stack_depth();
7231 :
7232 : /* At top level, permission check was done in ATPrepCmd, else do it */
7233 2902 : if (recursing)
7234 738 : ATSimplePermissions((*cmd)->subtype, rel,
7235 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7236 :
7237 2902 : if (rel->rd_rel->relispartition && !recursing)
7238 12 : ereport(ERROR,
7239 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7240 : errmsg("cannot add column to a partition")));
7241 :
7242 2890 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7243 :
7244 : /*
7245 : * Are we adding the column to a recursion child? If so, check whether to
7246 : * merge with an existing definition for the column. If we do merge, we
7247 : * must not recurse. Children will already have the column, and recursing
7248 : * into them would mess up attinhcount.
7249 : */
7250 2890 : if (colDef->inhcount > 0)
7251 : {
7252 : HeapTuple tuple;
7253 :
7254 : /* Does child already have a column by this name? */
7255 738 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7256 738 : if (HeapTupleIsValid(tuple))
7257 : {
7258 60 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7259 : Oid ctypeId;
7260 : int32 ctypmod;
7261 : Oid ccollid;
7262 :
7263 : /* Child column must match on type, typmod, and collation */
7264 60 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7265 60 : if (ctypeId != childatt->atttypid ||
7266 60 : ctypmod != childatt->atttypmod)
7267 0 : ereport(ERROR,
7268 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7269 : errmsg("child table \"%s\" has different type for column \"%s\"",
7270 : RelationGetRelationName(rel), colDef->colname)));
7271 60 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7272 60 : if (ccollid != childatt->attcollation)
7273 0 : ereport(ERROR,
7274 : (errcode(ERRCODE_COLLATION_MISMATCH),
7275 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7276 : RelationGetRelationName(rel), colDef->colname),
7277 : errdetail("\"%s\" versus \"%s\"",
7278 : get_collation_name(ccollid),
7279 : get_collation_name(childatt->attcollation))));
7280 :
7281 : /* Bump the existing child att's inhcount */
7282 60 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7283 : &childatt->attinhcount))
7284 0 : ereport(ERROR,
7285 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7286 : errmsg("too many inheritance parents"));
7287 60 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7288 :
7289 60 : heap_freetuple(tuple);
7290 :
7291 : /* Inform the user about the merge */
7292 60 : ereport(NOTICE,
7293 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7294 : colDef->colname, RelationGetRelationName(rel))));
7295 :
7296 60 : table_close(attrdesc, RowExclusiveLock);
7297 :
7298 : /* Make the child column change visible */
7299 60 : CommandCounterIncrement();
7300 :
7301 60 : return InvalidObjectAddress;
7302 : }
7303 : }
7304 :
7305 : /* skip if the name already exists and if_not_exists is true */
7306 2830 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7307 : {
7308 54 : table_close(attrdesc, RowExclusiveLock);
7309 54 : return InvalidObjectAddress;
7310 : }
7311 :
7312 : /*
7313 : * Okay, we need to add the column, so go ahead and do parse
7314 : * transformation. This can result in queueing up, or even immediately
7315 : * executing, subsidiary operations (such as creation of unique indexes);
7316 : * so we mustn't do it until we have made the if_not_exists check.
7317 : *
7318 : * When recursing, the command was already transformed and we needn't do
7319 : * so again. Also, if context isn't given we can't transform. (That
7320 : * currently happens only for AT_AddColumnToView; we expect that view.c
7321 : * passed us a ColumnDef that doesn't need work.)
7322 : */
7323 2746 : if (context != NULL && !recursing)
7324 : {
7325 2044 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7326 : cur_pass, context);
7327 : Assert(*cmd != NULL);
7328 2038 : colDef = castNode(ColumnDef, (*cmd)->def);
7329 : }
7330 :
7331 : /*
7332 : * Regular inheritance children are independent enough not to inherit the
7333 : * identity column from parent hence cannot recursively add identity
7334 : * column if the table has inheritance children.
7335 : *
7336 : * Partitions, on the other hand, are integral part of a partitioned table
7337 : * and inherit identity column. Hence propagate identity column down the
7338 : * partition hierarchy.
7339 : */
7340 2740 : if (colDef->identity &&
7341 54 : recurse &&
7342 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7343 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7344 6 : ereport(ERROR,
7345 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7346 : errmsg("cannot recursively add identity column to table that has child tables")));
7347 :
7348 2734 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7349 :
7350 2734 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7351 2734 : if (!HeapTupleIsValid(reltup))
7352 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7353 2734 : relform = (Form_pg_class) GETSTRUCT(reltup);
7354 2734 : relkind = relform->relkind;
7355 :
7356 : /* Determine the new attribute's number */
7357 2734 : newattnum = relform->relnatts + 1;
7358 2734 : if (newattnum > MaxHeapAttributeNumber)
7359 0 : ereport(ERROR,
7360 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7361 : errmsg("tables can have at most %d columns",
7362 : MaxHeapAttributeNumber)));
7363 :
7364 : /*
7365 : * Construct new attribute's pg_attribute entry.
7366 : */
7367 2734 : tupdesc = BuildDescForRelation(list_make1(colDef));
7368 :
7369 2722 : attribute = TupleDescAttr(tupdesc, 0);
7370 :
7371 : /* Fix up attribute number */
7372 2722 : attribute->attnum = newattnum;
7373 :
7374 : /* make sure datatype is legal for a column */
7375 2722 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7376 2722 : list_make1_oid(rel->rd_rel->reltype),
7377 : 0);
7378 :
7379 2692 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7380 :
7381 2692 : table_close(attrdesc, RowExclusiveLock);
7382 :
7383 : /*
7384 : * Update pg_class tuple as appropriate
7385 : */
7386 2692 : relform->relnatts = newattnum;
7387 :
7388 2692 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7389 :
7390 2692 : heap_freetuple(reltup);
7391 :
7392 : /* Post creation hook for new attribute */
7393 2692 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7394 :
7395 2692 : table_close(pgclass, RowExclusiveLock);
7396 :
7397 : /* Make the attribute's catalog entry visible */
7398 2692 : CommandCounterIncrement();
7399 :
7400 : /*
7401 : * Store the DEFAULT, if any, in the catalogs
7402 : */
7403 2692 : if (colDef->raw_default)
7404 : {
7405 : RawColumnDefault *rawEnt;
7406 :
7407 924 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7408 924 : rawEnt->attnum = attribute->attnum;
7409 924 : rawEnt->raw_default = copyObject(colDef->raw_default);
7410 924 : rawEnt->generated = colDef->generated;
7411 :
7412 : /*
7413 : * This function is intended for CREATE TABLE, so it processes a
7414 : * _list_ of defaults, but we just do one.
7415 : */
7416 924 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7417 : false, true, false, NULL);
7418 :
7419 : /* Make the additional catalog changes visible */
7420 900 : CommandCounterIncrement();
7421 : }
7422 :
7423 : /*
7424 : * Tell Phase 3 to fill in the default expression, if there is one.
7425 : *
7426 : * If there is no default, Phase 3 doesn't have to do anything, because
7427 : * that effectively means that the default is NULL. The heap tuple access
7428 : * routines always check for attnum > # of attributes in tuple, and return
7429 : * NULL if so, so without any modification of the tuple data we will get
7430 : * the effect of NULL values in the new column.
7431 : *
7432 : * An exception occurs when the new column is of a domain type: the domain
7433 : * might have a not-null constraint, or a check constraint that indirectly
7434 : * rejects nulls. If there are any domain constraints then we construct
7435 : * an explicit NULL default value that will be passed through
7436 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7437 : * rewriting the table which we really wouldn't have to do; but we do it
7438 : * to preserve the historical behavior that such a failure will be raised
7439 : * only if the table currently contains some rows.)
7440 : *
7441 : * Note: we use build_column_default, and not just the cooked default
7442 : * returned by AddRelationNewConstraints, so that the right thing happens
7443 : * when a datatype's default applies.
7444 : *
7445 : * Note: it might seem that this should happen at the end of Phase 2, so
7446 : * that the effects of subsequent subcommands can be taken into account.
7447 : * It's intentional that we do it now, though. The new column should be
7448 : * filled according to what is said in the ADD COLUMN subcommand, so that
7449 : * the effects are the same as if this subcommand had been run by itself
7450 : * and the later subcommands had been issued in new ALTER TABLE commands.
7451 : *
7452 : * We can skip this entirely for relations without storage, since Phase 3
7453 : * is certainly not going to touch them.
7454 : */
7455 2668 : if (RELKIND_HAS_STORAGE(relkind))
7456 : {
7457 : bool has_domain_constraints;
7458 2292 : bool has_missing = false;
7459 :
7460 : /*
7461 : * For an identity column, we can't use build_column_default(),
7462 : * because the sequence ownership isn't set yet. So do it manually.
7463 : */
7464 2292 : if (colDef->identity)
7465 : {
7466 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7467 :
7468 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7469 42 : nve->typeId = attribute->atttypid;
7470 :
7471 42 : defval = (Expr *) nve;
7472 : }
7473 : else
7474 2250 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7475 :
7476 : /* Build CoerceToDomain(NULL) expression if needed */
7477 2292 : has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7478 2292 : if (!defval && has_domain_constraints)
7479 : {
7480 : Oid baseTypeId;
7481 : int32 baseTypeMod;
7482 : Oid baseTypeColl;
7483 :
7484 6 : baseTypeMod = attribute->atttypmod;
7485 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7486 6 : baseTypeColl = get_typcollation(baseTypeId);
7487 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7488 6 : defval = (Expr *) coerce_to_target_type(NULL,
7489 : (Node *) defval,
7490 : baseTypeId,
7491 : attribute->atttypid,
7492 : attribute->atttypmod,
7493 : COERCION_ASSIGNMENT,
7494 : COERCE_IMPLICIT_CAST,
7495 : -1);
7496 6 : if (defval == NULL) /* should not happen */
7497 0 : elog(ERROR, "failed to coerce base type to domain");
7498 : }
7499 :
7500 2292 : if (defval)
7501 : {
7502 : NewColumnValue *newval;
7503 :
7504 : /* Prepare defval for execution, either here or in Phase 3 */
7505 808 : defval = expression_planner(defval);
7506 :
7507 : /* Add the new default to the newvals list */
7508 808 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7509 808 : newval->attnum = attribute->attnum;
7510 808 : newval->expr = defval;
7511 808 : newval->is_generated = (colDef->generated != '\0');
7512 :
7513 808 : tab->newvals = lappend(tab->newvals, newval);
7514 :
7515 : /*
7516 : * Attempt to skip a complete table rewrite by storing the
7517 : * specified DEFAULT value outside of the heap. This is only
7518 : * allowed for plain relations and non-generated columns, and the
7519 : * default expression can't be volatile (stable is OK). Note that
7520 : * contain_volatile_functions deems CoerceToDomain immutable, but
7521 : * here we consider that coercion to a domain with constraints is
7522 : * volatile; else it might fail even when the table is empty.
7523 : */
7524 808 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
7525 808 : !colDef->generated &&
7526 682 : !has_domain_constraints &&
7527 670 : !contain_volatile_functions((Node *) defval))
7528 502 : {
7529 : EState *estate;
7530 : ExprState *exprState;
7531 : Datum missingval;
7532 : bool missingIsNull;
7533 :
7534 : /* Evaluate the default expression */
7535 502 : estate = CreateExecutorState();
7536 502 : exprState = ExecPrepareExpr(defval, estate);
7537 502 : missingval = ExecEvalExpr(exprState,
7538 502 : GetPerTupleExprContext(estate),
7539 : &missingIsNull);
7540 : /* If it turns out NULL, nothing to do; else store it */
7541 502 : if (!missingIsNull)
7542 : {
7543 502 : StoreAttrMissingVal(rel, attribute->attnum, missingval);
7544 : /* Make the additional catalog change visible */
7545 502 : CommandCounterIncrement();
7546 502 : has_missing = true;
7547 : }
7548 502 : FreeExecutorState(estate);
7549 : }
7550 : else
7551 : {
7552 : /*
7553 : * Failed to use missing mode. We have to do a table rewrite
7554 : * to install the value --- unless it's a virtual generated
7555 : * column.
7556 : */
7557 306 : if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7558 216 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7559 : }
7560 : }
7561 :
7562 2292 : if (!has_missing)
7563 : {
7564 : /*
7565 : * If the new column is NOT NULL, and there is no missing value,
7566 : * tell Phase 3 it needs to check for NULLs.
7567 : */
7568 1790 : tab->verify_new_notnull |= colDef->is_not_null;
7569 : }
7570 : }
7571 :
7572 : /*
7573 : * Add needed dependency entries for the new column.
7574 : */
7575 2668 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7576 2668 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7577 :
7578 : /*
7579 : * Propagate to children as appropriate. Unlike most other ALTER
7580 : * routines, we have to do this one level of recursion at a time; we can't
7581 : * use find_all_inheritors to do it in one pass.
7582 : */
7583 : children =
7584 2668 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7585 :
7586 : /*
7587 : * If we are told not to recurse, there had better not be any child
7588 : * tables; else the addition would put them out of step.
7589 : */
7590 2668 : if (children && !recurse)
7591 12 : ereport(ERROR,
7592 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7593 : errmsg("column must be added to child tables too")));
7594 :
7595 : /* Children should see column as singly inherited */
7596 2656 : if (!recursing)
7597 : {
7598 1978 : childcmd = copyObject(*cmd);
7599 1978 : colDef = castNode(ColumnDef, childcmd->def);
7600 1978 : colDef->inhcount = 1;
7601 1978 : colDef->is_local = false;
7602 : }
7603 : else
7604 678 : childcmd = *cmd; /* no need to copy again */
7605 :
7606 3394 : foreach(child, children)
7607 : {
7608 738 : Oid childrelid = lfirst_oid(child);
7609 : Relation childrel;
7610 : AlteredTableInfo *childtab;
7611 :
7612 : /* find_inheritance_children already got lock */
7613 738 : childrel = table_open(childrelid, NoLock);
7614 738 : CheckAlterTableIsSafe(childrel);
7615 :
7616 : /* Find or create work queue entry for this table */
7617 738 : childtab = ATGetQueueEntry(wqueue, childrel);
7618 :
7619 : /* Recurse to child; return value is ignored */
7620 738 : ATExecAddColumn(wqueue, childtab, childrel,
7621 : &childcmd, recurse, true,
7622 : lockmode, cur_pass, context);
7623 :
7624 738 : table_close(childrel, NoLock);
7625 : }
7626 :
7627 2656 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7628 2656 : return address;
7629 : }
7630 :
7631 : /*
7632 : * If a new or renamed column will collide with the name of an existing
7633 : * column and if_not_exists is false then error out, else do nothing.
7634 : */
7635 : static bool
7636 3280 : check_for_column_name_collision(Relation rel, const char *colname,
7637 : bool if_not_exists)
7638 : {
7639 : HeapTuple attTuple;
7640 : int attnum;
7641 :
7642 : /*
7643 : * this test is deliberately not attisdropped-aware, since if one tries to
7644 : * add a column matching a dropped column name, it's gonna fail anyway.
7645 : */
7646 3280 : attTuple = SearchSysCache2(ATTNAME,
7647 : ObjectIdGetDatum(RelationGetRelid(rel)),
7648 : PointerGetDatum(colname));
7649 3280 : if (!HeapTupleIsValid(attTuple))
7650 3184 : return true;
7651 :
7652 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7653 96 : ReleaseSysCache(attTuple);
7654 :
7655 : /*
7656 : * We throw a different error message for conflicts with system column
7657 : * names, since they are normally not shown and the user might otherwise
7658 : * be confused about the reason for the conflict.
7659 : */
7660 96 : if (attnum <= 0)
7661 12 : ereport(ERROR,
7662 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7663 : errmsg("column name \"%s\" conflicts with a system column name",
7664 : colname)));
7665 : else
7666 : {
7667 84 : if (if_not_exists)
7668 : {
7669 54 : ereport(NOTICE,
7670 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7671 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7672 : colname, RelationGetRelationName(rel))));
7673 54 : return false;
7674 : }
7675 :
7676 30 : ereport(ERROR,
7677 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7678 : errmsg("column \"%s\" of relation \"%s\" already exists",
7679 : colname, RelationGetRelationName(rel))));
7680 : }
7681 :
7682 : return true;
7683 : }
7684 :
7685 : /*
7686 : * Install a column's dependency on its datatype.
7687 : */
7688 : static void
7689 3680 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7690 : {
7691 : ObjectAddress myself,
7692 : referenced;
7693 :
7694 3680 : myself.classId = RelationRelationId;
7695 3680 : myself.objectId = relid;
7696 3680 : myself.objectSubId = attnum;
7697 3680 : referenced.classId = TypeRelationId;
7698 3680 : referenced.objectId = typid;
7699 3680 : referenced.objectSubId = 0;
7700 3680 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7701 3680 : }
7702 :
7703 : /*
7704 : * Install a column's dependency on its collation.
7705 : */
7706 : static void
7707 3680 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7708 : {
7709 : ObjectAddress myself,
7710 : referenced;
7711 :
7712 : /* We know the default collation is pinned, so don't bother recording it */
7713 3680 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7714 : {
7715 18 : myself.classId = RelationRelationId;
7716 18 : myself.objectId = relid;
7717 18 : myself.objectSubId = attnum;
7718 18 : referenced.classId = CollationRelationId;
7719 18 : referenced.objectId = collid;
7720 18 : referenced.objectSubId = 0;
7721 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7722 : }
7723 3680 : }
7724 :
7725 : /*
7726 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7727 : *
7728 : * Return the address of the modified column. If the column was already
7729 : * nullable, InvalidObjectAddress is returned.
7730 : */
7731 : static ObjectAddress
7732 268 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7733 : LOCKMODE lockmode)
7734 : {
7735 : HeapTuple tuple;
7736 : HeapTuple conTup;
7737 : Form_pg_attribute attTup;
7738 : AttrNumber attnum;
7739 : Relation attr_rel;
7740 : ObjectAddress address;
7741 :
7742 : /*
7743 : * lookup the attribute
7744 : */
7745 268 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7746 :
7747 268 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7748 268 : if (!HeapTupleIsValid(tuple))
7749 18 : ereport(ERROR,
7750 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7751 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7752 : colName, RelationGetRelationName(rel))));
7753 250 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7754 250 : attnum = attTup->attnum;
7755 250 : ObjectAddressSubSet(address, RelationRelationId,
7756 : RelationGetRelid(rel), attnum);
7757 :
7758 : /* If the column is already nullable there's nothing to do. */
7759 250 : if (!attTup->attnotnull)
7760 : {
7761 0 : table_close(attr_rel, RowExclusiveLock);
7762 0 : return InvalidObjectAddress;
7763 : }
7764 :
7765 : /* Prevent them from altering a system attribute */
7766 250 : if (attnum <= 0)
7767 0 : ereport(ERROR,
7768 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7769 : errmsg("cannot alter system column \"%s\"",
7770 : colName)));
7771 :
7772 250 : if (attTup->attidentity)
7773 18 : ereport(ERROR,
7774 : (errcode(ERRCODE_SYNTAX_ERROR),
7775 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7776 : colName, RelationGetRelationName(rel))));
7777 :
7778 : /*
7779 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7780 : */
7781 232 : if (rel->rd_rel->relispartition)
7782 : {
7783 12 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7784 12 : Relation parent = table_open(parentId, AccessShareLock);
7785 12 : TupleDesc tupDesc = RelationGetDescr(parent);
7786 : AttrNumber parent_attnum;
7787 :
7788 12 : parent_attnum = get_attnum(parentId, colName);
7789 12 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7790 12 : ereport(ERROR,
7791 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7792 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7793 : colName)));
7794 0 : table_close(parent, AccessShareLock);
7795 : }
7796 :
7797 : /*
7798 : * Find the constraint that makes this column NOT NULL, and drop it.
7799 : * dropconstraint_internal() resets attnotnull.
7800 : */
7801 220 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7802 220 : if (conTup == NULL)
7803 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7804 : colName, RelationGetRelationName(rel));
7805 :
7806 : /* The normal case: we have a pg_constraint row, remove it */
7807 220 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7808 : false, lockmode);
7809 166 : heap_freetuple(conTup);
7810 :
7811 166 : InvokeObjectPostAlterHook(RelationRelationId,
7812 : RelationGetRelid(rel), attnum);
7813 :
7814 166 : table_close(attr_rel, RowExclusiveLock);
7815 :
7816 166 : return address;
7817 : }
7818 :
7819 : /*
7820 : * set_attnotnull
7821 : * Helper to update/validate the pg_attribute status of a not-null
7822 : * constraint
7823 : *
7824 : * pg_attribute.attnotnull is set true, if it isn't already.
7825 : * If queue_validation is true, also set up wqueue to validate the constraint.
7826 : * wqueue may be given as NULL when validation is not needed (e.g., on table
7827 : * creation).
7828 : */
7829 : static void
7830 25176 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7831 : bool is_valid, bool queue_validation)
7832 : {
7833 : Form_pg_attribute attr;
7834 : CompactAttribute *thisatt;
7835 :
7836 : Assert(!queue_validation || wqueue);
7837 :
7838 25176 : CheckAlterTableIsSafe(rel);
7839 :
7840 : /*
7841 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7842 : * attribute.
7843 : */
7844 25176 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7845 25176 : if (attr->attisdropped)
7846 0 : return;
7847 :
7848 25176 : if (!attr->attnotnull)
7849 : {
7850 : Relation attr_rel;
7851 : HeapTuple tuple;
7852 :
7853 1440 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7854 :
7855 1440 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7856 1440 : if (!HeapTupleIsValid(tuple))
7857 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7858 : attnum, RelationGetRelid(rel));
7859 :
7860 1440 : thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7861 1440 : thisatt->attnullability = ATTNULLABLE_VALID;
7862 :
7863 1440 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7864 :
7865 1440 : attr->attnotnull = true;
7866 1440 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7867 :
7868 : /*
7869 : * If the nullness isn't already proven by validated constraints, have
7870 : * ALTER TABLE phase 3 test for it.
7871 : */
7872 1440 : if (queue_validation && wqueue &&
7873 1238 : !NotNullImpliedByRelConstraints(rel, attr))
7874 : {
7875 : AlteredTableInfo *tab;
7876 :
7877 1188 : tab = ATGetQueueEntry(wqueue, rel);
7878 1188 : tab->verify_new_notnull = true;
7879 : }
7880 :
7881 1440 : CommandCounterIncrement();
7882 :
7883 1440 : table_close(attr_rel, RowExclusiveLock);
7884 1440 : heap_freetuple(tuple);
7885 : }
7886 : else
7887 : {
7888 23736 : CacheInvalidateRelcache(rel);
7889 : }
7890 : }
7891 :
7892 : /*
7893 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7894 : *
7895 : * Add a not-null constraint to a single table and its children. Returns
7896 : * the address of the constraint added to the parent relation, if one gets
7897 : * added, or InvalidObjectAddress otherwise.
7898 : *
7899 : * We must recurse to child tables during execution, rather than using
7900 : * ALTER TABLE's normal prep-time recursion.
7901 : */
7902 : static ObjectAddress
7903 706 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7904 : bool recurse, bool recursing, LOCKMODE lockmode)
7905 : {
7906 : HeapTuple tuple;
7907 : AttrNumber attnum;
7908 : ObjectAddress address;
7909 : Constraint *constraint;
7910 : CookedConstraint *ccon;
7911 : List *cooked;
7912 706 : bool is_no_inherit = false;
7913 :
7914 : /* Guard against stack overflow due to overly deep inheritance tree. */
7915 706 : check_stack_depth();
7916 :
7917 : /* At top level, permission check was done in ATPrepCmd, else do it */
7918 706 : if (recursing)
7919 : {
7920 298 : ATSimplePermissions(AT_AddConstraint, rel,
7921 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7922 : Assert(conName != NULL);
7923 : }
7924 :
7925 706 : attnum = get_attnum(RelationGetRelid(rel), colName);
7926 706 : if (attnum == InvalidAttrNumber)
7927 18 : ereport(ERROR,
7928 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7929 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7930 : colName, RelationGetRelationName(rel))));
7931 :
7932 : /* Prevent them from altering a system attribute */
7933 688 : if (attnum <= 0)
7934 0 : ereport(ERROR,
7935 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7936 : errmsg("cannot alter system column \"%s\"",
7937 : colName)));
7938 :
7939 : /* See if there's already a constraint */
7940 688 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7941 688 : if (HeapTupleIsValid(tuple))
7942 : {
7943 158 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7944 158 : bool changed = false;
7945 :
7946 : /*
7947 : * Don't let a NO INHERIT constraint be changed into inherit.
7948 : */
7949 158 : if (conForm->connoinherit && recurse)
7950 12 : ereport(ERROR,
7951 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7952 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7953 : NameStr(conForm->conname),
7954 : RelationGetRelationName(rel)));
7955 :
7956 : /*
7957 : * If we find an appropriate constraint, we're almost done, but just
7958 : * need to change some properties on it: if we're recursing, increment
7959 : * coninhcount; if not, set conislocal if not already set.
7960 : */
7961 146 : if (recursing)
7962 : {
7963 102 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
7964 : &conForm->coninhcount))
7965 0 : ereport(ERROR,
7966 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7967 : errmsg("too many inheritance parents"));
7968 102 : changed = true;
7969 : }
7970 44 : else if (!conForm->conislocal)
7971 : {
7972 0 : conForm->conislocal = true;
7973 0 : changed = true;
7974 : }
7975 44 : else if (!conForm->convalidated)
7976 : {
7977 : /*
7978 : * Flip attnotnull and convalidated, and also validate the
7979 : * constraint.
7980 : */
7981 24 : return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
7982 : recurse, recursing, lockmode);
7983 : }
7984 :
7985 122 : if (changed)
7986 : {
7987 : Relation constr_rel;
7988 :
7989 102 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7990 :
7991 102 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7992 102 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7993 102 : table_close(constr_rel, RowExclusiveLock);
7994 : }
7995 :
7996 122 : if (changed)
7997 102 : return address;
7998 : else
7999 20 : return InvalidObjectAddress;
8000 : }
8001 :
8002 : /*
8003 : * If we're asked not to recurse, and children exist, raise an error for
8004 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
8005 : * specified.
8006 : */
8007 554 : if (!recurse &&
8008 24 : find_inheritance_children(RelationGetRelid(rel),
8009 : NoLock) != NIL)
8010 : {
8011 18 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8012 6 : ereport(ERROR,
8013 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8014 : errmsg("constraint must be added to child tables too"),
8015 : errhint("Do not specify the ONLY keyword."));
8016 : else
8017 12 : is_no_inherit = true;
8018 : }
8019 :
8020 : /*
8021 : * No constraint exists; we must add one. First determine a name to use,
8022 : * if we haven't already.
8023 : */
8024 524 : if (!recursing)
8025 : {
8026 : Assert(conName == NULL);
8027 334 : conName = ChooseConstraintName(RelationGetRelationName(rel),
8028 : colName, "not_null",
8029 334 : RelationGetNamespace(rel),
8030 : NIL);
8031 : }
8032 :
8033 524 : constraint = makeNotNullConstraint(makeString(colName));
8034 524 : constraint->is_no_inherit = is_no_inherit;
8035 524 : constraint->conname = conName;
8036 :
8037 : /* and do it */
8038 524 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8039 524 : false, !recursing, false, NULL);
8040 524 : ccon = linitial(cooked);
8041 524 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8042 :
8043 524 : InvokeObjectPostAlterHook(RelationRelationId,
8044 : RelationGetRelid(rel), attnum);
8045 :
8046 : /* Mark pg_attribute.attnotnull for the column and queue validation */
8047 524 : set_attnotnull(wqueue, rel, attnum, true, true);
8048 :
8049 : /*
8050 : * Recurse to propagate the constraint to children that don't have one.
8051 : */
8052 524 : if (recurse)
8053 : {
8054 : List *children;
8055 :
8056 506 : children = find_inheritance_children(RelationGetRelid(rel),
8057 : lockmode);
8058 :
8059 1244 : foreach_oid(childoid, children)
8060 : {
8061 244 : Relation childrel = table_open(childoid, NoLock);
8062 :
8063 244 : CommandCounterIncrement();
8064 :
8065 244 : ATExecSetNotNull(wqueue, childrel, conName, colName,
8066 : recurse, true, lockmode);
8067 238 : table_close(childrel, NoLock);
8068 : }
8069 : }
8070 :
8071 518 : return address;
8072 : }
8073 :
8074 : /*
8075 : * NotNullImpliedByRelConstraints
8076 : * Does rel's existing constraints imply NOT NULL for the given attribute?
8077 : */
8078 : static bool
8079 1238 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8080 : {
8081 1238 : NullTest *nnulltest = makeNode(NullTest);
8082 :
8083 2476 : nnulltest->arg = (Expr *) makeVar(1,
8084 1238 : attr->attnum,
8085 : attr->atttypid,
8086 : attr->atttypmod,
8087 : attr->attcollation,
8088 : 0);
8089 1238 : nnulltest->nulltesttype = IS_NOT_NULL;
8090 :
8091 : /*
8092 : * argisrow = false is correct even for a composite column, because
8093 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8094 : * case, just IS DISTINCT FROM NULL.
8095 : */
8096 1238 : nnulltest->argisrow = false;
8097 1238 : nnulltest->location = -1;
8098 :
8099 1238 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8100 : {
8101 50 : ereport(DEBUG1,
8102 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8103 : RelationGetRelationName(rel), NameStr(attr->attname))));
8104 50 : return true;
8105 : }
8106 :
8107 1188 : return false;
8108 : }
8109 :
8110 : /*
8111 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8112 : *
8113 : * Return the address of the affected column.
8114 : */
8115 : static ObjectAddress
8116 612 : ATExecColumnDefault(Relation rel, const char *colName,
8117 : Node *newDefault, LOCKMODE lockmode)
8118 : {
8119 612 : TupleDesc tupdesc = RelationGetDescr(rel);
8120 : AttrNumber attnum;
8121 : ObjectAddress address;
8122 :
8123 : /*
8124 : * get the number of the attribute
8125 : */
8126 612 : attnum = get_attnum(RelationGetRelid(rel), colName);
8127 612 : if (attnum == InvalidAttrNumber)
8128 30 : ereport(ERROR,
8129 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8130 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8131 : colName, RelationGetRelationName(rel))));
8132 :
8133 : /* Prevent them from altering a system attribute */
8134 582 : if (attnum <= 0)
8135 0 : ereport(ERROR,
8136 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8137 : errmsg("cannot alter system column \"%s\"",
8138 : colName)));
8139 :
8140 582 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8141 18 : ereport(ERROR,
8142 : (errcode(ERRCODE_SYNTAX_ERROR),
8143 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8144 : colName, RelationGetRelationName(rel)),
8145 : /* translator: %s is an SQL ALTER command */
8146 : newDefault ? 0 : errhint("Use %s instead.",
8147 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8148 :
8149 564 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8150 12 : ereport(ERROR,
8151 : (errcode(ERRCODE_SYNTAX_ERROR),
8152 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8153 : colName, RelationGetRelationName(rel)),
8154 : newDefault ?
8155 : /* translator: %s is an SQL ALTER command */
8156 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8157 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8158 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8159 :
8160 : /*
8161 : * Remove any old default for the column. We use RESTRICT here for
8162 : * safety, but at present we do not expect anything to depend on the
8163 : * default.
8164 : *
8165 : * We treat removing the existing default as an internal operation when it
8166 : * is preparatory to adding a new default, but as a user-initiated
8167 : * operation when the user asked for a drop.
8168 : */
8169 552 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8170 : newDefault != NULL);
8171 :
8172 552 : if (newDefault)
8173 : {
8174 : /* SET DEFAULT */
8175 : RawColumnDefault *rawEnt;
8176 :
8177 378 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8178 378 : rawEnt->attnum = attnum;
8179 378 : rawEnt->raw_default = newDefault;
8180 378 : rawEnt->generated = '\0';
8181 :
8182 : /*
8183 : * This function is intended for CREATE TABLE, so it processes a
8184 : * _list_ of defaults, but we just do one.
8185 : */
8186 378 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8187 : false, true, false, NULL);
8188 : }
8189 :
8190 546 : ObjectAddressSubSet(address, RelationRelationId,
8191 : RelationGetRelid(rel), attnum);
8192 546 : return address;
8193 : }
8194 :
8195 : /*
8196 : * Add a pre-cooked default expression.
8197 : *
8198 : * Return the address of the affected column.
8199 : */
8200 : static ObjectAddress
8201 80 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8202 : Node *newDefault)
8203 : {
8204 : ObjectAddress address;
8205 :
8206 : /* We assume no checking is required */
8207 :
8208 : /*
8209 : * Remove any old default for the column. We use RESTRICT here for
8210 : * safety, but at present we do not expect anything to depend on the
8211 : * default. (In ordinary cases, there could not be a default in place
8212 : * anyway, but it's possible when combining LIKE with inheritance.)
8213 : */
8214 80 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8215 : true);
8216 :
8217 80 : (void) StoreAttrDefault(rel, attnum, newDefault, true);
8218 :
8219 80 : ObjectAddressSubSet(address, RelationRelationId,
8220 : RelationGetRelid(rel), attnum);
8221 80 : return address;
8222 : }
8223 :
8224 : /*
8225 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8226 : *
8227 : * Return the address of the affected column.
8228 : */
8229 : static ObjectAddress
8230 204 : ATExecAddIdentity(Relation rel, const char *colName,
8231 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8232 : {
8233 : Relation attrelation;
8234 : HeapTuple tuple;
8235 : Form_pg_attribute attTup;
8236 : AttrNumber attnum;
8237 : ObjectAddress address;
8238 204 : ColumnDef *cdef = castNode(ColumnDef, def);
8239 : bool ispartitioned;
8240 :
8241 204 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8242 204 : if (ispartitioned && !recurse)
8243 6 : ereport(ERROR,
8244 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8245 : errmsg("cannot add identity to a column of only the partitioned table"),
8246 : errhint("Do not specify the ONLY keyword.")));
8247 :
8248 198 : if (rel->rd_rel->relispartition && !recursing)
8249 12 : ereport(ERROR,
8250 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8251 : errmsg("cannot add identity to a column of a partition"));
8252 :
8253 186 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8254 :
8255 186 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8256 186 : if (!HeapTupleIsValid(tuple))
8257 0 : ereport(ERROR,
8258 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8259 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8260 : colName, RelationGetRelationName(rel))));
8261 186 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8262 186 : attnum = attTup->attnum;
8263 :
8264 : /* Can't alter a system attribute */
8265 186 : if (attnum <= 0)
8266 0 : ereport(ERROR,
8267 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8268 : errmsg("cannot alter system column \"%s\"",
8269 : colName)));
8270 :
8271 : /*
8272 : * Creating a column as identity implies NOT NULL, so adding the identity
8273 : * to an existing column that is not NOT NULL would create a state that
8274 : * cannot be reproduced without contortions.
8275 : */
8276 186 : if (!attTup->attnotnull)
8277 6 : ereport(ERROR,
8278 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8279 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8280 : colName, RelationGetRelationName(rel))));
8281 :
8282 180 : if (attTup->attidentity)
8283 18 : ereport(ERROR,
8284 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8285 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8286 : colName, RelationGetRelationName(rel))));
8287 :
8288 162 : if (attTup->atthasdef)
8289 6 : ereport(ERROR,
8290 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8291 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8292 : colName, RelationGetRelationName(rel))));
8293 :
8294 156 : attTup->attidentity = cdef->identity;
8295 156 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8296 :
8297 156 : InvokeObjectPostAlterHook(RelationRelationId,
8298 : RelationGetRelid(rel),
8299 : attTup->attnum);
8300 156 : ObjectAddressSubSet(address, RelationRelationId,
8301 : RelationGetRelid(rel), attnum);
8302 156 : heap_freetuple(tuple);
8303 :
8304 156 : table_close(attrelation, RowExclusiveLock);
8305 :
8306 : /*
8307 : * Recurse to propagate the identity column to partitions. Identity is
8308 : * not inherited in regular inheritance children.
8309 : */
8310 156 : if (recurse && ispartitioned)
8311 : {
8312 : List *children;
8313 : ListCell *lc;
8314 :
8315 14 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8316 :
8317 20 : foreach(lc, children)
8318 : {
8319 : Relation childrel;
8320 :
8321 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8322 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8323 6 : table_close(childrel, NoLock);
8324 : }
8325 : }
8326 :
8327 156 : return address;
8328 : }
8329 :
8330 : /*
8331 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8332 : *
8333 : * Return the address of the affected column.
8334 : */
8335 : static ObjectAddress
8336 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8337 : LOCKMODE lockmode, bool recurse, bool recursing)
8338 : {
8339 : ListCell *option;
8340 74 : DefElem *generatedEl = NULL;
8341 : HeapTuple tuple;
8342 : Form_pg_attribute attTup;
8343 : AttrNumber attnum;
8344 : Relation attrelation;
8345 : ObjectAddress address;
8346 : bool ispartitioned;
8347 :
8348 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8349 74 : if (ispartitioned && !recurse)
8350 6 : ereport(ERROR,
8351 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8352 : errmsg("cannot change identity column of only the partitioned table"),
8353 : errhint("Do not specify the ONLY keyword.")));
8354 :
8355 68 : if (rel->rd_rel->relispartition && !recursing)
8356 12 : ereport(ERROR,
8357 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8358 : errmsg("cannot change identity column of a partition"));
8359 :
8360 100 : foreach(option, castNode(List, def))
8361 : {
8362 44 : DefElem *defel = lfirst_node(DefElem, option);
8363 :
8364 44 : if (strcmp(defel->defname, "generated") == 0)
8365 : {
8366 44 : if (generatedEl)
8367 0 : ereport(ERROR,
8368 : (errcode(ERRCODE_SYNTAX_ERROR),
8369 : errmsg("conflicting or redundant options")));
8370 44 : generatedEl = defel;
8371 : }
8372 : else
8373 0 : elog(ERROR, "option \"%s\" not recognized",
8374 : defel->defname);
8375 : }
8376 :
8377 : /*
8378 : * Even if there is nothing to change here, we run all the checks. There
8379 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8380 : * there.
8381 : */
8382 :
8383 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8384 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8385 56 : if (!HeapTupleIsValid(tuple))
8386 0 : ereport(ERROR,
8387 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8388 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8389 : colName, RelationGetRelationName(rel))));
8390 :
8391 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8392 56 : attnum = attTup->attnum;
8393 :
8394 56 : if (attnum <= 0)
8395 0 : ereport(ERROR,
8396 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8397 : errmsg("cannot alter system column \"%s\"",
8398 : colName)));
8399 :
8400 56 : if (!attTup->attidentity)
8401 6 : ereport(ERROR,
8402 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8403 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8404 : colName, RelationGetRelationName(rel))));
8405 :
8406 50 : if (generatedEl)
8407 : {
8408 44 : attTup->attidentity = defGetInt32(generatedEl);
8409 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8410 :
8411 44 : InvokeObjectPostAlterHook(RelationRelationId,
8412 : RelationGetRelid(rel),
8413 : attTup->attnum);
8414 44 : ObjectAddressSubSet(address, RelationRelationId,
8415 : RelationGetRelid(rel), attnum);
8416 : }
8417 : else
8418 6 : address = InvalidObjectAddress;
8419 :
8420 50 : heap_freetuple(tuple);
8421 50 : table_close(attrelation, RowExclusiveLock);
8422 :
8423 : /*
8424 : * Recurse to propagate the identity change to partitions. Identity is not
8425 : * inherited in regular inheritance children.
8426 : */
8427 50 : if (generatedEl && recurse && ispartitioned)
8428 : {
8429 : List *children;
8430 : ListCell *lc;
8431 :
8432 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8433 :
8434 18 : foreach(lc, children)
8435 : {
8436 : Relation childrel;
8437 :
8438 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8439 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8440 12 : table_close(childrel, NoLock);
8441 : }
8442 : }
8443 :
8444 50 : return address;
8445 : }
8446 :
8447 : /*
8448 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8449 : *
8450 : * Return the address of the affected column.
8451 : */
8452 : static ObjectAddress
8453 68 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8454 : bool recurse, bool recursing)
8455 : {
8456 : HeapTuple tuple;
8457 : Form_pg_attribute attTup;
8458 : AttrNumber attnum;
8459 : Relation attrelation;
8460 : ObjectAddress address;
8461 : Oid seqid;
8462 : ObjectAddress seqaddress;
8463 : bool ispartitioned;
8464 :
8465 68 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8466 68 : if (ispartitioned && !recurse)
8467 6 : ereport(ERROR,
8468 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8469 : errmsg("cannot drop identity from a column of only the partitioned table"),
8470 : errhint("Do not specify the ONLY keyword.")));
8471 :
8472 62 : if (rel->rd_rel->relispartition && !recursing)
8473 6 : ereport(ERROR,
8474 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8475 : errmsg("cannot drop identity from a column of a partition"));
8476 :
8477 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8478 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8479 56 : if (!HeapTupleIsValid(tuple))
8480 0 : ereport(ERROR,
8481 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8482 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8483 : colName, RelationGetRelationName(rel))));
8484 :
8485 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8486 56 : attnum = attTup->attnum;
8487 :
8488 56 : if (attnum <= 0)
8489 0 : ereport(ERROR,
8490 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8491 : errmsg("cannot alter system column \"%s\"",
8492 : colName)));
8493 :
8494 56 : if (!attTup->attidentity)
8495 : {
8496 12 : if (!missing_ok)
8497 6 : ereport(ERROR,
8498 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8499 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8500 : colName, RelationGetRelationName(rel))));
8501 : else
8502 : {
8503 6 : ereport(NOTICE,
8504 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8505 : colName, RelationGetRelationName(rel))));
8506 6 : heap_freetuple(tuple);
8507 6 : table_close(attrelation, RowExclusiveLock);
8508 6 : return InvalidObjectAddress;
8509 : }
8510 : }
8511 :
8512 44 : attTup->attidentity = '\0';
8513 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8514 :
8515 44 : InvokeObjectPostAlterHook(RelationRelationId,
8516 : RelationGetRelid(rel),
8517 : attTup->attnum);
8518 44 : ObjectAddressSubSet(address, RelationRelationId,
8519 : RelationGetRelid(rel), attnum);
8520 44 : heap_freetuple(tuple);
8521 :
8522 44 : table_close(attrelation, RowExclusiveLock);
8523 :
8524 : /*
8525 : * Recurse to drop the identity from column in partitions. Identity is
8526 : * not inherited in regular inheritance children so ignore them.
8527 : */
8528 44 : if (recurse && ispartitioned)
8529 : {
8530 : List *children;
8531 : ListCell *lc;
8532 :
8533 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8534 :
8535 12 : foreach(lc, children)
8536 : {
8537 : Relation childrel;
8538 :
8539 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8540 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8541 6 : table_close(childrel, NoLock);
8542 : }
8543 : }
8544 :
8545 44 : if (!recursing)
8546 : {
8547 : /* drop the internal sequence */
8548 32 : seqid = getIdentitySequence(rel, attnum, false);
8549 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8550 : RelationRelationId, DEPENDENCY_INTERNAL);
8551 32 : CommandCounterIncrement();
8552 32 : seqaddress.classId = RelationRelationId;
8553 32 : seqaddress.objectId = seqid;
8554 32 : seqaddress.objectSubId = 0;
8555 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8556 : }
8557 :
8558 44 : return address;
8559 : }
8560 :
8561 : /*
8562 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8563 : *
8564 : * Return the address of the affected column.
8565 : */
8566 : static ObjectAddress
8567 180 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8568 : Node *newExpr, LOCKMODE lockmode)
8569 : {
8570 : HeapTuple tuple;
8571 : Form_pg_attribute attTup;
8572 : AttrNumber attnum;
8573 : char attgenerated;
8574 : bool rewrite;
8575 : Oid attrdefoid;
8576 : ObjectAddress address;
8577 : Expr *defval;
8578 : NewColumnValue *newval;
8579 : RawColumnDefault *rawEnt;
8580 :
8581 180 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8582 180 : if (!HeapTupleIsValid(tuple))
8583 0 : ereport(ERROR,
8584 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8585 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8586 : colName, RelationGetRelationName(rel))));
8587 :
8588 180 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8589 :
8590 180 : attnum = attTup->attnum;
8591 180 : if (attnum <= 0)
8592 0 : ereport(ERROR,
8593 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8594 : errmsg("cannot alter system column \"%s\"",
8595 : colName)));
8596 :
8597 180 : attgenerated = attTup->attgenerated;
8598 180 : if (!attgenerated)
8599 12 : ereport(ERROR,
8600 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8601 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8602 : colName, RelationGetRelationName(rel))));
8603 :
8604 : /*
8605 : * TODO: This could be done, just need to recheck any constraints
8606 : * afterwards.
8607 : */
8608 168 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8609 90 : rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8610 12 : ereport(ERROR,
8611 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8612 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables with check constraints"),
8613 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8614 : colName, RelationGetRelationName(rel))));
8615 :
8616 156 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8617 24 : tab->verify_new_notnull = true;
8618 :
8619 : /*
8620 : * We need to prevent this because a change of expression could affect a
8621 : * row filter and inject expressions that are not permitted in a row
8622 : * filter. XXX We could try to have a more precise check to catch only
8623 : * publications with row filters, or even re-verify the row filter
8624 : * expressions.
8625 : */
8626 234 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8627 78 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
8628 6 : ereport(ERROR,
8629 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8630 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables that are part of a publication"),
8631 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8632 : colName, RelationGetRelationName(rel))));
8633 :
8634 150 : rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8635 :
8636 150 : ReleaseSysCache(tuple);
8637 :
8638 150 : if (rewrite)
8639 : {
8640 : /*
8641 : * Clear all the missing values if we're rewriting the table, since
8642 : * this renders them pointless.
8643 : */
8644 78 : RelationClearMissing(rel);
8645 :
8646 : /* make sure we don't conflict with later attribute modifications */
8647 78 : CommandCounterIncrement();
8648 :
8649 : /*
8650 : * Find everything that depends on the column (constraints, indexes,
8651 : * etc), and record enough information to let us recreate the objects
8652 : * after rewrite.
8653 : */
8654 78 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8655 : }
8656 :
8657 : /*
8658 : * Drop the dependency records of the GENERATED expression, in particular
8659 : * its INTERNAL dependency on the column, which would otherwise cause
8660 : * dependency.c to refuse to perform the deletion.
8661 : */
8662 150 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8663 150 : if (!OidIsValid(attrdefoid))
8664 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8665 : RelationGetRelid(rel), attnum);
8666 150 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8667 :
8668 : /* Make above changes visible */
8669 150 : CommandCounterIncrement();
8670 :
8671 : /*
8672 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8673 : * safety, but at present we do not expect anything to depend on the
8674 : * expression.
8675 : */
8676 150 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8677 : false, false);
8678 :
8679 : /* Prepare to store the new expression, in the catalogs */
8680 150 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8681 150 : rawEnt->attnum = attnum;
8682 150 : rawEnt->raw_default = newExpr;
8683 150 : rawEnt->generated = attgenerated;
8684 :
8685 : /* Store the generated expression */
8686 150 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8687 : false, true, false, NULL);
8688 :
8689 : /* Make above new expression visible */
8690 150 : CommandCounterIncrement();
8691 :
8692 150 : if (rewrite)
8693 : {
8694 : /* Prepare for table rewrite */
8695 78 : defval = (Expr *) build_column_default(rel, attnum);
8696 :
8697 78 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8698 78 : newval->attnum = attnum;
8699 78 : newval->expr = expression_planner(defval);
8700 78 : newval->is_generated = true;
8701 :
8702 78 : tab->newvals = lappend(tab->newvals, newval);
8703 78 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8704 : }
8705 :
8706 : /* Drop any pg_statistic entry for the column */
8707 150 : RemoveStatistics(RelationGetRelid(rel), attnum);
8708 :
8709 150 : InvokeObjectPostAlterHook(RelationRelationId,
8710 : RelationGetRelid(rel), attnum);
8711 :
8712 150 : ObjectAddressSubSet(address, RelationRelationId,
8713 : RelationGetRelid(rel), attnum);
8714 150 : return address;
8715 : }
8716 :
8717 : /*
8718 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8719 : */
8720 : static void
8721 86 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8722 : {
8723 : /*
8724 : * Reject ONLY if there are child tables. We could implement this, but it
8725 : * is a bit complicated. GENERATED clauses must be attached to the column
8726 : * definition and cannot be added later like DEFAULT, so if a child table
8727 : * has a generation expression that the parent does not have, the child
8728 : * column will necessarily be an attislocal column. So to implement ONLY
8729 : * here, we'd need extra code to update attislocal of the direct child
8730 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8731 : * resulting state can be properly dumped and restored.
8732 : */
8733 110 : if (!recurse &&
8734 24 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8735 12 : ereport(ERROR,
8736 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8737 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8738 :
8739 : /*
8740 : * Cannot drop generation expression from inherited columns.
8741 : */
8742 74 : if (!recursing)
8743 : {
8744 : HeapTuple tuple;
8745 : Form_pg_attribute attTup;
8746 :
8747 62 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8748 62 : if (!HeapTupleIsValid(tuple))
8749 0 : ereport(ERROR,
8750 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8751 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8752 : cmd->name, RelationGetRelationName(rel))));
8753 :
8754 62 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8755 :
8756 62 : if (attTup->attinhcount > 0)
8757 12 : ereport(ERROR,
8758 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8759 : errmsg("cannot drop generation expression from inherited column")));
8760 : }
8761 62 : }
8762 :
8763 : /*
8764 : * Return the address of the affected column.
8765 : */
8766 : static ObjectAddress
8767 56 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8768 : {
8769 : HeapTuple tuple;
8770 : Form_pg_attribute attTup;
8771 : AttrNumber attnum;
8772 : Relation attrelation;
8773 : Oid attrdefoid;
8774 : ObjectAddress address;
8775 :
8776 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8777 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8778 56 : if (!HeapTupleIsValid(tuple))
8779 0 : ereport(ERROR,
8780 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8781 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8782 : colName, RelationGetRelationName(rel))));
8783 :
8784 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8785 56 : attnum = attTup->attnum;
8786 :
8787 56 : if (attnum <= 0)
8788 0 : ereport(ERROR,
8789 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8790 : errmsg("cannot alter system column \"%s\"",
8791 : colName)));
8792 :
8793 : /*
8794 : * TODO: This could be done, but it would need a table rewrite to
8795 : * materialize the generated values. Note that for the time being, we
8796 : * still error with missing_ok, so that we don't silently leave the column
8797 : * as generated.
8798 : */
8799 56 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8800 12 : ereport(ERROR,
8801 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8802 : errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8803 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8804 : colName, RelationGetRelationName(rel))));
8805 :
8806 44 : if (!attTup->attgenerated)
8807 : {
8808 24 : if (!missing_ok)
8809 12 : ereport(ERROR,
8810 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8811 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8812 : colName, RelationGetRelationName(rel))));
8813 : else
8814 : {
8815 12 : ereport(NOTICE,
8816 : (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8817 : colName, RelationGetRelationName(rel))));
8818 12 : heap_freetuple(tuple);
8819 12 : table_close(attrelation, RowExclusiveLock);
8820 12 : return InvalidObjectAddress;
8821 : }
8822 : }
8823 :
8824 : /*
8825 : * Mark the column as no longer generated. (The atthasdef flag needs to
8826 : * get cleared too, but RemoveAttrDefault will handle that.)
8827 : */
8828 20 : attTup->attgenerated = '\0';
8829 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8830 :
8831 20 : InvokeObjectPostAlterHook(RelationRelationId,
8832 : RelationGetRelid(rel),
8833 : attnum);
8834 20 : heap_freetuple(tuple);
8835 :
8836 20 : table_close(attrelation, RowExclusiveLock);
8837 :
8838 : /*
8839 : * Drop the dependency records of the GENERATED expression, in particular
8840 : * its INTERNAL dependency on the column, which would otherwise cause
8841 : * dependency.c to refuse to perform the deletion.
8842 : */
8843 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8844 20 : if (!OidIsValid(attrdefoid))
8845 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8846 : RelationGetRelid(rel), attnum);
8847 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8848 :
8849 : /* Make above changes visible */
8850 20 : CommandCounterIncrement();
8851 :
8852 : /*
8853 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8854 : * safety, but at present we do not expect anything to depend on the
8855 : * default.
8856 : */
8857 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8858 : false, false);
8859 :
8860 20 : ObjectAddressSubSet(address, RelationRelationId,
8861 : RelationGetRelid(rel), attnum);
8862 20 : return address;
8863 : }
8864 :
8865 : /*
8866 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8867 : *
8868 : * Return value is the address of the modified column
8869 : */
8870 : static ObjectAddress
8871 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8872 : {
8873 164 : int newtarget = 0;
8874 : bool newtarget_default;
8875 : Relation attrelation;
8876 : HeapTuple tuple,
8877 : newtuple;
8878 : Form_pg_attribute attrtuple;
8879 : AttrNumber attnum;
8880 : ObjectAddress address;
8881 : Datum repl_val[Natts_pg_attribute];
8882 : bool repl_null[Natts_pg_attribute];
8883 : bool repl_repl[Natts_pg_attribute];
8884 :
8885 : /*
8886 : * We allow referencing columns by numbers only for indexes, since table
8887 : * column numbers could contain gaps if columns are later dropped.
8888 : */
8889 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8890 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8891 : !colName)
8892 0 : ereport(ERROR,
8893 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8894 : errmsg("cannot refer to non-index column by number")));
8895 :
8896 : /* -1 was used in previous versions for the default setting */
8897 164 : if (newValue && intVal(newValue) != -1)
8898 : {
8899 120 : newtarget = intVal(newValue);
8900 120 : newtarget_default = false;
8901 : }
8902 : else
8903 44 : newtarget_default = true;
8904 :
8905 164 : if (!newtarget_default)
8906 : {
8907 : /*
8908 : * Limit target to a sane range
8909 : */
8910 120 : if (newtarget < 0)
8911 : {
8912 0 : ereport(ERROR,
8913 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8914 : errmsg("statistics target %d is too low",
8915 : newtarget)));
8916 : }
8917 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8918 : {
8919 0 : newtarget = MAX_STATISTICS_TARGET;
8920 0 : ereport(WARNING,
8921 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8922 : errmsg("lowering statistics target to %d",
8923 : newtarget)));
8924 : }
8925 : }
8926 :
8927 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8928 :
8929 164 : if (colName)
8930 : {
8931 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8932 :
8933 100 : if (!HeapTupleIsValid(tuple))
8934 12 : ereport(ERROR,
8935 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8936 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8937 : colName, RelationGetRelationName(rel))));
8938 : }
8939 : else
8940 : {
8941 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8942 :
8943 64 : if (!HeapTupleIsValid(tuple))
8944 12 : ereport(ERROR,
8945 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8946 : errmsg("column number %d of relation \"%s\" does not exist",
8947 : colNum, RelationGetRelationName(rel))));
8948 : }
8949 :
8950 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8951 :
8952 140 : attnum = attrtuple->attnum;
8953 140 : if (attnum <= 0)
8954 0 : ereport(ERROR,
8955 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8956 : errmsg("cannot alter system column \"%s\"",
8957 : colName)));
8958 :
8959 : /*
8960 : * Prevent this as long as the ANALYZE code skips virtual generated
8961 : * columns.
8962 : */
8963 140 : if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8964 0 : ereport(ERROR,
8965 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8966 : errmsg("cannot alter statistics on virtual generated column \"%s\"",
8967 : colName)));
8968 :
8969 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8970 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8971 : {
8972 52 : if (attnum > rel->rd_index->indnkeyatts)
8973 6 : ereport(ERROR,
8974 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8975 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8976 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8977 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8978 18 : ereport(ERROR,
8979 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8980 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8981 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8982 : errhint("Alter statistics on table column instead.")));
8983 : }
8984 :
8985 : /* Build new tuple. */
8986 116 : memset(repl_null, false, sizeof(repl_null));
8987 116 : memset(repl_repl, false, sizeof(repl_repl));
8988 116 : if (!newtarget_default)
8989 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8990 : else
8991 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8992 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8993 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8994 : repl_val, repl_null, repl_repl);
8995 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8996 :
8997 116 : InvokeObjectPostAlterHook(RelationRelationId,
8998 : RelationGetRelid(rel),
8999 : attrtuple->attnum);
9000 116 : ObjectAddressSubSet(address, RelationRelationId,
9001 : RelationGetRelid(rel), attnum);
9002 :
9003 116 : heap_freetuple(newtuple);
9004 :
9005 116 : ReleaseSysCache(tuple);
9006 :
9007 116 : table_close(attrelation, RowExclusiveLock);
9008 :
9009 116 : return address;
9010 : }
9011 :
9012 : /*
9013 : * Return value is the address of the modified column
9014 : */
9015 : static ObjectAddress
9016 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
9017 : bool isReset, LOCKMODE lockmode)
9018 : {
9019 : Relation attrelation;
9020 : HeapTuple tuple,
9021 : newtuple;
9022 : Form_pg_attribute attrtuple;
9023 : AttrNumber attnum;
9024 : Datum datum,
9025 : newOptions;
9026 : bool isnull;
9027 : ObjectAddress address;
9028 : Datum repl_val[Natts_pg_attribute];
9029 : bool repl_null[Natts_pg_attribute];
9030 : bool repl_repl[Natts_pg_attribute];
9031 :
9032 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9033 :
9034 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9035 :
9036 32 : if (!HeapTupleIsValid(tuple))
9037 0 : ereport(ERROR,
9038 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9039 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9040 : colName, RelationGetRelationName(rel))));
9041 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9042 :
9043 32 : attnum = attrtuple->attnum;
9044 32 : if (attnum <= 0)
9045 0 : ereport(ERROR,
9046 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9047 : errmsg("cannot alter system column \"%s\"",
9048 : colName)));
9049 :
9050 : /* Generate new proposed attoptions (text array) */
9051 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9052 : &isnull);
9053 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9054 : castNode(List, options), NULL, NULL,
9055 : false, isReset);
9056 : /* Validate new options */
9057 32 : (void) attribute_reloptions(newOptions, true);
9058 :
9059 : /* Build new tuple. */
9060 32 : memset(repl_null, false, sizeof(repl_null));
9061 32 : memset(repl_repl, false, sizeof(repl_repl));
9062 32 : if (newOptions != (Datum) 0)
9063 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9064 : else
9065 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
9066 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9067 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9068 : repl_val, repl_null, repl_repl);
9069 :
9070 : /* Update system catalog. */
9071 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9072 :
9073 32 : InvokeObjectPostAlterHook(RelationRelationId,
9074 : RelationGetRelid(rel),
9075 : attrtuple->attnum);
9076 32 : ObjectAddressSubSet(address, RelationRelationId,
9077 : RelationGetRelid(rel), attnum);
9078 :
9079 32 : heap_freetuple(newtuple);
9080 :
9081 32 : ReleaseSysCache(tuple);
9082 :
9083 32 : table_close(attrelation, RowExclusiveLock);
9084 :
9085 32 : return address;
9086 : }
9087 :
9088 : /*
9089 : * Helper function for ATExecSetStorage and ATExecSetCompression
9090 : *
9091 : * Set the attstorage and/or attcompression fields for index columns
9092 : * associated with the specified table column.
9093 : */
9094 : static void
9095 318 : SetIndexStorageProperties(Relation rel, Relation attrelation,
9096 : AttrNumber attnum,
9097 : bool setstorage, char newstorage,
9098 : bool setcompression, char newcompression,
9099 : LOCKMODE lockmode)
9100 : {
9101 : ListCell *lc;
9102 :
9103 402 : foreach(lc, RelationGetIndexList(rel))
9104 : {
9105 84 : Oid indexoid = lfirst_oid(lc);
9106 : Relation indrel;
9107 84 : AttrNumber indattnum = 0;
9108 : HeapTuple tuple;
9109 :
9110 84 : indrel = index_open(indexoid, lockmode);
9111 :
9112 144 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9113 : {
9114 90 : if (indrel->rd_index->indkey.values[i] == attnum)
9115 : {
9116 30 : indattnum = i + 1;
9117 30 : break;
9118 : }
9119 : }
9120 :
9121 84 : if (indattnum == 0)
9122 : {
9123 54 : index_close(indrel, lockmode);
9124 54 : continue;
9125 : }
9126 :
9127 30 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9128 :
9129 30 : if (HeapTupleIsValid(tuple))
9130 : {
9131 30 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9132 :
9133 30 : if (setstorage)
9134 24 : attrtuple->attstorage = newstorage;
9135 :
9136 30 : if (setcompression)
9137 6 : attrtuple->attcompression = newcompression;
9138 :
9139 30 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9140 :
9141 30 : InvokeObjectPostAlterHook(RelationRelationId,
9142 : RelationGetRelid(rel),
9143 : attrtuple->attnum);
9144 :
9145 30 : heap_freetuple(tuple);
9146 : }
9147 :
9148 30 : index_close(indrel, lockmode);
9149 : }
9150 318 : }
9151 :
9152 : /*
9153 : * ALTER TABLE ALTER COLUMN SET STORAGE
9154 : *
9155 : * Return value is the address of the modified column
9156 : */
9157 : static ObjectAddress
9158 246 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9159 : {
9160 : Relation attrelation;
9161 : HeapTuple tuple;
9162 : Form_pg_attribute attrtuple;
9163 : AttrNumber attnum;
9164 : ObjectAddress address;
9165 :
9166 246 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9167 :
9168 246 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9169 :
9170 246 : if (!HeapTupleIsValid(tuple))
9171 12 : ereport(ERROR,
9172 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9173 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9174 : colName, RelationGetRelationName(rel))));
9175 234 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9176 :
9177 234 : attnum = attrtuple->attnum;
9178 234 : if (attnum <= 0)
9179 0 : ereport(ERROR,
9180 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9181 : errmsg("cannot alter system column \"%s\"",
9182 : colName)));
9183 :
9184 234 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9185 :
9186 234 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9187 :
9188 234 : InvokeObjectPostAlterHook(RelationRelationId,
9189 : RelationGetRelid(rel),
9190 : attrtuple->attnum);
9191 :
9192 : /*
9193 : * Apply the change to indexes as well (only for simple index columns,
9194 : * matching behavior of index.c ConstructTupleDescriptor()).
9195 : */
9196 234 : SetIndexStorageProperties(rel, attrelation, attnum,
9197 234 : true, attrtuple->attstorage,
9198 : false, 0,
9199 : lockmode);
9200 :
9201 234 : heap_freetuple(tuple);
9202 :
9203 234 : table_close(attrelation, RowExclusiveLock);
9204 :
9205 234 : ObjectAddressSubSet(address, RelationRelationId,
9206 : RelationGetRelid(rel), attnum);
9207 234 : return address;
9208 : }
9209 :
9210 :
9211 : /*
9212 : * ALTER TABLE DROP COLUMN
9213 : *
9214 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9215 : * because we have to decide at runtime whether to recurse or not depending
9216 : * on whether attinhcount goes to zero or not. (We can't check this in a
9217 : * static pre-pass because it won't handle multiple inheritance situations
9218 : * correctly.)
9219 : */
9220 : static void
9221 1650 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9222 : AlterTableCmd *cmd, LOCKMODE lockmode,
9223 : AlterTableUtilityContext *context)
9224 : {
9225 1650 : if (rel->rd_rel->reloftype && !recursing)
9226 6 : ereport(ERROR,
9227 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9228 : errmsg("cannot drop column from typed table")));
9229 :
9230 1644 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9231 82 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9232 :
9233 1638 : if (recurse)
9234 1360 : cmd->recurse = true;
9235 1638 : }
9236 :
9237 : /*
9238 : * Drops column 'colName' from relation 'rel' and returns the address of the
9239 : * dropped column. The column is also dropped (or marked as no longer
9240 : * inherited from relation) from the relation's inheritance children, if any.
9241 : *
9242 : * In the recursive invocations for inheritance child relations, instead of
9243 : * dropping the column directly (if to be dropped at all), its object address
9244 : * is added to 'addrs', which must be non-NULL in such invocations. All
9245 : * columns are dropped at the same time after all the children have been
9246 : * checked recursively.
9247 : */
9248 : static ObjectAddress
9249 2194 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9250 : DropBehavior behavior,
9251 : bool recurse, bool recursing,
9252 : bool missing_ok, LOCKMODE lockmode,
9253 : ObjectAddresses *addrs)
9254 : {
9255 : HeapTuple tuple;
9256 : Form_pg_attribute targetatt;
9257 : AttrNumber attnum;
9258 : List *children;
9259 : ObjectAddress object;
9260 : bool is_expr;
9261 :
9262 : /* At top level, permission check was done in ATPrepCmd, else do it */
9263 2194 : if (recursing)
9264 556 : ATSimplePermissions(AT_DropColumn, rel,
9265 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9266 :
9267 : /* Initialize addrs on the first invocation */
9268 : Assert(!recursing || addrs != NULL);
9269 :
9270 : /* since this function recurses, it could be driven to stack overflow */
9271 2194 : check_stack_depth();
9272 :
9273 2194 : if (!recursing)
9274 1638 : addrs = new_object_addresses();
9275 :
9276 : /*
9277 : * get the number of the attribute
9278 : */
9279 2194 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9280 2194 : if (!HeapTupleIsValid(tuple))
9281 : {
9282 54 : if (!missing_ok)
9283 : {
9284 36 : ereport(ERROR,
9285 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9286 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9287 : colName, RelationGetRelationName(rel))));
9288 : }
9289 : else
9290 : {
9291 18 : ereport(NOTICE,
9292 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9293 : colName, RelationGetRelationName(rel))));
9294 18 : return InvalidObjectAddress;
9295 : }
9296 : }
9297 2140 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9298 :
9299 2140 : attnum = targetatt->attnum;
9300 :
9301 : /* Can't drop a system attribute */
9302 2140 : if (attnum <= 0)
9303 6 : ereport(ERROR,
9304 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9305 : errmsg("cannot drop system column \"%s\"",
9306 : colName)));
9307 :
9308 : /*
9309 : * Don't drop inherited columns, unless recursing (presumably from a drop
9310 : * of the parent column)
9311 : */
9312 2134 : if (targetatt->attinhcount > 0 && !recursing)
9313 48 : ereport(ERROR,
9314 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9315 : errmsg("cannot drop inherited column \"%s\"",
9316 : colName)));
9317 :
9318 : /*
9319 : * Don't drop columns used in the partition key, either. (If we let this
9320 : * go through, the key column's dependencies would cause a cascaded drop
9321 : * of the whole table, which is surely not what the user expected.)
9322 : */
9323 2086 : if (has_partition_attrs(rel,
9324 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9325 : &is_expr))
9326 30 : ereport(ERROR,
9327 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9328 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9329 : colName, RelationGetRelationName(rel))));
9330 :
9331 2056 : ReleaseSysCache(tuple);
9332 :
9333 : /*
9334 : * Propagate to children as appropriate. Unlike most other ALTER
9335 : * routines, we have to do this one level of recursion at a time; we can't
9336 : * use find_all_inheritors to do it in one pass.
9337 : */
9338 : children =
9339 2056 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9340 :
9341 2056 : if (children)
9342 : {
9343 : Relation attr_rel;
9344 : ListCell *child;
9345 :
9346 : /*
9347 : * In case of a partitioned table, the column must be dropped from the
9348 : * partitions as well.
9349 : */
9350 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9351 6 : ereport(ERROR,
9352 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9353 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9354 : errhint("Do not specify the ONLY keyword.")));
9355 :
9356 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9357 882 : foreach(child, children)
9358 : {
9359 592 : Oid childrelid = lfirst_oid(child);
9360 : Relation childrel;
9361 : Form_pg_attribute childatt;
9362 :
9363 : /* find_inheritance_children already got lock */
9364 592 : childrel = table_open(childrelid, NoLock);
9365 592 : CheckAlterTableIsSafe(childrel);
9366 :
9367 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9368 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9369 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9370 : colName, childrelid);
9371 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9372 :
9373 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9374 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9375 : childrelid, colName);
9376 :
9377 592 : if (recurse)
9378 : {
9379 : /*
9380 : * If the child column has other definition sources, just
9381 : * decrement its inheritance count; if not, recurse to delete
9382 : * it.
9383 : */
9384 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9385 : {
9386 : /* Time to delete this child column, too */
9387 556 : ATExecDropColumn(wqueue, childrel, colName,
9388 : behavior, true, true,
9389 : false, lockmode, addrs);
9390 : }
9391 : else
9392 : {
9393 : /* Child column must survive my deletion */
9394 12 : childatt->attinhcount--;
9395 :
9396 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9397 :
9398 : /* Make update visible */
9399 12 : CommandCounterIncrement();
9400 : }
9401 : }
9402 : else
9403 : {
9404 : /*
9405 : * If we were told to drop ONLY in this table (no recursion),
9406 : * we need to mark the inheritors' attributes as locally
9407 : * defined rather than inherited.
9408 : */
9409 24 : childatt->attinhcount--;
9410 24 : childatt->attislocal = true;
9411 :
9412 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9413 :
9414 : /* Make update visible */
9415 24 : CommandCounterIncrement();
9416 : }
9417 :
9418 586 : heap_freetuple(tuple);
9419 :
9420 586 : table_close(childrel, NoLock);
9421 : }
9422 290 : table_close(attr_rel, RowExclusiveLock);
9423 : }
9424 :
9425 : /* Add object to delete */
9426 2044 : object.classId = RelationRelationId;
9427 2044 : object.objectId = RelationGetRelid(rel);
9428 2044 : object.objectSubId = attnum;
9429 2044 : add_exact_object_address(&object, addrs);
9430 :
9431 2044 : if (!recursing)
9432 : {
9433 : /* Recursion has ended, drop everything that was collected */
9434 1494 : performMultipleDeletions(addrs, behavior, 0);
9435 1440 : free_object_addresses(addrs);
9436 : }
9437 :
9438 1990 : return object;
9439 : }
9440 :
9441 : /*
9442 : * Prepare to add a primary key on a table, by adding not-null constraints
9443 : * on all columns.
9444 : *
9445 : * The not-null constraints for a primary key must cover the whole inheritance
9446 : * hierarchy (failing to ensure that leads to funny corner cases). For the
9447 : * normal case where we're asked to recurse, this routine checks if the
9448 : * not-null constraints exist already, and if not queues a requirement for
9449 : * them to be created by phase 2.
9450 : *
9451 : * For the case where we're asked not to recurse, we verify that a not-null
9452 : * constraint exists on each column of each (direct) child table, throwing an
9453 : * error if not. Not throwing an error would also work, because a not-null
9454 : * constraint would be created anyway, but it'd cause a silent scan of the
9455 : * child table to verify absence of nulls. We prefer to let the user know so
9456 : * that they can add the constraint manually without having to hold
9457 : * AccessExclusiveLock while at it.
9458 : *
9459 : * However, it's also important that we do not acquire locks on children if
9460 : * the not-null constraints already exist on the parent, to avoid risking
9461 : * deadlocks during parallel pg_restore of PKs on partitioned tables.
9462 : */
9463 : static void
9464 16062 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9465 : bool recurse, LOCKMODE lockmode,
9466 : AlterTableUtilityContext *context)
9467 : {
9468 : Constraint *pkconstr;
9469 16062 : List *children = NIL;
9470 16062 : bool got_children = false;
9471 :
9472 16062 : pkconstr = castNode(Constraint, cmd->def);
9473 16062 : if (pkconstr->contype != CONSTR_PRIMARY)
9474 9092 : return;
9475 :
9476 : /* Verify that columns are not-null, or request that they be made so */
9477 15196 : foreach_node(String, column, pkconstr->keys)
9478 : {
9479 : AlterTableCmd *newcmd;
9480 : Constraint *nnconstr;
9481 : HeapTuple tuple;
9482 :
9483 : /*
9484 : * First check if a suitable constraint exists. If it does, we don't
9485 : * need to request another one. We do need to bail out if it's not
9486 : * valid, though.
9487 : */
9488 1316 : tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9489 1316 : if (tuple != NULL)
9490 : {
9491 806 : verifyNotNullPKCompatible(tuple, strVal(column));
9492 :
9493 : /* All good with this one; don't request another */
9494 794 : heap_freetuple(tuple);
9495 794 : continue;
9496 : }
9497 510 : else if (!recurse)
9498 : {
9499 : /*
9500 : * No constraint on this column. Asked not to recurse, we won't
9501 : * create one here, but verify that all children have one.
9502 : */
9503 36 : if (!got_children)
9504 : {
9505 36 : children = find_inheritance_children(RelationGetRelid(rel),
9506 : lockmode);
9507 : /* only search for children on the first time through */
9508 36 : got_children = true;
9509 : }
9510 :
9511 72 : foreach_oid(childrelid, children)
9512 : {
9513 : HeapTuple tup;
9514 :
9515 36 : tup = findNotNullConstraint(childrelid, strVal(column));
9516 36 : if (!tup)
9517 6 : ereport(ERROR,
9518 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9519 : strVal(column), get_rel_name(childrelid)));
9520 : /* verify it's good enough */
9521 30 : verifyNotNullPKCompatible(tup, strVal(column));
9522 : }
9523 : }
9524 :
9525 : /* This column is not already not-null, so add it to the queue */
9526 492 : nnconstr = makeNotNullConstraint(column);
9527 :
9528 492 : newcmd = makeNode(AlterTableCmd);
9529 492 : newcmd->subtype = AT_AddConstraint;
9530 : /* note we force recurse=true here; see above */
9531 492 : newcmd->recurse = true;
9532 492 : newcmd->def = (Node *) nnconstr;
9533 :
9534 492 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9535 : }
9536 : }
9537 :
9538 : /*
9539 : * Verify whether the given not-null constraint is compatible with a
9540 : * primary key. If not, an error is thrown.
9541 : */
9542 : static void
9543 836 : verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9544 : {
9545 836 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
9546 :
9547 836 : if (conForm->contype != CONSTRAINT_NOTNULL)
9548 0 : elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9549 :
9550 : /* a NO INHERIT constraint is no good */
9551 836 : if (conForm->connoinherit)
9552 12 : ereport(ERROR,
9553 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9554 : errmsg("cannot create primary key on column \"%s\"", colname),
9555 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9556 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9557 : NameStr(conForm->conname), colname,
9558 : get_rel_name(conForm->conrelid), "NO INHERIT"),
9559 : errhint("You might need to make the existing constraint inheritable using %s.",
9560 : "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9561 :
9562 : /* an unvalidated constraint is no good */
9563 824 : if (!conForm->convalidated)
9564 12 : ereport(ERROR,
9565 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9566 : errmsg("cannot create primary key on column \"%s\"", colname),
9567 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9568 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9569 : NameStr(conForm->conname), colname,
9570 : get_rel_name(conForm->conrelid), "NOT VALID"),
9571 : errhint("You might need to validate it using %s.",
9572 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
9573 812 : }
9574 :
9575 : /*
9576 : * ALTER TABLE ADD INDEX
9577 : *
9578 : * There is no such command in the grammar, but parse_utilcmd.c converts
9579 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9580 : * us schedule creation of the index at the appropriate time during ALTER.
9581 : *
9582 : * Return value is the address of the new index.
9583 : */
9584 : static ObjectAddress
9585 1898 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9586 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9587 : {
9588 : bool check_rights;
9589 : bool skip_build;
9590 : bool quiet;
9591 : ObjectAddress address;
9592 :
9593 : Assert(IsA(stmt, IndexStmt));
9594 : Assert(!stmt->concurrent);
9595 :
9596 : /* The IndexStmt has already been through transformIndexStmt */
9597 : Assert(stmt->transformed);
9598 :
9599 : /* suppress schema rights check when rebuilding existing index */
9600 1898 : check_rights = !is_rebuild;
9601 : /* skip index build if phase 3 will do it or we're reusing an old one */
9602 1898 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9603 : /* suppress notices when rebuilding existing index */
9604 1898 : quiet = is_rebuild;
9605 :
9606 1898 : address = DefineIndex(RelationGetRelid(rel),
9607 : stmt,
9608 : InvalidOid, /* no predefined OID */
9609 : InvalidOid, /* no parent index */
9610 : InvalidOid, /* no parent constraint */
9611 : -1, /* total_parts unknown */
9612 : true, /* is_alter_table */
9613 : check_rights,
9614 : false, /* check_not_in_use - we did it already */
9615 : skip_build,
9616 : quiet);
9617 :
9618 : /*
9619 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9620 : * new index instead of building from scratch. Restore associated fields.
9621 : * This may store InvalidSubTransactionId in both fields, in which case
9622 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9623 : * this after the CCI that made catalog rows visible to any rebuild. The
9624 : * DROP of the old edition of this index will have scheduled the storage
9625 : * for deletion at commit, so cancel that pending deletion.
9626 : */
9627 1728 : if (RelFileNumberIsValid(stmt->oldNumber))
9628 : {
9629 74 : Relation irel = index_open(address.objectId, NoLock);
9630 :
9631 74 : irel->rd_createSubid = stmt->oldCreateSubid;
9632 74 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9633 74 : RelationPreserveStorage(irel->rd_locator, true);
9634 74 : index_close(irel, NoLock);
9635 : }
9636 :
9637 1728 : return address;
9638 : }
9639 :
9640 : /*
9641 : * ALTER TABLE ADD STATISTICS
9642 : *
9643 : * This is no such command in the grammar, but we use this internally to add
9644 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9645 : * column type change.
9646 : */
9647 : static ObjectAddress
9648 14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9649 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9650 : {
9651 : ObjectAddress address;
9652 :
9653 : Assert(IsA(stmt, CreateStatsStmt));
9654 :
9655 : /* The CreateStatsStmt has already been through transformStatsStmt */
9656 : Assert(stmt->transformed);
9657 :
9658 14 : address = CreateStatistics(stmt);
9659 :
9660 14 : return address;
9661 : }
9662 :
9663 : /*
9664 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9665 : *
9666 : * Returns the address of the new constraint.
9667 : */
9668 : static ObjectAddress
9669 10420 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9670 : IndexStmt *stmt, LOCKMODE lockmode)
9671 : {
9672 10420 : Oid index_oid = stmt->indexOid;
9673 : Relation indexRel;
9674 : char *indexName;
9675 : IndexInfo *indexInfo;
9676 : char *constraintName;
9677 : char constraintType;
9678 : ObjectAddress address;
9679 : bits16 flags;
9680 :
9681 : Assert(IsA(stmt, IndexStmt));
9682 : Assert(OidIsValid(index_oid));
9683 : Assert(stmt->isconstraint);
9684 :
9685 : /*
9686 : * Doing this on partitioned tables is not a simple feature to implement,
9687 : * so let's punt for now.
9688 : */
9689 10420 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9690 6 : ereport(ERROR,
9691 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9692 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9693 :
9694 10414 : indexRel = index_open(index_oid, AccessShareLock);
9695 :
9696 10414 : indexName = pstrdup(RelationGetRelationName(indexRel));
9697 :
9698 10414 : indexInfo = BuildIndexInfo(indexRel);
9699 :
9700 : /* this should have been checked at parse time */
9701 10414 : if (!indexInfo->ii_Unique)
9702 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9703 :
9704 : /*
9705 : * Determine name to assign to constraint. We require a constraint to
9706 : * have the same name as the underlying index; therefore, use the index's
9707 : * existing name as the default constraint name, and if the user
9708 : * explicitly gives some other name for the constraint, rename the index
9709 : * to match.
9710 : */
9711 10414 : constraintName = stmt->idxname;
9712 10414 : if (constraintName == NULL)
9713 10388 : constraintName = indexName;
9714 26 : else if (strcmp(constraintName, indexName) != 0)
9715 : {
9716 20 : ereport(NOTICE,
9717 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9718 : indexName, constraintName)));
9719 20 : RenameRelationInternal(index_oid, constraintName, false, true);
9720 : }
9721 :
9722 : /* Extra checks needed if making primary key */
9723 10414 : if (stmt->primary)
9724 5882 : index_check_primary_key(rel, indexInfo, true, stmt);
9725 :
9726 : /* Note we currently don't support EXCLUSION constraints here */
9727 10408 : if (stmt->primary)
9728 5876 : constraintType = CONSTRAINT_PRIMARY;
9729 : else
9730 4532 : constraintType = CONSTRAINT_UNIQUE;
9731 :
9732 : /* Create the catalog entries for the constraint */
9733 10408 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9734 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9735 20816 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9736 10408 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9737 10408 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9738 :
9739 10408 : address = index_constraint_create(rel,
9740 : index_oid,
9741 : InvalidOid,
9742 : indexInfo,
9743 : constraintName,
9744 : constraintType,
9745 : flags,
9746 : allowSystemTableMods,
9747 : false); /* is_internal */
9748 :
9749 10408 : index_close(indexRel, NoLock);
9750 :
9751 10408 : return address;
9752 : }
9753 :
9754 : /*
9755 : * ALTER TABLE ADD CONSTRAINT
9756 : *
9757 : * Return value is the address of the new constraint; if no constraint was
9758 : * added, InvalidObjectAddress is returned.
9759 : */
9760 : static ObjectAddress
9761 12600 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9762 : Constraint *newConstraint, bool recurse, bool is_readd,
9763 : LOCKMODE lockmode)
9764 : {
9765 12600 : ObjectAddress address = InvalidObjectAddress;
9766 :
9767 : Assert(IsA(newConstraint, Constraint));
9768 :
9769 : /*
9770 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9771 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9772 : * parse_utilcmd.c).
9773 : */
9774 12600 : switch (newConstraint->contype)
9775 : {
9776 9962 : case CONSTR_CHECK:
9777 : case CONSTR_NOTNULL:
9778 : address =
9779 9962 : ATAddCheckNNConstraint(wqueue, tab, rel,
9780 : newConstraint, recurse, false, is_readd,
9781 : lockmode);
9782 9818 : break;
9783 :
9784 2638 : case CONSTR_FOREIGN:
9785 :
9786 : /*
9787 : * Assign or validate constraint name
9788 : */
9789 2638 : if (newConstraint->conname)
9790 : {
9791 1220 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9792 : RelationGetRelid(rel),
9793 1220 : newConstraint->conname))
9794 0 : ereport(ERROR,
9795 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9796 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9797 : newConstraint->conname,
9798 : RelationGetRelationName(rel))));
9799 : }
9800 : else
9801 1418 : newConstraint->conname =
9802 1418 : ChooseConstraintName(RelationGetRelationName(rel),
9803 1418 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9804 : "fkey",
9805 1418 : RelationGetNamespace(rel),
9806 : NIL);
9807 :
9808 2638 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9809 : newConstraint,
9810 : recurse, false,
9811 : lockmode);
9812 2090 : break;
9813 :
9814 0 : default:
9815 0 : elog(ERROR, "unrecognized constraint type: %d",
9816 : (int) newConstraint->contype);
9817 : }
9818 :
9819 11908 : return address;
9820 : }
9821 :
9822 : /*
9823 : * Generate the column-name portion of the constraint name for a new foreign
9824 : * key given the list of column names that reference the referenced
9825 : * table. This will be passed to ChooseConstraintName along with the parent
9826 : * table name and the "fkey" suffix.
9827 : *
9828 : * We know that less than NAMEDATALEN characters will actually be used, so we
9829 : * can truncate the result once we've generated that many.
9830 : *
9831 : * XXX see also ChooseExtendedStatisticNameAddition and
9832 : * ChooseIndexNameAddition.
9833 : */
9834 : static char *
9835 1418 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9836 : {
9837 : char buf[NAMEDATALEN * 2];
9838 1418 : int buflen = 0;
9839 : ListCell *lc;
9840 :
9841 1418 : buf[0] = '\0';
9842 3234 : foreach(lc, colnames)
9843 : {
9844 1816 : const char *name = strVal(lfirst(lc));
9845 :
9846 1816 : if (buflen > 0)
9847 398 : buf[buflen++] = '_'; /* insert _ between names */
9848 :
9849 : /*
9850 : * At this point we have buflen <= NAMEDATALEN. name should be less
9851 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9852 : */
9853 1816 : strlcpy(buf + buflen, name, NAMEDATALEN);
9854 1816 : buflen += strlen(buf + buflen);
9855 1816 : if (buflen >= NAMEDATALEN)
9856 0 : break;
9857 : }
9858 1418 : return pstrdup(buf);
9859 : }
9860 :
9861 : /*
9862 : * Add a check or not-null constraint to a single table and its children.
9863 : * Returns the address of the constraint added to the parent relation,
9864 : * if one gets added, or InvalidObjectAddress otherwise.
9865 : *
9866 : * Subroutine for ATExecAddConstraint.
9867 : *
9868 : * We must recurse to child tables during execution, rather than using
9869 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9870 : * constraints *must* be given the same name, else they won't be seen as
9871 : * related later. If the user didn't explicitly specify a name, then
9872 : * AddRelationNewConstraints would normally assign different names to the
9873 : * child constraints. To fix that, we must capture the name assigned at
9874 : * the parent table and pass that down.
9875 : */
9876 : static ObjectAddress
9877 10884 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9878 : Constraint *constr, bool recurse, bool recursing,
9879 : bool is_readd, LOCKMODE lockmode)
9880 : {
9881 : List *newcons;
9882 : ListCell *lcon;
9883 : List *children;
9884 : ListCell *child;
9885 10884 : ObjectAddress address = InvalidObjectAddress;
9886 :
9887 : /* Guard against stack overflow due to overly deep inheritance tree. */
9888 10884 : check_stack_depth();
9889 :
9890 : /* At top level, permission check was done in ATPrepCmd, else do it */
9891 10884 : if (recursing)
9892 788 : ATSimplePermissions(AT_AddConstraint, rel,
9893 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9894 :
9895 : /*
9896 : * Call AddRelationNewConstraints to do the work, making sure it works on
9897 : * a copy of the Constraint so transformExpr can't modify the original. It
9898 : * returns a list of cooked constraints.
9899 : *
9900 : * If the constraint ends up getting merged with a pre-existing one, it's
9901 : * omitted from the returned list, which is what we want: we do not need
9902 : * to do any validation work. That can only happen at child tables,
9903 : * though, since we disallow merging at the top level.
9904 : */
9905 10884 : newcons = AddRelationNewConstraints(rel, NIL,
9906 10884 : list_make1(copyObject(constr)),
9907 10884 : recursing || is_readd, /* allow_merge */
9908 10884 : !recursing, /* is_local */
9909 : is_readd, /* is_internal */
9910 10884 : NULL); /* queryString not available
9911 : * here */
9912 :
9913 : /* we don't expect more than one constraint here */
9914 : Assert(list_length(newcons) <= 1);
9915 :
9916 : /* Add each to-be-validated constraint to Phase 3's queue */
9917 21296 : foreach(lcon, newcons)
9918 : {
9919 10550 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9920 :
9921 10550 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9922 : {
9923 : NewConstraint *newcon;
9924 :
9925 882 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9926 882 : newcon->name = ccon->name;
9927 882 : newcon->contype = ccon->contype;
9928 882 : newcon->qual = ccon->expr;
9929 :
9930 882 : tab->constraints = lappend(tab->constraints, newcon);
9931 : }
9932 :
9933 : /* Save the actually assigned name if it was defaulted */
9934 10550 : if (constr->conname == NULL)
9935 8856 : constr->conname = ccon->name;
9936 :
9937 : /*
9938 : * If adding a valid not-null constraint, set the pg_attribute flag
9939 : * and tell phase 3 to verify existing rows, if needed. For an
9940 : * invalid constraint, just set attnotnull, without queueing
9941 : * verification.
9942 : */
9943 10550 : if (constr->contype == CONSTR_NOTNULL)
9944 9128 : set_attnotnull(wqueue, rel, ccon->attnum,
9945 9128 : !constr->skip_validation,
9946 9128 : !constr->skip_validation);
9947 :
9948 10550 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9949 : }
9950 :
9951 : /* At this point we must have a locked-down name to use */
9952 : Assert(newcons == NIL || constr->conname != NULL);
9953 :
9954 : /* Advance command counter in case same table is visited multiple times */
9955 10746 : CommandCounterIncrement();
9956 :
9957 : /*
9958 : * If the constraint got merged with an existing constraint, we're done.
9959 : * We mustn't recurse to child tables in this case, because they've
9960 : * already got the constraint, and visiting them again would lead to an
9961 : * incorrect value for coninhcount.
9962 : */
9963 10746 : if (newcons == NIL)
9964 196 : return address;
9965 :
9966 : /*
9967 : * If adding a NO INHERIT constraint, no need to find our children.
9968 : */
9969 10550 : if (constr->is_no_inherit)
9970 78 : return address;
9971 :
9972 : /*
9973 : * Propagate to children as appropriate. Unlike most other ALTER
9974 : * routines, we have to do this one level of recursion at a time; we can't
9975 : * use find_all_inheritors to do it in one pass.
9976 : */
9977 : children =
9978 10472 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9979 :
9980 : /*
9981 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9982 : * constraint creation only if there are no children currently. Error out
9983 : * otherwise.
9984 : */
9985 10472 : if (!recurse && children != NIL)
9986 6 : ereport(ERROR,
9987 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9988 : errmsg("constraint must be added to child tables too")));
9989 :
9990 : /*
9991 : * Recurse to create the constraint on each child.
9992 : */
9993 11224 : foreach(child, children)
9994 : {
9995 788 : Oid childrelid = lfirst_oid(child);
9996 : Relation childrel;
9997 : AlteredTableInfo *childtab;
9998 :
9999 : /* find_inheritance_children already got lock */
10000 788 : childrel = table_open(childrelid, NoLock);
10001 788 : CheckAlterTableIsSafe(childrel);
10002 :
10003 : /* Find or create work queue entry for this table */
10004 788 : childtab = ATGetQueueEntry(wqueue, childrel);
10005 :
10006 : /* Recurse to this child */
10007 788 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
10008 : constr, recurse, true, is_readd, lockmode);
10009 :
10010 758 : table_close(childrel, NoLock);
10011 : }
10012 :
10013 10436 : return address;
10014 : }
10015 :
10016 : /*
10017 : * Add a foreign-key constraint to a single table; return the new constraint's
10018 : * address.
10019 : *
10020 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
10021 : * lock on the rel, and have done appropriate validity checks for it.
10022 : * We do permissions checks here, however.
10023 : *
10024 : * When the referenced or referencing tables (or both) are partitioned,
10025 : * multiple pg_constraint rows are required -- one for each partitioned table
10026 : * and each partition on each side (fortunately, not one for every combination
10027 : * thereof). We also need action triggers on each leaf partition on the
10028 : * referenced side, and check triggers on each leaf partition on the
10029 : * referencing side.
10030 : */
10031 : static ObjectAddress
10032 2638 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
10033 : Constraint *fkconstraint,
10034 : bool recurse, bool recursing, LOCKMODE lockmode)
10035 : {
10036 : Relation pkrel;
10037 2638 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
10038 2638 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
10039 2638 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
10040 2638 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
10041 2638 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10042 2638 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10043 2638 : Oid opclasses[INDEX_MAX_KEYS] = {0};
10044 2638 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10045 2638 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10046 2638 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10047 2638 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10048 : bool with_period;
10049 : bool pk_has_without_overlaps;
10050 : int i;
10051 : int numfks,
10052 : numpks,
10053 : numfkdelsetcols;
10054 : Oid indexOid;
10055 : bool old_check_ok;
10056 : ObjectAddress address;
10057 2638 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10058 :
10059 : /*
10060 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10061 : * delete rows out from under us.
10062 : */
10063 2638 : if (OidIsValid(fkconstraint->old_pktable_oid))
10064 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10065 : else
10066 2566 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10067 :
10068 : /*
10069 : * Validity checks (permission checks wait till we have the column
10070 : * numbers)
10071 : */
10072 2632 : if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10073 6 : ereport(ERROR,
10074 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
10075 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10076 : RelationGetRelationName(rel),
10077 : RelationGetRelationName(pkrel)));
10078 :
10079 2626 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10080 340 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10081 0 : ereport(ERROR,
10082 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10083 : errmsg("referenced relation \"%s\" is not a table",
10084 : RelationGetRelationName(pkrel))));
10085 :
10086 2626 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
10087 2 : ereport(ERROR,
10088 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10089 : errmsg("permission denied: \"%s\" is a system catalog",
10090 : RelationGetRelationName(pkrel))));
10091 :
10092 : /*
10093 : * References from permanent or unlogged tables to temp tables, and from
10094 : * permanent tables to unlogged tables, are disallowed because the
10095 : * referenced data can vanish out from under us. References from temp
10096 : * tables to any other table type are also disallowed, because other
10097 : * backends might need to run the RI triggers on the perm table, but they
10098 : * can't reliably see tuples in the local buffers of other backends.
10099 : */
10100 2624 : switch (rel->rd_rel->relpersistence)
10101 : {
10102 2334 : case RELPERSISTENCE_PERMANENT:
10103 2334 : if (!RelationIsPermanent(pkrel))
10104 0 : ereport(ERROR,
10105 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10106 : errmsg("constraints on permanent tables may reference only permanent tables")));
10107 2334 : break;
10108 12 : case RELPERSISTENCE_UNLOGGED:
10109 12 : if (!RelationIsPermanent(pkrel)
10110 12 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10111 0 : ereport(ERROR,
10112 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10113 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10114 12 : break;
10115 278 : case RELPERSISTENCE_TEMP:
10116 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10117 0 : ereport(ERROR,
10118 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10119 : errmsg("constraints on temporary tables may reference only temporary tables")));
10120 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10121 0 : ereport(ERROR,
10122 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10123 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
10124 278 : break;
10125 : }
10126 :
10127 : /*
10128 : * Look up the referencing attributes to make sure they exist, and record
10129 : * their attnums and type and collation OIDs.
10130 : */
10131 2624 : numfks = transformColumnNameList(RelationGetRelid(rel),
10132 : fkconstraint->fk_attrs,
10133 : fkattnum, fktypoid, fkcolloid);
10134 2594 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10135 2594 : if (with_period && !fkconstraint->fk_with_period)
10136 24 : ereport(ERROR,
10137 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10138 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10139 :
10140 2570 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10141 : fkconstraint->fk_del_set_cols,
10142 : fkdelsetcols, NULL, NULL);
10143 2564 : numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10144 : numfkdelsetcols,
10145 : fkdelsetcols,
10146 : fkconstraint->fk_del_set_cols);
10147 :
10148 : /*
10149 : * If the attribute list for the referenced table was omitted, lookup the
10150 : * definition of the primary key and use it. Otherwise, validate the
10151 : * supplied attribute list. In either case, discover the index OID and
10152 : * index opclasses, and the attnums and type and collation OIDs of the
10153 : * attributes.
10154 : */
10155 2558 : if (fkconstraint->pk_attrs == NIL)
10156 : {
10157 1184 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10158 : &fkconstraint->pk_attrs,
10159 : pkattnum, pktypoid, pkcolloid,
10160 : opclasses, &pk_has_without_overlaps);
10161 :
10162 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10163 1184 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10164 24 : ereport(ERROR,
10165 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10166 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10167 : }
10168 : else
10169 : {
10170 1374 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
10171 : fkconstraint->pk_attrs,
10172 : pkattnum, pktypoid, pkcolloid);
10173 :
10174 : /* Since we got pk_attrs, one should be a period. */
10175 1344 : if (with_period && !fkconstraint->pk_with_period)
10176 24 : ereport(ERROR,
10177 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10178 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10179 :
10180 : /* Look for an index matching the column list */
10181 1320 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10182 : with_period, opclasses, &pk_has_without_overlaps);
10183 : }
10184 :
10185 : /*
10186 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10187 : * must use PERIOD.
10188 : */
10189 2444 : if (pk_has_without_overlaps && !with_period)
10190 12 : ereport(ERROR,
10191 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10192 : errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10193 :
10194 : /*
10195 : * Now we can check permissions.
10196 : */
10197 2432 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10198 :
10199 : /*
10200 : * Check some things for generated columns.
10201 : */
10202 5704 : for (i = 0; i < numfks; i++)
10203 : {
10204 3302 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10205 :
10206 3302 : if (attgenerated)
10207 : {
10208 : /*
10209 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10210 : */
10211 48 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10212 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10213 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10214 12 : ereport(ERROR,
10215 : (errcode(ERRCODE_SYNTAX_ERROR),
10216 : errmsg("invalid %s action for foreign key constraint containing generated column",
10217 : "ON UPDATE")));
10218 36 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10219 24 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10220 12 : ereport(ERROR,
10221 : (errcode(ERRCODE_SYNTAX_ERROR),
10222 : errmsg("invalid %s action for foreign key constraint containing generated column",
10223 : "ON DELETE")));
10224 : }
10225 :
10226 : /*
10227 : * FKs on virtual columns are not supported. This would require
10228 : * various additional support in ri_triggers.c, including special
10229 : * handling in ri_NullCheck(), ri_KeysEqual(),
10230 : * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10231 : * as NULL there). Also not really practical as long as you can't
10232 : * index virtual columns.
10233 : */
10234 3278 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10235 6 : ereport(ERROR,
10236 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10237 : errmsg("foreign key constraints on virtual generated columns are not supported")));
10238 : }
10239 :
10240 : /*
10241 : * Some actions are currently unsupported for foreign keys using PERIOD.
10242 : */
10243 2402 : if (fkconstraint->fk_with_period)
10244 : {
10245 248 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10246 236 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10247 218 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10248 200 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10249 66 : ereport(ERROR,
10250 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10251 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10252 : "ON UPDATE"));
10253 :
10254 182 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10255 176 : fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10256 176 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10257 176 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10258 6 : ereport(ERROR,
10259 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10260 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10261 : "ON DELETE"));
10262 : }
10263 :
10264 : /*
10265 : * Look up the equality operators to use in the constraint.
10266 : *
10267 : * Note that we have to be careful about the difference between the actual
10268 : * PK column type and the opclass' declared input type, which might be
10269 : * only binary-compatible with it. The declared opcintype is the right
10270 : * thing to probe pg_amop with.
10271 : */
10272 2330 : if (numfks != numpks)
10273 0 : ereport(ERROR,
10274 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10275 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10276 :
10277 : /*
10278 : * On the strength of a previous constraint, we might avoid scanning
10279 : * tables to validate this one. See below.
10280 : */
10281 2330 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10282 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10283 :
10284 5074 : for (i = 0; i < numpks; i++)
10285 : {
10286 2984 : Oid pktype = pktypoid[i];
10287 2984 : Oid fktype = fktypoid[i];
10288 : Oid fktyped;
10289 2984 : Oid pkcoll = pkcolloid[i];
10290 2984 : Oid fkcoll = fkcolloid[i];
10291 : HeapTuple cla_ht;
10292 : Form_pg_opclass cla_tup;
10293 : Oid amid;
10294 : Oid opfamily;
10295 : Oid opcintype;
10296 : bool for_overlaps;
10297 : CompareType cmptype;
10298 : Oid pfeqop;
10299 : Oid ppeqop;
10300 : Oid ffeqop;
10301 : int16 eqstrategy;
10302 : Oid pfeqop_right;
10303 :
10304 : /* We need several fields out of the pg_opclass entry */
10305 2984 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10306 2984 : if (!HeapTupleIsValid(cla_ht))
10307 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10308 2984 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10309 2984 : amid = cla_tup->opcmethod;
10310 2984 : opfamily = cla_tup->opcfamily;
10311 2984 : opcintype = cla_tup->opcintype;
10312 2984 : ReleaseSysCache(cla_ht);
10313 :
10314 : /*
10315 : * Get strategy number from index AM.
10316 : *
10317 : * For a normal foreign-key constraint, this should not fail, since we
10318 : * already checked that the index is unique and should therefore have
10319 : * appropriate equal operators. For a period foreign key, this could
10320 : * fail if we selected a non-matching exclusion constraint earlier.
10321 : * (XXX Maybe we should do these lookups earlier so we don't end up
10322 : * doing that.)
10323 : */
10324 2984 : for_overlaps = with_period && i == numpks - 1;
10325 2984 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10326 2984 : eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10327 2984 : if (eqstrategy == InvalidStrategy)
10328 0 : ereport(ERROR,
10329 : errcode(ERRCODE_UNDEFINED_OBJECT),
10330 : for_overlaps
10331 : ? errmsg("could not identify an overlaps operator for foreign key")
10332 : : errmsg("could not identify an equality operator for foreign key"),
10333 : errdetail("Could not translate compare type %d for operator family \"%s\", input type %s, access method \"%s\".",
10334 : cmptype, get_opfamily_name(opfamily, false), format_type_be(opcintype), get_am_name(amid)));
10335 :
10336 : /*
10337 : * There had better be a primary equality operator for the index.
10338 : * We'll use it for PK = PK comparisons.
10339 : */
10340 2984 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10341 : eqstrategy);
10342 :
10343 2984 : if (!OidIsValid(ppeqop))
10344 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10345 : eqstrategy, opcintype, opcintype, opfamily);
10346 :
10347 : /*
10348 : * Are there equality operators that take exactly the FK type? Assume
10349 : * we should look through any domain here.
10350 : */
10351 2984 : fktyped = getBaseType(fktype);
10352 :
10353 2984 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10354 : eqstrategy);
10355 2984 : if (OidIsValid(pfeqop))
10356 : {
10357 2344 : pfeqop_right = fktyped;
10358 2344 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10359 : eqstrategy);
10360 : }
10361 : else
10362 : {
10363 : /* keep compiler quiet */
10364 640 : pfeqop_right = InvalidOid;
10365 640 : ffeqop = InvalidOid;
10366 : }
10367 :
10368 2984 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10369 : {
10370 : /*
10371 : * Otherwise, look for an implicit cast from the FK type to the
10372 : * opcintype, and if found, use the primary equality operator.
10373 : * This is a bit tricky because opcintype might be a polymorphic
10374 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10375 : * whether the two actual column types can be concurrently cast to
10376 : * that type. (Otherwise, we'd fail to reject combinations such
10377 : * as int[] and point[].)
10378 : */
10379 : Oid input_typeids[2];
10380 : Oid target_typeids[2];
10381 :
10382 640 : input_typeids[0] = pktype;
10383 640 : input_typeids[1] = fktype;
10384 640 : target_typeids[0] = opcintype;
10385 640 : target_typeids[1] = opcintype;
10386 640 : if (can_coerce_type(2, input_typeids, target_typeids,
10387 : COERCION_IMPLICIT))
10388 : {
10389 412 : pfeqop = ffeqop = ppeqop;
10390 412 : pfeqop_right = opcintype;
10391 : }
10392 : }
10393 :
10394 2984 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10395 228 : ereport(ERROR,
10396 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10397 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10398 : fkconstraint->conname),
10399 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10400 : "are of incompatible types: %s and %s.",
10401 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10402 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10403 : format_type_be(fktype),
10404 : format_type_be(pktype))));
10405 :
10406 : /*
10407 : * This shouldn't be possible, but better check to make sure we have a
10408 : * consistent state for the check below.
10409 : */
10410 2756 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10411 0 : elog(ERROR, "key columns are not both collatable");
10412 :
10413 2756 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10414 : {
10415 : bool pkcolldet;
10416 : bool fkcolldet;
10417 :
10418 106 : pkcolldet = get_collation_isdeterministic(pkcoll);
10419 106 : fkcolldet = get_collation_isdeterministic(fkcoll);
10420 :
10421 : /*
10422 : * SQL requires that both collations are the same. This is
10423 : * because we need a consistent notion of equality on both
10424 : * columns. We relax this by allowing different collations if
10425 : * they are both deterministic. (This is also for backward
10426 : * compatibility, because PostgreSQL has always allowed this.)
10427 : */
10428 106 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10429 12 : ereport(ERROR,
10430 : (errcode(ERRCODE_COLLATION_MISMATCH),
10431 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10432 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10433 : "have incompatible collations: \"%s\" and \"%s\". "
10434 : "If either collation is nondeterministic, then both collations have to be the same.",
10435 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10436 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10437 : get_collation_name(fkcoll),
10438 : get_collation_name(pkcoll))));
10439 : }
10440 :
10441 2744 : if (old_check_ok)
10442 : {
10443 : /*
10444 : * When a pfeqop changes, revalidate the constraint. We could
10445 : * permit intra-opfamily changes, but that adds subtle complexity
10446 : * without any concrete benefit for core types. We need not
10447 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10448 : */
10449 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10450 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10451 : old_pfeqop_item);
10452 : }
10453 2744 : if (old_check_ok)
10454 : {
10455 : Oid old_fktype;
10456 : Oid new_fktype;
10457 : CoercionPathType old_pathtype;
10458 : CoercionPathType new_pathtype;
10459 : Oid old_castfunc;
10460 : Oid new_castfunc;
10461 : Oid old_fkcoll;
10462 : Oid new_fkcoll;
10463 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10464 6 : fkattnum[i] - 1);
10465 :
10466 : /*
10467 : * Identify coercion pathways from each of the old and new FK-side
10468 : * column types to the right (foreign) operand type of the pfeqop.
10469 : * We may assume that pg_constraint.conkey is not changing.
10470 : */
10471 6 : old_fktype = attr->atttypid;
10472 6 : new_fktype = fktype;
10473 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10474 : &old_castfunc);
10475 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10476 : &new_castfunc);
10477 :
10478 6 : old_fkcoll = attr->attcollation;
10479 6 : new_fkcoll = fkcoll;
10480 :
10481 : /*
10482 : * Upon a change to the cast from the FK column to its pfeqop
10483 : * operand, revalidate the constraint. For this evaluation, a
10484 : * binary coercion cast is equivalent to no cast at all. While
10485 : * type implementors should design implicit casts with an eye
10486 : * toward consistency of operations like equality, we cannot
10487 : * assume here that they have done so.
10488 : *
10489 : * A function with a polymorphic argument could change behavior
10490 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10491 : * when the cast destination is polymorphic, we only avoid
10492 : * revalidation if the input type has not changed at all. Given
10493 : * just the core data types and operator classes, this requirement
10494 : * prevents no would-be optimizations.
10495 : *
10496 : * If the cast converts from a base type to a domain thereon, then
10497 : * that domain type must be the opcintype of the unique index.
10498 : * Necessarily, the primary key column must then be of the domain
10499 : * type. Since the constraint was previously valid, all values on
10500 : * the foreign side necessarily exist on the primary side and in
10501 : * turn conform to the domain. Consequently, we need not treat
10502 : * domains specially here.
10503 : *
10504 : * If the collation changes, revalidation is required, unless both
10505 : * collations are deterministic, because those share the same
10506 : * notion of equality (because texteq reduces to bitwise
10507 : * equality).
10508 : *
10509 : * We need not directly consider the PK type. It's necessarily
10510 : * binary coercible to the opcintype of the unique index column,
10511 : * and ri_triggers.c will only deal with PK datums in terms of
10512 : * that opcintype. Changing the opcintype also changes pfeqop.
10513 : */
10514 6 : old_check_ok = (new_pathtype == old_pathtype &&
10515 6 : new_castfunc == old_castfunc &&
10516 6 : (!IsPolymorphicType(pfeqop_right) ||
10517 12 : new_fktype == old_fktype) &&
10518 0 : (new_fkcoll == old_fkcoll ||
10519 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10520 : }
10521 :
10522 2744 : pfeqoperators[i] = pfeqop;
10523 2744 : ppeqoperators[i] = ppeqop;
10524 2744 : ffeqoperators[i] = ffeqop;
10525 : }
10526 :
10527 : /*
10528 : * For FKs with PERIOD we need additional operators to check whether the
10529 : * referencing row's range is contained by the aggregated ranges of the
10530 : * referenced row(s). For rangetypes and multirangetypes this is
10531 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10532 : * support for now. FKs will look these up at "runtime", but we should
10533 : * make sure the lookup works here, even if we don't use the values.
10534 : */
10535 2090 : if (with_period)
10536 : {
10537 : Oid periodoperoid;
10538 : Oid aggedperiodoperoid;
10539 : Oid intersectoperoid;
10540 :
10541 158 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10542 : &intersectoperoid);
10543 : }
10544 :
10545 : /* First, create the constraint catalog entry itself. */
10546 2090 : address = addFkConstraint(addFkBothSides,
10547 : fkconstraint->conname, fkconstraint, rel, pkrel,
10548 : indexOid,
10549 : InvalidOid, /* no parent constraint */
10550 : numfks,
10551 : pkattnum,
10552 : fkattnum,
10553 : pfeqoperators,
10554 : ppeqoperators,
10555 : ffeqoperators,
10556 : numfkdelsetcols,
10557 : fkdelsetcols,
10558 : false,
10559 : with_period);
10560 :
10561 : /* Next process the action triggers at the referenced side and recurse */
10562 2090 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10563 : indexOid,
10564 : address.objectId,
10565 : numfks,
10566 : pkattnum,
10567 : fkattnum,
10568 : pfeqoperators,
10569 : ppeqoperators,
10570 : ffeqoperators,
10571 : numfkdelsetcols,
10572 : fkdelsetcols,
10573 : old_check_ok,
10574 : InvalidOid, InvalidOid,
10575 : with_period);
10576 :
10577 : /* Lastly create the check triggers at the referencing side and recurse */
10578 2090 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10579 : indexOid,
10580 : address.objectId,
10581 : numfks,
10582 : pkattnum,
10583 : fkattnum,
10584 : pfeqoperators,
10585 : ppeqoperators,
10586 : ffeqoperators,
10587 : numfkdelsetcols,
10588 : fkdelsetcols,
10589 : old_check_ok,
10590 : lockmode,
10591 : InvalidOid, InvalidOid,
10592 : with_period);
10593 :
10594 : /*
10595 : * Done. Close pk table, but keep lock until we've committed.
10596 : */
10597 2090 : table_close(pkrel, NoLock);
10598 :
10599 2090 : return address;
10600 : }
10601 :
10602 : /*
10603 : * validateFkOnDeleteSetColumns
10604 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10605 : * column lists are valid.
10606 : *
10607 : * If there are duplicates in the fksetcolsattnums[] array, this silently
10608 : * removes the dups. The new count of numfksetcols is returned.
10609 : */
10610 : static int
10611 2564 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10612 : int numfksetcols, int16 *fksetcolsattnums,
10613 : List *fksetcols)
10614 : {
10615 2564 : int numcolsout = 0;
10616 :
10617 2594 : for (int i = 0; i < numfksetcols; i++)
10618 : {
10619 36 : int16 setcol_attnum = fksetcolsattnums[i];
10620 36 : bool seen = false;
10621 :
10622 : /* Make sure it's in fkattnums[] */
10623 66 : for (int j = 0; j < numfks; j++)
10624 : {
10625 60 : if (fkattnums[j] == setcol_attnum)
10626 : {
10627 30 : seen = true;
10628 30 : break;
10629 : }
10630 : }
10631 :
10632 36 : if (!seen)
10633 : {
10634 6 : char *col = strVal(list_nth(fksetcols, i));
10635 :
10636 6 : ereport(ERROR,
10637 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10638 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10639 : }
10640 :
10641 : /* Now check for dups */
10642 30 : seen = false;
10643 30 : for (int j = 0; j < numcolsout; j++)
10644 : {
10645 6 : if (fksetcolsattnums[j] == setcol_attnum)
10646 : {
10647 6 : seen = true;
10648 6 : break;
10649 : }
10650 : }
10651 30 : if (!seen)
10652 24 : fksetcolsattnums[numcolsout++] = setcol_attnum;
10653 : }
10654 2558 : return numcolsout;
10655 : }
10656 :
10657 : /*
10658 : * addFkConstraint
10659 : * Install pg_constraint entries to implement a foreign key constraint.
10660 : * Caller must separately invoke addFkRecurseReferenced and
10661 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10662 : * and (for partitioned tables) recurse to partitions.
10663 : *
10664 : * fkside: the side of the FK (or both) to create. Caller should
10665 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10666 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10667 : * addFkBothSides.
10668 : * constraintname: the base name for the constraint being added,
10669 : * copied to fkconstraint->conname if the latter is not set
10670 : * fkconstraint: the constraint being added
10671 : * rel: the root referencing relation
10672 : * pkrel: the referenced relation; might be a partition, if recursing
10673 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10674 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10675 : * top-level constraint
10676 : * numfks: the number of columns in the foreign key
10677 : * pkattnum: the attnum array of referenced attributes
10678 : * fkattnum: the attnum array of referencing attributes
10679 : * pf/pp/ffeqoperators: OID array of operators between columns
10680 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10681 : * (...) clause
10682 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10683 : * NULL/DEFAULT clause
10684 : * with_period: true if this is a temporal FK
10685 : */
10686 : static ObjectAddress
10687 3930 : addFkConstraint(addFkConstraintSides fkside,
10688 : char *constraintname, Constraint *fkconstraint,
10689 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10690 : int numfks, int16 *pkattnum,
10691 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10692 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10693 : bool is_internal, bool with_period)
10694 : {
10695 : ObjectAddress address;
10696 : Oid constrOid;
10697 : char *conname;
10698 : bool conislocal;
10699 : int16 coninhcount;
10700 : bool connoinherit;
10701 :
10702 : /*
10703 : * Verify relkind for each referenced partition. At the top level, this
10704 : * is redundant with a previous check, but we need it when recursing.
10705 : */
10706 3930 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10707 832 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10708 0 : ereport(ERROR,
10709 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10710 : errmsg("referenced relation \"%s\" is not a table",
10711 : RelationGetRelationName(pkrel))));
10712 :
10713 : /*
10714 : * Caller supplies us with a constraint name; however, it may be used in
10715 : * this partition, so come up with a different one in that case. Unless
10716 : * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10717 : * supplied name with an underscore and digit(s) appended.
10718 : */
10719 3930 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10720 : RelationGetRelid(rel),
10721 : constraintname))
10722 1028 : conname = ChooseConstraintName(constraintname,
10723 : NULL,
10724 : "",
10725 1028 : RelationGetNamespace(rel), NIL);
10726 : else
10727 2902 : conname = constraintname;
10728 :
10729 3930 : if (fkconstraint->conname == NULL)
10730 412 : fkconstraint->conname = pstrdup(conname);
10731 :
10732 3930 : if (OidIsValid(parentConstr))
10733 : {
10734 1840 : conislocal = false;
10735 1840 : coninhcount = 1;
10736 1840 : connoinherit = false;
10737 : }
10738 : else
10739 : {
10740 2090 : conislocal = true;
10741 2090 : coninhcount = 0;
10742 :
10743 : /*
10744 : * always inherit for partitioned tables, never for legacy inheritance
10745 : */
10746 2090 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10747 : }
10748 :
10749 : /*
10750 : * Record the FK constraint in pg_constraint.
10751 : */
10752 3930 : constrOid = CreateConstraintEntry(conname,
10753 3930 : RelationGetNamespace(rel),
10754 : CONSTRAINT_FOREIGN,
10755 3930 : fkconstraint->deferrable,
10756 3930 : fkconstraint->initdeferred,
10757 3930 : fkconstraint->is_enforced,
10758 3930 : fkconstraint->initially_valid,
10759 : parentConstr,
10760 : RelationGetRelid(rel),
10761 : fkattnum,
10762 : numfks,
10763 : numfks,
10764 : InvalidOid, /* not a domain constraint */
10765 : indexOid,
10766 : RelationGetRelid(pkrel),
10767 : pkattnum,
10768 : pfeqoperators,
10769 : ppeqoperators,
10770 : ffeqoperators,
10771 : numfks,
10772 3930 : fkconstraint->fk_upd_action,
10773 3930 : fkconstraint->fk_del_action,
10774 : fkdelsetcols,
10775 : numfkdelsetcols,
10776 3930 : fkconstraint->fk_matchtype,
10777 : NULL, /* no exclusion constraint */
10778 : NULL, /* no check constraint */
10779 : NULL,
10780 : conislocal, /* islocal */
10781 : coninhcount, /* inhcount */
10782 : connoinherit, /* conNoInherit */
10783 : with_period, /* conPeriod */
10784 : is_internal); /* is_internal */
10785 :
10786 3930 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10787 :
10788 : /*
10789 : * In partitioning cases, create the dependency entries for this
10790 : * constraint. (For non-partitioned cases, relevant entries were created
10791 : * by CreateConstraintEntry.)
10792 : *
10793 : * On the referenced side, we need the constraint to have an internal
10794 : * dependency on its parent constraint; this means that this constraint
10795 : * cannot be dropped on its own -- only through the parent constraint. It
10796 : * also means the containing partition cannot be dropped on its own, but
10797 : * it can be detached, at which point this dependency is removed (after
10798 : * verifying that no rows are referenced via this FK.)
10799 : *
10800 : * When processing the referencing side, we link the constraint via the
10801 : * special partitioning dependencies: the parent constraint is the primary
10802 : * dependent, and the partition on which the foreign key exists is the
10803 : * secondary dependency. That way, this constraint is dropped if either
10804 : * of these objects is.
10805 : *
10806 : * Note that this is only necessary for the subsidiary pg_constraint rows
10807 : * in partitions; the topmost row doesn't need any of this.
10808 : */
10809 3930 : if (OidIsValid(parentConstr))
10810 : {
10811 : ObjectAddress referenced;
10812 :
10813 1840 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10814 :
10815 : Assert(fkside != addFkBothSides);
10816 1840 : if (fkside == addFkReferencedSide)
10817 1022 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10818 : else
10819 : {
10820 818 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10821 818 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10822 818 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10823 : }
10824 : }
10825 :
10826 : /* make new constraint visible, in case we add more */
10827 3930 : CommandCounterIncrement();
10828 :
10829 3930 : return address;
10830 : }
10831 :
10832 : /*
10833 : * addFkRecurseReferenced
10834 : * Recursive helper for the referenced side of foreign key creation,
10835 : * which creates the action triggers and recurses
10836 : *
10837 : * If the referenced relation is a plain relation, create the necessary action
10838 : * triggers that implement the constraint. If the referenced relation is a
10839 : * partitioned table, then we create a pg_constraint row referencing the parent
10840 : * of the referencing side for it and recurse on this routine for each
10841 : * partition.
10842 : *
10843 : * fkconstraint: the constraint being added
10844 : * rel: the root referencing relation
10845 : * pkrel: the referenced relation; might be a partition, if recursing
10846 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10847 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10848 : * top-level constraint
10849 : * numfks: the number of columns in the foreign key
10850 : * pkattnum: the attnum array of referenced attributes
10851 : * fkattnum: the attnum array of referencing attributes
10852 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10853 : * NULL/DEFAULT (...) clause
10854 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10855 : * NULL/DEFAULT clause
10856 : * pf/pp/ffeqoperators: OID array of operators between columns
10857 : * old_check_ok: true if this constraint replaces an existing one that
10858 : * was already validated (thus this one doesn't need validation)
10859 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10860 : * partition, the OIDs of the parent action triggers for DELETE and
10861 : * UPDATE respectively.
10862 : * with_period: true if this is a temporal FK
10863 : */
10864 : static void
10865 3202 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10866 : Relation pkrel, Oid indexOid, Oid parentConstr,
10867 : int numfks,
10868 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10869 : Oid *ppeqoperators, Oid *ffeqoperators,
10870 : int numfkdelsetcols, int16 *fkdelsetcols,
10871 : bool old_check_ok,
10872 : Oid parentDelTrigger, Oid parentUpdTrigger,
10873 : bool with_period)
10874 : {
10875 3202 : Oid deleteTriggerOid = InvalidOid,
10876 3202 : updateTriggerOid = InvalidOid;
10877 :
10878 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10879 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10880 :
10881 : /*
10882 : * Create action triggers to enforce the constraint, or skip them if the
10883 : * constraint is NOT ENFORCED.
10884 : */
10885 3202 : if (fkconstraint->is_enforced)
10886 3172 : createForeignKeyActionTriggers(RelationGetRelid(rel),
10887 : RelationGetRelid(pkrel),
10888 : fkconstraint,
10889 : parentConstr, indexOid,
10890 : parentDelTrigger, parentUpdTrigger,
10891 : &deleteTriggerOid, &updateTriggerOid);
10892 :
10893 : /*
10894 : * If the referenced table is partitioned, recurse on ourselves to handle
10895 : * each partition. We need one pg_constraint row created for each
10896 : * partition in addition to the pg_constraint row for the parent table.
10897 : */
10898 3202 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10899 : {
10900 510 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10901 :
10902 1406 : for (int i = 0; i < pd->nparts; i++)
10903 : {
10904 : Relation partRel;
10905 : AttrMap *map;
10906 : AttrNumber *mapped_pkattnum;
10907 : Oid partIndexId;
10908 : ObjectAddress address;
10909 :
10910 : /* XXX would it be better to acquire these locks beforehand? */
10911 896 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10912 :
10913 : /*
10914 : * Map the attribute numbers in the referenced side of the FK
10915 : * definition to match the partition's column layout.
10916 : */
10917 896 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10918 : RelationGetDescr(pkrel),
10919 : false);
10920 896 : if (map)
10921 : {
10922 130 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10923 272 : for (int j = 0; j < numfks; j++)
10924 142 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10925 : }
10926 : else
10927 766 : mapped_pkattnum = pkattnum;
10928 :
10929 : /* Determine the index to use at this level */
10930 896 : partIndexId = index_get_partition(partRel, indexOid);
10931 896 : if (!OidIsValid(partIndexId))
10932 0 : elog(ERROR, "index for %u not found in partition %s",
10933 : indexOid, RelationGetRelationName(partRel));
10934 :
10935 : /* Create entry at this level ... */
10936 896 : address = addFkConstraint(addFkReferencedSide,
10937 : fkconstraint->conname, fkconstraint, rel,
10938 : partRel, partIndexId, parentConstr,
10939 : numfks, mapped_pkattnum,
10940 : fkattnum, pfeqoperators, ppeqoperators,
10941 : ffeqoperators, numfkdelsetcols,
10942 : fkdelsetcols, true, with_period);
10943 : /* ... and recurse to our children */
10944 896 : addFkRecurseReferenced(fkconstraint, rel, partRel,
10945 : partIndexId, address.objectId, numfks,
10946 : mapped_pkattnum, fkattnum,
10947 : pfeqoperators, ppeqoperators, ffeqoperators,
10948 : numfkdelsetcols, fkdelsetcols,
10949 : old_check_ok,
10950 : deleteTriggerOid, updateTriggerOid,
10951 : with_period);
10952 :
10953 : /* Done -- clean up (but keep the lock) */
10954 896 : table_close(partRel, NoLock);
10955 896 : if (map)
10956 : {
10957 130 : pfree(mapped_pkattnum);
10958 130 : free_attrmap(map);
10959 : }
10960 : }
10961 : }
10962 3202 : }
10963 :
10964 : /*
10965 : * addFkRecurseReferencing
10966 : * Recursive helper for the referencing side of foreign key creation,
10967 : * which creates the check triggers and recurses
10968 : *
10969 : * If the referencing relation is a plain relation, create the necessary check
10970 : * triggers that implement the constraint, and set up for Phase 3 constraint
10971 : * verification. If the referencing relation is a partitioned table, then
10972 : * we create a pg_constraint row for it and recurse on this routine for each
10973 : * partition.
10974 : *
10975 : * We assume that the referenced relation is locked against concurrent
10976 : * deletions. If it's a partitioned relation, every partition must be so
10977 : * locked.
10978 : *
10979 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
10980 : * of an ALTER TABLE sequence.
10981 : * fkconstraint: the constraint being added
10982 : * rel: the referencing relation; might be a partition, if recursing
10983 : * pkrel: the root referenced relation
10984 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10985 : * parentConstr: the OID of the parent constraint (there is always one)
10986 : * numfks: the number of columns in the foreign key
10987 : * pkattnum: the attnum array of referenced attributes
10988 : * fkattnum: the attnum array of referencing attributes
10989 : * pf/pp/ffeqoperators: OID array of operators between columns
10990 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10991 : * (...) clause
10992 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10993 : * NULL/DEFAULT clause
10994 : * old_check_ok: true if this constraint replaces an existing one that
10995 : * was already validated (thus this one doesn't need validation)
10996 : * lockmode: the lockmode to acquire on partitions when recursing
10997 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
10998 : * a partition, the OIDs of the parent check triggers for INSERT and
10999 : * UPDATE respectively.
11000 : * with_period: true if this is a temporal FK
11001 : */
11002 : static void
11003 2908 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
11004 : Relation pkrel, Oid indexOid, Oid parentConstr,
11005 : int numfks, int16 *pkattnum, int16 *fkattnum,
11006 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11007 : int numfkdelsetcols, int16 *fkdelsetcols,
11008 : bool old_check_ok, LOCKMODE lockmode,
11009 : Oid parentInsTrigger, Oid parentUpdTrigger,
11010 : bool with_period)
11011 : {
11012 2908 : Oid insertTriggerOid = InvalidOid,
11013 2908 : updateTriggerOid = InvalidOid;
11014 :
11015 : Assert(OidIsValid(parentConstr));
11016 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
11017 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
11018 :
11019 2908 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11020 0 : ereport(ERROR,
11021 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11022 : errmsg("foreign key constraints are not supported on foreign tables")));
11023 :
11024 : /*
11025 : * Add check triggers if the constraint is ENFORCED, and if needed,
11026 : * schedule them to be checked in Phase 3.
11027 : *
11028 : * If the relation is partitioned, drill down to do it to its partitions.
11029 : */
11030 2908 : if (fkconstraint->is_enforced)
11031 2872 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
11032 : RelationGetRelid(pkrel),
11033 : fkconstraint,
11034 : parentConstr,
11035 : indexOid,
11036 : parentInsTrigger, parentUpdTrigger,
11037 : &insertTriggerOid, &updateTriggerOid);
11038 :
11039 2908 : if (rel->rd_rel->relkind == RELKIND_RELATION)
11040 : {
11041 : /*
11042 : * Tell Phase 3 to check that the constraint is satisfied by existing
11043 : * rows. We can skip this during table creation, when constraint is
11044 : * specified as NOT ENFORCED, or when requested explicitly by
11045 : * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11046 : * recreating a constraint following a SET DATA TYPE operation that
11047 : * did not impugn its validity.
11048 : */
11049 2420 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11050 836 : fkconstraint->is_enforced)
11051 : {
11052 : NewConstraint *newcon;
11053 : AlteredTableInfo *tab;
11054 :
11055 836 : tab = ATGetQueueEntry(wqueue, rel);
11056 :
11057 836 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11058 836 : newcon->name = get_constraint_name(parentConstr);
11059 836 : newcon->contype = CONSTR_FOREIGN;
11060 836 : newcon->refrelid = RelationGetRelid(pkrel);
11061 836 : newcon->refindid = indexOid;
11062 836 : newcon->conid = parentConstr;
11063 836 : newcon->conwithperiod = fkconstraint->fk_with_period;
11064 836 : newcon->qual = (Node *) fkconstraint;
11065 :
11066 836 : tab->constraints = lappend(tab->constraints, newcon);
11067 : }
11068 : }
11069 488 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11070 : {
11071 488 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
11072 : Relation trigrel;
11073 :
11074 : /*
11075 : * Triggers of the foreign keys will be manipulated a bunch of times
11076 : * in the loop below. To avoid repeatedly opening/closing the trigger
11077 : * catalog relation, we open it here and pass it to the subroutines
11078 : * called below.
11079 : */
11080 488 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11081 :
11082 : /*
11083 : * Recurse to take appropriate action on each partition; either we
11084 : * find an existing constraint to reparent to ours, or we create a new
11085 : * one.
11086 : */
11087 906 : for (int i = 0; i < pd->nparts; i++)
11088 : {
11089 424 : Relation partition = table_open(pd->oids[i], lockmode);
11090 : List *partFKs;
11091 : AttrMap *attmap;
11092 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11093 : bool attached;
11094 : ObjectAddress address;
11095 :
11096 424 : CheckAlterTableIsSafe(partition);
11097 :
11098 418 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
11099 : RelationGetDescr(rel),
11100 : false);
11101 1070 : for (int j = 0; j < numfks; j++)
11102 652 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11103 :
11104 : /* Check whether an existing constraint can be repurposed */
11105 418 : partFKs = copyObject(RelationGetFKeyList(partition));
11106 418 : attached = false;
11107 860 : foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11108 : {
11109 36 : if (tryAttachPartitionForeignKey(wqueue,
11110 : fk,
11111 : partition,
11112 : parentConstr,
11113 : numfks,
11114 : mapped_fkattnum,
11115 : pkattnum,
11116 : pfeqoperators,
11117 : insertTriggerOid,
11118 : updateTriggerOid,
11119 : trigrel))
11120 : {
11121 12 : attached = true;
11122 12 : break;
11123 : }
11124 : }
11125 418 : if (attached)
11126 : {
11127 12 : table_close(partition, NoLock);
11128 12 : continue;
11129 : }
11130 :
11131 : /*
11132 : * No luck finding a good constraint to reuse; create our own.
11133 : */
11134 406 : address = addFkConstraint(addFkReferencingSide,
11135 : fkconstraint->conname, fkconstraint,
11136 : partition, pkrel, indexOid, parentConstr,
11137 : numfks, pkattnum,
11138 : mapped_fkattnum, pfeqoperators,
11139 : ppeqoperators, ffeqoperators,
11140 : numfkdelsetcols, fkdelsetcols, true,
11141 : with_period);
11142 :
11143 : /* call ourselves to finalize the creation and we're done */
11144 406 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11145 : indexOid,
11146 : address.objectId,
11147 : numfks,
11148 : pkattnum,
11149 : mapped_fkattnum,
11150 : pfeqoperators,
11151 : ppeqoperators,
11152 : ffeqoperators,
11153 : numfkdelsetcols,
11154 : fkdelsetcols,
11155 : old_check_ok,
11156 : lockmode,
11157 : insertTriggerOid,
11158 : updateTriggerOid,
11159 : with_period);
11160 :
11161 406 : table_close(partition, NoLock);
11162 : }
11163 :
11164 482 : table_close(trigrel, RowExclusiveLock);
11165 : }
11166 2902 : }
11167 :
11168 : /*
11169 : * CloneForeignKeyConstraints
11170 : * Clone foreign keys from a partitioned table to a newly acquired
11171 : * partition.
11172 : *
11173 : * partitionRel is a partition of parentRel, so we can be certain that it has
11174 : * the same columns with the same datatypes. The columns may be in different
11175 : * order, though.
11176 : *
11177 : * wqueue must be passed to set up phase 3 constraint checking, unless the
11178 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
11179 : * PARTITION OF).
11180 : */
11181 : static void
11182 9950 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
11183 : Relation partitionRel)
11184 : {
11185 : /* This only works for declarative partitioning */
11186 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11187 :
11188 : /*
11189 : * Clone constraints for which the parent is on the referenced side.
11190 : */
11191 9950 : CloneFkReferenced(parentRel, partitionRel);
11192 :
11193 : /*
11194 : * Now clone constraints where the parent is on the referencing side.
11195 : */
11196 9950 : CloneFkReferencing(wqueue, parentRel, partitionRel);
11197 9932 : }
11198 :
11199 : /*
11200 : * CloneFkReferenced
11201 : * Subroutine for CloneForeignKeyConstraints
11202 : *
11203 : * Find all the FKs that have the parent relation on the referenced side;
11204 : * clone those constraints to the given partition. This is to be called
11205 : * when the partition is being created or attached.
11206 : *
11207 : * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
11208 : *
11209 : * This recurses to partitions, if the relation being attached is partitioned.
11210 : * Recursion is done by calling addFkRecurseReferenced.
11211 : */
11212 : static void
11213 9950 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
11214 : {
11215 : Relation pg_constraint;
11216 : AttrMap *attmap;
11217 : ListCell *cell;
11218 : SysScanDesc scan;
11219 : ScanKeyData key[2];
11220 : HeapTuple tuple;
11221 9950 : List *clone = NIL;
11222 : Relation trigrel;
11223 :
11224 : /*
11225 : * Search for any constraints where this partition's parent is in the
11226 : * referenced side. However, we must not clone any constraint whose
11227 : * parent constraint is also going to be cloned, to avoid duplicates. So
11228 : * do it in two steps: first construct the list of constraints to clone,
11229 : * then go over that list cloning those whose parents are not in the list.
11230 : * (We must not rely on the parent being seen first, since the catalog
11231 : * scan could return children first.)
11232 : */
11233 9950 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11234 9950 : ScanKeyInit(&key[0],
11235 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11236 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11237 9950 : ScanKeyInit(&key[1],
11238 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11239 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11240 : /* This is a seqscan, as we don't have a usable index ... */
11241 9950 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11242 : NULL, 2, key);
11243 10256 : while ((tuple = systable_getnext(scan)) != NULL)
11244 : {
11245 306 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11246 :
11247 306 : clone = lappend_oid(clone, constrForm->oid);
11248 : }
11249 9950 : systable_endscan(scan);
11250 9950 : table_close(pg_constraint, RowShareLock);
11251 :
11252 : /*
11253 : * Triggers of the foreign keys will be manipulated a bunch of times in
11254 : * the loop below. To avoid repeatedly opening/closing the trigger
11255 : * catalog relation, we open it here and pass it to the subroutines called
11256 : * below.
11257 : */
11258 9950 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11259 :
11260 9950 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11261 : RelationGetDescr(parentRel),
11262 : false);
11263 10256 : foreach(cell, clone)
11264 : {
11265 306 : Oid constrOid = lfirst_oid(cell);
11266 : Form_pg_constraint constrForm;
11267 : Relation fkRel;
11268 : Oid indexOid;
11269 : Oid partIndexId;
11270 : int numfks;
11271 : AttrNumber conkey[INDEX_MAX_KEYS];
11272 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11273 : AttrNumber confkey[INDEX_MAX_KEYS];
11274 : Oid conpfeqop[INDEX_MAX_KEYS];
11275 : Oid conppeqop[INDEX_MAX_KEYS];
11276 : Oid conffeqop[INDEX_MAX_KEYS];
11277 : int numfkdelsetcols;
11278 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11279 : Constraint *fkconstraint;
11280 : ObjectAddress address;
11281 306 : Oid deleteTriggerOid = InvalidOid,
11282 306 : updateTriggerOid = InvalidOid;
11283 :
11284 306 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11285 306 : if (!HeapTupleIsValid(tuple))
11286 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11287 306 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11288 :
11289 : /*
11290 : * As explained above: don't try to clone a constraint for which we're
11291 : * going to clone the parent.
11292 : */
11293 306 : if (list_member_oid(clone, constrForm->conparentid))
11294 : {
11295 126 : ReleaseSysCache(tuple);
11296 180 : continue;
11297 : }
11298 :
11299 : /*
11300 : * Don't clone self-referencing foreign keys, which can be in the
11301 : * partitioned table or in the partition-to-be.
11302 : */
11303 180 : if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11304 138 : constrForm->conrelid == RelationGetRelid(partitionRel))
11305 : {
11306 54 : ReleaseSysCache(tuple);
11307 54 : continue;
11308 : }
11309 :
11310 : /* We need the same lock level that CreateTrigger will acquire */
11311 126 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11312 :
11313 126 : indexOid = constrForm->conindid;
11314 126 : DeconstructFkConstraintRow(tuple,
11315 : &numfks,
11316 : conkey,
11317 : confkey,
11318 : conpfeqop,
11319 : conppeqop,
11320 : conffeqop,
11321 : &numfkdelsetcols,
11322 : confdelsetcols);
11323 :
11324 258 : for (int i = 0; i < numfks; i++)
11325 132 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11326 :
11327 126 : fkconstraint = makeNode(Constraint);
11328 126 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11329 126 : fkconstraint->conname = NameStr(constrForm->conname);
11330 126 : fkconstraint->deferrable = constrForm->condeferrable;
11331 126 : fkconstraint->initdeferred = constrForm->condeferred;
11332 126 : fkconstraint->location = -1;
11333 126 : fkconstraint->pktable = NULL;
11334 : /* ->fk_attrs determined below */
11335 126 : fkconstraint->pk_attrs = NIL;
11336 126 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11337 126 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11338 126 : fkconstraint->fk_del_action = constrForm->confdeltype;
11339 126 : fkconstraint->fk_del_set_cols = NIL;
11340 126 : fkconstraint->old_conpfeqop = NIL;
11341 126 : fkconstraint->old_pktable_oid = InvalidOid;
11342 126 : fkconstraint->is_enforced = constrForm->conenforced;
11343 126 : fkconstraint->skip_validation = false;
11344 126 : fkconstraint->initially_valid = constrForm->convalidated;
11345 :
11346 : /* set up colnames that are used to generate the constraint name */
11347 258 : for (int i = 0; i < numfks; i++)
11348 : {
11349 : Form_pg_attribute att;
11350 :
11351 132 : att = TupleDescAttr(RelationGetDescr(fkRel),
11352 132 : conkey[i] - 1);
11353 132 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11354 132 : makeString(NameStr(att->attname)));
11355 : }
11356 :
11357 : /*
11358 : * Add the new foreign key constraint pointing to the new partition.
11359 : * Because this new partition appears in the referenced side of the
11360 : * constraint, we don't need to set up for Phase 3 check.
11361 : */
11362 126 : partIndexId = index_get_partition(partitionRel, indexOid);
11363 126 : if (!OidIsValid(partIndexId))
11364 0 : elog(ERROR, "index for %u not found in partition %s",
11365 : indexOid, RelationGetRelationName(partitionRel));
11366 :
11367 : /*
11368 : * Get the "action" triggers belonging to the constraint to pass as
11369 : * parent OIDs for similar triggers that will be created on the
11370 : * partition in addFkRecurseReferenced().
11371 : */
11372 126 : if (constrForm->conenforced)
11373 126 : GetForeignKeyActionTriggers(trigrel, constrOid,
11374 : constrForm->confrelid, constrForm->conrelid,
11375 : &deleteTriggerOid, &updateTriggerOid);
11376 :
11377 : /* Add this constraint ... */
11378 126 : address = addFkConstraint(addFkReferencedSide,
11379 : fkconstraint->conname, fkconstraint, fkRel,
11380 : partitionRel, partIndexId, constrOid,
11381 : numfks, mapped_confkey,
11382 : conkey, conpfeqop, conppeqop, conffeqop,
11383 : numfkdelsetcols, confdelsetcols, false,
11384 126 : constrForm->conperiod);
11385 : /* ... and recurse */
11386 126 : addFkRecurseReferenced(fkconstraint,
11387 : fkRel,
11388 : partitionRel,
11389 : partIndexId,
11390 : address.objectId,
11391 : numfks,
11392 : mapped_confkey,
11393 : conkey,
11394 : conpfeqop,
11395 : conppeqop,
11396 : conffeqop,
11397 : numfkdelsetcols,
11398 : confdelsetcols,
11399 : true,
11400 : deleteTriggerOid,
11401 : updateTriggerOid,
11402 126 : constrForm->conperiod);
11403 :
11404 126 : table_close(fkRel, NoLock);
11405 126 : ReleaseSysCache(tuple);
11406 : }
11407 :
11408 9950 : table_close(trigrel, RowExclusiveLock);
11409 9950 : }
11410 :
11411 : /*
11412 : * CloneFkReferencing
11413 : * Subroutine for CloneForeignKeyConstraints
11414 : *
11415 : * For each FK constraint of the parent relation in the given list, find an
11416 : * equivalent constraint in its partition relation that can be reparented;
11417 : * if one cannot be found, create a new constraint in the partition as its
11418 : * child.
11419 : *
11420 : * If wqueue is given, it is used to set up phase-3 verification for each
11421 : * cloned constraint; omit it if such verification is not needed
11422 : * (example: the partition is being created anew).
11423 : */
11424 : static void
11425 9950 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11426 : {
11427 : AttrMap *attmap;
11428 : List *partFKs;
11429 9950 : List *clone = NIL;
11430 : ListCell *cell;
11431 : Relation trigrel;
11432 :
11433 : /* obtain a list of constraints that we need to clone */
11434 11140 : foreach(cell, RelationGetFKeyList(parentRel))
11435 : {
11436 1196 : ForeignKeyCacheInfo *fk = lfirst(cell);
11437 :
11438 : /*
11439 : * Refuse to attach a table as partition that this partitioned table
11440 : * already has a foreign key to. This isn't useful schema, which is
11441 : * proven by the fact that there have been no user complaints that
11442 : * it's already impossible to achieve this in the opposite direction,
11443 : * i.e., creating a foreign key that references a partition. This
11444 : * restriction allows us to dodge some complexities around
11445 : * pg_constraint and pg_trigger row creations that would be needed
11446 : * during ATTACH/DETACH for this kind of relationship.
11447 : */
11448 1196 : if (fk->confrelid == RelationGetRelid(partRel))
11449 6 : ereport(ERROR,
11450 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11451 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11452 : RelationGetRelationName(partRel),
11453 : get_constraint_name(fk->conoid))));
11454 :
11455 1190 : clone = lappend_oid(clone, fk->conoid);
11456 : }
11457 :
11458 : /*
11459 : * Silently do nothing if there's nothing to do. In particular, this
11460 : * avoids throwing a spurious error for foreign tables.
11461 : */
11462 9944 : if (clone == NIL)
11463 9424 : return;
11464 :
11465 520 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11466 0 : ereport(ERROR,
11467 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11468 : errmsg("foreign key constraints are not supported on foreign tables")));
11469 :
11470 : /*
11471 : * Triggers of the foreign keys will be manipulated a bunch of times in
11472 : * the loop below. To avoid repeatedly opening/closing the trigger
11473 : * catalog relation, we open it here and pass it to the subroutines called
11474 : * below.
11475 : */
11476 520 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11477 :
11478 : /*
11479 : * The constraint key may differ, if the columns in the partition are
11480 : * different. This map is used to convert them.
11481 : */
11482 520 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11483 : RelationGetDescr(parentRel),
11484 : false);
11485 :
11486 520 : partFKs = copyObject(RelationGetFKeyList(partRel));
11487 :
11488 1698 : foreach(cell, clone)
11489 : {
11490 1190 : Oid parentConstrOid = lfirst_oid(cell);
11491 : Form_pg_constraint constrForm;
11492 : Relation pkrel;
11493 : HeapTuple tuple;
11494 : int numfks;
11495 : AttrNumber conkey[INDEX_MAX_KEYS];
11496 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11497 : AttrNumber confkey[INDEX_MAX_KEYS];
11498 : Oid conpfeqop[INDEX_MAX_KEYS];
11499 : Oid conppeqop[INDEX_MAX_KEYS];
11500 : Oid conffeqop[INDEX_MAX_KEYS];
11501 : int numfkdelsetcols;
11502 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11503 : Constraint *fkconstraint;
11504 : bool attached;
11505 : Oid indexOid;
11506 : ObjectAddress address;
11507 : ListCell *lc;
11508 1190 : Oid insertTriggerOid = InvalidOid,
11509 1190 : updateTriggerOid = InvalidOid;
11510 : bool with_period;
11511 :
11512 1190 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11513 1190 : if (!HeapTupleIsValid(tuple))
11514 0 : elog(ERROR, "cache lookup failed for constraint %u",
11515 : parentConstrOid);
11516 1190 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11517 :
11518 : /* Don't clone constraints whose parents are being cloned */
11519 1190 : if (list_member_oid(clone, constrForm->conparentid))
11520 : {
11521 634 : ReleaseSysCache(tuple);
11522 772 : continue;
11523 : }
11524 :
11525 : /*
11526 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11527 : * relation, that means to lock all partitions.
11528 : */
11529 556 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11530 556 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11531 226 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11532 : ShareRowExclusiveLock, NULL);
11533 :
11534 556 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11535 : conpfeqop, conppeqop, conffeqop,
11536 : &numfkdelsetcols, confdelsetcols);
11537 1334 : for (int i = 0; i < numfks; i++)
11538 778 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11539 :
11540 : /*
11541 : * Get the "check" triggers belonging to the constraint, if it is
11542 : * ENFORCED, to pass as parent OIDs for similar triggers that will be
11543 : * created on the partition in addFkRecurseReferencing(). They are
11544 : * also passed to tryAttachPartitionForeignKey() below to simply
11545 : * assign as parents to the partition's existing "check" triggers,
11546 : * that is, if the corresponding constraints is deemed attachable to
11547 : * the parent constraint.
11548 : */
11549 556 : if (constrForm->conenforced)
11550 544 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11551 : constrForm->confrelid, constrForm->conrelid,
11552 : &insertTriggerOid, &updateTriggerOid);
11553 :
11554 : /*
11555 : * Before creating a new constraint, see whether any existing FKs are
11556 : * fit for the purpose. If one is, attach the parent constraint to
11557 : * it, and don't clone anything. This way we avoid the expensive
11558 : * verification step and don't end up with a duplicate FK, and we
11559 : * don't need to recurse to partitions for this constraint.
11560 : */
11561 556 : attached = false;
11562 646 : foreach(lc, partFKs)
11563 : {
11564 234 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11565 :
11566 234 : if (tryAttachPartitionForeignKey(wqueue,
11567 : fk,
11568 : partRel,
11569 : parentConstrOid,
11570 : numfks,
11571 : mapped_conkey,
11572 : confkey,
11573 : conpfeqop,
11574 : insertTriggerOid,
11575 : updateTriggerOid,
11576 : trigrel))
11577 : {
11578 138 : attached = true;
11579 138 : table_close(pkrel, NoLock);
11580 138 : break;
11581 : }
11582 : }
11583 550 : if (attached)
11584 : {
11585 138 : ReleaseSysCache(tuple);
11586 138 : continue;
11587 : }
11588 :
11589 : /* No dice. Set up to create our own constraint */
11590 412 : fkconstraint = makeNode(Constraint);
11591 412 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11592 : /* ->conname determined below */
11593 412 : fkconstraint->deferrable = constrForm->condeferrable;
11594 412 : fkconstraint->initdeferred = constrForm->condeferred;
11595 412 : fkconstraint->location = -1;
11596 412 : fkconstraint->pktable = NULL;
11597 : /* ->fk_attrs determined below */
11598 412 : fkconstraint->pk_attrs = NIL;
11599 412 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11600 412 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11601 412 : fkconstraint->fk_del_action = constrForm->confdeltype;
11602 412 : fkconstraint->fk_del_set_cols = NIL;
11603 412 : fkconstraint->old_conpfeqop = NIL;
11604 412 : fkconstraint->old_pktable_oid = InvalidOid;
11605 412 : fkconstraint->is_enforced = constrForm->conenforced;
11606 412 : fkconstraint->skip_validation = false;
11607 412 : fkconstraint->initially_valid = constrForm->convalidated;
11608 932 : for (int i = 0; i < numfks; i++)
11609 : {
11610 : Form_pg_attribute att;
11611 :
11612 520 : att = TupleDescAttr(RelationGetDescr(partRel),
11613 520 : mapped_conkey[i] - 1);
11614 520 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11615 520 : makeString(NameStr(att->attname)));
11616 : }
11617 :
11618 412 : indexOid = constrForm->conindid;
11619 412 : with_period = constrForm->conperiod;
11620 :
11621 : /* Create the pg_constraint entry at this level */
11622 412 : address = addFkConstraint(addFkReferencingSide,
11623 412 : NameStr(constrForm->conname), fkconstraint,
11624 : partRel, pkrel, indexOid, parentConstrOid,
11625 : numfks, confkey,
11626 : mapped_conkey, conpfeqop,
11627 : conppeqop, conffeqop,
11628 : numfkdelsetcols, confdelsetcols,
11629 : false, with_period);
11630 :
11631 : /* Done with the cloned constraint's tuple */
11632 412 : ReleaseSysCache(tuple);
11633 :
11634 : /* Create the check triggers, and recurse to partitions, if any */
11635 412 : addFkRecurseReferencing(wqueue,
11636 : fkconstraint,
11637 : partRel,
11638 : pkrel,
11639 : indexOid,
11640 : address.objectId,
11641 : numfks,
11642 : confkey,
11643 : mapped_conkey,
11644 : conpfeqop,
11645 : conppeqop,
11646 : conffeqop,
11647 : numfkdelsetcols,
11648 : confdelsetcols,
11649 : false, /* no old check exists */
11650 : AccessExclusiveLock,
11651 : insertTriggerOid,
11652 : updateTriggerOid,
11653 : with_period);
11654 406 : table_close(pkrel, NoLock);
11655 : }
11656 :
11657 508 : table_close(trigrel, RowExclusiveLock);
11658 : }
11659 :
11660 : /*
11661 : * When the parent of a partition receives [the referencing side of] a foreign
11662 : * key, we must propagate that foreign key to the partition. However, the
11663 : * partition might already have an equivalent foreign key; this routine
11664 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11665 : * by the other parameters. If they are equivalent, create the link between
11666 : * the two constraints and return true.
11667 : *
11668 : * If the given FK does not match the one defined by rest of the params,
11669 : * return false.
11670 : */
11671 : static bool
11672 270 : tryAttachPartitionForeignKey(List **wqueue,
11673 : ForeignKeyCacheInfo *fk,
11674 : Relation partition,
11675 : Oid parentConstrOid,
11676 : int numfks,
11677 : AttrNumber *mapped_conkey,
11678 : AttrNumber *confkey,
11679 : Oid *conpfeqop,
11680 : Oid parentInsTrigger,
11681 : Oid parentUpdTrigger,
11682 : Relation trigrel)
11683 : {
11684 : HeapTuple parentConstrTup;
11685 : Form_pg_constraint parentConstr;
11686 : HeapTuple partcontup;
11687 : Form_pg_constraint partConstr;
11688 :
11689 270 : parentConstrTup = SearchSysCache1(CONSTROID,
11690 : ObjectIdGetDatum(parentConstrOid));
11691 270 : if (!HeapTupleIsValid(parentConstrTup))
11692 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11693 270 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11694 :
11695 : /*
11696 : * Do some quick & easy initial checks. If any of these fail, we cannot
11697 : * use this constraint.
11698 : */
11699 270 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11700 : {
11701 0 : ReleaseSysCache(parentConstrTup);
11702 0 : return false;
11703 : }
11704 762 : for (int i = 0; i < numfks; i++)
11705 : {
11706 492 : if (fk->conkey[i] != mapped_conkey[i] ||
11707 492 : fk->confkey[i] != confkey[i] ||
11708 492 : fk->conpfeqop[i] != conpfeqop[i])
11709 : {
11710 0 : ReleaseSysCache(parentConstrTup);
11711 0 : return false;
11712 : }
11713 : }
11714 :
11715 : /* Looks good so far; perform more extensive checks. */
11716 270 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11717 270 : if (!HeapTupleIsValid(partcontup))
11718 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11719 270 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11720 :
11721 : /*
11722 : * An error should be raised if the constraint enforceability is
11723 : * different. Returning false without raising an error, as we do for other
11724 : * attributes, could lead to a duplicate constraint with the same
11725 : * enforceability as the parent. While this may be acceptable, it may not
11726 : * be ideal. Therefore, it's better to raise an error and allow the user
11727 : * to correct the enforceability before proceeding.
11728 : */
11729 270 : if (partConstr->conenforced != parentConstr->conenforced)
11730 6 : ereport(ERROR,
11731 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11732 : errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11733 : NameStr(parentConstr->conname),
11734 : NameStr(partConstr->conname),
11735 : RelationGetRelationName(partition))));
11736 :
11737 264 : if (OidIsValid(partConstr->conparentid) ||
11738 228 : partConstr->condeferrable != parentConstr->condeferrable ||
11739 200 : partConstr->condeferred != parentConstr->condeferred ||
11740 200 : partConstr->confupdtype != parentConstr->confupdtype ||
11741 164 : partConstr->confdeltype != parentConstr->confdeltype ||
11742 164 : partConstr->confmatchtype != parentConstr->confmatchtype)
11743 : {
11744 114 : ReleaseSysCache(parentConstrTup);
11745 114 : ReleaseSysCache(partcontup);
11746 114 : return false;
11747 : }
11748 :
11749 150 : ReleaseSysCache(parentConstrTup);
11750 150 : ReleaseSysCache(partcontup);
11751 :
11752 : /* Looks good! Attach this constraint. */
11753 150 : AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11754 : parentConstrOid, parentInsTrigger,
11755 : parentUpdTrigger, trigrel);
11756 :
11757 150 : return true;
11758 : }
11759 :
11760 : /*
11761 : * AttachPartitionForeignKey
11762 : *
11763 : * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11764 : * attaching the constraint, removing redundant triggers and entries from
11765 : * pg_constraint, and setting the constraint's parent.
11766 : */
11767 : static void
11768 150 : AttachPartitionForeignKey(List **wqueue,
11769 : Relation partition,
11770 : Oid partConstrOid,
11771 : Oid parentConstrOid,
11772 : Oid parentInsTrigger,
11773 : Oid parentUpdTrigger,
11774 : Relation trigrel)
11775 : {
11776 : HeapTuple parentConstrTup;
11777 : Form_pg_constraint parentConstr;
11778 : HeapTuple partcontup;
11779 : Form_pg_constraint partConstr;
11780 : bool queueValidation;
11781 : Oid partConstrFrelid;
11782 : Oid partConstrRelid;
11783 : bool parentConstrIsEnforced;
11784 :
11785 : /* Fetch the parent constraint tuple */
11786 150 : parentConstrTup = SearchSysCache1(CONSTROID,
11787 : ObjectIdGetDatum(parentConstrOid));
11788 150 : if (!HeapTupleIsValid(parentConstrTup))
11789 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11790 150 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11791 150 : parentConstrIsEnforced = parentConstr->conenforced;
11792 :
11793 : /* Fetch the child constraint tuple */
11794 150 : partcontup = SearchSysCache1(CONSTROID,
11795 : ObjectIdGetDatum(partConstrOid));
11796 150 : if (!HeapTupleIsValid(partcontup))
11797 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11798 150 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11799 150 : partConstrFrelid = partConstr->confrelid;
11800 150 : partConstrRelid = partConstr->conrelid;
11801 :
11802 : /*
11803 : * If the referenced table is partitioned, then the partition we're
11804 : * attaching now has extra pg_constraint rows and action triggers that are
11805 : * no longer needed. Remove those.
11806 : */
11807 150 : if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11808 : {
11809 24 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11810 :
11811 24 : RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11812 : partConstrRelid);
11813 :
11814 24 : table_close(pg_constraint, RowShareLock);
11815 : }
11816 :
11817 : /*
11818 : * Will we need to validate this constraint? A valid parent constraint
11819 : * implies that all child constraints have been validated, so if this one
11820 : * isn't, we must trigger phase 3 validation.
11821 : */
11822 150 : queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11823 :
11824 150 : ReleaseSysCache(partcontup);
11825 150 : ReleaseSysCache(parentConstrTup);
11826 :
11827 : /*
11828 : * The action triggers in the new partition become redundant -- the parent
11829 : * table already has equivalent ones, and those will be able to reach the
11830 : * partition. Remove the ones in the partition. We identify them because
11831 : * they have our constraint OID, as well as being on the referenced rel.
11832 : */
11833 150 : DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11834 : partConstrRelid);
11835 :
11836 150 : ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11837 : RelationGetRelid(partition));
11838 :
11839 : /*
11840 : * Like the constraint, attach partition's "check" triggers to the
11841 : * corresponding parent triggers if the constraint is ENFORCED. NOT
11842 : * ENFORCED constraints do not have these triggers.
11843 : */
11844 150 : if (parentConstrIsEnforced)
11845 : {
11846 : Oid insertTriggerOid,
11847 : updateTriggerOid;
11848 :
11849 138 : GetForeignKeyCheckTriggers(trigrel,
11850 : partConstrOid, partConstrFrelid, partConstrRelid,
11851 : &insertTriggerOid, &updateTriggerOid);
11852 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11853 138 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11854 : RelationGetRelid(partition));
11855 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11856 138 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11857 : RelationGetRelid(partition));
11858 : }
11859 :
11860 : /*
11861 : * We updated this pg_constraint row above to set its parent; validating
11862 : * it will cause its convalidated flag to change, so we need CCI here. In
11863 : * addition, we need it unconditionally for the rare case where the parent
11864 : * table has *two* identical constraints; when reaching this function for
11865 : * the second one, we must have made our changes visible, otherwise we
11866 : * would try to attach both to this one.
11867 : */
11868 150 : CommandCounterIncrement();
11869 :
11870 : /* If validation is needed, put it in the queue now. */
11871 150 : if (queueValidation)
11872 : {
11873 : Relation conrel;
11874 :
11875 18 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11876 :
11877 18 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11878 18 : if (!HeapTupleIsValid(partcontup))
11879 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11880 :
11881 : /* Use the same lock as for AT_ValidateConstraint */
11882 18 : QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
11883 : ShareUpdateExclusiveLock);
11884 18 : ReleaseSysCache(partcontup);
11885 18 : table_close(conrel, RowExclusiveLock);
11886 : }
11887 150 : }
11888 :
11889 : /*
11890 : * RemoveInheritedConstraint
11891 : *
11892 : * Removes the constraint and its associated trigger from the specified
11893 : * relation, which inherited the given constraint.
11894 : */
11895 : static void
11896 24 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
11897 : Oid conrelid)
11898 : {
11899 : ObjectAddresses *objs;
11900 : HeapTuple consttup;
11901 : ScanKeyData key;
11902 : SysScanDesc scan;
11903 : HeapTuple trigtup;
11904 :
11905 24 : ScanKeyInit(&key,
11906 : Anum_pg_constraint_conrelid,
11907 : BTEqualStrategyNumber, F_OIDEQ,
11908 : ObjectIdGetDatum(conrelid));
11909 :
11910 24 : scan = systable_beginscan(conrel,
11911 : ConstraintRelidTypidNameIndexId,
11912 : true, NULL, 1, &key);
11913 24 : objs = new_object_addresses();
11914 240 : while ((consttup = systable_getnext(scan)) != NULL)
11915 : {
11916 216 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11917 :
11918 216 : if (conform->conparentid != conoid)
11919 168 : continue;
11920 : else
11921 : {
11922 : ObjectAddress addr;
11923 : SysScanDesc scan2;
11924 : ScanKeyData key2;
11925 : int n PG_USED_FOR_ASSERTS_ONLY;
11926 :
11927 48 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11928 48 : add_exact_object_address(&addr, objs);
11929 :
11930 : /*
11931 : * First we must delete the dependency record that binds the
11932 : * constraint records together.
11933 : */
11934 48 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11935 : conform->oid,
11936 : DEPENDENCY_INTERNAL,
11937 : ConstraintRelationId,
11938 : conoid);
11939 : Assert(n == 1); /* actually only one is expected */
11940 :
11941 : /*
11942 : * Now search for the triggers for this constraint and set them up
11943 : * for deletion too
11944 : */
11945 48 : ScanKeyInit(&key2,
11946 : Anum_pg_trigger_tgconstraint,
11947 : BTEqualStrategyNumber, F_OIDEQ,
11948 : ObjectIdGetDatum(conform->oid));
11949 48 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11950 : true, NULL, 1, &key2);
11951 144 : while ((trigtup = systable_getnext(scan2)) != NULL)
11952 : {
11953 96 : ObjectAddressSet(addr, TriggerRelationId,
11954 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11955 96 : add_exact_object_address(&addr, objs);
11956 : }
11957 48 : systable_endscan(scan2);
11958 : }
11959 : }
11960 : /* make the dependency deletions visible */
11961 24 : CommandCounterIncrement();
11962 24 : performMultipleDeletions(objs, DROP_RESTRICT,
11963 : PERFORM_DELETION_INTERNAL);
11964 24 : systable_endscan(scan);
11965 24 : }
11966 :
11967 : /*
11968 : * DropForeignKeyConstraintTriggers
11969 : *
11970 : * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11971 : * action triggers for the foreign key constraint.
11972 : *
11973 : * If valid confrelid and conrelid values are not provided, the respective
11974 : * trigger check will be skipped, and the trigger will be considered for
11975 : * removal.
11976 : */
11977 : static void
11978 222 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
11979 : Oid conrelid)
11980 : {
11981 : ScanKeyData key;
11982 : SysScanDesc scan;
11983 : HeapTuple trigtup;
11984 :
11985 222 : ScanKeyInit(&key,
11986 : Anum_pg_trigger_tgconstraint,
11987 : BTEqualStrategyNumber, F_OIDEQ,
11988 : ObjectIdGetDatum(conoid));
11989 222 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11990 : NULL, 1, &key);
11991 954 : while ((trigtup = systable_getnext(scan)) != NULL)
11992 : {
11993 732 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11994 : ObjectAddress trigger;
11995 :
11996 : /* Invalid if trigger is not for a referential integrity constraint */
11997 732 : if (!OidIsValid(trgform->tgconstrrelid))
11998 276 : continue;
11999 732 : if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12000 276 : continue;
12001 456 : if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12002 0 : continue;
12003 :
12004 : /* We should be dropping trigger related to foreign key constraint */
12005 : Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12006 : trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12007 : trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12008 : trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12009 : trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12010 : trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12011 : trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12012 : trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12013 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12014 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12015 : trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12016 : trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12017 :
12018 : /*
12019 : * The constraint is originally set up to contain this trigger as an
12020 : * implementation object, so there's a dependency record that links
12021 : * the two; however, since the trigger is no longer needed, we remove
12022 : * the dependency link in order to be able to drop the trigger while
12023 : * keeping the constraint intact.
12024 : */
12025 456 : deleteDependencyRecordsFor(TriggerRelationId,
12026 : trgform->oid,
12027 : false);
12028 : /* make dependency deletion visible to performDeletion */
12029 456 : CommandCounterIncrement();
12030 456 : ObjectAddressSet(trigger, TriggerRelationId,
12031 : trgform->oid);
12032 456 : performDeletion(&trigger, DROP_RESTRICT, 0);
12033 : /* make trigger drop visible, in case the loop iterates */
12034 456 : CommandCounterIncrement();
12035 : }
12036 :
12037 222 : systable_endscan(scan);
12038 222 : }
12039 :
12040 : /*
12041 : * GetForeignKeyActionTriggers
12042 : * Returns delete and update "action" triggers of the given relation
12043 : * belonging to the given constraint
12044 : */
12045 : static void
12046 126 : GetForeignKeyActionTriggers(Relation trigrel,
12047 : Oid conoid, Oid confrelid, Oid conrelid,
12048 : Oid *deleteTriggerOid,
12049 : Oid *updateTriggerOid)
12050 : {
12051 : ScanKeyData key;
12052 : SysScanDesc scan;
12053 : HeapTuple trigtup;
12054 :
12055 126 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12056 126 : ScanKeyInit(&key,
12057 : Anum_pg_trigger_tgconstraint,
12058 : BTEqualStrategyNumber, F_OIDEQ,
12059 : ObjectIdGetDatum(conoid));
12060 :
12061 126 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12062 : NULL, 1, &key);
12063 252 : while ((trigtup = systable_getnext(scan)) != NULL)
12064 : {
12065 252 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12066 :
12067 252 : if (trgform->tgconstrrelid != conrelid)
12068 0 : continue;
12069 252 : if (trgform->tgrelid != confrelid)
12070 0 : continue;
12071 : /* Only ever look at "action" triggers on the PK side. */
12072 252 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12073 0 : continue;
12074 252 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
12075 : {
12076 : Assert(*deleteTriggerOid == InvalidOid);
12077 126 : *deleteTriggerOid = trgform->oid;
12078 : }
12079 126 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12080 : {
12081 : Assert(*updateTriggerOid == InvalidOid);
12082 126 : *updateTriggerOid = trgform->oid;
12083 : }
12084 : #ifndef USE_ASSERT_CHECKING
12085 : /* In an assert-enabled build, continue looking to find duplicates */
12086 252 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12087 126 : break;
12088 : #endif
12089 : }
12090 :
12091 126 : if (!OidIsValid(*deleteTriggerOid))
12092 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12093 : conoid);
12094 126 : if (!OidIsValid(*updateTriggerOid))
12095 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12096 : conoid);
12097 :
12098 126 : systable_endscan(scan);
12099 126 : }
12100 :
12101 : /*
12102 : * GetForeignKeyCheckTriggers
12103 : * Returns insert and update "check" triggers of the given relation
12104 : * belonging to the given constraint
12105 : */
12106 : static void
12107 772 : GetForeignKeyCheckTriggers(Relation trigrel,
12108 : Oid conoid, Oid confrelid, Oid conrelid,
12109 : Oid *insertTriggerOid,
12110 : Oid *updateTriggerOid)
12111 : {
12112 : ScanKeyData key;
12113 : SysScanDesc scan;
12114 : HeapTuple trigtup;
12115 :
12116 772 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
12117 772 : ScanKeyInit(&key,
12118 : Anum_pg_trigger_tgconstraint,
12119 : BTEqualStrategyNumber, F_OIDEQ,
12120 : ObjectIdGetDatum(conoid));
12121 :
12122 772 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12123 : NULL, 1, &key);
12124 2516 : while ((trigtup = systable_getnext(scan)) != NULL)
12125 : {
12126 2516 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12127 :
12128 2516 : if (trgform->tgconstrrelid != confrelid)
12129 888 : continue;
12130 1628 : if (trgform->tgrelid != conrelid)
12131 0 : continue;
12132 : /* Only ever look at "check" triggers on the FK side. */
12133 1628 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12134 84 : continue;
12135 1544 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
12136 : {
12137 : Assert(*insertTriggerOid == InvalidOid);
12138 772 : *insertTriggerOid = trgform->oid;
12139 : }
12140 772 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12141 : {
12142 : Assert(*updateTriggerOid == InvalidOid);
12143 772 : *updateTriggerOid = trgform->oid;
12144 : }
12145 : #ifndef USE_ASSERT_CHECKING
12146 : /* In an assert-enabled build, continue looking to find duplicates. */
12147 1544 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12148 772 : break;
12149 : #endif
12150 : }
12151 :
12152 772 : if (!OidIsValid(*insertTriggerOid))
12153 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12154 : conoid);
12155 772 : if (!OidIsValid(*updateTriggerOid))
12156 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12157 : conoid);
12158 :
12159 772 : systable_endscan(scan);
12160 772 : }
12161 :
12162 : /*
12163 : * ALTER TABLE ALTER CONSTRAINT
12164 : *
12165 : * Update the attributes of a constraint.
12166 : *
12167 : * Currently only works for Foreign Key and not null constraints.
12168 : *
12169 : * If the constraint is modified, returns its address; otherwise, return
12170 : * InvalidObjectAddress.
12171 : */
12172 : static ObjectAddress
12173 282 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
12174 : bool recurse, LOCKMODE lockmode)
12175 : {
12176 : Relation conrel;
12177 : Relation tgrel;
12178 : SysScanDesc scan;
12179 : ScanKeyData skey[3];
12180 : HeapTuple contuple;
12181 : Form_pg_constraint currcon;
12182 : ObjectAddress address;
12183 :
12184 : /*
12185 : * Disallow altering ONLY a partitioned table, as it would make no sense.
12186 : * This is okay for legacy inheritance.
12187 : */
12188 282 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12189 0 : ereport(ERROR,
12190 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12191 : errmsg("constraint must be altered in child tables too"),
12192 : errhint("Do not specify the ONLY keyword."));
12193 :
12194 :
12195 282 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12196 282 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12197 :
12198 : /*
12199 : * Find and check the target constraint
12200 : */
12201 282 : ScanKeyInit(&skey[0],
12202 : Anum_pg_constraint_conrelid,
12203 : BTEqualStrategyNumber, F_OIDEQ,
12204 : ObjectIdGetDatum(RelationGetRelid(rel)));
12205 282 : ScanKeyInit(&skey[1],
12206 : Anum_pg_constraint_contypid,
12207 : BTEqualStrategyNumber, F_OIDEQ,
12208 : ObjectIdGetDatum(InvalidOid));
12209 282 : ScanKeyInit(&skey[2],
12210 : Anum_pg_constraint_conname,
12211 : BTEqualStrategyNumber, F_NAMEEQ,
12212 282 : CStringGetDatum(cmdcon->conname));
12213 282 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12214 : true, NULL, 3, skey);
12215 :
12216 : /* There can be at most one matching row */
12217 282 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12218 6 : ereport(ERROR,
12219 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12220 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12221 : cmdcon->conname, RelationGetRelationName(rel))));
12222 :
12223 276 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12224 276 : if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12225 0 : ereport(ERROR,
12226 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12227 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12228 : cmdcon->conname, RelationGetRelationName(rel))));
12229 276 : if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12230 12 : ereport(ERROR,
12231 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12232 : errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12233 : cmdcon->conname, RelationGetRelationName(rel))));
12234 264 : if (cmdcon->alterInheritability &&
12235 90 : currcon->contype != CONSTRAINT_NOTNULL)
12236 24 : ereport(ERROR,
12237 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12238 : errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12239 : cmdcon->conname, RelationGetRelationName(rel)));
12240 :
12241 : /* Refuse to modify inheritability of inherited constraints */
12242 240 : if (cmdcon->alterInheritability &&
12243 66 : cmdcon->noinherit && currcon->coninhcount > 0)
12244 6 : ereport(ERROR,
12245 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12246 : errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12247 : NameStr(currcon->conname),
12248 : RelationGetRelationName(rel)));
12249 :
12250 : /*
12251 : * If it's not the topmost constraint, raise an error.
12252 : *
12253 : * Altering a non-topmost constraint leaves some triggers untouched, since
12254 : * they are not directly connected to this constraint; also, pg_dump would
12255 : * ignore the deferrability status of the individual constraint, since it
12256 : * only dumps topmost constraints. Avoid these problems by refusing this
12257 : * operation and telling the user to alter the parent constraint instead.
12258 : */
12259 234 : if (OidIsValid(currcon->conparentid))
12260 : {
12261 : HeapTuple tp;
12262 12 : Oid parent = currcon->conparentid;
12263 12 : char *ancestorname = NULL;
12264 12 : char *ancestortable = NULL;
12265 :
12266 : /* Loop to find the topmost constraint */
12267 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12268 : {
12269 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
12270 :
12271 : /* If no parent, this is the constraint we want */
12272 24 : if (!OidIsValid(contup->conparentid))
12273 : {
12274 12 : ancestorname = pstrdup(NameStr(contup->conname));
12275 12 : ancestortable = get_rel_name(contup->conrelid);
12276 12 : ReleaseSysCache(tp);
12277 12 : break;
12278 : }
12279 :
12280 12 : parent = contup->conparentid;
12281 12 : ReleaseSysCache(tp);
12282 : }
12283 :
12284 12 : ereport(ERROR,
12285 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12286 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12287 : cmdcon->conname, RelationGetRelationName(rel)),
12288 : ancestorname && ancestortable ?
12289 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12290 : cmdcon->conname, ancestorname, ancestortable) : 0,
12291 : errhint("You may alter the constraint it derives from instead.")));
12292 : }
12293 :
12294 222 : address = InvalidObjectAddress;
12295 :
12296 : /*
12297 : * Do the actual catalog work, and recurse if necessary.
12298 : */
12299 222 : if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12300 : contuple, recurse, lockmode))
12301 210 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12302 :
12303 216 : systable_endscan(scan);
12304 :
12305 216 : table_close(tgrel, RowExclusiveLock);
12306 216 : table_close(conrel, RowExclusiveLock);
12307 :
12308 216 : return address;
12309 : }
12310 :
12311 : /*
12312 : * A subroutine of ATExecAlterConstraint that calls the respective routines for
12313 : * altering constraint's enforceability, deferrability or inheritability.
12314 : */
12315 : static bool
12316 222 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
12317 : Relation conrel, Relation tgrel, Relation rel,
12318 : HeapTuple contuple, bool recurse,
12319 : LOCKMODE lockmode)
12320 : {
12321 : Form_pg_constraint currcon;
12322 222 : bool changed = false;
12323 222 : List *otherrelids = NIL;
12324 :
12325 222 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12326 :
12327 : /*
12328 : * Do the catalog work for the enforceability or deferrability change,
12329 : * recurse if necessary.
12330 : *
12331 : * Note that even if deferrability is requested to be altered along with
12332 : * enforceability, we don't need to explicitly update multiple entries in
12333 : * pg_trigger related to deferrability.
12334 : *
12335 : * Modifying enforceability involves either creating or dropping the
12336 : * trigger, during which the deferrability setting will be adjusted
12337 : * automatically.
12338 : */
12339 288 : if (cmdcon->alterEnforceability &&
12340 66 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12341 : currcon->conrelid, currcon->confrelid,
12342 : contuple, lockmode, InvalidOid,
12343 : InvalidOid, InvalidOid, InvalidOid))
12344 60 : changed = true;
12345 :
12346 258 : else if (cmdcon->alterDeferrability &&
12347 96 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12348 : contuple, recurse, &otherrelids,
12349 : lockmode))
12350 : {
12351 : /*
12352 : * AlterConstrUpdateConstraintEntry already invalidated relcache for
12353 : * the relations having the constraint itself; here we also invalidate
12354 : * for relations that have any triggers that are part of the
12355 : * constraint.
12356 : */
12357 306 : foreach_oid(relid, otherrelids)
12358 114 : CacheInvalidateRelcacheByRelid(relid);
12359 :
12360 96 : changed = true;
12361 : }
12362 :
12363 : /*
12364 : * Do the catalog work for the inheritability change.
12365 : */
12366 276 : if (cmdcon->alterInheritability &&
12367 60 : ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12368 : lockmode))
12369 54 : changed = true;
12370 :
12371 216 : return changed;
12372 : }
12373 :
12374 : /*
12375 : * Returns true if the constraint's enforceability is altered.
12376 : *
12377 : * Depending on whether the constraint is being set to ENFORCED or NOT
12378 : * ENFORCED, it creates or drops the trigger accordingly.
12379 : *
12380 : * Note that we must recurse even when trying to change a constraint to not
12381 : * enforced if it is already not enforced, in case descendant constraints
12382 : * might be enforced and need to be changed to not enforced. Conversely, we
12383 : * should do nothing if a constraint is being set to enforced and is already
12384 : * enforced, as descendant constraints cannot be different in that case.
12385 : */
12386 : static bool
12387 150 : ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
12388 : Relation conrel, Relation tgrel,
12389 : Oid fkrelid, Oid pkrelid,
12390 : HeapTuple contuple, LOCKMODE lockmode,
12391 : Oid ReferencedParentDelTrigger,
12392 : Oid ReferencedParentUpdTrigger,
12393 : Oid ReferencingParentInsTrigger,
12394 : Oid ReferencingParentUpdTrigger)
12395 : {
12396 : Form_pg_constraint currcon;
12397 : Oid conoid;
12398 : Relation rel;
12399 150 : bool changed = false;
12400 :
12401 : /* Since this function recurses, it could be driven to stack overflow */
12402 150 : check_stack_depth();
12403 :
12404 : Assert(cmdcon->alterEnforceability);
12405 :
12406 150 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12407 150 : conoid = currcon->oid;
12408 :
12409 : /* Should be foreign key constraint */
12410 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12411 :
12412 150 : rel = table_open(currcon->conrelid, lockmode);
12413 :
12414 150 : if (currcon->conenforced != cmdcon->is_enforced)
12415 : {
12416 144 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12417 144 : changed = true;
12418 : }
12419 :
12420 : /* Drop triggers */
12421 150 : if (!cmdcon->is_enforced)
12422 : {
12423 : /*
12424 : * When setting a constraint to NOT ENFORCED, the constraint triggers
12425 : * need to be dropped. Therefore, we must process the child relations
12426 : * first, followed by the parent, to account for dependencies.
12427 : */
12428 126 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12429 54 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12430 18 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12431 : fkrelid, pkrelid, contuple,
12432 : lockmode, InvalidOid, InvalidOid,
12433 : InvalidOid, InvalidOid);
12434 :
12435 : /* Drop all the triggers */
12436 72 : DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
12437 : }
12438 78 : else if (changed) /* Create triggers */
12439 : {
12440 78 : Oid ReferencedDelTriggerOid = InvalidOid,
12441 78 : ReferencedUpdTriggerOid = InvalidOid,
12442 78 : ReferencingInsTriggerOid = InvalidOid,
12443 78 : ReferencingUpdTriggerOid = InvalidOid;
12444 :
12445 : /* Prepare the minimal information required for trigger creation. */
12446 78 : Constraint *fkconstraint = makeNode(Constraint);
12447 :
12448 78 : fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12449 78 : fkconstraint->fk_matchtype = currcon->confmatchtype;
12450 78 : fkconstraint->fk_upd_action = currcon->confupdtype;
12451 78 : fkconstraint->fk_del_action = currcon->confdeltype;
12452 :
12453 : /* Create referenced triggers */
12454 78 : if (currcon->conrelid == fkrelid)
12455 36 : createForeignKeyActionTriggers(currcon->conrelid,
12456 : currcon->confrelid,
12457 : fkconstraint,
12458 : conoid,
12459 : currcon->conindid,
12460 : ReferencedParentDelTrigger,
12461 : ReferencedParentUpdTrigger,
12462 : &ReferencedDelTriggerOid,
12463 : &ReferencedUpdTriggerOid);
12464 :
12465 : /* Create referencing triggers */
12466 78 : if (currcon->confrelid == pkrelid)
12467 78 : createForeignKeyCheckTriggers(currcon->conrelid,
12468 : pkrelid,
12469 : fkconstraint,
12470 : conoid,
12471 : currcon->conindid,
12472 : ReferencingParentInsTrigger,
12473 : ReferencingParentUpdTrigger,
12474 : &ReferencingInsTriggerOid,
12475 : &ReferencingUpdTriggerOid);
12476 :
12477 : /*
12478 : * Tell Phase 3 to check that the constraint is satisfied by existing
12479 : * rows.
12480 : */
12481 78 : if (rel->rd_rel->relkind == RELKIND_RELATION)
12482 : {
12483 : AlteredTableInfo *tab;
12484 : NewConstraint *newcon;
12485 :
12486 60 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12487 60 : newcon->name = fkconstraint->conname;
12488 60 : newcon->contype = CONSTR_FOREIGN;
12489 60 : newcon->refrelid = currcon->confrelid;
12490 60 : newcon->refindid = currcon->conindid;
12491 60 : newcon->conid = currcon->oid;
12492 60 : newcon->qual = (Node *) fkconstraint;
12493 :
12494 : /* Find or create work queue entry for this table */
12495 60 : tab = ATGetQueueEntry(wqueue, rel);
12496 60 : tab->constraints = lappend(tab->constraints, newcon);
12497 : }
12498 :
12499 : /*
12500 : * If the table at either end of the constraint is partitioned, we
12501 : * need to recurse and create triggers for each constraint that is a
12502 : * child of this one.
12503 : */
12504 138 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12505 60 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12506 18 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12507 : fkrelid, pkrelid, contuple,
12508 : lockmode, ReferencedDelTriggerOid,
12509 : ReferencedUpdTriggerOid,
12510 : ReferencingInsTriggerOid,
12511 : ReferencingUpdTriggerOid);
12512 : }
12513 :
12514 150 : table_close(rel, NoLock);
12515 :
12516 150 : return changed;
12517 : }
12518 :
12519 : /*
12520 : * Returns true if the constraint's deferrability is altered.
12521 : *
12522 : * *otherrelids is appended OIDs of relations containing affected triggers.
12523 : *
12524 : * Note that we must recurse even when the values are correct, in case
12525 : * indirect descendants have had their constraints altered locally.
12526 : * (This could be avoided if we forbade altering constraints in partitions
12527 : * but existing releases don't do that.)
12528 : */
12529 : static bool
12530 162 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
12531 : Relation conrel, Relation tgrel, Relation rel,
12532 : HeapTuple contuple, bool recurse,
12533 : List **otherrelids, LOCKMODE lockmode)
12534 : {
12535 : Form_pg_constraint currcon;
12536 : Oid refrelid;
12537 162 : bool changed = false;
12538 :
12539 : /* since this function recurses, it could be driven to stack overflow */
12540 162 : check_stack_depth();
12541 :
12542 : Assert(cmdcon->alterDeferrability);
12543 :
12544 162 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12545 162 : refrelid = currcon->confrelid;
12546 :
12547 : /* Should be foreign key constraint */
12548 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12549 :
12550 : /*
12551 : * If called to modify a constraint that's already in the desired state,
12552 : * silently do nothing.
12553 : */
12554 162 : if (currcon->condeferrable != cmdcon->deferrable ||
12555 6 : currcon->condeferred != cmdcon->initdeferred)
12556 : {
12557 162 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12558 162 : changed = true;
12559 :
12560 : /*
12561 : * Now we need to update the multiple entries in pg_trigger that
12562 : * implement the constraint.
12563 : */
12564 162 : AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12565 162 : cmdcon->deferrable,
12566 162 : cmdcon->initdeferred, otherrelids);
12567 : }
12568 :
12569 : /*
12570 : * If the table at either end of the constraint is partitioned, we need to
12571 : * handle every constraint that is a child of this one.
12572 : */
12573 162 : if (recurse && changed &&
12574 300 : (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12575 138 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12576 42 : AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12577 : contuple, recurse, otherrelids,
12578 : lockmode);
12579 :
12580 162 : return changed;
12581 : }
12582 :
12583 : /*
12584 : * Returns true if the constraint's inheritability is altered.
12585 : */
12586 : static bool
12587 60 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
12588 : Relation conrel, Relation rel,
12589 : HeapTuple contuple, LOCKMODE lockmode)
12590 : {
12591 : Form_pg_constraint currcon;
12592 : AttrNumber colNum;
12593 : char *colName;
12594 : List *children;
12595 :
12596 : Assert(cmdcon->alterInheritability);
12597 :
12598 60 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12599 :
12600 : /* The current implementation only works for NOT NULL constraints */
12601 : Assert(currcon->contype == CONSTRAINT_NOTNULL);
12602 :
12603 : /*
12604 : * If called to modify a constraint that's already in the desired state,
12605 : * silently do nothing.
12606 : */
12607 60 : if (cmdcon->noinherit == currcon->connoinherit)
12608 0 : return false;
12609 :
12610 60 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12611 60 : CommandCounterIncrement();
12612 :
12613 : /* Fetch the column number and name */
12614 60 : colNum = extractNotNullColumn(contuple);
12615 60 : colName = get_attname(currcon->conrelid, colNum, false);
12616 :
12617 : /*
12618 : * Propagate the change to children. For this subcommand type we don't
12619 : * recursively affect children, just the immediate level.
12620 : */
12621 60 : children = find_inheritance_children(RelationGetRelid(rel),
12622 : lockmode);
12623 192 : foreach_oid(childoid, children)
12624 : {
12625 : ObjectAddress addr;
12626 :
12627 84 : if (cmdcon->noinherit)
12628 : {
12629 : HeapTuple childtup;
12630 : Form_pg_constraint childcon;
12631 :
12632 30 : childtup = findNotNullConstraint(childoid, colName);
12633 30 : if (!childtup)
12634 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12635 : colName, childoid);
12636 30 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12637 : Assert(childcon->coninhcount > 0);
12638 30 : childcon->coninhcount--;
12639 30 : childcon->conislocal = true;
12640 30 : CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12641 30 : heap_freetuple(childtup);
12642 : }
12643 : else
12644 : {
12645 54 : Relation childrel = table_open(childoid, NoLock);
12646 :
12647 54 : addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12648 : colName, true, true, lockmode);
12649 48 : if (OidIsValid(addr.objectId))
12650 48 : CommandCounterIncrement();
12651 48 : table_close(childrel, NoLock);
12652 : }
12653 : }
12654 :
12655 54 : return true;
12656 : }
12657 :
12658 : /*
12659 : * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12660 : * trigger's deferrability.
12661 : *
12662 : * The arguments to this function have the same meaning as the arguments to
12663 : * ATExecAlterConstrDeferrability.
12664 : */
12665 : static void
12666 162 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12667 : bool deferrable, bool initdeferred,
12668 : List **otherrelids)
12669 : {
12670 : HeapTuple tgtuple;
12671 : ScanKeyData tgkey;
12672 : SysScanDesc tgscan;
12673 :
12674 162 : ScanKeyInit(&tgkey,
12675 : Anum_pg_trigger_tgconstraint,
12676 : BTEqualStrategyNumber, F_OIDEQ,
12677 : ObjectIdGetDatum(conoid));
12678 162 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12679 : NULL, 1, &tgkey);
12680 630 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12681 : {
12682 468 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12683 : Form_pg_trigger copy_tg;
12684 : HeapTuple tgCopyTuple;
12685 :
12686 : /*
12687 : * Remember OIDs of other relation(s) involved in FK constraint.
12688 : * (Note: it's likely that we could skip forcing a relcache inval for
12689 : * other rels that don't have a trigger whose properties change, but
12690 : * let's be conservative.)
12691 : */
12692 468 : if (tgform->tgrelid != RelationGetRelid(rel))
12693 228 : *otherrelids = list_append_unique_oid(*otherrelids,
12694 : tgform->tgrelid);
12695 :
12696 : /*
12697 : * Update enable status and deferrability of RI_FKey_noaction_del,
12698 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12699 : * triggers, but not others; see createForeignKeyActionTriggers and
12700 : * CreateFKCheckTrigger.
12701 : */
12702 468 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12703 372 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12704 258 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12705 138 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12706 18 : continue;
12707 :
12708 450 : tgCopyTuple = heap_copytuple(tgtuple);
12709 450 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12710 :
12711 450 : copy_tg->tgdeferrable = deferrable;
12712 450 : copy_tg->tginitdeferred = initdeferred;
12713 450 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12714 :
12715 450 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12716 :
12717 450 : heap_freetuple(tgCopyTuple);
12718 : }
12719 :
12720 162 : systable_endscan(tgscan);
12721 162 : }
12722 :
12723 : /*
12724 : * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12725 : * the specified constraint.
12726 : *
12727 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12728 : * list of child relations and recursing; instead it uses the conparentid
12729 : * relationships. This may need to be reconsidered.
12730 : *
12731 : * The arguments to this function have the same meaning as the arguments to
12732 : * ATExecAlterConstrEnforceability.
12733 : */
12734 : static void
12735 36 : AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12736 : Relation conrel, Relation tgrel,
12737 : Oid fkrelid, Oid pkrelid,
12738 : HeapTuple contuple, LOCKMODE lockmode,
12739 : Oid ReferencedParentDelTrigger,
12740 : Oid ReferencedParentUpdTrigger,
12741 : Oid ReferencingParentInsTrigger,
12742 : Oid ReferencingParentUpdTrigger)
12743 : {
12744 : Form_pg_constraint currcon;
12745 : Oid conoid;
12746 : ScanKeyData pkey;
12747 : SysScanDesc pscan;
12748 : HeapTuple childtup;
12749 :
12750 36 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12751 36 : conoid = currcon->oid;
12752 :
12753 36 : ScanKeyInit(&pkey,
12754 : Anum_pg_constraint_conparentid,
12755 : BTEqualStrategyNumber, F_OIDEQ,
12756 : ObjectIdGetDatum(conoid));
12757 :
12758 36 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12759 : true, NULL, 1, &pkey);
12760 :
12761 120 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12762 84 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12763 : pkrelid, childtup, lockmode,
12764 : ReferencedParentDelTrigger,
12765 : ReferencedParentUpdTrigger,
12766 : ReferencingParentInsTrigger,
12767 : ReferencingParentUpdTrigger);
12768 :
12769 36 : systable_endscan(pscan);
12770 36 : }
12771 :
12772 : /*
12773 : * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12774 : * the specified constraint.
12775 : *
12776 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12777 : * list of child relations and recursing; instead it uses the conparentid
12778 : * relationships. This may need to be reconsidered.
12779 : *
12780 : * The arguments to this function have the same meaning as the arguments to
12781 : * ATExecAlterConstrDeferrability.
12782 : */
12783 : static void
12784 42 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12785 : Relation conrel, Relation tgrel, Relation rel,
12786 : HeapTuple contuple, bool recurse,
12787 : List **otherrelids, LOCKMODE lockmode)
12788 : {
12789 : Form_pg_constraint currcon;
12790 : Oid conoid;
12791 : ScanKeyData pkey;
12792 : SysScanDesc pscan;
12793 : HeapTuple childtup;
12794 :
12795 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12796 42 : conoid = currcon->oid;
12797 :
12798 42 : ScanKeyInit(&pkey,
12799 : Anum_pg_constraint_conparentid,
12800 : BTEqualStrategyNumber, F_OIDEQ,
12801 : ObjectIdGetDatum(conoid));
12802 :
12803 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12804 : true, NULL, 1, &pkey);
12805 :
12806 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12807 : {
12808 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12809 : Relation childrel;
12810 :
12811 66 : childrel = table_open(childcon->conrelid, lockmode);
12812 :
12813 66 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12814 : childtup, recurse, otherrelids, lockmode);
12815 66 : table_close(childrel, NoLock);
12816 : }
12817 :
12818 42 : systable_endscan(pscan);
12819 42 : }
12820 :
12821 : /*
12822 : * Update the constraint entry for the given ATAlterConstraint command, and
12823 : * invoke the appropriate hooks.
12824 : */
12825 : static void
12826 366 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
12827 : HeapTuple contuple)
12828 : {
12829 : HeapTuple copyTuple;
12830 : Form_pg_constraint copy_con;
12831 :
12832 : Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12833 : cmdcon->alterInheritability);
12834 :
12835 366 : copyTuple = heap_copytuple(contuple);
12836 366 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12837 :
12838 366 : if (cmdcon->alterEnforceability)
12839 : {
12840 144 : copy_con->conenforced = cmdcon->is_enforced;
12841 :
12842 : /*
12843 : * NB: The convalidated status is irrelevant when the constraint is
12844 : * set to NOT ENFORCED, but for consistency, it should still be set
12845 : * appropriately. Similarly, if the constraint is later changed to
12846 : * ENFORCED, validation will be performed during phase 3, so it makes
12847 : * sense to mark it as valid in that case.
12848 : */
12849 144 : copy_con->convalidated = cmdcon->is_enforced;
12850 : }
12851 366 : if (cmdcon->alterDeferrability)
12852 : {
12853 168 : copy_con->condeferrable = cmdcon->deferrable;
12854 168 : copy_con->condeferred = cmdcon->initdeferred;
12855 : }
12856 366 : if (cmdcon->alterInheritability)
12857 60 : copy_con->connoinherit = cmdcon->noinherit;
12858 :
12859 366 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12860 366 : InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12861 :
12862 : /* Make new constraint flags visible to others */
12863 366 : CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12864 :
12865 366 : heap_freetuple(copyTuple);
12866 366 : }
12867 :
12868 : /*
12869 : * ALTER TABLE VALIDATE CONSTRAINT
12870 : *
12871 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12872 : * there's no good way to skip recursing when handling foreign keys: there is
12873 : * no need to lock children in that case, yet we wouldn't be able to avoid
12874 : * doing so at that level.
12875 : *
12876 : * Return value is the address of the validated constraint. If the constraint
12877 : * was already validated, InvalidObjectAddress is returned.
12878 : */
12879 : static ObjectAddress
12880 556 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12881 : bool recurse, bool recursing, LOCKMODE lockmode)
12882 : {
12883 : Relation conrel;
12884 : SysScanDesc scan;
12885 : ScanKeyData skey[3];
12886 : HeapTuple tuple;
12887 : Form_pg_constraint con;
12888 : ObjectAddress address;
12889 :
12890 556 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12891 :
12892 : /*
12893 : * Find and check the target constraint
12894 : */
12895 556 : ScanKeyInit(&skey[0],
12896 : Anum_pg_constraint_conrelid,
12897 : BTEqualStrategyNumber, F_OIDEQ,
12898 : ObjectIdGetDatum(RelationGetRelid(rel)));
12899 556 : ScanKeyInit(&skey[1],
12900 : Anum_pg_constraint_contypid,
12901 : BTEqualStrategyNumber, F_OIDEQ,
12902 : ObjectIdGetDatum(InvalidOid));
12903 556 : ScanKeyInit(&skey[2],
12904 : Anum_pg_constraint_conname,
12905 : BTEqualStrategyNumber, F_NAMEEQ,
12906 : CStringGetDatum(constrName));
12907 556 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12908 : true, NULL, 3, skey);
12909 :
12910 : /* There can be at most one matching row */
12911 556 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12912 0 : ereport(ERROR,
12913 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12914 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12915 : constrName, RelationGetRelationName(rel))));
12916 :
12917 556 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12918 556 : if (con->contype != CONSTRAINT_FOREIGN &&
12919 240 : con->contype != CONSTRAINT_CHECK &&
12920 96 : con->contype != CONSTRAINT_NOTNULL)
12921 0 : ereport(ERROR,
12922 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12923 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key, check, or not-null constraint",
12924 : constrName, RelationGetRelationName(rel)));
12925 :
12926 556 : if (!con->conenforced)
12927 6 : ereport(ERROR,
12928 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12929 : errmsg("cannot validate NOT ENFORCED constraint")));
12930 :
12931 550 : if (!con->convalidated)
12932 : {
12933 532 : if (con->contype == CONSTRAINT_FOREIGN)
12934 : {
12935 310 : QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
12936 : }
12937 222 : else if (con->contype == CONSTRAINT_CHECK)
12938 : {
12939 126 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12940 : tuple, recurse, recursing, lockmode);
12941 : }
12942 96 : else if (con->contype == CONSTRAINT_NOTNULL)
12943 : {
12944 96 : QueueNNConstraintValidation(wqueue, conrel, rel,
12945 : tuple, recurse, recursing, lockmode);
12946 : }
12947 :
12948 532 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12949 : }
12950 : else
12951 18 : address = InvalidObjectAddress; /* already validated */
12952 :
12953 550 : systable_endscan(scan);
12954 :
12955 550 : table_close(conrel, RowExclusiveLock);
12956 :
12957 550 : return address;
12958 : }
12959 :
12960 : /*
12961 : * QueueFKConstraintValidation
12962 : *
12963 : * Add an entry to the wqueue to validate the given foreign key constraint in
12964 : * Phase 3 and update the convalidated field in the pg_constraint catalog
12965 : * for the specified relation and all its children.
12966 : */
12967 : static void
12968 334 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12969 : HeapTuple contuple, LOCKMODE lockmode)
12970 : {
12971 : Form_pg_constraint con;
12972 : AlteredTableInfo *tab;
12973 : HeapTuple copyTuple;
12974 : Form_pg_constraint copy_con;
12975 :
12976 334 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12977 : Assert(con->contype == CONSTRAINT_FOREIGN);
12978 : Assert(!con->convalidated);
12979 :
12980 334 : if (rel->rd_rel->relkind == RELKIND_RELATION)
12981 : {
12982 : NewConstraint *newcon;
12983 : Constraint *fkconstraint;
12984 :
12985 : /* Queue validation for phase 3 */
12986 322 : fkconstraint = makeNode(Constraint);
12987 : /* for now this is all we need */
12988 322 : fkconstraint->conname = pstrdup(NameStr(con->conname));
12989 :
12990 322 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12991 322 : newcon->name = fkconstraint->conname;
12992 322 : newcon->contype = CONSTR_FOREIGN;
12993 322 : newcon->refrelid = con->confrelid;
12994 322 : newcon->refindid = con->conindid;
12995 322 : newcon->conid = con->oid;
12996 322 : newcon->qual = (Node *) fkconstraint;
12997 :
12998 : /* Find or create work queue entry for this table */
12999 322 : tab = ATGetQueueEntry(wqueue, rel);
13000 322 : tab->constraints = lappend(tab->constraints, newcon);
13001 : }
13002 :
13003 : /*
13004 : * If the table at either end of the constraint is partitioned, we need to
13005 : * recurse and handle every constraint that is a child of this constraint.
13006 : */
13007 656 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13008 322 : get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13009 : {
13010 : ScanKeyData pkey;
13011 : SysScanDesc pscan;
13012 : HeapTuple childtup;
13013 :
13014 18 : ScanKeyInit(&pkey,
13015 : Anum_pg_constraint_conparentid,
13016 : BTEqualStrategyNumber, F_OIDEQ,
13017 : ObjectIdGetDatum(con->oid));
13018 :
13019 18 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13020 : true, NULL, 1, &pkey);
13021 :
13022 36 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13023 : {
13024 : Form_pg_constraint childcon;
13025 : Relation childrel;
13026 :
13027 18 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13028 :
13029 : /*
13030 : * If the child constraint has already been validated, no further
13031 : * action is required for it or its descendants, as they are all
13032 : * valid.
13033 : */
13034 18 : if (childcon->convalidated)
13035 12 : continue;
13036 :
13037 6 : childrel = table_open(childcon->conrelid, lockmode);
13038 :
13039 6 : QueueFKConstraintValidation(wqueue, conrel, childrel, childtup,
13040 : lockmode);
13041 6 : table_close(childrel, NoLock);
13042 : }
13043 :
13044 18 : systable_endscan(pscan);
13045 : }
13046 :
13047 : /*
13048 : * Now update the catalog, while we have the door open.
13049 : */
13050 334 : copyTuple = heap_copytuple(contuple);
13051 334 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13052 334 : copy_con->convalidated = true;
13053 334 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13054 :
13055 334 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13056 :
13057 334 : heap_freetuple(copyTuple);
13058 334 : }
13059 :
13060 : /*
13061 : * QueueCheckConstraintValidation
13062 : *
13063 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
13064 : * and update the convalidated field in the pg_constraint catalog for the
13065 : * specified relation and all its inheriting children.
13066 : */
13067 : static void
13068 126 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13069 : char *constrName, HeapTuple contuple,
13070 : bool recurse, bool recursing, LOCKMODE lockmode)
13071 : {
13072 : Form_pg_constraint con;
13073 : AlteredTableInfo *tab;
13074 : HeapTuple copyTuple;
13075 : Form_pg_constraint copy_con;
13076 :
13077 126 : List *children = NIL;
13078 : ListCell *child;
13079 : NewConstraint *newcon;
13080 : Datum val;
13081 : char *conbin;
13082 :
13083 126 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13084 : Assert(con->contype == CONSTRAINT_CHECK);
13085 :
13086 : /*
13087 : * If we're recursing, the parent has already done this, so skip it. Also,
13088 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13089 : * for it in the children.
13090 : */
13091 126 : if (!recursing && !con->connoinherit)
13092 72 : children = find_all_inheritors(RelationGetRelid(rel),
13093 : lockmode, NULL);
13094 :
13095 : /*
13096 : * For CHECK constraints, we must ensure that we only mark the constraint
13097 : * as validated on the parent if it's already validated on the children.
13098 : *
13099 : * We recurse before validating on the parent, to reduce risk of
13100 : * deadlocks.
13101 : */
13102 246 : foreach(child, children)
13103 : {
13104 120 : Oid childoid = lfirst_oid(child);
13105 : Relation childrel;
13106 :
13107 120 : if (childoid == RelationGetRelid(rel))
13108 72 : continue;
13109 :
13110 : /*
13111 : * If we are told not to recurse, there had better not be any child
13112 : * tables, because we can't mark the constraint on the parent valid
13113 : * unless it is valid for all child tables.
13114 : */
13115 48 : if (!recurse)
13116 0 : ereport(ERROR,
13117 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13118 : errmsg("constraint must be validated on child tables too")));
13119 :
13120 : /* find_all_inheritors already got lock */
13121 48 : childrel = table_open(childoid, NoLock);
13122 :
13123 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
13124 : true, lockmode);
13125 48 : table_close(childrel, NoLock);
13126 : }
13127 :
13128 : /* Queue validation for phase 3 */
13129 126 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13130 126 : newcon->name = constrName;
13131 126 : newcon->contype = CONSTR_CHECK;
13132 126 : newcon->refrelid = InvalidOid;
13133 126 : newcon->refindid = InvalidOid;
13134 126 : newcon->conid = con->oid;
13135 :
13136 126 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13137 : Anum_pg_constraint_conbin);
13138 126 : conbin = TextDatumGetCString(val);
13139 126 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13140 :
13141 : /* Find or create work queue entry for this table */
13142 126 : tab = ATGetQueueEntry(wqueue, rel);
13143 126 : tab->constraints = lappend(tab->constraints, newcon);
13144 :
13145 : /*
13146 : * Invalidate relcache so that others see the new validated constraint.
13147 : */
13148 126 : CacheInvalidateRelcache(rel);
13149 :
13150 : /*
13151 : * Now update the catalog, while we have the door open.
13152 : */
13153 126 : copyTuple = heap_copytuple(contuple);
13154 126 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13155 126 : copy_con->convalidated = true;
13156 126 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13157 :
13158 126 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13159 :
13160 126 : heap_freetuple(copyTuple);
13161 126 : }
13162 :
13163 : /*
13164 : * QueueNNConstraintValidation
13165 : *
13166 : * Add an entry to the wqueue to validate the given not-null constraint in
13167 : * Phase 3 and update the convalidated field in the pg_constraint catalog for
13168 : * the specified relation and all its inheriting children.
13169 : */
13170 : static void
13171 96 : QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13172 : HeapTuple contuple, bool recurse, bool recursing,
13173 : LOCKMODE lockmode)
13174 : {
13175 : Form_pg_constraint con;
13176 : AlteredTableInfo *tab;
13177 : HeapTuple copyTuple;
13178 : Form_pg_constraint copy_con;
13179 96 : List *children = NIL;
13180 : AttrNumber attnum;
13181 : char *colname;
13182 :
13183 96 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13184 : Assert(con->contype == CONSTRAINT_NOTNULL);
13185 :
13186 96 : attnum = extractNotNullColumn(contuple);
13187 :
13188 : /*
13189 : * If we're recursing, we've already done this for parent, so skip it.
13190 : * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13191 : * look for it in the children.
13192 : *
13193 : * We recurse before validating on the parent, to reduce risk of
13194 : * deadlocks.
13195 : */
13196 96 : if (!recursing && !con->connoinherit)
13197 60 : children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13198 :
13199 96 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13200 330 : foreach_oid(childoid, children)
13201 : {
13202 : Relation childrel;
13203 : HeapTuple contup;
13204 : Form_pg_constraint childcon;
13205 : char *conname;
13206 :
13207 138 : if (childoid == RelationGetRelid(rel))
13208 60 : continue;
13209 :
13210 : /*
13211 : * If we are told not to recurse, there had better not be any child
13212 : * tables, because we can't mark the constraint on the parent valid
13213 : * unless it is valid for all child tables.
13214 : */
13215 78 : if (!recurse)
13216 0 : ereport(ERROR,
13217 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13218 : errmsg("constraint must be validated on child tables too"));
13219 :
13220 : /*
13221 : * The column on child might have a different attnum, so search by
13222 : * column name.
13223 : */
13224 78 : contup = findNotNullConstraint(childoid, colname);
13225 78 : if (!contup)
13226 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13227 : colname, get_rel_name(childoid));
13228 78 : childcon = (Form_pg_constraint) GETSTRUCT(contup);
13229 78 : if (childcon->convalidated)
13230 42 : continue;
13231 :
13232 : /* find_all_inheritors already got lock */
13233 36 : childrel = table_open(childoid, NoLock);
13234 36 : conname = pstrdup(NameStr(childcon->conname));
13235 :
13236 : /* XXX improve ATExecValidateConstraint API to avoid double search */
13237 36 : ATExecValidateConstraint(wqueue, childrel, conname,
13238 : false, true, lockmode);
13239 36 : table_close(childrel, NoLock);
13240 : }
13241 :
13242 : /* Set attnotnull appropriately without queueing another validation */
13243 96 : set_attnotnull(NULL, rel, attnum, true, false);
13244 :
13245 96 : tab = ATGetQueueEntry(wqueue, rel);
13246 96 : tab->verify_new_notnull = true;
13247 :
13248 : /*
13249 : * Invalidate relcache so that others see the new validated constraint.
13250 : */
13251 96 : CacheInvalidateRelcache(rel);
13252 :
13253 : /*
13254 : * Now update the catalogs, while we have the door open.
13255 : */
13256 96 : copyTuple = heap_copytuple(contuple);
13257 96 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13258 96 : copy_con->convalidated = true;
13259 96 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13260 :
13261 96 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13262 :
13263 96 : heap_freetuple(copyTuple);
13264 96 : }
13265 :
13266 : /*
13267 : * transformColumnNameList - transform list of column names
13268 : *
13269 : * Lookup each name and return its attnum and, optionally, type and collation
13270 : * OIDs
13271 : *
13272 : * Note: the name of this function suggests that it's general-purpose,
13273 : * but actually it's only used to look up names appearing in foreign-key
13274 : * clauses. The error messages would need work to use it in other cases,
13275 : * and perhaps the validity checks as well.
13276 : */
13277 : static int
13278 6568 : transformColumnNameList(Oid relId, List *colList,
13279 : int16 *attnums, Oid *atttypids, Oid *attcollids)
13280 : {
13281 : ListCell *l;
13282 : int attnum;
13283 :
13284 6568 : attnum = 0;
13285 12020 : foreach(l, colList)
13286 : {
13287 5518 : char *attname = strVal(lfirst(l));
13288 : HeapTuple atttuple;
13289 : Form_pg_attribute attform;
13290 :
13291 5518 : atttuple = SearchSysCacheAttName(relId, attname);
13292 5518 : if (!HeapTupleIsValid(atttuple))
13293 54 : ereport(ERROR,
13294 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13295 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13296 : attname)));
13297 5464 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13298 5464 : if (attform->attnum < 0)
13299 12 : ereport(ERROR,
13300 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13301 : errmsg("system columns cannot be used in foreign keys")));
13302 5452 : if (attnum >= INDEX_MAX_KEYS)
13303 0 : ereport(ERROR,
13304 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
13305 : errmsg("cannot have more than %d keys in a foreign key",
13306 : INDEX_MAX_KEYS)));
13307 5452 : attnums[attnum] = attform->attnum;
13308 5452 : if (atttypids != NULL)
13309 5416 : atttypids[attnum] = attform->atttypid;
13310 5452 : if (attcollids != NULL)
13311 5416 : attcollids[attnum] = attform->attcollation;
13312 5452 : ReleaseSysCache(atttuple);
13313 5452 : attnum++;
13314 : }
13315 :
13316 6502 : return attnum;
13317 : }
13318 :
13319 : /*
13320 : * transformFkeyGetPrimaryKey -
13321 : *
13322 : * Look up the names, attnums, types, and collations of the primary key attributes
13323 : * for the pkrel. Also return the index OID and index opclasses of the
13324 : * index supporting the primary key. Also return whether the index has
13325 : * WITHOUT OVERLAPS.
13326 : *
13327 : * All parameters except pkrel are output parameters. Also, the function
13328 : * return value is the number of attributes in the primary key.
13329 : *
13330 : * Used when the column list in the REFERENCES specification is omitted.
13331 : */
13332 : static int
13333 1184 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
13334 : List **attnamelist,
13335 : int16 *attnums, Oid *atttypids, Oid *attcollids,
13336 : Oid *opclasses, bool *pk_has_without_overlaps)
13337 : {
13338 : List *indexoidlist;
13339 : ListCell *indexoidscan;
13340 1184 : HeapTuple indexTuple = NULL;
13341 1184 : Form_pg_index indexStruct = NULL;
13342 : Datum indclassDatum;
13343 : oidvector *indclass;
13344 : int i;
13345 :
13346 : /*
13347 : * Get the list of index OIDs for the table from the relcache, and look up
13348 : * each one in the pg_index syscache until we find one marked primary key
13349 : * (hopefully there isn't more than one such). Insist it's valid, too.
13350 : */
13351 1184 : *indexOid = InvalidOid;
13352 :
13353 1184 : indexoidlist = RelationGetIndexList(pkrel);
13354 :
13355 1190 : foreach(indexoidscan, indexoidlist)
13356 : {
13357 1190 : Oid indexoid = lfirst_oid(indexoidscan);
13358 :
13359 1190 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13360 1190 : if (!HeapTupleIsValid(indexTuple))
13361 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13362 1190 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13363 1190 : if (indexStruct->indisprimary && indexStruct->indisvalid)
13364 : {
13365 : /*
13366 : * Refuse to use a deferrable primary key. This is per SQL spec,
13367 : * and there would be a lot of interesting semantic problems if we
13368 : * tried to allow it.
13369 : */
13370 1184 : if (!indexStruct->indimmediate)
13371 0 : ereport(ERROR,
13372 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13373 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13374 : RelationGetRelationName(pkrel))));
13375 :
13376 1184 : *indexOid = indexoid;
13377 1184 : break;
13378 : }
13379 6 : ReleaseSysCache(indexTuple);
13380 : }
13381 :
13382 1184 : list_free(indexoidlist);
13383 :
13384 : /*
13385 : * Check that we found it
13386 : */
13387 1184 : if (!OidIsValid(*indexOid))
13388 0 : ereport(ERROR,
13389 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13390 : errmsg("there is no primary key for referenced table \"%s\"",
13391 : RelationGetRelationName(pkrel))));
13392 :
13393 : /* Must get indclass the hard way */
13394 1184 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13395 : Anum_pg_index_indclass);
13396 1184 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13397 :
13398 : /*
13399 : * Now build the list of PK attributes from the indkey definition (we
13400 : * assume a primary key cannot have expressional elements)
13401 : */
13402 1184 : *attnamelist = NIL;
13403 2798 : for (i = 0; i < indexStruct->indnkeyatts; i++)
13404 : {
13405 1614 : int pkattno = indexStruct->indkey.values[i];
13406 :
13407 1614 : attnums[i] = pkattno;
13408 1614 : atttypids[i] = attnumTypeId(pkrel, pkattno);
13409 1614 : attcollids[i] = attnumCollationId(pkrel, pkattno);
13410 1614 : opclasses[i] = indclass->values[i];
13411 1614 : *attnamelist = lappend(*attnamelist,
13412 1614 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13413 : }
13414 :
13415 1184 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13416 :
13417 1184 : ReleaseSysCache(indexTuple);
13418 :
13419 1184 : return i;
13420 : }
13421 :
13422 : /*
13423 : * transformFkeyCheckAttrs -
13424 : *
13425 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13426 : * reference as part of a foreign key constraint.
13427 : *
13428 : * Returns the OID of the unique index supporting the constraint and
13429 : * populates the caller-provided 'opclasses' array with the opclasses
13430 : * associated with the index columns. Also sets whether the index
13431 : * uses WITHOUT OVERLAPS.
13432 : *
13433 : * Raises an ERROR on validation failure.
13434 : */
13435 : static Oid
13436 1320 : transformFkeyCheckAttrs(Relation pkrel,
13437 : int numattrs, int16 *attnums,
13438 : bool with_period, Oid *opclasses,
13439 : bool *pk_has_without_overlaps)
13440 : {
13441 1320 : Oid indexoid = InvalidOid;
13442 1320 : bool found = false;
13443 1320 : bool found_deferrable = false;
13444 : List *indexoidlist;
13445 : ListCell *indexoidscan;
13446 : int i,
13447 : j;
13448 :
13449 : /*
13450 : * Reject duplicate appearances of columns in the referenced-columns list.
13451 : * Such a case is forbidden by the SQL standard, and even if we thought it
13452 : * useful to allow it, there would be ambiguity about how to match the
13453 : * list to unique indexes (in particular, it'd be unclear which index
13454 : * opclass goes with which FK column).
13455 : */
13456 3092 : for (i = 0; i < numattrs; i++)
13457 : {
13458 2346 : for (j = i + 1; j < numattrs; j++)
13459 : {
13460 574 : if (attnums[i] == attnums[j])
13461 24 : ereport(ERROR,
13462 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13463 : errmsg("foreign key referenced-columns list must not contain duplicates")));
13464 : }
13465 : }
13466 :
13467 : /*
13468 : * Get the list of index OIDs for the table from the relcache, and look up
13469 : * each one in the pg_index syscache, and match unique indexes to the list
13470 : * of attnums we are given.
13471 : */
13472 1296 : indexoidlist = RelationGetIndexList(pkrel);
13473 :
13474 1478 : foreach(indexoidscan, indexoidlist)
13475 : {
13476 : HeapTuple indexTuple;
13477 : Form_pg_index indexStruct;
13478 :
13479 1466 : indexoid = lfirst_oid(indexoidscan);
13480 1466 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13481 1466 : if (!HeapTupleIsValid(indexTuple))
13482 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13483 1466 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13484 :
13485 : /*
13486 : * Must have the right number of columns; must be unique (or if
13487 : * temporal then exclusion instead) and not a partial index; forget it
13488 : * if there are any expressions, too. Invalid indexes are out as well.
13489 : */
13490 2824 : if (indexStruct->indnkeyatts == numattrs &&
13491 1358 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13492 2684 : indexStruct->indisvalid &&
13493 2684 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13494 1342 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13495 : {
13496 : Datum indclassDatum;
13497 : oidvector *indclass;
13498 :
13499 : /* Must get indclass the hard way */
13500 1342 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13501 : Anum_pg_index_indclass);
13502 1342 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13503 :
13504 : /*
13505 : * The given attnum list may match the index columns in any order.
13506 : * Check for a match, and extract the appropriate opclasses while
13507 : * we're at it.
13508 : *
13509 : * We know that attnums[] is duplicate-free per the test at the
13510 : * start of this function, and we checked above that the number of
13511 : * index columns agrees, so if we find a match for each attnums[]
13512 : * entry then we must have a one-to-one match in some order.
13513 : */
13514 3102 : for (i = 0; i < numattrs; i++)
13515 : {
13516 1818 : found = false;
13517 2426 : for (j = 0; j < numattrs; j++)
13518 : {
13519 2368 : if (attnums[i] == indexStruct->indkey.values[j])
13520 : {
13521 1760 : opclasses[i] = indclass->values[j];
13522 1760 : found = true;
13523 1760 : break;
13524 : }
13525 : }
13526 1818 : if (!found)
13527 58 : break;
13528 : }
13529 : /* The last attribute in the index must be the PERIOD FK part */
13530 1342 : if (found && with_period)
13531 : {
13532 128 : int16 periodattnum = attnums[numattrs - 1];
13533 :
13534 128 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13535 : }
13536 :
13537 : /*
13538 : * Refuse to use a deferrable unique/primary key. This is per SQL
13539 : * spec, and there would be a lot of interesting semantic problems
13540 : * if we tried to allow it.
13541 : */
13542 1342 : if (found && !indexStruct->indimmediate)
13543 : {
13544 : /*
13545 : * Remember that we found an otherwise matching index, so that
13546 : * we can generate a more appropriate error message.
13547 : */
13548 0 : found_deferrable = true;
13549 0 : found = false;
13550 : }
13551 :
13552 : /* We need to know whether the index has WITHOUT OVERLAPS */
13553 1342 : if (found)
13554 1284 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13555 : }
13556 1466 : ReleaseSysCache(indexTuple);
13557 1466 : if (found)
13558 1284 : break;
13559 : }
13560 :
13561 1296 : if (!found)
13562 : {
13563 12 : if (found_deferrable)
13564 0 : ereport(ERROR,
13565 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13566 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13567 : RelationGetRelationName(pkrel))));
13568 : else
13569 12 : ereport(ERROR,
13570 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13571 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13572 : RelationGetRelationName(pkrel))));
13573 : }
13574 :
13575 1284 : list_free(indexoidlist);
13576 :
13577 1284 : return indexoid;
13578 : }
13579 :
13580 : /*
13581 : * findFkeyCast -
13582 : *
13583 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13584 : * Caller has equal regard for binary coercibility and for an exact match.
13585 : */
13586 : static CoercionPathType
13587 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13588 : {
13589 : CoercionPathType ret;
13590 :
13591 12 : if (targetTypeId == sourceTypeId)
13592 : {
13593 12 : ret = COERCION_PATH_RELABELTYPE;
13594 12 : *funcid = InvalidOid;
13595 : }
13596 : else
13597 : {
13598 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13599 : COERCION_IMPLICIT, funcid);
13600 0 : if (ret == COERCION_PATH_NONE)
13601 : /* A previously-relied-upon cast is now gone. */
13602 0 : elog(ERROR, "could not find cast from %u to %u",
13603 : sourceTypeId, targetTypeId);
13604 : }
13605 :
13606 12 : return ret;
13607 : }
13608 :
13609 : /*
13610 : * Permissions checks on the referenced table for ADD FOREIGN KEY
13611 : *
13612 : * Note: we have already checked that the user owns the referencing table,
13613 : * else we'd have failed much earlier; no additional checks are needed for it.
13614 : */
13615 : static void
13616 2432 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13617 : {
13618 2432 : Oid roleid = GetUserId();
13619 : AclResult aclresult;
13620 : int i;
13621 :
13622 : /* Okay if we have relation-level REFERENCES permission */
13623 2432 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13624 : ACL_REFERENCES);
13625 2432 : if (aclresult == ACLCHECK_OK)
13626 2432 : return;
13627 : /* Else we must have REFERENCES on each column */
13628 0 : for (i = 0; i < natts; i++)
13629 : {
13630 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13631 : roleid, ACL_REFERENCES);
13632 0 : if (aclresult != ACLCHECK_OK)
13633 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13634 0 : RelationGetRelationName(rel));
13635 : }
13636 : }
13637 :
13638 : /*
13639 : * Scan the existing rows in a table to verify they meet a proposed FK
13640 : * constraint.
13641 : *
13642 : * Caller must have opened and locked both relations appropriately.
13643 : */
13644 : static void
13645 1212 : validateForeignKeyConstraint(char *conname,
13646 : Relation rel,
13647 : Relation pkrel,
13648 : Oid pkindOid,
13649 : Oid constraintOid,
13650 : bool hasperiod)
13651 : {
13652 : TupleTableSlot *slot;
13653 : TableScanDesc scan;
13654 1212 : Trigger trig = {0};
13655 : Snapshot snapshot;
13656 : MemoryContext oldcxt;
13657 : MemoryContext perTupCxt;
13658 :
13659 1212 : ereport(DEBUG1,
13660 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13661 :
13662 : /*
13663 : * Build a trigger call structure; we'll need it either way.
13664 : */
13665 1212 : trig.tgoid = InvalidOid;
13666 1212 : trig.tgname = conname;
13667 1212 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13668 1212 : trig.tgisinternal = true;
13669 1212 : trig.tgconstrrelid = RelationGetRelid(pkrel);
13670 1212 : trig.tgconstrindid = pkindOid;
13671 1212 : trig.tgconstraint = constraintOid;
13672 1212 : trig.tgdeferrable = false;
13673 1212 : trig.tginitdeferred = false;
13674 : /* we needn't fill in remaining fields */
13675 :
13676 : /*
13677 : * See if we can do it with a single LEFT JOIN query. A false result
13678 : * indicates we must proceed with the fire-the-trigger method. We can't do
13679 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13680 : * left joins.
13681 : */
13682 1212 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13683 1024 : return;
13684 :
13685 : /*
13686 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13687 : * if that tuple had just been inserted. If any of those fail, it should
13688 : * ereport(ERROR) and that's that.
13689 : */
13690 114 : snapshot = RegisterSnapshot(GetLatestSnapshot());
13691 114 : slot = table_slot_create(rel, NULL);
13692 114 : scan = table_beginscan(rel, snapshot, 0, NULL);
13693 :
13694 114 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
13695 : "validateForeignKeyConstraint",
13696 : ALLOCSET_SMALL_SIZES);
13697 114 : oldcxt = MemoryContextSwitchTo(perTupCxt);
13698 :
13699 200 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13700 : {
13701 104 : LOCAL_FCINFO(fcinfo, 0);
13702 104 : TriggerData trigdata = {0};
13703 :
13704 104 : CHECK_FOR_INTERRUPTS();
13705 :
13706 : /*
13707 : * Make a call to the trigger function
13708 : *
13709 : * No parameters are passed, but we do set a context
13710 : */
13711 520 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13712 :
13713 : /*
13714 : * We assume RI_FKey_check_ins won't look at flinfo...
13715 : */
13716 104 : trigdata.type = T_TriggerData;
13717 104 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
13718 104 : trigdata.tg_relation = rel;
13719 104 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13720 104 : trigdata.tg_trigslot = slot;
13721 104 : trigdata.tg_trigger = &trig;
13722 :
13723 104 : fcinfo->context = (Node *) &trigdata;
13724 :
13725 104 : RI_FKey_check_ins(fcinfo);
13726 :
13727 86 : MemoryContextReset(perTupCxt);
13728 : }
13729 :
13730 96 : MemoryContextSwitchTo(oldcxt);
13731 96 : MemoryContextDelete(perTupCxt);
13732 96 : table_endscan(scan);
13733 96 : UnregisterSnapshot(snapshot);
13734 96 : ExecDropSingleTupleTableSlot(slot);
13735 : }
13736 :
13737 : /*
13738 : * CreateFKCheckTrigger
13739 : * Creates the insert (on_insert=true) or update "check" trigger that
13740 : * implements a given foreign key
13741 : *
13742 : * Returns the OID of the so created trigger.
13743 : */
13744 : static Oid
13745 5900 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13746 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13747 : bool on_insert)
13748 : {
13749 : ObjectAddress trigAddress;
13750 : CreateTrigStmt *fk_trigger;
13751 :
13752 : /*
13753 : * Note: for a self-referential FK (referencing and referenced tables are
13754 : * the same), it is important that the ON UPDATE action fires before the
13755 : * CHECK action, since both triggers will fire on the same row during an
13756 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13757 : * state of the row. Triggers fire in name order, so we ensure this by
13758 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13759 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13760 : */
13761 5900 : fk_trigger = makeNode(CreateTrigStmt);
13762 5900 : fk_trigger->replace = false;
13763 5900 : fk_trigger->isconstraint = true;
13764 5900 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
13765 5900 : fk_trigger->relation = NULL;
13766 :
13767 : /* Either ON INSERT or ON UPDATE */
13768 5900 : if (on_insert)
13769 : {
13770 2950 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13771 2950 : fk_trigger->events = TRIGGER_TYPE_INSERT;
13772 : }
13773 : else
13774 : {
13775 2950 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13776 2950 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13777 : }
13778 :
13779 5900 : fk_trigger->args = NIL;
13780 5900 : fk_trigger->row = true;
13781 5900 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13782 5900 : fk_trigger->columns = NIL;
13783 5900 : fk_trigger->whenClause = NULL;
13784 5900 : fk_trigger->transitionRels = NIL;
13785 5900 : fk_trigger->deferrable = fkconstraint->deferrable;
13786 5900 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13787 5900 : fk_trigger->constrrel = NULL;
13788 :
13789 5900 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13790 : constraintOid, indexOid, InvalidOid,
13791 : parentTrigOid, NULL, true, false);
13792 :
13793 : /* Make changes-so-far visible */
13794 5900 : CommandCounterIncrement();
13795 :
13796 5900 : return trigAddress.objectId;
13797 : }
13798 :
13799 : /*
13800 : * createForeignKeyActionTriggers
13801 : * Create the referenced-side "action" triggers that implement a foreign
13802 : * key.
13803 : *
13804 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
13805 : * *updateTrigOid.
13806 : */
13807 : static void
13808 3208 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13809 : Oid constraintOid, Oid indexOid,
13810 : Oid parentDelTrigger, Oid parentUpdTrigger,
13811 : Oid *deleteTrigOid, Oid *updateTrigOid)
13812 : {
13813 : CreateTrigStmt *fk_trigger;
13814 : ObjectAddress trigAddress;
13815 :
13816 : /*
13817 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13818 : * DELETE action on the referenced table.
13819 : */
13820 3208 : fk_trigger = makeNode(CreateTrigStmt);
13821 3208 : fk_trigger->replace = false;
13822 3208 : fk_trigger->isconstraint = true;
13823 3208 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13824 3208 : fk_trigger->relation = NULL;
13825 3208 : fk_trigger->args = NIL;
13826 3208 : fk_trigger->row = true;
13827 3208 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13828 3208 : fk_trigger->events = TRIGGER_TYPE_DELETE;
13829 3208 : fk_trigger->columns = NIL;
13830 3208 : fk_trigger->whenClause = NULL;
13831 3208 : fk_trigger->transitionRels = NIL;
13832 3208 : fk_trigger->constrrel = NULL;
13833 :
13834 3208 : switch (fkconstraint->fk_del_action)
13835 : {
13836 2574 : case FKCONSTR_ACTION_NOACTION:
13837 2574 : fk_trigger->deferrable = fkconstraint->deferrable;
13838 2574 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13839 2574 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13840 2574 : break;
13841 30 : case FKCONSTR_ACTION_RESTRICT:
13842 30 : fk_trigger->deferrable = false;
13843 30 : fk_trigger->initdeferred = false;
13844 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13845 30 : break;
13846 446 : case FKCONSTR_ACTION_CASCADE:
13847 446 : fk_trigger->deferrable = false;
13848 446 : fk_trigger->initdeferred = false;
13849 446 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13850 446 : break;
13851 98 : case FKCONSTR_ACTION_SETNULL:
13852 98 : fk_trigger->deferrable = false;
13853 98 : fk_trigger->initdeferred = false;
13854 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13855 98 : break;
13856 60 : case FKCONSTR_ACTION_SETDEFAULT:
13857 60 : fk_trigger->deferrable = false;
13858 60 : fk_trigger->initdeferred = false;
13859 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13860 60 : break;
13861 0 : default:
13862 0 : elog(ERROR, "unrecognized FK action type: %d",
13863 : (int) fkconstraint->fk_del_action);
13864 : break;
13865 : }
13866 :
13867 3208 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13868 : constraintOid, indexOid, InvalidOid,
13869 : parentDelTrigger, NULL, true, false);
13870 3208 : if (deleteTrigOid)
13871 3208 : *deleteTrigOid = trigAddress.objectId;
13872 :
13873 : /* Make changes-so-far visible */
13874 3208 : CommandCounterIncrement();
13875 :
13876 : /*
13877 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13878 : * UPDATE action on the referenced table.
13879 : */
13880 3208 : fk_trigger = makeNode(CreateTrigStmt);
13881 3208 : fk_trigger->replace = false;
13882 3208 : fk_trigger->isconstraint = true;
13883 3208 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13884 3208 : fk_trigger->relation = NULL;
13885 3208 : fk_trigger->args = NIL;
13886 3208 : fk_trigger->row = true;
13887 3208 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13888 3208 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13889 3208 : fk_trigger->columns = NIL;
13890 3208 : fk_trigger->whenClause = NULL;
13891 3208 : fk_trigger->transitionRels = NIL;
13892 3208 : fk_trigger->constrrel = NULL;
13893 :
13894 3208 : switch (fkconstraint->fk_upd_action)
13895 : {
13896 2766 : case FKCONSTR_ACTION_NOACTION:
13897 2766 : fk_trigger->deferrable = fkconstraint->deferrable;
13898 2766 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13899 2766 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13900 2766 : break;
13901 36 : case FKCONSTR_ACTION_RESTRICT:
13902 36 : fk_trigger->deferrable = false;
13903 36 : fk_trigger->initdeferred = false;
13904 36 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13905 36 : break;
13906 300 : case FKCONSTR_ACTION_CASCADE:
13907 300 : fk_trigger->deferrable = false;
13908 300 : fk_trigger->initdeferred = false;
13909 300 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13910 300 : break;
13911 64 : case FKCONSTR_ACTION_SETNULL:
13912 64 : fk_trigger->deferrable = false;
13913 64 : fk_trigger->initdeferred = false;
13914 64 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13915 64 : break;
13916 42 : case FKCONSTR_ACTION_SETDEFAULT:
13917 42 : fk_trigger->deferrable = false;
13918 42 : fk_trigger->initdeferred = false;
13919 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13920 42 : break;
13921 0 : default:
13922 0 : elog(ERROR, "unrecognized FK action type: %d",
13923 : (int) fkconstraint->fk_upd_action);
13924 : break;
13925 : }
13926 :
13927 3208 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13928 : constraintOid, indexOid, InvalidOid,
13929 : parentUpdTrigger, NULL, true, false);
13930 3208 : if (updateTrigOid)
13931 3208 : *updateTrigOid = trigAddress.objectId;
13932 3208 : }
13933 :
13934 : /*
13935 : * createForeignKeyCheckTriggers
13936 : * Create the referencing-side "check" triggers that implement a foreign
13937 : * key.
13938 : *
13939 : * Returns the OIDs of the so created triggers in *insertTrigOid and
13940 : * *updateTrigOid.
13941 : */
13942 : static void
13943 2950 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
13944 : Constraint *fkconstraint, Oid constraintOid,
13945 : Oid indexOid,
13946 : Oid parentInsTrigger, Oid parentUpdTrigger,
13947 : Oid *insertTrigOid, Oid *updateTrigOid)
13948 : {
13949 2950 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13950 : constraintOid, indexOid,
13951 : parentInsTrigger, true);
13952 2950 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13953 : constraintOid, indexOid,
13954 : parentUpdTrigger, false);
13955 2950 : }
13956 :
13957 : /*
13958 : * ALTER TABLE DROP CONSTRAINT
13959 : *
13960 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
13961 : */
13962 : static void
13963 792 : ATExecDropConstraint(Relation rel, const char *constrName,
13964 : DropBehavior behavior, bool recurse,
13965 : bool missing_ok, LOCKMODE lockmode)
13966 : {
13967 : Relation conrel;
13968 : SysScanDesc scan;
13969 : ScanKeyData skey[3];
13970 : HeapTuple tuple;
13971 792 : bool found = false;
13972 :
13973 792 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13974 :
13975 : /*
13976 : * Find and drop the target constraint
13977 : */
13978 792 : ScanKeyInit(&skey[0],
13979 : Anum_pg_constraint_conrelid,
13980 : BTEqualStrategyNumber, F_OIDEQ,
13981 : ObjectIdGetDatum(RelationGetRelid(rel)));
13982 792 : ScanKeyInit(&skey[1],
13983 : Anum_pg_constraint_contypid,
13984 : BTEqualStrategyNumber, F_OIDEQ,
13985 : ObjectIdGetDatum(InvalidOid));
13986 792 : ScanKeyInit(&skey[2],
13987 : Anum_pg_constraint_conname,
13988 : BTEqualStrategyNumber, F_NAMEEQ,
13989 : CStringGetDatum(constrName));
13990 792 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13991 : true, NULL, 3, skey);
13992 :
13993 : /* There can be at most one matching row */
13994 792 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
13995 : {
13996 756 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
13997 : missing_ok, lockmode);
13998 570 : found = true;
13999 : }
14000 :
14001 606 : systable_endscan(scan);
14002 :
14003 606 : if (!found)
14004 : {
14005 36 : if (!missing_ok)
14006 24 : ereport(ERROR,
14007 : errcode(ERRCODE_UNDEFINED_OBJECT),
14008 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14009 : constrName, RelationGetRelationName(rel)));
14010 : else
14011 12 : ereport(NOTICE,
14012 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14013 : constrName, RelationGetRelationName(rel)));
14014 : }
14015 :
14016 582 : table_close(conrel, RowExclusiveLock);
14017 582 : }
14018 :
14019 : /*
14020 : * Remove a constraint, using its pg_constraint tuple
14021 : *
14022 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14023 : * DROP NOT NULL.
14024 : *
14025 : * Returns the address of the constraint being removed.
14026 : */
14027 : static ObjectAddress
14028 1186 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
14029 : bool recurse, bool recursing, bool missing_ok,
14030 : LOCKMODE lockmode)
14031 : {
14032 : Relation conrel;
14033 : Form_pg_constraint con;
14034 : ObjectAddress conobj;
14035 : List *children;
14036 1186 : bool is_no_inherit_constraint = false;
14037 : char *constrName;
14038 1186 : char *colname = NULL;
14039 :
14040 : /* Guard against stack overflow due to overly deep inheritance tree. */
14041 1186 : check_stack_depth();
14042 :
14043 : /* At top level, permission check was done in ATPrepCmd, else do it */
14044 1186 : if (recursing)
14045 210 : ATSimplePermissions(AT_DropConstraint, rel,
14046 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
14047 :
14048 1180 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14049 :
14050 1180 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14051 1180 : constrName = NameStr(con->conname);
14052 :
14053 : /* Don't allow drop of inherited constraints */
14054 1180 : if (con->coninhcount > 0 && !recursing)
14055 156 : ereport(ERROR,
14056 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14057 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14058 : constrName, RelationGetRelationName(rel))));
14059 :
14060 : /*
14061 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14062 : *
14063 : * While doing that, we're in a good position to disallow dropping a not-
14064 : * null constraint underneath a primary key, a replica identity index, or
14065 : * a generated identity column.
14066 : */
14067 1024 : if (con->contype == CONSTRAINT_NOTNULL)
14068 : {
14069 314 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14070 314 : AttrNumber attnum = extractNotNullColumn(constraintTup);
14071 : Bitmapset *pkattrs;
14072 : Bitmapset *irattrs;
14073 : HeapTuple atttup;
14074 : Form_pg_attribute attForm;
14075 :
14076 : /* save column name for recursion step */
14077 314 : colname = get_attname(RelationGetRelid(rel), attnum, false);
14078 :
14079 : /*
14080 : * Disallow if it's in the primary key. For partitioned tables we
14081 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14082 : * return NULL if the primary key is invalid; but we still need to
14083 : * protect not-null constraints under such a constraint, so check the
14084 : * slow way.
14085 : */
14086 314 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
14087 :
14088 314 : if (pkattrs == NULL &&
14089 278 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14090 : {
14091 18 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14092 :
14093 18 : if (OidIsValid(pkindex))
14094 : {
14095 0 : Relation pk = relation_open(pkindex, AccessShareLock);
14096 :
14097 0 : pkattrs = NULL;
14098 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14099 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14100 :
14101 0 : relation_close(pk, AccessShareLock);
14102 : }
14103 : }
14104 :
14105 350 : if (pkattrs &&
14106 36 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
14107 24 : ereport(ERROR,
14108 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14109 : errmsg("column \"%s\" is in a primary key",
14110 : get_attname(RelationGetRelid(rel), attnum, false)));
14111 :
14112 : /* Disallow if it's in the replica identity */
14113 290 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
14114 290 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
14115 12 : ereport(ERROR,
14116 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14117 : errmsg("column \"%s\" is in index used as replica identity",
14118 : get_attname(RelationGetRelid(rel), attnum, false)));
14119 :
14120 : /* Disallow if it's a GENERATED AS IDENTITY column */
14121 278 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
14122 278 : if (!HeapTupleIsValid(atttup))
14123 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14124 : attnum, RelationGetRelid(rel));
14125 278 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14126 278 : if (attForm->attidentity != '\0')
14127 0 : ereport(ERROR,
14128 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14129 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
14130 : get_attname(RelationGetRelid(rel), attnum,
14131 : false),
14132 : RelationGetRelationName(rel)));
14133 :
14134 : /* All good -- reset attnotnull if needed */
14135 278 : if (attForm->attnotnull)
14136 : {
14137 278 : attForm->attnotnull = false;
14138 278 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14139 : }
14140 :
14141 278 : table_close(attrel, RowExclusiveLock);
14142 : }
14143 :
14144 988 : is_no_inherit_constraint = con->connoinherit;
14145 :
14146 : /*
14147 : * If it's a foreign-key constraint, we'd better lock the referenced table
14148 : * and check that that's not in use, just as we've already done for the
14149 : * constrained table (else we might, eg, be dropping a trigger that has
14150 : * unfired events). But we can/must skip that in the self-referential
14151 : * case.
14152 : */
14153 988 : if (con->contype == CONSTRAINT_FOREIGN &&
14154 168 : con->confrelid != RelationGetRelid(rel))
14155 : {
14156 : Relation frel;
14157 :
14158 : /* Must match lock taken by RemoveTriggerById: */
14159 168 : frel = table_open(con->confrelid, AccessExclusiveLock);
14160 168 : CheckAlterTableIsSafe(frel);
14161 162 : table_close(frel, NoLock);
14162 : }
14163 :
14164 : /*
14165 : * Perform the actual constraint deletion
14166 : */
14167 982 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14168 982 : performDeletion(&conobj, behavior, 0);
14169 :
14170 : /*
14171 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14172 : * are dropped via the dependency mechanism, so we're done here.
14173 : */
14174 946 : if (con->contype != CONSTRAINT_CHECK &&
14175 628 : con->contype != CONSTRAINT_NOTNULL &&
14176 350 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14177 : {
14178 78 : table_close(conrel, RowExclusiveLock);
14179 78 : return conobj;
14180 : }
14181 :
14182 : /*
14183 : * Propagate to children as appropriate. Unlike most other ALTER
14184 : * routines, we have to do this one level of recursion at a time; we can't
14185 : * use find_all_inheritors to do it in one pass.
14186 : */
14187 868 : if (!is_no_inherit_constraint)
14188 584 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14189 : else
14190 284 : children = NIL;
14191 :
14192 2108 : foreach_oid(childrelid, children)
14193 : {
14194 : Relation childrel;
14195 : HeapTuple tuple;
14196 : Form_pg_constraint childcon;
14197 :
14198 : /* find_inheritance_children already got lock */
14199 384 : childrel = table_open(childrelid, NoLock);
14200 384 : CheckAlterTableIsSafe(childrel);
14201 :
14202 : /*
14203 : * We search for not-null constraints by column name, and others by
14204 : * constraint name.
14205 : */
14206 384 : if (con->contype == CONSTRAINT_NOTNULL)
14207 : {
14208 148 : tuple = findNotNullConstraint(childrelid, colname);
14209 148 : if (!HeapTupleIsValid(tuple))
14210 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14211 : colname, RelationGetRelid(childrel));
14212 : }
14213 : else
14214 : {
14215 : SysScanDesc scan;
14216 : ScanKeyData skey[3];
14217 :
14218 236 : ScanKeyInit(&skey[0],
14219 : Anum_pg_constraint_conrelid,
14220 : BTEqualStrategyNumber, F_OIDEQ,
14221 : ObjectIdGetDatum(childrelid));
14222 236 : ScanKeyInit(&skey[1],
14223 : Anum_pg_constraint_contypid,
14224 : BTEqualStrategyNumber, F_OIDEQ,
14225 : ObjectIdGetDatum(InvalidOid));
14226 236 : ScanKeyInit(&skey[2],
14227 : Anum_pg_constraint_conname,
14228 : BTEqualStrategyNumber, F_NAMEEQ,
14229 : CStringGetDatum(constrName));
14230 236 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14231 : true, NULL, 3, skey);
14232 : /* There can only be one, so no need to loop */
14233 236 : tuple = systable_getnext(scan);
14234 236 : if (!HeapTupleIsValid(tuple))
14235 0 : ereport(ERROR,
14236 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14237 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14238 : constrName,
14239 : RelationGetRelationName(childrel))));
14240 236 : tuple = heap_copytuple(tuple);
14241 236 : systable_endscan(scan);
14242 : }
14243 :
14244 384 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14245 :
14246 : /* Right now only CHECK and not-null constraints can be inherited */
14247 384 : if (childcon->contype != CONSTRAINT_CHECK &&
14248 148 : childcon->contype != CONSTRAINT_NOTNULL)
14249 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14250 :
14251 384 : if (childcon->coninhcount <= 0) /* shouldn't happen */
14252 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14253 : childrelid, NameStr(childcon->conname));
14254 :
14255 384 : if (recurse)
14256 : {
14257 : /*
14258 : * If the child constraint has other definition sources, just
14259 : * decrement its inheritance count; if not, recurse to delete it.
14260 : */
14261 282 : if (childcon->coninhcount == 1 && !childcon->conislocal)
14262 : {
14263 : /* Time to delete this child constraint, too */
14264 210 : dropconstraint_internal(childrel, tuple, behavior,
14265 : recurse, true, missing_ok,
14266 : lockmode);
14267 : }
14268 : else
14269 : {
14270 : /* Child constraint must survive my deletion */
14271 72 : childcon->coninhcount--;
14272 72 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14273 :
14274 : /* Make update visible */
14275 72 : CommandCounterIncrement();
14276 : }
14277 : }
14278 : else
14279 : {
14280 : /*
14281 : * If we were told to drop ONLY in this table (no recursion) and
14282 : * there are no further parents for this constraint, we need to
14283 : * mark the inheritors' constraints as locally defined rather than
14284 : * inherited.
14285 : */
14286 102 : childcon->coninhcount--;
14287 102 : if (childcon->coninhcount == 0)
14288 102 : childcon->conislocal = true;
14289 :
14290 102 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14291 :
14292 : /* Make update visible */
14293 102 : CommandCounterIncrement();
14294 : }
14295 :
14296 378 : heap_freetuple(tuple);
14297 :
14298 378 : table_close(childrel, NoLock);
14299 : }
14300 :
14301 862 : table_close(conrel, RowExclusiveLock);
14302 :
14303 862 : return conobj;
14304 : }
14305 :
14306 : /*
14307 : * ALTER COLUMN TYPE
14308 : *
14309 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14310 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14311 : * transformed (and must be, because we rely on some transformed fields).
14312 : *
14313 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14314 : * table will be done "in parallel" during phase 3, so all the USING
14315 : * expressions should be parsed assuming the original column types. Also,
14316 : * this allows a USING expression to refer to a field that will be dropped.
14317 : *
14318 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14319 : * the first two execution steps in phase 2; they must not see the effects
14320 : * of any other subcommand types, since the USING expressions are parsed
14321 : * against the unmodified table's state.
14322 : */
14323 : static void
14324 1282 : ATPrepAlterColumnType(List **wqueue,
14325 : AlteredTableInfo *tab, Relation rel,
14326 : bool recurse, bool recursing,
14327 : AlterTableCmd *cmd, LOCKMODE lockmode,
14328 : AlterTableUtilityContext *context)
14329 : {
14330 1282 : char *colName = cmd->name;
14331 1282 : ColumnDef *def = (ColumnDef *) cmd->def;
14332 1282 : TypeName *typeName = def->typeName;
14333 1282 : Node *transform = def->cooked_default;
14334 : HeapTuple tuple;
14335 : Form_pg_attribute attTup;
14336 : AttrNumber attnum;
14337 : Oid targettype;
14338 : int32 targettypmod;
14339 : Oid targetcollid;
14340 : NewColumnValue *newval;
14341 1282 : ParseState *pstate = make_parsestate(NULL);
14342 : AclResult aclresult;
14343 : bool is_expr;
14344 :
14345 1282 : pstate->p_sourcetext = context->queryString;
14346 :
14347 1282 : if (rel->rd_rel->reloftype && !recursing)
14348 6 : ereport(ERROR,
14349 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14350 : errmsg("cannot alter column type of typed table"),
14351 : parser_errposition(pstate, def->location)));
14352 :
14353 : /* lookup the attribute so we can check inheritance status */
14354 1276 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14355 1276 : if (!HeapTupleIsValid(tuple))
14356 0 : ereport(ERROR,
14357 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14358 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14359 : colName, RelationGetRelationName(rel)),
14360 : parser_errposition(pstate, def->location)));
14361 1276 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14362 1276 : attnum = attTup->attnum;
14363 :
14364 : /* Can't alter a system attribute */
14365 1276 : if (attnum <= 0)
14366 6 : ereport(ERROR,
14367 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14368 : errmsg("cannot alter system column \"%s\"", colName),
14369 : parser_errposition(pstate, def->location)));
14370 :
14371 : /*
14372 : * Cannot specify USING when altering type of a generated column, because
14373 : * that would violate the generation expression.
14374 : */
14375 1270 : if (attTup->attgenerated && def->cooked_default)
14376 12 : ereport(ERROR,
14377 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14378 : errmsg("cannot specify USING when altering type of generated column"),
14379 : errdetail("Column \"%s\" is a generated column.", colName),
14380 : parser_errposition(pstate, def->location)));
14381 :
14382 : /*
14383 : * Don't alter inherited columns. At outer level, there had better not be
14384 : * any inherited definition; when recursing, we assume this was checked at
14385 : * the parent level (see below).
14386 : */
14387 1258 : if (attTup->attinhcount > 0 && !recursing)
14388 6 : ereport(ERROR,
14389 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14390 : errmsg("cannot alter inherited column \"%s\"", colName),
14391 : parser_errposition(pstate, def->location)));
14392 :
14393 : /* Don't alter columns used in the partition key */
14394 1252 : if (has_partition_attrs(rel,
14395 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
14396 : &is_expr))
14397 18 : ereport(ERROR,
14398 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14399 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14400 : colName, RelationGetRelationName(rel)),
14401 : parser_errposition(pstate, def->location)));
14402 :
14403 : /* Look up the target type */
14404 1234 : typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14405 :
14406 1228 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14407 1228 : if (aclresult != ACLCHECK_OK)
14408 12 : aclcheck_error_type(aclresult, targettype);
14409 :
14410 : /* And the collation */
14411 1216 : targetcollid = GetColumnDefCollation(pstate, def, targettype);
14412 :
14413 : /* make sure datatype is legal for a column */
14414 1210 : CheckAttributeType(colName, targettype, targetcollid,
14415 1210 : list_make1_oid(rel->rd_rel->reltype),
14416 : 0);
14417 :
14418 1204 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14419 : {
14420 : /* do nothing */
14421 : }
14422 1168 : else if (tab->relkind == RELKIND_RELATION ||
14423 202 : tab->relkind == RELKIND_PARTITIONED_TABLE)
14424 : {
14425 : /*
14426 : * Set up an expression to transform the old data value to the new
14427 : * type. If a USING option was given, use the expression as
14428 : * transformed by transformAlterTableStmt, else just take the old
14429 : * value and try to coerce it. We do this first so that type
14430 : * incompatibility can be detected before we waste effort, and because
14431 : * we need the expression to be parsed against the original table row
14432 : * type.
14433 : */
14434 1032 : if (!transform)
14435 : {
14436 810 : transform = (Node *) makeVar(1, attnum,
14437 : attTup->atttypid, attTup->atttypmod,
14438 : attTup->attcollation,
14439 : 0);
14440 : }
14441 :
14442 1032 : transform = coerce_to_target_type(pstate,
14443 : transform, exprType(transform),
14444 : targettype, targettypmod,
14445 : COERCION_ASSIGNMENT,
14446 : COERCE_IMPLICIT_CAST,
14447 : -1);
14448 1032 : if (transform == NULL)
14449 : {
14450 : /* error text depends on whether USING was specified or not */
14451 24 : if (def->cooked_default != NULL)
14452 6 : ereport(ERROR,
14453 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14454 : errmsg("result of USING clause for column \"%s\""
14455 : " cannot be cast automatically to type %s",
14456 : colName, format_type_be(targettype)),
14457 : errhint("You might need to add an explicit cast.")));
14458 : else
14459 18 : ereport(ERROR,
14460 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14461 : errmsg("column \"%s\" cannot be cast automatically to type %s",
14462 : colName, format_type_be(targettype)),
14463 : !attTup->attgenerated ?
14464 : /* translator: USING is SQL, don't translate it */
14465 : errhint("You might need to specify \"USING %s::%s\".",
14466 : quote_identifier(colName),
14467 : format_type_with_typemod(targettype,
14468 : targettypmod)) : 0));
14469 : }
14470 :
14471 : /* Fix collations after all else */
14472 1008 : assign_expr_collations(pstate, transform);
14473 :
14474 : /* Plan the expr now so we can accurately assess the need to rewrite. */
14475 1008 : transform = (Node *) expression_planner((Expr *) transform);
14476 :
14477 : /*
14478 : * Add a work queue item to make ATRewriteTable update the column
14479 : * contents.
14480 : */
14481 1008 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
14482 1008 : newval->attnum = attnum;
14483 1008 : newval->expr = (Expr *) transform;
14484 1008 : newval->is_generated = false;
14485 :
14486 1008 : tab->newvals = lappend(tab->newvals, newval);
14487 1008 : if (ATColumnChangeRequiresRewrite(transform, attnum))
14488 812 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
14489 : }
14490 136 : else if (transform)
14491 12 : ereport(ERROR,
14492 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14493 : errmsg("\"%s\" is not a table",
14494 : RelationGetRelationName(rel))));
14495 :
14496 1168 : if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14497 : {
14498 : /*
14499 : * For relations or columns without storage, do this check now.
14500 : * Regular tables will check it later when the table is being
14501 : * rewritten.
14502 : */
14503 226 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14504 : }
14505 :
14506 1120 : ReleaseSysCache(tuple);
14507 :
14508 : /*
14509 : * Recurse manually by queueing a new command for each child, if
14510 : * necessary. We cannot apply ATSimpleRecursion here because we need to
14511 : * remap attribute numbers in the USING expression, if any.
14512 : *
14513 : * If we are told not to recurse, there had better not be any child
14514 : * tables; else the alter would put them out of step.
14515 : */
14516 1120 : if (recurse)
14517 : {
14518 862 : Oid relid = RelationGetRelid(rel);
14519 : List *child_oids,
14520 : *child_numparents;
14521 : ListCell *lo,
14522 : *li;
14523 :
14524 862 : child_oids = find_all_inheritors(relid, lockmode,
14525 : &child_numparents);
14526 :
14527 : /*
14528 : * find_all_inheritors does the recursive search of the inheritance
14529 : * hierarchy, so all we have to do is process all of the relids in the
14530 : * list that it returns.
14531 : */
14532 1932 : forboth(lo, child_oids, li, child_numparents)
14533 : {
14534 1094 : Oid childrelid = lfirst_oid(lo);
14535 1094 : int numparents = lfirst_int(li);
14536 : Relation childrel;
14537 : HeapTuple childtuple;
14538 : Form_pg_attribute childattTup;
14539 :
14540 1094 : if (childrelid == relid)
14541 862 : continue;
14542 :
14543 : /* find_all_inheritors already got lock */
14544 232 : childrel = relation_open(childrelid, NoLock);
14545 232 : CheckAlterTableIsSafe(childrel);
14546 :
14547 : /*
14548 : * Verify that the child doesn't have any inherited definitions of
14549 : * this column that came from outside this inheritance hierarchy.
14550 : * (renameatt makes a similar test, though in a different way
14551 : * because of its different recursion mechanism.)
14552 : */
14553 232 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14554 : colName);
14555 232 : if (!HeapTupleIsValid(childtuple))
14556 0 : ereport(ERROR,
14557 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14558 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14559 : colName, RelationGetRelationName(childrel))));
14560 232 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14561 :
14562 232 : if (childattTup->attinhcount > numparents)
14563 6 : ereport(ERROR,
14564 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14565 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14566 : colName, RelationGetRelationName(childrel))));
14567 :
14568 226 : ReleaseSysCache(childtuple);
14569 :
14570 : /*
14571 : * Remap the attribute numbers. If no USING expression was
14572 : * specified, there is no need for this step.
14573 : */
14574 226 : if (def->cooked_default)
14575 : {
14576 : AttrMap *attmap;
14577 : bool found_whole_row;
14578 :
14579 : /* create a copy to scribble on */
14580 78 : cmd = copyObject(cmd);
14581 :
14582 78 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14583 : RelationGetDescr(rel),
14584 : false);
14585 156 : ((ColumnDef *) cmd->def)->cooked_default =
14586 78 : map_variable_attnos(def->cooked_default,
14587 : 1, 0,
14588 : attmap,
14589 : InvalidOid, &found_whole_row);
14590 78 : if (found_whole_row)
14591 6 : ereport(ERROR,
14592 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14593 : errmsg("cannot convert whole-row table reference"),
14594 : errdetail("USING expression contains a whole-row table reference.")));
14595 72 : pfree(attmap);
14596 : }
14597 220 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14598 208 : relation_close(childrel, NoLock);
14599 : }
14600 : }
14601 308 : else if (!recursing &&
14602 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
14603 0 : ereport(ERROR,
14604 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14605 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
14606 : colName)));
14607 :
14608 1096 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14609 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14610 1090 : }
14611 :
14612 : /*
14613 : * When the data type of a column is changed, a rewrite might not be required
14614 : * if the new type is sufficiently identical to the old one, and the USING
14615 : * clause isn't trying to insert some other value. It's safe to skip the
14616 : * rewrite in these cases:
14617 : *
14618 : * - the old type is binary coercible to the new type
14619 : * - the new type is an unconstrained domain over the old type
14620 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14621 : *
14622 : * In the case of a constrained domain, we could get by with scanning the
14623 : * table and checking the constraint rather than actually rewriting it, but we
14624 : * don't currently try to do that.
14625 : */
14626 : static bool
14627 1126 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
14628 : {
14629 : Assert(expr != NULL);
14630 :
14631 : for (;;)
14632 : {
14633 : /* only one varno, so no need to check that */
14634 1126 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14635 196 : return false;
14636 930 : else if (IsA(expr, RelabelType))
14637 106 : expr = (Node *) ((RelabelType *) expr)->arg;
14638 824 : else if (IsA(expr, CoerceToDomain))
14639 : {
14640 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
14641 :
14642 0 : if (DomainHasConstraints(d->resulttype))
14643 0 : return true;
14644 0 : expr = (Node *) d->arg;
14645 : }
14646 824 : else if (IsA(expr, FuncExpr))
14647 : {
14648 618 : FuncExpr *f = (FuncExpr *) expr;
14649 :
14650 618 : switch (f->funcid)
14651 : {
14652 18 : case F_TIMESTAMPTZ_TIMESTAMP:
14653 : case F_TIMESTAMP_TIMESTAMPTZ:
14654 18 : if (TimestampTimestampTzRequiresRewrite())
14655 6 : return true;
14656 : else
14657 12 : expr = linitial(f->args);
14658 12 : break;
14659 600 : default:
14660 600 : return true;
14661 : }
14662 : }
14663 : else
14664 206 : return true;
14665 : }
14666 : }
14667 :
14668 : /*
14669 : * ALTER COLUMN .. SET DATA TYPE
14670 : *
14671 : * Return the address of the modified column.
14672 : */
14673 : static ObjectAddress
14674 1054 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
14675 : AlterTableCmd *cmd, LOCKMODE lockmode)
14676 : {
14677 1054 : char *colName = cmd->name;
14678 1054 : ColumnDef *def = (ColumnDef *) cmd->def;
14679 1054 : TypeName *typeName = def->typeName;
14680 : HeapTuple heapTup;
14681 : Form_pg_attribute attTup,
14682 : attOldTup;
14683 : AttrNumber attnum;
14684 : HeapTuple typeTuple;
14685 : Form_pg_type tform;
14686 : Oid targettype;
14687 : int32 targettypmod;
14688 : Oid targetcollid;
14689 : Node *defaultexpr;
14690 : Relation attrelation;
14691 : Relation depRel;
14692 : ScanKeyData key[3];
14693 : SysScanDesc scan;
14694 : HeapTuple depTup;
14695 : ObjectAddress address;
14696 :
14697 : /*
14698 : * Clear all the missing values if we're rewriting the table, since this
14699 : * renders them pointless.
14700 : */
14701 1054 : if (tab->rewrite)
14702 : {
14703 : Relation newrel;
14704 :
14705 752 : newrel = table_open(RelationGetRelid(rel), NoLock);
14706 752 : RelationClearMissing(newrel);
14707 752 : relation_close(newrel, NoLock);
14708 : /* make sure we don't conflict with later attribute modifications */
14709 752 : CommandCounterIncrement();
14710 : }
14711 :
14712 1054 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14713 :
14714 : /* Look up the target column */
14715 1054 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14716 1054 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14717 0 : ereport(ERROR,
14718 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14719 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14720 : colName, RelationGetRelationName(rel))));
14721 1054 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14722 1054 : attnum = attTup->attnum;
14723 1054 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14724 :
14725 : /* Check for multiple ALTER TYPE on same column --- can't cope */
14726 1054 : if (attTup->atttypid != attOldTup->atttypid ||
14727 1054 : attTup->atttypmod != attOldTup->atttypmod)
14728 0 : ereport(ERROR,
14729 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14730 : errmsg("cannot alter type of column \"%s\" twice",
14731 : colName)));
14732 :
14733 : /* Look up the target type (should not fail, since prep found it) */
14734 1054 : typeTuple = typenameType(NULL, typeName, &targettypmod);
14735 1054 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
14736 1054 : targettype = tform->oid;
14737 : /* And the collation */
14738 1054 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
14739 :
14740 : /*
14741 : * If there is a default expression for the column, get it and ensure we
14742 : * can coerce it to the new datatype. (We must do this before changing
14743 : * the column type, because build_column_default itself will try to
14744 : * coerce, and will not issue the error message we want if it fails.)
14745 : *
14746 : * We remove any implicit coercion steps at the top level of the old
14747 : * default expression; this has been agreed to satisfy the principle of
14748 : * least surprise. (The conversion to the new column type should act like
14749 : * it started from what the user sees as the stored expression, and the
14750 : * implicit coercions aren't going to be shown.)
14751 : */
14752 1054 : if (attTup->atthasdef)
14753 : {
14754 92 : defaultexpr = build_column_default(rel, attnum);
14755 : Assert(defaultexpr);
14756 92 : defaultexpr = strip_implicit_coercions(defaultexpr);
14757 92 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14758 : defaultexpr, exprType(defaultexpr),
14759 : targettype, targettypmod,
14760 : COERCION_ASSIGNMENT,
14761 : COERCE_IMPLICIT_CAST,
14762 : -1);
14763 92 : if (defaultexpr == NULL)
14764 : {
14765 6 : if (attTup->attgenerated)
14766 0 : ereport(ERROR,
14767 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14768 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14769 : colName, format_type_be(targettype))));
14770 : else
14771 6 : ereport(ERROR,
14772 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14773 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14774 : colName, format_type_be(targettype))));
14775 : }
14776 : }
14777 : else
14778 962 : defaultexpr = NULL;
14779 :
14780 : /*
14781 : * Find everything that depends on the column (constraints, indexes, etc),
14782 : * and record enough information to let us recreate the objects.
14783 : *
14784 : * The actual recreation does not happen here, but only after we have
14785 : * performed all the individual ALTER TYPE operations. We have to save
14786 : * the info before executing ALTER TYPE, though, else the deparser will
14787 : * get confused.
14788 : */
14789 1048 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
14790 :
14791 : /*
14792 : * Now scan for dependencies of this column on other things. The only
14793 : * things we should find are the dependency on the column datatype and
14794 : * possibly a collation dependency. Those can be removed.
14795 : */
14796 1012 : depRel = table_open(DependRelationId, RowExclusiveLock);
14797 :
14798 1012 : ScanKeyInit(&key[0],
14799 : Anum_pg_depend_classid,
14800 : BTEqualStrategyNumber, F_OIDEQ,
14801 : ObjectIdGetDatum(RelationRelationId));
14802 1012 : ScanKeyInit(&key[1],
14803 : Anum_pg_depend_objid,
14804 : BTEqualStrategyNumber, F_OIDEQ,
14805 : ObjectIdGetDatum(RelationGetRelid(rel)));
14806 1012 : ScanKeyInit(&key[2],
14807 : Anum_pg_depend_objsubid,
14808 : BTEqualStrategyNumber, F_INT4EQ,
14809 : Int32GetDatum((int32) attnum));
14810 :
14811 1012 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
14812 : NULL, 3, key);
14813 :
14814 1016 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14815 : {
14816 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14817 : ObjectAddress foundObject;
14818 :
14819 4 : foundObject.classId = foundDep->refclassid;
14820 4 : foundObject.objectId = foundDep->refobjid;
14821 4 : foundObject.objectSubId = foundDep->refobjsubid;
14822 :
14823 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
14824 0 : elog(ERROR, "found unexpected dependency type '%c'",
14825 : foundDep->deptype);
14826 4 : if (!(foundDep->refclassid == TypeRelationId &&
14827 4 : foundDep->refobjid == attTup->atttypid) &&
14828 0 : !(foundDep->refclassid == CollationRelationId &&
14829 0 : foundDep->refobjid == attTup->attcollation))
14830 0 : elog(ERROR, "found unexpected dependency for column: %s",
14831 : getObjectDescription(&foundObject, false));
14832 :
14833 4 : CatalogTupleDelete(depRel, &depTup->t_self);
14834 : }
14835 :
14836 1012 : systable_endscan(scan);
14837 :
14838 1012 : table_close(depRel, RowExclusiveLock);
14839 :
14840 : /*
14841 : * Here we go --- change the recorded column type and collation. (Note
14842 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14843 : * fix up the missing value if any.
14844 : */
14845 1012 : if (attTup->atthasmissing)
14846 : {
14847 : Datum missingval;
14848 : bool missingNull;
14849 :
14850 : /* if rewrite is true the missing value should already be cleared */
14851 : Assert(tab->rewrite == 0);
14852 :
14853 : /* Get the missing value datum */
14854 6 : missingval = heap_getattr(heapTup,
14855 : Anum_pg_attribute_attmissingval,
14856 : attrelation->rd_att,
14857 : &missingNull);
14858 :
14859 : /* if it's a null array there is nothing to do */
14860 :
14861 6 : if (!missingNull)
14862 : {
14863 : /*
14864 : * Get the datum out of the array and repack it in a new array
14865 : * built with the new type data. We assume that since the table
14866 : * doesn't need rewriting, the actual Datum doesn't need to be
14867 : * changed, only the array metadata.
14868 : */
14869 :
14870 6 : int one = 1;
14871 : bool isNull;
14872 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
14873 6 : bool nullsAtt[Natts_pg_attribute] = {0};
14874 6 : bool replacesAtt[Natts_pg_attribute] = {0};
14875 : HeapTuple newTup;
14876 :
14877 12 : missingval = array_get_element(missingval,
14878 : 1,
14879 : &one,
14880 : 0,
14881 6 : attTup->attlen,
14882 6 : attTup->attbyval,
14883 6 : attTup->attalign,
14884 : &isNull);
14885 6 : missingval = PointerGetDatum(construct_array(&missingval,
14886 : 1,
14887 : targettype,
14888 6 : tform->typlen,
14889 6 : tform->typbyval,
14890 6 : tform->typalign));
14891 :
14892 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14893 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14894 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14895 :
14896 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14897 : valuesAtt, nullsAtt, replacesAtt);
14898 6 : heap_freetuple(heapTup);
14899 6 : heapTup = newTup;
14900 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14901 : }
14902 : }
14903 :
14904 1012 : attTup->atttypid = targettype;
14905 1012 : attTup->atttypmod = targettypmod;
14906 1012 : attTup->attcollation = targetcollid;
14907 1012 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14908 0 : ereport(ERROR,
14909 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14910 : errmsg("too many array dimensions"));
14911 1012 : attTup->attndims = list_length(typeName->arrayBounds);
14912 1012 : attTup->attlen = tform->typlen;
14913 1012 : attTup->attbyval = tform->typbyval;
14914 1012 : attTup->attalign = tform->typalign;
14915 1012 : attTup->attstorage = tform->typstorage;
14916 1012 : attTup->attcompression = InvalidCompressionMethod;
14917 :
14918 1012 : ReleaseSysCache(typeTuple);
14919 :
14920 1012 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14921 :
14922 1012 : table_close(attrelation, RowExclusiveLock);
14923 :
14924 : /* Install dependencies on new datatype and collation */
14925 1012 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
14926 1012 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
14927 :
14928 : /*
14929 : * Drop any pg_statistic entry for the column, since it's now wrong type
14930 : */
14931 1012 : RemoveStatistics(RelationGetRelid(rel), attnum);
14932 :
14933 1012 : InvokeObjectPostAlterHook(RelationRelationId,
14934 : RelationGetRelid(rel), attnum);
14935 :
14936 : /*
14937 : * Update the default, if present, by brute force --- remove and re-add
14938 : * the default. Probably unsafe to take shortcuts, since the new version
14939 : * may well have additional dependencies. (It's okay to do this now,
14940 : * rather than after other ALTER TYPE commands, since the default won't
14941 : * depend on other column types.)
14942 : */
14943 1012 : if (defaultexpr)
14944 : {
14945 : /*
14946 : * If it's a GENERATED default, drop its dependency records, in
14947 : * particular its INTERNAL dependency on the column, which would
14948 : * otherwise cause dependency.c to refuse to perform the deletion.
14949 : */
14950 86 : if (attTup->attgenerated)
14951 : {
14952 36 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14953 :
14954 36 : if (!OidIsValid(attrdefoid))
14955 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14956 : RelationGetRelid(rel), attnum);
14957 36 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14958 : }
14959 :
14960 : /*
14961 : * Make updates-so-far visible, particularly the new pg_attribute row
14962 : * which will be updated again.
14963 : */
14964 86 : CommandCounterIncrement();
14965 :
14966 : /*
14967 : * We use RESTRICT here for safety, but at present we do not expect
14968 : * anything to depend on the default.
14969 : */
14970 86 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
14971 : true);
14972 :
14973 86 : (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
14974 : }
14975 :
14976 1012 : ObjectAddressSubSet(address, RelationRelationId,
14977 : RelationGetRelid(rel), attnum);
14978 :
14979 : /* Cleanup */
14980 1012 : heap_freetuple(heapTup);
14981 :
14982 1012 : return address;
14983 : }
14984 :
14985 : /*
14986 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
14987 : * that depends on the column (constraints, indexes, etc), and record enough
14988 : * information to let us recreate the objects.
14989 : */
14990 : static void
14991 1126 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
14992 : Relation rel, AttrNumber attnum, const char *colName)
14993 : {
14994 : Relation depRel;
14995 : ScanKeyData key[3];
14996 : SysScanDesc scan;
14997 : HeapTuple depTup;
14998 :
14999 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15000 :
15001 1126 : depRel = table_open(DependRelationId, RowExclusiveLock);
15002 :
15003 1126 : ScanKeyInit(&key[0],
15004 : Anum_pg_depend_refclassid,
15005 : BTEqualStrategyNumber, F_OIDEQ,
15006 : ObjectIdGetDatum(RelationRelationId));
15007 1126 : ScanKeyInit(&key[1],
15008 : Anum_pg_depend_refobjid,
15009 : BTEqualStrategyNumber, F_OIDEQ,
15010 : ObjectIdGetDatum(RelationGetRelid(rel)));
15011 1126 : ScanKeyInit(&key[2],
15012 : Anum_pg_depend_refobjsubid,
15013 : BTEqualStrategyNumber, F_INT4EQ,
15014 : Int32GetDatum((int32) attnum));
15015 :
15016 1126 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15017 : NULL, 3, key);
15018 :
15019 2252 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15020 : {
15021 1162 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15022 : ObjectAddress foundObject;
15023 :
15024 1162 : foundObject.classId = foundDep->classid;
15025 1162 : foundObject.objectId = foundDep->objid;
15026 1162 : foundObject.objectSubId = foundDep->objsubid;
15027 :
15028 1162 : switch (foundObject.classId)
15029 : {
15030 274 : case RelationRelationId:
15031 : {
15032 274 : char relKind = get_rel_relkind(foundObject.objectId);
15033 :
15034 274 : if (relKind == RELKIND_INDEX ||
15035 : relKind == RELKIND_PARTITIONED_INDEX)
15036 : {
15037 : Assert(foundObject.objectSubId == 0);
15038 236 : RememberIndexForRebuilding(foundObject.objectId, tab);
15039 : }
15040 38 : else if (relKind == RELKIND_SEQUENCE)
15041 : {
15042 : /*
15043 : * This must be a SERIAL column's sequence. We need
15044 : * not do anything to it.
15045 : */
15046 : Assert(foundObject.objectSubId == 0);
15047 : }
15048 : else
15049 : {
15050 : /* Not expecting any other direct dependencies... */
15051 0 : elog(ERROR, "unexpected object depending on column: %s",
15052 : getObjectDescription(&foundObject, false));
15053 : }
15054 274 : break;
15055 : }
15056 :
15057 674 : case ConstraintRelationId:
15058 : Assert(foundObject.objectSubId == 0);
15059 674 : RememberConstraintForRebuilding(foundObject.objectId, tab);
15060 674 : break;
15061 :
15062 0 : case ProcedureRelationId:
15063 :
15064 : /*
15065 : * A new-style SQL function can depend on a column, if that
15066 : * column is referenced in the parsed function body. Ideally
15067 : * we'd automatically update the function by deparsing and
15068 : * reparsing it, but that's risky and might well fail anyhow.
15069 : * FIXME someday.
15070 : *
15071 : * This is only a problem for AT_AlterColumnType, not
15072 : * AT_SetExpression.
15073 : */
15074 0 : if (subtype == AT_AlterColumnType)
15075 0 : ereport(ERROR,
15076 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15077 : errmsg("cannot alter type of a column used by a function or procedure"),
15078 : errdetail("%s depends on column \"%s\"",
15079 : getObjectDescription(&foundObject, false),
15080 : colName)));
15081 0 : break;
15082 :
15083 12 : case RewriteRelationId:
15084 :
15085 : /*
15086 : * View/rule bodies have pretty much the same issues as
15087 : * function bodies. FIXME someday.
15088 : */
15089 12 : if (subtype == AT_AlterColumnType)
15090 12 : ereport(ERROR,
15091 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15092 : errmsg("cannot alter type of a column used by a view or rule"),
15093 : errdetail("%s depends on column \"%s\"",
15094 : getObjectDescription(&foundObject, false),
15095 : colName)));
15096 0 : break;
15097 :
15098 0 : case TriggerRelationId:
15099 :
15100 : /*
15101 : * A trigger can depend on a column because the column is
15102 : * specified as an update target, or because the column is
15103 : * used in the trigger's WHEN condition. The first case would
15104 : * not require any extra work, but the second case would
15105 : * require updating the WHEN expression, which has the same
15106 : * issues as above. Since we can't easily tell which case
15107 : * applies, we punt for both. FIXME someday.
15108 : */
15109 0 : if (subtype == AT_AlterColumnType)
15110 0 : ereport(ERROR,
15111 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15112 : errmsg("cannot alter type of a column used in a trigger definition"),
15113 : errdetail("%s depends on column \"%s\"",
15114 : getObjectDescription(&foundObject, false),
15115 : colName)));
15116 0 : break;
15117 :
15118 0 : case PolicyRelationId:
15119 :
15120 : /*
15121 : * A policy can depend on a column because the column is
15122 : * specified in the policy's USING or WITH CHECK qual
15123 : * expressions. It might be possible to rewrite and recheck
15124 : * the policy expression, but punt for now. It's certainly
15125 : * easy enough to remove and recreate the policy; still, FIXME
15126 : * someday.
15127 : */
15128 0 : if (subtype == AT_AlterColumnType)
15129 0 : ereport(ERROR,
15130 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15131 : errmsg("cannot alter type of a column used in a policy definition"),
15132 : errdetail("%s depends on column \"%s\"",
15133 : getObjectDescription(&foundObject, false),
15134 : colName)));
15135 0 : break;
15136 :
15137 188 : case AttrDefaultRelationId:
15138 : {
15139 188 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
15140 :
15141 188 : if (col.objectId == RelationGetRelid(rel) &&
15142 188 : col.objectSubId == attnum)
15143 : {
15144 : /*
15145 : * Ignore the column's own default expression. The
15146 : * caller deals with it.
15147 : */
15148 : }
15149 : else
15150 : {
15151 : /*
15152 : * This must be a reference from the expression of a
15153 : * generated column elsewhere in the same table.
15154 : * Changing the type/generated expression of a column
15155 : * that is used by a generated column is not allowed
15156 : * by SQL standard, so just punt for now. It might be
15157 : * doable with some thinking and effort.
15158 : */
15159 24 : if (subtype == AT_AlterColumnType)
15160 24 : ereport(ERROR,
15161 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15162 : errmsg("cannot alter type of a column used by a generated column"),
15163 : errdetail("Column \"%s\" is used by generated column \"%s\".",
15164 : colName,
15165 : get_attname(col.objectId,
15166 : col.objectSubId,
15167 : false))));
15168 : }
15169 164 : break;
15170 : }
15171 :
15172 14 : case StatisticExtRelationId:
15173 :
15174 : /*
15175 : * Give the extended-stats machinery a chance to fix anything
15176 : * that this column type change would break.
15177 : */
15178 14 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
15179 14 : break;
15180 :
15181 0 : case PublicationRelRelationId:
15182 :
15183 : /*
15184 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15185 : * clause. Same issues as above. FIXME someday.
15186 : */
15187 0 : if (subtype == AT_AlterColumnType)
15188 0 : ereport(ERROR,
15189 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15190 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
15191 : errdetail("%s depends on column \"%s\"",
15192 : getObjectDescription(&foundObject, false),
15193 : colName)));
15194 0 : break;
15195 :
15196 0 : default:
15197 :
15198 : /*
15199 : * We don't expect any other sorts of objects to depend on a
15200 : * column.
15201 : */
15202 0 : elog(ERROR, "unexpected object depending on column: %s",
15203 : getObjectDescription(&foundObject, false));
15204 : break;
15205 : }
15206 : }
15207 :
15208 1090 : systable_endscan(scan);
15209 1090 : table_close(depRel, NoLock);
15210 1090 : }
15211 :
15212 : /*
15213 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
15214 : * needs to be reset.
15215 : */
15216 : static void
15217 444 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
15218 : {
15219 444 : if (!get_index_isreplident(indoid))
15220 426 : return;
15221 :
15222 18 : if (tab->replicaIdentityIndex)
15223 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15224 :
15225 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
15226 : }
15227 :
15228 : /*
15229 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
15230 : */
15231 : static void
15232 444 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
15233 : {
15234 444 : if (!get_index_isclustered(indoid))
15235 426 : return;
15236 :
15237 18 : if (tab->clusterOnIndex)
15238 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15239 :
15240 18 : tab->clusterOnIndex = get_rel_name(indoid);
15241 : }
15242 :
15243 : /*
15244 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15245 : * to be rebuilt (which we might already know).
15246 : */
15247 : static void
15248 686 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
15249 : {
15250 : /*
15251 : * This de-duplication check is critical for two independent reasons: we
15252 : * mustn't try to recreate the same constraint twice, and if a constraint
15253 : * depends on more than one column whose type is to be altered, we must
15254 : * capture its definition string before applying any of the column type
15255 : * changes. ruleutils.c will get confused if we ask again later.
15256 : */
15257 686 : if (!list_member_oid(tab->changedConstraintOids, conoid))
15258 : {
15259 : /* OK, capture the constraint's existing definition string */
15260 596 : char *defstring = pg_get_constraintdef_command(conoid);
15261 : Oid indoid;
15262 :
15263 : /*
15264 : * It is critical to create not-null constraints ahead of primary key
15265 : * indexes; otherwise, the not-null constraint would be created by the
15266 : * primary key, and the constraint name would be wrong.
15267 : */
15268 596 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15269 : {
15270 198 : tab->changedConstraintOids = lcons_oid(conoid,
15271 : tab->changedConstraintOids);
15272 198 : tab->changedConstraintDefs = lcons(defstring,
15273 : tab->changedConstraintDefs);
15274 : }
15275 : else
15276 : {
15277 :
15278 398 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
15279 : conoid);
15280 398 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
15281 : defstring);
15282 : }
15283 :
15284 : /*
15285 : * For the index of a constraint, if any, remember if it is used for
15286 : * the table's replica identity or if it is a clustered index, so that
15287 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15288 : * those properties.
15289 : */
15290 596 : indoid = get_constraint_index(conoid);
15291 596 : if (OidIsValid(indoid))
15292 : {
15293 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
15294 228 : RememberClusterOnForRebuilding(indoid, tab);
15295 : }
15296 : }
15297 686 : }
15298 :
15299 : /*
15300 : * Subroutine for ATExecAlterColumnType: remember that an index needs
15301 : * to be rebuilt (which we might already know).
15302 : */
15303 : static void
15304 236 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
15305 : {
15306 : /*
15307 : * This de-duplication check is critical for two independent reasons: we
15308 : * mustn't try to recreate the same index twice, and if an index depends
15309 : * on more than one column whose type is to be altered, we must capture
15310 : * its definition string before applying any of the column type changes.
15311 : * ruleutils.c will get confused if we ask again later.
15312 : */
15313 236 : if (!list_member_oid(tab->changedIndexOids, indoid))
15314 : {
15315 : /*
15316 : * Before adding it as an index-to-rebuild, we'd better see if it
15317 : * belongs to a constraint, and if so rebuild the constraint instead.
15318 : * Typically this check fails, because constraint indexes normally
15319 : * have only dependencies on their constraint. But it's possible for
15320 : * such an index to also have direct dependencies on table columns,
15321 : * for example with a partial exclusion constraint.
15322 : */
15323 228 : Oid conoid = get_index_constraint(indoid);
15324 :
15325 228 : if (OidIsValid(conoid))
15326 : {
15327 12 : RememberConstraintForRebuilding(conoid, tab);
15328 : }
15329 : else
15330 : {
15331 : /* OK, capture the index's existing definition string */
15332 216 : char *defstring = pg_get_indexdef_string(indoid);
15333 :
15334 216 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
15335 : indoid);
15336 216 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
15337 : defstring);
15338 :
15339 : /*
15340 : * Remember if this index is used for the table's replica identity
15341 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15342 : * can queue up commands necessary to restore those properties.
15343 : */
15344 216 : RememberReplicaIdentityForRebuilding(indoid, tab);
15345 216 : RememberClusterOnForRebuilding(indoid, tab);
15346 : }
15347 : }
15348 236 : }
15349 :
15350 : /*
15351 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
15352 : * needs to be rebuilt (which we might already know).
15353 : */
15354 : static void
15355 14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
15356 : {
15357 : /*
15358 : * This de-duplication check is critical for two independent reasons: we
15359 : * mustn't try to recreate the same statistics object twice, and if the
15360 : * statistics object depends on more than one column whose type is to be
15361 : * altered, we must capture its definition string before applying any of
15362 : * the type changes. ruleutils.c will get confused if we ask again later.
15363 : */
15364 14 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15365 : {
15366 : /* OK, capture the statistics object's existing definition string */
15367 14 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
15368 :
15369 14 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
15370 : stxoid);
15371 14 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
15372 : defstring);
15373 : }
15374 14 : }
15375 :
15376 : /*
15377 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15378 : * operations for a particular relation. We have to drop and recreate all the
15379 : * indexes and constraints that depend on the altered columns. We do the
15380 : * actual dropping here, but re-creation is managed by adding work queue
15381 : * entries to do those steps later.
15382 : */
15383 : static void
15384 1120 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
15385 : {
15386 : ObjectAddress obj;
15387 : ObjectAddresses *objects;
15388 : ListCell *def_item;
15389 : ListCell *oid_item;
15390 :
15391 : /*
15392 : * Collect all the constraints and indexes to drop so we can process them
15393 : * in a single call. That way we don't have to worry about dependencies
15394 : * among them.
15395 : */
15396 1120 : objects = new_object_addresses();
15397 :
15398 : /*
15399 : * Re-parse the index and constraint definitions, and attach them to the
15400 : * appropriate work queue entries. We do this before dropping because in
15401 : * the case of a FOREIGN KEY constraint, we might not yet have exclusive
15402 : * lock on the table the constraint is attached to, and we need to get
15403 : * that before reparsing/dropping.
15404 : *
15405 : * We can't rely on the output of deparsing to tell us which relation to
15406 : * operate on, because concurrent activity might have made the name
15407 : * resolve differently. Instead, we've got to use the OID of the
15408 : * constraint or index we're processing to figure out which relation to
15409 : * operate on.
15410 : */
15411 1716 : forboth(oid_item, tab->changedConstraintOids,
15412 : def_item, tab->changedConstraintDefs)
15413 : {
15414 596 : Oid oldId = lfirst_oid(oid_item);
15415 : HeapTuple tup;
15416 : Form_pg_constraint con;
15417 : Oid relid;
15418 : Oid confrelid;
15419 : char contype;
15420 : bool conislocal;
15421 :
15422 596 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15423 596 : if (!HeapTupleIsValid(tup)) /* should not happen */
15424 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15425 596 : con = (Form_pg_constraint) GETSTRUCT(tup);
15426 596 : if (OidIsValid(con->conrelid))
15427 582 : relid = con->conrelid;
15428 : else
15429 : {
15430 : /* must be a domain constraint */
15431 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
15432 14 : if (!OidIsValid(relid))
15433 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15434 : }
15435 596 : confrelid = con->confrelid;
15436 596 : contype = con->contype;
15437 596 : conislocal = con->conislocal;
15438 596 : ReleaseSysCache(tup);
15439 :
15440 596 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
15441 596 : add_exact_object_address(&obj, objects);
15442 :
15443 : /*
15444 : * If the constraint is inherited (only), we don't want to inject a
15445 : * new definition here; it'll get recreated when
15446 : * ATAddCheckNNConstraint recurses from adding the parent table's
15447 : * constraint. But we had to carry the info this far so that we can
15448 : * drop the constraint below.
15449 : */
15450 596 : if (!conislocal)
15451 28 : continue;
15452 :
15453 : /*
15454 : * When rebuilding an FK constraint that references the table we're
15455 : * modifying, we might not yet have any lock on the FK's table, so get
15456 : * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
15457 : * step, so there's no value in asking for anything weaker.
15458 : */
15459 568 : if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
15460 36 : LockRelationOid(relid, AccessExclusiveLock);
15461 :
15462 568 : ATPostAlterTypeParse(oldId, relid, confrelid,
15463 568 : (char *) lfirst(def_item),
15464 568 : wqueue, lockmode, tab->rewrite);
15465 : }
15466 1336 : forboth(oid_item, tab->changedIndexOids,
15467 : def_item, tab->changedIndexDefs)
15468 : {
15469 216 : Oid oldId = lfirst_oid(oid_item);
15470 : Oid relid;
15471 :
15472 216 : relid = IndexGetRelation(oldId, false);
15473 216 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15474 216 : (char *) lfirst(def_item),
15475 216 : wqueue, lockmode, tab->rewrite);
15476 :
15477 216 : ObjectAddressSet(obj, RelationRelationId, oldId);
15478 216 : add_exact_object_address(&obj, objects);
15479 : }
15480 :
15481 : /* add dependencies for new statistics */
15482 1134 : forboth(oid_item, tab->changedStatisticsOids,
15483 : def_item, tab->changedStatisticsDefs)
15484 : {
15485 14 : Oid oldId = lfirst_oid(oid_item);
15486 : Oid relid;
15487 :
15488 14 : relid = StatisticsGetRelation(oldId, false);
15489 14 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15490 14 : (char *) lfirst(def_item),
15491 14 : wqueue, lockmode, tab->rewrite);
15492 :
15493 14 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15494 14 : add_exact_object_address(&obj, objects);
15495 : }
15496 :
15497 : /*
15498 : * Queue up command to restore replica identity index marking
15499 : */
15500 1120 : if (tab->replicaIdentityIndex)
15501 : {
15502 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15503 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
15504 :
15505 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15506 18 : subcmd->name = tab->replicaIdentityIndex;
15507 18 : cmd->subtype = AT_ReplicaIdentity;
15508 18 : cmd->def = (Node *) subcmd;
15509 :
15510 : /* do it after indexes and constraints */
15511 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15512 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15513 : }
15514 :
15515 : /*
15516 : * Queue up command to restore marking of index used for cluster.
15517 : */
15518 1120 : if (tab->clusterOnIndex)
15519 : {
15520 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15521 :
15522 18 : cmd->subtype = AT_ClusterOn;
15523 18 : cmd->name = tab->clusterOnIndex;
15524 :
15525 : /* do it after indexes and constraints */
15526 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15527 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15528 : }
15529 :
15530 : /*
15531 : * It should be okay to use DROP_RESTRICT here, since nothing else should
15532 : * be depending on these objects.
15533 : */
15534 1120 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
15535 :
15536 1120 : free_object_addresses(objects);
15537 :
15538 : /*
15539 : * The objects will get recreated during subsequent passes over the work
15540 : * queue.
15541 : */
15542 1120 : }
15543 :
15544 : /*
15545 : * Parse the previously-saved definition string for a constraint, index or
15546 : * statistics object against the newly-established column data type(s), and
15547 : * queue up the resulting command parsetrees for execution.
15548 : *
15549 : * This might fail if, for example, you have a WHERE clause that uses an
15550 : * operator that's not available for the new column type.
15551 : */
15552 : static void
15553 798 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15554 : List **wqueue, LOCKMODE lockmode, bool rewrite)
15555 : {
15556 : List *raw_parsetree_list;
15557 : List *querytree_list;
15558 : ListCell *list_item;
15559 : Relation rel;
15560 :
15561 : /*
15562 : * We expect that we will get only ALTER TABLE and CREATE INDEX
15563 : * statements. Hence, there is no need to pass them through
15564 : * parse_analyze_*() or the rewriter, but instead we need to pass them
15565 : * through parse_utilcmd.c to make them ready for execution.
15566 : */
15567 798 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15568 798 : querytree_list = NIL;
15569 1596 : foreach(list_item, raw_parsetree_list)
15570 : {
15571 798 : RawStmt *rs = lfirst_node(RawStmt, list_item);
15572 798 : Node *stmt = rs->stmt;
15573 :
15574 798 : if (IsA(stmt, IndexStmt))
15575 216 : querytree_list = lappend(querytree_list,
15576 216 : transformIndexStmt(oldRelId,
15577 : (IndexStmt *) stmt,
15578 : cmd));
15579 582 : else if (IsA(stmt, AlterTableStmt))
15580 : {
15581 : List *beforeStmts;
15582 : List *afterStmts;
15583 :
15584 554 : stmt = (Node *) transformAlterTableStmt(oldRelId,
15585 : (AlterTableStmt *) stmt,
15586 : cmd,
15587 : &beforeStmts,
15588 : &afterStmts);
15589 554 : querytree_list = list_concat(querytree_list, beforeStmts);
15590 554 : querytree_list = lappend(querytree_list, stmt);
15591 554 : querytree_list = list_concat(querytree_list, afterStmts);
15592 : }
15593 28 : else if (IsA(stmt, CreateStatsStmt))
15594 14 : querytree_list = lappend(querytree_list,
15595 14 : transformStatsStmt(oldRelId,
15596 : (CreateStatsStmt *) stmt,
15597 : cmd));
15598 : else
15599 14 : querytree_list = lappend(querytree_list, stmt);
15600 : }
15601 :
15602 : /* Caller should already have acquired whatever lock we need. */
15603 798 : rel = relation_open(oldRelId, NoLock);
15604 :
15605 : /*
15606 : * Attach each generated command to the proper place in the work queue.
15607 : * Note this could result in creation of entirely new work-queue entries.
15608 : *
15609 : * Also note that we have to tweak the command subtypes, because it turns
15610 : * out that re-creation of indexes and constraints has to act a bit
15611 : * differently from initial creation.
15612 : */
15613 1596 : foreach(list_item, querytree_list)
15614 : {
15615 798 : Node *stm = (Node *) lfirst(list_item);
15616 : AlteredTableInfo *tab;
15617 :
15618 798 : tab = ATGetQueueEntry(wqueue, rel);
15619 :
15620 798 : if (IsA(stm, IndexStmt))
15621 : {
15622 216 : IndexStmt *stmt = (IndexStmt *) stm;
15623 : AlterTableCmd *newcmd;
15624 :
15625 216 : if (!rewrite)
15626 56 : TryReuseIndex(oldId, stmt);
15627 216 : stmt->reset_default_tblspc = true;
15628 : /* keep the index's comment */
15629 216 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15630 :
15631 216 : newcmd = makeNode(AlterTableCmd);
15632 216 : newcmd->subtype = AT_ReAddIndex;
15633 216 : newcmd->def = (Node *) stmt;
15634 216 : tab->subcmds[AT_PASS_OLD_INDEX] =
15635 216 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15636 : }
15637 582 : else if (IsA(stm, AlterTableStmt))
15638 : {
15639 554 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
15640 : ListCell *lcmd;
15641 :
15642 1108 : foreach(lcmd, stmt->cmds)
15643 : {
15644 554 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
15645 :
15646 554 : if (cmd->subtype == AT_AddIndex)
15647 : {
15648 : IndexStmt *indstmt;
15649 : Oid indoid;
15650 :
15651 228 : indstmt = castNode(IndexStmt, cmd->def);
15652 228 : indoid = get_constraint_index(oldId);
15653 :
15654 228 : if (!rewrite)
15655 48 : TryReuseIndex(indoid, indstmt);
15656 : /* keep any comment on the index */
15657 228 : indstmt->idxcomment = GetComment(indoid,
15658 : RelationRelationId, 0);
15659 228 : indstmt->reset_default_tblspc = true;
15660 :
15661 228 : cmd->subtype = AT_ReAddIndex;
15662 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
15663 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15664 :
15665 : /* recreate any comment on the constraint */
15666 228 : RebuildConstraintComment(tab,
15667 : AT_PASS_OLD_INDEX,
15668 : oldId,
15669 : rel,
15670 : NIL,
15671 228 : indstmt->idxname);
15672 : }
15673 326 : else if (cmd->subtype == AT_AddConstraint)
15674 : {
15675 326 : Constraint *con = castNode(Constraint, cmd->def);
15676 :
15677 326 : con->old_pktable_oid = refRelId;
15678 : /* rewriting neither side of a FK */
15679 326 : if (con->contype == CONSTR_FOREIGN &&
15680 72 : !rewrite && tab->rewrite == 0)
15681 6 : TryReuseForeignKey(oldId, con);
15682 326 : con->reset_default_tblspc = true;
15683 326 : cmd->subtype = AT_ReAddConstraint;
15684 326 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15685 326 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15686 :
15687 : /*
15688 : * Recreate any comment on the constraint. If we have
15689 : * recreated a primary key, then transformTableConstraint
15690 : * has added an unnamed not-null constraint here; skip
15691 : * this in that case.
15692 : */
15693 326 : if (con->conname)
15694 326 : RebuildConstraintComment(tab,
15695 : AT_PASS_OLD_CONSTR,
15696 : oldId,
15697 : rel,
15698 : NIL,
15699 326 : con->conname);
15700 : else
15701 : Assert(con->contype == CONSTR_NOTNULL);
15702 : }
15703 : else
15704 0 : elog(ERROR, "unexpected statement subtype: %d",
15705 : (int) cmd->subtype);
15706 : }
15707 : }
15708 28 : else if (IsA(stm, AlterDomainStmt))
15709 : {
15710 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
15711 :
15712 14 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
15713 : {
15714 14 : Constraint *con = castNode(Constraint, stmt->def);
15715 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15716 :
15717 14 : cmd->subtype = AT_ReAddDomainConstraint;
15718 14 : cmd->def = (Node *) stmt;
15719 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15720 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15721 :
15722 : /* recreate any comment on the constraint */
15723 14 : RebuildConstraintComment(tab,
15724 : AT_PASS_OLD_CONSTR,
15725 : oldId,
15726 : NULL,
15727 : stmt->typeName,
15728 14 : con->conname);
15729 : }
15730 : else
15731 0 : elog(ERROR, "unexpected statement subtype: %d",
15732 : (int) stmt->subtype);
15733 : }
15734 14 : else if (IsA(stm, CreateStatsStmt))
15735 : {
15736 14 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
15737 : AlterTableCmd *newcmd;
15738 :
15739 : /* keep the statistics object's comment */
15740 14 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15741 :
15742 14 : newcmd = makeNode(AlterTableCmd);
15743 14 : newcmd->subtype = AT_ReAddStatistics;
15744 14 : newcmd->def = (Node *) stmt;
15745 14 : tab->subcmds[AT_PASS_MISC] =
15746 14 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15747 : }
15748 : else
15749 0 : elog(ERROR, "unexpected statement type: %d",
15750 : (int) nodeTag(stm));
15751 : }
15752 :
15753 798 : relation_close(rel, NoLock);
15754 798 : }
15755 :
15756 : /*
15757 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15758 : * for a table or domain constraint that is being rebuilt.
15759 : *
15760 : * objid is the OID of the constraint.
15761 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15762 : * as a string list) for a domain constraint.
15763 : * (We could dig that info, as well as the conname, out of the pg_constraint
15764 : * entry; but callers already have them so might as well pass them.)
15765 : */
15766 : static void
15767 568 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
15768 : Relation rel, List *domname,
15769 : const char *conname)
15770 : {
15771 : CommentStmt *cmd;
15772 : char *comment_str;
15773 : AlterTableCmd *newcmd;
15774 :
15775 : /* Look for comment for object wanted, and leave if none */
15776 568 : comment_str = GetComment(objid, ConstraintRelationId, 0);
15777 568 : if (comment_str == NULL)
15778 478 : return;
15779 :
15780 : /* Build CommentStmt node, copying all input data for safety */
15781 90 : cmd = makeNode(CommentStmt);
15782 90 : if (rel)
15783 : {
15784 78 : cmd->objtype = OBJECT_TABCONSTRAINT;
15785 78 : cmd->object = (Node *)
15786 78 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
15787 : makeString(pstrdup(RelationGetRelationName(rel))),
15788 : makeString(pstrdup(conname)));
15789 : }
15790 : else
15791 : {
15792 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
15793 12 : cmd->object = (Node *)
15794 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
15795 : makeString(pstrdup(conname)));
15796 : }
15797 90 : cmd->comment = comment_str;
15798 :
15799 : /* Append it to list of commands */
15800 90 : newcmd = makeNode(AlterTableCmd);
15801 90 : newcmd->subtype = AT_ReAddComment;
15802 90 : newcmd->def = (Node *) cmd;
15803 90 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15804 : }
15805 :
15806 : /*
15807 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15808 : * for the real analysis, then mutates the IndexStmt based on that verdict.
15809 : */
15810 : static void
15811 104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
15812 : {
15813 104 : if (CheckIndexCompatible(oldId,
15814 104 : stmt->accessMethod,
15815 104 : stmt->indexParams,
15816 104 : stmt->excludeOpNames,
15817 104 : stmt->iswithoutoverlaps))
15818 : {
15819 104 : Relation irel = index_open(oldId, NoLock);
15820 :
15821 : /* If it's a partitioned index, there is no storage to share. */
15822 104 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15823 : {
15824 74 : stmt->oldNumber = irel->rd_locator.relNumber;
15825 74 : stmt->oldCreateSubid = irel->rd_createSubid;
15826 74 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15827 : }
15828 104 : index_close(irel, NoLock);
15829 : }
15830 104 : }
15831 :
15832 : /*
15833 : * Subroutine for ATPostAlterTypeParse().
15834 : *
15835 : * Stash the old P-F equality operator into the Constraint node, for possible
15836 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15837 : * this constraint can be skipped.
15838 : */
15839 : static void
15840 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
15841 : {
15842 : HeapTuple tup;
15843 : Datum adatum;
15844 : ArrayType *arr;
15845 : Oid *rawarr;
15846 : int numkeys;
15847 : int i;
15848 :
15849 : Assert(con->contype == CONSTR_FOREIGN);
15850 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15851 :
15852 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15853 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
15854 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15855 :
15856 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15857 : Anum_pg_constraint_conpfeqop);
15858 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15859 6 : numkeys = ARR_DIMS(arr)[0];
15860 : /* test follows the one in ri_FetchConstraintInfo() */
15861 6 : if (ARR_NDIM(arr) != 1 ||
15862 6 : ARR_HASNULL(arr) ||
15863 6 : ARR_ELEMTYPE(arr) != OIDOID)
15864 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
15865 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
15866 :
15867 : /* stash a List of the operator Oids in our Constraint node */
15868 12 : for (i = 0; i < numkeys; i++)
15869 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15870 :
15871 6 : ReleaseSysCache(tup);
15872 6 : }
15873 :
15874 : /*
15875 : * ALTER COLUMN .. OPTIONS ( ... )
15876 : *
15877 : * Returns the address of the modified column
15878 : */
15879 : static ObjectAddress
15880 172 : ATExecAlterColumnGenericOptions(Relation rel,
15881 : const char *colName,
15882 : List *options,
15883 : LOCKMODE lockmode)
15884 : {
15885 : Relation ftrel;
15886 : Relation attrel;
15887 : ForeignServer *server;
15888 : ForeignDataWrapper *fdw;
15889 : HeapTuple tuple;
15890 : HeapTuple newtuple;
15891 : bool isnull;
15892 : Datum repl_val[Natts_pg_attribute];
15893 : bool repl_null[Natts_pg_attribute];
15894 : bool repl_repl[Natts_pg_attribute];
15895 : Datum datum;
15896 : Form_pg_foreign_table fttableform;
15897 : Form_pg_attribute atttableform;
15898 : AttrNumber attnum;
15899 : ObjectAddress address;
15900 :
15901 172 : if (options == NIL)
15902 0 : return InvalidObjectAddress;
15903 :
15904 : /* First, determine FDW validator associated to the foreign table. */
15905 172 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15906 172 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15907 172 : if (!HeapTupleIsValid(tuple))
15908 0 : ereport(ERROR,
15909 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15910 : errmsg("foreign table \"%s\" does not exist",
15911 : RelationGetRelationName(rel))));
15912 172 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15913 172 : server = GetForeignServer(fttableform->ftserver);
15914 172 : fdw = GetForeignDataWrapper(server->fdwid);
15915 :
15916 172 : table_close(ftrel, AccessShareLock);
15917 172 : ReleaseSysCache(tuple);
15918 :
15919 172 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
15920 172 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15921 172 : if (!HeapTupleIsValid(tuple))
15922 0 : ereport(ERROR,
15923 : (errcode(ERRCODE_UNDEFINED_COLUMN),
15924 : errmsg("column \"%s\" of relation \"%s\" does not exist",
15925 : colName, RelationGetRelationName(rel))));
15926 :
15927 : /* Prevent them from altering a system attribute */
15928 172 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15929 172 : attnum = atttableform->attnum;
15930 172 : if (attnum <= 0)
15931 6 : ereport(ERROR,
15932 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15933 : errmsg("cannot alter system column \"%s\"", colName)));
15934 :
15935 :
15936 : /* Initialize buffers for new tuple values */
15937 166 : memset(repl_val, 0, sizeof(repl_val));
15938 166 : memset(repl_null, false, sizeof(repl_null));
15939 166 : memset(repl_repl, false, sizeof(repl_repl));
15940 :
15941 : /* Extract the current options */
15942 166 : datum = SysCacheGetAttr(ATTNAME,
15943 : tuple,
15944 : Anum_pg_attribute_attfdwoptions,
15945 : &isnull);
15946 166 : if (isnull)
15947 156 : datum = PointerGetDatum(NULL);
15948 :
15949 : /* Transform the options */
15950 166 : datum = transformGenericOptions(AttributeRelationId,
15951 : datum,
15952 : options,
15953 : fdw->fdwvalidator);
15954 :
15955 166 : if (PointerIsValid(DatumGetPointer(datum)))
15956 166 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15957 : else
15958 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15959 :
15960 166 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15961 :
15962 : /* Everything looks good - update the tuple */
15963 :
15964 166 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15965 : repl_val, repl_null, repl_repl);
15966 :
15967 166 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15968 :
15969 166 : InvokeObjectPostAlterHook(RelationRelationId,
15970 : RelationGetRelid(rel),
15971 : atttableform->attnum);
15972 166 : ObjectAddressSubSet(address, RelationRelationId,
15973 : RelationGetRelid(rel), attnum);
15974 :
15975 166 : ReleaseSysCache(tuple);
15976 :
15977 166 : table_close(attrel, RowExclusiveLock);
15978 :
15979 166 : heap_freetuple(newtuple);
15980 :
15981 166 : return address;
15982 : }
15983 :
15984 : /*
15985 : * ALTER TABLE OWNER
15986 : *
15987 : * recursing is true if we are recursing from a table to its indexes,
15988 : * sequences, or toast table. We don't allow the ownership of those things to
15989 : * be changed separately from the parent table. Also, we can skip permission
15990 : * checks (this is necessary not just an optimization, else we'd fail to
15991 : * handle toast tables properly).
15992 : *
15993 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
15994 : * free-standing composite type.
15995 : */
15996 : void
15997 3776 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
15998 : {
15999 : Relation target_rel;
16000 : Relation class_rel;
16001 : HeapTuple tuple;
16002 : Form_pg_class tuple_class;
16003 :
16004 : /*
16005 : * Get exclusive lock till end of transaction on the target table. Use
16006 : * relation_open so that we can work on indexes and sequences.
16007 : */
16008 3776 : target_rel = relation_open(relationOid, lockmode);
16009 :
16010 : /* Get its pg_class tuple, too */
16011 3776 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
16012 :
16013 3776 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16014 3776 : if (!HeapTupleIsValid(tuple))
16015 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
16016 3776 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16017 :
16018 : /* Can we change the ownership of this tuple? */
16019 3776 : switch (tuple_class->relkind)
16020 : {
16021 3430 : case RELKIND_RELATION:
16022 : case RELKIND_VIEW:
16023 : case RELKIND_MATVIEW:
16024 : case RELKIND_FOREIGN_TABLE:
16025 : case RELKIND_PARTITIONED_TABLE:
16026 : /* ok to change owner */
16027 3430 : break;
16028 96 : case RELKIND_INDEX:
16029 96 : if (!recursing)
16030 : {
16031 : /*
16032 : * Because ALTER INDEX OWNER used to be allowed, and in fact
16033 : * is generated by old versions of pg_dump, we give a warning
16034 : * and do nothing rather than erroring out. Also, to avoid
16035 : * unnecessary chatter while restoring those old dumps, say
16036 : * nothing at all if the command would be a no-op anyway.
16037 : */
16038 0 : if (tuple_class->relowner != newOwnerId)
16039 0 : ereport(WARNING,
16040 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16041 : errmsg("cannot change owner of index \"%s\"",
16042 : NameStr(tuple_class->relname)),
16043 : errhint("Change the ownership of the index's table instead.")));
16044 : /* quick hack to exit via the no-op path */
16045 0 : newOwnerId = tuple_class->relowner;
16046 : }
16047 96 : break;
16048 20 : case RELKIND_PARTITIONED_INDEX:
16049 20 : if (recursing)
16050 20 : break;
16051 0 : ereport(ERROR,
16052 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16053 : errmsg("cannot change owner of index \"%s\"",
16054 : NameStr(tuple_class->relname)),
16055 : errhint("Change the ownership of the index's table instead.")));
16056 : break;
16057 180 : case RELKIND_SEQUENCE:
16058 180 : if (!recursing &&
16059 132 : tuple_class->relowner != newOwnerId)
16060 : {
16061 : /* if it's an owned sequence, disallow changing it by itself */
16062 : Oid tableId;
16063 : int32 colId;
16064 :
16065 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16066 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16067 0 : ereport(ERROR,
16068 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16069 : errmsg("cannot change owner of sequence \"%s\"",
16070 : NameStr(tuple_class->relname)),
16071 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
16072 : NameStr(tuple_class->relname),
16073 : get_rel_name(tableId))));
16074 : }
16075 180 : break;
16076 8 : case RELKIND_COMPOSITE_TYPE:
16077 8 : if (recursing)
16078 8 : break;
16079 0 : ereport(ERROR,
16080 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16081 : errmsg("\"%s\" is a composite type",
16082 : NameStr(tuple_class->relname)),
16083 : /* translator: %s is an SQL ALTER command */
16084 : errhint("Use %s instead.",
16085 : "ALTER TYPE")));
16086 : break;
16087 42 : case RELKIND_TOASTVALUE:
16088 42 : if (recursing)
16089 42 : break;
16090 : /* FALL THRU */
16091 : default:
16092 0 : ereport(ERROR,
16093 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16094 : errmsg("cannot change owner of relation \"%s\"",
16095 : NameStr(tuple_class->relname)),
16096 : errdetail_relkind_not_supported(tuple_class->relkind)));
16097 : }
16098 :
16099 : /*
16100 : * If the new owner is the same as the existing owner, consider the
16101 : * command to have succeeded. This is for dump restoration purposes.
16102 : */
16103 3776 : if (tuple_class->relowner != newOwnerId)
16104 : {
16105 : Datum repl_val[Natts_pg_class];
16106 : bool repl_null[Natts_pg_class];
16107 : bool repl_repl[Natts_pg_class];
16108 : Acl *newAcl;
16109 : Datum aclDatum;
16110 : bool isNull;
16111 : HeapTuple newtuple;
16112 :
16113 : /* skip permission checks when recursing to index or toast table */
16114 500 : if (!recursing)
16115 : {
16116 : /* Superusers can always do it */
16117 282 : if (!superuser())
16118 : {
16119 42 : Oid namespaceOid = tuple_class->relnamespace;
16120 : AclResult aclresult;
16121 :
16122 : /* Otherwise, must be owner of the existing object */
16123 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16124 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
16125 0 : RelationGetRelationName(target_rel));
16126 :
16127 : /* Must be able to become new owner */
16128 42 : check_can_set_role(GetUserId(), newOwnerId);
16129 :
16130 : /* New owner must have CREATE privilege on namespace */
16131 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16132 : ACL_CREATE);
16133 30 : if (aclresult != ACLCHECK_OK)
16134 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
16135 0 : get_namespace_name(namespaceOid));
16136 : }
16137 : }
16138 :
16139 488 : memset(repl_null, false, sizeof(repl_null));
16140 488 : memset(repl_repl, false, sizeof(repl_repl));
16141 :
16142 488 : repl_repl[Anum_pg_class_relowner - 1] = true;
16143 488 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16144 :
16145 : /*
16146 : * Determine the modified ACL for the new owner. This is only
16147 : * necessary when the ACL is non-null.
16148 : */
16149 488 : aclDatum = SysCacheGetAttr(RELOID, tuple,
16150 : Anum_pg_class_relacl,
16151 : &isNull);
16152 488 : if (!isNull)
16153 : {
16154 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16155 : tuple_class->relowner, newOwnerId);
16156 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
16157 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16158 : }
16159 :
16160 488 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16161 :
16162 488 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16163 :
16164 488 : heap_freetuple(newtuple);
16165 :
16166 : /*
16167 : * We must similarly update any per-column ACLs to reflect the new
16168 : * owner; for neatness reasons that's split out as a subroutine.
16169 : */
16170 488 : change_owner_fix_column_acls(relationOid,
16171 : tuple_class->relowner,
16172 : newOwnerId);
16173 :
16174 : /*
16175 : * Update owner dependency reference, if any. A composite type has
16176 : * none, because it's tracked for the pg_type entry instead of here;
16177 : * indexes and TOAST tables don't have their own entries either.
16178 : */
16179 488 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16180 480 : tuple_class->relkind != RELKIND_INDEX &&
16181 384 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16182 364 : tuple_class->relkind != RELKIND_TOASTVALUE)
16183 322 : changeDependencyOnOwner(RelationRelationId, relationOid,
16184 : newOwnerId);
16185 :
16186 : /*
16187 : * Also change the ownership of the table's row type, if it has one
16188 : */
16189 488 : if (OidIsValid(tuple_class->reltype))
16190 296 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16191 :
16192 : /*
16193 : * If we are operating on a table or materialized view, also change
16194 : * the ownership of any indexes and sequences that belong to the
16195 : * relation, as well as its toast table (if it has one).
16196 : */
16197 488 : if (tuple_class->relkind == RELKIND_RELATION ||
16198 262 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16199 224 : tuple_class->relkind == RELKIND_MATVIEW ||
16200 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
16201 : {
16202 : List *index_oid_list;
16203 : ListCell *i;
16204 :
16205 : /* Find all the indexes belonging to this relation */
16206 306 : index_oid_list = RelationGetIndexList(target_rel);
16207 :
16208 : /* For each index, recursively change its ownership */
16209 422 : foreach(i, index_oid_list)
16210 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16211 :
16212 306 : list_free(index_oid_list);
16213 : }
16214 :
16215 : /* If it has a toast table, recurse to change its ownership */
16216 488 : if (tuple_class->reltoastrelid != InvalidOid)
16217 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16218 : true, lockmode);
16219 :
16220 : /* If it has dependent sequences, recurse to change them too */
16221 488 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16222 : }
16223 :
16224 3764 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16225 :
16226 3764 : ReleaseSysCache(tuple);
16227 3764 : table_close(class_rel, RowExclusiveLock);
16228 3764 : relation_close(target_rel, NoLock);
16229 3764 : }
16230 :
16231 : /*
16232 : * change_owner_fix_column_acls
16233 : *
16234 : * Helper function for ATExecChangeOwner. Scan the columns of the table
16235 : * and fix any non-null column ACLs to reflect the new owner.
16236 : */
16237 : static void
16238 488 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16239 : {
16240 : Relation attRelation;
16241 : SysScanDesc scan;
16242 : ScanKeyData key[1];
16243 : HeapTuple attributeTuple;
16244 :
16245 488 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16246 488 : ScanKeyInit(&key[0],
16247 : Anum_pg_attribute_attrelid,
16248 : BTEqualStrategyNumber, F_OIDEQ,
16249 : ObjectIdGetDatum(relationOid));
16250 488 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16251 : true, NULL, 1, key);
16252 3386 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16253 : {
16254 2898 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16255 : Datum repl_val[Natts_pg_attribute];
16256 : bool repl_null[Natts_pg_attribute];
16257 : bool repl_repl[Natts_pg_attribute];
16258 : Acl *newAcl;
16259 : Datum aclDatum;
16260 : bool isNull;
16261 : HeapTuple newtuple;
16262 :
16263 : /* Ignore dropped columns */
16264 2898 : if (att->attisdropped)
16265 2896 : continue;
16266 :
16267 2898 : aclDatum = heap_getattr(attributeTuple,
16268 : Anum_pg_attribute_attacl,
16269 : RelationGetDescr(attRelation),
16270 : &isNull);
16271 : /* Null ACLs do not require changes */
16272 2898 : if (isNull)
16273 2896 : continue;
16274 :
16275 2 : memset(repl_null, false, sizeof(repl_null));
16276 2 : memset(repl_repl, false, sizeof(repl_repl));
16277 :
16278 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16279 : oldOwnerId, newOwnerId);
16280 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
16281 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16282 :
16283 2 : newtuple = heap_modify_tuple(attributeTuple,
16284 : RelationGetDescr(attRelation),
16285 : repl_val, repl_null, repl_repl);
16286 :
16287 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16288 :
16289 2 : heap_freetuple(newtuple);
16290 : }
16291 488 : systable_endscan(scan);
16292 488 : table_close(attRelation, RowExclusiveLock);
16293 488 : }
16294 :
16295 : /*
16296 : * change_owner_recurse_to_sequences
16297 : *
16298 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
16299 : * for sequences that are dependent on serial columns, and changes their
16300 : * ownership.
16301 : */
16302 : static void
16303 488 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16304 : {
16305 : Relation depRel;
16306 : SysScanDesc scan;
16307 : ScanKeyData key[2];
16308 : HeapTuple tup;
16309 :
16310 : /*
16311 : * SERIAL sequences are those having an auto dependency on one of the
16312 : * table's columns (we don't care *which* column, exactly).
16313 : */
16314 488 : depRel = table_open(DependRelationId, AccessShareLock);
16315 :
16316 488 : ScanKeyInit(&key[0],
16317 : Anum_pg_depend_refclassid,
16318 : BTEqualStrategyNumber, F_OIDEQ,
16319 : ObjectIdGetDatum(RelationRelationId));
16320 488 : ScanKeyInit(&key[1],
16321 : Anum_pg_depend_refobjid,
16322 : BTEqualStrategyNumber, F_OIDEQ,
16323 : ObjectIdGetDatum(relationOid));
16324 : /* we leave refobjsubid unspecified */
16325 :
16326 488 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16327 : NULL, 2, key);
16328 :
16329 1378 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
16330 : {
16331 890 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16332 : Relation seqRel;
16333 :
16334 : /* skip dependencies other than auto dependencies on columns */
16335 890 : if (depForm->refobjsubid == 0 ||
16336 352 : depForm->classid != RelationRelationId ||
16337 142 : depForm->objsubid != 0 ||
16338 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16339 748 : continue;
16340 :
16341 : /* Use relation_open just in case it's an index */
16342 142 : seqRel = relation_open(depForm->objid, lockmode);
16343 :
16344 : /* skip non-sequence relations */
16345 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16346 : {
16347 : /* No need to keep the lock */
16348 116 : relation_close(seqRel, lockmode);
16349 116 : continue;
16350 : }
16351 :
16352 : /* We don't need to close the sequence while we alter it. */
16353 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16354 :
16355 : /* Now we can close it. Keep the lock till end of transaction. */
16356 26 : relation_close(seqRel, NoLock);
16357 : }
16358 :
16359 488 : systable_endscan(scan);
16360 :
16361 488 : relation_close(depRel, AccessShareLock);
16362 488 : }
16363 :
16364 : /*
16365 : * ALTER TABLE CLUSTER ON
16366 : *
16367 : * The only thing we have to do is to change the indisclustered bits.
16368 : *
16369 : * Return the address of the new clustering index.
16370 : */
16371 : static ObjectAddress
16372 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16373 : {
16374 : Oid indexOid;
16375 : ObjectAddress address;
16376 :
16377 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16378 :
16379 64 : if (!OidIsValid(indexOid))
16380 0 : ereport(ERROR,
16381 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16382 : errmsg("index \"%s\" for table \"%s\" does not exist",
16383 : indexName, RelationGetRelationName(rel))));
16384 :
16385 : /* Check index is valid to cluster on */
16386 64 : check_index_is_clusterable(rel, indexOid, lockmode);
16387 :
16388 : /* And do the work */
16389 64 : mark_index_clustered(rel, indexOid, false);
16390 :
16391 58 : ObjectAddressSet(address,
16392 : RelationRelationId, indexOid);
16393 :
16394 58 : return address;
16395 : }
16396 :
16397 : /*
16398 : * ALTER TABLE SET WITHOUT CLUSTER
16399 : *
16400 : * We have to find any indexes on the table that have indisclustered bit
16401 : * set and turn it off.
16402 : */
16403 : static void
16404 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
16405 : {
16406 18 : mark_index_clustered(rel, InvalidOid, false);
16407 12 : }
16408 :
16409 : /*
16410 : * Preparation phase for SET ACCESS METHOD
16411 : *
16412 : * Check that the access method exists and determine whether a change is
16413 : * actually needed.
16414 : */
16415 : static void
16416 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
16417 : {
16418 : Oid amoid;
16419 :
16420 : /*
16421 : * Look up the access method name and check that it differs from the
16422 : * table's current AM. If DEFAULT was specified for a partitioned table
16423 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16424 : */
16425 110 : if (amname != NULL)
16426 74 : amoid = get_table_am_oid(amname, false);
16427 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16428 18 : amoid = InvalidOid;
16429 : else
16430 18 : amoid = get_table_am_oid(default_table_access_method, false);
16431 :
16432 : /* if it's a match, phase 3 doesn't need to do anything */
16433 110 : if (rel->rd_rel->relam == amoid)
16434 12 : return;
16435 :
16436 : /* Save info for Phase 3 to do the real work */
16437 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
16438 98 : tab->newAccessMethod = amoid;
16439 98 : tab->chgAccessMethod = true;
16440 : }
16441 :
16442 : /*
16443 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16444 : * storage that have an interest in preserving AM.
16445 : *
16446 : * Since these have no storage, setting the access method is a catalog only
16447 : * operation.
16448 : */
16449 : static void
16450 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
16451 : {
16452 : Relation pg_class;
16453 : Oid oldAccessMethodId;
16454 : HeapTuple tuple;
16455 : Form_pg_class rd_rel;
16456 44 : Oid reloid = RelationGetRelid(rel);
16457 :
16458 : /*
16459 : * Shouldn't be called on relations having storage; these are processed in
16460 : * phase 3.
16461 : */
16462 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16463 :
16464 : /* Get a modifiable copy of the relation's pg_class row. */
16465 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
16466 :
16467 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16468 44 : if (!HeapTupleIsValid(tuple))
16469 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
16470 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16471 :
16472 : /* Update the pg_class row. */
16473 44 : oldAccessMethodId = rd_rel->relam;
16474 44 : rd_rel->relam = newAccessMethodId;
16475 :
16476 : /* Leave if no update required */
16477 44 : if (rd_rel->relam == oldAccessMethodId)
16478 : {
16479 0 : heap_freetuple(tuple);
16480 0 : table_close(pg_class, RowExclusiveLock);
16481 0 : return;
16482 : }
16483 :
16484 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16485 :
16486 : /*
16487 : * Update the dependency on the new access method. No dependency is added
16488 : * if the new access method is InvalidOid (default case). Be very careful
16489 : * that this has to compare the previous value stored in pg_class with the
16490 : * new one.
16491 : */
16492 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16493 20 : {
16494 : ObjectAddress relobj,
16495 : referenced;
16496 :
16497 : /*
16498 : * New access method is defined and there was no dependency
16499 : * previously, so record a new one.
16500 : */
16501 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
16502 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16503 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16504 : }
16505 24 : else if (OidIsValid(oldAccessMethodId) &&
16506 24 : !OidIsValid(rd_rel->relam))
16507 : {
16508 : /*
16509 : * There was an access method defined, and no new one, so just remove
16510 : * the existing dependency.
16511 : */
16512 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
16513 : AccessMethodRelationId,
16514 : DEPENDENCY_NORMAL);
16515 : }
16516 : else
16517 : {
16518 : Assert(OidIsValid(oldAccessMethodId) &&
16519 : OidIsValid(rd_rel->relam));
16520 :
16521 : /* Both are valid, so update the dependency */
16522 12 : changeDependencyFor(RelationRelationId, reloid,
16523 : AccessMethodRelationId,
16524 : oldAccessMethodId, rd_rel->relam);
16525 : }
16526 :
16527 : /* make the relam and dependency changes visible */
16528 44 : CommandCounterIncrement();
16529 :
16530 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16531 :
16532 44 : heap_freetuple(tuple);
16533 44 : table_close(pg_class, RowExclusiveLock);
16534 : }
16535 :
16536 : /*
16537 : * ALTER TABLE SET TABLESPACE
16538 : */
16539 : static void
16540 160 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16541 : {
16542 : Oid tablespaceId;
16543 :
16544 : /* Check that the tablespace exists */
16545 160 : tablespaceId = get_tablespace_oid(tablespacename, false);
16546 :
16547 : /* Check permissions except when moving to database's default */
16548 160 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16549 : {
16550 : AclResult aclresult;
16551 :
16552 68 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16553 68 : if (aclresult != ACLCHECK_OK)
16554 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16555 : }
16556 :
16557 : /* Save info for Phase 3 to do the real work */
16558 160 : if (OidIsValid(tab->newTableSpace))
16559 0 : ereport(ERROR,
16560 : (errcode(ERRCODE_SYNTAX_ERROR),
16561 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
16562 :
16563 160 : tab->newTableSpace = tablespaceId;
16564 160 : }
16565 :
16566 : /*
16567 : * Set, reset, or replace reloptions.
16568 : */
16569 : static void
16570 958 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
16571 : LOCKMODE lockmode)
16572 : {
16573 : Oid relid;
16574 : Relation pgclass;
16575 : HeapTuple tuple;
16576 : HeapTuple newtuple;
16577 : Datum datum;
16578 : Datum newOptions;
16579 : Datum repl_val[Natts_pg_class];
16580 : bool repl_null[Natts_pg_class];
16581 : bool repl_repl[Natts_pg_class];
16582 958 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16583 :
16584 958 : if (defList == NIL && operation != AT_ReplaceRelOptions)
16585 0 : return; /* nothing to do */
16586 :
16587 958 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
16588 :
16589 : /* Fetch heap tuple */
16590 958 : relid = RelationGetRelid(rel);
16591 958 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16592 958 : if (!HeapTupleIsValid(tuple))
16593 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16594 :
16595 958 : if (operation == AT_ReplaceRelOptions)
16596 : {
16597 : /*
16598 : * If we're supposed to replace the reloptions list, we just pretend
16599 : * there were none before.
16600 : */
16601 198 : datum = (Datum) 0;
16602 : }
16603 : else
16604 : {
16605 : bool isnull;
16606 :
16607 : /* Get the old reloptions */
16608 760 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16609 : &isnull);
16610 760 : if (isnull)
16611 472 : datum = (Datum) 0;
16612 : }
16613 :
16614 : /* Generate new proposed reloptions (text array) */
16615 958 : newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16616 : operation == AT_ResetRelOptions);
16617 :
16618 : /* Validate */
16619 952 : switch (rel->rd_rel->relkind)
16620 : {
16621 530 : case RELKIND_RELATION:
16622 : case RELKIND_MATVIEW:
16623 530 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16624 530 : break;
16625 6 : case RELKIND_PARTITIONED_TABLE:
16626 6 : (void) partitioned_table_reloptions(newOptions, true);
16627 0 : break;
16628 300 : case RELKIND_VIEW:
16629 300 : (void) view_reloptions(newOptions, true);
16630 282 : break;
16631 116 : case RELKIND_INDEX:
16632 : case RELKIND_PARTITIONED_INDEX:
16633 116 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16634 94 : break;
16635 0 : case RELKIND_TOASTVALUE:
16636 : /* fall through to error -- shouldn't ever get here */
16637 : default:
16638 0 : ereport(ERROR,
16639 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16640 : errmsg("cannot set options for relation \"%s\"",
16641 : RelationGetRelationName(rel)),
16642 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16643 : break;
16644 : }
16645 :
16646 : /* Special-case validation of view options */
16647 906 : if (rel->rd_rel->relkind == RELKIND_VIEW)
16648 : {
16649 282 : Query *view_query = get_view_query(rel);
16650 282 : List *view_options = untransformRelOptions(newOptions);
16651 : ListCell *cell;
16652 282 : bool check_option = false;
16653 :
16654 384 : foreach(cell, view_options)
16655 : {
16656 102 : DefElem *defel = (DefElem *) lfirst(cell);
16657 :
16658 102 : if (strcmp(defel->defname, "check_option") == 0)
16659 24 : check_option = true;
16660 : }
16661 :
16662 : /*
16663 : * If the check option is specified, look to see if the view is
16664 : * actually auto-updatable or not.
16665 : */
16666 282 : if (check_option)
16667 : {
16668 : const char *view_updatable_error =
16669 24 : view_query_is_auto_updatable(view_query, true);
16670 :
16671 24 : if (view_updatable_error)
16672 0 : ereport(ERROR,
16673 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16674 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16675 : errhint("%s", _(view_updatable_error))));
16676 : }
16677 : }
16678 :
16679 : /*
16680 : * All we need do here is update the pg_class row; the new options will be
16681 : * propagated into relcaches during post-commit cache inval.
16682 : */
16683 906 : memset(repl_val, 0, sizeof(repl_val));
16684 906 : memset(repl_null, false, sizeof(repl_null));
16685 906 : memset(repl_repl, false, sizeof(repl_repl));
16686 :
16687 906 : if (newOptions != (Datum) 0)
16688 608 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16689 : else
16690 298 : repl_null[Anum_pg_class_reloptions - 1] = true;
16691 :
16692 906 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16693 :
16694 906 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16695 : repl_val, repl_null, repl_repl);
16696 :
16697 906 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16698 906 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16699 :
16700 906 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16701 :
16702 906 : heap_freetuple(newtuple);
16703 :
16704 906 : ReleaseSysCache(tuple);
16705 :
16706 : /* repeat the whole exercise for the toast table, if there's one */
16707 906 : if (OidIsValid(rel->rd_rel->reltoastrelid))
16708 : {
16709 : Relation toastrel;
16710 262 : Oid toastid = rel->rd_rel->reltoastrelid;
16711 :
16712 262 : toastrel = table_open(toastid, lockmode);
16713 :
16714 : /* Fetch heap tuple */
16715 262 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16716 262 : if (!HeapTupleIsValid(tuple))
16717 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
16718 :
16719 262 : if (operation == AT_ReplaceRelOptions)
16720 : {
16721 : /*
16722 : * If we're supposed to replace the reloptions list, we just
16723 : * pretend there were none before.
16724 : */
16725 0 : datum = (Datum) 0;
16726 : }
16727 : else
16728 : {
16729 : bool isnull;
16730 :
16731 : /* Get the old reloptions */
16732 262 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16733 : &isnull);
16734 262 : if (isnull)
16735 226 : datum = (Datum) 0;
16736 : }
16737 :
16738 262 : newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16739 : false, operation == AT_ResetRelOptions);
16740 :
16741 262 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16742 :
16743 262 : memset(repl_val, 0, sizeof(repl_val));
16744 262 : memset(repl_null, false, sizeof(repl_null));
16745 262 : memset(repl_repl, false, sizeof(repl_repl));
16746 :
16747 262 : if (newOptions != (Datum) 0)
16748 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16749 : else
16750 220 : repl_null[Anum_pg_class_reloptions - 1] = true;
16751 :
16752 262 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16753 :
16754 262 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16755 : repl_val, repl_null, repl_repl);
16756 :
16757 262 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16758 :
16759 262 : InvokeObjectPostAlterHookArg(RelationRelationId,
16760 : RelationGetRelid(toastrel), 0,
16761 : InvalidOid, true);
16762 :
16763 262 : heap_freetuple(newtuple);
16764 :
16765 262 : ReleaseSysCache(tuple);
16766 :
16767 262 : table_close(toastrel, NoLock);
16768 : }
16769 :
16770 906 : table_close(pgclass, RowExclusiveLock);
16771 : }
16772 :
16773 : /*
16774 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16775 : * rewriting to be done, so we just want to copy the data as fast as possible.
16776 : */
16777 : static void
16778 164 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16779 : {
16780 : Relation rel;
16781 : Oid reltoastrelid;
16782 : RelFileNumber newrelfilenumber;
16783 : RelFileLocator newrlocator;
16784 164 : List *reltoastidxids = NIL;
16785 : ListCell *lc;
16786 :
16787 : /*
16788 : * Need lock here in case we are recursing to toast table or index
16789 : */
16790 164 : rel = relation_open(tableOid, lockmode);
16791 :
16792 : /* Check first if relation can be moved to new tablespace */
16793 164 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16794 : {
16795 2 : InvokeObjectPostAlterHook(RelationRelationId,
16796 : RelationGetRelid(rel), 0);
16797 2 : relation_close(rel, NoLock);
16798 2 : return;
16799 : }
16800 :
16801 162 : reltoastrelid = rel->rd_rel->reltoastrelid;
16802 : /* Fetch the list of indexes on toast relation if necessary */
16803 162 : if (OidIsValid(reltoastrelid))
16804 : {
16805 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
16806 :
16807 20 : reltoastidxids = RelationGetIndexList(toastRel);
16808 20 : relation_close(toastRel, lockmode);
16809 : }
16810 :
16811 : /*
16812 : * Relfilenumbers are not unique in databases across tablespaces, so we
16813 : * need to allocate a new one in the new tablespace.
16814 : */
16815 162 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16816 162 : rel->rd_rel->relpersistence);
16817 :
16818 : /* Open old and new relation */
16819 162 : newrlocator = rel->rd_locator;
16820 162 : newrlocator.relNumber = newrelfilenumber;
16821 162 : newrlocator.spcOid = newTableSpace;
16822 :
16823 : /* hand off to AM to actually create new rel storage and copy the data */
16824 162 : if (rel->rd_rel->relkind == RELKIND_INDEX)
16825 : {
16826 62 : index_copy_data(rel, newrlocator);
16827 : }
16828 : else
16829 : {
16830 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16831 100 : table_relation_copy_data(rel, &newrlocator);
16832 : }
16833 :
16834 : /*
16835 : * Update the pg_class row.
16836 : *
16837 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16838 : * executed on pg_class or its indexes (the above copy wouldn't contain
16839 : * the updated pg_class entry), but that's forbidden with
16840 : * CheckRelationTableSpaceMove().
16841 : */
16842 162 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16843 :
16844 162 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16845 :
16846 162 : RelationAssumeNewRelfilelocator(rel);
16847 :
16848 162 : relation_close(rel, NoLock);
16849 :
16850 : /* Make sure the reltablespace change is visible */
16851 162 : CommandCounterIncrement();
16852 :
16853 : /* Move associated toast relation and/or indexes, too */
16854 162 : if (OidIsValid(reltoastrelid))
16855 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16856 182 : foreach(lc, reltoastidxids)
16857 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16858 :
16859 : /* Clean up */
16860 162 : list_free(reltoastidxids);
16861 : }
16862 :
16863 : /*
16864 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16865 : * storage that have an interest in preserving tablespace.
16866 : *
16867 : * Since these have no storage the tablespace can be updated with a simple
16868 : * metadata only operation to update the tablespace.
16869 : */
16870 : static void
16871 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
16872 : {
16873 : /*
16874 : * Shouldn't be called on relations having storage; these are processed in
16875 : * phase 3.
16876 : */
16877 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16878 :
16879 : /* check if relation can be moved to its new tablespace */
16880 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16881 : {
16882 0 : InvokeObjectPostAlterHook(RelationRelationId,
16883 : RelationGetRelid(rel),
16884 : 0);
16885 0 : return;
16886 : }
16887 :
16888 : /* Update can be done, so change reltablespace */
16889 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16890 :
16891 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16892 :
16893 : /* Make sure the reltablespace change is visible */
16894 30 : CommandCounterIncrement();
16895 : }
16896 :
16897 : /*
16898 : * Alter Table ALL ... SET TABLESPACE
16899 : *
16900 : * Allows a user to move all objects of some type in a given tablespace in the
16901 : * current database to another tablespace. Objects can be chosen based on the
16902 : * owner of the object also, to allow users to move only their objects.
16903 : * The user must have CREATE rights on the new tablespace, as usual. The main
16904 : * permissions handling is done by the lower-level table move function.
16905 : *
16906 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
16907 : * lock can't be acquired then we ereport(ERROR).
16908 : */
16909 : Oid
16910 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
16911 : {
16912 30 : List *relations = NIL;
16913 : ListCell *l;
16914 : ScanKeyData key[1];
16915 : Relation rel;
16916 : TableScanDesc scan;
16917 : HeapTuple tuple;
16918 : Oid orig_tablespaceoid;
16919 : Oid new_tablespaceoid;
16920 30 : List *role_oids = roleSpecsToIds(stmt->roles);
16921 :
16922 : /* Ensure we were not asked to move something we can't */
16923 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16924 12 : stmt->objtype != OBJECT_MATVIEW)
16925 0 : ereport(ERROR,
16926 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16927 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16928 :
16929 : /* Get the orig and new tablespace OIDs */
16930 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16931 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16932 :
16933 : /* Can't move shared relations in to or out of pg_global */
16934 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16935 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16936 : new_tablespaceoid == GLOBALTABLESPACE_OID)
16937 0 : ereport(ERROR,
16938 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16939 : errmsg("cannot move relations in to or out of pg_global tablespace")));
16940 :
16941 : /*
16942 : * Must have CREATE rights on the new tablespace, unless it is the
16943 : * database default tablespace (which all users implicitly have CREATE
16944 : * rights on).
16945 : */
16946 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16947 : {
16948 : AclResult aclresult;
16949 :
16950 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16951 : ACL_CREATE);
16952 0 : if (aclresult != ACLCHECK_OK)
16953 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
16954 0 : get_tablespace_name(new_tablespaceoid));
16955 : }
16956 :
16957 : /*
16958 : * Now that the checks are done, check if we should set either to
16959 : * InvalidOid because it is our database's default tablespace.
16960 : */
16961 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
16962 0 : orig_tablespaceoid = InvalidOid;
16963 :
16964 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
16965 30 : new_tablespaceoid = InvalidOid;
16966 :
16967 : /* no-op */
16968 30 : if (orig_tablespaceoid == new_tablespaceoid)
16969 0 : return new_tablespaceoid;
16970 :
16971 : /*
16972 : * Walk the list of objects in the tablespace and move them. This will
16973 : * only find objects in our database, of course.
16974 : */
16975 30 : ScanKeyInit(&key[0],
16976 : Anum_pg_class_reltablespace,
16977 : BTEqualStrategyNumber, F_OIDEQ,
16978 : ObjectIdGetDatum(orig_tablespaceoid));
16979 :
16980 30 : rel = table_open(RelationRelationId, AccessShareLock);
16981 30 : scan = table_beginscan_catalog(rel, 1, key);
16982 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16983 : {
16984 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16985 102 : Oid relOid = relForm->oid;
16986 :
16987 : /*
16988 : * Do not move objects in pg_catalog as part of this, if an admin
16989 : * really wishes to do so, they can issue the individual ALTER
16990 : * commands directly.
16991 : *
16992 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
16993 : * (TOAST will be moved with the main table).
16994 : */
16995 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
16996 204 : relForm->relisshared ||
16997 204 : isAnyTempNamespace(relForm->relnamespace) ||
16998 102 : IsToastNamespace(relForm->relnamespace))
16999 0 : continue;
17000 :
17001 : /* Only move the object type requested */
17002 102 : if ((stmt->objtype == OBJECT_TABLE &&
17003 60 : relForm->relkind != RELKIND_RELATION &&
17004 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17005 66 : (stmt->objtype == OBJECT_INDEX &&
17006 36 : relForm->relkind != RELKIND_INDEX &&
17007 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17008 60 : (stmt->objtype == OBJECT_MATVIEW &&
17009 6 : relForm->relkind != RELKIND_MATVIEW))
17010 42 : continue;
17011 :
17012 : /* Check if we are only moving objects owned by certain roles */
17013 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17014 0 : continue;
17015 :
17016 : /*
17017 : * Handle permissions-checking here since we are locking the tables
17018 : * and also to avoid doing a bunch of work only to fail part-way. Note
17019 : * that permissions will also be checked by AlterTableInternal().
17020 : *
17021 : * Caller must be considered an owner on the table to move it.
17022 : */
17023 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17024 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
17025 0 : NameStr(relForm->relname));
17026 :
17027 60 : if (stmt->nowait &&
17028 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
17029 0 : ereport(ERROR,
17030 : (errcode(ERRCODE_OBJECT_IN_USE),
17031 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
17032 : get_namespace_name(relForm->relnamespace),
17033 : NameStr(relForm->relname))));
17034 : else
17035 60 : LockRelationOid(relOid, AccessExclusiveLock);
17036 :
17037 : /* Add to our list of objects to move */
17038 60 : relations = lappend_oid(relations, relOid);
17039 : }
17040 :
17041 30 : table_endscan(scan);
17042 30 : table_close(rel, AccessShareLock);
17043 :
17044 30 : if (relations == NIL)
17045 12 : ereport(NOTICE,
17046 : (errcode(ERRCODE_NO_DATA_FOUND),
17047 : errmsg("no matching relations in tablespace \"%s\" found",
17048 : orig_tablespaceoid == InvalidOid ? "(database default)" :
17049 : get_tablespace_name(orig_tablespaceoid))));
17050 :
17051 : /* Everything is locked, loop through and move all of the relations. */
17052 90 : foreach(l, relations)
17053 : {
17054 60 : List *cmds = NIL;
17055 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
17056 :
17057 60 : cmd->subtype = AT_SetTableSpace;
17058 60 : cmd->name = stmt->new_tablespacename;
17059 :
17060 60 : cmds = lappend(cmds, cmd);
17061 :
17062 60 : EventTriggerAlterTableStart((Node *) stmt);
17063 : /* OID is set by AlterTableInternal */
17064 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
17065 60 : EventTriggerAlterTableEnd();
17066 : }
17067 :
17068 30 : return new_tablespaceoid;
17069 : }
17070 :
17071 : static void
17072 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
17073 : {
17074 : SMgrRelation dstrel;
17075 :
17076 : /*
17077 : * Since we copy the file directly without looking at the shared buffers,
17078 : * we'd better first flush out any pages of the source relation that are
17079 : * in shared buffers. We assume no new changes will be made while we are
17080 : * holding exclusive lock on the rel.
17081 : */
17082 62 : FlushRelationBuffers(rel);
17083 :
17084 : /*
17085 : * Create and copy all forks of the relation, and schedule unlinking of
17086 : * old physical files.
17087 : *
17088 : * NOTE: any conflict in relfilenumber value will be caught in
17089 : * RelationCreateStorage().
17090 : */
17091 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17092 :
17093 : /* copy main fork */
17094 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
17095 62 : rel->rd_rel->relpersistence);
17096 :
17097 : /* copy those extra forks that exist */
17098 248 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17099 186 : forkNum <= MAX_FORKNUM; forkNum++)
17100 : {
17101 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
17102 : {
17103 0 : smgrcreate(dstrel, forkNum, false);
17104 :
17105 : /*
17106 : * WAL log creation if the relation is persistent, or this is the
17107 : * init fork of an unlogged relation.
17108 : */
17109 0 : if (RelationIsPermanent(rel) ||
17110 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17111 : forkNum == INIT_FORKNUM))
17112 0 : log_smgrcreate(&newrlocator, forkNum);
17113 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17114 0 : rel->rd_rel->relpersistence);
17115 : }
17116 : }
17117 :
17118 : /* drop old relation, and close new one */
17119 62 : RelationDropStorage(rel);
17120 62 : smgrclose(dstrel);
17121 62 : }
17122 :
17123 : /*
17124 : * ALTER TABLE ENABLE/DISABLE TRIGGER
17125 : *
17126 : * We just pass this off to trigger.c.
17127 : */
17128 : static void
17129 346 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17130 : char fires_when, bool skip_system, bool recurse,
17131 : LOCKMODE lockmode)
17132 : {
17133 346 : EnableDisableTrigger(rel, trigname, InvalidOid,
17134 : fires_when, skip_system, recurse,
17135 : lockmode);
17136 :
17137 346 : InvokeObjectPostAlterHook(RelationRelationId,
17138 : RelationGetRelid(rel), 0);
17139 346 : }
17140 :
17141 : /*
17142 : * ALTER TABLE ENABLE/DISABLE RULE
17143 : *
17144 : * We just pass this off to rewriteDefine.c.
17145 : */
17146 : static void
17147 52 : ATExecEnableDisableRule(Relation rel, const char *rulename,
17148 : char fires_when, LOCKMODE lockmode)
17149 : {
17150 52 : EnableDisableRule(rel, rulename, fires_when);
17151 :
17152 52 : InvokeObjectPostAlterHook(RelationRelationId,
17153 : RelationGetRelid(rel), 0);
17154 52 : }
17155 :
17156 : /*
17157 : * ALTER TABLE INHERIT
17158 : *
17159 : * Add a parent to the child's parents. This verifies that all the columns and
17160 : * check constraints of the parent appear in the child and that they have the
17161 : * same data types and expressions.
17162 : */
17163 : static void
17164 436 : ATPrepAddInherit(Relation child_rel)
17165 : {
17166 436 : if (child_rel->rd_rel->reloftype)
17167 6 : ereport(ERROR,
17168 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17169 : errmsg("cannot change inheritance of typed table")));
17170 :
17171 430 : if (child_rel->rd_rel->relispartition)
17172 6 : ereport(ERROR,
17173 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17174 : errmsg("cannot change inheritance of a partition")));
17175 :
17176 424 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17177 6 : ereport(ERROR,
17178 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17179 : errmsg("cannot change inheritance of partitioned table")));
17180 418 : }
17181 :
17182 : /*
17183 : * Return the address of the new parent relation.
17184 : */
17185 : static ObjectAddress
17186 418 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17187 : {
17188 : Relation parent_rel;
17189 : List *children;
17190 : ObjectAddress address;
17191 : const char *trigger_name;
17192 :
17193 : /*
17194 : * A self-exclusive lock is needed here. See the similar case in
17195 : * MergeAttributes() for a full explanation.
17196 : */
17197 418 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17198 :
17199 : /*
17200 : * Must be owner of both parent and child -- child was checked by
17201 : * ATSimplePermissions call in ATPrepCmd
17202 : */
17203 418 : ATSimplePermissions(AT_AddInherit, parent_rel,
17204 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
17205 :
17206 : /* Permanent rels cannot inherit from temporary ones */
17207 418 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17208 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17209 0 : ereport(ERROR,
17210 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17211 : errmsg("cannot inherit from temporary relation \"%s\"",
17212 : RelationGetRelationName(parent_rel))));
17213 :
17214 : /* If parent rel is temp, it must belong to this session */
17215 418 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17216 6 : !parent_rel->rd_islocaltemp)
17217 0 : ereport(ERROR,
17218 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17219 : errmsg("cannot inherit from temporary relation of another session")));
17220 :
17221 : /* Ditto for the child */
17222 418 : if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17223 6 : !child_rel->rd_islocaltemp)
17224 0 : ereport(ERROR,
17225 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17226 : errmsg("cannot inherit to temporary relation of another session")));
17227 :
17228 : /* Prevent partitioned tables from becoming inheritance parents */
17229 418 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17230 6 : ereport(ERROR,
17231 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17232 : errmsg("cannot inherit from partitioned table \"%s\"",
17233 : parent->relname)));
17234 :
17235 : /* Likewise for partitions */
17236 412 : if (parent_rel->rd_rel->relispartition)
17237 6 : ereport(ERROR,
17238 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17239 : errmsg("cannot inherit from a partition")));
17240 :
17241 : /*
17242 : * Prevent circularity by seeing if proposed parent inherits from child.
17243 : * (In particular, this disallows making a rel inherit from itself.)
17244 : *
17245 : * This is not completely bulletproof because of race conditions: in
17246 : * multi-level inheritance trees, someone else could concurrently be
17247 : * making another inheritance link that closes the loop but does not join
17248 : * either of the rels we have locked. Preventing that seems to require
17249 : * exclusive locks on the entire inheritance tree, which is a cure worse
17250 : * than the disease. find_all_inheritors() will cope with circularity
17251 : * anyway, so don't sweat it too much.
17252 : *
17253 : * We use weakest lock we can on child's children, namely AccessShareLock.
17254 : */
17255 406 : children = find_all_inheritors(RelationGetRelid(child_rel),
17256 : AccessShareLock, NULL);
17257 :
17258 406 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
17259 12 : ereport(ERROR,
17260 : (errcode(ERRCODE_DUPLICATE_TABLE),
17261 : errmsg("circular inheritance not allowed"),
17262 : errdetail("\"%s\" is already a child of \"%s\".",
17263 : parent->relname,
17264 : RelationGetRelationName(child_rel))));
17265 :
17266 : /*
17267 : * If child_rel has row-level triggers with transition tables, we
17268 : * currently don't allow it to become an inheritance child. See also
17269 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
17270 : */
17271 394 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17272 394 : if (trigger_name != NULL)
17273 6 : ereport(ERROR,
17274 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17275 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17276 : trigger_name, RelationGetRelationName(child_rel)),
17277 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17278 :
17279 : /* OK to create inheritance */
17280 388 : CreateInheritance(child_rel, parent_rel, false);
17281 :
17282 298 : ObjectAddressSet(address, RelationRelationId,
17283 : RelationGetRelid(parent_rel));
17284 :
17285 : /* keep our lock on the parent relation until commit */
17286 298 : table_close(parent_rel, NoLock);
17287 :
17288 298 : return address;
17289 : }
17290 :
17291 : /*
17292 : * CreateInheritance
17293 : * Catalog manipulation portion of creating inheritance between a child
17294 : * table and a parent table.
17295 : *
17296 : * Common to ATExecAddInherit() and ATExecAttachPartition().
17297 : */
17298 : static void
17299 3016 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17300 : {
17301 : Relation catalogRelation;
17302 : SysScanDesc scan;
17303 : ScanKeyData key;
17304 : HeapTuple inheritsTuple;
17305 : int32 inhseqno;
17306 :
17307 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17308 3016 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17309 :
17310 : /*
17311 : * Check for duplicates in the list of parents, and determine the highest
17312 : * inhseqno already present; we'll use the next one for the new parent.
17313 : * Also, if proposed child is a partition, it cannot already be
17314 : * inheriting.
17315 : *
17316 : * Note: we do not reject the case where the child already inherits from
17317 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17318 : */
17319 3016 : ScanKeyInit(&key,
17320 : Anum_pg_inherits_inhrelid,
17321 : BTEqualStrategyNumber, F_OIDEQ,
17322 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17323 3016 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17324 : true, NULL, 1, &key);
17325 :
17326 : /* inhseqno sequences start at 1 */
17327 3016 : inhseqno = 0;
17328 3082 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17329 : {
17330 72 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17331 :
17332 72 : if (inh->inhparent == RelationGetRelid(parent_rel))
17333 6 : ereport(ERROR,
17334 : (errcode(ERRCODE_DUPLICATE_TABLE),
17335 : errmsg("relation \"%s\" would be inherited from more than once",
17336 : RelationGetRelationName(parent_rel))));
17337 :
17338 66 : if (inh->inhseqno > inhseqno)
17339 66 : inhseqno = inh->inhseqno;
17340 : }
17341 3010 : systable_endscan(scan);
17342 :
17343 : /* Match up the columns and bump attinhcount as needed */
17344 3010 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17345 :
17346 : /* Match up the constraints and bump coninhcount as needed */
17347 2878 : MergeConstraintsIntoExisting(child_rel, parent_rel);
17348 :
17349 : /*
17350 : * OK, it looks valid. Make the catalog entries that show inheritance.
17351 : */
17352 2818 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
17353 : RelationGetRelid(parent_rel),
17354 : inhseqno + 1,
17355 : catalogRelation,
17356 2818 : parent_rel->rd_rel->relkind ==
17357 : RELKIND_PARTITIONED_TABLE);
17358 :
17359 : /* Now we're done with pg_inherits */
17360 2818 : table_close(catalogRelation, RowExclusiveLock);
17361 2818 : }
17362 :
17363 : /*
17364 : * Obtain the source-text form of the constraint expression for a check
17365 : * constraint, given its pg_constraint tuple
17366 : */
17367 : static char *
17368 188 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
17369 : {
17370 : Form_pg_constraint con;
17371 : bool isnull;
17372 : Datum attr;
17373 : Datum expr;
17374 :
17375 188 : con = (Form_pg_constraint) GETSTRUCT(contup);
17376 188 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17377 188 : if (isnull)
17378 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
17379 :
17380 188 : expr = DirectFunctionCall2(pg_get_expr, attr,
17381 : ObjectIdGetDatum(con->conrelid));
17382 188 : return TextDatumGetCString(expr);
17383 : }
17384 :
17385 : /*
17386 : * Determine whether two check constraints are functionally equivalent
17387 : *
17388 : * The test we apply is to see whether they reverse-compile to the same
17389 : * source string. This insulates us from issues like whether attributes
17390 : * have the same physical column numbers in parent and child relations.
17391 : *
17392 : * Note that we ignore enforceability as there are cases where constraints
17393 : * with differing enforceability are allowed.
17394 : */
17395 : static bool
17396 94 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
17397 : {
17398 94 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
17399 94 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
17400 :
17401 94 : if (acon->condeferrable != bcon->condeferrable ||
17402 94 : acon->condeferred != bcon->condeferred ||
17403 94 : strcmp(decompile_conbin(a, tupleDesc),
17404 94 : decompile_conbin(b, tupleDesc)) != 0)
17405 6 : return false;
17406 : else
17407 88 : return true;
17408 : }
17409 :
17410 : /*
17411 : * Check columns in child table match up with columns in parent, and increment
17412 : * their attinhcount.
17413 : *
17414 : * Called by CreateInheritance
17415 : *
17416 : * Currently all parent columns must be found in child. Missing columns are an
17417 : * error. One day we might consider creating new columns like CREATE TABLE
17418 : * does. However, that is widely unpopular --- in the common use case of
17419 : * partitioned tables it's a foot-gun.
17420 : *
17421 : * The data type must match exactly. If the parent column is NOT NULL then
17422 : * the child must be as well. Defaults are not compared, however.
17423 : */
17424 : static void
17425 3010 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17426 : {
17427 : Relation attrrel;
17428 : TupleDesc parent_desc;
17429 :
17430 3010 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17431 3010 : parent_desc = RelationGetDescr(parent_rel);
17432 :
17433 9684 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17434 : {
17435 6806 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17436 6806 : char *parent_attname = NameStr(parent_att->attname);
17437 : HeapTuple tuple;
17438 :
17439 : /* Ignore dropped columns in the parent. */
17440 6806 : if (parent_att->attisdropped)
17441 296 : continue;
17442 :
17443 : /* Find same column in child (matching on column name). */
17444 6510 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17445 6510 : if (HeapTupleIsValid(tuple))
17446 : {
17447 6498 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17448 :
17449 6498 : if (parent_att->atttypid != child_att->atttypid ||
17450 6492 : parent_att->atttypmod != child_att->atttypmod)
17451 12 : ereport(ERROR,
17452 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17453 : errmsg("child table \"%s\" has different type for column \"%s\"",
17454 : RelationGetRelationName(child_rel), parent_attname)));
17455 :
17456 6486 : if (parent_att->attcollation != child_att->attcollation)
17457 6 : ereport(ERROR,
17458 : (errcode(ERRCODE_COLLATION_MISMATCH),
17459 : errmsg("child table \"%s\" has different collation for column \"%s\"",
17460 : RelationGetRelationName(child_rel), parent_attname)));
17461 :
17462 : /*
17463 : * If the parent has a not-null constraint that's not NO INHERIT,
17464 : * make sure the child has one too.
17465 : *
17466 : * Other constraints are checked elsewhere.
17467 : */
17468 6480 : if (parent_att->attnotnull && !child_att->attnotnull)
17469 : {
17470 : HeapTuple contup;
17471 :
17472 48 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17473 48 : parent_att->attnum);
17474 48 : if (HeapTupleIsValid(contup) &&
17475 48 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17476 30 : ereport(ERROR,
17477 : errcode(ERRCODE_DATATYPE_MISMATCH),
17478 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17479 : parent_attname, RelationGetRelationName(child_rel)));
17480 : }
17481 :
17482 : /*
17483 : * Child column must be generated if and only if parent column is.
17484 : */
17485 6450 : if (parent_att->attgenerated && !child_att->attgenerated)
17486 36 : ereport(ERROR,
17487 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17488 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17489 6414 : if (child_att->attgenerated && !parent_att->attgenerated)
17490 24 : ereport(ERROR,
17491 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17492 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17493 :
17494 6390 : if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17495 12 : ereport(ERROR,
17496 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17497 : errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17498 : errdetail("Parent column is %s, child column is %s.",
17499 : parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17500 : child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17501 :
17502 : /*
17503 : * Regular inheritance children are independent enough not to
17504 : * inherit identity columns. But partitions are integral part of
17505 : * a partitioned table and inherit identity column.
17506 : */
17507 6378 : if (ispartition)
17508 5692 : child_att->attidentity = parent_att->attidentity;
17509 :
17510 : /*
17511 : * OK, bump the child column's inheritance count. (If we fail
17512 : * later on, this change will just roll back.)
17513 : */
17514 6378 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
17515 : &child_att->attinhcount))
17516 0 : ereport(ERROR,
17517 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17518 : errmsg("too many inheritance parents"));
17519 :
17520 : /*
17521 : * In case of partitions, we must enforce that value of attislocal
17522 : * is same in all partitions. (Note: there are only inherited
17523 : * attributes in partitions)
17524 : */
17525 6378 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17526 : {
17527 : Assert(child_att->attinhcount == 1);
17528 5692 : child_att->attislocal = false;
17529 : }
17530 :
17531 6378 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17532 6378 : heap_freetuple(tuple);
17533 : }
17534 : else
17535 : {
17536 12 : ereport(ERROR,
17537 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17538 : errmsg("child table is missing column \"%s\"", parent_attname)));
17539 : }
17540 : }
17541 :
17542 2878 : table_close(attrrel, RowExclusiveLock);
17543 2878 : }
17544 :
17545 : /*
17546 : * Check constraints in child table match up with constraints in parent,
17547 : * and increment their coninhcount.
17548 : *
17549 : * Constraints that are marked ONLY in the parent are ignored.
17550 : *
17551 : * Called by CreateInheritance
17552 : *
17553 : * Currently all constraints in parent must be present in the child. One day we
17554 : * may consider adding new constraints like CREATE TABLE does.
17555 : *
17556 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
17557 : * constraints. As long as tables have more like 10 constraints it shouldn't be
17558 : * a problem though. Even 100 constraints ought not be the end of the world.
17559 : *
17560 : * XXX See MergeWithExistingConstraint too if you change this code.
17561 : */
17562 : static void
17563 2878 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
17564 : {
17565 : Relation constraintrel;
17566 : SysScanDesc parent_scan;
17567 : ScanKeyData parent_key;
17568 : HeapTuple parent_tuple;
17569 2878 : Oid parent_relid = RelationGetRelid(parent_rel);
17570 : AttrMap *attmap;
17571 :
17572 2878 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17573 :
17574 : /* Outer loop scans through the parent's constraint definitions */
17575 2878 : ScanKeyInit(&parent_key,
17576 : Anum_pg_constraint_conrelid,
17577 : BTEqualStrategyNumber, F_OIDEQ,
17578 : ObjectIdGetDatum(parent_relid));
17579 2878 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17580 : true, NULL, 1, &parent_key);
17581 :
17582 2878 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17583 : RelationGetDescr(child_rel),
17584 : true);
17585 :
17586 4838 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17587 : {
17588 2020 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17589 : SysScanDesc child_scan;
17590 : ScanKeyData child_key;
17591 : HeapTuple child_tuple;
17592 : AttrNumber parent_attno;
17593 2020 : bool found = false;
17594 :
17595 2020 : if (parent_con->contype != CONSTRAINT_CHECK &&
17596 1882 : parent_con->contype != CONSTRAINT_NOTNULL)
17597 898 : continue;
17598 :
17599 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17600 1166 : if (parent_con->connoinherit)
17601 44 : continue;
17602 :
17603 1122 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17604 1004 : parent_attno = extractNotNullColumn(parent_tuple);
17605 : else
17606 118 : parent_attno = InvalidAttrNumber;
17607 :
17608 : /* Search for a child constraint matching this one */
17609 1122 : ScanKeyInit(&child_key,
17610 : Anum_pg_constraint_conrelid,
17611 : BTEqualStrategyNumber, F_OIDEQ,
17612 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17613 1122 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17614 : true, NULL, 1, &child_key);
17615 :
17616 1720 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17617 : {
17618 1696 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17619 : HeapTuple child_copy;
17620 :
17621 1696 : if (child_con->contype != parent_con->contype)
17622 284 : continue;
17623 :
17624 : /*
17625 : * CHECK constraint are matched by constraint name, NOT NULL ones
17626 : * by attribute number.
17627 : */
17628 1412 : if (child_con->contype == CONSTRAINT_CHECK)
17629 : {
17630 124 : if (strcmp(NameStr(parent_con->conname),
17631 124 : NameStr(child_con->conname)) != 0)
17632 30 : continue;
17633 : }
17634 1288 : else if (child_con->contype == CONSTRAINT_NOTNULL)
17635 : {
17636 : Form_pg_attribute parent_attr;
17637 : Form_pg_attribute child_attr;
17638 : AttrNumber child_attno;
17639 :
17640 1288 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17641 1288 : child_attno = extractNotNullColumn(child_tuple);
17642 1288 : if (parent_attno != attmap->attnums[child_attno - 1])
17643 284 : continue;
17644 :
17645 1004 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17646 : /* there shouldn't be constraints on dropped columns */
17647 1004 : if (parent_attr->attisdropped || child_attr->attisdropped)
17648 0 : elog(ERROR, "found not-null constraint on dropped columns");
17649 : }
17650 :
17651 1098 : if (child_con->contype == CONSTRAINT_CHECK &&
17652 94 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17653 6 : ereport(ERROR,
17654 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17655 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17656 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17657 :
17658 : /*
17659 : * If the child constraint is "no inherit" then cannot merge
17660 : */
17661 1092 : if (child_con->connoinherit)
17662 12 : ereport(ERROR,
17663 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17664 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17665 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17666 :
17667 : /*
17668 : * If the child constraint is "not valid" then cannot merge with a
17669 : * valid parent constraint
17670 : */
17671 1080 : if (parent_con->convalidated && child_con->conenforced &&
17672 1026 : !child_con->convalidated)
17673 12 : ereport(ERROR,
17674 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17675 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17676 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17677 :
17678 : /*
17679 : * A NOT ENFORCED child constraint cannot be merged with an
17680 : * ENFORCED parent constraint. However, the reverse is allowed,
17681 : * where the child constraint is ENFORCED.
17682 : */
17683 1068 : if (parent_con->conenforced && !child_con->conenforced)
17684 6 : ereport(ERROR,
17685 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17686 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17687 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17688 :
17689 : /*
17690 : * OK, bump the child constraint's inheritance count. (If we fail
17691 : * later on, this change will just roll back.)
17692 : */
17693 1062 : child_copy = heap_copytuple(child_tuple);
17694 1062 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17695 :
17696 1062 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
17697 : &child_con->coninhcount))
17698 0 : ereport(ERROR,
17699 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17700 : errmsg("too many inheritance parents"));
17701 :
17702 : /*
17703 : * In case of partitions, an inherited constraint must be
17704 : * inherited only once since it cannot have multiple parents and
17705 : * it is never considered local.
17706 : */
17707 1062 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17708 : {
17709 : Assert(child_con->coninhcount == 1);
17710 916 : child_con->conislocal = false;
17711 : }
17712 :
17713 1062 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17714 1062 : heap_freetuple(child_copy);
17715 :
17716 1062 : found = true;
17717 1062 : break;
17718 : }
17719 :
17720 1086 : systable_endscan(child_scan);
17721 :
17722 1086 : if (!found)
17723 : {
17724 24 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17725 0 : ereport(ERROR,
17726 : errcode(ERRCODE_DATATYPE_MISMATCH),
17727 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17728 : get_attname(parent_relid,
17729 : extractNotNullColumn(parent_tuple),
17730 : false),
17731 : RelationGetRelationName(child_rel)));
17732 :
17733 24 : ereport(ERROR,
17734 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17735 : errmsg("child table is missing constraint \"%s\"",
17736 : NameStr(parent_con->conname))));
17737 : }
17738 : }
17739 :
17740 2818 : systable_endscan(parent_scan);
17741 2818 : table_close(constraintrel, RowExclusiveLock);
17742 2818 : }
17743 :
17744 : /*
17745 : * ALTER TABLE NO INHERIT
17746 : *
17747 : * Return value is the address of the relation that is no longer parent.
17748 : */
17749 : static ObjectAddress
17750 86 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
17751 : {
17752 : ObjectAddress address;
17753 : Relation parent_rel;
17754 :
17755 86 : if (rel->rd_rel->relispartition)
17756 0 : ereport(ERROR,
17757 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17758 : errmsg("cannot change inheritance of a partition")));
17759 :
17760 : /*
17761 : * AccessShareLock on the parent is probably enough, seeing that DROP
17762 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
17763 : * be inspecting the parent's schema.
17764 : */
17765 86 : parent_rel = table_openrv(parent, AccessShareLock);
17766 :
17767 : /*
17768 : * We don't bother to check ownership of the parent table --- ownership of
17769 : * the child is presumed enough rights.
17770 : */
17771 :
17772 : /* Off to RemoveInheritance() where most of the work happens */
17773 86 : RemoveInheritance(rel, parent_rel, false);
17774 :
17775 80 : ObjectAddressSet(address, RelationRelationId,
17776 : RelationGetRelid(parent_rel));
17777 :
17778 : /* keep our lock on the parent relation until commit */
17779 80 : table_close(parent_rel, NoLock);
17780 :
17781 80 : return address;
17782 : }
17783 :
17784 : /*
17785 : * MarkInheritDetached
17786 : *
17787 : * Set inhdetachpending for a partition, for ATExecDetachPartition
17788 : * in concurrent mode. While at it, verify that no other partition is
17789 : * already pending detach.
17790 : */
17791 : static void
17792 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
17793 : {
17794 : Relation catalogRelation;
17795 : SysScanDesc scan;
17796 : ScanKeyData key;
17797 : HeapTuple inheritsTuple;
17798 146 : bool found = false;
17799 :
17800 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17801 :
17802 : /*
17803 : * Find pg_inherits entries by inhparent. (We need to scan them all in
17804 : * order to verify that no other partition is pending detach.)
17805 : */
17806 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17807 146 : ScanKeyInit(&key,
17808 : Anum_pg_inherits_inhparent,
17809 : BTEqualStrategyNumber, F_OIDEQ,
17810 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17811 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17812 : true, NULL, 1, &key);
17813 :
17814 430 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17815 : {
17816 : Form_pg_inherits inhForm;
17817 :
17818 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17819 286 : if (inhForm->inhdetachpending)
17820 2 : ereport(ERROR,
17821 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17822 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17823 : get_rel_name(inhForm->inhrelid),
17824 : get_namespace_name(parent_rel->rd_rel->relnamespace),
17825 : RelationGetRelationName(parent_rel)),
17826 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17827 :
17828 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
17829 : {
17830 : HeapTuple newtup;
17831 :
17832 144 : newtup = heap_copytuple(inheritsTuple);
17833 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17834 :
17835 144 : CatalogTupleUpdate(catalogRelation,
17836 : &inheritsTuple->t_self,
17837 : newtup);
17838 144 : found = true;
17839 144 : heap_freetuple(newtup);
17840 : /* keep looking, to ensure we catch others pending detach */
17841 : }
17842 : }
17843 :
17844 : /* Done */
17845 144 : systable_endscan(scan);
17846 144 : table_close(catalogRelation, RowExclusiveLock);
17847 :
17848 144 : if (!found)
17849 0 : ereport(ERROR,
17850 : (errcode(ERRCODE_UNDEFINED_TABLE),
17851 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17852 : RelationGetRelationName(child_rel),
17853 : RelationGetRelationName(parent_rel))));
17854 144 : }
17855 :
17856 : /*
17857 : * RemoveInheritance
17858 : *
17859 : * Drop a parent from the child's parents. This just adjusts the attinhcount
17860 : * and attislocal of the columns and removes the pg_inherit and pg_depend
17861 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17862 : *
17863 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17864 : * up attislocal stays true, which means if a child is ever removed from a
17865 : * parent then its columns will never be automatically dropped which may
17866 : * surprise. But at least we'll never surprise by dropping columns someone
17867 : * isn't expecting to be dropped which would actually mean data loss.
17868 : *
17869 : * coninhcount and conislocal for inherited constraints are adjusted in
17870 : * exactly the same way.
17871 : *
17872 : * Common to ATExecDropInherit() and ATExecDetachPartition().
17873 : */
17874 : static void
17875 578 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17876 : {
17877 : Relation catalogRelation;
17878 : SysScanDesc scan;
17879 : ScanKeyData key[3];
17880 : HeapTuple attributeTuple,
17881 : constraintTuple;
17882 : AttrMap *attmap;
17883 : List *connames;
17884 : List *nncolumns;
17885 : bool found;
17886 : bool is_partitioning;
17887 :
17888 578 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17889 :
17890 578 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17891 : RelationGetRelid(parent_rel),
17892 : expect_detached,
17893 578 : RelationGetRelationName(child_rel));
17894 578 : if (!found)
17895 : {
17896 24 : if (is_partitioning)
17897 18 : ereport(ERROR,
17898 : (errcode(ERRCODE_UNDEFINED_TABLE),
17899 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17900 : RelationGetRelationName(child_rel),
17901 : RelationGetRelationName(parent_rel))));
17902 : else
17903 6 : ereport(ERROR,
17904 : (errcode(ERRCODE_UNDEFINED_TABLE),
17905 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17906 : RelationGetRelationName(parent_rel),
17907 : RelationGetRelationName(child_rel))));
17908 : }
17909 :
17910 : /*
17911 : * Search through child columns looking for ones matching parent rel
17912 : */
17913 554 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17914 554 : ScanKeyInit(&key[0],
17915 : Anum_pg_attribute_attrelid,
17916 : BTEqualStrategyNumber, F_OIDEQ,
17917 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17918 554 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17919 : true, NULL, 1, key);
17920 4946 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17921 : {
17922 4392 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17923 :
17924 : /* Ignore if dropped or not inherited */
17925 4392 : if (att->attisdropped)
17926 6 : continue;
17927 4386 : if (att->attinhcount <= 0)
17928 3354 : continue;
17929 :
17930 1032 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
17931 1032 : NameStr(att->attname)))
17932 : {
17933 : /* Decrement inhcount and possibly set islocal to true */
17934 978 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
17935 978 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17936 :
17937 978 : copy_att->attinhcount--;
17938 978 : if (copy_att->attinhcount == 0)
17939 948 : copy_att->attislocal = true;
17940 :
17941 978 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17942 978 : heap_freetuple(copyTuple);
17943 : }
17944 : }
17945 554 : systable_endscan(scan);
17946 554 : table_close(catalogRelation, RowExclusiveLock);
17947 :
17948 : /*
17949 : * Likewise, find inherited check and not-null constraints and disinherit
17950 : * them. To do this, we first need a list of the names of the parent's
17951 : * check constraints. (We cheat a bit by only checking for name matches,
17952 : * assuming that the expressions will match.)
17953 : *
17954 : * For NOT NULL columns, we store column numbers to match, mapping them in
17955 : * to the child rel's attribute numbers.
17956 : */
17957 554 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17958 : RelationGetDescr(parent_rel),
17959 : false);
17960 :
17961 554 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17962 554 : ScanKeyInit(&key[0],
17963 : Anum_pg_constraint_conrelid,
17964 : BTEqualStrategyNumber, F_OIDEQ,
17965 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17966 554 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17967 : true, NULL, 1, key);
17968 :
17969 554 : connames = NIL;
17970 554 : nncolumns = NIL;
17971 :
17972 1082 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17973 : {
17974 528 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17975 :
17976 528 : if (con->connoinherit)
17977 98 : continue;
17978 :
17979 430 : if (con->contype == CONSTRAINT_CHECK)
17980 12 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
17981 430 : if (con->contype == CONSTRAINT_NOTNULL)
17982 : {
17983 196 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
17984 :
17985 196 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
17986 : }
17987 : }
17988 :
17989 554 : systable_endscan(scan);
17990 :
17991 : /* Now scan the child's constraints to find matches */
17992 554 : ScanKeyInit(&key[0],
17993 : Anum_pg_constraint_conrelid,
17994 : BTEqualStrategyNumber, F_OIDEQ,
17995 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17996 554 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17997 : true, NULL, 1, key);
17998 :
17999 1272 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18000 : {
18001 718 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18002 718 : bool match = false;
18003 :
18004 : /*
18005 : * Match CHECK constraints by name, not-null constraints by column
18006 : * number, and ignore all others.
18007 : */
18008 718 : if (con->contype == CONSTRAINT_CHECK)
18009 : {
18010 350 : foreach_ptr(char, chkname, connames)
18011 : {
18012 18 : if (con->contype == CONSTRAINT_CHECK &&
18013 18 : strcmp(NameStr(con->conname), chkname) == 0)
18014 : {
18015 12 : match = true;
18016 12 : connames = foreach_delete_current(connames, chkname);
18017 12 : break;
18018 : }
18019 : }
18020 : }
18021 546 : else if (con->contype == CONSTRAINT_NOTNULL)
18022 : {
18023 256 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18024 :
18025 518 : foreach_int(prevattno, nncolumns)
18026 : {
18027 202 : if (prevattno == child_attno)
18028 : {
18029 196 : match = true;
18030 196 : nncolumns = foreach_delete_current(nncolumns, prevattno);
18031 196 : break;
18032 : }
18033 : }
18034 : }
18035 : else
18036 290 : continue;
18037 :
18038 428 : if (match)
18039 : {
18040 : /* Decrement inhcount and possibly set islocal to true */
18041 208 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
18042 208 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18043 :
18044 208 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
18045 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18046 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
18047 :
18048 208 : copy_con->coninhcount--;
18049 208 : if (copy_con->coninhcount == 0)
18050 190 : copy_con->conislocal = true;
18051 :
18052 208 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18053 208 : heap_freetuple(copyTuple);
18054 : }
18055 : }
18056 :
18057 : /* We should have matched all constraints */
18058 554 : if (connames != NIL || nncolumns != NIL)
18059 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18060 : list_length(connames) + list_length(nncolumns),
18061 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18062 :
18063 554 : systable_endscan(scan);
18064 554 : table_close(catalogRelation, RowExclusiveLock);
18065 :
18066 554 : drop_parent_dependency(RelationGetRelid(child_rel),
18067 : RelationRelationId,
18068 : RelationGetRelid(parent_rel),
18069 : child_dependency_type(is_partitioning));
18070 :
18071 : /*
18072 : * Post alter hook of this inherits. Since object_access_hook doesn't take
18073 : * multiple object identifiers, we relay oid of parent relation using
18074 : * auxiliary_id argument.
18075 : */
18076 554 : InvokeObjectPostAlterHookArg(InheritsRelationId,
18077 : RelationGetRelid(child_rel), 0,
18078 : RelationGetRelid(parent_rel), false);
18079 554 : }
18080 :
18081 : /*
18082 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18083 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18084 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18085 : * be TypeRelationId). There's no convenient way to do this, so go trawling
18086 : * through pg_depend.
18087 : */
18088 : static void
18089 566 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18090 : DependencyType deptype)
18091 : {
18092 : Relation catalogRelation;
18093 : SysScanDesc scan;
18094 : ScanKeyData key[3];
18095 : HeapTuple depTuple;
18096 :
18097 566 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18098 :
18099 566 : ScanKeyInit(&key[0],
18100 : Anum_pg_depend_classid,
18101 : BTEqualStrategyNumber, F_OIDEQ,
18102 : ObjectIdGetDatum(RelationRelationId));
18103 566 : ScanKeyInit(&key[1],
18104 : Anum_pg_depend_objid,
18105 : BTEqualStrategyNumber, F_OIDEQ,
18106 : ObjectIdGetDatum(relid));
18107 566 : ScanKeyInit(&key[2],
18108 : Anum_pg_depend_objsubid,
18109 : BTEqualStrategyNumber, F_INT4EQ,
18110 : Int32GetDatum(0));
18111 :
18112 566 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18113 : NULL, 3, key);
18114 :
18115 1758 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18116 : {
18117 1192 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18118 :
18119 1192 : if (dep->refclassid == refclassid &&
18120 608 : dep->refobjid == refobjid &&
18121 566 : dep->refobjsubid == 0 &&
18122 566 : dep->deptype == deptype)
18123 566 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18124 : }
18125 :
18126 566 : systable_endscan(scan);
18127 566 : table_close(catalogRelation, RowExclusiveLock);
18128 566 : }
18129 :
18130 : /*
18131 : * ALTER TABLE OF
18132 : *
18133 : * Attach a table to a composite type, as though it had been created with CREATE
18134 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18135 : * subject table must not have inheritance parents. These restrictions ensure
18136 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18137 : *
18138 : * The address of the type is returned.
18139 : */
18140 : static ObjectAddress
18141 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18142 : {
18143 66 : Oid relid = RelationGetRelid(rel);
18144 : Type typetuple;
18145 : Form_pg_type typeform;
18146 : Oid typeid;
18147 : Relation inheritsRelation,
18148 : relationRelation;
18149 : SysScanDesc scan;
18150 : ScanKeyData key;
18151 : AttrNumber table_attno,
18152 : type_attno;
18153 : TupleDesc typeTupleDesc,
18154 : tableTupleDesc;
18155 : ObjectAddress tableobj,
18156 : typeobj;
18157 : HeapTuple classtuple;
18158 :
18159 : /* Validate the type. */
18160 66 : typetuple = typenameType(NULL, ofTypename, NULL);
18161 66 : check_of_type(typetuple);
18162 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
18163 66 : typeid = typeform->oid;
18164 :
18165 : /* Fail if the table has any inheritance parents. */
18166 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18167 66 : ScanKeyInit(&key,
18168 : Anum_pg_inherits_inhrelid,
18169 : BTEqualStrategyNumber, F_OIDEQ,
18170 : ObjectIdGetDatum(relid));
18171 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18172 : true, NULL, 1, &key);
18173 66 : if (HeapTupleIsValid(systable_getnext(scan)))
18174 6 : ereport(ERROR,
18175 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18176 : errmsg("typed tables cannot inherit")));
18177 60 : systable_endscan(scan);
18178 60 : table_close(inheritsRelation, AccessShareLock);
18179 :
18180 : /*
18181 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
18182 : * require that the order also match. However, attnotnull need not match.
18183 : */
18184 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18185 60 : tableTupleDesc = RelationGetDescr(rel);
18186 60 : table_attno = 1;
18187 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18188 : {
18189 : Form_pg_attribute type_attr,
18190 : table_attr;
18191 : const char *type_attname,
18192 : *table_attname;
18193 :
18194 : /* Get the next non-dropped type attribute. */
18195 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18196 154 : if (type_attr->attisdropped)
18197 44 : continue;
18198 110 : type_attname = NameStr(type_attr->attname);
18199 :
18200 : /* Get the next non-dropped table attribute. */
18201 : do
18202 : {
18203 122 : if (table_attno > tableTupleDesc->natts)
18204 6 : ereport(ERROR,
18205 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18206 : errmsg("table is missing column \"%s\"",
18207 : type_attname)));
18208 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18209 116 : table_attno++;
18210 116 : } while (table_attr->attisdropped);
18211 104 : table_attname = NameStr(table_attr->attname);
18212 :
18213 : /* Compare name. */
18214 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18215 6 : ereport(ERROR,
18216 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18217 : errmsg("table has column \"%s\" where type requires \"%s\"",
18218 : table_attname, type_attname)));
18219 :
18220 : /* Compare type. */
18221 98 : if (table_attr->atttypid != type_attr->atttypid ||
18222 92 : table_attr->atttypmod != type_attr->atttypmod ||
18223 86 : table_attr->attcollation != type_attr->attcollation)
18224 12 : ereport(ERROR,
18225 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18226 : errmsg("table \"%s\" has different type for column \"%s\"",
18227 : RelationGetRelationName(rel), type_attname)));
18228 : }
18229 36 : ReleaseTupleDesc(typeTupleDesc);
18230 :
18231 : /* Any remaining columns at the end of the table had better be dropped. */
18232 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
18233 : {
18234 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18235 : table_attno - 1);
18236 :
18237 6 : if (!table_attr->attisdropped)
18238 6 : ereport(ERROR,
18239 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18240 : errmsg("table has extra column \"%s\"",
18241 : NameStr(table_attr->attname))));
18242 : }
18243 :
18244 : /* If the table was already typed, drop the existing dependency. */
18245 30 : if (rel->rd_rel->reloftype)
18246 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18247 : DEPENDENCY_NORMAL);
18248 :
18249 : /* Record a dependency on the new type. */
18250 30 : tableobj.classId = RelationRelationId;
18251 30 : tableobj.objectId = relid;
18252 30 : tableobj.objectSubId = 0;
18253 30 : typeobj.classId = TypeRelationId;
18254 30 : typeobj.objectId = typeid;
18255 30 : typeobj.objectSubId = 0;
18256 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18257 :
18258 : /* Update pg_class.reloftype */
18259 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18260 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18261 30 : if (!HeapTupleIsValid(classtuple))
18262 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18263 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18264 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18265 :
18266 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18267 :
18268 30 : heap_freetuple(classtuple);
18269 30 : table_close(relationRelation, RowExclusiveLock);
18270 :
18271 30 : ReleaseSysCache(typetuple);
18272 :
18273 30 : return typeobj;
18274 : }
18275 :
18276 : /*
18277 : * ALTER TABLE NOT OF
18278 : *
18279 : * Detach a typed table from its originating type. Just clear reloftype and
18280 : * remove the dependency.
18281 : */
18282 : static void
18283 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
18284 : {
18285 6 : Oid relid = RelationGetRelid(rel);
18286 : Relation relationRelation;
18287 : HeapTuple tuple;
18288 :
18289 6 : if (!OidIsValid(rel->rd_rel->reloftype))
18290 0 : ereport(ERROR,
18291 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18292 : errmsg("\"%s\" is not a typed table",
18293 : RelationGetRelationName(rel))));
18294 :
18295 : /*
18296 : * We don't bother to check ownership of the type --- ownership of the
18297 : * table is presumed enough rights. No lock required on the type, either.
18298 : */
18299 :
18300 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18301 : DEPENDENCY_NORMAL);
18302 :
18303 : /* Clear pg_class.reloftype */
18304 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18305 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18306 6 : if (!HeapTupleIsValid(tuple))
18307 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18308 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18309 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18310 :
18311 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18312 :
18313 6 : heap_freetuple(tuple);
18314 6 : table_close(relationRelation, RowExclusiveLock);
18315 6 : }
18316 :
18317 : /*
18318 : * relation_mark_replica_identity: Update a table's replica identity
18319 : *
18320 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18321 : * index. Otherwise, it must be InvalidOid.
18322 : *
18323 : * Caller had better hold an exclusive lock on the relation, as the results
18324 : * of running two of these concurrently wouldn't be pretty.
18325 : */
18326 : static void
18327 460 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18328 : bool is_internal)
18329 : {
18330 : Relation pg_index;
18331 : Relation pg_class;
18332 : HeapTuple pg_class_tuple;
18333 : HeapTuple pg_index_tuple;
18334 : Form_pg_class pg_class_form;
18335 : Form_pg_index pg_index_form;
18336 : ListCell *index;
18337 :
18338 : /*
18339 : * Check whether relreplident has changed, and update it if so.
18340 : */
18341 460 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18342 460 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
18343 : ObjectIdGetDatum(RelationGetRelid(rel)));
18344 460 : if (!HeapTupleIsValid(pg_class_tuple))
18345 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
18346 : RelationGetRelationName(rel));
18347 460 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18348 460 : if (pg_class_form->relreplident != ri_type)
18349 : {
18350 410 : pg_class_form->relreplident = ri_type;
18351 410 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18352 : }
18353 460 : table_close(pg_class, RowExclusiveLock);
18354 460 : heap_freetuple(pg_class_tuple);
18355 :
18356 : /*
18357 : * Update the per-index indisreplident flags correctly.
18358 : */
18359 460 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
18360 1180 : foreach(index, RelationGetIndexList(rel))
18361 : {
18362 720 : Oid thisIndexOid = lfirst_oid(index);
18363 720 : bool dirty = false;
18364 :
18365 720 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18366 : ObjectIdGetDatum(thisIndexOid));
18367 720 : if (!HeapTupleIsValid(pg_index_tuple))
18368 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18369 720 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18370 :
18371 720 : if (thisIndexOid == indexOid)
18372 : {
18373 : /* Set the bit if not already set. */
18374 240 : if (!pg_index_form->indisreplident)
18375 : {
18376 222 : dirty = true;
18377 222 : pg_index_form->indisreplident = true;
18378 : }
18379 : }
18380 : else
18381 : {
18382 : /* Unset the bit if set. */
18383 480 : if (pg_index_form->indisreplident)
18384 : {
18385 52 : dirty = true;
18386 52 : pg_index_form->indisreplident = false;
18387 : }
18388 : }
18389 :
18390 720 : if (dirty)
18391 : {
18392 274 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18393 274 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18394 : InvalidOid, is_internal);
18395 :
18396 : /*
18397 : * Invalidate the relcache for the table, so that after we commit
18398 : * all sessions will refresh the table's replica identity index
18399 : * before attempting any UPDATE or DELETE on the table. (If we
18400 : * changed the table's pg_class row above, then a relcache inval
18401 : * is already queued due to that; but we might not have.)
18402 : */
18403 274 : CacheInvalidateRelcache(rel);
18404 : }
18405 720 : heap_freetuple(pg_index_tuple);
18406 : }
18407 :
18408 460 : table_close(pg_index, RowExclusiveLock);
18409 460 : }
18410 :
18411 : /*
18412 : * ALTER TABLE <name> REPLICA IDENTITY ...
18413 : */
18414 : static void
18415 508 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
18416 : {
18417 : Oid indexOid;
18418 : Relation indexRel;
18419 : int key;
18420 :
18421 508 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18422 : {
18423 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18424 6 : return;
18425 : }
18426 502 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18427 : {
18428 166 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18429 166 : return;
18430 : }
18431 336 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18432 : {
18433 48 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18434 48 : return;
18435 : }
18436 288 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18437 : {
18438 : /* fallthrough */ ;
18439 : }
18440 : else
18441 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18442 :
18443 : /* Check that the index exists */
18444 288 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18445 288 : if (!OidIsValid(indexOid))
18446 0 : ereport(ERROR,
18447 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18448 : errmsg("index \"%s\" for table \"%s\" does not exist",
18449 : stmt->name, RelationGetRelationName(rel))));
18450 :
18451 288 : indexRel = index_open(indexOid, ShareLock);
18452 :
18453 : /* Check that the index is on the relation we're altering. */
18454 288 : if (indexRel->rd_index == NULL ||
18455 288 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
18456 6 : ereport(ERROR,
18457 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18458 : errmsg("\"%s\" is not an index for table \"%s\"",
18459 : RelationGetRelationName(indexRel),
18460 : RelationGetRelationName(rel))));
18461 :
18462 : /*
18463 : * The AM must support uniqueness, and the index must in fact be unique.
18464 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18465 : * exclusion), we can use that too.
18466 : */
18467 282 : if ((!indexRel->rd_indam->amcanunique ||
18468 262 : !indexRel->rd_index->indisunique) &&
18469 26 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18470 12 : ereport(ERROR,
18471 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18472 : errmsg("cannot use non-unique index \"%s\" as replica identity",
18473 : RelationGetRelationName(indexRel))));
18474 : /* Deferred indexes are not guaranteed to be always unique. */
18475 270 : if (!indexRel->rd_index->indimmediate)
18476 12 : ereport(ERROR,
18477 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18478 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
18479 : RelationGetRelationName(indexRel))));
18480 : /* Expression indexes aren't supported. */
18481 258 : if (RelationGetIndexExpressions(indexRel) != NIL)
18482 6 : ereport(ERROR,
18483 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18484 : errmsg("cannot use expression index \"%s\" as replica identity",
18485 : RelationGetRelationName(indexRel))));
18486 : /* Predicate indexes aren't supported. */
18487 252 : if (RelationGetIndexPredicate(indexRel) != NIL)
18488 6 : ereport(ERROR,
18489 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18490 : errmsg("cannot use partial index \"%s\" as replica identity",
18491 : RelationGetRelationName(indexRel))));
18492 :
18493 : /* Check index for nullable columns. */
18494 552 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18495 : {
18496 312 : int16 attno = indexRel->rd_index->indkey.values[key];
18497 : Form_pg_attribute attr;
18498 :
18499 : /*
18500 : * Reject any other system columns. (Going forward, we'll disallow
18501 : * indexes containing such columns in the first place, but they might
18502 : * exist in older branches.)
18503 : */
18504 312 : if (attno <= 0)
18505 0 : ereport(ERROR,
18506 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18507 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18508 : RelationGetRelationName(indexRel), attno)));
18509 :
18510 312 : attr = TupleDescAttr(rel->rd_att, attno - 1);
18511 312 : if (!attr->attnotnull)
18512 6 : ereport(ERROR,
18513 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18514 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18515 : RelationGetRelationName(indexRel),
18516 : NameStr(attr->attname))));
18517 : }
18518 :
18519 : /* This index is suitable for use as a replica identity. Mark it. */
18520 240 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18521 :
18522 240 : index_close(indexRel, NoLock);
18523 : }
18524 :
18525 : /*
18526 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18527 : */
18528 : static void
18529 312 : ATExecSetRowSecurity(Relation rel, bool rls)
18530 : {
18531 : Relation pg_class;
18532 : Oid relid;
18533 : HeapTuple tuple;
18534 :
18535 312 : relid = RelationGetRelid(rel);
18536 :
18537 : /* Pull the record for this relation and update it */
18538 312 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18539 :
18540 312 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18541 :
18542 312 : if (!HeapTupleIsValid(tuple))
18543 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18544 :
18545 312 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18546 312 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18547 :
18548 312 : InvokeObjectPostAlterHook(RelationRelationId,
18549 : RelationGetRelid(rel), 0);
18550 :
18551 312 : table_close(pg_class, RowExclusiveLock);
18552 312 : heap_freetuple(tuple);
18553 312 : }
18554 :
18555 : /*
18556 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18557 : */
18558 : static void
18559 128 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
18560 : {
18561 : Relation pg_class;
18562 : Oid relid;
18563 : HeapTuple tuple;
18564 :
18565 128 : relid = RelationGetRelid(rel);
18566 :
18567 128 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18568 :
18569 128 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18570 :
18571 128 : if (!HeapTupleIsValid(tuple))
18572 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18573 :
18574 128 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18575 128 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18576 :
18577 128 : InvokeObjectPostAlterHook(RelationRelationId,
18578 : RelationGetRelid(rel), 0);
18579 :
18580 128 : table_close(pg_class, RowExclusiveLock);
18581 128 : heap_freetuple(tuple);
18582 128 : }
18583 :
18584 : /*
18585 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
18586 : */
18587 : static void
18588 58 : ATExecGenericOptions(Relation rel, List *options)
18589 : {
18590 : Relation ftrel;
18591 : ForeignServer *server;
18592 : ForeignDataWrapper *fdw;
18593 : HeapTuple tuple;
18594 : bool isnull;
18595 : Datum repl_val[Natts_pg_foreign_table];
18596 : bool repl_null[Natts_pg_foreign_table];
18597 : bool repl_repl[Natts_pg_foreign_table];
18598 : Datum datum;
18599 : Form_pg_foreign_table tableform;
18600 :
18601 58 : if (options == NIL)
18602 0 : return;
18603 :
18604 58 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18605 :
18606 58 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18607 : ObjectIdGetDatum(rel->rd_id));
18608 58 : if (!HeapTupleIsValid(tuple))
18609 0 : ereport(ERROR,
18610 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18611 : errmsg("foreign table \"%s\" does not exist",
18612 : RelationGetRelationName(rel))));
18613 58 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18614 58 : server = GetForeignServer(tableform->ftserver);
18615 58 : fdw = GetForeignDataWrapper(server->fdwid);
18616 :
18617 58 : memset(repl_val, 0, sizeof(repl_val));
18618 58 : memset(repl_null, false, sizeof(repl_null));
18619 58 : memset(repl_repl, false, sizeof(repl_repl));
18620 :
18621 : /* Extract the current options */
18622 58 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
18623 : tuple,
18624 : Anum_pg_foreign_table_ftoptions,
18625 : &isnull);
18626 58 : if (isnull)
18627 4 : datum = PointerGetDatum(NULL);
18628 :
18629 : /* Transform the options */
18630 58 : datum = transformGenericOptions(ForeignTableRelationId,
18631 : datum,
18632 : options,
18633 : fdw->fdwvalidator);
18634 :
18635 56 : if (PointerIsValid(DatumGetPointer(datum)))
18636 56 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18637 : else
18638 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18639 :
18640 56 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18641 :
18642 : /* Everything looks good - update the tuple */
18643 :
18644 56 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18645 : repl_val, repl_null, repl_repl);
18646 :
18647 56 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18648 :
18649 : /*
18650 : * Invalidate relcache so that all sessions will refresh any cached plans
18651 : * that might depend on the old options.
18652 : */
18653 56 : CacheInvalidateRelcache(rel);
18654 :
18655 56 : InvokeObjectPostAlterHook(ForeignTableRelationId,
18656 : RelationGetRelid(rel), 0);
18657 :
18658 56 : table_close(ftrel, RowExclusiveLock);
18659 :
18660 56 : heap_freetuple(tuple);
18661 : }
18662 :
18663 : /*
18664 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
18665 : *
18666 : * Return value is the address of the modified column
18667 : */
18668 : static ObjectAddress
18669 90 : ATExecSetCompression(Relation rel,
18670 : const char *column,
18671 : Node *newValue,
18672 : LOCKMODE lockmode)
18673 : {
18674 : Relation attrel;
18675 : HeapTuple tuple;
18676 : Form_pg_attribute atttableform;
18677 : AttrNumber attnum;
18678 : char *compression;
18679 : char cmethod;
18680 : ObjectAddress address;
18681 :
18682 90 : compression = strVal(newValue);
18683 :
18684 90 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
18685 :
18686 : /* copy the cache entry so we can scribble on it below */
18687 90 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18688 90 : if (!HeapTupleIsValid(tuple))
18689 0 : ereport(ERROR,
18690 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18691 : errmsg("column \"%s\" of relation \"%s\" does not exist",
18692 : column, RelationGetRelationName(rel))));
18693 :
18694 : /* prevent them from altering a system attribute */
18695 90 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18696 90 : attnum = atttableform->attnum;
18697 90 : if (attnum <= 0)
18698 0 : ereport(ERROR,
18699 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18700 : errmsg("cannot alter system column \"%s\"", column)));
18701 :
18702 : /*
18703 : * Check that column type is compressible, then get the attribute
18704 : * compression method code
18705 : */
18706 90 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18707 :
18708 : /* update pg_attribute entry */
18709 84 : atttableform->attcompression = cmethod;
18710 84 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18711 :
18712 84 : InvokeObjectPostAlterHook(RelationRelationId,
18713 : RelationGetRelid(rel),
18714 : attnum);
18715 :
18716 : /*
18717 : * Apply the change to indexes as well (only for simple index columns,
18718 : * matching behavior of index.c ConstructTupleDescriptor()).
18719 : */
18720 84 : SetIndexStorageProperties(rel, attrel, attnum,
18721 : false, 0,
18722 : true, cmethod,
18723 : lockmode);
18724 :
18725 84 : heap_freetuple(tuple);
18726 :
18727 84 : table_close(attrel, RowExclusiveLock);
18728 :
18729 : /* make changes visible */
18730 84 : CommandCounterIncrement();
18731 :
18732 84 : ObjectAddressSubSet(address, RelationRelationId,
18733 : RelationGetRelid(rel), attnum);
18734 84 : return address;
18735 : }
18736 :
18737 :
18738 : /*
18739 : * Preparation phase for SET LOGGED/UNLOGGED
18740 : *
18741 : * This verifies that we're not trying to change a temp table. Also,
18742 : * existing foreign key constraints are checked to avoid ending up with
18743 : * permanent tables referencing unlogged tables.
18744 : */
18745 : static void
18746 100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
18747 : {
18748 : Relation pg_constraint;
18749 : HeapTuple tuple;
18750 : SysScanDesc scan;
18751 : ScanKeyData skey[1];
18752 :
18753 : /*
18754 : * Disallow changing status for a temp table. Also verify whether we can
18755 : * get away with doing nothing; in such cases we don't need to run the
18756 : * checks below, either.
18757 : */
18758 100 : switch (rel->rd_rel->relpersistence)
18759 : {
18760 0 : case RELPERSISTENCE_TEMP:
18761 0 : ereport(ERROR,
18762 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18763 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
18764 : RelationGetRelationName(rel)),
18765 : errtable(rel)));
18766 : break;
18767 56 : case RELPERSISTENCE_PERMANENT:
18768 56 : if (toLogged)
18769 : /* nothing to do */
18770 12 : return;
18771 50 : break;
18772 44 : case RELPERSISTENCE_UNLOGGED:
18773 44 : if (!toLogged)
18774 : /* nothing to do */
18775 6 : return;
18776 38 : break;
18777 : }
18778 :
18779 : /*
18780 : * Check that the table is not part of any publication when changing to
18781 : * UNLOGGED, as UNLOGGED tables can't be published.
18782 : */
18783 138 : if (!toLogged &&
18784 50 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
18785 0 : ereport(ERROR,
18786 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18787 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18788 : RelationGetRelationName(rel)),
18789 : errdetail("Unlogged relations cannot be replicated.")));
18790 :
18791 : /*
18792 : * Check existing foreign key constraints to preserve the invariant that
18793 : * permanent tables cannot reference unlogged ones. Self-referencing
18794 : * foreign keys can safely be ignored.
18795 : */
18796 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18797 :
18798 : /*
18799 : * Scan conrelid if changing to permanent, else confrelid. This also
18800 : * determines whether a useful index exists.
18801 : */
18802 88 : ScanKeyInit(&skey[0],
18803 : toLogged ? Anum_pg_constraint_conrelid :
18804 : Anum_pg_constraint_confrelid,
18805 : BTEqualStrategyNumber, F_OIDEQ,
18806 : ObjectIdGetDatum(RelationGetRelid(rel)));
18807 88 : scan = systable_beginscan(pg_constraint,
18808 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18809 : true, NULL, 1, skey);
18810 :
18811 142 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18812 : {
18813 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
18814 :
18815 66 : if (con->contype == CONSTRAINT_FOREIGN)
18816 : {
18817 : Oid foreignrelid;
18818 : Relation foreignrel;
18819 :
18820 : /* the opposite end of what we used as scankey */
18821 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
18822 :
18823 : /* ignore if self-referencing */
18824 30 : if (RelationGetRelid(rel) == foreignrelid)
18825 12 : continue;
18826 :
18827 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
18828 :
18829 18 : if (toLogged)
18830 : {
18831 6 : if (!RelationIsPermanent(foreignrel))
18832 6 : ereport(ERROR,
18833 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18834 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18835 : RelationGetRelationName(rel),
18836 : RelationGetRelationName(foreignrel)),
18837 : errtableconstraint(rel, NameStr(con->conname))));
18838 : }
18839 : else
18840 : {
18841 12 : if (RelationIsPermanent(foreignrel))
18842 6 : ereport(ERROR,
18843 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18844 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18845 : RelationGetRelationName(rel),
18846 : RelationGetRelationName(foreignrel)),
18847 : errtableconstraint(rel, NameStr(con->conname))));
18848 : }
18849 :
18850 6 : relation_close(foreignrel, AccessShareLock);
18851 : }
18852 : }
18853 :
18854 76 : systable_endscan(scan);
18855 :
18856 76 : table_close(pg_constraint, AccessShareLock);
18857 :
18858 : /* force rewrite if necessary; see comment in ATRewriteTables */
18859 76 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
18860 76 : if (toLogged)
18861 32 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18862 : else
18863 44 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18864 76 : tab->chgPersistence = true;
18865 : }
18866 :
18867 : /*
18868 : * Execute ALTER TABLE SET SCHEMA
18869 : */
18870 : ObjectAddress
18871 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
18872 : {
18873 : Relation rel;
18874 : Oid relid;
18875 : Oid oldNspOid;
18876 : Oid nspOid;
18877 : RangeVar *newrv;
18878 : ObjectAddresses *objsMoved;
18879 : ObjectAddress myself;
18880 :
18881 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
18882 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
18883 : RangeVarCallbackForAlterRelation,
18884 : stmt);
18885 :
18886 102 : if (!OidIsValid(relid))
18887 : {
18888 12 : ereport(NOTICE,
18889 : (errmsg("relation \"%s\" does not exist, skipping",
18890 : stmt->relation->relname)));
18891 12 : return InvalidObjectAddress;
18892 : }
18893 :
18894 90 : rel = relation_open(relid, NoLock);
18895 :
18896 90 : oldNspOid = RelationGetNamespace(rel);
18897 :
18898 : /* If it's an owned sequence, disallow moving it by itself. */
18899 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18900 : {
18901 : Oid tableId;
18902 : int32 colId;
18903 :
18904 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18905 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18906 6 : ereport(ERROR,
18907 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18908 : errmsg("cannot move an owned sequence into another schema"),
18909 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
18910 : RelationGetRelationName(rel),
18911 : get_rel_name(tableId))));
18912 : }
18913 :
18914 : /* Get and lock schema OID and check its permissions. */
18915 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18916 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18917 :
18918 : /* common checks on switching namespaces */
18919 84 : CheckSetNamespace(oldNspOid, nspOid);
18920 :
18921 84 : objsMoved = new_object_addresses();
18922 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18923 84 : free_object_addresses(objsMoved);
18924 :
18925 84 : ObjectAddressSet(myself, RelationRelationId, relid);
18926 :
18927 84 : if (oldschema)
18928 84 : *oldschema = oldNspOid;
18929 :
18930 : /* close rel, but keep lock until commit */
18931 84 : relation_close(rel, NoLock);
18932 :
18933 84 : return myself;
18934 : }
18935 :
18936 : /*
18937 : * The guts of relocating a table or materialized view to another namespace:
18938 : * besides moving the relation itself, its dependent objects are relocated to
18939 : * the new schema.
18940 : */
18941 : void
18942 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
18943 : ObjectAddresses *objsMoved)
18944 : {
18945 : Relation classRel;
18946 :
18947 : Assert(objsMoved != NULL);
18948 :
18949 : /* OK, modify the pg_class row and pg_depend entry */
18950 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
18951 :
18952 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18953 : nspOid, true, objsMoved);
18954 :
18955 : /* Fix the table's row type too, if it has one */
18956 86 : if (OidIsValid(rel->rd_rel->reltype))
18957 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18958 : false, /* isImplicitArray */
18959 : false, /* ignoreDependent */
18960 : false, /* errorOnTableType */
18961 : objsMoved);
18962 :
18963 : /* Fix other dependent stuff */
18964 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18965 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18966 : objsMoved, AccessExclusiveLock);
18967 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18968 : false, objsMoved);
18969 :
18970 86 : table_close(classRel, RowExclusiveLock);
18971 86 : }
18972 :
18973 : /*
18974 : * The guts of relocating a relation to another namespace: fix the pg_class
18975 : * entry, and the pg_depend entry if any. Caller must already have
18976 : * opened and write-locked pg_class.
18977 : */
18978 : void
18979 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
18980 : Oid oldNspOid, Oid newNspOid,
18981 : bool hasDependEntry,
18982 : ObjectAddresses *objsMoved)
18983 : {
18984 : HeapTuple classTup;
18985 : Form_pg_class classForm;
18986 : ObjectAddress thisobj;
18987 188 : bool already_done = false;
18988 :
18989 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
18990 188 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
18991 188 : if (!HeapTupleIsValid(classTup))
18992 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
18993 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
18994 :
18995 : Assert(classForm->relnamespace == oldNspOid);
18996 :
18997 188 : thisobj.classId = RelationRelationId;
18998 188 : thisobj.objectId = relOid;
18999 188 : thisobj.objectSubId = 0;
19000 :
19001 : /*
19002 : * If the object has already been moved, don't move it again. If it's
19003 : * already in the right place, don't move it, but still fire the object
19004 : * access hook.
19005 : */
19006 188 : already_done = object_address_present(&thisobj, objsMoved);
19007 188 : if (!already_done && oldNspOid != newNspOid)
19008 146 : {
19009 146 : ItemPointerData otid = classTup->t_self;
19010 :
19011 : /* check for duplicate name (more friendly than unique-index failure) */
19012 146 : if (get_relname_relid(NameStr(classForm->relname),
19013 : newNspOid) != InvalidOid)
19014 0 : ereport(ERROR,
19015 : (errcode(ERRCODE_DUPLICATE_TABLE),
19016 : errmsg("relation \"%s\" already exists in schema \"%s\"",
19017 : NameStr(classForm->relname),
19018 : get_namespace_name(newNspOid))));
19019 :
19020 : /* classTup is a copy, so OK to scribble on */
19021 146 : classForm->relnamespace = newNspOid;
19022 :
19023 146 : CatalogTupleUpdate(classRel, &otid, classTup);
19024 146 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19025 :
19026 :
19027 : /* Update dependency on schema if caller said so */
19028 250 : if (hasDependEntry &&
19029 104 : changeDependencyFor(RelationRelationId,
19030 : relOid,
19031 : NamespaceRelationId,
19032 : oldNspOid,
19033 : newNspOid) != 1)
19034 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
19035 : NameStr(classForm->relname));
19036 : }
19037 : else
19038 42 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19039 188 : if (!already_done)
19040 : {
19041 188 : add_exact_object_address(&thisobj, objsMoved);
19042 :
19043 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19044 : }
19045 :
19046 188 : heap_freetuple(classTup);
19047 188 : }
19048 :
19049 : /*
19050 : * Move all indexes for the specified relation to another namespace.
19051 : *
19052 : * Note: we assume adequate permission checking was done by the caller,
19053 : * and that the caller has a suitable lock on the owning relation.
19054 : */
19055 : static void
19056 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
19057 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19058 : {
19059 : List *indexList;
19060 : ListCell *l;
19061 :
19062 86 : indexList = RelationGetIndexList(rel);
19063 :
19064 132 : foreach(l, indexList)
19065 : {
19066 46 : Oid indexOid = lfirst_oid(l);
19067 : ObjectAddress thisobj;
19068 :
19069 46 : thisobj.classId = RelationRelationId;
19070 46 : thisobj.objectId = indexOid;
19071 46 : thisobj.objectSubId = 0;
19072 :
19073 : /*
19074 : * Note: currently, the index will not have its own dependency on the
19075 : * namespace, so we don't need to do changeDependencyFor(). There's no
19076 : * row type in pg_type, either.
19077 : *
19078 : * XXX this objsMoved test may be pointless -- surely we have a single
19079 : * dependency link from a relation to each index?
19080 : */
19081 46 : if (!object_address_present(&thisobj, objsMoved))
19082 : {
19083 46 : AlterRelationNamespaceInternal(classRel, indexOid,
19084 : oldNspOid, newNspOid,
19085 : false, objsMoved);
19086 46 : add_exact_object_address(&thisobj, objsMoved);
19087 : }
19088 : }
19089 :
19090 86 : list_free(indexList);
19091 86 : }
19092 :
19093 : /*
19094 : * Move all identity and SERIAL-column sequences of the specified relation to another
19095 : * namespace.
19096 : *
19097 : * Note: we assume adequate permission checking was done by the caller,
19098 : * and that the caller has a suitable lock on the owning relation.
19099 : */
19100 : static void
19101 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
19102 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19103 : LOCKMODE lockmode)
19104 : {
19105 : Relation depRel;
19106 : SysScanDesc scan;
19107 : ScanKeyData key[2];
19108 : HeapTuple tup;
19109 :
19110 : /*
19111 : * SERIAL sequences are those having an auto dependency on one of the
19112 : * table's columns (we don't care *which* column, exactly).
19113 : */
19114 86 : depRel = table_open(DependRelationId, AccessShareLock);
19115 :
19116 86 : ScanKeyInit(&key[0],
19117 : Anum_pg_depend_refclassid,
19118 : BTEqualStrategyNumber, F_OIDEQ,
19119 : ObjectIdGetDatum(RelationRelationId));
19120 86 : ScanKeyInit(&key[1],
19121 : Anum_pg_depend_refobjid,
19122 : BTEqualStrategyNumber, F_OIDEQ,
19123 : ObjectIdGetDatum(RelationGetRelid(rel)));
19124 : /* we leave refobjsubid unspecified */
19125 :
19126 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19127 : NULL, 2, key);
19128 :
19129 616 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
19130 : {
19131 530 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19132 : Relation seqRel;
19133 :
19134 : /* skip dependencies other than auto dependencies on columns */
19135 530 : if (depForm->refobjsubid == 0 ||
19136 382 : depForm->classid != RelationRelationId ||
19137 42 : depForm->objsubid != 0 ||
19138 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19139 488 : continue;
19140 :
19141 : /* Use relation_open just in case it's an index */
19142 42 : seqRel = relation_open(depForm->objid, lockmode);
19143 :
19144 : /* skip non-sequence relations */
19145 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19146 : {
19147 : /* No need to keep the lock */
19148 0 : relation_close(seqRel, lockmode);
19149 0 : continue;
19150 : }
19151 :
19152 : /* Fix the pg_class and pg_depend entries */
19153 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
19154 : oldNspOid, newNspOid,
19155 : true, objsMoved);
19156 :
19157 : /*
19158 : * Sequences used to have entries in pg_type, but no longer do. If we
19159 : * ever re-instate that, we'll need to move the pg_type entry to the
19160 : * new namespace, too (using AlterTypeNamespaceInternal).
19161 : */
19162 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19163 :
19164 : /* Now we can close it. Keep the lock till end of transaction. */
19165 42 : relation_close(seqRel, NoLock);
19166 : }
19167 :
19168 86 : systable_endscan(scan);
19169 :
19170 86 : relation_close(depRel, AccessShareLock);
19171 86 : }
19172 :
19173 :
19174 : /*
19175 : * This code supports
19176 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19177 : *
19178 : * Because we only support this for TEMP tables, it's sufficient to remember
19179 : * the state in a backend-local data structure.
19180 : */
19181 :
19182 : /*
19183 : * Register a newly-created relation's ON COMMIT action.
19184 : */
19185 : void
19186 176 : register_on_commit_action(Oid relid, OnCommitAction action)
19187 : {
19188 : OnCommitItem *oc;
19189 : MemoryContext oldcxt;
19190 :
19191 : /*
19192 : * We needn't bother registering the relation unless there is an ON COMMIT
19193 : * action we need to take.
19194 : */
19195 176 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19196 24 : return;
19197 :
19198 152 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
19199 :
19200 152 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19201 152 : oc->relid = relid;
19202 152 : oc->oncommit = action;
19203 152 : oc->creating_subid = GetCurrentSubTransactionId();
19204 152 : oc->deleting_subid = InvalidSubTransactionId;
19205 :
19206 : /*
19207 : * We use lcons() here so that ON COMMIT actions are processed in reverse
19208 : * order of registration. That might not be essential but it seems
19209 : * reasonable.
19210 : */
19211 152 : on_commits = lcons(oc, on_commits);
19212 :
19213 152 : MemoryContextSwitchTo(oldcxt);
19214 : }
19215 :
19216 : /*
19217 : * Unregister any ON COMMIT action when a relation is deleted.
19218 : *
19219 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19220 : */
19221 : void
19222 47784 : remove_on_commit_action(Oid relid)
19223 : {
19224 : ListCell *l;
19225 :
19226 47930 : foreach(l, on_commits)
19227 : {
19228 286 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19229 :
19230 286 : if (oc->relid == relid)
19231 : {
19232 140 : oc->deleting_subid = GetCurrentSubTransactionId();
19233 140 : break;
19234 : }
19235 : }
19236 47784 : }
19237 :
19238 : /*
19239 : * Perform ON COMMIT actions.
19240 : *
19241 : * This is invoked just before actually committing, since it's possible
19242 : * to encounter errors.
19243 : */
19244 : void
19245 815886 : PreCommit_on_commit_actions(void)
19246 : {
19247 : ListCell *l;
19248 815886 : List *oids_to_truncate = NIL;
19249 815886 : List *oids_to_drop = NIL;
19250 :
19251 816710 : foreach(l, on_commits)
19252 : {
19253 824 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19254 :
19255 : /* Ignore entry if already dropped in this xact */
19256 824 : if (oc->deleting_subid != InvalidSubTransactionId)
19257 74 : continue;
19258 :
19259 750 : switch (oc->oncommit)
19260 : {
19261 0 : case ONCOMMIT_NOOP:
19262 : case ONCOMMIT_PRESERVE_ROWS:
19263 : /* Do nothing (there shouldn't be such entries, actually) */
19264 0 : break;
19265 696 : case ONCOMMIT_DELETE_ROWS:
19266 :
19267 : /*
19268 : * If this transaction hasn't accessed any temporary
19269 : * relations, we can skip truncating ON COMMIT DELETE ROWS
19270 : * tables, as they must still be empty.
19271 : */
19272 696 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
19273 448 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19274 696 : break;
19275 54 : case ONCOMMIT_DROP:
19276 54 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19277 54 : break;
19278 : }
19279 824 : }
19280 :
19281 : /*
19282 : * Truncate relations before dropping so that all dependencies between
19283 : * relations are removed after they are worked on. Doing it like this
19284 : * might be a waste as it is possible that a relation being truncated will
19285 : * be dropped anyway due to its parent being dropped, but this makes the
19286 : * code more robust because of not having to re-check that the relation
19287 : * exists at truncation time.
19288 : */
19289 815886 : if (oids_to_truncate != NIL)
19290 382 : heap_truncate(oids_to_truncate);
19291 :
19292 815880 : if (oids_to_drop != NIL)
19293 : {
19294 48 : ObjectAddresses *targetObjects = new_object_addresses();
19295 :
19296 102 : foreach(l, oids_to_drop)
19297 : {
19298 : ObjectAddress object;
19299 :
19300 54 : object.classId = RelationRelationId;
19301 54 : object.objectId = lfirst_oid(l);
19302 54 : object.objectSubId = 0;
19303 :
19304 : Assert(!object_address_present(&object, targetObjects));
19305 :
19306 54 : add_exact_object_address(&object, targetObjects);
19307 : }
19308 :
19309 : /*
19310 : * Object deletion might involve toast table access (to clean up
19311 : * toasted catalog entries), so ensure we have a valid snapshot.
19312 : */
19313 48 : PushActiveSnapshot(GetTransactionSnapshot());
19314 :
19315 : /*
19316 : * Since this is an automatic drop, rather than one directly initiated
19317 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19318 : */
19319 48 : performMultipleDeletions(targetObjects, DROP_CASCADE,
19320 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
19321 :
19322 48 : PopActiveSnapshot();
19323 :
19324 : #ifdef USE_ASSERT_CHECKING
19325 :
19326 : /*
19327 : * Note that table deletion will call remove_on_commit_action, so the
19328 : * entry should get marked as deleted.
19329 : */
19330 : foreach(l, on_commits)
19331 : {
19332 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19333 :
19334 : if (oc->oncommit != ONCOMMIT_DROP)
19335 : continue;
19336 :
19337 : Assert(oc->deleting_subid != InvalidSubTransactionId);
19338 : }
19339 : #endif
19340 : }
19341 815880 : }
19342 :
19343 : /*
19344 : * Post-commit or post-abort cleanup for ON COMMIT management.
19345 : *
19346 : * All we do here is remove no-longer-needed OnCommitItem entries.
19347 : *
19348 : * During commit, remove entries that were deleted during this transaction;
19349 : * during abort, remove those created during this transaction.
19350 : */
19351 : void
19352 864380 : AtEOXact_on_commit_actions(bool isCommit)
19353 : {
19354 : ListCell *cur_item;
19355 :
19356 865234 : foreach(cur_item, on_commits)
19357 : {
19358 854 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19359 :
19360 956 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19361 102 : oc->creating_subid != InvalidSubTransactionId)
19362 : {
19363 : /* cur_item must be removed */
19364 152 : on_commits = foreach_delete_current(on_commits, cur_item);
19365 152 : pfree(oc);
19366 : }
19367 : else
19368 : {
19369 : /* cur_item must be preserved */
19370 702 : oc->creating_subid = InvalidSubTransactionId;
19371 702 : oc->deleting_subid = InvalidSubTransactionId;
19372 : }
19373 : }
19374 864380 : }
19375 :
19376 : /*
19377 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19378 : *
19379 : * During subabort, we can immediately remove entries created during this
19380 : * subtransaction. During subcommit, just relabel entries marked during
19381 : * this subtransaction as being the parent's responsibility.
19382 : */
19383 : void
19384 20006 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
19385 : SubTransactionId parentSubid)
19386 : {
19387 : ListCell *cur_item;
19388 :
19389 20006 : foreach(cur_item, on_commits)
19390 : {
19391 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19392 :
19393 0 : if (!isCommit && oc->creating_subid == mySubid)
19394 : {
19395 : /* cur_item must be removed */
19396 0 : on_commits = foreach_delete_current(on_commits, cur_item);
19397 0 : pfree(oc);
19398 : }
19399 : else
19400 : {
19401 : /* cur_item must be preserved */
19402 0 : if (oc->creating_subid == mySubid)
19403 0 : oc->creating_subid = parentSubid;
19404 0 : if (oc->deleting_subid == mySubid)
19405 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19406 : }
19407 : }
19408 20006 : }
19409 :
19410 : /*
19411 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19412 : * the relation to be locked only if (1) it's a plain or partitioned table,
19413 : * materialized view, or TOAST table and (2) the current user is the owner (or
19414 : * the superuser) or has been granted MAINTAIN. This meets the
19415 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19416 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19417 : */
19418 : void
19419 1042 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
19420 : Oid relId, Oid oldRelId, void *arg)
19421 : {
19422 : char relkind;
19423 : AclResult aclresult;
19424 :
19425 : /* Nothing to do if the relation was not found. */
19426 1042 : if (!OidIsValid(relId))
19427 6 : return;
19428 :
19429 : /*
19430 : * If the relation does exist, check whether it's an index. But note that
19431 : * the relation might have been dropped between the time we did the name
19432 : * lookup and now. In that case, there's nothing to do.
19433 : */
19434 1036 : relkind = get_rel_relkind(relId);
19435 1036 : if (!relkind)
19436 0 : return;
19437 1036 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19438 140 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19439 28 : ereport(ERROR,
19440 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19441 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19442 :
19443 : /* Check permissions */
19444 1008 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19445 1008 : if (aclresult != ACLCHECK_OK)
19446 30 : aclcheck_error(aclresult,
19447 30 : get_relkind_objtype(get_rel_relkind(relId)),
19448 30 : relation->relname);
19449 : }
19450 :
19451 : /*
19452 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19453 : */
19454 : static void
19455 3574 : RangeVarCallbackForTruncate(const RangeVar *relation,
19456 : Oid relId, Oid oldRelId, void *arg)
19457 : {
19458 : HeapTuple tuple;
19459 :
19460 : /* Nothing to do if the relation was not found. */
19461 3574 : if (!OidIsValid(relId))
19462 0 : return;
19463 :
19464 3574 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19465 3574 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19466 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19467 :
19468 3574 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
19469 3568 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
19470 :
19471 3536 : ReleaseSysCache(tuple);
19472 : }
19473 :
19474 : /*
19475 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19476 : * the owner of the relation, or superuser.
19477 : */
19478 : void
19479 15966 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
19480 : Oid relId, Oid oldRelId, void *arg)
19481 : {
19482 : HeapTuple tuple;
19483 :
19484 : /* Nothing to do if the relation was not found. */
19485 15966 : if (!OidIsValid(relId))
19486 14 : return;
19487 :
19488 15952 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19489 15952 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19490 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19491 :
19492 15952 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19493 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
19494 6 : relation->relname);
19495 :
19496 31772 : if (!allowSystemTableMods &&
19497 15826 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19498 2 : ereport(ERROR,
19499 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19500 : errmsg("permission denied: \"%s\" is a system catalog",
19501 : relation->relname)));
19502 :
19503 15944 : ReleaseSysCache(tuple);
19504 : }
19505 :
19506 : /*
19507 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
19508 : * processing.
19509 : */
19510 : static void
19511 35658 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
19512 : void *arg)
19513 : {
19514 35658 : Node *stmt = (Node *) arg;
19515 : ObjectType reltype;
19516 : HeapTuple tuple;
19517 : Form_pg_class classform;
19518 : AclResult aclresult;
19519 : char relkind;
19520 :
19521 35658 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19522 35658 : if (!HeapTupleIsValid(tuple))
19523 214 : return; /* concurrently dropped */
19524 35444 : classform = (Form_pg_class) GETSTRUCT(tuple);
19525 35444 : relkind = classform->relkind;
19526 :
19527 : /* Must own relation. */
19528 35444 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19529 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
19530 :
19531 : /* No system table modifications unless explicitly allowed. */
19532 35384 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
19533 30 : ereport(ERROR,
19534 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19535 : errmsg("permission denied: \"%s\" is a system catalog",
19536 : rv->relname)));
19537 :
19538 : /*
19539 : * Extract the specified relation type from the statement parse tree.
19540 : *
19541 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
19542 : * have CREATE rights on the containing namespace.
19543 : */
19544 35354 : if (IsA(stmt, RenameStmt))
19545 : {
19546 488 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19547 : GetUserId(), ACL_CREATE);
19548 488 : if (aclresult != ACLCHECK_OK)
19549 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
19550 0 : get_namespace_name(classform->relnamespace));
19551 488 : reltype = ((RenameStmt *) stmt)->renameType;
19552 : }
19553 34866 : else if (IsA(stmt, AlterObjectSchemaStmt))
19554 92 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19555 :
19556 34774 : else if (IsA(stmt, AlterTableStmt))
19557 34774 : reltype = ((AlterTableStmt *) stmt)->objtype;
19558 : else
19559 : {
19560 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19561 : reltype = OBJECT_TABLE; /* placate compiler */
19562 : }
19563 :
19564 : /*
19565 : * For compatibility with prior releases, we allow ALTER TABLE to be used
19566 : * with most other types of relations (but not composite types). We allow
19567 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
19568 : * otherwise. Otherwise, the user must select the correct form of the
19569 : * command for the relation at issue.
19570 : */
19571 35354 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19572 0 : ereport(ERROR,
19573 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19574 : errmsg("\"%s\" is not a sequence", rv->relname)));
19575 :
19576 35354 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19577 0 : ereport(ERROR,
19578 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19579 : errmsg("\"%s\" is not a view", rv->relname)));
19580 :
19581 35354 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19582 0 : ereport(ERROR,
19583 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19584 : errmsg("\"%s\" is not a materialized view", rv->relname)));
19585 :
19586 35354 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19587 0 : ereport(ERROR,
19588 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19589 : errmsg("\"%s\" is not a foreign table", rv->relname)));
19590 :
19591 35354 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19592 0 : ereport(ERROR,
19593 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19594 : errmsg("\"%s\" is not a composite type", rv->relname)));
19595 :
19596 35354 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19597 : relkind != RELKIND_PARTITIONED_INDEX
19598 32 : && !IsA(stmt, RenameStmt))
19599 6 : ereport(ERROR,
19600 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19601 : errmsg("\"%s\" is not an index", rv->relname)));
19602 :
19603 : /*
19604 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19605 : * TYPE for that.
19606 : */
19607 35348 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19608 0 : ereport(ERROR,
19609 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19610 : errmsg("\"%s\" is a composite type", rv->relname),
19611 : /* translator: %s is an SQL ALTER command */
19612 : errhint("Use %s instead.",
19613 : "ALTER TYPE")));
19614 :
19615 : /*
19616 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19617 : * to a different schema, such as indexes and TOAST tables.
19618 : */
19619 35348 : if (IsA(stmt, AlterObjectSchemaStmt))
19620 : {
19621 92 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19622 0 : ereport(ERROR,
19623 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19624 : errmsg("cannot change schema of index \"%s\"",
19625 : rv->relname),
19626 : errhint("Change the schema of the table instead.")));
19627 92 : else if (relkind == RELKIND_COMPOSITE_TYPE)
19628 0 : ereport(ERROR,
19629 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19630 : errmsg("cannot change schema of composite type \"%s\"",
19631 : rv->relname),
19632 : /* translator: %s is an SQL ALTER command */
19633 : errhint("Use %s instead.",
19634 : "ALTER TYPE")));
19635 92 : else if (relkind == RELKIND_TOASTVALUE)
19636 0 : ereport(ERROR,
19637 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19638 : errmsg("cannot change schema of TOAST table \"%s\"",
19639 : rv->relname),
19640 : errhint("Change the schema of the table instead.")));
19641 : }
19642 :
19643 35348 : ReleaseSysCache(tuple);
19644 : }
19645 :
19646 : /*
19647 : * Transform any expressions present in the partition key
19648 : *
19649 : * Returns a transformed PartitionSpec.
19650 : */
19651 : static PartitionSpec *
19652 5090 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
19653 : {
19654 : PartitionSpec *newspec;
19655 : ParseState *pstate;
19656 : ParseNamespaceItem *nsitem;
19657 : ListCell *l;
19658 :
19659 5090 : newspec = makeNode(PartitionSpec);
19660 :
19661 5090 : newspec->strategy = partspec->strategy;
19662 5090 : newspec->partParams = NIL;
19663 5090 : newspec->location = partspec->location;
19664 :
19665 : /* Check valid number of columns for strategy */
19666 7648 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19667 2558 : list_length(partspec->partParams) != 1)
19668 6 : ereport(ERROR,
19669 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19670 : errmsg("cannot use \"list\" partition strategy with more than one column")));
19671 :
19672 : /*
19673 : * Create a dummy ParseState and insert the target relation as its sole
19674 : * rangetable entry. We need a ParseState for transformExpr.
19675 : */
19676 5084 : pstate = make_parsestate(NULL);
19677 5084 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19678 : NULL, false, true);
19679 5084 : addNSItemToQuery(pstate, nsitem, true, true, true);
19680 :
19681 : /* take care of any partition expressions */
19682 10604 : foreach(l, partspec->partParams)
19683 : {
19684 5544 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
19685 :
19686 5544 : if (pelem->expr)
19687 : {
19688 : /* Copy, to avoid scribbling on the input */
19689 320 : pelem = copyObject(pelem);
19690 :
19691 : /* Now do parse transformation of the expression */
19692 320 : pelem->expr = transformExpr(pstate, pelem->expr,
19693 : EXPR_KIND_PARTITION_EXPRESSION);
19694 :
19695 : /* we have to fix its collations too */
19696 296 : assign_expr_collations(pstate, pelem->expr);
19697 : }
19698 :
19699 5520 : newspec->partParams = lappend(newspec->partParams, pelem);
19700 : }
19701 :
19702 5060 : return newspec;
19703 : }
19704 :
19705 : /*
19706 : * Compute per-partition-column information from a list of PartitionElems.
19707 : * Expressions in the PartitionElems must be parse-analyzed already.
19708 : */
19709 : static void
19710 5060 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19711 : List **partexprs, Oid *partopclass, Oid *partcollation,
19712 : PartitionStrategy strategy)
19713 : {
19714 : int attn;
19715 : ListCell *lc;
19716 : Oid am_oid;
19717 :
19718 5060 : attn = 0;
19719 10484 : foreach(lc, partParams)
19720 : {
19721 5520 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
19722 : Oid atttype;
19723 : Oid attcollation;
19724 :
19725 5520 : if (pelem->name != NULL)
19726 : {
19727 : /* Simple attribute reference */
19728 : HeapTuple atttuple;
19729 : Form_pg_attribute attform;
19730 :
19731 5224 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
19732 5224 : pelem->name);
19733 5224 : if (!HeapTupleIsValid(atttuple))
19734 12 : ereport(ERROR,
19735 : (errcode(ERRCODE_UNDEFINED_COLUMN),
19736 : errmsg("column \"%s\" named in partition key does not exist",
19737 : pelem->name),
19738 : parser_errposition(pstate, pelem->location)));
19739 5212 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19740 :
19741 5212 : if (attform->attnum <= 0)
19742 6 : ereport(ERROR,
19743 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19744 : errmsg("cannot use system column \"%s\" in partition key",
19745 : pelem->name),
19746 : parser_errposition(pstate, pelem->location)));
19747 :
19748 : /*
19749 : * Stored generated columns cannot work: They are computed after
19750 : * BEFORE triggers, but partition routing is done before all
19751 : * triggers. Maybe virtual generated columns could be made to
19752 : * work, but then they would need to be handled as an expression
19753 : * below.
19754 : */
19755 5206 : if (attform->attgenerated)
19756 12 : ereport(ERROR,
19757 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19758 : errmsg("cannot use generated column in partition key"),
19759 : errdetail("Column \"%s\" is a generated column.",
19760 : pelem->name),
19761 : parser_errposition(pstate, pelem->location)));
19762 :
19763 5194 : partattrs[attn] = attform->attnum;
19764 5194 : atttype = attform->atttypid;
19765 5194 : attcollation = attform->attcollation;
19766 5194 : ReleaseSysCache(atttuple);
19767 : }
19768 : else
19769 : {
19770 : /* Expression */
19771 296 : Node *expr = pelem->expr;
19772 : char partattname[16];
19773 :
19774 : Assert(expr != NULL);
19775 296 : atttype = exprType(expr);
19776 296 : attcollation = exprCollation(expr);
19777 :
19778 : /*
19779 : * The expression must be of a storable type (e.g., not RECORD).
19780 : * The test is the same as for whether a table column is of a safe
19781 : * type (which is why we needn't check for the non-expression
19782 : * case).
19783 : */
19784 296 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19785 296 : CheckAttributeType(partattname,
19786 : atttype, attcollation,
19787 : NIL, CHKATYPE_IS_PARTKEY);
19788 :
19789 : /*
19790 : * Strip any top-level COLLATE clause. This ensures that we treat
19791 : * "x COLLATE y" and "(x COLLATE y)" alike.
19792 : */
19793 284 : while (IsA(expr, CollateExpr))
19794 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
19795 :
19796 284 : if (IsA(expr, Var) &&
19797 12 : ((Var *) expr)->varattno > 0)
19798 : {
19799 : /*
19800 : * User wrote "(column)" or "(column COLLATE something)".
19801 : * Treat it like simple attribute anyway.
19802 : */
19803 6 : partattrs[attn] = ((Var *) expr)->varattno;
19804 : }
19805 : else
19806 : {
19807 278 : Bitmapset *expr_attrs = NULL;
19808 : int i;
19809 :
19810 278 : partattrs[attn] = 0; /* marks the column as expression */
19811 278 : *partexprs = lappend(*partexprs, expr);
19812 :
19813 : /*
19814 : * transformPartitionSpec() should have already rejected
19815 : * subqueries, aggregates, window functions, and SRFs, based
19816 : * on the EXPR_KIND_ for partition expressions.
19817 : */
19818 :
19819 : /*
19820 : * Cannot allow system column references, since that would
19821 : * make partition routing impossible: their values won't be
19822 : * known yet when we need to do that.
19823 : */
19824 278 : pull_varattnos(expr, 1, &expr_attrs);
19825 2224 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
19826 : {
19827 1946 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
19828 : expr_attrs))
19829 0 : ereport(ERROR,
19830 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19831 : errmsg("partition key expressions cannot contain system column references")));
19832 : }
19833 :
19834 : /*
19835 : * Stored generated columns cannot work: They are computed
19836 : * after BEFORE triggers, but partition routing is done before
19837 : * all triggers. Virtual generated columns could probably
19838 : * work, but it would require more work elsewhere (for example
19839 : * SET EXPRESSION would need to check whether the column is
19840 : * used in partition keys). Seems safer to prohibit for now.
19841 : */
19842 278 : i = -1;
19843 612 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
19844 : {
19845 346 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
19846 :
19847 346 : if (attno > 0 &&
19848 340 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19849 12 : ereport(ERROR,
19850 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19851 : errmsg("cannot use generated column in partition key"),
19852 : errdetail("Column \"%s\" is a generated column.",
19853 : get_attname(RelationGetRelid(rel), attno, false)),
19854 : parser_errposition(pstate, pelem->location)));
19855 : }
19856 :
19857 : /*
19858 : * Preprocess the expression before checking for mutability.
19859 : * This is essential for the reasons described in
19860 : * contain_mutable_functions_after_planning. However, we call
19861 : * expression_planner for ourselves rather than using that
19862 : * function, because if constant-folding reduces the
19863 : * expression to a constant, we'd like to know that so we can
19864 : * complain below.
19865 : *
19866 : * Like contain_mutable_functions_after_planning, assume that
19867 : * expression_planner won't scribble on its input, so this
19868 : * won't affect the partexprs entry we saved above.
19869 : */
19870 266 : expr = (Node *) expression_planner((Expr *) expr);
19871 :
19872 : /*
19873 : * Partition expressions cannot contain mutable functions,
19874 : * because a given row must always map to the same partition
19875 : * as long as there is no change in the partition boundary
19876 : * structure.
19877 : */
19878 266 : if (contain_mutable_functions(expr))
19879 6 : ereport(ERROR,
19880 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19881 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
19882 :
19883 : /*
19884 : * While it is not exactly *wrong* for a partition expression
19885 : * to be a constant, it seems better to reject such keys.
19886 : */
19887 260 : if (IsA(expr, Const))
19888 12 : ereport(ERROR,
19889 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19890 : errmsg("cannot use constant expression as partition key")));
19891 : }
19892 : }
19893 :
19894 : /*
19895 : * Apply collation override if any
19896 : */
19897 5448 : if (pelem->collation)
19898 54 : attcollation = get_collation_oid(pelem->collation, false);
19899 :
19900 : /*
19901 : * Check we have a collation iff it's a collatable type. The only
19902 : * expected failures here are (1) COLLATE applied to a noncollatable
19903 : * type, or (2) partition expression had an unresolved collation. But
19904 : * we might as well code this to be a complete consistency check.
19905 : */
19906 5448 : if (type_is_collatable(atttype))
19907 : {
19908 640 : if (!OidIsValid(attcollation))
19909 0 : ereport(ERROR,
19910 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
19911 : errmsg("could not determine which collation to use for partition expression"),
19912 : errhint("Use the COLLATE clause to set the collation explicitly.")));
19913 : }
19914 : else
19915 : {
19916 4808 : if (OidIsValid(attcollation))
19917 0 : ereport(ERROR,
19918 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19919 : errmsg("collations are not supported by type %s",
19920 : format_type_be(atttype))));
19921 : }
19922 :
19923 5448 : partcollation[attn] = attcollation;
19924 :
19925 : /*
19926 : * Identify the appropriate operator class. For list and range
19927 : * partitioning, we use a btree operator class; hash partitioning uses
19928 : * a hash operator class.
19929 : */
19930 5448 : if (strategy == PARTITION_STRATEGY_HASH)
19931 314 : am_oid = HASH_AM_OID;
19932 : else
19933 5134 : am_oid = BTREE_AM_OID;
19934 :
19935 5448 : if (!pelem->opclass)
19936 : {
19937 5316 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19938 :
19939 5316 : if (!OidIsValid(partopclass[attn]))
19940 : {
19941 12 : if (strategy == PARTITION_STRATEGY_HASH)
19942 0 : ereport(ERROR,
19943 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19944 : errmsg("data type %s has no default operator class for access method \"%s\"",
19945 : format_type_be(atttype), "hash"),
19946 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19947 : else
19948 12 : ereport(ERROR,
19949 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19950 : errmsg("data type %s has no default operator class for access method \"%s\"",
19951 : format_type_be(atttype), "btree"),
19952 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19953 : }
19954 : }
19955 : else
19956 132 : partopclass[attn] = ResolveOpClass(pelem->opclass,
19957 : atttype,
19958 : am_oid == HASH_AM_OID ? "hash" : "btree",
19959 : am_oid);
19960 :
19961 5424 : attn++;
19962 : }
19963 4964 : }
19964 :
19965 : /*
19966 : * PartConstraintImpliedByRelConstraint
19967 : * Do scanrel's existing constraints imply the partition constraint?
19968 : *
19969 : * "Existing constraints" include its check constraints and column-level
19970 : * not-null constraints. partConstraint describes the partition constraint,
19971 : * in implicit-AND form.
19972 : */
19973 : bool
19974 3578 : PartConstraintImpliedByRelConstraint(Relation scanrel,
19975 : List *partConstraint)
19976 : {
19977 3578 : List *existConstraint = NIL;
19978 3578 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19979 : int i;
19980 :
19981 3578 : if (constr && constr->has_not_null)
19982 : {
19983 932 : int natts = scanrel->rd_att->natts;
19984 :
19985 2966 : for (i = 1; i <= natts; i++)
19986 : {
19987 2034 : CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
19988 :
19989 : /* invalid not-null constraint must be ignored here */
19990 2034 : if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
19991 : {
19992 1250 : Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
19993 1250 : NullTest *ntest = makeNode(NullTest);
19994 :
19995 1250 : ntest->arg = (Expr *) makeVar(1,
19996 : i,
19997 : wholeatt->atttypid,
19998 : wholeatt->atttypmod,
19999 : wholeatt->attcollation,
20000 : 0);
20001 1250 : ntest->nulltesttype = IS_NOT_NULL;
20002 :
20003 : /*
20004 : * argisrow=false is correct even for a composite column,
20005 : * because attnotnull does not represent a SQL-spec IS NOT
20006 : * NULL test in such a case, just IS DISTINCT FROM NULL.
20007 : */
20008 1250 : ntest->argisrow = false;
20009 1250 : ntest->location = -1;
20010 1250 : existConstraint = lappend(existConstraint, ntest);
20011 : }
20012 : }
20013 : }
20014 :
20015 3578 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20016 : }
20017 :
20018 : /*
20019 : * ConstraintImpliedByRelConstraint
20020 : * Do scanrel's existing constraints imply the given constraint?
20021 : *
20022 : * testConstraint is the constraint to validate. provenConstraint is a
20023 : * caller-provided list of conditions which this function may assume
20024 : * to be true. Both provenConstraint and testConstraint must be in
20025 : * implicit-AND form, must only contain immutable clauses, and must
20026 : * contain only Vars with varno = 1.
20027 : */
20028 : bool
20029 4816 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20030 : {
20031 4816 : List *existConstraint = list_copy(provenConstraint);
20032 4816 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20033 : int num_check,
20034 : i;
20035 :
20036 4816 : num_check = (constr != NULL) ? constr->num_check : 0;
20037 5336 : for (i = 0; i < num_check; i++)
20038 : {
20039 : Node *cexpr;
20040 :
20041 : /*
20042 : * If this constraint hasn't been fully validated yet, we must ignore
20043 : * it here.
20044 : */
20045 520 : if (!constr->check[i].ccvalid)
20046 6 : continue;
20047 :
20048 : /*
20049 : * NOT ENFORCED constraints are always marked as invalid, which should
20050 : * have been ignored.
20051 : */
20052 : Assert(constr->check[i].ccenforced);
20053 :
20054 514 : cexpr = stringToNode(constr->check[i].ccbin);
20055 :
20056 : /*
20057 : * Run each expression through const-simplification and
20058 : * canonicalization. It is necessary, because we will be comparing it
20059 : * to similarly-processed partition constraint expressions, and may
20060 : * fail to detect valid matches without this.
20061 : */
20062 514 : cexpr = eval_const_expressions(NULL, cexpr);
20063 514 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20064 :
20065 514 : existConstraint = list_concat(existConstraint,
20066 514 : make_ands_implicit((Expr *) cexpr));
20067 : }
20068 :
20069 : /*
20070 : * Try to make the proof. Since we are comparing CHECK constraints, we
20071 : * need to use weak implication, i.e., we assume existConstraint is
20072 : * not-false and try to prove the same for testConstraint.
20073 : *
20074 : * Note that predicate_implied_by assumes its first argument is known
20075 : * immutable. That should always be true for both NOT NULL and partition
20076 : * constraints, so we don't test it here.
20077 : */
20078 4816 : return predicate_implied_by(testConstraint, existConstraint, true);
20079 : }
20080 :
20081 : /*
20082 : * QueuePartitionConstraintValidation
20083 : *
20084 : * Add an entry to wqueue to have the given partition constraint validated by
20085 : * Phase 3, for the given relation, and all its children.
20086 : *
20087 : * We first verify whether the given constraint is implied by pre-existing
20088 : * relation constraints; if it is, there's no need to scan the table to
20089 : * validate, so don't queue in that case.
20090 : */
20091 : static void
20092 2948 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
20093 : List *partConstraint,
20094 : bool validate_default)
20095 : {
20096 : /*
20097 : * Based on the table's existing constraints, determine whether or not we
20098 : * may skip scanning the table.
20099 : */
20100 2948 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20101 : {
20102 92 : if (!validate_default)
20103 70 : ereport(DEBUG1,
20104 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20105 : RelationGetRelationName(scanrel))));
20106 : else
20107 22 : ereport(DEBUG1,
20108 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20109 : RelationGetRelationName(scanrel))));
20110 92 : return;
20111 : }
20112 :
20113 : /*
20114 : * Constraints proved insufficient. For plain relations, queue a
20115 : * validation item now; for partitioned tables, recurse to process each
20116 : * partition.
20117 : */
20118 2856 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20119 : {
20120 : AlteredTableInfo *tab;
20121 :
20122 : /* Grab a work queue entry. */
20123 2402 : tab = ATGetQueueEntry(wqueue, scanrel);
20124 : Assert(tab->partition_constraint == NULL);
20125 2402 : tab->partition_constraint = (Expr *) linitial(partConstraint);
20126 2402 : tab->validate_default = validate_default;
20127 : }
20128 454 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20129 : {
20130 406 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20131 : int i;
20132 :
20133 790 : for (i = 0; i < partdesc->nparts; i++)
20134 : {
20135 : Relation part_rel;
20136 : List *thisPartConstraint;
20137 :
20138 : /*
20139 : * This is the minimum lock we need to prevent deadlocks.
20140 : */
20141 384 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20142 :
20143 : /*
20144 : * Adjust the constraint for scanrel so that it matches this
20145 : * partition's attribute numbers.
20146 : */
20147 : thisPartConstraint =
20148 384 : map_partition_varattnos(partConstraint, 1,
20149 : part_rel, scanrel);
20150 :
20151 384 : QueuePartitionConstraintValidation(wqueue, part_rel,
20152 : thisPartConstraint,
20153 : validate_default);
20154 384 : table_close(part_rel, NoLock); /* keep lock till commit */
20155 : }
20156 : }
20157 : }
20158 :
20159 : /*
20160 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20161 : *
20162 : * Return the address of the newly attached partition.
20163 : */
20164 : static ObjectAddress
20165 2784 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
20166 : AlterTableUtilityContext *context)
20167 : {
20168 : Relation attachrel,
20169 : catalog;
20170 : List *attachrel_children;
20171 : List *partConstraint;
20172 : SysScanDesc scan;
20173 : ScanKeyData skey;
20174 : AttrNumber attno;
20175 : int natts;
20176 : TupleDesc tupleDesc;
20177 : ObjectAddress address;
20178 : const char *trigger_name;
20179 : Oid defaultPartOid;
20180 : List *partBoundConstraint;
20181 2784 : ParseState *pstate = make_parsestate(NULL);
20182 :
20183 2784 : pstate->p_sourcetext = context->queryString;
20184 :
20185 : /*
20186 : * We must lock the default partition if one exists, because attaching a
20187 : * new partition will change its partition constraint.
20188 : */
20189 : defaultPartOid =
20190 2784 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20191 2784 : if (OidIsValid(defaultPartOid))
20192 184 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20193 :
20194 2784 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20195 :
20196 : /*
20197 : * XXX I think it'd be a good idea to grab locks on all tables referenced
20198 : * by FKs at this point also.
20199 : */
20200 :
20201 : /*
20202 : * Must be owner of both parent and source table -- parent was checked by
20203 : * ATSimplePermissions call in ATPrepCmd
20204 : */
20205 2778 : ATSimplePermissions(AT_AttachPartition, attachrel,
20206 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
20207 :
20208 : /* A partition can only have one parent */
20209 2772 : if (attachrel->rd_rel->relispartition)
20210 6 : ereport(ERROR,
20211 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20212 : errmsg("\"%s\" is already a partition",
20213 : RelationGetRelationName(attachrel))));
20214 :
20215 2766 : if (OidIsValid(attachrel->rd_rel->reloftype))
20216 6 : ereport(ERROR,
20217 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20218 : errmsg("cannot attach a typed table as partition")));
20219 :
20220 : /*
20221 : * Table being attached should not already be part of inheritance; either
20222 : * as a child table...
20223 : */
20224 2760 : catalog = table_open(InheritsRelationId, AccessShareLock);
20225 2760 : ScanKeyInit(&skey,
20226 : Anum_pg_inherits_inhrelid,
20227 : BTEqualStrategyNumber, F_OIDEQ,
20228 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20229 2760 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20230 : NULL, 1, &skey);
20231 2760 : if (HeapTupleIsValid(systable_getnext(scan)))
20232 6 : ereport(ERROR,
20233 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20234 : errmsg("cannot attach inheritance child as partition")));
20235 2754 : systable_endscan(scan);
20236 :
20237 : /* ...or as a parent table (except the case when it is partitioned) */
20238 2754 : ScanKeyInit(&skey,
20239 : Anum_pg_inherits_inhparent,
20240 : BTEqualStrategyNumber, F_OIDEQ,
20241 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20242 2754 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20243 : 1, &skey);
20244 2754 : if (HeapTupleIsValid(systable_getnext(scan)) &&
20245 258 : attachrel->rd_rel->relkind == RELKIND_RELATION)
20246 6 : ereport(ERROR,
20247 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20248 : errmsg("cannot attach inheritance parent as partition")));
20249 2748 : systable_endscan(scan);
20250 2748 : table_close(catalog, AccessShareLock);
20251 :
20252 : /*
20253 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
20254 : * particular, this disallows making a rel a partition of itself.)
20255 : *
20256 : * We do that by checking if rel is a member of the list of attachrel's
20257 : * partitions provided the latter is partitioned at all. We want to avoid
20258 : * having to construct this list again, so we request the strongest lock
20259 : * on all partitions. We need the strongest lock, because we may decide
20260 : * to scan them if we find out that the table being attached (or its leaf
20261 : * partitions) may contain rows that violate the partition constraint. If
20262 : * the table has a constraint that would prevent such rows, which by
20263 : * definition is present in all the partitions, we need not scan the
20264 : * table, nor its partitions. But we cannot risk a deadlock by taking a
20265 : * weaker lock now and the stronger one only when needed.
20266 : */
20267 2748 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20268 : AccessExclusiveLock, NULL);
20269 2748 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20270 12 : ereport(ERROR,
20271 : (errcode(ERRCODE_DUPLICATE_TABLE),
20272 : errmsg("circular inheritance not allowed"),
20273 : errdetail("\"%s\" is already a child of \"%s\".",
20274 : RelationGetRelationName(rel),
20275 : RelationGetRelationName(attachrel))));
20276 :
20277 : /* If the parent is permanent, so must be all of its partitions. */
20278 2736 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20279 2694 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20280 6 : ereport(ERROR,
20281 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20282 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20283 : RelationGetRelationName(rel))));
20284 :
20285 : /* Temp parent cannot have a partition that is itself not a temp */
20286 2730 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20287 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20288 18 : ereport(ERROR,
20289 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20290 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20291 : RelationGetRelationName(rel))));
20292 :
20293 : /* If the parent is temp, it must belong to this session */
20294 2712 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20295 24 : !rel->rd_islocaltemp)
20296 0 : ereport(ERROR,
20297 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20298 : errmsg("cannot attach as partition of temporary relation of another session")));
20299 :
20300 : /* Ditto for the partition */
20301 2712 : if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20302 24 : !attachrel->rd_islocaltemp)
20303 0 : ereport(ERROR,
20304 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20305 : errmsg("cannot attach temporary relation of another session as partition")));
20306 :
20307 : /*
20308 : * Check if attachrel has any identity columns or any columns that aren't
20309 : * in the parent.
20310 : */
20311 2712 : tupleDesc = RelationGetDescr(attachrel);
20312 2712 : natts = tupleDesc->natts;
20313 9230 : for (attno = 1; attno <= natts; attno++)
20314 : {
20315 6560 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20316 6560 : char *attributeName = NameStr(attribute->attname);
20317 :
20318 : /* Ignore dropped */
20319 6560 : if (attribute->attisdropped)
20320 580 : continue;
20321 :
20322 5980 : if (attribute->attidentity)
20323 24 : ereport(ERROR,
20324 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20325 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20326 : RelationGetRelationName(attachrel), attributeName),
20327 : errdetail("The new partition may not contain an identity column."));
20328 :
20329 : /* Try to find the column in parent (matching on column name) */
20330 5956 : if (!SearchSysCacheExists2(ATTNAME,
20331 : ObjectIdGetDatum(RelationGetRelid(rel)),
20332 : CStringGetDatum(attributeName)))
20333 18 : ereport(ERROR,
20334 : (errcode(ERRCODE_DATATYPE_MISMATCH),
20335 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20336 : RelationGetRelationName(attachrel), attributeName,
20337 : RelationGetRelationName(rel)),
20338 : errdetail("The new partition may contain only the columns present in parent.")));
20339 : }
20340 :
20341 : /*
20342 : * If child_rel has row-level triggers with transition tables, we
20343 : * currently don't allow it to become a partition. See also prohibitions
20344 : * in ATExecAddInherit() and CreateTrigger().
20345 : */
20346 2670 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20347 2670 : if (trigger_name != NULL)
20348 6 : ereport(ERROR,
20349 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20350 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20351 : trigger_name, RelationGetRelationName(attachrel)),
20352 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
20353 :
20354 : /*
20355 : * Check that the new partition's bound is valid and does not overlap any
20356 : * of existing partitions of the parent - note that it does not return on
20357 : * error.
20358 : */
20359 2664 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
20360 : cmd->bound, pstate);
20361 :
20362 : /* OK to create inheritance. Rest of the checks performed there */
20363 2628 : CreateInheritance(attachrel, rel, true);
20364 :
20365 : /* Update the pg_class entry. */
20366 2520 : StorePartitionBound(attachrel, rel, cmd->bound);
20367 :
20368 : /* Ensure there exists a correct set of indexes in the partition. */
20369 2520 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20370 :
20371 : /* and triggers */
20372 2490 : CloneRowTriggersToPartition(rel, attachrel);
20373 :
20374 : /*
20375 : * Clone foreign key constraints. Callee is responsible for setting up
20376 : * for phase 3 constraint verification.
20377 : */
20378 2484 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
20379 :
20380 : /*
20381 : * Generate partition constraint from the partition bound specification.
20382 : * If the parent itself is a partition, make sure to include its
20383 : * constraint as well.
20384 : */
20385 2466 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20386 :
20387 : /*
20388 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20389 : * since it's needed later to construct the constraint expression for
20390 : * validating against the default partition, if any.
20391 : */
20392 2466 : partConstraint = list_concat_copy(partBoundConstraint,
20393 2466 : RelationGetPartitionQual(rel));
20394 :
20395 : /* Skip validation if there are no constraints to validate. */
20396 2466 : if (partConstraint)
20397 : {
20398 : /*
20399 : * Run the partition quals through const-simplification similar to
20400 : * check constraints. We skip canonicalize_qual, though, because
20401 : * partition quals should be in canonical form already.
20402 : */
20403 : partConstraint =
20404 2416 : (List *) eval_const_expressions(NULL,
20405 : (Node *) partConstraint);
20406 :
20407 : /* XXX this sure looks wrong */
20408 2416 : partConstraint = list_make1(make_ands_explicit(partConstraint));
20409 :
20410 : /*
20411 : * Adjust the generated constraint to match this partition's attribute
20412 : * numbers.
20413 : */
20414 2416 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20415 : rel);
20416 :
20417 : /* Validate partition constraints against the table being attached. */
20418 2416 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20419 : false);
20420 : }
20421 :
20422 : /*
20423 : * If we're attaching a partition other than the default partition and a
20424 : * default one exists, then that partition's partition constraint changes,
20425 : * so add an entry to the work queue to validate it, too. (We must not do
20426 : * this when the partition being attached is the default one; we already
20427 : * did it above!)
20428 : */
20429 2466 : if (OidIsValid(defaultPartOid))
20430 : {
20431 : Relation defaultrel;
20432 : List *defPartConstraint;
20433 :
20434 : Assert(!cmd->bound->is_default);
20435 :
20436 : /* we already hold a lock on the default partition */
20437 148 : defaultrel = table_open(defaultPartOid, NoLock);
20438 : defPartConstraint =
20439 148 : get_proposed_default_constraint(partBoundConstraint);
20440 :
20441 : /*
20442 : * Map the Vars in the constraint expression from rel's attnos to
20443 : * defaultrel's.
20444 : */
20445 : defPartConstraint =
20446 148 : map_partition_varattnos(defPartConstraint,
20447 : 1, defaultrel, rel);
20448 148 : QueuePartitionConstraintValidation(wqueue, defaultrel,
20449 : defPartConstraint, true);
20450 :
20451 : /* keep our lock until commit. */
20452 148 : table_close(defaultrel, NoLock);
20453 : }
20454 :
20455 2466 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20456 :
20457 : /*
20458 : * If the partition we just attached is partitioned itself, invalidate
20459 : * relcache for all descendent partitions too to ensure that their
20460 : * rd_partcheck expression trees are rebuilt; partitions already locked at
20461 : * the beginning of this function.
20462 : */
20463 2466 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20464 : {
20465 : ListCell *l;
20466 :
20467 1100 : foreach(l, attachrel_children)
20468 : {
20469 720 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
20470 : }
20471 : }
20472 :
20473 : /* keep our lock until commit */
20474 2466 : table_close(attachrel, NoLock);
20475 :
20476 2466 : return address;
20477 : }
20478 :
20479 : /*
20480 : * AttachPartitionEnsureIndexes
20481 : * subroutine for ATExecAttachPartition to create/match indexes
20482 : *
20483 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20484 : * PARTITION: every partition must have an index attached to each index on the
20485 : * partitioned table.
20486 : */
20487 : static void
20488 2520 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
20489 : {
20490 : List *idxes;
20491 : List *attachRelIdxs;
20492 : Relation *attachrelIdxRels;
20493 : IndexInfo **attachInfos;
20494 : ListCell *cell;
20495 : MemoryContext cxt;
20496 : MemoryContext oldcxt;
20497 :
20498 2520 : cxt = AllocSetContextCreate(CurrentMemoryContext,
20499 : "AttachPartitionEnsureIndexes",
20500 : ALLOCSET_DEFAULT_SIZES);
20501 2520 : oldcxt = MemoryContextSwitchTo(cxt);
20502 :
20503 2520 : idxes = RelationGetIndexList(rel);
20504 2520 : attachRelIdxs = RelationGetIndexList(attachrel);
20505 2520 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20506 2520 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20507 :
20508 : /* Build arrays of all existing indexes and their IndexInfos */
20509 5410 : foreach_oid(cldIdxId, attachRelIdxs)
20510 : {
20511 370 : int i = foreach_current_index(cldIdxId);
20512 :
20513 370 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20514 370 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20515 : }
20516 :
20517 : /*
20518 : * If we're attaching a foreign table, we must fail if any of the indexes
20519 : * is a constraint index; otherwise, there's nothing to do here. Do this
20520 : * before starting work, to avoid wasting the effort of building a few
20521 : * non-unique indexes before coming across a unique one.
20522 : */
20523 2520 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20524 : {
20525 86 : foreach(cell, idxes)
20526 : {
20527 36 : Oid idx = lfirst_oid(cell);
20528 36 : Relation idxRel = index_open(idx, AccessShareLock);
20529 :
20530 36 : if (idxRel->rd_index->indisunique ||
20531 24 : idxRel->rd_index->indisprimary)
20532 12 : ereport(ERROR,
20533 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20534 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20535 : RelationGetRelationName(attachrel),
20536 : RelationGetRelationName(rel)),
20537 : errdetail("Partitioned table \"%s\" contains unique indexes.",
20538 : RelationGetRelationName(rel))));
20539 24 : index_close(idxRel, AccessShareLock);
20540 : }
20541 :
20542 50 : goto out;
20543 : }
20544 :
20545 : /*
20546 : * For each index on the partitioned table, find a matching one in the
20547 : * partition-to-be; if one is not found, create one.
20548 : */
20549 2878 : foreach(cell, idxes)
20550 : {
20551 438 : Oid idx = lfirst_oid(cell);
20552 438 : Relation idxRel = index_open(idx, AccessShareLock);
20553 : IndexInfo *info;
20554 : AttrMap *attmap;
20555 438 : bool found = false;
20556 : Oid constraintOid;
20557 :
20558 : /*
20559 : * Ignore indexes in the partitioned table other than partitioned
20560 : * indexes.
20561 : */
20562 438 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20563 : {
20564 0 : index_close(idxRel, AccessShareLock);
20565 0 : continue;
20566 : }
20567 :
20568 : /* construct an indexinfo to compare existing indexes against */
20569 438 : info = BuildIndexInfo(idxRel);
20570 438 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20571 : RelationGetDescr(rel),
20572 : false);
20573 438 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
20574 :
20575 : /*
20576 : * Scan the list of existing indexes in the partition-to-be, and mark
20577 : * the first matching, valid, unattached one we find, if any, as
20578 : * partition of the parent index. If we find one, we're done.
20579 : */
20580 498 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20581 : {
20582 262 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20583 262 : Oid cldConstrOid = InvalidOid;
20584 :
20585 : /* does this index have a parent? if so, can't use it */
20586 262 : if (attachrelIdxRels[i]->rd_rel->relispartition)
20587 12 : continue;
20588 :
20589 : /* If this index is invalid, can't use it */
20590 250 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
20591 6 : continue;
20592 :
20593 244 : if (CompareIndexInfo(attachInfos[i], info,
20594 244 : attachrelIdxRels[i]->rd_indcollation,
20595 244 : idxRel->rd_indcollation,
20596 244 : attachrelIdxRels[i]->rd_opfamily,
20597 244 : idxRel->rd_opfamily,
20598 : attmap))
20599 : {
20600 : /*
20601 : * If this index is being created in the parent because of a
20602 : * constraint, then the child needs to have a constraint also,
20603 : * so look for one. If there is no such constraint, this
20604 : * index is no good, so keep looking.
20605 : */
20606 208 : if (OidIsValid(constraintOid))
20607 : {
20608 : cldConstrOid =
20609 110 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
20610 : cldIdxId);
20611 : /* no dice */
20612 110 : if (!OidIsValid(cldConstrOid))
20613 6 : continue;
20614 :
20615 : /* Ensure they're both the same type of constraint */
20616 208 : if (get_constraint_type(constraintOid) !=
20617 104 : get_constraint_type(cldConstrOid))
20618 0 : continue;
20619 : }
20620 :
20621 : /* bingo. */
20622 202 : IndexSetParentIndex(attachrelIdxRels[i], idx);
20623 202 : if (OidIsValid(constraintOid))
20624 104 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20625 : RelationGetRelid(attachrel));
20626 202 : found = true;
20627 :
20628 202 : CommandCounterIncrement();
20629 202 : break;
20630 : }
20631 : }
20632 :
20633 : /*
20634 : * If no suitable index was found in the partition-to-be, create one
20635 : * now. Note that if this is a PK, not-null constraints must already
20636 : * exist.
20637 : */
20638 438 : if (!found)
20639 : {
20640 : IndexStmt *stmt;
20641 : Oid conOid;
20642 :
20643 236 : stmt = generateClonedIndexStmt(NULL,
20644 : idxRel, attmap,
20645 : &conOid);
20646 236 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
20647 : RelationGetRelid(idxRel),
20648 : conOid,
20649 : -1,
20650 : true, false, false, false, false);
20651 : }
20652 :
20653 420 : index_close(idxRel, AccessShareLock);
20654 : }
20655 :
20656 2490 : out:
20657 : /* Clean up. */
20658 2848 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20659 358 : index_close(attachrelIdxRels[i], AccessShareLock);
20660 2490 : MemoryContextSwitchTo(oldcxt);
20661 2490 : MemoryContextDelete(cxt);
20662 2490 : }
20663 :
20664 : /*
20665 : * CloneRowTriggersToPartition
20666 : * subroutine for ATExecAttachPartition/DefineRelation to create row
20667 : * triggers on partitions
20668 : */
20669 : static void
20670 2910 : CloneRowTriggersToPartition(Relation parent, Relation partition)
20671 : {
20672 : Relation pg_trigger;
20673 : ScanKeyData key;
20674 : SysScanDesc scan;
20675 : HeapTuple tuple;
20676 : MemoryContext perTupCxt;
20677 :
20678 2910 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20679 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20680 2910 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20681 2910 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20682 : true, NULL, 1, &key);
20683 :
20684 2910 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
20685 : "clone trig", ALLOCSET_SMALL_SIZES);
20686 :
20687 4550 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20688 : {
20689 1646 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20690 : CreateTrigStmt *trigStmt;
20691 1646 : Node *qual = NULL;
20692 : Datum value;
20693 : bool isnull;
20694 1646 : List *cols = NIL;
20695 1646 : List *trigargs = NIL;
20696 : MemoryContext oldcxt;
20697 :
20698 : /*
20699 : * Ignore statement-level triggers; those are not cloned.
20700 : */
20701 1646 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20702 1490 : continue;
20703 :
20704 : /*
20705 : * Don't clone internal triggers, because the constraint cloning code
20706 : * will.
20707 : */
20708 1646 : if (trigForm->tgisinternal)
20709 1490 : continue;
20710 :
20711 : /*
20712 : * Complain if we find an unexpected trigger type.
20713 : */
20714 156 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20715 138 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
20716 0 : elog(ERROR, "unexpected trigger \"%s\" found",
20717 : NameStr(trigForm->tgname));
20718 :
20719 : /* Use short-lived context for CREATE TRIGGER */
20720 156 : oldcxt = MemoryContextSwitchTo(perTupCxt);
20721 :
20722 : /*
20723 : * If there is a WHEN clause, generate a 'cooked' version of it that's
20724 : * appropriate for the partition.
20725 : */
20726 156 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20727 : RelationGetDescr(pg_trigger), &isnull);
20728 156 : if (!isnull)
20729 : {
20730 6 : qual = stringToNode(TextDatumGetCString(value));
20731 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20732 : partition, parent);
20733 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20734 : partition, parent);
20735 : }
20736 :
20737 : /*
20738 : * If there is a column list, transform it to a list of column names.
20739 : * Note we don't need to map this list in any way ...
20740 : */
20741 156 : if (trigForm->tgattr.dim1 > 0)
20742 : {
20743 : int i;
20744 :
20745 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
20746 : {
20747 : Form_pg_attribute col;
20748 :
20749 6 : col = TupleDescAttr(parent->rd_att,
20750 6 : trigForm->tgattr.values[i] - 1);
20751 6 : cols = lappend(cols,
20752 6 : makeString(pstrdup(NameStr(col->attname))));
20753 : }
20754 : }
20755 :
20756 : /* Reconstruct trigger arguments list. */
20757 156 : if (trigForm->tgnargs > 0)
20758 : {
20759 : char *p;
20760 :
20761 12 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20762 : RelationGetDescr(pg_trigger), &isnull);
20763 12 : if (isnull)
20764 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20765 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
20766 :
20767 12 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20768 :
20769 36 : for (int i = 0; i < trigForm->tgnargs; i++)
20770 : {
20771 24 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
20772 24 : p += strlen(p) + 1;
20773 : }
20774 : }
20775 :
20776 156 : trigStmt = makeNode(CreateTrigStmt);
20777 156 : trigStmt->replace = false;
20778 156 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20779 156 : trigStmt->trigname = NameStr(trigForm->tgname);
20780 156 : trigStmt->relation = NULL;
20781 156 : trigStmt->funcname = NULL; /* passed separately */
20782 156 : trigStmt->args = trigargs;
20783 156 : trigStmt->row = true;
20784 156 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20785 156 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20786 156 : trigStmt->columns = cols;
20787 156 : trigStmt->whenClause = NULL; /* passed separately */
20788 156 : trigStmt->transitionRels = NIL; /* not supported at present */
20789 156 : trigStmt->deferrable = trigForm->tgdeferrable;
20790 156 : trigStmt->initdeferred = trigForm->tginitdeferred;
20791 156 : trigStmt->constrrel = NULL; /* passed separately */
20792 :
20793 156 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20794 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20795 : trigForm->tgfoid, trigForm->oid, qual,
20796 156 : false, true, trigForm->tgenabled);
20797 :
20798 150 : MemoryContextSwitchTo(oldcxt);
20799 150 : MemoryContextReset(perTupCxt);
20800 : }
20801 :
20802 2904 : MemoryContextDelete(perTupCxt);
20803 :
20804 2904 : systable_endscan(scan);
20805 2904 : table_close(pg_trigger, RowExclusiveLock);
20806 2904 : }
20807 :
20808 : /*
20809 : * ALTER TABLE DETACH PARTITION
20810 : *
20811 : * Return the address of the relation that is no longer a partition of rel.
20812 : *
20813 : * If concurrent mode is requested, we run in two transactions. A side-
20814 : * effect is that this command cannot run in a multi-part ALTER TABLE.
20815 : * Currently, that's enforced by the grammar.
20816 : *
20817 : * The strategy for concurrency is to first modify the partition's
20818 : * pg_inherit catalog row to make it visible to everyone that the
20819 : * partition is detached, lock the partition against writes, and commit
20820 : * the transaction; anyone who requests the partition descriptor from
20821 : * that point onwards has to ignore such a partition. In a second
20822 : * transaction, we wait until all transactions that could have seen the
20823 : * partition as attached are gone, then we remove the rest of partition
20824 : * metadata (pg_inherits and pg_class.relpartbounds).
20825 : */
20826 : static ObjectAddress
20827 558 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
20828 : RangeVar *name, bool concurrent)
20829 : {
20830 : Relation partRel;
20831 : ObjectAddress address;
20832 : Oid defaultPartOid;
20833 :
20834 : /*
20835 : * We must lock the default partition, because detaching this partition
20836 : * will change its partition constraint.
20837 : */
20838 : defaultPartOid =
20839 558 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20840 558 : if (OidIsValid(defaultPartOid))
20841 : {
20842 : /*
20843 : * Concurrent detaching when a default partition exists is not
20844 : * supported. The main problem is that the default partition
20845 : * constraint would change. And there's a definitional problem: what
20846 : * should happen to the tuples that are being inserted that belong to
20847 : * the partition being detached? Putting them on the partition being
20848 : * detached would be wrong, since they'd become "lost" after the
20849 : * detaching completes but we cannot put them in the default partition
20850 : * either until we alter its partition constraint.
20851 : *
20852 : * I think we could solve this problem if we effected the constraint
20853 : * change before committing the first transaction. But the lock would
20854 : * have to remain AEL and it would cause concurrent query planning to
20855 : * be blocked, so changing it that way would be even worse.
20856 : */
20857 106 : if (concurrent)
20858 12 : ereport(ERROR,
20859 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20860 : errmsg("cannot detach partitions concurrently when a default partition exists")));
20861 94 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20862 : }
20863 :
20864 : /*
20865 : * In concurrent mode, the partition is locked with share-update-exclusive
20866 : * in the first transaction. This allows concurrent transactions to be
20867 : * doing DML to the partition.
20868 : */
20869 546 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20870 : AccessExclusiveLock);
20871 :
20872 : /*
20873 : * Check inheritance conditions and either delete the pg_inherits row (in
20874 : * non-concurrent mode) or just set the inhdetachpending flag.
20875 : */
20876 534 : if (!concurrent)
20877 388 : RemoveInheritance(partRel, rel, false);
20878 : else
20879 146 : MarkInheritDetached(partRel, rel);
20880 :
20881 : /*
20882 : * Ensure that foreign keys still hold after this detach. This keeps
20883 : * locks on the referencing tables, which prevents concurrent transactions
20884 : * from adding rows that we wouldn't see. For this to work in concurrent
20885 : * mode, it is critical that the partition appears as no longer attached
20886 : * for the RI queries as soon as the first transaction commits.
20887 : */
20888 514 : ATDetachCheckNoForeignKeyRefs(partRel);
20889 :
20890 : /*
20891 : * Concurrent mode has to work harder; first we add a new constraint to
20892 : * the partition that matches the partition constraint. Then we close our
20893 : * existing transaction, and in a new one wait for all processes to catch
20894 : * up on the catalog updates we've done so far; at that point we can
20895 : * complete the operation.
20896 : */
20897 480 : if (concurrent)
20898 : {
20899 : Oid partrelid,
20900 : parentrelid;
20901 : LOCKTAG tag;
20902 : char *parentrelname;
20903 : char *partrelname;
20904 :
20905 : /*
20906 : * Add a new constraint to the partition being detached, which
20907 : * supplants the partition constraint (unless there is one already).
20908 : */
20909 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
20910 :
20911 : /*
20912 : * We're almost done now; the only traces that remain are the
20913 : * pg_inherits tuple and the partition's relpartbounds. Before we can
20914 : * remove those, we need to wait until all transactions that know that
20915 : * this is a partition are gone.
20916 : */
20917 :
20918 : /*
20919 : * Remember relation OIDs to re-acquire them later; and relation names
20920 : * too, for error messages if something is dropped in between.
20921 : */
20922 140 : partrelid = RelationGetRelid(partRel);
20923 140 : parentrelid = RelationGetRelid(rel);
20924 140 : parentrelname = MemoryContextStrdup(PortalContext,
20925 140 : RelationGetRelationName(rel));
20926 140 : partrelname = MemoryContextStrdup(PortalContext,
20927 140 : RelationGetRelationName(partRel));
20928 :
20929 : /* Invalidate relcache entries for the parent -- must be before close */
20930 140 : CacheInvalidateRelcache(rel);
20931 :
20932 140 : table_close(partRel, NoLock);
20933 140 : table_close(rel, NoLock);
20934 140 : tab->rel = NULL;
20935 :
20936 : /* Make updated catalog entry visible */
20937 140 : PopActiveSnapshot();
20938 140 : CommitTransactionCommand();
20939 :
20940 140 : StartTransactionCommand();
20941 :
20942 : /*
20943 : * Now wait. This ensures that all queries that were planned
20944 : * including the partition are finished before we remove the rest of
20945 : * catalog entries. We don't need or indeed want to acquire this
20946 : * lock, though -- that would block later queries.
20947 : *
20948 : * We don't need to concern ourselves with waiting for a lock on the
20949 : * partition itself, since we will acquire AccessExclusiveLock below.
20950 : */
20951 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20952 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
20953 :
20954 : /*
20955 : * Now acquire locks in both relations again. Note they may have been
20956 : * removed in the meantime, so care is required.
20957 : */
20958 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20959 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
20960 :
20961 : /* If the relations aren't there, something bad happened; bail out */
20962 90 : if (rel == NULL)
20963 : {
20964 0 : if (partRel != NULL) /* shouldn't happen */
20965 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20966 : partrelname);
20967 0 : ereport(ERROR,
20968 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20969 : errmsg("partitioned table \"%s\" was removed concurrently",
20970 : parentrelname)));
20971 : }
20972 90 : if (partRel == NULL)
20973 0 : ereport(ERROR,
20974 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20975 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
20976 :
20977 90 : tab->rel = rel;
20978 : }
20979 :
20980 : /* Do the final part of detaching */
20981 430 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20982 :
20983 428 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20984 :
20985 : /* keep our lock until commit */
20986 428 : table_close(partRel, NoLock);
20987 :
20988 428 : return address;
20989 : }
20990 :
20991 : /*
20992 : * Second part of ALTER TABLE .. DETACH.
20993 : *
20994 : * This is separate so that it can be run independently when the second
20995 : * transaction of the concurrent algorithm fails (crash or abort).
20996 : */
20997 : static void
20998 444 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
20999 : Oid defaultPartOid)
21000 : {
21001 : Relation classRel;
21002 : List *fks;
21003 : ListCell *cell;
21004 : List *indexes;
21005 : Datum new_val[Natts_pg_class];
21006 : bool new_null[Natts_pg_class],
21007 : new_repl[Natts_pg_class];
21008 : HeapTuple tuple,
21009 : newtuple;
21010 444 : Relation trigrel = NULL;
21011 444 : List *fkoids = NIL;
21012 :
21013 444 : if (concurrent)
21014 : {
21015 : /*
21016 : * We can remove the pg_inherits row now. (In the non-concurrent case,
21017 : * this was already done).
21018 : */
21019 104 : RemoveInheritance(partRel, rel, true);
21020 : }
21021 :
21022 : /* Drop any triggers that were cloned on creation/attach. */
21023 444 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
21024 :
21025 : /*
21026 : * Detach any foreign keys that are inherited. This includes creating
21027 : * additional action triggers.
21028 : */
21029 444 : fks = copyObject(RelationGetFKeyList(partRel));
21030 444 : if (fks != NIL)
21031 72 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21032 :
21033 : /*
21034 : * It's possible that the partition being detached has a foreign key that
21035 : * references a partitioned table. When that happens, there are multiple
21036 : * pg_constraint rows for the partition: one points to the partitioned
21037 : * table itself, while the others point to each of its partitions. Only
21038 : * the topmost one is to be considered here; the child constraints must be
21039 : * left alone, because conceptually those aren't coming from our parent
21040 : * partitioned table, but from this partition itself.
21041 : *
21042 : * We implement this by collecting all the constraint OIDs in a first scan
21043 : * of the FK array, and skipping in the loop below those constraints whose
21044 : * parents are listed here.
21045 : */
21046 1044 : foreach_node(ForeignKeyCacheInfo, fk, fks)
21047 156 : fkoids = lappend_oid(fkoids, fk->conoid);
21048 :
21049 600 : foreach(cell, fks)
21050 : {
21051 156 : ForeignKeyCacheInfo *fk = lfirst(cell);
21052 : HeapTuple contup;
21053 : Form_pg_constraint conform;
21054 :
21055 156 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21056 156 : if (!HeapTupleIsValid(contup))
21057 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21058 156 : conform = (Form_pg_constraint) GETSTRUCT(contup);
21059 :
21060 : /*
21061 : * Consider only inherited foreign keys, and only if their parents
21062 : * aren't in the list.
21063 : */
21064 156 : if (conform->contype != CONSTRAINT_FOREIGN ||
21065 288 : !OidIsValid(conform->conparentid) ||
21066 132 : list_member_oid(fkoids, conform->conparentid))
21067 : {
21068 66 : ReleaseSysCache(contup);
21069 66 : continue;
21070 : }
21071 :
21072 : /*
21073 : * The constraint on this table must be marked no longer a child of
21074 : * the parent's constraint, as do its check triggers.
21075 : */
21076 90 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
21077 :
21078 : /*
21079 : * Also, look up the partition's "check" triggers corresponding to the
21080 : * ENFORCED constraint being detached and detach them from the parent
21081 : * triggers. NOT ENFORCED constraints do not have these triggers;
21082 : * therefore, this step is not needed.
21083 : */
21084 90 : if (fk->conenforced)
21085 : {
21086 : Oid insertTriggerOid,
21087 : updateTriggerOid;
21088 :
21089 90 : GetForeignKeyCheckTriggers(trigrel,
21090 : fk->conoid, fk->confrelid, fk->conrelid,
21091 : &insertTriggerOid, &updateTriggerOid);
21092 : Assert(OidIsValid(insertTriggerOid));
21093 90 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21094 : RelationGetRelid(partRel));
21095 : Assert(OidIsValid(updateTriggerOid));
21096 90 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21097 : RelationGetRelid(partRel));
21098 : }
21099 :
21100 : /*
21101 : * Lastly, create the action triggers on the referenced table, using
21102 : * addFkRecurseReferenced, which requires some elaborate setup (so put
21103 : * it in a separate block). While at it, if the table is partitioned,
21104 : * that function will recurse to create the pg_constraint rows and
21105 : * action triggers for each partition.
21106 : *
21107 : * Note there's no need to do addFkConstraint() here, because the
21108 : * pg_constraint row already exists.
21109 : */
21110 : {
21111 : Constraint *fkconstraint;
21112 : int numfks;
21113 : AttrNumber conkey[INDEX_MAX_KEYS];
21114 : AttrNumber confkey[INDEX_MAX_KEYS];
21115 : Oid conpfeqop[INDEX_MAX_KEYS];
21116 : Oid conppeqop[INDEX_MAX_KEYS];
21117 : Oid conffeqop[INDEX_MAX_KEYS];
21118 : int numfkdelsetcols;
21119 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21120 : Relation refdRel;
21121 :
21122 90 : DeconstructFkConstraintRow(contup,
21123 : &numfks,
21124 : conkey,
21125 : confkey,
21126 : conpfeqop,
21127 : conppeqop,
21128 : conffeqop,
21129 : &numfkdelsetcols,
21130 : confdelsetcols);
21131 :
21132 : /* Create a synthetic node we'll use throughout */
21133 90 : fkconstraint = makeNode(Constraint);
21134 90 : fkconstraint->contype = CONSTRAINT_FOREIGN;
21135 90 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
21136 90 : fkconstraint->deferrable = conform->condeferrable;
21137 90 : fkconstraint->initdeferred = conform->condeferred;
21138 90 : fkconstraint->is_enforced = conform->conenforced;
21139 90 : fkconstraint->skip_validation = true;
21140 90 : fkconstraint->initially_valid = conform->convalidated;
21141 : /* a few irrelevant fields omitted here */
21142 90 : fkconstraint->pktable = NULL;
21143 90 : fkconstraint->fk_attrs = NIL;
21144 90 : fkconstraint->pk_attrs = NIL;
21145 90 : fkconstraint->fk_matchtype = conform->confmatchtype;
21146 90 : fkconstraint->fk_upd_action = conform->confupdtype;
21147 90 : fkconstraint->fk_del_action = conform->confdeltype;
21148 90 : fkconstraint->fk_del_set_cols = NIL;
21149 90 : fkconstraint->old_conpfeqop = NIL;
21150 90 : fkconstraint->old_pktable_oid = InvalidOid;
21151 90 : fkconstraint->location = -1;
21152 :
21153 : /* set up colnames, used to generate the constraint name */
21154 228 : for (int i = 0; i < numfks; i++)
21155 : {
21156 : Form_pg_attribute att;
21157 :
21158 138 : att = TupleDescAttr(RelationGetDescr(partRel),
21159 138 : conkey[i] - 1);
21160 :
21161 138 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21162 138 : makeString(NameStr(att->attname)));
21163 : }
21164 :
21165 90 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
21166 :
21167 90 : addFkRecurseReferenced(fkconstraint, partRel,
21168 : refdRel,
21169 : conform->conindid,
21170 : fk->conoid,
21171 : numfks,
21172 : confkey,
21173 : conkey,
21174 : conpfeqop,
21175 : conppeqop,
21176 : conffeqop,
21177 : numfkdelsetcols,
21178 : confdelsetcols,
21179 : true,
21180 : InvalidOid, InvalidOid,
21181 90 : conform->conperiod);
21182 90 : table_close(refdRel, NoLock); /* keep lock till end of xact */
21183 : }
21184 :
21185 90 : ReleaseSysCache(contup);
21186 : }
21187 444 : list_free_deep(fks);
21188 444 : if (trigrel)
21189 72 : table_close(trigrel, RowExclusiveLock);
21190 :
21191 : /*
21192 : * Any sub-constraints that are in the referenced-side of a larger
21193 : * constraint have to be removed. This partition is no longer part of the
21194 : * key space of the constraint.
21195 : */
21196 480 : foreach(cell, GetParentedForeignKeyRefs(partRel))
21197 : {
21198 38 : Oid constrOid = lfirst_oid(cell);
21199 : ObjectAddress constraint;
21200 :
21201 38 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21202 38 : deleteDependencyRecordsForClass(ConstraintRelationId,
21203 : constrOid,
21204 : ConstraintRelationId,
21205 : DEPENDENCY_INTERNAL);
21206 38 : CommandCounterIncrement();
21207 :
21208 38 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21209 38 : performDeletion(&constraint, DROP_RESTRICT, 0);
21210 : }
21211 :
21212 : /* Now we can detach indexes */
21213 442 : indexes = RelationGetIndexList(partRel);
21214 624 : foreach(cell, indexes)
21215 : {
21216 182 : Oid idxid = lfirst_oid(cell);
21217 : Oid parentidx;
21218 : Relation idx;
21219 : Oid constrOid;
21220 : Oid parentConstrOid;
21221 :
21222 182 : if (!has_superclass(idxid))
21223 12 : continue;
21224 :
21225 170 : parentidx = get_partition_parent(idxid, false);
21226 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21227 :
21228 170 : idx = index_open(idxid, AccessExclusiveLock);
21229 170 : IndexSetParentIndex(idx, InvalidOid);
21230 :
21231 : /*
21232 : * If there's a constraint associated with the index, detach it too.
21233 : * Careful: it is possible for a constraint index in a partition to be
21234 : * the child of a non-constraint index, so verify whether the parent
21235 : * index does actually have a constraint.
21236 : */
21237 170 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
21238 : idxid);
21239 170 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
21240 : parentidx);
21241 170 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21242 72 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21243 :
21244 170 : index_close(idx, NoLock);
21245 : }
21246 :
21247 : /* Update pg_class tuple */
21248 442 : classRel = table_open(RelationRelationId, RowExclusiveLock);
21249 442 : tuple = SearchSysCacheCopy1(RELOID,
21250 : ObjectIdGetDatum(RelationGetRelid(partRel)));
21251 442 : if (!HeapTupleIsValid(tuple))
21252 0 : elog(ERROR, "cache lookup failed for relation %u",
21253 : RelationGetRelid(partRel));
21254 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21255 :
21256 : /* Clear relpartbound and reset relispartition */
21257 442 : memset(new_val, 0, sizeof(new_val));
21258 442 : memset(new_null, false, sizeof(new_null));
21259 442 : memset(new_repl, false, sizeof(new_repl));
21260 442 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21261 442 : new_null[Anum_pg_class_relpartbound - 1] = true;
21262 442 : new_repl[Anum_pg_class_relpartbound - 1] = true;
21263 442 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21264 : new_val, new_null, new_repl);
21265 :
21266 442 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21267 442 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21268 442 : heap_freetuple(newtuple);
21269 442 : table_close(classRel, RowExclusiveLock);
21270 :
21271 : /*
21272 : * Drop identity property from all identity columns of partition.
21273 : */
21274 1258 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21275 : {
21276 816 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21277 :
21278 816 : if (!attr->attisdropped && attr->attidentity)
21279 6 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21280 : AccessExclusiveLock, true, true);
21281 : }
21282 :
21283 442 : if (OidIsValid(defaultPartOid))
21284 : {
21285 : /*
21286 : * If the relation being detached is the default partition itself,
21287 : * remove it from the parent's pg_partitioned_table entry.
21288 : *
21289 : * If not, we must invalidate default partition's relcache entry, as
21290 : * in StorePartitionBound: its partition constraint depends on every
21291 : * other partition's partition constraint.
21292 : */
21293 46 : if (RelationGetRelid(partRel) == defaultPartOid)
21294 2 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
21295 : else
21296 44 : CacheInvalidateRelcacheByRelid(defaultPartOid);
21297 : }
21298 :
21299 : /*
21300 : * Invalidate the parent's relcache so that the partition is no longer
21301 : * included in its partition descriptor.
21302 : */
21303 442 : CacheInvalidateRelcache(rel);
21304 :
21305 : /*
21306 : * If the partition we just detached is partitioned itself, invalidate
21307 : * relcache for all descendent partitions too to ensure that their
21308 : * rd_partcheck expression trees are rebuilt; must lock partitions before
21309 : * doing so, using the same lockmode as what partRel has been locked with
21310 : * by the caller.
21311 : */
21312 442 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21313 : {
21314 : List *children;
21315 :
21316 56 : children = find_all_inheritors(RelationGetRelid(partRel),
21317 : AccessExclusiveLock, NULL);
21318 180 : foreach(cell, children)
21319 : {
21320 124 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
21321 : }
21322 : }
21323 442 : }
21324 :
21325 : /*
21326 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21327 : *
21328 : * To use when a DETACH PARTITION command previously did not run to
21329 : * completion; this completes the detaching process.
21330 : */
21331 : static ObjectAddress
21332 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
21333 : {
21334 : Relation partRel;
21335 : ObjectAddress address;
21336 14 : Snapshot snap = GetActiveSnapshot();
21337 :
21338 14 : partRel = table_openrv(name, AccessExclusiveLock);
21339 :
21340 : /*
21341 : * Wait until existing snapshots are gone. This is important if the
21342 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21343 : * user could immediately run DETACH FINALIZE without actually waiting for
21344 : * existing transactions. We must not complete the detach action until
21345 : * all such queries are complete (otherwise we would present them with an
21346 : * inconsistent view of catalogs).
21347 : */
21348 14 : WaitForOlderSnapshots(snap->xmin, false);
21349 :
21350 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21351 :
21352 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21353 :
21354 14 : table_close(partRel, NoLock);
21355 :
21356 14 : return address;
21357 : }
21358 :
21359 : /*
21360 : * DetachAddConstraintIfNeeded
21361 : * Subroutine for ATExecDetachPartition. Create a constraint that
21362 : * takes the place of the partition constraint, but avoid creating
21363 : * a dupe if a constraint already exists which implies the needed
21364 : * constraint.
21365 : */
21366 : static void
21367 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
21368 : {
21369 : List *constraintExpr;
21370 :
21371 140 : constraintExpr = RelationGetPartitionQual(partRel);
21372 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
21373 :
21374 : /*
21375 : * Avoid adding a new constraint if the needed constraint is implied by an
21376 : * existing constraint
21377 : */
21378 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
21379 : {
21380 : AlteredTableInfo *tab;
21381 : Constraint *n;
21382 :
21383 134 : tab = ATGetQueueEntry(wqueue, partRel);
21384 :
21385 : /* Add constraint on partition, equivalent to the partition constraint */
21386 134 : n = makeNode(Constraint);
21387 134 : n->contype = CONSTR_CHECK;
21388 134 : n->conname = NULL;
21389 134 : n->location = -1;
21390 134 : n->is_no_inherit = false;
21391 134 : n->raw_expr = NULL;
21392 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
21393 134 : n->is_enforced = true;
21394 134 : n->initially_valid = true;
21395 134 : n->skip_validation = true;
21396 : /* It's a re-add, since it nominally already exists */
21397 134 : ATAddCheckNNConstraint(wqueue, tab, partRel, n,
21398 : true, false, true, ShareUpdateExclusiveLock);
21399 : }
21400 140 : }
21401 :
21402 : /*
21403 : * DropClonedTriggersFromPartition
21404 : * subroutine for ATExecDetachPartition to remove any triggers that were
21405 : * cloned to the partition when it was created-as-partition or attached.
21406 : * This undoes what CloneRowTriggersToPartition did.
21407 : */
21408 : static void
21409 444 : DropClonedTriggersFromPartition(Oid partitionId)
21410 : {
21411 : ScanKeyData skey;
21412 : SysScanDesc scan;
21413 : HeapTuple trigtup;
21414 : Relation tgrel;
21415 : ObjectAddresses *objects;
21416 :
21417 444 : objects = new_object_addresses();
21418 :
21419 : /*
21420 : * Scan pg_trigger to search for all triggers on this rel.
21421 : */
21422 444 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21423 : F_OIDEQ, ObjectIdGetDatum(partitionId));
21424 444 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21425 444 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21426 : true, NULL, 1, &skey);
21427 766 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21428 : {
21429 322 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21430 : ObjectAddress trig;
21431 :
21432 : /* Ignore triggers that weren't cloned */
21433 322 : if (!OidIsValid(pg_trigger->tgparentid))
21434 304 : continue;
21435 :
21436 : /*
21437 : * Ignore internal triggers that are implementation objects of foreign
21438 : * keys, because these will be detached when the foreign keys
21439 : * themselves are.
21440 : */
21441 274 : if (OidIsValid(pg_trigger->tgconstrrelid))
21442 256 : continue;
21443 :
21444 : /*
21445 : * This is ugly, but necessary: remove the dependency markings on the
21446 : * trigger so that it can be removed.
21447 : */
21448 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21449 : TriggerRelationId,
21450 : DEPENDENCY_PARTITION_PRI);
21451 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21452 : RelationRelationId,
21453 : DEPENDENCY_PARTITION_SEC);
21454 :
21455 : /* remember this trigger to remove it below */
21456 18 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21457 18 : add_exact_object_address(&trig, objects);
21458 : }
21459 :
21460 : /* make the dependency removal visible to the deletion below */
21461 444 : CommandCounterIncrement();
21462 444 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
21463 :
21464 : /* done */
21465 444 : free_object_addresses(objects);
21466 444 : systable_endscan(scan);
21467 444 : table_close(tgrel, RowExclusiveLock);
21468 444 : }
21469 :
21470 : /*
21471 : * Before acquiring lock on an index, acquire the same lock on the owning
21472 : * table.
21473 : */
21474 : struct AttachIndexCallbackState
21475 : {
21476 : Oid partitionOid;
21477 : Oid parentTblOid;
21478 : bool lockedParentTbl;
21479 : };
21480 :
21481 : static void
21482 512 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21483 : void *arg)
21484 : {
21485 : struct AttachIndexCallbackState *state;
21486 : Form_pg_class classform;
21487 : HeapTuple tuple;
21488 :
21489 512 : state = (struct AttachIndexCallbackState *) arg;
21490 :
21491 512 : if (!state->lockedParentTbl)
21492 : {
21493 488 : LockRelationOid(state->parentTblOid, AccessShareLock);
21494 488 : state->lockedParentTbl = true;
21495 : }
21496 :
21497 : /*
21498 : * If we previously locked some other heap, and the name we're looking up
21499 : * no longer refers to an index on that relation, release the now-useless
21500 : * lock. XXX maybe we should do *after* we verify whether the index does
21501 : * not actually belong to the same relation ...
21502 : */
21503 512 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21504 : {
21505 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
21506 0 : state->partitionOid = InvalidOid;
21507 : }
21508 :
21509 : /* Didn't find a relation, so no need for locking or permission checks. */
21510 512 : if (!OidIsValid(relOid))
21511 8 : return;
21512 :
21513 504 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21514 504 : if (!HeapTupleIsValid(tuple))
21515 0 : return; /* concurrently dropped, so nothing to do */
21516 504 : classform = (Form_pg_class) GETSTRUCT(tuple);
21517 504 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21518 394 : classform->relkind != RELKIND_INDEX)
21519 6 : ereport(ERROR,
21520 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21521 : errmsg("\"%s\" is not an index", rv->relname)));
21522 498 : ReleaseSysCache(tuple);
21523 :
21524 : /*
21525 : * Since we need only examine the heap's tupledesc, an access share lock
21526 : * on it (preventing any DDL) is sufficient.
21527 : */
21528 498 : state->partitionOid = IndexGetRelation(relOid, false);
21529 498 : LockRelationOid(state->partitionOid, AccessShareLock);
21530 : }
21531 :
21532 : /*
21533 : * ALTER INDEX i1 ATTACH PARTITION i2
21534 : */
21535 : static ObjectAddress
21536 488 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
21537 : {
21538 : Relation partIdx;
21539 : Relation partTbl;
21540 : Relation parentTbl;
21541 : ObjectAddress address;
21542 : Oid partIdxId;
21543 : Oid currParent;
21544 : struct AttachIndexCallbackState state;
21545 :
21546 : /*
21547 : * We need to obtain lock on the index 'name' to modify it, but we also
21548 : * need to read its owning table's tuple descriptor -- so we need to lock
21549 : * both. To avoid deadlocks, obtain lock on the table before doing so on
21550 : * the index. Furthermore, we need to examine the parent table of the
21551 : * partition, so lock that one too.
21552 : */
21553 488 : state.partitionOid = InvalidOid;
21554 488 : state.parentTblOid = parentIdx->rd_index->indrelid;
21555 488 : state.lockedParentTbl = false;
21556 : partIdxId =
21557 488 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
21558 : RangeVarCallbackForAttachIndex,
21559 : &state);
21560 : /* Not there? */
21561 476 : if (!OidIsValid(partIdxId))
21562 0 : ereport(ERROR,
21563 : (errcode(ERRCODE_UNDEFINED_OBJECT),
21564 : errmsg("index \"%s\" does not exist", name->relname)));
21565 :
21566 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21567 476 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
21568 :
21569 : /* we already hold locks on both tables, so this is safe: */
21570 476 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21571 476 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21572 :
21573 476 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21574 :
21575 : /* Silently do nothing if already in the right state */
21576 952 : currParent = partIdx->rd_rel->relispartition ?
21577 476 : get_partition_parent(partIdxId, false) : InvalidOid;
21578 476 : if (currParent != RelationGetRelid(parentIdx))
21579 : {
21580 : IndexInfo *childInfo;
21581 : IndexInfo *parentInfo;
21582 : AttrMap *attmap;
21583 : bool found;
21584 : int i;
21585 : PartitionDesc partDesc;
21586 : Oid constraintOid,
21587 452 : cldConstrId = InvalidOid;
21588 :
21589 : /*
21590 : * If this partition already has an index attached, refuse the
21591 : * operation.
21592 : */
21593 452 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21594 :
21595 446 : if (OidIsValid(currParent))
21596 0 : ereport(ERROR,
21597 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21598 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21599 : RelationGetRelationName(partIdx),
21600 : RelationGetRelationName(parentIdx)),
21601 : errdetail("Index \"%s\" is already attached to another index.",
21602 : RelationGetRelationName(partIdx))));
21603 :
21604 : /* Make sure it indexes a partition of the other index's table */
21605 446 : partDesc = RelationGetPartitionDesc(parentTbl, true);
21606 446 : found = false;
21607 740 : for (i = 0; i < partDesc->nparts; i++)
21608 : {
21609 734 : if (partDesc->oids[i] == state.partitionOid)
21610 : {
21611 440 : found = true;
21612 440 : break;
21613 : }
21614 : }
21615 446 : if (!found)
21616 6 : ereport(ERROR,
21617 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21618 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21619 : RelationGetRelationName(partIdx),
21620 : RelationGetRelationName(parentIdx)),
21621 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21622 : RelationGetRelationName(partIdx),
21623 : RelationGetRelationName(parentTbl))));
21624 :
21625 : /* Ensure the indexes are compatible */
21626 440 : childInfo = BuildIndexInfo(partIdx);
21627 440 : parentInfo = BuildIndexInfo(parentIdx);
21628 440 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21629 : RelationGetDescr(parentTbl),
21630 : false);
21631 440 : if (!CompareIndexInfo(childInfo, parentInfo,
21632 440 : partIdx->rd_indcollation,
21633 440 : parentIdx->rd_indcollation,
21634 440 : partIdx->rd_opfamily,
21635 440 : parentIdx->rd_opfamily,
21636 : attmap))
21637 42 : ereport(ERROR,
21638 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21639 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21640 : RelationGetRelationName(partIdx),
21641 : RelationGetRelationName(parentIdx)),
21642 : errdetail("The index definitions do not match.")));
21643 :
21644 : /*
21645 : * If there is a constraint in the parent, make sure there is one in
21646 : * the child too.
21647 : */
21648 398 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21649 : RelationGetRelid(parentIdx));
21650 :
21651 398 : if (OidIsValid(constraintOid))
21652 : {
21653 196 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
21654 : partIdxId);
21655 196 : if (!OidIsValid(cldConstrId))
21656 6 : ereport(ERROR,
21657 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21658 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21659 : RelationGetRelationName(partIdx),
21660 : RelationGetRelationName(parentIdx)),
21661 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21662 : RelationGetRelationName(parentIdx),
21663 : RelationGetRelationName(parentTbl),
21664 : RelationGetRelationName(partIdx))));
21665 : }
21666 :
21667 : /*
21668 : * If it's a primary key, make sure the columns in the partition are
21669 : * NOT NULL.
21670 : */
21671 392 : if (parentIdx->rd_index->indisprimary)
21672 166 : verifyPartitionIndexNotNull(childInfo, partTbl);
21673 :
21674 : /* All good -- do it */
21675 392 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21676 392 : if (OidIsValid(constraintOid))
21677 190 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
21678 : RelationGetRelid(partTbl));
21679 :
21680 392 : free_attrmap(attmap);
21681 :
21682 392 : validatePartitionedIndex(parentIdx, parentTbl);
21683 : }
21684 :
21685 416 : relation_close(parentTbl, AccessShareLock);
21686 : /* keep these locks till commit */
21687 416 : relation_close(partTbl, NoLock);
21688 416 : relation_close(partIdx, NoLock);
21689 :
21690 416 : return address;
21691 : }
21692 :
21693 : /*
21694 : * Verify whether the given partition already contains an index attached
21695 : * to the given partitioned index. If so, raise an error.
21696 : */
21697 : static void
21698 452 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21699 : {
21700 : Oid existingIdx;
21701 :
21702 452 : existingIdx = index_get_partition(partitionTbl,
21703 : RelationGetRelid(parentIdx));
21704 452 : if (OidIsValid(existingIdx))
21705 6 : ereport(ERROR,
21706 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21707 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21708 : RelationGetRelationName(partIdx),
21709 : RelationGetRelationName(parentIdx)),
21710 : errdetail("Another index is already attached for partition \"%s\".",
21711 : RelationGetRelationName(partitionTbl))));
21712 446 : }
21713 :
21714 : /*
21715 : * Verify whether the set of attached partition indexes to a parent index on
21716 : * a partitioned table is complete. If it is, mark the parent index valid.
21717 : *
21718 : * This should be called each time a partition index is attached.
21719 : */
21720 : static void
21721 434 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
21722 : {
21723 : Relation inheritsRel;
21724 : SysScanDesc scan;
21725 : ScanKeyData key;
21726 434 : int tuples = 0;
21727 : HeapTuple inhTup;
21728 434 : bool updated = false;
21729 :
21730 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21731 :
21732 : /*
21733 : * Scan pg_inherits for this parent index. Count each valid index we find
21734 : * (verifying the pg_index entry for each), and if we reach the total
21735 : * amount we expect, we can mark this parent index as valid.
21736 : */
21737 434 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21738 434 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21739 : BTEqualStrategyNumber, F_OIDEQ,
21740 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21741 434 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21742 : NULL, 1, &key);
21743 1162 : while ((inhTup = systable_getnext(scan)) != NULL)
21744 : {
21745 728 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21746 : HeapTuple indTup;
21747 : Form_pg_index indexForm;
21748 :
21749 728 : indTup = SearchSysCache1(INDEXRELID,
21750 : ObjectIdGetDatum(inhForm->inhrelid));
21751 728 : if (!HeapTupleIsValid(indTup))
21752 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21753 728 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21754 728 : if (indexForm->indisvalid)
21755 666 : tuples += 1;
21756 728 : ReleaseSysCache(indTup);
21757 : }
21758 :
21759 : /* Done with pg_inherits */
21760 434 : systable_endscan(scan);
21761 434 : table_close(inheritsRel, AccessShareLock);
21762 :
21763 : /*
21764 : * If we found as many inherited indexes as the partitioned table has
21765 : * partitions, we're good; update pg_index to set indisvalid.
21766 : */
21767 434 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21768 : {
21769 : Relation idxRel;
21770 : HeapTuple indTup;
21771 : Form_pg_index indexForm;
21772 :
21773 208 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
21774 208 : indTup = SearchSysCacheCopy1(INDEXRELID,
21775 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21776 208 : if (!HeapTupleIsValid(indTup))
21777 0 : elog(ERROR, "cache lookup failed for index %u",
21778 : RelationGetRelid(partedIdx));
21779 208 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21780 :
21781 208 : indexForm->indisvalid = true;
21782 208 : updated = true;
21783 :
21784 208 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21785 :
21786 208 : table_close(idxRel, RowExclusiveLock);
21787 208 : heap_freetuple(indTup);
21788 : }
21789 :
21790 : /*
21791 : * If this index is in turn a partition of a larger index, validating it
21792 : * might cause the parent to become valid also. Try that.
21793 : */
21794 434 : if (updated && partedIdx->rd_rel->relispartition)
21795 : {
21796 : Oid parentIdxId,
21797 : parentTblId;
21798 : Relation parentIdx,
21799 : parentTbl;
21800 :
21801 : /* make sure we see the validation we just did */
21802 42 : CommandCounterIncrement();
21803 :
21804 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21805 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21806 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21807 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21808 : Assert(!parentIdx->rd_index->indisvalid);
21809 :
21810 42 : validatePartitionedIndex(parentIdx, parentTbl);
21811 :
21812 42 : relation_close(parentIdx, AccessExclusiveLock);
21813 42 : relation_close(parentTbl, AccessExclusiveLock);
21814 : }
21815 434 : }
21816 :
21817 : /*
21818 : * When attaching an index as a partition of a partitioned index which is a
21819 : * primary key, verify that all the columns in the partition are marked NOT
21820 : * NULL.
21821 : */
21822 : static void
21823 166 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
21824 : {
21825 336 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21826 : {
21827 170 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
21828 170 : iinfo->ii_IndexAttrNumbers[i] - 1);
21829 :
21830 170 : if (!att->attnotnull)
21831 0 : ereport(ERROR,
21832 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21833 : errmsg("invalid primary key definition"),
21834 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21835 : NameStr(att->attname),
21836 : RelationGetRelationName(partition)));
21837 : }
21838 166 : }
21839 :
21840 : /*
21841 : * Return an OID list of constraints that reference the given relation
21842 : * that are marked as having a parent constraints.
21843 : */
21844 : static List *
21845 958 : GetParentedForeignKeyRefs(Relation partition)
21846 : {
21847 : Relation pg_constraint;
21848 : HeapTuple tuple;
21849 : SysScanDesc scan;
21850 : ScanKeyData key[2];
21851 958 : List *constraints = NIL;
21852 :
21853 : /*
21854 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
21855 : * scan.
21856 : */
21857 1362 : if (RelationGetIndexList(partition) == NIL ||
21858 404 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
21859 : INDEX_ATTR_BITMAP_KEY)))
21860 710 : return NIL;
21861 :
21862 : /* Search for constraints referencing this table */
21863 248 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21864 248 : ScanKeyInit(&key[0],
21865 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21866 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21867 248 : ScanKeyInit(&key[1],
21868 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
21869 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21870 :
21871 : /* XXX This is a seqscan, as we don't have a usable index */
21872 248 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21873 372 : while ((tuple = systable_getnext(scan)) != NULL)
21874 : {
21875 124 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21876 :
21877 : /*
21878 : * We only need to process constraints that are part of larger ones.
21879 : */
21880 124 : if (!OidIsValid(constrForm->conparentid))
21881 0 : continue;
21882 :
21883 124 : constraints = lappend_oid(constraints, constrForm->oid);
21884 : }
21885 :
21886 248 : systable_endscan(scan);
21887 248 : table_close(pg_constraint, AccessShareLock);
21888 :
21889 248 : return constraints;
21890 : }
21891 :
21892 : /*
21893 : * During DETACH PARTITION, verify that any foreign keys pointing to the
21894 : * partitioned table would not become invalid. An error is raised if any
21895 : * referenced values exist.
21896 : */
21897 : static void
21898 514 : ATDetachCheckNoForeignKeyRefs(Relation partition)
21899 : {
21900 : List *constraints;
21901 : ListCell *cell;
21902 :
21903 514 : constraints = GetParentedForeignKeyRefs(partition);
21904 :
21905 566 : foreach(cell, constraints)
21906 : {
21907 86 : Oid constrOid = lfirst_oid(cell);
21908 : HeapTuple tuple;
21909 : Form_pg_constraint constrForm;
21910 : Relation rel;
21911 86 : Trigger trig = {0};
21912 :
21913 86 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21914 86 : if (!HeapTupleIsValid(tuple))
21915 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21916 86 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21917 :
21918 : Assert(OidIsValid(constrForm->conparentid));
21919 : Assert(constrForm->confrelid == RelationGetRelid(partition));
21920 :
21921 : /* prevent data changes into the referencing table until commit */
21922 86 : rel = table_open(constrForm->conrelid, ShareLock);
21923 :
21924 86 : trig.tgoid = InvalidOid;
21925 86 : trig.tgname = NameStr(constrForm->conname);
21926 86 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
21927 86 : trig.tgisinternal = true;
21928 86 : trig.tgconstrrelid = RelationGetRelid(partition);
21929 86 : trig.tgconstrindid = constrForm->conindid;
21930 86 : trig.tgconstraint = constrForm->oid;
21931 86 : trig.tgdeferrable = false;
21932 86 : trig.tginitdeferred = false;
21933 : /* we needn't fill in remaining fields */
21934 :
21935 86 : RI_PartitionRemove_Check(&trig, rel, partition);
21936 :
21937 52 : ReleaseSysCache(tuple);
21938 :
21939 52 : table_close(rel, NoLock);
21940 : }
21941 480 : }
21942 :
21943 : /*
21944 : * resolve column compression specification to compression method.
21945 : */
21946 : static char
21947 257566 : GetAttributeCompression(Oid atttypid, const char *compression)
21948 : {
21949 : char cmethod;
21950 :
21951 257566 : if (compression == NULL || strcmp(compression, "default") == 0)
21952 257386 : return InvalidCompressionMethod;
21953 :
21954 : /*
21955 : * To specify a nondefault method, the column data type must be toastable.
21956 : * Note this says nothing about whether the column's attstorage setting
21957 : * permits compression; we intentionally allow attstorage and
21958 : * attcompression to be independent. But with a non-toastable type,
21959 : * attstorage could not be set to a value that would permit compression.
21960 : *
21961 : * We don't actually need to enforce this, since nothing bad would happen
21962 : * if attcompression were non-default; it would never be consulted. But
21963 : * it seems more user-friendly to complain about a certainly-useless
21964 : * attempt to set the property.
21965 : */
21966 180 : if (!TypeIsToastable(atttypid))
21967 6 : ereport(ERROR,
21968 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21969 : errmsg("column data type %s does not support compression",
21970 : format_type_be(atttypid))));
21971 :
21972 174 : cmethod = CompressionNameToMethod(compression);
21973 174 : if (!CompressionMethodIsValid(cmethod))
21974 12 : ereport(ERROR,
21975 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21976 : errmsg("invalid compression method \"%s\"", compression)));
21977 :
21978 162 : return cmethod;
21979 : }
21980 :
21981 : /*
21982 : * resolve column storage specification
21983 : */
21984 : static char
21985 254 : GetAttributeStorage(Oid atttypid, const char *storagemode)
21986 : {
21987 254 : char cstorage = 0;
21988 :
21989 254 : if (pg_strcasecmp(storagemode, "plain") == 0)
21990 54 : cstorage = TYPSTORAGE_PLAIN;
21991 200 : else if (pg_strcasecmp(storagemode, "external") == 0)
21992 158 : cstorage = TYPSTORAGE_EXTERNAL;
21993 42 : else if (pg_strcasecmp(storagemode, "extended") == 0)
21994 16 : cstorage = TYPSTORAGE_EXTENDED;
21995 26 : else if (pg_strcasecmp(storagemode, "main") == 0)
21996 20 : cstorage = TYPSTORAGE_MAIN;
21997 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
21998 6 : cstorage = get_typstorage(atttypid);
21999 : else
22000 0 : ereport(ERROR,
22001 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22002 : errmsg("invalid storage type \"%s\"",
22003 : storagemode)));
22004 :
22005 : /*
22006 : * safety check: do not allow toasted storage modes unless column datatype
22007 : * is TOAST-aware.
22008 : */
22009 254 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22010 6 : ereport(ERROR,
22011 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22012 : errmsg("column data type %s can only have storage PLAIN",
22013 : format_type_be(atttypid))));
22014 :
22015 248 : return cstorage;
22016 : }
|