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_largeobject_metadata.h"
46 : #include "catalog/pg_namespace.h"
47 : #include "catalog/pg_opclass.h"
48 : #include "catalog/pg_policy.h"
49 : #include "catalog/pg_proc.h"
50 : #include "catalog/pg_publication_rel.h"
51 : #include "catalog/pg_rewrite.h"
52 : #include "catalog/pg_statistic_ext.h"
53 : #include "catalog/pg_tablespace.h"
54 : #include "catalog/pg_trigger.h"
55 : #include "catalog/pg_type.h"
56 : #include "catalog/storage.h"
57 : #include "catalog/storage_xlog.h"
58 : #include "catalog/toasting.h"
59 : #include "commands/cluster.h"
60 : #include "commands/comment.h"
61 : #include "commands/defrem.h"
62 : #include "commands/event_trigger.h"
63 : #include "commands/sequence.h"
64 : #include "commands/tablecmds.h"
65 : #include "commands/tablespace.h"
66 : #include "commands/trigger.h"
67 : #include "commands/typecmds.h"
68 : #include "commands/user.h"
69 : #include "commands/vacuum.h"
70 : #include "common/int.h"
71 : #include "executor/executor.h"
72 : #include "foreign/fdwapi.h"
73 : #include "foreign/foreign.h"
74 : #include "miscadmin.h"
75 : #include "nodes/makefuncs.h"
76 : #include "nodes/nodeFuncs.h"
77 : #include "nodes/parsenodes.h"
78 : #include "optimizer/optimizer.h"
79 : #include "parser/parse_coerce.h"
80 : #include "parser/parse_collate.h"
81 : #include "parser/parse_expr.h"
82 : #include "parser/parse_relation.h"
83 : #include "parser/parse_type.h"
84 : #include "parser/parse_utilcmd.h"
85 : #include "parser/parser.h"
86 : #include "partitioning/partbounds.h"
87 : #include "partitioning/partdesc.h"
88 : #include "pgstat.h"
89 : #include "rewrite/rewriteDefine.h"
90 : #include "rewrite/rewriteHandler.h"
91 : #include "rewrite/rewriteManip.h"
92 : #include "storage/bufmgr.h"
93 : #include "storage/lmgr.h"
94 : #include "storage/lock.h"
95 : #include "storage/predicate.h"
96 : #include "storage/smgr.h"
97 : #include "tcop/utility.h"
98 : #include "utils/acl.h"
99 : #include "utils/builtins.h"
100 : #include "utils/fmgroids.h"
101 : #include "utils/inval.h"
102 : #include "utils/lsyscache.h"
103 : #include "utils/memutils.h"
104 : #include "utils/partcache.h"
105 : #include "utils/relcache.h"
106 : #include "utils/ruleutils.h"
107 : #include "utils/snapmgr.h"
108 : #include "utils/syscache.h"
109 : #include "utils/timestamp.h"
110 : #include "utils/typcache.h"
111 : #include "utils/usercontext.h"
112 :
113 : /*
114 : * ON COMMIT action list
115 : */
116 : typedef struct OnCommitItem
117 : {
118 : Oid relid; /* relid of relation */
119 : OnCommitAction oncommit; /* what to do at end of xact */
120 :
121 : /*
122 : * If this entry was created during the current transaction,
123 : * creating_subid is the ID of the creating subxact; if created in a prior
124 : * transaction, creating_subid is zero. If deleted during the current
125 : * transaction, deleting_subid is the ID of the deleting subxact; if no
126 : * deletion request is pending, deleting_subid is zero.
127 : */
128 : SubTransactionId creating_subid;
129 : SubTransactionId deleting_subid;
130 : } OnCommitItem;
131 :
132 : static List *on_commits = NIL;
133 :
134 :
135 : /*
136 : * State information for ALTER TABLE
137 : *
138 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
139 : * structs, one for each table modified by the operation (the named table
140 : * plus any child tables that are affected). We save lists of subcommands
141 : * to apply to this table (possibly modified by parse transformation steps);
142 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
143 : * necessary information is stored in the constraints and newvals lists.
144 : *
145 : * Phase 2 is divided into multiple passes; subcommands are executed in
146 : * a pass determined by subcommand type.
147 : */
148 :
149 : typedef enum AlterTablePass
150 : {
151 : AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
152 : AT_PASS_DROP, /* DROP (all flavors) */
153 : AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
154 : AT_PASS_ADD_COL, /* ADD COLUMN */
155 : AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
156 : AT_PASS_OLD_INDEX, /* re-add existing indexes */
157 : AT_PASS_OLD_CONSTR, /* re-add existing constraints */
158 : /* We could support a RENAME COLUMN pass here, but not currently used */
159 : AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
160 : AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
161 : AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
162 : AT_PASS_ADD_INDEX, /* ADD indexes */
163 : AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
164 : AT_PASS_MISC, /* other stuff */
165 : } AlterTablePass;
166 :
167 : #define AT_NUM_PASSES (AT_PASS_MISC + 1)
168 :
169 : typedef struct AlteredTableInfo
170 : {
171 : /* Information saved before any work commences: */
172 : Oid relid; /* Relation to work on */
173 : char relkind; /* Its relkind */
174 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
175 :
176 : /*
177 : * Transiently set during Phase 2, normally set to NULL.
178 : *
179 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
180 : * returns control. This can be exploited by ATExecCmd subroutines to
181 : * close/reopen across transaction boundaries.
182 : */
183 : Relation rel;
184 :
185 : /* Information saved by Phase 1 for Phase 2: */
186 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
187 : /* Information saved by Phases 1/2 for Phase 3: */
188 : List *constraints; /* List of NewConstraint */
189 : List *newvals; /* List of NewColumnValue */
190 : List *afterStmts; /* List of utility command parsetrees */
191 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
192 : int rewrite; /* Reason for forced rewrite, if any */
193 : bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
194 : Oid newAccessMethod; /* new access method; 0 means no change,
195 : * if above is true */
196 : Oid newTableSpace; /* new tablespace; 0 means no change */
197 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
198 : char newrelpersistence; /* if above is true */
199 : Expr *partition_constraint; /* for attach partition validation */
200 : /* true, if validating default due to some other attach/detach */
201 : bool validate_default;
202 : /* Objects to rebuild after completing ALTER TYPE operations */
203 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
204 : List *changedConstraintDefs; /* string definitions of same */
205 : List *changedIndexOids; /* OIDs of indexes to rebuild */
206 : List *changedIndexDefs; /* string definitions of same */
207 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
208 : char *clusterOnIndex; /* index to use for CLUSTER */
209 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
210 : List *changedStatisticsDefs; /* string definitions of same */
211 : } AlteredTableInfo;
212 :
213 : /* Struct describing one new constraint to check in Phase 3 scan */
214 : /* Note: new not-null constraints are handled elsewhere */
215 : typedef struct NewConstraint
216 : {
217 : char *name; /* Constraint name, or NULL if none */
218 : ConstrType contype; /* CHECK or FOREIGN */
219 : Oid refrelid; /* PK rel, if FOREIGN */
220 : Oid refindid; /* OID of PK's index, if FOREIGN */
221 : bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
222 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
223 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
224 : ExprState *qualstate; /* Execution state for CHECK expr */
225 : } NewConstraint;
226 :
227 : /*
228 : * Struct describing one new column value that needs to be computed during
229 : * Phase 3 copy (this could be either a new column with a non-null default, or
230 : * a column that we're changing the type of). Columns without such an entry
231 : * are just copied from the old table during ATRewriteTable. Note that the
232 : * expr is an expression over *old* table values, except when is_generated
233 : * is true; then it is an expression over columns of the *new* tuple.
234 : */
235 : typedef struct NewColumnValue
236 : {
237 : AttrNumber attnum; /* which column */
238 : Expr *expr; /* expression to compute */
239 : ExprState *exprstate; /* execution state */
240 : bool is_generated; /* is it a GENERATED expression? */
241 : } NewColumnValue;
242 :
243 : /*
244 : * Error-reporting support for RemoveRelations
245 : */
246 : struct dropmsgstrings
247 : {
248 : char kind;
249 : int nonexistent_code;
250 : const char *nonexistent_msg;
251 : const char *skipping_msg;
252 : const char *nota_msg;
253 : const char *drophint_msg;
254 : };
255 :
256 : static const struct dropmsgstrings dropmsgstringarray[] = {
257 : {RELKIND_RELATION,
258 : ERRCODE_UNDEFINED_TABLE,
259 : gettext_noop("table \"%s\" does not exist"),
260 : gettext_noop("table \"%s\" does not exist, skipping"),
261 : gettext_noop("\"%s\" is not a table"),
262 : gettext_noop("Use DROP TABLE to remove a table.")},
263 : {RELKIND_SEQUENCE,
264 : ERRCODE_UNDEFINED_TABLE,
265 : gettext_noop("sequence \"%s\" does not exist"),
266 : gettext_noop("sequence \"%s\" does not exist, skipping"),
267 : gettext_noop("\"%s\" is not a sequence"),
268 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
269 : {RELKIND_VIEW,
270 : ERRCODE_UNDEFINED_TABLE,
271 : gettext_noop("view \"%s\" does not exist"),
272 : gettext_noop("view \"%s\" does not exist, skipping"),
273 : gettext_noop("\"%s\" is not a view"),
274 : gettext_noop("Use DROP VIEW to remove a view.")},
275 : {RELKIND_MATVIEW,
276 : ERRCODE_UNDEFINED_TABLE,
277 : gettext_noop("materialized view \"%s\" does not exist"),
278 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
279 : gettext_noop("\"%s\" is not a materialized view"),
280 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
281 : {RELKIND_INDEX,
282 : ERRCODE_UNDEFINED_OBJECT,
283 : gettext_noop("index \"%s\" does not exist"),
284 : gettext_noop("index \"%s\" does not exist, skipping"),
285 : gettext_noop("\"%s\" is not an index"),
286 : gettext_noop("Use DROP INDEX to remove an index.")},
287 : {RELKIND_COMPOSITE_TYPE,
288 : ERRCODE_UNDEFINED_OBJECT,
289 : gettext_noop("type \"%s\" does not exist"),
290 : gettext_noop("type \"%s\" does not exist, skipping"),
291 : gettext_noop("\"%s\" is not a type"),
292 : gettext_noop("Use DROP TYPE to remove a type.")},
293 : {RELKIND_FOREIGN_TABLE,
294 : ERRCODE_UNDEFINED_OBJECT,
295 : gettext_noop("foreign table \"%s\" does not exist"),
296 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
297 : gettext_noop("\"%s\" is not a foreign table"),
298 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
299 : {RELKIND_PARTITIONED_TABLE,
300 : ERRCODE_UNDEFINED_TABLE,
301 : gettext_noop("table \"%s\" does not exist"),
302 : gettext_noop("table \"%s\" does not exist, skipping"),
303 : gettext_noop("\"%s\" is not a table"),
304 : gettext_noop("Use DROP TABLE to remove a table.")},
305 : {RELKIND_PARTITIONED_INDEX,
306 : ERRCODE_UNDEFINED_OBJECT,
307 : gettext_noop("index \"%s\" does not exist"),
308 : gettext_noop("index \"%s\" does not exist, skipping"),
309 : gettext_noop("\"%s\" is not an index"),
310 : gettext_noop("Use DROP INDEX to remove an index.")},
311 : {'\0', 0, NULL, NULL, NULL, NULL}
312 : };
313 :
314 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
315 : struct DropRelationCallbackState
316 : {
317 : /* These fields are set by RemoveRelations: */
318 : char expected_relkind;
319 : LOCKMODE heap_lockmode;
320 : /* These fields are state to track which subsidiary locks are held: */
321 : Oid heapOid;
322 : Oid partParentOid;
323 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
324 : char actual_relkind;
325 : char actual_relpersistence;
326 : };
327 :
328 : /* Alter table target-type flags for ATSimplePermissions */
329 : #define ATT_TABLE 0x0001
330 : #define ATT_VIEW 0x0002
331 : #define ATT_MATVIEW 0x0004
332 : #define ATT_INDEX 0x0008
333 : #define ATT_COMPOSITE_TYPE 0x0010
334 : #define ATT_FOREIGN_TABLE 0x0020
335 : #define ATT_PARTITIONED_INDEX 0x0040
336 : #define ATT_SEQUENCE 0x0080
337 : #define ATT_PARTITIONED_TABLE 0x0100
338 :
339 : /*
340 : * ForeignTruncateInfo
341 : *
342 : * Information related to truncation of foreign tables. This is used for
343 : * the elements in a hash table. It uses the server OID as lookup key,
344 : * and includes a per-server list of all foreign tables involved in the
345 : * truncation.
346 : */
347 : typedef struct ForeignTruncateInfo
348 : {
349 : Oid serverid;
350 : List *rels;
351 : } ForeignTruncateInfo;
352 :
353 : /* Partial or complete FK creation in addFkConstraint() */
354 : typedef enum addFkConstraintSides
355 : {
356 : addFkReferencedSide,
357 : addFkReferencingSide,
358 : addFkBothSides,
359 : } addFkConstraintSides;
360 :
361 : /*
362 : * Partition tables are expected to be dropped when the parent partitioned
363 : * table gets dropped. Hence for partitioning we use AUTO dependency.
364 : * Otherwise, for regular inheritance use NORMAL dependency.
365 : */
366 : #define child_dependency_type(child_is_partition) \
367 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
368 :
369 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
370 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
371 : static void truncate_check_activity(Relation rel);
372 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
373 : Oid relId, Oid oldRelId, void *arg);
374 : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
375 : bool is_partition, List **supconstr,
376 : List **supnotnulls);
377 : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
378 : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
379 : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
380 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
381 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
382 : static void StoreCatalogInheritance(Oid relationId, List *supers,
383 : bool child_is_partition);
384 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
385 : int32 seqNumber, Relation inhRelation,
386 : bool child_is_partition);
387 : static int findAttrByName(const char *attributeName, const List *columns);
388 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
389 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
390 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
391 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
392 : LOCKMODE lockmode);
393 : static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
394 : ATAlterConstraint *cmdcon,
395 : bool recurse, LOCKMODE lockmode);
396 : static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
397 : Relation tgrel, Relation rel, HeapTuple contuple,
398 : bool recurse, LOCKMODE lockmode);
399 : static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
400 : Relation conrel, Relation tgrel,
401 : Oid fkrelid, Oid pkrelid,
402 : HeapTuple contuple, LOCKMODE lockmode,
403 : Oid ReferencedParentDelTrigger,
404 : Oid ReferencedParentUpdTrigger,
405 : Oid ReferencingParentInsTrigger,
406 : Oid ReferencingParentUpdTrigger);
407 : static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
408 : Relation conrel, Relation tgrel, Relation rel,
409 : HeapTuple contuple, bool recurse,
410 : List **otherrelids, LOCKMODE lockmode);
411 : static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
412 : Relation conrel, Relation rel,
413 : HeapTuple contuple, LOCKMODE lockmode);
414 : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
415 : bool deferrable, bool initdeferred,
416 : List **otherrelids);
417 : static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
418 : Relation conrel, Relation tgrel,
419 : Oid fkrelid, Oid pkrelid,
420 : HeapTuple contuple, LOCKMODE lockmode,
421 : Oid ReferencedParentDelTrigger,
422 : Oid ReferencedParentUpdTrigger,
423 : Oid ReferencingParentInsTrigger,
424 : Oid ReferencingParentUpdTrigger);
425 : static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
426 : Relation conrel, Relation tgrel, Relation rel,
427 : HeapTuple contuple, bool recurse,
428 : List **otherrelids, LOCKMODE lockmode);
429 : static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
430 : HeapTuple contuple);
431 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
432 : Relation rel, char *constrName,
433 : bool recurse, bool recursing, LOCKMODE lockmode);
434 : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
435 : Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
436 : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
437 : char *constrName, HeapTuple contuple,
438 : bool recurse, bool recursing, LOCKMODE lockmode);
439 : static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
440 : HeapTuple contuple, bool recurse, bool recursing,
441 : LOCKMODE lockmode);
442 : static int transformColumnNameList(Oid relId, List *colList,
443 : int16 *attnums, Oid *atttypids, Oid *attcollids);
444 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
445 : List **attnamelist,
446 : int16 *attnums, Oid *atttypids, Oid *attcollids,
447 : Oid *opclasses, bool *pk_has_without_overlaps);
448 : static Oid transformFkeyCheckAttrs(Relation pkrel,
449 : int numattrs, int16 *attnums,
450 : bool with_period, Oid *opclasses,
451 : bool *pk_has_without_overlaps);
452 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
453 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
454 : Oid *funcid);
455 : static void validateForeignKeyConstraint(char *conname,
456 : Relation rel, Relation pkrel,
457 : Oid pkindOid, Oid constraintOid, bool hasperiod);
458 : static void CheckAlterTableIsSafe(Relation rel);
459 : static void ATController(AlterTableStmt *parsetree,
460 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
461 : AlterTableUtilityContext *context);
462 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
463 : bool recurse, bool recursing, LOCKMODE lockmode,
464 : AlterTableUtilityContext *context);
465 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
466 : AlterTableUtilityContext *context);
467 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
468 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
469 : AlterTableUtilityContext *context);
470 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
471 : Relation rel, AlterTableCmd *cmd,
472 : bool recurse, LOCKMODE lockmode,
473 : AlterTablePass cur_pass,
474 : AlterTableUtilityContext *context);
475 : static void ATRewriteTables(AlterTableStmt *parsetree,
476 : List **wqueue, LOCKMODE lockmode,
477 : AlterTableUtilityContext *context);
478 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
479 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
480 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
481 : static void ATSimpleRecursion(List **wqueue, Relation rel,
482 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
483 : AlterTableUtilityContext *context);
484 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
485 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
486 : LOCKMODE lockmode,
487 : AlterTableUtilityContext *context);
488 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
489 : DropBehavior behavior);
490 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
491 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
492 : AlterTableUtilityContext *context);
493 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
494 : Relation rel, AlterTableCmd **cmd,
495 : bool recurse, bool recursing,
496 : LOCKMODE lockmode, AlterTablePass cur_pass,
497 : AlterTableUtilityContext *context);
498 : static bool check_for_column_name_collision(Relation rel, const char *colname,
499 : bool if_not_exists);
500 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
501 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
502 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
503 : LOCKMODE lockmode);
504 : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
505 : bool is_valid, bool queue_validation);
506 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
507 : char *conName, char *colName,
508 : bool recurse, bool recursing,
509 : LOCKMODE lockmode);
510 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
511 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
512 : List *testConstraint, List *provenConstraint);
513 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
514 : Node *newDefault, LOCKMODE lockmode);
515 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
516 : Node *newDefault);
517 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
518 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
519 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
520 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
521 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
522 : bool recurse, bool recursing);
523 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
524 : Node *newExpr, LOCKMODE lockmode);
525 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
526 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
527 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
528 : Node *newValue, LOCKMODE lockmode);
529 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
530 : Node *options, bool isReset, LOCKMODE lockmode);
531 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
532 : Node *newValue, LOCKMODE lockmode);
533 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
534 : AlterTableCmd *cmd, LOCKMODE lockmode,
535 : AlterTableUtilityContext *context);
536 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
537 : DropBehavior behavior,
538 : bool recurse, bool recursing,
539 : bool missing_ok, LOCKMODE lockmode,
540 : ObjectAddresses *addrs);
541 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
542 : bool recurse, LOCKMODE lockmode,
543 : AlterTableUtilityContext *context);
544 : static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
545 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
546 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
547 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
548 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
549 : static ObjectAddress ATExecAddConstraint(List **wqueue,
550 : AlteredTableInfo *tab, Relation rel,
551 : Constraint *newConstraint, bool recurse, bool is_readd,
552 : LOCKMODE lockmode);
553 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
554 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
555 : IndexStmt *stmt, LOCKMODE lockmode);
556 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
557 : AlteredTableInfo *tab, Relation rel,
558 : Constraint *constr,
559 : bool recurse, bool recursing, bool is_readd,
560 : LOCKMODE lockmode);
561 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
562 : Relation rel, Constraint *fkconstraint,
563 : bool recurse, bool recursing,
564 : LOCKMODE lockmode);
565 : static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
566 : int numfksetcols, int16 *fksetcolsattnums,
567 : List *fksetcols);
568 : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
569 : char *constraintname,
570 : Constraint *fkconstraint, Relation rel,
571 : Relation pkrel, Oid indexOid,
572 : Oid parentConstr,
573 : int numfks, int16 *pkattnum, int16 *fkattnum,
574 : Oid *pfeqoperators, Oid *ppeqoperators,
575 : Oid *ffeqoperators, int numfkdelsetcols,
576 : int16 *fkdelsetcols, bool is_internal,
577 : bool with_period);
578 : static void addFkRecurseReferenced(Constraint *fkconstraint,
579 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
580 : int numfks, int16 *pkattnum, int16 *fkattnum,
581 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
582 : int numfkdelsetcols, int16 *fkdelsetcols,
583 : bool old_check_ok,
584 : Oid parentDelTrigger, Oid parentUpdTrigger,
585 : bool with_period);
586 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
587 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
588 : int numfks, int16 *pkattnum, int16 *fkattnum,
589 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
590 : int numfkdelsetcols, int16 *fkdelsetcols,
591 : bool old_check_ok, LOCKMODE lockmode,
592 : Oid parentInsTrigger, Oid parentUpdTrigger,
593 : bool with_period);
594 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
595 : Relation partitionRel);
596 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
597 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
598 : Relation partRel);
599 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
600 : Constraint *fkconstraint, Oid constraintOid,
601 : Oid indexOid,
602 : Oid parentInsTrigger, Oid parentUpdTrigger,
603 : Oid *insertTrigOid, Oid *updateTrigOid);
604 : static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
605 : Constraint *fkconstraint, Oid constraintOid,
606 : Oid indexOid,
607 : Oid parentDelTrigger, Oid parentUpdTrigger,
608 : Oid *deleteTrigOid, Oid *updateTrigOid);
609 : static bool tryAttachPartitionForeignKey(List **wqueue,
610 : ForeignKeyCacheInfo *fk,
611 : Relation partition,
612 : Oid parentConstrOid, int numfks,
613 : AttrNumber *mapped_conkey, AttrNumber *confkey,
614 : Oid *conpfeqop,
615 : Oid parentInsTrigger,
616 : Oid parentUpdTrigger,
617 : Relation trigrel);
618 : static void AttachPartitionForeignKey(List **wqueue, Relation partition,
619 : Oid partConstrOid, Oid parentConstrOid,
620 : Oid parentInsTrigger, Oid parentUpdTrigger,
621 : Relation trigrel);
622 : static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
623 : Oid conoid, Oid conrelid);
624 : static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
625 : Oid confrelid, Oid conrelid);
626 : static void GetForeignKeyActionTriggers(Relation trigrel,
627 : Oid conoid, Oid confrelid, Oid conrelid,
628 : Oid *deleteTriggerOid,
629 : Oid *updateTriggerOid);
630 : static void GetForeignKeyCheckTriggers(Relation trigrel,
631 : Oid conoid, Oid confrelid, Oid conrelid,
632 : Oid *insertTriggerOid,
633 : Oid *updateTriggerOid);
634 : static void ATExecDropConstraint(Relation rel, const char *constrName,
635 : DropBehavior behavior, bool recurse,
636 : bool missing_ok, LOCKMODE lockmode);
637 : static ObjectAddress dropconstraint_internal(Relation rel,
638 : HeapTuple constraintTup, DropBehavior behavior,
639 : bool recurse, bool recursing,
640 : bool missing_ok, LOCKMODE lockmode);
641 : static void ATPrepAlterColumnType(List **wqueue,
642 : AlteredTableInfo *tab, Relation rel,
643 : bool recurse, bool recursing,
644 : AlterTableCmd *cmd, LOCKMODE lockmode,
645 : AlterTableUtilityContext *context);
646 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
647 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
648 : AlterTableCmd *cmd, LOCKMODE lockmode);
649 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
650 : Relation rel, AttrNumber attnum, const char *colName);
651 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
652 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
653 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
654 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
655 : LOCKMODE lockmode);
656 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
657 : char *cmd, List **wqueue, LOCKMODE lockmode,
658 : bool rewrite);
659 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
660 : Oid objid, Relation rel, List *domname,
661 : const char *conname);
662 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
663 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
664 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
665 : List *options, LOCKMODE lockmode);
666 : static void change_owner_fix_column_acls(Oid relationOid,
667 : Oid oldOwnerId, Oid newOwnerId);
668 : static void change_owner_recurse_to_sequences(Oid relationOid,
669 : Oid newOwnerId, LOCKMODE lockmode);
670 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
671 : LOCKMODE lockmode);
672 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
673 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
674 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
675 : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
676 : bool toLogged);
677 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
678 : const char *tablespacename, LOCKMODE lockmode);
679 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
680 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
681 : static void ATExecSetRelOptions(Relation rel, List *defList,
682 : AlterTableType operation,
683 : LOCKMODE lockmode);
684 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
685 : char fires_when, bool skip_system, bool recurse,
686 : LOCKMODE lockmode);
687 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
688 : char fires_when, LOCKMODE lockmode);
689 : static void ATPrepAddInherit(Relation child_rel);
690 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
691 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
692 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
693 : DependencyType deptype);
694 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
695 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
696 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
697 : static void ATExecGenericOptions(Relation rel, List *options);
698 : static void ATExecSetRowSecurity(Relation rel, bool rls);
699 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
700 : static ObjectAddress ATExecSetCompression(Relation rel,
701 : const char *column, Node *newValue, LOCKMODE lockmode);
702 :
703 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
704 : static const char *storage_name(char c);
705 :
706 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
707 : Oid oldRelOid, void *arg);
708 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
709 : Oid oldrelid, void *arg);
710 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
711 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
712 : List **partexprs, Oid *partopclass, Oid *partcollation,
713 : PartitionStrategy strategy);
714 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
715 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
716 : bool expect_detached);
717 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
718 : PartitionCmd *cmd,
719 : AlterTableUtilityContext *context);
720 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
721 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
722 : List *partConstraint,
723 : bool validate_default);
724 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
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 63464 : 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 63464 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
784 : Oid ofTypeId;
785 : ObjectAddress address;
786 : LOCKMODE parentLockmode;
787 63464 : 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 63464 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
794 :
795 : /*
796 : * Check consistency of arguments
797 : */
798 63464 : 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 63452 : if (stmt->partspec != NULL)
805 : {
806 5136 : if (relkind != RELKIND_RELATION)
807 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
808 :
809 5136 : relkind = RELKIND_PARTITIONED_TABLE;
810 5136 : partitioned = true;
811 : }
812 : else
813 58316 : partitioned = false;
814 :
815 63452 : if (relkind == RELKIND_PARTITIONED_TABLE &&
816 5136 : 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 63446 : 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 63446 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
836 3220 : && 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 63446 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
858 : ShareUpdateExclusiveLock);
859 :
860 : /* Determine the list of OIDs of the parents. */
861 63446 : inheritOids = NIL;
862 74152 : foreach(listptr, stmt->inhRelations)
863 : {
864 10706 : RangeVar *rv = (RangeVar *) lfirst(listptr);
865 : Oid parentOid;
866 :
867 10706 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
868 :
869 : /*
870 : * Reject duplications in the list of parents.
871 : */
872 10706 : 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 10706 : 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 63446 : if (stmt->tablespacename)
886 : {
887 128 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
888 :
889 122 : 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 63318 : else if (stmt->partbound)
895 : {
896 : Assert(list_length(inheritOids) == 1);
897 8180 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
898 : }
899 : else
900 55138 : tablespaceId = InvalidOid;
901 :
902 : /* still nothing? use the default */
903 63434 : if (!OidIsValid(tablespaceId))
904 63296 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
905 : partitioned);
906 :
907 : /* Check permissions except when using database's default */
908 63428 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
909 : {
910 : AclResult aclresult;
911 :
912 164 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
913 : ACL_CREATE);
914 164 : 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 63422 : 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 63404 : if (!OidIsValid(ownerId))
927 63158 : ownerId = GetUserId();
928 :
929 : /*
930 : * Parse and validate reloptions, if any.
931 : */
932 63404 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
933 : true, false);
934 :
935 63386 : switch (relkind)
936 : {
937 16786 : case RELKIND_VIEW:
938 16786 : (void) view_reloptions(reloptions, true);
939 16768 : break;
940 5112 : case RELKIND_PARTITIONED_TABLE:
941 5112 : (void) partitioned_table_reloptions(reloptions, true);
942 5106 : break;
943 41488 : default:
944 41488 : (void) heap_reloptions(relkind, reloptions, true);
945 : }
946 :
947 63266 : if (stmt->ofTypename)
948 : {
949 : AclResult aclresult;
950 :
951 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
952 :
953 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
954 86 : if (aclresult != ACLCHECK_OK)
955 6 : aclcheck_error_type(aclresult, ofTypeId);
956 : }
957 : else
958 63180 : 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 63020 : stmt->tableElts =
966 63260 : MergeAttributes(stmt->tableElts, inheritOids,
967 63260 : stmt->relation->relpersistence,
968 63260 : 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 63020 : 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 62972 : rawDefaults = NIL;
988 62972 : cookedDefaults = NIL;
989 62972 : attnum = 0;
990 :
991 319544 : foreach(listptr, stmt->tableElts)
992 : {
993 256572 : ColumnDef *colDef = lfirst(listptr);
994 :
995 256572 : attnum++;
996 256572 : if (colDef->raw_default != NULL)
997 : {
998 : RawColumnDefault *rawEnt;
999 :
1000 : Assert(colDef->cooked_default == NULL);
1001 :
1002 3262 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
1003 3262 : rawEnt->attnum = attnum;
1004 3262 : rawEnt->raw_default = colDef->raw_default;
1005 3262 : rawEnt->generated = colDef->generated;
1006 3262 : rawDefaults = lappend(rawDefaults, rawEnt);
1007 : }
1008 253310 : else if (colDef->cooked_default != NULL)
1009 : {
1010 : CookedConstraint *cooked;
1011 :
1012 402 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
1013 402 : cooked->contype = CONSTR_DEFAULT;
1014 402 : cooked->conoid = InvalidOid; /* until created */
1015 402 : cooked->name = NULL;
1016 402 : cooked->attnum = attnum;
1017 402 : cooked->expr = colDef->cooked_default;
1018 402 : cooked->is_enforced = true;
1019 402 : cooked->skip_validation = false;
1020 402 : cooked->is_local = true; /* not used for defaults */
1021 402 : cooked->inhcount = 0; /* ditto */
1022 402 : cooked->is_no_inherit = false;
1023 402 : 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 62972 : 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 62850 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1038 : {
1039 39240 : if (stmt->partbound)
1040 : {
1041 : Assert(list_length(inheritOids) == 1);
1042 7998 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1043 : }
1044 :
1045 39240 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1046 34120 : 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 62954 : 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 62954 : 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 62906 : 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 62906 : 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 62906 : if (rawDefaults)
1104 2796 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1105 : true, true, false, queryString);
1106 :
1107 : /*
1108 : * Make column generation expressions visible for use by partitioning.
1109 : */
1110 62714 : CommandCounterIncrement();
1111 :
1112 : /* Process and store partition bound, if any. */
1113 62714 : if (stmt->partbound)
1114 : {
1115 : PartitionBoundSpec *bound;
1116 : ParseState *pstate;
1117 8102 : Oid parentId = linitial_oid(inheritOids),
1118 : defaultPartOid;
1119 : Relation parent,
1120 8102 : defaultRel = NULL;
1121 : ParseNamespaceItem *nsitem;
1122 :
1123 : /* Already have strong enough lock on the parent */
1124 8102 : 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 8102 : 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 8084 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1157 : true));
1158 8084 : if (OidIsValid(defaultPartOid))
1159 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1160 :
1161 : /* Transform the bound values */
1162 8084 : pstate = make_parsestate(NULL);
1163 8084 : 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 8084 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1171 : NULL, false, false);
1172 8084 : addNSItemToQuery(pstate, nsitem, false, true, true);
1173 :
1174 8084 : 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 7880 : 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 7766 : 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 7748 : StorePartitionBound(rel, parent, bound);
1198 :
1199 7748 : table_close(parent, NoLock);
1200 : }
1201 :
1202 : /* Store inheritance information for new rel. */
1203 62360 : 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 62360 : 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 5106 : List *partexprs = NIL;
1217 :
1218 5106 : pstate = make_parsestate(NULL);
1219 5106 : pstate->p_sourcetext = queryString;
1220 :
1221 5106 : partnatts = list_length(stmt->partspec->partParams);
1222 :
1223 : /* Protect fixed-size arrays here and in executor */
1224 5106 : 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 5106 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1237 :
1238 5076 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1239 : partattrs, &partexprs, partopclass,
1240 5076 : partcollation, stmt->partspec->strategy);
1241 :
1242 4944 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1243 : partexprs,
1244 : partopclass, partcollation);
1245 :
1246 : /* make it all visible */
1247 4944 : 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 62198 : if (stmt->partbound)
1258 : {
1259 7742 : 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 7742 : parent = table_open(parentId, NoLock);
1266 7742 : idxlist = RelationGetIndexList(parent);
1267 :
1268 : /*
1269 : * For each index in the parent table, create one in the partition
1270 : */
1271 9162 : foreach(cell, idxlist)
1272 : {
1273 1438 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1274 : AttrMap *attmap;
1275 : IndexStmt *idxstmt;
1276 : Oid constraintOid;
1277 :
1278 1438 : 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 1402 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1295 : RelationGetDescr(parent),
1296 : false);
1297 : idxstmt =
1298 1402 : generateClonedIndexStmt(NULL, idxRel,
1299 : attmap, &constraintOid);
1300 1402 : DefineIndex(RelationGetRelid(rel),
1301 : idxstmt,
1302 : InvalidOid,
1303 : RelationGetRelid(idxRel),
1304 : constraintOid,
1305 : -1,
1306 : false, false, false, false, false);
1307 :
1308 1396 : index_close(idxRel, AccessShareLock);
1309 : }
1310 :
1311 7724 : list_free(idxlist);
1312 :
1313 : /*
1314 : * If there are any row-level triggers, clone them to the new
1315 : * partition.
1316 : */
1317 7724 : if (parent->trigdesc != NULL)
1318 444 : 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 7724 : CloneForeignKeyConstraints(NULL, parent, rel);
1325 :
1326 7724 : 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 62180 : if (stmt->constraints)
1335 736 : 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 62150 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1345 : old_notnulls);
1346 139498 : foreach_int(attrnum, nncols)
1347 15354 : set_attnotnull(NULL, rel, attrnum, true, false);
1348 :
1349 62072 : 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 62072 : relation_close(rel, NoLock);
1356 :
1357 62072 : 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 66010 : 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 66010 : natts = list_length(columns);
1386 66010 : desc = CreateTemplateTupleDesc(natts);
1387 :
1388 66010 : attnum = 0;
1389 :
1390 325836 : foreach(l, columns)
1391 : {
1392 259886 : 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 259886 : attnum++;
1402 :
1403 259886 : attname = entry->colname;
1404 259886 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1405 :
1406 259886 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1407 259886 : if (aclresult != ACLCHECK_OK)
1408 42 : aclcheck_error_type(aclresult, atttypid);
1409 :
1410 259844 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1411 259844 : attdim = list_length(entry->typeName->arrayBounds);
1412 259844 : if (attdim > PG_INT16_MAX)
1413 0 : ereport(ERROR,
1414 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1415 : errmsg("too many array dimensions"));
1416 :
1417 259844 : 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 259844 : TupleDescInitEntry(desc, attnum, attname,
1424 : atttypid, atttypmod, attdim);
1425 259844 : att = TupleDescAttr(desc, attnum - 1);
1426 :
1427 : /* Override TupleDescInitEntry's settings as requested */
1428 259844 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1429 :
1430 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1431 259844 : att->attnotnull = entry->is_not_null;
1432 259844 : att->attislocal = entry->is_local;
1433 259844 : att->attinhcount = entry->inhcount;
1434 259844 : att->attidentity = entry->identity;
1435 259844 : att->attgenerated = entry->generated;
1436 259844 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1437 259832 : if (entry->storage)
1438 20624 : att->attstorage = entry->storage;
1439 239208 : else if (entry->storage_name)
1440 26 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1441 :
1442 259826 : populate_compact_attribute(desc, attnum - 1);
1443 : }
1444 :
1445 65950 : 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 1088 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1454 : {
1455 : const struct dropmsgstrings *rentry;
1456 :
1457 1208 : 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 1366 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1476 : {
1477 1366 : if (rentry->kind == rightkind)
1478 : {
1479 1046 : 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 908 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1488 908 : 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 17630 : RemoveRelations(DropStmt *drop)
1529 : {
1530 : ObjectAddresses *objects;
1531 : char relkind;
1532 : ListCell *cell;
1533 17630 : int flags = 0;
1534 17630 : LOCKMODE lockmode = AccessExclusiveLock;
1535 :
1536 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1537 17630 : 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 144 : lockmode = ShareUpdateExclusiveLock;
1545 : Assert(drop->removeType == OBJECT_INDEX);
1546 144 : 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 138 : 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 17624 : switch (drop->removeType)
1564 : {
1565 15370 : case OBJECT_TABLE:
1566 15370 : relkind = RELKIND_RELATION;
1567 15370 : break;
1568 :
1569 826 : case OBJECT_INDEX:
1570 826 : relkind = RELKIND_INDEX;
1571 826 : break;
1572 :
1573 178 : case OBJECT_SEQUENCE:
1574 178 : relkind = RELKIND_SEQUENCE;
1575 178 : break;
1576 :
1577 960 : case OBJECT_VIEW:
1578 960 : relkind = RELKIND_VIEW;
1579 960 : break;
1580 :
1581 126 : case OBJECT_MATVIEW:
1582 126 : relkind = RELKIND_MATVIEW;
1583 126 : break;
1584 :
1585 164 : case OBJECT_FOREIGN_TABLE:
1586 164 : relkind = RELKIND_FOREIGN_TABLE;
1587 164 : 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 17624 : objects = new_object_addresses();
1598 :
1599 39258 : foreach(cell, drop->objects)
1600 : {
1601 21798 : 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 21798 : AcceptInvalidationMessages();
1617 :
1618 : /* Look up the appropriate relation using namespace search. */
1619 21798 : state.expected_relkind = relkind;
1620 43596 : state.heap_lockmode = drop->concurrent ?
1621 21798 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1622 : /* We must initialize these fields to show that no locks are held: */
1623 21798 : state.heapOid = InvalidOid;
1624 21798 : state.partParentOid = InvalidOid;
1625 :
1626 21798 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1627 : RangeVarCallbackForDropRelation,
1628 : &state);
1629 :
1630 : /* Not there? */
1631 21778 : if (!OidIsValid(relOid))
1632 : {
1633 1088 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1634 950 : 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 20690 : if (drop->concurrent &&
1642 132 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1643 : {
1644 : Assert(list_length(drop->objects) == 1 &&
1645 : drop->removeType == OBJECT_INDEX);
1646 114 : flags |= PERFORM_DELETION_CONCURRENTLY;
1647 : }
1648 :
1649 : /*
1650 : * Concurrent index drop cannot be used with partitioned indexes,
1651 : * either.
1652 : */
1653 20690 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1654 114 : 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 20684 : 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 20684 : obj.classId = RelationRelationId;
1674 20684 : obj.objectId = relOid;
1675 20684 : obj.objectSubId = 0;
1676 :
1677 20684 : add_exact_object_address(&obj, objects);
1678 : }
1679 :
1680 17460 : performMultipleDeletions(objects, drop->behavior, flags);
1681 :
1682 17318 : free_object_addresses(objects);
1683 17318 : }
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 22230 : 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 22230 : bool invalid_system_index = false;
1702 :
1703 22230 : state = (struct DropRelationCallbackState *) arg;
1704 22230 : 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 22230 : 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 22230 : 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 22230 : if (!OidIsValid(relOid))
1730 1100 : return;
1731 :
1732 21130 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1733 21130 : if (!HeapTupleIsValid(tuple))
1734 0 : return; /* concurrently dropped, so nothing to do */
1735 21130 : classform = (Form_pg_class) GETSTRUCT(tuple);
1736 21130 : is_partition = classform->relispartition;
1737 :
1738 : /* Pass back some data to save lookups in RemoveRelations */
1739 21130 : state->actual_relkind = classform->relkind;
1740 21130 : 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 21130 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1751 3030 : expected_relkind = RELKIND_RELATION;
1752 18100 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1753 84 : expected_relkind = RELKIND_INDEX;
1754 : else
1755 18016 : expected_relkind = classform->relkind;
1756 :
1757 21130 : 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 21130 : 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 21112 : 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 21112 : 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 21110 : 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 21110 : if (expected_relkind == RELKIND_INDEX &&
1814 : relOid != oldRelOid)
1815 : {
1816 814 : state->heapOid = IndexGetRelation(relOid, true);
1817 814 : if (OidIsValid(state->heapOid))
1818 814 : 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 21110 : if (is_partition && relOid != oldRelOid)
1828 : {
1829 618 : state->partParentOid = get_partition_parent(relOid, true);
1830 618 : if (OidIsValid(state->partParentOid))
1831 618 : 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 1764 : ExecuteTruncate(TruncateStmt *stmt)
1852 : {
1853 1764 : List *rels = NIL;
1854 1764 : List *relids = NIL;
1855 1764 : List *relids_logged = NIL;
1856 : ListCell *cell;
1857 :
1858 : /*
1859 : * Open, exclusive-lock, and check all the explicitly-specified relations
1860 : */
1861 3740 : foreach(cell, stmt->relations)
1862 : {
1863 2032 : RangeVar *rv = lfirst(cell);
1864 : Relation rel;
1865 2032 : bool recurse = rv->inh;
1866 : Oid myrelid;
1867 2032 : LOCKMODE lockmode = AccessExclusiveLock;
1868 :
1869 2032 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1870 : 0, RangeVarCallbackForTruncate,
1871 : NULL);
1872 :
1873 : /* don't throw error for "TRUNCATE foo, foo" */
1874 1994 : if (list_member_oid(relids, myrelid))
1875 2 : continue;
1876 :
1877 : /* open the relation, we already hold a lock on it */
1878 1992 : 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 1992 : truncate_check_activity(rel);
1885 :
1886 1986 : rels = lappend(rels, rel);
1887 1986 : relids = lappend_oid(relids, myrelid);
1888 :
1889 : /* Log this relation only if needed for logical decoding */
1890 1986 : if (RelationIsLogicallyLogged(rel))
1891 74 : relids_logged = lappend_oid(relids_logged, myrelid);
1892 :
1893 1986 : if (recurse)
1894 : {
1895 : ListCell *child;
1896 : List *children;
1897 :
1898 1932 : children = find_all_inheritors(myrelid, lockmode, NULL);
1899 :
1900 5668 : foreach(child, children)
1901 : {
1902 3736 : Oid childrelid = lfirst_oid(child);
1903 :
1904 3736 : if (list_member_oid(relids, childrelid))
1905 1932 : 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 54 : 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 1708 : ExecuteTruncateGuts(rels, relids, relids_logged,
1950 1708 : stmt->behavior, stmt->restart_seqs, false);
1951 :
1952 : /* And close the rels */
1953 5242 : foreach(cell, rels)
1954 : {
1955 3616 : Relation rel = (Relation) lfirst(cell);
1956 :
1957 3616 : table_close(rel, NoLock);
1958 : }
1959 1626 : }
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 1734 : 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 1734 : List *seq_relids = NIL;
1983 1734 : 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 1734 : rels = list_copy(explicit_rels);
2002 1734 : 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 1734 : if (behavior == DROP_RESTRICT)
2043 1694 : 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 1660 : if (restart_seqs)
2053 : {
2054 44 : foreach(cell, rels)
2055 : {
2056 22 : Relation rel = (Relation) lfirst(cell);
2057 22 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2058 : ListCell *seqcell;
2059 :
2060 54 : foreach(seqcell, seqlist)
2061 : {
2062 32 : Oid seq_relid = lfirst_oid(seqcell);
2063 : Relation seq_rel;
2064 :
2065 32 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2066 :
2067 : /* This check must match AlterSequence! */
2068 32 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2069 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2070 0 : RelationGetRelationName(seq_rel));
2071 :
2072 32 : seq_relids = lappend_oid(seq_relids, seq_relid);
2073 :
2074 32 : relation_close(seq_rel, NoLock);
2075 : }
2076 : }
2077 : }
2078 :
2079 : /* Prepare to catch AFTER triggers. */
2080 1660 : 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 1660 : estate = CreateExecutorState();
2092 : resultRelInfos = (ResultRelInfo *)
2093 1660 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2094 1660 : resultRelInfo = resultRelInfos;
2095 5434 : foreach(cell, rels)
2096 : {
2097 3774 : Relation rel = (Relation) lfirst(cell);
2098 :
2099 3774 : InitResultRelInfo(resultRelInfo,
2100 : rel,
2101 : 0, /* dummy rangetable index */
2102 : NULL,
2103 : 0);
2104 3774 : estate->es_opened_result_relations =
2105 3774 : lappend(estate->es_opened_result_relations, resultRelInfo);
2106 3774 : 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 1660 : resultRelInfo = resultRelInfos;
2116 5434 : foreach(cell, rels)
2117 : {
2118 : UserContext ucxt;
2119 :
2120 3774 : if (run_as_table_owner)
2121 56 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2122 : &ucxt);
2123 3774 : ExecBSTruncateTriggers(estate, resultRelInfo);
2124 3774 : if (run_as_table_owner)
2125 56 : RestoreUserContext(&ucxt);
2126 3774 : resultRelInfo++;
2127 : }
2128 :
2129 : /*
2130 : * OK, truncate each table.
2131 : */
2132 1660 : mySubid = GetCurrentSubTransactionId();
2133 :
2134 5434 : foreach(cell, rels)
2135 : {
2136 3774 : Relation rel = (Relation) lfirst(cell);
2137 :
2138 : /* Skip partitioned tables as there is nothing to do */
2139 3774 : 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 3070 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2150 34 : {
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 3036 : if (rel->rd_createSubid == mySubid ||
2192 3010 : rel->rd_newRelfilelocatorSubid == mySubid)
2193 : {
2194 : /* Immediate, non-rollbackable truncation is OK */
2195 90 : heap_truncate_one_rel(rel);
2196 : }
2197 : else
2198 : {
2199 : Oid heap_relid;
2200 : Oid toast_relid;
2201 2946 : 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 2946 : 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 2946 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2219 :
2220 2946 : heap_relid = RelationGetRelid(rel);
2221 :
2222 : /*
2223 : * The same for the toast table, if any.
2224 : */
2225 2946 : toast_relid = rel->rd_rel->reltoastrelid;
2226 2946 : if (OidIsValid(toast_relid))
2227 : {
2228 1784 : Relation toastrel = relation_open(toast_relid,
2229 : AccessExclusiveLock);
2230 :
2231 1784 : RelationSetNewRelfilenumber(toastrel,
2232 1784 : toastrel->rd_rel->relpersistence);
2233 1784 : table_close(toastrel, NoLock);
2234 : }
2235 :
2236 : /*
2237 : * Reconstruct the indexes to match, and we're done.
2238 : */
2239 2946 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2240 : &reindex_params);
2241 : }
2242 :
2243 3036 : pgstat_count_truncate(rel);
2244 : }
2245 :
2246 : /* Now go through the hash table, and truncate foreign tables */
2247 1660 : 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 1684 : foreach(cell, seq_relids)
2279 : {
2280 32 : Oid seq_relid = lfirst_oid(cell);
2281 :
2282 32 : 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 1652 : if (relids_logged != NIL)
2293 : {
2294 : xl_heap_truncate xlrec;
2295 62 : int i = 0;
2296 :
2297 : /* should only get here if wal_level >= logical */
2298 : Assert(XLogLogicalInfoActive());
2299 :
2300 62 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2301 160 : foreach(cell, relids_logged)
2302 98 : logrelids[i++] = lfirst_oid(cell);
2303 :
2304 62 : xlrec.dbId = MyDatabaseId;
2305 62 : xlrec.nrelids = list_length(relids_logged);
2306 62 : xlrec.flags = 0;
2307 62 : if (behavior == DROP_CASCADE)
2308 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2309 62 : if (restart_seqs)
2310 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2311 :
2312 62 : XLogBeginInsert();
2313 62 : XLogRegisterData(&xlrec, SizeOfHeapTruncate);
2314 62 : XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2315 :
2316 62 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2317 :
2318 62 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2319 : }
2320 :
2321 : /*
2322 : * Process all AFTER STATEMENT TRUNCATE triggers.
2323 : */
2324 1652 : resultRelInfo = resultRelInfos;
2325 5418 : foreach(cell, rels)
2326 : {
2327 : UserContext ucxt;
2328 :
2329 3766 : if (run_as_table_owner)
2330 56 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2331 : &ucxt);
2332 3766 : ExecASTruncateTriggers(estate, resultRelInfo);
2333 3766 : if (run_as_table_owner)
2334 56 : RestoreUserContext(&ucxt);
2335 3766 : resultRelInfo++;
2336 : }
2337 :
2338 : /* Handle queued AFTER triggers */
2339 1652 : AfterTriggerEndQuery(estate);
2340 :
2341 : /* We can clean up the EState now */
2342 1652 : FreeExecutorState(estate);
2343 :
2344 : /*
2345 : * Close any rels opened by CASCADE (can't do this while EState still
2346 : * holds refs)
2347 : */
2348 1652 : rels = list_difference_ptr(rels, explicit_rels);
2349 1746 : foreach(cell, rels)
2350 : {
2351 94 : Relation rel = (Relation) lfirst(cell);
2352 :
2353 94 : table_close(rel, NoLock);
2354 : }
2355 1652 : }
2356 :
2357 : /*
2358 : * Check that a given relation is safe to truncate. Subroutine for
2359 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2360 : */
2361 : static void
2362 4046 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2363 : {
2364 4046 : 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 4046 : 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 4008 : else if (reltuple->relkind != RELKIND_RELATION &&
2384 724 : 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 and pg_largeobject_metadata to be truncated as part of
2393 : * pg_upgrade, because we need to change its relfilenode to match the old
2394 : * cluster, and allowing a TRUNCATE command to be executed is the easiest
2395 : * way of doing that.
2396 : */
2397 4042 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2398 114 : && (!IsBinaryUpgrade ||
2399 56 : (relid != LargeObjectRelationId &&
2400 : relid != LargeObjectMetadataRelationId)))
2401 2 : ereport(ERROR,
2402 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2403 : errmsg("permission denied: \"%s\" is a system catalog",
2404 : relname)));
2405 :
2406 4040 : InvokeObjectTruncateHook(relid);
2407 4040 : }
2408 :
2409 : /*
2410 : * Check that current user has the permission to truncate given relation.
2411 : */
2412 : static void
2413 2244 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2414 : {
2415 2244 : char *relname = NameStr(reltuple->relname);
2416 : AclResult aclresult;
2417 :
2418 : /* Permissions checks */
2419 2244 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2420 2244 : if (aclresult != ACLCHECK_OK)
2421 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2422 : relname);
2423 2212 : }
2424 :
2425 : /*
2426 : * Set of extra sanity checks to check if a given relation is safe to
2427 : * truncate. This is split with truncate_check_rel() as
2428 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2429 : */
2430 : static void
2431 3882 : truncate_check_activity(Relation rel)
2432 : {
2433 : /*
2434 : * Don't allow truncate on temp tables of other backends ... their local
2435 : * buffer manager is not going to cope.
2436 : */
2437 3882 : if (RELATION_IS_OTHER_TEMP(rel))
2438 0 : ereport(ERROR,
2439 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2440 : errmsg("cannot truncate temporary tables of other sessions")));
2441 :
2442 : /*
2443 : * Also check for active uses of the relation in the current transaction,
2444 : * including open scans and pending AFTER trigger events.
2445 : */
2446 3882 : CheckTableNotInUse(rel, "TRUNCATE");
2447 3876 : }
2448 :
2449 : /*
2450 : * storage_name
2451 : * returns the name corresponding to a typstorage/attstorage enum value
2452 : */
2453 : static const char *
2454 24 : storage_name(char c)
2455 : {
2456 24 : switch (c)
2457 : {
2458 0 : case TYPSTORAGE_PLAIN:
2459 0 : return "PLAIN";
2460 0 : case TYPSTORAGE_EXTERNAL:
2461 0 : return "EXTERNAL";
2462 12 : case TYPSTORAGE_EXTENDED:
2463 12 : return "EXTENDED";
2464 12 : case TYPSTORAGE_MAIN:
2465 12 : return "MAIN";
2466 0 : default:
2467 0 : return "???";
2468 : }
2469 : }
2470 :
2471 : /*----------
2472 : * MergeAttributes
2473 : * Returns new schema given initial schema and superclasses.
2474 : *
2475 : * Input arguments:
2476 : * 'columns' is the column/attribute definition for the table. (It's a list
2477 : * of ColumnDef's.) It is destructively changed.
2478 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2479 : * 'relpersistence' is the persistence type of the table.
2480 : * 'is_partition' tells if the table is a partition.
2481 : *
2482 : * Output arguments:
2483 : * 'supconstr' receives a list of CookedConstraint representing
2484 : * CHECK constraints belonging to parent relations, updated as
2485 : * necessary to be valid for the child.
2486 : * 'supnotnulls' receives a list of CookedConstraint representing
2487 : * not-null constraints based on those from parent relations.
2488 : *
2489 : * Return value:
2490 : * Completed schema list.
2491 : *
2492 : * Notes:
2493 : * The order in which the attributes are inherited is very important.
2494 : * Intuitively, the inherited attributes should come first. If a table
2495 : * inherits from multiple parents, the order of those attributes are
2496 : * according to the order of the parents specified in CREATE TABLE.
2497 : *
2498 : * Here's an example:
2499 : *
2500 : * create table person (name text, age int4, location point);
2501 : * create table emp (salary int4, manager text) inherits(person);
2502 : * create table student (gpa float8) inherits (person);
2503 : * create table stud_emp (percent int4) inherits (emp, student);
2504 : *
2505 : * The order of the attributes of stud_emp is:
2506 : *
2507 : * person {1:name, 2:age, 3:location}
2508 : * / \
2509 : * {6:gpa} student emp {4:salary, 5:manager}
2510 : * \ /
2511 : * stud_emp {7:percent}
2512 : *
2513 : * If the same attribute name appears multiple times, then it appears
2514 : * in the result table in the proper location for its first appearance.
2515 : *
2516 : * Constraints (including not-null constraints) for the child table
2517 : * are the union of all relevant constraints, from both the child schema
2518 : * and parent tables. In addition, in legacy inheritance, each column that
2519 : * appears in a primary key in any of the parents also gets a NOT NULL
2520 : * constraint (partitioning doesn't need this, because the PK itself gets
2521 : * inherited.)
2522 : *
2523 : * The default value for a child column is defined as:
2524 : * (1) If the child schema specifies a default, that value is used.
2525 : * (2) If neither the child nor any parent specifies a default, then
2526 : * the column will not have a default.
2527 : * (3) If conflicting defaults are inherited from different parents
2528 : * (and not overridden by the child), an error is raised.
2529 : * (4) Otherwise the inherited default is used.
2530 : *
2531 : * Note that the default-value infrastructure is used for generated
2532 : * columns' expressions too, so most of the preceding paragraph applies
2533 : * to generation expressions too. We insist that a child column be
2534 : * generated if and only if its parent(s) are, but it need not have
2535 : * the same generation expression.
2536 : *----------
2537 : */
2538 : static List *
2539 63260 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2540 : bool is_partition, List **supconstr, List **supnotnulls)
2541 : {
2542 63260 : List *inh_columns = NIL;
2543 63260 : List *constraints = NIL;
2544 63260 : List *nnconstraints = NIL;
2545 63260 : bool have_bogus_defaults = false;
2546 : int child_attno;
2547 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2548 63260 : List *saved_columns = NIL;
2549 : ListCell *lc;
2550 :
2551 : /*
2552 : * Check for and reject tables with too many columns. We perform this
2553 : * check relatively early for two reasons: (a) we don't run the risk of
2554 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2555 : * okay if we're processing <= 1600 columns, but could take minutes to
2556 : * execute if the user attempts to create a table with hundreds of
2557 : * thousands of columns.
2558 : *
2559 : * Note that we also need to check that we do not exceed this figure after
2560 : * including columns from inherited relations.
2561 : */
2562 63260 : if (list_length(columns) > MaxHeapAttributeNumber)
2563 0 : ereport(ERROR,
2564 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2565 : errmsg("tables can have at most %d columns",
2566 : MaxHeapAttributeNumber)));
2567 :
2568 : /*
2569 : * Check for duplicate names in the explicit list of attributes.
2570 : *
2571 : * Although we might consider merging such entries in the same way that we
2572 : * handle name conflicts for inherited attributes, it seems to make more
2573 : * sense to assume such conflicts are errors.
2574 : *
2575 : * We don't use foreach() here because we have two nested loops over the
2576 : * columns list, with possible element deletions in the inner one. If we
2577 : * used foreach_delete_current() it could only fix up the state of one of
2578 : * the loops, so it seems cleaner to use looping over list indexes for
2579 : * both loops. Note that any deletion will happen beyond where the outer
2580 : * loop is, so its index never needs adjustment.
2581 : */
2582 300124 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2583 : {
2584 236888 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2585 :
2586 236888 : if (!is_partition && coldef->typeName == NULL)
2587 : {
2588 : /*
2589 : * Typed table column option that does not belong to a column from
2590 : * the type. This works because the columns from the type come
2591 : * first in the list. (We omit this check for partition column
2592 : * lists; those are processed separately below.)
2593 : */
2594 6 : ereport(ERROR,
2595 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2596 : errmsg("column \"%s\" does not exist",
2597 : coldef->colname)));
2598 : }
2599 :
2600 : /* restpos scans all entries beyond coldef; incr is in loop body */
2601 6587988 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2602 : {
2603 6351124 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2604 :
2605 6351124 : if (strcmp(coldef->colname, restdef->colname) == 0)
2606 : {
2607 50 : if (coldef->is_from_type)
2608 : {
2609 : /*
2610 : * merge the column options into the column from the type
2611 : */
2612 32 : coldef->is_not_null = restdef->is_not_null;
2613 32 : coldef->raw_default = restdef->raw_default;
2614 32 : coldef->cooked_default = restdef->cooked_default;
2615 32 : coldef->constraints = restdef->constraints;
2616 32 : coldef->is_from_type = false;
2617 32 : columns = list_delete_nth_cell(columns, restpos);
2618 : }
2619 : else
2620 18 : ereport(ERROR,
2621 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2622 : errmsg("column \"%s\" specified more than once",
2623 : coldef->colname)));
2624 : }
2625 : else
2626 6351074 : restpos++;
2627 : }
2628 : }
2629 :
2630 : /*
2631 : * In case of a partition, there are no new column definitions, only dummy
2632 : * ColumnDefs created for column constraints. Set them aside for now and
2633 : * process them at the end.
2634 : */
2635 63236 : if (is_partition)
2636 : {
2637 8168 : saved_columns = columns;
2638 8168 : columns = NIL;
2639 : }
2640 :
2641 : /*
2642 : * Scan the parents left-to-right, and merge their attributes to form a
2643 : * list of inherited columns (inh_columns).
2644 : */
2645 63236 : child_attno = 0;
2646 73834 : foreach(lc, supers)
2647 : {
2648 10682 : Oid parent = lfirst_oid(lc);
2649 : Relation relation;
2650 : TupleDesc tupleDesc;
2651 : TupleConstr *constr;
2652 : AttrMap *newattmap;
2653 : List *inherited_defaults;
2654 : List *cols_with_defaults;
2655 : List *nnconstrs;
2656 : ListCell *lc1;
2657 : ListCell *lc2;
2658 10682 : Bitmapset *nncols = NULL;
2659 :
2660 : /* caller already got lock */
2661 10682 : relation = table_open(parent, NoLock);
2662 :
2663 : /*
2664 : * Check for active uses of the parent partitioned table in the
2665 : * current transaction, such as being used in some manner by an
2666 : * enclosing command.
2667 : */
2668 10682 : if (is_partition)
2669 8168 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2670 :
2671 : /*
2672 : * We do not allow partitioned tables and partitions to participate in
2673 : * regular inheritance.
2674 : */
2675 10676 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2676 6 : ereport(ERROR,
2677 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2678 : errmsg("cannot inherit from partitioned table \"%s\"",
2679 : RelationGetRelationName(relation))));
2680 10670 : if (relation->rd_rel->relispartition && !is_partition)
2681 6 : ereport(ERROR,
2682 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2683 : errmsg("cannot inherit from partition \"%s\"",
2684 : RelationGetRelationName(relation))));
2685 :
2686 10664 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2687 8164 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2688 8144 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2689 0 : ereport(ERROR,
2690 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2691 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2692 : RelationGetRelationName(relation))));
2693 :
2694 : /*
2695 : * If the parent is permanent, so must be all of its partitions. Note
2696 : * that inheritance allows that case.
2697 : */
2698 10664 : if (is_partition &&
2699 8162 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2700 : relpersistence == RELPERSISTENCE_TEMP)
2701 6 : ereport(ERROR,
2702 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2703 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2704 : RelationGetRelationName(relation))));
2705 :
2706 : /* Permanent rels cannot inherit from temporary ones */
2707 10658 : if (relpersistence != RELPERSISTENCE_TEMP &&
2708 10292 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2709 24 : ereport(ERROR,
2710 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2711 : errmsg(!is_partition
2712 : ? "cannot inherit from temporary relation \"%s\""
2713 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2714 : RelationGetRelationName(relation))));
2715 :
2716 : /* If existing rel is temp, it must belong to this session */
2717 10634 : if (RELATION_IS_OTHER_TEMP(relation))
2718 0 : ereport(ERROR,
2719 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2720 : errmsg(!is_partition
2721 : ? "cannot inherit from temporary relation of another session"
2722 : : "cannot create as partition of temporary relation of another session")));
2723 :
2724 : /*
2725 : * We should have an UNDER permission flag for this, but for now,
2726 : * demand that creator of a child table own the parent.
2727 : */
2728 10634 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2729 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2730 0 : RelationGetRelationName(relation));
2731 :
2732 10634 : tupleDesc = RelationGetDescr(relation);
2733 10634 : constr = tupleDesc->constr;
2734 :
2735 : /*
2736 : * newattmap->attnums[] will contain the child-table attribute numbers
2737 : * for the attributes of this parent table. (They are not the same
2738 : * for parents after the first one, nor if we have dropped columns.)
2739 : */
2740 10634 : newattmap = make_attrmap(tupleDesc->natts);
2741 :
2742 : /* We can't process inherited defaults until newattmap is complete. */
2743 10634 : inherited_defaults = cols_with_defaults = NIL;
2744 :
2745 : /*
2746 : * Request attnotnull on columns that have a not-null constraint
2747 : * that's not marked NO INHERIT (even if not valid).
2748 : */
2749 10634 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2750 : true, false);
2751 23662 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2752 2394 : nncols = bms_add_member(nncols, cc->attnum);
2753 :
2754 32048 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2755 21414 : parent_attno++)
2756 : {
2757 21450 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2758 : parent_attno - 1);
2759 21450 : char *attributeName = NameStr(attribute->attname);
2760 : int exist_attno;
2761 : ColumnDef *newdef;
2762 : ColumnDef *mergeddef;
2763 :
2764 : /*
2765 : * Ignore dropped columns in the parent.
2766 : */
2767 21450 : if (attribute->attisdropped)
2768 198 : continue; /* leave newattmap->attnums entry as zero */
2769 :
2770 : /*
2771 : * Create new column definition
2772 : */
2773 21252 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2774 : attribute->atttypmod, attribute->attcollation);
2775 21252 : newdef->storage = attribute->attstorage;
2776 21252 : newdef->generated = attribute->attgenerated;
2777 21252 : if (CompressionMethodIsValid(attribute->attcompression))
2778 36 : newdef->compression =
2779 36 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2780 :
2781 : /*
2782 : * Regular inheritance children are independent enough not to
2783 : * inherit identity columns. But partitions are integral part of
2784 : * a partitioned table and inherit identity column.
2785 : */
2786 21252 : if (is_partition)
2787 16642 : newdef->identity = attribute->attidentity;
2788 :
2789 : /*
2790 : * Does it match some previously considered column from another
2791 : * parent?
2792 : */
2793 21252 : exist_attno = findAttrByName(attributeName, inh_columns);
2794 21252 : if (exist_attno > 0)
2795 : {
2796 : /*
2797 : * Yes, try to merge the two column definitions.
2798 : */
2799 370 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2800 :
2801 334 : newattmap->attnums[parent_attno - 1] = exist_attno;
2802 :
2803 : /*
2804 : * Partitions have only one parent, so conflict should never
2805 : * occur.
2806 : */
2807 : Assert(!is_partition);
2808 : }
2809 : else
2810 : {
2811 : /*
2812 : * No, create a new inherited column
2813 : */
2814 20882 : newdef->inhcount = 1;
2815 20882 : newdef->is_local = false;
2816 20882 : inh_columns = lappend(inh_columns, newdef);
2817 :
2818 20882 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2819 20882 : mergeddef = newdef;
2820 : }
2821 :
2822 : /*
2823 : * mark attnotnull if parent has it
2824 : */
2825 21216 : if (bms_is_member(parent_attno, nncols))
2826 2394 : mergeddef->is_not_null = true;
2827 :
2828 : /*
2829 : * Locate default/generation expression if any
2830 : */
2831 21216 : if (attribute->atthasdef)
2832 : {
2833 : Node *this_default;
2834 :
2835 716 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2836 716 : if (this_default == NULL)
2837 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2838 : parent_attno, RelationGetRelationName(relation));
2839 :
2840 : /*
2841 : * If it's a GENERATED default, it might contain Vars that
2842 : * need to be mapped to the inherited column(s)' new numbers.
2843 : * We can't do that till newattmap is ready, so just remember
2844 : * all the inherited default expressions for the moment.
2845 : */
2846 716 : inherited_defaults = lappend(inherited_defaults, this_default);
2847 716 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2848 : }
2849 : }
2850 :
2851 : /*
2852 : * Now process any inherited default expressions, adjusting attnos
2853 : * using the completed newattmap map.
2854 : */
2855 11314 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2856 : {
2857 716 : Node *this_default = (Node *) lfirst(lc1);
2858 716 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2859 : bool found_whole_row;
2860 :
2861 : /* Adjust Vars to match new table's column numbering */
2862 716 : this_default = map_variable_attnos(this_default,
2863 : 1, 0,
2864 : newattmap,
2865 : InvalidOid, &found_whole_row);
2866 :
2867 : /*
2868 : * For the moment we have to reject whole-row variables. We could
2869 : * convert them, if we knew the new table's rowtype OID, but that
2870 : * hasn't been assigned yet. (A variable could only appear in a
2871 : * generation expression, so the error message is correct.)
2872 : */
2873 716 : if (found_whole_row)
2874 0 : ereport(ERROR,
2875 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2876 : errmsg("cannot convert whole-row table reference"),
2877 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2878 : def->colname,
2879 : RelationGetRelationName(relation))));
2880 :
2881 : /*
2882 : * If we already had a default from some prior parent, check to
2883 : * see if they are the same. If so, no problem; if not, mark the
2884 : * column as having a bogus default. Below, we will complain if
2885 : * the bogus default isn't overridden by the child columns.
2886 : */
2887 : Assert(def->raw_default == NULL);
2888 716 : if (def->cooked_default == NULL)
2889 674 : def->cooked_default = this_default;
2890 42 : else if (!equal(def->cooked_default, this_default))
2891 : {
2892 36 : def->cooked_default = &bogus_marker;
2893 36 : have_bogus_defaults = true;
2894 : }
2895 : }
2896 :
2897 : /*
2898 : * Now copy the CHECK constraints of this parent, adjusting attnos
2899 : * using the completed newattmap map. Identically named constraints
2900 : * are merged if possible, else we throw error.
2901 : */
2902 10598 : if (constr && constr->num_check > 0)
2903 : {
2904 334 : ConstrCheck *check = constr->check;
2905 :
2906 1070 : for (int i = 0; i < constr->num_check; i++)
2907 : {
2908 736 : char *name = check[i].ccname;
2909 : Node *expr;
2910 : bool found_whole_row;
2911 :
2912 : /* ignore if the constraint is non-inheritable */
2913 736 : if (check[i].ccnoinherit)
2914 48 : continue;
2915 :
2916 : /* Adjust Vars to match new table's column numbering */
2917 688 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2918 : 1, 0,
2919 : newattmap,
2920 : InvalidOid, &found_whole_row);
2921 :
2922 : /*
2923 : * For the moment we have to reject whole-row variables. We
2924 : * could convert them, if we knew the new table's rowtype OID,
2925 : * but that hasn't been assigned yet.
2926 : */
2927 688 : if (found_whole_row)
2928 0 : ereport(ERROR,
2929 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2930 : errmsg("cannot convert whole-row table reference"),
2931 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2932 : name,
2933 : RelationGetRelationName(relation))));
2934 :
2935 688 : constraints = MergeCheckConstraint(constraints, name, expr,
2936 688 : check[i].ccenforced);
2937 : }
2938 : }
2939 :
2940 : /*
2941 : * Also copy the not-null constraints from this parent. The
2942 : * attnotnull markings were already installed above.
2943 : */
2944 23590 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2945 : {
2946 : Assert(nn->contype == CONSTR_NOTNULL);
2947 :
2948 2394 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2949 :
2950 2394 : nnconstraints = lappend(nnconstraints, nn);
2951 : }
2952 :
2953 10598 : free_attrmap(newattmap);
2954 :
2955 : /*
2956 : * Close the parent rel, but keep our lock on it until xact commit.
2957 : * That will prevent someone else from deleting or ALTERing the parent
2958 : * before the child is committed.
2959 : */
2960 10598 : table_close(relation, NoLock);
2961 : }
2962 :
2963 : /*
2964 : * If we had no inherited attributes, the result columns are just the
2965 : * explicitly declared columns. Otherwise, we need to merge the declared
2966 : * columns into the inherited column list. Although, we never have any
2967 : * explicitly declared columns if the table is a partition.
2968 : */
2969 63152 : if (inh_columns != NIL)
2970 : {
2971 10162 : int newcol_attno = 0;
2972 :
2973 11136 : foreach(lc, columns)
2974 : {
2975 1052 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2976 1052 : char *attributeName = newdef->colname;
2977 : int exist_attno;
2978 :
2979 : /*
2980 : * Partitions have only one parent and have no column definitions
2981 : * of their own, so conflict should never occur.
2982 : */
2983 : Assert(!is_partition);
2984 :
2985 1052 : newcol_attno++;
2986 :
2987 : /*
2988 : * Does it match some inherited column?
2989 : */
2990 1052 : exist_attno = findAttrByName(attributeName, inh_columns);
2991 1052 : if (exist_attno > 0)
2992 : {
2993 : /*
2994 : * Yes, try to merge the two column definitions.
2995 : */
2996 380 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2997 : }
2998 : else
2999 : {
3000 : /*
3001 : * No, attach new column unchanged to result columns.
3002 : */
3003 672 : inh_columns = lappend(inh_columns, newdef);
3004 : }
3005 : }
3006 :
3007 10084 : columns = inh_columns;
3008 :
3009 : /*
3010 : * Check that we haven't exceeded the legal # of columns after merging
3011 : * in inherited columns.
3012 : */
3013 10084 : if (list_length(columns) > MaxHeapAttributeNumber)
3014 0 : ereport(ERROR,
3015 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
3016 : errmsg("tables can have at most %d columns",
3017 : MaxHeapAttributeNumber)));
3018 : }
3019 :
3020 : /*
3021 : * Now that we have the column definition list for a partition, we can
3022 : * check whether the columns referenced in the column constraint specs
3023 : * actually exist. Also, merge column defaults.
3024 : */
3025 63074 : if (is_partition)
3026 : {
3027 8356 : foreach(lc, saved_columns)
3028 : {
3029 254 : ColumnDef *restdef = lfirst(lc);
3030 254 : bool found = false;
3031 : ListCell *l;
3032 :
3033 960 : foreach(l, columns)
3034 : {
3035 742 : ColumnDef *coldef = lfirst(l);
3036 :
3037 742 : if (strcmp(coldef->colname, restdef->colname) == 0)
3038 : {
3039 254 : found = true;
3040 :
3041 : /*
3042 : * Check for conflicts related to generated columns.
3043 : *
3044 : * Same rules as above: generated-ness has to match the
3045 : * parent, but the contents of the generation expression
3046 : * can be different.
3047 : */
3048 254 : if (coldef->generated)
3049 : {
3050 146 : if (restdef->raw_default && !restdef->generated)
3051 12 : ereport(ERROR,
3052 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3053 : errmsg("column \"%s\" inherits from generated column but specifies default",
3054 : restdef->colname)));
3055 134 : if (restdef->identity)
3056 0 : ereport(ERROR,
3057 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3058 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3059 : restdef->colname)));
3060 : }
3061 : else
3062 : {
3063 108 : if (restdef->generated)
3064 12 : ereport(ERROR,
3065 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3066 : errmsg("child column \"%s\" specifies generation expression",
3067 : restdef->colname),
3068 : errhint("A child table column cannot be generated unless its parent column is.")));
3069 : }
3070 :
3071 230 : if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3072 12 : ereport(ERROR,
3073 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3074 : errmsg("column \"%s\" inherits from generated column of different kind",
3075 : restdef->colname),
3076 : errdetail("Parent column is %s, child column is %s.",
3077 : coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3078 : restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3079 :
3080 : /*
3081 : * Override the parent's default value for this column
3082 : * (coldef->cooked_default) with the partition's local
3083 : * definition (restdef->raw_default), if there's one. It
3084 : * should be physically impossible to get a cooked default
3085 : * in the local definition or a raw default in the
3086 : * inherited definition, but make sure they're nulls, for
3087 : * future-proofing.
3088 : */
3089 : Assert(restdef->cooked_default == NULL);
3090 : Assert(coldef->raw_default == NULL);
3091 218 : if (restdef->raw_default)
3092 : {
3093 146 : coldef->raw_default = restdef->raw_default;
3094 146 : coldef->cooked_default = NULL;
3095 : }
3096 : }
3097 : }
3098 :
3099 : /* complain for constraints on columns not in parent */
3100 218 : if (!found)
3101 0 : ereport(ERROR,
3102 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3103 : errmsg("column \"%s\" does not exist",
3104 : restdef->colname)));
3105 : }
3106 : }
3107 :
3108 : /*
3109 : * If we found any conflicting parent default values, check to make sure
3110 : * they were overridden by the child.
3111 : */
3112 63038 : if (have_bogus_defaults)
3113 : {
3114 90 : foreach(lc, columns)
3115 : {
3116 72 : ColumnDef *def = lfirst(lc);
3117 :
3118 72 : if (def->cooked_default == &bogus_marker)
3119 : {
3120 18 : if (def->generated)
3121 12 : ereport(ERROR,
3122 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3123 : errmsg("column \"%s\" inherits conflicting generation expressions",
3124 : def->colname),
3125 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3126 : else
3127 6 : ereport(ERROR,
3128 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3129 : errmsg("column \"%s\" inherits conflicting default values",
3130 : def->colname),
3131 : errhint("To resolve the conflict, specify a default explicitly.")));
3132 : }
3133 : }
3134 : }
3135 :
3136 63020 : *supconstr = constraints;
3137 63020 : *supnotnulls = nnconstraints;
3138 :
3139 63020 : return columns;
3140 : }
3141 :
3142 :
3143 : /*
3144 : * MergeCheckConstraint
3145 : * Try to merge an inherited CHECK constraint with previous ones
3146 : *
3147 : * If we inherit identically-named constraints from multiple parents, we must
3148 : * merge them, or throw an error if they don't have identical definitions.
3149 : *
3150 : * constraints is a list of CookedConstraint structs for previous constraints.
3151 : *
3152 : * If the new constraint matches an existing one, then the existing
3153 : * constraint's inheritance count is updated. If there is a conflict (same
3154 : * name but different expression), throw an error. If the constraint neither
3155 : * matches nor conflicts with an existing one, a new constraint is appended to
3156 : * the list.
3157 : */
3158 : static List *
3159 688 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3160 : {
3161 : ListCell *lc;
3162 : CookedConstraint *newcon;
3163 :
3164 2212 : foreach(lc, constraints)
3165 : {
3166 1674 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3167 :
3168 : Assert(ccon->contype == CONSTR_CHECK);
3169 :
3170 : /* Non-matching names never conflict */
3171 1674 : if (strcmp(ccon->name, name) != 0)
3172 1524 : continue;
3173 :
3174 150 : if (equal(expr, ccon->expr))
3175 : {
3176 : /* OK to merge constraint with existing */
3177 150 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3178 : &ccon->inhcount))
3179 0 : ereport(ERROR,
3180 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3181 : errmsg("too many inheritance parents"));
3182 :
3183 : /*
3184 : * When enforceability differs, the merged constraint should be
3185 : * marked as ENFORCED because one of the parents is ENFORCED.
3186 : */
3187 150 : if (!ccon->is_enforced && is_enforced)
3188 : {
3189 48 : ccon->is_enforced = true;
3190 48 : ccon->skip_validation = false;
3191 : }
3192 :
3193 150 : return constraints;
3194 : }
3195 :
3196 0 : ereport(ERROR,
3197 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3198 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3199 : name)));
3200 : }
3201 :
3202 : /*
3203 : * Constraint couldn't be merged with an existing one and also didn't
3204 : * conflict with an existing one, so add it as a new one to the list.
3205 : */
3206 538 : newcon = palloc0_object(CookedConstraint);
3207 538 : newcon->contype = CONSTR_CHECK;
3208 538 : newcon->name = pstrdup(name);
3209 538 : newcon->expr = expr;
3210 538 : newcon->inhcount = 1;
3211 538 : newcon->is_enforced = is_enforced;
3212 538 : newcon->skip_validation = !is_enforced;
3213 538 : return lappend(constraints, newcon);
3214 : }
3215 :
3216 : /*
3217 : * MergeChildAttribute
3218 : * Merge given child attribute definition into given inherited attribute.
3219 : *
3220 : * Input arguments:
3221 : * 'inh_columns' is the list of inherited ColumnDefs.
3222 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3223 : * 'newcol_attno' is the attribute number in child table's schema definition
3224 : * 'newdef' is the column/attribute definition from the child table.
3225 : *
3226 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3227 : * ColumnDef remains unchanged.
3228 : *
3229 : * Notes:
3230 : * - The attribute is merged according to the rules laid out in the prologue
3231 : * of MergeAttributes().
3232 : * - If matching inherited attribute exists but the child attribute can not be
3233 : * merged into it, the function throws respective errors.
3234 : * - A partition can not have its own column definitions. Hence this function
3235 : * is applicable only to a regular inheritance child.
3236 : */
3237 : static void
3238 380 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3239 : {
3240 380 : char *attributeName = newdef->colname;
3241 : ColumnDef *inhdef;
3242 : Oid inhtypeid,
3243 : newtypeid;
3244 : int32 inhtypmod,
3245 : newtypmod;
3246 : Oid inhcollid,
3247 : newcollid;
3248 :
3249 380 : if (exist_attno == newcol_attno)
3250 346 : ereport(NOTICE,
3251 : (errmsg("merging column \"%s\" with inherited definition",
3252 : attributeName)));
3253 : else
3254 34 : ereport(NOTICE,
3255 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3256 : errdetail("User-specified column moved to the position of the inherited column.")));
3257 :
3258 380 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3259 :
3260 : /*
3261 : * Must have the same type and typmod
3262 : */
3263 380 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3264 380 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3265 380 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3266 12 : ereport(ERROR,
3267 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3268 : errmsg("column \"%s\" has a type conflict",
3269 : attributeName),
3270 : errdetail("%s versus %s",
3271 : format_type_with_typemod(inhtypeid, inhtypmod),
3272 : format_type_with_typemod(newtypeid, newtypmod))));
3273 :
3274 : /*
3275 : * Must have the same collation
3276 : */
3277 368 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3278 368 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3279 368 : if (inhcollid != newcollid)
3280 6 : ereport(ERROR,
3281 : (errcode(ERRCODE_COLLATION_MISMATCH),
3282 : errmsg("column \"%s\" has a collation conflict",
3283 : attributeName),
3284 : errdetail("\"%s\" versus \"%s\"",
3285 : get_collation_name(inhcollid),
3286 : get_collation_name(newcollid))));
3287 :
3288 : /*
3289 : * Identity is never inherited by a regular inheritance child. Pick
3290 : * child's identity definition if there's one.
3291 : */
3292 362 : inhdef->identity = newdef->identity;
3293 :
3294 : /*
3295 : * Copy storage parameter
3296 : */
3297 362 : if (inhdef->storage == 0)
3298 0 : inhdef->storage = newdef->storage;
3299 362 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3300 6 : ereport(ERROR,
3301 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3302 : errmsg("column \"%s\" has a storage parameter conflict",
3303 : attributeName),
3304 : errdetail("%s versus %s",
3305 : storage_name(inhdef->storage),
3306 : storage_name(newdef->storage))));
3307 :
3308 : /*
3309 : * Copy compression parameter
3310 : */
3311 356 : if (inhdef->compression == NULL)
3312 350 : inhdef->compression = newdef->compression;
3313 6 : else if (newdef->compression != NULL)
3314 : {
3315 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3316 6 : ereport(ERROR,
3317 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3318 : errmsg("column \"%s\" has a compression method conflict",
3319 : attributeName),
3320 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3321 : }
3322 :
3323 : /*
3324 : * Merge of not-null constraints = OR 'em together
3325 : */
3326 350 : inhdef->is_not_null |= newdef->is_not_null;
3327 :
3328 : /*
3329 : * Check for conflicts related to generated columns.
3330 : *
3331 : * If the parent column is generated, the child column will be made a
3332 : * generated column if it isn't already. If it is a generated column,
3333 : * we'll take its generation expression in preference to the parent's. We
3334 : * must check that the child column doesn't specify a default value or
3335 : * identity, which matches the rules for a single column in
3336 : * parse_utilcmd.c.
3337 : *
3338 : * Conversely, if the parent column is not generated, the child column
3339 : * can't be either. (We used to allow that, but it results in being able
3340 : * to override the generation expression via UPDATEs through the parent.)
3341 : */
3342 350 : if (inhdef->generated)
3343 : {
3344 62 : if (newdef->raw_default && !newdef->generated)
3345 12 : ereport(ERROR,
3346 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3347 : errmsg("column \"%s\" inherits from generated column but specifies default",
3348 : inhdef->colname)));
3349 50 : if (newdef->identity)
3350 12 : ereport(ERROR,
3351 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3352 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3353 : inhdef->colname)));
3354 : }
3355 : else
3356 : {
3357 288 : if (newdef->generated)
3358 12 : ereport(ERROR,
3359 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3360 : errmsg("child column \"%s\" specifies generation expression",
3361 : inhdef->colname),
3362 : errhint("A child table column cannot be generated unless its parent column is.")));
3363 : }
3364 :
3365 314 : if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3366 12 : ereport(ERROR,
3367 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3368 : errmsg("column \"%s\" inherits from generated column of different kind",
3369 : inhdef->colname),
3370 : errdetail("Parent column is %s, child column is %s.",
3371 : inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3372 : newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3373 :
3374 : /*
3375 : * If new def has a default, override previous default
3376 : */
3377 302 : if (newdef->raw_default != NULL)
3378 : {
3379 30 : inhdef->raw_default = newdef->raw_default;
3380 30 : inhdef->cooked_default = newdef->cooked_default;
3381 : }
3382 :
3383 : /* Mark the column as locally defined */
3384 302 : inhdef->is_local = true;
3385 302 : }
3386 :
3387 : /*
3388 : * MergeInheritedAttribute
3389 : * Merge given parent attribute definition into specified attribute
3390 : * inherited from the previous parents.
3391 : *
3392 : * Input arguments:
3393 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3394 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3395 : * 'newdef' is the new parent column/attribute definition to be merged.
3396 : *
3397 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3398 : *
3399 : * Notes:
3400 : * - The attribute is merged according to the rules laid out in the prologue
3401 : * of MergeAttributes().
3402 : * - If matching inherited attribute exists but the new attribute can not be
3403 : * merged into it, the function throws respective errors.
3404 : * - A partition inherits from only a single parent. Hence this function is
3405 : * applicable only to a regular inheritance.
3406 : */
3407 : static ColumnDef *
3408 370 : MergeInheritedAttribute(List *inh_columns,
3409 : int exist_attno,
3410 : const ColumnDef *newdef)
3411 : {
3412 370 : char *attributeName = newdef->colname;
3413 : ColumnDef *prevdef;
3414 : Oid prevtypeid,
3415 : newtypeid;
3416 : int32 prevtypmod,
3417 : newtypmod;
3418 : Oid prevcollid,
3419 : newcollid;
3420 :
3421 370 : ereport(NOTICE,
3422 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3423 : attributeName)));
3424 370 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3425 :
3426 : /*
3427 : * Must have the same type and typmod
3428 : */
3429 370 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3430 370 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3431 370 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3432 0 : ereport(ERROR,
3433 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3434 : errmsg("inherited column \"%s\" has a type conflict",
3435 : attributeName),
3436 : errdetail("%s versus %s",
3437 : format_type_with_typemod(prevtypeid, prevtypmod),
3438 : format_type_with_typemod(newtypeid, newtypmod))));
3439 :
3440 : /*
3441 : * Must have the same collation
3442 : */
3443 370 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3444 370 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3445 370 : if (prevcollid != newcollid)
3446 0 : ereport(ERROR,
3447 : (errcode(ERRCODE_COLLATION_MISMATCH),
3448 : errmsg("inherited column \"%s\" has a collation conflict",
3449 : attributeName),
3450 : errdetail("\"%s\" versus \"%s\"",
3451 : get_collation_name(prevcollid),
3452 : get_collation_name(newcollid))));
3453 :
3454 : /*
3455 : * Copy/check storage parameter
3456 : */
3457 370 : if (prevdef->storage == 0)
3458 0 : prevdef->storage = newdef->storage;
3459 370 : else if (prevdef->storage != newdef->storage)
3460 6 : ereport(ERROR,
3461 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3462 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3463 : attributeName),
3464 : errdetail("%s versus %s",
3465 : storage_name(prevdef->storage),
3466 : storage_name(newdef->storage))));
3467 :
3468 : /*
3469 : * Copy/check compression parameter
3470 : */
3471 364 : if (prevdef->compression == NULL)
3472 346 : prevdef->compression = newdef->compression;
3473 18 : else if (newdef->compression != NULL)
3474 : {
3475 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3476 6 : ereport(ERROR,
3477 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3478 : errmsg("column \"%s\" has a compression method conflict",
3479 : attributeName),
3480 : errdetail("%s versus %s",
3481 : prevdef->compression, newdef->compression)));
3482 : }
3483 :
3484 : /*
3485 : * Check for GENERATED conflicts
3486 : */
3487 358 : if (prevdef->generated != newdef->generated)
3488 24 : ereport(ERROR,
3489 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3490 : errmsg("inherited column \"%s\" has a generation conflict",
3491 : attributeName)));
3492 :
3493 : /*
3494 : * Default and other constraints are handled by the caller.
3495 : */
3496 :
3497 334 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3498 : &prevdef->inhcount))
3499 0 : ereport(ERROR,
3500 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3501 : errmsg("too many inheritance parents"));
3502 :
3503 334 : return prevdef;
3504 : }
3505 :
3506 : /*
3507 : * StoreCatalogInheritance
3508 : * Updates the system catalogs with proper inheritance information.
3509 : *
3510 : * supers is a list of the OIDs of the new relation's direct ancestors.
3511 : */
3512 : static void
3513 62360 : StoreCatalogInheritance(Oid relationId, List *supers,
3514 : bool child_is_partition)
3515 : {
3516 : Relation relation;
3517 : int32 seqNumber;
3518 : ListCell *entry;
3519 :
3520 : /*
3521 : * sanity checks
3522 : */
3523 : Assert(OidIsValid(relationId));
3524 :
3525 62360 : if (supers == NIL)
3526 52636 : return;
3527 :
3528 : /*
3529 : * Store INHERITS information in pg_inherits using direct ancestors only.
3530 : * Also enter dependencies on the direct ancestors, and make sure they are
3531 : * marked with relhassubclass = true.
3532 : *
3533 : * (Once upon a time, both direct and indirect ancestors were found here
3534 : * and then entered into pg_ipl. Since that catalog doesn't exist
3535 : * anymore, there's no need to look for indirect ancestors.)
3536 : */
3537 9724 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3538 :
3539 9724 : seqNumber = 1;
3540 19782 : foreach(entry, supers)
3541 : {
3542 10058 : Oid parentOid = lfirst_oid(entry);
3543 :
3544 10058 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3545 : child_is_partition);
3546 10058 : seqNumber++;
3547 : }
3548 :
3549 9724 : table_close(relation, RowExclusiveLock);
3550 : }
3551 :
3552 : /*
3553 : * Make catalog entries showing relationId as being an inheritance child
3554 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3555 : */
3556 : static void
3557 12544 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3558 : int32 seqNumber, Relation inhRelation,
3559 : bool child_is_partition)
3560 : {
3561 : ObjectAddress childobject,
3562 : parentobject;
3563 :
3564 : /* store the pg_inherits row */
3565 12544 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3566 :
3567 : /*
3568 : * Store a dependency too
3569 : */
3570 12544 : parentobject.classId = RelationRelationId;
3571 12544 : parentobject.objectId = parentOid;
3572 12544 : parentobject.objectSubId = 0;
3573 12544 : childobject.classId = RelationRelationId;
3574 12544 : childobject.objectId = relationId;
3575 12544 : childobject.objectSubId = 0;
3576 :
3577 12544 : recordDependencyOn(&childobject, &parentobject,
3578 : child_dependency_type(child_is_partition));
3579 :
3580 : /*
3581 : * Post creation hook of this inheritance. Since object_access_hook
3582 : * doesn't take multiple object identifiers, we relay oid of parent
3583 : * relation using auxiliary_id argument.
3584 : */
3585 12544 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3586 : relationId, 0,
3587 : parentOid, false);
3588 :
3589 : /*
3590 : * Mark the parent as having subclasses.
3591 : */
3592 12544 : SetRelationHasSubclass(parentOid, true);
3593 12544 : }
3594 :
3595 : /*
3596 : * Look for an existing column entry with the given name.
3597 : *
3598 : * Returns the index (starting with 1) if attribute already exists in columns,
3599 : * 0 if it doesn't.
3600 : */
3601 : static int
3602 22304 : findAttrByName(const char *attributeName, const List *columns)
3603 : {
3604 : ListCell *lc;
3605 22304 : int i = 1;
3606 :
3607 39880 : foreach(lc, columns)
3608 : {
3609 18326 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3610 750 : return i;
3611 :
3612 17576 : i++;
3613 : }
3614 21554 : return 0;
3615 : }
3616 :
3617 :
3618 : /*
3619 : * SetRelationHasSubclass
3620 : * Set the value of the relation's relhassubclass field in pg_class.
3621 : *
3622 : * It's always safe to set this field to true, because all SQL commands are
3623 : * ready to see true and then find no children. On the other hand, commands
3624 : * generally assume zero children if this is false.
3625 : *
3626 : * Caller must hold any self-exclusive lock until end of transaction. If the
3627 : * new value is false, caller must have acquired that lock before reading the
3628 : * evidence that justified the false value. That way, it properly waits if
3629 : * another backend is simultaneously concluding no need to change the tuple
3630 : * (new and old values are true).
3631 : *
3632 : * NOTE: an important side-effect of this operation is that an SI invalidation
3633 : * message is sent out to all backends --- including me --- causing plans
3634 : * referencing the relation to be rebuilt with the new list of children.
3635 : * This must happen even if we find that no change is needed in the pg_class
3636 : * row.
3637 : */
3638 : void
3639 15594 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3640 : {
3641 : Relation relationRelation;
3642 : HeapTuple tuple;
3643 : Form_pg_class classtuple;
3644 :
3645 : Assert(CheckRelationOidLockedByMe(relationId,
3646 : ShareUpdateExclusiveLock, false) ||
3647 : CheckRelationOidLockedByMe(relationId,
3648 : ShareRowExclusiveLock, true));
3649 :
3650 : /*
3651 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3652 : */
3653 15594 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3654 15594 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3655 15594 : if (!HeapTupleIsValid(tuple))
3656 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3657 15594 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3658 :
3659 15594 : if (classtuple->relhassubclass != relhassubclass)
3660 : {
3661 7898 : classtuple->relhassubclass = relhassubclass;
3662 7898 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3663 : }
3664 : else
3665 : {
3666 : /* no need to change tuple, but force relcache rebuild anyway */
3667 7696 : CacheInvalidateRelcacheByTuple(tuple);
3668 : }
3669 :
3670 15594 : heap_freetuple(tuple);
3671 15594 : table_close(relationRelation, RowExclusiveLock);
3672 15594 : }
3673 :
3674 : /*
3675 : * CheckRelationTableSpaceMove
3676 : * Check if relation can be moved to new tablespace.
3677 : *
3678 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3679 : *
3680 : * Returns true if the relation can be moved to the new tablespace; raises
3681 : * an error if it is not possible to do the move; returns false if the move
3682 : * would have no effect.
3683 : */
3684 : bool
3685 232 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3686 : {
3687 : Oid oldTableSpaceId;
3688 :
3689 : /*
3690 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3691 : * stored as 0.
3692 : */
3693 232 : oldTableSpaceId = rel->rd_rel->reltablespace;
3694 232 : if (newTableSpaceId == oldTableSpaceId ||
3695 224 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3696 16 : return false;
3697 :
3698 : /*
3699 : * We cannot support moving mapped relations into different tablespaces.
3700 : * (In particular this eliminates all shared catalogs.)
3701 : */
3702 216 : if (RelationIsMapped(rel))
3703 0 : ereport(ERROR,
3704 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3705 : errmsg("cannot move system relation \"%s\"",
3706 : RelationGetRelationName(rel))));
3707 :
3708 : /* Cannot move a non-shared relation into pg_global */
3709 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3710 12 : ereport(ERROR,
3711 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3712 : errmsg("only shared relations can be placed in pg_global tablespace")));
3713 :
3714 : /*
3715 : * Do not allow moving temp tables of other backends ... their local
3716 : * buffer manager is not going to cope.
3717 : */
3718 204 : if (RELATION_IS_OTHER_TEMP(rel))
3719 0 : ereport(ERROR,
3720 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3721 : errmsg("cannot move temporary tables of other sessions")));
3722 :
3723 204 : return true;
3724 : }
3725 :
3726 : /*
3727 : * SetRelationTableSpace
3728 : * Set new reltablespace and relfilenumber in pg_class entry.
3729 : *
3730 : * newTableSpaceId is the new tablespace for the relation, and
3731 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3732 : * InvalidRelFileNumber, this field is not updated.
3733 : *
3734 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3735 : *
3736 : * The caller of this routine had better check if a relation can be
3737 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3738 : * first, and is responsible for making the change visible with
3739 : * CommandCounterIncrement().
3740 : */
3741 : void
3742 204 : SetRelationTableSpace(Relation rel,
3743 : Oid newTableSpaceId,
3744 : RelFileNumber newRelFilenumber)
3745 : {
3746 : Relation pg_class;
3747 : HeapTuple tuple;
3748 : ItemPointerData otid;
3749 : Form_pg_class rd_rel;
3750 204 : Oid reloid = RelationGetRelid(rel);
3751 :
3752 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3753 :
3754 : /* Get a modifiable copy of the relation's pg_class row. */
3755 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3756 :
3757 204 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3758 204 : if (!HeapTupleIsValid(tuple))
3759 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3760 204 : otid = tuple->t_self;
3761 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3762 :
3763 : /* Update the pg_class row. */
3764 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3765 204 : InvalidOid : newTableSpaceId;
3766 204 : if (RelFileNumberIsValid(newRelFilenumber))
3767 160 : rd_rel->relfilenode = newRelFilenumber;
3768 204 : CatalogTupleUpdate(pg_class, &otid, tuple);
3769 204 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3770 :
3771 : /*
3772 : * Record dependency on tablespace. This is only required for relations
3773 : * that have no physical storage.
3774 : */
3775 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3776 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3777 : rd_rel->reltablespace);
3778 :
3779 204 : heap_freetuple(tuple);
3780 204 : table_close(pg_class, RowExclusiveLock);
3781 204 : }
3782 :
3783 : /*
3784 : * renameatt_check - basic sanity checks before attribute rename
3785 : */
3786 : static void
3787 1020 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3788 : {
3789 1020 : char relkind = classform->relkind;
3790 :
3791 1020 : if (classform->reloftype && !recursing)
3792 6 : ereport(ERROR,
3793 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3794 : errmsg("cannot rename column of typed table")));
3795 :
3796 : /*
3797 : * Renaming the columns of sequences or toast tables doesn't actually
3798 : * break anything from the system's point of view, since internal
3799 : * references are by attnum. But it doesn't seem right to allow users to
3800 : * change names that are hardcoded into the system, hence the following
3801 : * restriction.
3802 : */
3803 1014 : if (relkind != RELKIND_RELATION &&
3804 84 : relkind != RELKIND_VIEW &&
3805 84 : relkind != RELKIND_MATVIEW &&
3806 36 : relkind != RELKIND_COMPOSITE_TYPE &&
3807 36 : relkind != RELKIND_INDEX &&
3808 36 : relkind != RELKIND_PARTITIONED_INDEX &&
3809 0 : relkind != RELKIND_FOREIGN_TABLE &&
3810 : relkind != RELKIND_PARTITIONED_TABLE)
3811 0 : ereport(ERROR,
3812 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3813 : errmsg("cannot rename columns of relation \"%s\"",
3814 : NameStr(classform->relname)),
3815 : errdetail_relkind_not_supported(relkind)));
3816 :
3817 : /*
3818 : * permissions checking. only the owner of a class can change its schema.
3819 : */
3820 1014 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3821 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3822 0 : NameStr(classform->relname));
3823 1014 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3824 2 : ereport(ERROR,
3825 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3826 : errmsg("permission denied: \"%s\" is a system catalog",
3827 : NameStr(classform->relname))));
3828 1012 : }
3829 :
3830 : /*
3831 : * renameatt_internal - workhorse for renameatt
3832 : *
3833 : * Return value is the attribute number in the 'myrelid' relation.
3834 : */
3835 : static AttrNumber
3836 552 : renameatt_internal(Oid myrelid,
3837 : const char *oldattname,
3838 : const char *newattname,
3839 : bool recurse,
3840 : bool recursing,
3841 : int expected_parents,
3842 : DropBehavior behavior)
3843 : {
3844 : Relation targetrelation;
3845 : Relation attrelation;
3846 : HeapTuple atttup;
3847 : Form_pg_attribute attform;
3848 : AttrNumber attnum;
3849 :
3850 : /*
3851 : * Grab an exclusive lock on the target table, which we will NOT release
3852 : * until end of transaction.
3853 : */
3854 552 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3855 552 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3856 :
3857 : /*
3858 : * if the 'recurse' flag is set then we are supposed to rename this
3859 : * attribute in all classes that inherit from 'relname' (as well as in
3860 : * 'relname').
3861 : *
3862 : * any permissions or problems with duplicate attributes will cause the
3863 : * whole transaction to abort, which is what we want -- all or nothing.
3864 : */
3865 552 : if (recurse)
3866 : {
3867 : List *child_oids,
3868 : *child_numparents;
3869 : ListCell *lo,
3870 : *li;
3871 :
3872 : /*
3873 : * we need the number of parents for each child so that the recursive
3874 : * calls to renameatt() can determine whether there are any parents
3875 : * outside the inheritance hierarchy being processed.
3876 : */
3877 248 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3878 : &child_numparents);
3879 :
3880 : /*
3881 : * find_all_inheritors does the recursive search of the inheritance
3882 : * hierarchy, so all we have to do is process all of the relids in the
3883 : * list that it returns.
3884 : */
3885 734 : forboth(lo, child_oids, li, child_numparents)
3886 : {
3887 516 : Oid childrelid = lfirst_oid(lo);
3888 516 : int numparents = lfirst_int(li);
3889 :
3890 516 : if (childrelid == myrelid)
3891 248 : continue;
3892 : /* note we need not recurse again */
3893 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3894 : }
3895 : }
3896 : else
3897 : {
3898 : /*
3899 : * If we are told not to recurse, there had better not be any child
3900 : * tables; else the rename would put them out of step.
3901 : *
3902 : * expected_parents will only be 0 if we are not already recursing.
3903 : */
3904 340 : if (expected_parents == 0 &&
3905 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3906 12 : ereport(ERROR,
3907 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3908 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3909 : oldattname)));
3910 : }
3911 :
3912 : /* rename attributes in typed tables of composite type */
3913 510 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3914 : {
3915 : List *child_oids;
3916 : ListCell *lo;
3917 :
3918 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3919 24 : RelationGetRelationName(targetrelation),
3920 : behavior);
3921 :
3922 24 : foreach(lo, child_oids)
3923 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3924 : }
3925 :
3926 504 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3927 :
3928 504 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3929 504 : if (!HeapTupleIsValid(atttup))
3930 24 : ereport(ERROR,
3931 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3932 : errmsg("column \"%s\" does not exist",
3933 : oldattname)));
3934 480 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3935 :
3936 480 : attnum = attform->attnum;
3937 480 : if (attnum <= 0)
3938 0 : ereport(ERROR,
3939 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3940 : errmsg("cannot rename system column \"%s\"",
3941 : oldattname)));
3942 :
3943 : /*
3944 : * if the attribute is inherited, forbid the renaming. if this is a
3945 : * top-level call to renameatt(), then expected_parents will be 0, so the
3946 : * effect of this code will be to prohibit the renaming if the attribute
3947 : * is inherited at all. if this is a recursive call to renameatt(),
3948 : * expected_parents will be the number of parents the current relation has
3949 : * within the inheritance hierarchy being processed, so we'll prohibit the
3950 : * renaming only if there are additional parents from elsewhere.
3951 : */
3952 480 : if (attform->attinhcount > expected_parents)
3953 30 : ereport(ERROR,
3954 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3955 : errmsg("cannot rename inherited column \"%s\"",
3956 : oldattname)));
3957 :
3958 : /* new name should not already exist */
3959 450 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3960 :
3961 : /* apply the update */
3962 438 : namestrcpy(&(attform->attname), newattname);
3963 :
3964 438 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3965 :
3966 438 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3967 :
3968 438 : heap_freetuple(atttup);
3969 :
3970 438 : table_close(attrelation, RowExclusiveLock);
3971 :
3972 438 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3973 :
3974 438 : return attnum;
3975 : }
3976 :
3977 : /*
3978 : * Perform permissions and integrity checks before acquiring a relation lock.
3979 : */
3980 : static void
3981 424 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3982 : void *arg)
3983 : {
3984 : HeapTuple tuple;
3985 : Form_pg_class form;
3986 :
3987 424 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3988 424 : if (!HeapTupleIsValid(tuple))
3989 40 : return; /* concurrently dropped */
3990 384 : form = (Form_pg_class) GETSTRUCT(tuple);
3991 384 : renameatt_check(relid, form, false);
3992 376 : ReleaseSysCache(tuple);
3993 : }
3994 :
3995 : /*
3996 : * renameatt - changes the name of an attribute in a relation
3997 : *
3998 : * The returned ObjectAddress is that of the renamed column.
3999 : */
4000 : ObjectAddress
4001 316 : renameatt(RenameStmt *stmt)
4002 : {
4003 : Oid relid;
4004 : AttrNumber attnum;
4005 : ObjectAddress address;
4006 :
4007 : /* lock level taken here should match renameatt_internal */
4008 316 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4009 316 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4010 : RangeVarCallbackForRenameAttribute,
4011 : NULL);
4012 :
4013 302 : if (!OidIsValid(relid))
4014 : {
4015 24 : ereport(NOTICE,
4016 : (errmsg("relation \"%s\" does not exist, skipping",
4017 : stmt->relation->relname)));
4018 24 : return InvalidObjectAddress;
4019 : }
4020 :
4021 : attnum =
4022 278 : renameatt_internal(relid,
4023 278 : stmt->subname, /* old att name */
4024 278 : stmt->newname, /* new att name */
4025 278 : stmt->relation->inh, /* recursive? */
4026 : false, /* recursing? */
4027 : 0, /* expected inhcount */
4028 : stmt->behavior);
4029 :
4030 194 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4031 :
4032 194 : return address;
4033 : }
4034 :
4035 : /*
4036 : * same logic as renameatt_internal
4037 : */
4038 : static ObjectAddress
4039 90 : rename_constraint_internal(Oid myrelid,
4040 : Oid mytypid,
4041 : const char *oldconname,
4042 : const char *newconname,
4043 : bool recurse,
4044 : bool recursing,
4045 : int expected_parents)
4046 : {
4047 90 : Relation targetrelation = NULL;
4048 : Oid constraintOid;
4049 : HeapTuple tuple;
4050 : Form_pg_constraint con;
4051 : ObjectAddress address;
4052 :
4053 : Assert(!myrelid || !mytypid);
4054 :
4055 90 : if (mytypid)
4056 : {
4057 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4058 : }
4059 : else
4060 : {
4061 84 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4062 :
4063 : /*
4064 : * don't tell it whether we're recursing; we allow changing typed
4065 : * tables here
4066 : */
4067 84 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4068 :
4069 84 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4070 : }
4071 :
4072 90 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4073 90 : if (!HeapTupleIsValid(tuple))
4074 0 : elog(ERROR, "cache lookup failed for constraint %u",
4075 : constraintOid);
4076 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4077 :
4078 90 : if (myrelid &&
4079 84 : (con->contype == CONSTRAINT_CHECK ||
4080 24 : con->contype == CONSTRAINT_NOTNULL) &&
4081 66 : !con->connoinherit)
4082 : {
4083 54 : if (recurse)
4084 : {
4085 : List *child_oids,
4086 : *child_numparents;
4087 : ListCell *lo,
4088 : *li;
4089 :
4090 36 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4091 : &child_numparents);
4092 :
4093 84 : forboth(lo, child_oids, li, child_numparents)
4094 : {
4095 48 : Oid childrelid = lfirst_oid(lo);
4096 48 : int numparents = lfirst_int(li);
4097 :
4098 48 : if (childrelid == myrelid)
4099 36 : continue;
4100 :
4101 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4102 : }
4103 : }
4104 : else
4105 : {
4106 24 : if (expected_parents == 0 &&
4107 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4108 6 : ereport(ERROR,
4109 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4110 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4111 : oldconname)));
4112 : }
4113 :
4114 48 : if (con->coninhcount > expected_parents)
4115 6 : ereport(ERROR,
4116 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4117 : errmsg("cannot rename inherited constraint \"%s\"",
4118 : oldconname)));
4119 : }
4120 :
4121 78 : if (con->conindid
4122 18 : && (con->contype == CONSTRAINT_PRIMARY
4123 6 : || con->contype == CONSTRAINT_UNIQUE
4124 0 : || con->contype == CONSTRAINT_EXCLUSION))
4125 : /* rename the index; this renames the constraint as well */
4126 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4127 : else
4128 60 : RenameConstraintById(constraintOid, newconname);
4129 :
4130 78 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4131 :
4132 78 : ReleaseSysCache(tuple);
4133 :
4134 78 : if (targetrelation)
4135 : {
4136 : /*
4137 : * Invalidate relcache so as others can see the new constraint name.
4138 : */
4139 72 : CacheInvalidateRelcache(targetrelation);
4140 :
4141 72 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4142 : }
4143 :
4144 78 : return address;
4145 : }
4146 :
4147 : ObjectAddress
4148 84 : RenameConstraint(RenameStmt *stmt)
4149 : {
4150 84 : Oid relid = InvalidOid;
4151 84 : Oid typid = InvalidOid;
4152 :
4153 84 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4154 : {
4155 : Relation rel;
4156 : HeapTuple tup;
4157 :
4158 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4159 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4160 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4161 6 : if (!HeapTupleIsValid(tup))
4162 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4163 6 : checkDomainOwner(tup);
4164 6 : ReleaseSysCache(tup);
4165 6 : table_close(rel, NoLock);
4166 : }
4167 : else
4168 : {
4169 : /* lock level taken here should match rename_constraint_internal */
4170 78 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4171 78 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4172 : RangeVarCallbackForRenameAttribute,
4173 : NULL);
4174 78 : if (!OidIsValid(relid))
4175 : {
4176 6 : ereport(NOTICE,
4177 : (errmsg("relation \"%s\" does not exist, skipping",
4178 : stmt->relation->relname)));
4179 6 : return InvalidObjectAddress;
4180 : }
4181 : }
4182 :
4183 : return
4184 78 : rename_constraint_internal(relid, typid,
4185 78 : stmt->subname,
4186 78 : stmt->newname,
4187 150 : (stmt->relation &&
4188 72 : stmt->relation->inh), /* recursive? */
4189 : false, /* recursing? */
4190 : 0 /* expected inhcount */ );
4191 : }
4192 :
4193 : /*
4194 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4195 : * RENAME
4196 : */
4197 : ObjectAddress
4198 512 : RenameRelation(RenameStmt *stmt)
4199 : {
4200 512 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4201 : Oid relid;
4202 : ObjectAddress address;
4203 :
4204 : /*
4205 : * Grab an exclusive lock on the target table, index, sequence, view,
4206 : * materialized view, or foreign table, which we will NOT release until
4207 : * end of transaction.
4208 : *
4209 : * Lock level used here should match RenameRelationInternal, to avoid lock
4210 : * escalation. However, because ALTER INDEX can be used with any relation
4211 : * type, we mustn't believe without verification.
4212 : */
4213 : for (;;)
4214 12 : {
4215 : LOCKMODE lockmode;
4216 : char relkind;
4217 : bool obj_is_index;
4218 :
4219 524 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4220 :
4221 524 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4222 524 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4223 : RangeVarCallbackForAlterRelation,
4224 : stmt);
4225 :
4226 474 : if (!OidIsValid(relid))
4227 : {
4228 18 : ereport(NOTICE,
4229 : (errmsg("relation \"%s\" does not exist, skipping",
4230 : stmt->relation->relname)));
4231 18 : return InvalidObjectAddress;
4232 : }
4233 :
4234 : /*
4235 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4236 : * to rename a table), but we might've used the wrong lock level. If
4237 : * that happens, retry with the correct lock level. We don't bother
4238 : * if we already acquired AccessExclusiveLock with an index, however.
4239 : */
4240 456 : relkind = get_rel_relkind(relid);
4241 456 : obj_is_index = (relkind == RELKIND_INDEX ||
4242 : relkind == RELKIND_PARTITIONED_INDEX);
4243 456 : if (obj_is_index || is_index_stmt == obj_is_index)
4244 : break;
4245 :
4246 12 : UnlockRelationOid(relid, lockmode);
4247 12 : is_index_stmt = obj_is_index;
4248 : }
4249 :
4250 : /* Do the work */
4251 444 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4252 :
4253 432 : ObjectAddressSet(address, RelationRelationId, relid);
4254 :
4255 432 : return address;
4256 : }
4257 :
4258 : /*
4259 : * RenameRelationInternal - change the name of a relation
4260 : */
4261 : void
4262 1676 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4263 : {
4264 : Relation targetrelation;
4265 : Relation relrelation; /* for RELATION relation */
4266 : ItemPointerData otid;
4267 : HeapTuple reltup;
4268 : Form_pg_class relform;
4269 : Oid namespaceId;
4270 :
4271 : /*
4272 : * Grab a lock on the target relation, which we will NOT release until end
4273 : * of transaction. We need at least a self-exclusive lock so that
4274 : * concurrent DDL doesn't overwrite the rename if they start updating
4275 : * while still seeing the old version. The lock also guards against
4276 : * triggering relcache reloads in concurrent sessions, which might not
4277 : * handle this information changing under them. For indexes, we can use a
4278 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4279 : * specially.
4280 : */
4281 1676 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4282 1676 : namespaceId = RelationGetNamespace(targetrelation);
4283 :
4284 : /*
4285 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4286 : */
4287 1676 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4288 :
4289 1676 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4290 1676 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4291 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4292 1676 : otid = reltup->t_self;
4293 1676 : relform = (Form_pg_class) GETSTRUCT(reltup);
4294 :
4295 1676 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4296 12 : ereport(ERROR,
4297 : (errcode(ERRCODE_DUPLICATE_TABLE),
4298 : errmsg("relation \"%s\" already exists",
4299 : newrelname)));
4300 :
4301 : /*
4302 : * RenameRelation is careful not to believe the caller's idea of the
4303 : * relation kind being handled. We don't have to worry about this, but
4304 : * let's not be totally oblivious to it. We can process an index as
4305 : * not-an-index, but not the other way around.
4306 : */
4307 : Assert(!is_index ||
4308 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4309 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4310 :
4311 : /*
4312 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4313 : * because it's a copy...)
4314 : */
4315 1664 : namestrcpy(&(relform->relname), newrelname);
4316 :
4317 1664 : CatalogTupleUpdate(relrelation, &otid, reltup);
4318 1664 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4319 :
4320 1664 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4321 : InvalidOid, is_internal);
4322 :
4323 1664 : heap_freetuple(reltup);
4324 1664 : table_close(relrelation, RowExclusiveLock);
4325 :
4326 : /*
4327 : * Also rename the associated type, if any.
4328 : */
4329 1664 : if (OidIsValid(targetrelation->rd_rel->reltype))
4330 126 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4331 : newrelname, namespaceId);
4332 :
4333 : /*
4334 : * Also rename the associated constraint, if any.
4335 : */
4336 1664 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4337 874 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4338 : {
4339 808 : Oid constraintId = get_index_constraint(myrelid);
4340 :
4341 808 : if (OidIsValid(constraintId))
4342 36 : RenameConstraintById(constraintId, newrelname);
4343 : }
4344 :
4345 : /*
4346 : * Close rel, but keep lock!
4347 : */
4348 1664 : relation_close(targetrelation, NoLock);
4349 1664 : }
4350 :
4351 : /*
4352 : * ResetRelRewrite - reset relrewrite
4353 : */
4354 : void
4355 596 : ResetRelRewrite(Oid myrelid)
4356 : {
4357 : Relation relrelation; /* for RELATION relation */
4358 : HeapTuple reltup;
4359 : Form_pg_class relform;
4360 :
4361 : /*
4362 : * Find relation's pg_class tuple.
4363 : */
4364 596 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4365 :
4366 596 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4367 596 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4368 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4369 596 : relform = (Form_pg_class) GETSTRUCT(reltup);
4370 :
4371 : /*
4372 : * Update pg_class tuple.
4373 : */
4374 596 : relform->relrewrite = InvalidOid;
4375 :
4376 596 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4377 :
4378 596 : heap_freetuple(reltup);
4379 596 : table_close(relrelation, RowExclusiveLock);
4380 596 : }
4381 :
4382 : /*
4383 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4384 : * any open reference to the target table besides the one just acquired by
4385 : * the calling command; this implies there's an open cursor or active plan.
4386 : * We need this check because our lock doesn't protect us against stomping
4387 : * on our own foot, only other people's feet!
4388 : *
4389 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4390 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4391 : * possibly be relaxed to only error out for certain types of alterations.
4392 : * But the use-case for allowing any of these things is not obvious, so we
4393 : * won't work hard at it for now.
4394 : *
4395 : * We also reject these commands if there are any pending AFTER trigger events
4396 : * for the rel. This is certainly necessary for the rewriting variants of
4397 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4398 : * events would try to fetch the wrong tuples. It might be overly cautious
4399 : * in other cases, but again it seems better to err on the side of paranoia.
4400 : *
4401 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4402 : * we are worried about active indexscans on the index. The trigger-event
4403 : * check can be skipped, since we are doing no damage to the parent table.
4404 : *
4405 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4406 : */
4407 : void
4408 170766 : CheckTableNotInUse(Relation rel, const char *stmt)
4409 : {
4410 : int expected_refcnt;
4411 :
4412 170766 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4413 170766 : if (rel->rd_refcnt != expected_refcnt)
4414 42 : ereport(ERROR,
4415 : (errcode(ERRCODE_OBJECT_IN_USE),
4416 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4417 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4418 : stmt, RelationGetRelationName(rel))));
4419 :
4420 170724 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4421 278378 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4422 138148 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4423 18 : ereport(ERROR,
4424 : (errcode(ERRCODE_OBJECT_IN_USE),
4425 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4426 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4427 : stmt, RelationGetRelationName(rel))));
4428 170706 : }
4429 :
4430 : /*
4431 : * CheckAlterTableIsSafe
4432 : * Verify that it's safe to allow ALTER TABLE on this relation.
4433 : *
4434 : * This consists of CheckTableNotInUse() plus a check that the relation
4435 : * isn't another session's temp table. We must split out the temp-table
4436 : * check because there are callers of CheckTableNotInUse() that don't want
4437 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4438 : * an orphaned temp schema.) Compare truncate_check_activity().
4439 : */
4440 : static void
4441 61480 : CheckAlterTableIsSafe(Relation rel)
4442 : {
4443 : /*
4444 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4445 : * manager is not going to cope if we need to change the table's contents.
4446 : * Even if we don't, there may be optimizations that assume temp tables
4447 : * aren't subject to such interference.
4448 : */
4449 61480 : if (RELATION_IS_OTHER_TEMP(rel))
4450 0 : ereport(ERROR,
4451 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4452 : errmsg("cannot alter temporary tables of other sessions")));
4453 :
4454 : /*
4455 : * Also check for active uses of the relation in the current transaction,
4456 : * including open scans and pending AFTER trigger events.
4457 : */
4458 61480 : CheckTableNotInUse(rel, "ALTER TABLE");
4459 61444 : }
4460 :
4461 : /*
4462 : * AlterTableLookupRelation
4463 : * Look up, and lock, the OID for the relation named by an alter table
4464 : * statement.
4465 : */
4466 : Oid
4467 32466 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4468 : {
4469 64844 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4470 32466 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4471 : RangeVarCallbackForAlterRelation,
4472 : stmt);
4473 : }
4474 :
4475 : /*
4476 : * AlterTable
4477 : * Execute ALTER TABLE, which can be a list of subcommands
4478 : *
4479 : * ALTER TABLE is performed in three phases:
4480 : * 1. Examine subcommands and perform pre-transformation checking.
4481 : * 2. Validate and transform subcommands, and update system catalogs.
4482 : * 3. Scan table(s) to check new constraints, and optionally recopy
4483 : * the data into new table(s).
4484 : * Phase 3 is not performed unless one or more of the subcommands requires
4485 : * it. The intention of this design is to allow multiple independent
4486 : * updates of the table schema to be performed with only one pass over the
4487 : * data.
4488 : *
4489 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4490 : * each table to be affected (there may be multiple affected tables if the
4491 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4492 : * validation of the subcommands. Because earlier subcommands may change
4493 : * the catalog state seen by later commands, there are limits to what can
4494 : * be done in this phase. Generally, this phase acquires table locks,
4495 : * checks permissions and relkind, and recurses to find child tables.
4496 : *
4497 : * ATRewriteCatalogs performs phase 2 for each affected table.
4498 : * Certain subcommands need to be performed before others to avoid
4499 : * unnecessary conflicts; for example, DROP COLUMN should come before
4500 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4501 : * lists, one for each logical "pass" of phase 2.
4502 : *
4503 : * ATRewriteTables performs phase 3 for those tables that need it.
4504 : *
4505 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4506 : * since phase 1 already does it. However, for certain subcommand types
4507 : * it is only possible to determine how to recurse at phase 2 time; for
4508 : * those cases, phase 1 sets the cmd->recurse flag.
4509 : *
4510 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4511 : * the whole operation; we don't have to do anything special to clean up.
4512 : *
4513 : * The caller must lock the relation, with an appropriate lock level
4514 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4515 : * or higher. We pass the lock level down
4516 : * so that we can apply it recursively to inherited tables. Note that the
4517 : * lock level we want as we recurse might well be higher than required for
4518 : * that specific subcommand. So we pass down the overall lock requirement,
4519 : * rather than reassess it at lower levels.
4520 : *
4521 : * The caller also provides a "context" which is to be passed back to
4522 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4523 : * Some of the fields therein, such as the relid, are used here as well.
4524 : */
4525 : void
4526 32240 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4527 : AlterTableUtilityContext *context)
4528 : {
4529 : Relation rel;
4530 :
4531 : /* Caller is required to provide an adequate lock. */
4532 32240 : rel = relation_open(context->relid, NoLock);
4533 :
4534 32240 : CheckAlterTableIsSafe(rel);
4535 :
4536 32222 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4537 28514 : }
4538 :
4539 : /*
4540 : * AlterTableInternal
4541 : *
4542 : * ALTER TABLE with target specified by OID
4543 : *
4544 : * We do not reject if the relation is already open, because it's quite
4545 : * likely that one or more layers of caller have it open. That means it
4546 : * is unsafe to use this entry point for alterations that could break
4547 : * existing query plans. On the assumption it's not used for such, we
4548 : * don't have to reject pending AFTER triggers, either.
4549 : *
4550 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4551 : * used for any subcommand types that require parse transformation or
4552 : * could generate subcommands that have to be passed to ProcessUtility.
4553 : */
4554 : void
4555 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4556 : {
4557 : Relation rel;
4558 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4559 :
4560 278 : rel = relation_open(relid, lockmode);
4561 :
4562 278 : EventTriggerAlterTableRelid(relid);
4563 :
4564 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4565 278 : }
4566 :
4567 : /*
4568 : * AlterTableGetLockLevel
4569 : *
4570 : * Sets the overall lock level required for the supplied list of subcommands.
4571 : * Policy for doing this set according to needs of AlterTable(), see
4572 : * comments there for overall explanation.
4573 : *
4574 : * Function is called before and after parsing, so it must give same
4575 : * answer each time it is called. Some subcommands are transformed
4576 : * into other subcommand types, so the transform must never be made to a
4577 : * lower lock level than previously assigned. All transforms are noted below.
4578 : *
4579 : * Since this is called before we lock the table we cannot use table metadata
4580 : * to influence the type of lock we acquire.
4581 : *
4582 : * There should be no lockmodes hardcoded into the subcommand functions. All
4583 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4584 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4585 : * and does not travel through this section of code and cannot be combined with
4586 : * any of the subcommands given here.
4587 : *
4588 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4589 : * so any changes that might affect SELECTs running on standbys need to use
4590 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4591 : * have a solution for that also.
4592 : *
4593 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4594 : * that takes a lock less than AccessExclusiveLock can change object definitions
4595 : * while pg_dump is running. Be careful to check that the appropriate data is
4596 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4597 : * otherwise we might end up with an inconsistent dump that can't restore.
4598 : */
4599 : LOCKMODE
4600 32744 : AlterTableGetLockLevel(List *cmds)
4601 : {
4602 : /*
4603 : * This only works if we read catalog tables using MVCC snapshots.
4604 : */
4605 : ListCell *lcmd;
4606 32744 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4607 :
4608 66684 : foreach(lcmd, cmds)
4609 : {
4610 33940 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4611 33940 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4612 :
4613 33940 : switch (cmd->subtype)
4614 : {
4615 : /*
4616 : * These subcommands rewrite the heap, so require full locks.
4617 : */
4618 3742 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4619 : * to SELECT */
4620 : case AT_SetAccessMethod: /* must rewrite heap */
4621 : case AT_SetTableSpace: /* must rewrite heap */
4622 : case AT_AlterColumnType: /* must rewrite heap */
4623 3742 : cmd_lockmode = AccessExclusiveLock;
4624 3742 : break;
4625 :
4626 : /*
4627 : * These subcommands may require addition of toast tables. If
4628 : * we add a toast table to a table currently being scanned, we
4629 : * might miss data added to the new toast table by concurrent
4630 : * insert transactions.
4631 : */
4632 238 : case AT_SetStorage: /* may add toast tables, see
4633 : * ATRewriteCatalogs() */
4634 238 : cmd_lockmode = AccessExclusiveLock;
4635 238 : break;
4636 :
4637 : /*
4638 : * Removing constraints can affect SELECTs that have been
4639 : * optimized assuming the constraint holds true. See also
4640 : * CloneFkReferenced.
4641 : */
4642 1130 : case AT_DropConstraint: /* as DROP INDEX */
4643 : case AT_DropNotNull: /* may change some SQL plans */
4644 1130 : cmd_lockmode = AccessExclusiveLock;
4645 1130 : break;
4646 :
4647 : /*
4648 : * Subcommands that may be visible to concurrent SELECTs
4649 : */
4650 1758 : case AT_DropColumn: /* change visible to SELECT */
4651 : case AT_AddColumnToView: /* CREATE VIEW */
4652 : case AT_DropOids: /* used to equiv to DropColumn */
4653 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4654 : case AT_EnableReplicaRule: /* may change SELECT rules */
4655 : case AT_EnableRule: /* may change SELECT rules */
4656 : case AT_DisableRule: /* may change SELECT rules */
4657 1758 : cmd_lockmode = AccessExclusiveLock;
4658 1758 : break;
4659 :
4660 : /*
4661 : * Changing owner may remove implicit SELECT privileges
4662 : */
4663 2030 : case AT_ChangeOwner: /* change visible to SELECT */
4664 2030 : cmd_lockmode = AccessExclusiveLock;
4665 2030 : break;
4666 :
4667 : /*
4668 : * Changing foreign table options may affect optimization.
4669 : */
4670 254 : case AT_GenericOptions:
4671 : case AT_AlterColumnGenericOptions:
4672 254 : cmd_lockmode = AccessExclusiveLock;
4673 254 : break;
4674 :
4675 : /*
4676 : * These subcommands affect write operations only.
4677 : */
4678 342 : case AT_EnableTrig:
4679 : case AT_EnableAlwaysTrig:
4680 : case AT_EnableReplicaTrig:
4681 : case AT_EnableTrigAll:
4682 : case AT_EnableTrigUser:
4683 : case AT_DisableTrig:
4684 : case AT_DisableTrigAll:
4685 : case AT_DisableTrigUser:
4686 342 : cmd_lockmode = ShareRowExclusiveLock;
4687 342 : break;
4688 :
4689 : /*
4690 : * These subcommands affect write operations only. XXX
4691 : * Theoretically, these could be ShareRowExclusiveLock.
4692 : */
4693 2954 : case AT_ColumnDefault:
4694 : case AT_CookedColumnDefault:
4695 : case AT_AlterConstraint:
4696 : case AT_AddIndex: /* from ADD CONSTRAINT */
4697 : case AT_AddIndexConstraint:
4698 : case AT_ReplicaIdentity:
4699 : case AT_SetNotNull:
4700 : case AT_EnableRowSecurity:
4701 : case AT_DisableRowSecurity:
4702 : case AT_ForceRowSecurity:
4703 : case AT_NoForceRowSecurity:
4704 : case AT_AddIdentity:
4705 : case AT_DropIdentity:
4706 : case AT_SetIdentity:
4707 : case AT_SetExpression:
4708 : case AT_DropExpression:
4709 : case AT_SetCompression:
4710 2954 : cmd_lockmode = AccessExclusiveLock;
4711 2954 : break;
4712 :
4713 15614 : case AT_AddConstraint:
4714 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4715 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4716 15614 : if (IsA(cmd->def, Constraint))
4717 : {
4718 15614 : Constraint *con = (Constraint *) cmd->def;
4719 :
4720 15614 : switch (con->contype)
4721 : {
4722 11900 : case CONSTR_EXCLUSION:
4723 : case CONSTR_PRIMARY:
4724 : case CONSTR_UNIQUE:
4725 :
4726 : /*
4727 : * Cases essentially the same as CREATE INDEX. We
4728 : * could reduce the lock strength to ShareLock if
4729 : * we can work out how to allow concurrent catalog
4730 : * updates. XXX Might be set down to
4731 : * ShareRowExclusiveLock but requires further
4732 : * analysis.
4733 : */
4734 11900 : cmd_lockmode = AccessExclusiveLock;
4735 11900 : break;
4736 2594 : case CONSTR_FOREIGN:
4737 :
4738 : /*
4739 : * We add triggers to both tables when we add a
4740 : * Foreign Key, so the lock level must be at least
4741 : * as strong as CREATE TRIGGER.
4742 : */
4743 2594 : cmd_lockmode = ShareRowExclusiveLock;
4744 2594 : break;
4745 :
4746 1120 : default:
4747 1120 : cmd_lockmode = AccessExclusiveLock;
4748 : }
4749 : }
4750 15614 : break;
4751 :
4752 : /*
4753 : * These subcommands affect inheritance behaviour. Queries
4754 : * started before us will continue to see the old inheritance
4755 : * behaviour, while queries started after we commit will see
4756 : * new behaviour. No need to prevent reads or writes to the
4757 : * subtable while we hook it up though. Changing the TupDesc
4758 : * may be a problem, so keep highest lock.
4759 : */
4760 558 : case AT_AddInherit:
4761 : case AT_DropInherit:
4762 558 : cmd_lockmode = AccessExclusiveLock;
4763 558 : break;
4764 :
4765 : /*
4766 : * These subcommands affect implicit row type conversion. They
4767 : * have affects similar to CREATE/DROP CAST on queries. don't
4768 : * provide for invalidating parse trees as a result of such
4769 : * changes, so we keep these at AccessExclusiveLock.
4770 : */
4771 72 : case AT_AddOf:
4772 : case AT_DropOf:
4773 72 : cmd_lockmode = AccessExclusiveLock;
4774 72 : break;
4775 :
4776 : /*
4777 : * Only used by CREATE OR REPLACE VIEW which must conflict
4778 : * with an SELECTs currently using the view.
4779 : */
4780 194 : case AT_ReplaceRelOptions:
4781 194 : cmd_lockmode = AccessExclusiveLock;
4782 194 : break;
4783 :
4784 : /*
4785 : * These subcommands affect general strategies for performance
4786 : * and maintenance, though don't change the semantic results
4787 : * from normal data reads and writes. Delaying an ALTER TABLE
4788 : * behind currently active writes only delays the point where
4789 : * the new strategy begins to take effect, so there is no
4790 : * benefit in waiting. In this case the minimum restriction
4791 : * applies: we don't currently allow concurrent catalog
4792 : * updates.
4793 : */
4794 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4795 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4796 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4797 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4798 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4799 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4800 234 : break;
4801 :
4802 112 : case AT_SetLogged:
4803 : case AT_SetUnLogged:
4804 112 : cmd_lockmode = AccessExclusiveLock;
4805 112 : break;
4806 :
4807 476 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4808 476 : cmd_lockmode = ShareUpdateExclusiveLock;
4809 476 : break;
4810 :
4811 : /*
4812 : * Rel options are more complex than first appears. Options
4813 : * are set here for tables, views and indexes; for historical
4814 : * reasons these can all be used with ALTER TABLE, so we can't
4815 : * decide between them using the basic grammar.
4816 : */
4817 770 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4818 : * getTables() */
4819 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4820 : * getTables() */
4821 770 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4822 770 : break;
4823 :
4824 2846 : case AT_AttachPartition:
4825 2846 : cmd_lockmode = ShareUpdateExclusiveLock;
4826 2846 : break;
4827 :
4828 596 : case AT_DetachPartition:
4829 596 : if (((PartitionCmd *) cmd->def)->concurrent)
4830 164 : cmd_lockmode = ShareUpdateExclusiveLock;
4831 : else
4832 432 : cmd_lockmode = AccessExclusiveLock;
4833 596 : break;
4834 :
4835 20 : case AT_DetachPartitionFinalize:
4836 20 : cmd_lockmode = ShareUpdateExclusiveLock;
4837 20 : break;
4838 :
4839 0 : default: /* oops */
4840 0 : elog(ERROR, "unrecognized alter table type: %d",
4841 : (int) cmd->subtype);
4842 : break;
4843 : }
4844 :
4845 : /*
4846 : * Take the greatest lockmode from any subcommand
4847 : */
4848 33940 : if (cmd_lockmode > lockmode)
4849 28422 : lockmode = cmd_lockmode;
4850 : }
4851 :
4852 32744 : return lockmode;
4853 : }
4854 :
4855 : /*
4856 : * ATController provides top level control over the phases.
4857 : *
4858 : * parsetree is passed in to allow it to be passed to event triggers
4859 : * when requested.
4860 : */
4861 : static void
4862 32500 : ATController(AlterTableStmt *parsetree,
4863 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4864 : AlterTableUtilityContext *context)
4865 : {
4866 32500 : List *wqueue = NIL;
4867 : ListCell *lcmd;
4868 :
4869 : /* Phase 1: preliminary examination of commands, create work queue */
4870 65784 : foreach(lcmd, cmds)
4871 : {
4872 33690 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4873 :
4874 33690 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4875 : }
4876 :
4877 : /* Close the relation, but keep lock until commit */
4878 32094 : relation_close(rel, NoLock);
4879 :
4880 : /* Phase 2: update system catalogs */
4881 32094 : ATRewriteCatalogs(&wqueue, lockmode, context);
4882 :
4883 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4884 29266 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4885 28792 : }
4886 :
4887 : /*
4888 : * ATPrepCmd
4889 : *
4890 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4891 : * recursion and permission checks.
4892 : *
4893 : * Caller must have acquired appropriate lock type on relation already.
4894 : * This lock should be held until commit.
4895 : */
4896 : static void
4897 34630 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4898 : bool recurse, bool recursing, LOCKMODE lockmode,
4899 : AlterTableUtilityContext *context)
4900 : {
4901 : AlteredTableInfo *tab;
4902 34630 : AlterTablePass pass = AT_PASS_UNSET;
4903 :
4904 : /* Find or create work queue entry for this table */
4905 34630 : tab = ATGetQueueEntry(wqueue, rel);
4906 :
4907 : /*
4908 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4909 : * partitions that are pending detach.
4910 : */
4911 34630 : if (rel->rd_rel->relispartition &&
4912 2692 : cmd->subtype != AT_DetachPartitionFinalize &&
4913 1346 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4914 2 : ereport(ERROR,
4915 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4916 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4917 : RelationGetRelationName(rel)),
4918 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4919 :
4920 : /*
4921 : * Copy the original subcommand for each table, so we can scribble on it.
4922 : * This avoids conflicts when different child tables need to make
4923 : * different parse transformations (for example, the same column may have
4924 : * different column numbers in different children).
4925 : */
4926 34628 : cmd = copyObject(cmd);
4927 :
4928 : /*
4929 : * Do permissions and relkind checking, recursion to child tables if
4930 : * needed, and any additional phase-1 processing needed. (But beware of
4931 : * adding any processing that looks at table details that another
4932 : * subcommand could change. In some cases we reject multiple subcommands
4933 : * that could try to change the same state in contrary ways.)
4934 : */
4935 34628 : switch (cmd->subtype)
4936 : {
4937 2190 : case AT_AddColumn: /* ADD COLUMN */
4938 2190 : ATSimplePermissions(cmd->subtype, rel,
4939 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4940 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4941 2190 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4942 : lockmode, context);
4943 : /* Recursion occurs during execution phase */
4944 2178 : pass = AT_PASS_ADD_COL;
4945 2178 : break;
4946 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4947 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4948 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4949 : lockmode, context);
4950 : /* Recursion occurs during execution phase */
4951 24 : pass = AT_PASS_ADD_COL;
4952 24 : break;
4953 620 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4954 :
4955 : /*
4956 : * We allow defaults on views so that INSERT into a view can have
4957 : * default-ish behavior. This works because the rewriter
4958 : * substitutes default values into INSERTs before it expands
4959 : * rules.
4960 : */
4961 620 : ATSimplePermissions(cmd->subtype, rel,
4962 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4963 : ATT_FOREIGN_TABLE);
4964 620 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4965 : /* No command-specific prep needed */
4966 620 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4967 620 : break;
4968 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4969 : /* This is currently used only in CREATE TABLE */
4970 : /* (so the permission check really isn't necessary) */
4971 80 : ATSimplePermissions(cmd->subtype, rel,
4972 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4973 : /* This command never recurses */
4974 80 : pass = AT_PASS_ADD_OTHERCONSTR;
4975 80 : break;
4976 172 : case AT_AddIdentity:
4977 172 : ATSimplePermissions(cmd->subtype, rel,
4978 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4979 : ATT_FOREIGN_TABLE);
4980 : /* Set up recursion for phase 2; no other prep needed */
4981 172 : if (recurse)
4982 166 : cmd->recurse = true;
4983 172 : pass = AT_PASS_ADD_OTHERCONSTR;
4984 172 : break;
4985 62 : case AT_SetIdentity:
4986 62 : ATSimplePermissions(cmd->subtype, rel,
4987 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4988 : ATT_FOREIGN_TABLE);
4989 : /* Set up recursion for phase 2; no other prep needed */
4990 62 : if (recurse)
4991 56 : cmd->recurse = true;
4992 : /* This should run after AddIdentity, so do it in MISC pass */
4993 62 : pass = AT_PASS_MISC;
4994 62 : break;
4995 56 : case AT_DropIdentity:
4996 56 : ATSimplePermissions(cmd->subtype, rel,
4997 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4998 : ATT_FOREIGN_TABLE);
4999 : /* Set up recursion for phase 2; no other prep needed */
5000 56 : if (recurse)
5001 50 : cmd->recurse = true;
5002 56 : pass = AT_PASS_DROP;
5003 56 : break;
5004 274 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5005 274 : ATSimplePermissions(cmd->subtype, rel,
5006 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5007 : /* Set up recursion for phase 2; no other prep needed */
5008 268 : if (recurse)
5009 250 : cmd->recurse = true;
5010 268 : pass = AT_PASS_DROP;
5011 268 : break;
5012 420 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5013 420 : ATSimplePermissions(cmd->subtype, rel,
5014 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5015 : /* Set up recursion for phase 2; no other prep needed */
5016 414 : if (recurse)
5017 384 : cmd->recurse = true;
5018 414 : pass = AT_PASS_COL_ATTRS;
5019 414 : break;
5020 222 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5021 222 : ATSimplePermissions(cmd->subtype, rel,
5022 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5023 222 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5024 222 : pass = AT_PASS_SET_EXPRESSION;
5025 222 : break;
5026 86 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5027 86 : ATSimplePermissions(cmd->subtype, rel,
5028 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5029 86 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5030 86 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5031 62 : pass = AT_PASS_DROP;
5032 62 : break;
5033 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5034 164 : ATSimplePermissions(cmd->subtype, rel,
5035 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
5036 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
5037 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5038 : /* No command-specific prep needed */
5039 164 : pass = AT_PASS_MISC;
5040 164 : break;
5041 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5042 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5043 44 : ATSimplePermissions(cmd->subtype, rel,
5044 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5045 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5046 : /* This command never recurses */
5047 32 : pass = AT_PASS_MISC;
5048 32 : break;
5049 260 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5050 260 : ATSimplePermissions(cmd->subtype, rel,
5051 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5052 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5053 260 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5054 : /* No command-specific prep needed */
5055 260 : pass = AT_PASS_MISC;
5056 260 : break;
5057 78 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5058 78 : ATSimplePermissions(cmd->subtype, rel,
5059 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5060 : /* This command never recurses */
5061 : /* No command-specific prep needed */
5062 78 : pass = AT_PASS_MISC;
5063 78 : break;
5064 1664 : case AT_DropColumn: /* DROP COLUMN */
5065 1664 : ATSimplePermissions(cmd->subtype, rel,
5066 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5067 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5068 1658 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5069 : lockmode, context);
5070 : /* Recursion occurs during execution phase */
5071 1646 : pass = AT_PASS_DROP;
5072 1646 : break;
5073 0 : case AT_AddIndex: /* ADD INDEX */
5074 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5075 : /* This command never recurses */
5076 : /* No command-specific prep needed */
5077 0 : pass = AT_PASS_ADD_INDEX;
5078 0 : break;
5079 16078 : case AT_AddConstraint: /* ADD CONSTRAINT */
5080 16078 : ATSimplePermissions(cmd->subtype, rel,
5081 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5082 16078 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5083 16048 : if (recurse)
5084 : {
5085 : /* recurses at exec time; lock descendants and set flag */
5086 15680 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5087 15680 : cmd->recurse = true;
5088 : }
5089 16048 : pass = AT_PASS_ADD_CONSTR;
5090 16048 : break;
5091 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5092 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5093 : /* This command never recurses */
5094 : /* No command-specific prep needed */
5095 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5096 0 : break;
5097 818 : case AT_DropConstraint: /* DROP CONSTRAINT */
5098 818 : ATSimplePermissions(cmd->subtype, rel,
5099 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5100 818 : ATCheckPartitionsNotInUse(rel, lockmode);
5101 : /* Other recursion occurs during execution phase */
5102 : /* No command-specific prep needed except saving recurse flag */
5103 812 : if (recurse)
5104 776 : cmd->recurse = true;
5105 812 : pass = AT_PASS_DROP;
5106 812 : break;
5107 1432 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5108 1432 : ATSimplePermissions(cmd->subtype, rel,
5109 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5110 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5111 : /* See comments for ATPrepAlterColumnType */
5112 1432 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5113 : AT_PASS_UNSET, context);
5114 : Assert(cmd != NULL);
5115 : /* Performs own recursion */
5116 1426 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5117 : lockmode, context);
5118 1228 : pass = AT_PASS_ALTER_TYPE;
5119 1228 : break;
5120 172 : case AT_AlterColumnGenericOptions:
5121 172 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5122 : /* This command never recurses */
5123 : /* No command-specific prep needed */
5124 172 : pass = AT_PASS_MISC;
5125 172 : break;
5126 2006 : case AT_ChangeOwner: /* ALTER OWNER */
5127 : /* This command never recurses */
5128 : /* No command-specific prep needed */
5129 2006 : pass = AT_PASS_MISC;
5130 2006 : break;
5131 64 : case AT_ClusterOn: /* CLUSTER ON */
5132 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5133 64 : ATSimplePermissions(cmd->subtype, rel,
5134 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5135 : /* These commands never recurse */
5136 : /* No command-specific prep needed */
5137 64 : pass = AT_PASS_MISC;
5138 64 : break;
5139 112 : case AT_SetLogged: /* SET LOGGED */
5140 : case AT_SetUnLogged: /* SET UNLOGGED */
5141 112 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5142 100 : if (tab->chgPersistence)
5143 0 : ereport(ERROR,
5144 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5145 : errmsg("cannot change persistence setting twice")));
5146 100 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5147 88 : pass = AT_PASS_MISC;
5148 88 : break;
5149 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5150 6 : ATSimplePermissions(cmd->subtype, rel,
5151 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5152 6 : pass = AT_PASS_DROP;
5153 6 : break;
5154 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5155 128 : ATSimplePermissions(cmd->subtype, rel,
5156 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5157 :
5158 : /* check if another access method change was already requested */
5159 128 : if (tab->chgAccessMethod)
5160 18 : ereport(ERROR,
5161 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5162 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5163 :
5164 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5165 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5166 110 : break;
5167 164 : case AT_SetTableSpace: /* SET TABLESPACE */
5168 164 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5169 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5170 : /* This command never recurses */
5171 164 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5172 164 : pass = AT_PASS_MISC; /* doesn't actually matter */
5173 164 : break;
5174 962 : case AT_SetRelOptions: /* SET (...) */
5175 : case AT_ResetRelOptions: /* RESET (...) */
5176 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5177 962 : ATSimplePermissions(cmd->subtype, rel,
5178 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5179 : ATT_MATVIEW | ATT_INDEX);
5180 : /* This command never recurses */
5181 : /* No command-specific prep needed */
5182 960 : pass = AT_PASS_MISC;
5183 960 : break;
5184 464 : case AT_AddInherit: /* INHERIT */
5185 464 : ATSimplePermissions(cmd->subtype, rel,
5186 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5187 : /* This command never recurses */
5188 464 : ATPrepAddInherit(rel);
5189 446 : pass = AT_PASS_MISC;
5190 446 : break;
5191 94 : case AT_DropInherit: /* NO INHERIT */
5192 94 : ATSimplePermissions(cmd->subtype, rel,
5193 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5194 : /* This command never recurses */
5195 : /* No command-specific prep needed */
5196 94 : pass = AT_PASS_MISC;
5197 94 : break;
5198 294 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5199 294 : ATSimplePermissions(cmd->subtype, rel,
5200 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5201 : /* Recursion occurs during execution phase */
5202 288 : if (recurse)
5203 288 : cmd->recurse = true;
5204 288 : pass = AT_PASS_MISC;
5205 288 : break;
5206 476 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5207 476 : ATSimplePermissions(cmd->subtype, rel,
5208 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5209 : /* Recursion occurs during execution phase */
5210 : /* No command-specific prep needed except saving recurse flag */
5211 476 : if (recurse)
5212 476 : cmd->recurse = true;
5213 476 : pass = AT_PASS_MISC;
5214 476 : break;
5215 494 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5216 494 : ATSimplePermissions(cmd->subtype, rel,
5217 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5218 494 : pass = AT_PASS_MISC;
5219 : /* This command never recurses */
5220 : /* No command-specific prep needed */
5221 494 : break;
5222 342 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5223 : case AT_EnableAlwaysTrig:
5224 : case AT_EnableReplicaTrig:
5225 : case AT_EnableTrigAll:
5226 : case AT_EnableTrigUser:
5227 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5228 : case AT_DisableTrigAll:
5229 : case AT_DisableTrigUser:
5230 342 : ATSimplePermissions(cmd->subtype, rel,
5231 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5232 : /* Set up recursion for phase 2; no other prep needed */
5233 342 : if (recurse)
5234 314 : cmd->recurse = true;
5235 342 : pass = AT_PASS_MISC;
5236 342 : break;
5237 598 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5238 : case AT_EnableAlwaysRule:
5239 : case AT_EnableReplicaRule:
5240 : case AT_DisableRule:
5241 : case AT_AddOf: /* OF */
5242 : case AT_DropOf: /* NOT OF */
5243 : case AT_EnableRowSecurity:
5244 : case AT_DisableRowSecurity:
5245 : case AT_ForceRowSecurity:
5246 : case AT_NoForceRowSecurity:
5247 598 : ATSimplePermissions(cmd->subtype, rel,
5248 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5249 : /* These commands never recurse */
5250 : /* No command-specific prep needed */
5251 598 : pass = AT_PASS_MISC;
5252 598 : break;
5253 58 : case AT_GenericOptions:
5254 58 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5255 : /* No command-specific prep needed */
5256 58 : pass = AT_PASS_MISC;
5257 58 : break;
5258 2834 : case AT_AttachPartition:
5259 2834 : ATSimplePermissions(cmd->subtype, rel,
5260 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5261 : /* No command-specific prep needed */
5262 2828 : pass = AT_PASS_MISC;
5263 2828 : break;
5264 596 : case AT_DetachPartition:
5265 596 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5266 : /* No command-specific prep needed */
5267 578 : pass = AT_PASS_MISC;
5268 578 : break;
5269 20 : case AT_DetachPartitionFinalize:
5270 20 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5271 : /* No command-specific prep needed */
5272 14 : pass = AT_PASS_MISC;
5273 14 : break;
5274 0 : default: /* oops */
5275 0 : elog(ERROR, "unrecognized alter table type: %d",
5276 : (int) cmd->subtype);
5277 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5278 : break;
5279 : }
5280 : Assert(pass > AT_PASS_UNSET);
5281 :
5282 : /* Add the subcommand to the appropriate list for phase 2 */
5283 34212 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5284 34212 : }
5285 :
5286 : /*
5287 : * ATRewriteCatalogs
5288 : *
5289 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5290 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5291 : * conflicts).
5292 : */
5293 : static void
5294 32094 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5295 : AlterTableUtilityContext *context)
5296 : {
5297 : ListCell *ltab;
5298 :
5299 : /*
5300 : * We process all the tables "in parallel", one pass at a time. This is
5301 : * needed because we may have to propagate work from one table to another
5302 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5303 : * re-adding of the foreign key constraint to the other table). Work can
5304 : * only be propagated into later passes, however.
5305 : */
5306 404614 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5307 : {
5308 : /* Go through each table that needs to be processed */
5309 763868 : foreach(ltab, *wqueue)
5310 : {
5311 391348 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5312 391348 : List *subcmds = tab->subcmds[pass];
5313 : ListCell *lcmd;
5314 :
5315 391348 : if (subcmds == NIL)
5316 335656 : continue;
5317 :
5318 : /*
5319 : * Open the relation and store it in tab. This allows subroutines
5320 : * close and reopen, if necessary. Appropriate lock was obtained
5321 : * by phase 1, needn't get it again.
5322 : */
5323 55692 : tab->rel = relation_open(tab->relid, NoLock);
5324 :
5325 112298 : foreach(lcmd, subcmds)
5326 59434 : ATExecCmd(wqueue, tab,
5327 59434 : lfirst_node(AlterTableCmd, lcmd),
5328 : lockmode, pass, context);
5329 :
5330 : /*
5331 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5332 : * (this is not done in ATExecAlterColumnType since it should be
5333 : * done only once if multiple columns of a table are altered).
5334 : */
5335 52864 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5336 1300 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5337 :
5338 52864 : if (tab->rel)
5339 : {
5340 52864 : relation_close(tab->rel, NoLock);
5341 52864 : tab->rel = NULL;
5342 : }
5343 : }
5344 : }
5345 :
5346 : /* Check to see if a toast table must be added. */
5347 62694 : foreach(ltab, *wqueue)
5348 : {
5349 33428 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5350 :
5351 : /*
5352 : * If the table is source table of ATTACH PARTITION command, we did
5353 : * not modify anything about it that will change its toasting
5354 : * requirement, so no need to check.
5355 : */
5356 33428 : if (((tab->relkind == RELKIND_RELATION ||
5357 6142 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5358 31526 : tab->partition_constraint == NULL) ||
5359 3990 : tab->relkind == RELKIND_MATVIEW)
5360 29488 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5361 : }
5362 29266 : }
5363 :
5364 : /*
5365 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5366 : */
5367 : static void
5368 59434 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5369 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5370 : AlterTableUtilityContext *context)
5371 : {
5372 59434 : ObjectAddress address = InvalidObjectAddress;
5373 59434 : Relation rel = tab->rel;
5374 :
5375 59434 : switch (cmd->subtype)
5376 : {
5377 2196 : case AT_AddColumn: /* ADD COLUMN */
5378 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5379 2196 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5380 2196 : cmd->recurse, false,
5381 : lockmode, cur_pass, context);
5382 2058 : break;
5383 584 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5384 584 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5385 518 : break;
5386 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5387 80 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5388 80 : break;
5389 172 : case AT_AddIdentity:
5390 172 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5391 : cur_pass, context);
5392 : Assert(cmd != NULL);
5393 160 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5394 106 : break;
5395 62 : case AT_SetIdentity:
5396 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5397 : cur_pass, context);
5398 : Assert(cmd != NULL);
5399 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5400 38 : break;
5401 56 : case AT_DropIdentity:
5402 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5403 38 : break;
5404 268 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5405 268 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5406 166 : break;
5407 414 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5408 414 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5409 414 : cmd->recurse, false, lockmode);
5410 384 : break;
5411 222 : case AT_SetExpression:
5412 222 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5413 192 : break;
5414 56 : case AT_DropExpression:
5415 56 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5416 32 : break;
5417 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5418 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5419 116 : break;
5420 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5421 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5422 26 : break;
5423 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5424 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5425 6 : break;
5426 260 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5427 260 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5428 248 : break;
5429 78 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5430 78 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5431 : lockmode);
5432 72 : break;
5433 1646 : case AT_DropColumn: /* DROP COLUMN */
5434 1646 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5435 1646 : cmd->behavior, cmd->recurse, false,
5436 1646 : cmd->missing_ok, lockmode,
5437 : NULL);
5438 1466 : break;
5439 1178 : case AT_AddIndex: /* ADD INDEX */
5440 1178 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5441 : lockmode);
5442 1008 : break;
5443 456 : case AT_ReAddIndex: /* ADD INDEX */
5444 456 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5445 : lockmode);
5446 456 : break;
5447 74 : case AT_ReAddStatistics: /* ADD STATISTICS */
5448 74 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5449 : true, lockmode);
5450 74 : break;
5451 28558 : case AT_AddConstraint: /* ADD CONSTRAINT */
5452 : /* Transform the command only during initial examination */
5453 28558 : if (cur_pass == AT_PASS_ADD_CONSTR)
5454 16018 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5455 16048 : cmd->recurse, lockmode,
5456 : cur_pass, context);
5457 : /* Depending on constraint type, might be no more work to do now */
5458 28528 : if (cmd != NULL)
5459 : address =
5460 12510 : ATExecAddConstraint(wqueue, tab, rel,
5461 12510 : (Constraint *) cmd->def,
5462 12510 : cmd->recurse, false, lockmode);
5463 27848 : break;
5464 338 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5465 : address =
5466 338 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5467 : true, true, lockmode);
5468 326 : break;
5469 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5470 : * constraint */
5471 : address =
5472 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5473 14 : ((AlterDomainStmt *) cmd->def)->def,
5474 : NULL);
5475 8 : break;
5476 78 : case AT_ReAddComment: /* Re-add existing comment */
5477 78 : address = CommentObject((CommentStmt *) cmd->def);
5478 78 : break;
5479 10640 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5480 10640 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5481 : lockmode);
5482 10628 : break;
5483 288 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5484 288 : address = ATExecAlterConstraint(wqueue, rel,
5485 288 : castNode(ATAlterConstraint, cmd->def),
5486 288 : cmd->recurse, lockmode);
5487 222 : break;
5488 476 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5489 476 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5490 : false, lockmode);
5491 470 : break;
5492 812 : case AT_DropConstraint: /* DROP CONSTRAINT */
5493 812 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5494 812 : cmd->recurse,
5495 812 : cmd->missing_ok, lockmode);
5496 602 : break;
5497 1192 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5498 : /* parse transformation was done earlier */
5499 1192 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5500 1150 : break;
5501 172 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5502 : address =
5503 172 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5504 172 : (List *) cmd->def, lockmode);
5505 166 : break;
5506 2006 : case AT_ChangeOwner: /* ALTER OWNER */
5507 2000 : ATExecChangeOwner(RelationGetRelid(rel),
5508 2006 : get_rolespec_oid(cmd->newowner, false),
5509 : false, lockmode);
5510 1988 : break;
5511 64 : case AT_ClusterOn: /* CLUSTER ON */
5512 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5513 58 : break;
5514 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5515 18 : ATExecDropCluster(rel, lockmode);
5516 12 : break;
5517 88 : case AT_SetLogged: /* SET LOGGED */
5518 : case AT_SetUnLogged: /* SET UNLOGGED */
5519 88 : break;
5520 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5521 : /* nothing to do here, oid columns don't exist anymore */
5522 6 : break;
5523 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5524 :
5525 : /*
5526 : * Only do this for partitioned tables, for which this is just a
5527 : * catalog change. Tables with storage are handled by Phase 3.
5528 : */
5529 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5530 50 : tab->chgAccessMethod)
5531 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5532 92 : break;
5533 164 : case AT_SetTableSpace: /* SET TABLESPACE */
5534 :
5535 : /*
5536 : * Only do this for partitioned tables and indexes, for which this
5537 : * is just a catalog change. Other relation types which have
5538 : * storage are handled by Phase 3.
5539 : */
5540 164 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5541 152 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5542 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5543 :
5544 158 : break;
5545 960 : case AT_SetRelOptions: /* SET (...) */
5546 : case AT_ResetRelOptions: /* RESET (...) */
5547 : case AT_ReplaceRelOptions: /* replace entire option list */
5548 960 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5549 908 : break;
5550 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5551 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5552 : TRIGGER_FIRES_ON_ORIGIN, false,
5553 122 : cmd->recurse,
5554 : lockmode);
5555 122 : break;
5556 42 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5557 42 : ATExecEnableDisableTrigger(rel, cmd->name,
5558 : TRIGGER_FIRES_ALWAYS, false,
5559 42 : cmd->recurse,
5560 : lockmode);
5561 42 : break;
5562 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5563 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5564 : TRIGGER_FIRES_ON_REPLICA, false,
5565 16 : cmd->recurse,
5566 : lockmode);
5567 16 : break;
5568 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5569 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5570 : TRIGGER_DISABLED, false,
5571 138 : cmd->recurse,
5572 : lockmode);
5573 138 : break;
5574 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5575 0 : ATExecEnableDisableTrigger(rel, NULL,
5576 : TRIGGER_FIRES_ON_ORIGIN, false,
5577 0 : cmd->recurse,
5578 : lockmode);
5579 0 : break;
5580 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5581 12 : ATExecEnableDisableTrigger(rel, NULL,
5582 : TRIGGER_DISABLED, false,
5583 12 : cmd->recurse,
5584 : lockmode);
5585 12 : break;
5586 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5587 0 : ATExecEnableDisableTrigger(rel, NULL,
5588 : TRIGGER_FIRES_ON_ORIGIN, true,
5589 0 : cmd->recurse,
5590 : lockmode);
5591 0 : break;
5592 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5593 12 : ATExecEnableDisableTrigger(rel, NULL,
5594 : TRIGGER_DISABLED, true,
5595 12 : cmd->recurse,
5596 : lockmode);
5597 12 : break;
5598 :
5599 8 : case AT_EnableRule: /* ENABLE RULE name */
5600 8 : ATExecEnableDisableRule(rel, cmd->name,
5601 : RULE_FIRES_ON_ORIGIN, lockmode);
5602 8 : break;
5603 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5604 0 : ATExecEnableDisableRule(rel, cmd->name,
5605 : RULE_FIRES_ALWAYS, lockmode);
5606 0 : break;
5607 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5608 6 : ATExecEnableDisableRule(rel, cmd->name,
5609 : RULE_FIRES_ON_REPLICA, lockmode);
5610 6 : break;
5611 32 : case AT_DisableRule: /* DISABLE RULE name */
5612 32 : ATExecEnableDisableRule(rel, cmd->name,
5613 : RULE_DISABLED, lockmode);
5614 32 : break;
5615 :
5616 446 : case AT_AddInherit:
5617 446 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5618 326 : break;
5619 94 : case AT_DropInherit:
5620 94 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5621 88 : break;
5622 66 : case AT_AddOf:
5623 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5624 30 : break;
5625 6 : case AT_DropOf:
5626 6 : ATExecDropOf(rel, lockmode);
5627 6 : break;
5628 512 : case AT_ReplicaIdentity:
5629 512 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5630 464 : break;
5631 338 : case AT_EnableRowSecurity:
5632 338 : ATExecSetRowSecurity(rel, true);
5633 338 : break;
5634 10 : case AT_DisableRowSecurity:
5635 10 : ATExecSetRowSecurity(rel, false);
5636 10 : break;
5637 100 : case AT_ForceRowSecurity:
5638 100 : ATExecForceNoForceRowSecurity(rel, true);
5639 100 : break;
5640 32 : case AT_NoForceRowSecurity:
5641 32 : ATExecForceNoForceRowSecurity(rel, false);
5642 32 : break;
5643 58 : case AT_GenericOptions:
5644 58 : ATExecGenericOptions(rel, (List *) cmd->def);
5645 56 : break;
5646 2828 : case AT_AttachPartition:
5647 2828 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5648 : cur_pass, context);
5649 : Assert(cmd != NULL);
5650 2804 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5651 2424 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5652 : context);
5653 : else
5654 380 : address = ATExecAttachPartitionIdx(wqueue, rel,
5655 380 : ((PartitionCmd *) cmd->def)->name);
5656 2414 : break;
5657 578 : case AT_DetachPartition:
5658 578 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5659 : cur_pass, context);
5660 : Assert(cmd != NULL);
5661 : /* ATPrepCmd ensures it must be a table */
5662 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5663 578 : address = ATExecDetachPartition(wqueue, tab, rel,
5664 578 : ((PartitionCmd *) cmd->def)->name,
5665 578 : ((PartitionCmd *) cmd->def)->concurrent);
5666 448 : break;
5667 14 : case AT_DetachPartitionFinalize:
5668 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5669 14 : break;
5670 0 : default: /* oops */
5671 0 : elog(ERROR, "unrecognized alter table type: %d",
5672 : (int) cmd->subtype);
5673 : break;
5674 : }
5675 :
5676 : /*
5677 : * Report the subcommand to interested event triggers.
5678 : */
5679 56606 : if (cmd)
5680 40588 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5681 :
5682 : /*
5683 : * Bump the command counter to ensure the next subcommand in the sequence
5684 : * can see the changes so far
5685 : */
5686 56606 : CommandCounterIncrement();
5687 56606 : }
5688 :
5689 : /*
5690 : * ATParseTransformCmd: perform parse transformation for one subcommand
5691 : *
5692 : * Returns the transformed subcommand tree, if there is one, else NULL.
5693 : *
5694 : * The parser may hand back additional AlterTableCmd(s) and/or other
5695 : * utility statements, either before or after the original subcommand.
5696 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5697 : * AlteredTableInfo (they had better be for later passes than the current one).
5698 : * Utility statements that are supposed to happen before the AlterTableCmd
5699 : * are executed immediately. Those that are supposed to happen afterwards
5700 : * are added to the tab->afterStmts list to be done at the very end.
5701 : */
5702 : static AlterTableCmd *
5703 23196 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5704 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5705 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5706 : {
5707 23196 : AlterTableCmd *newcmd = NULL;
5708 23196 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5709 : List *beforeStmts;
5710 : List *afterStmts;
5711 : ListCell *lc;
5712 :
5713 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5714 23196 : atstmt->relation =
5715 23196 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5716 23196 : pstrdup(RelationGetRelationName(rel)),
5717 : -1);
5718 23196 : atstmt->relation->inh = recurse;
5719 23196 : atstmt->cmds = list_make1(cmd);
5720 23196 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5721 23196 : atstmt->missing_ok = false;
5722 :
5723 : /* Transform the AlterTableStmt */
5724 23196 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5725 : atstmt,
5726 : context->queryString,
5727 : &beforeStmts,
5728 : &afterStmts);
5729 :
5730 : /* Execute any statements that should happen before these subcommand(s) */
5731 23616 : foreach(lc, beforeStmts)
5732 : {
5733 498 : Node *stmt = (Node *) lfirst(lc);
5734 :
5735 498 : ProcessUtilityForAlterTable(stmt, context);
5736 486 : CommandCounterIncrement();
5737 : }
5738 :
5739 : /* Examine the transformed subcommands and schedule them appropriately */
5740 54582 : foreach(lc, atstmt->cmds)
5741 : {
5742 31464 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5743 : AlterTablePass pass;
5744 :
5745 : /*
5746 : * This switch need only cover the subcommand types that can be added
5747 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5748 : * executing the subcommand immediately, as a substitute for the
5749 : * original subcommand. (Note, however, that this does cause
5750 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5751 : * which is important for index and foreign key constraints.)
5752 : *
5753 : * We assume we needn't do any phase-1 checks for added subcommands.
5754 : */
5755 31464 : switch (cmd2->subtype)
5756 : {
5757 1202 : case AT_AddIndex:
5758 1202 : pass = AT_PASS_ADD_INDEX;
5759 1202 : break;
5760 10640 : case AT_AddIndexConstraint:
5761 10640 : pass = AT_PASS_ADD_INDEXCONSTR;
5762 10640 : break;
5763 12522 : case AT_AddConstraint:
5764 : /* Recursion occurs during execution phase */
5765 12522 : if (recurse)
5766 12474 : cmd2->recurse = true;
5767 12522 : switch (castNode(Constraint, cmd2->def)->contype)
5768 : {
5769 8984 : case CONSTR_NOTNULL:
5770 8984 : pass = AT_PASS_COL_ATTRS;
5771 8984 : break;
5772 0 : case CONSTR_PRIMARY:
5773 : case CONSTR_UNIQUE:
5774 : case CONSTR_EXCLUSION:
5775 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5776 0 : break;
5777 3538 : default:
5778 3538 : pass = AT_PASS_ADD_OTHERCONSTR;
5779 3538 : break;
5780 : }
5781 12522 : break;
5782 0 : case AT_AlterColumnGenericOptions:
5783 : /* This command never recurses */
5784 : /* No command-specific prep needed */
5785 0 : pass = AT_PASS_MISC;
5786 0 : break;
5787 7100 : default:
5788 7100 : pass = cur_pass;
5789 7100 : break;
5790 : }
5791 :
5792 31464 : if (pass < cur_pass)
5793 : {
5794 : /* Cannot schedule into a pass we already finished */
5795 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5796 : pass);
5797 : }
5798 31464 : else if (pass > cur_pass)
5799 : {
5800 : /* OK, queue it up for later */
5801 24364 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5802 : }
5803 : else
5804 : {
5805 : /*
5806 : * We should see at most one subcommand for the current pass,
5807 : * which is the transformed version of the original subcommand.
5808 : */
5809 7100 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5810 : {
5811 : /* Found the transformed version of our subcommand */
5812 7100 : newcmd = cmd2;
5813 : }
5814 : else
5815 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5816 : pass);
5817 : }
5818 : }
5819 :
5820 : /* Queue up any after-statements to happen at the end */
5821 23118 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5822 :
5823 23118 : return newcmd;
5824 : }
5825 :
5826 : /*
5827 : * ATRewriteTables: ALTER TABLE phase 3
5828 : */
5829 : static void
5830 29266 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5831 : AlterTableUtilityContext *context)
5832 : {
5833 : ListCell *ltab;
5834 :
5835 : /* Go through each table that needs to be checked or rewritten */
5836 62246 : foreach(ltab, *wqueue)
5837 : {
5838 33362 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5839 :
5840 : /* Relations without storage may be ignored here */
5841 33362 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5842 5834 : continue;
5843 :
5844 : /*
5845 : * If we change column data types, the operation has to be propagated
5846 : * to tables that use this table's rowtype as a column type.
5847 : * tab->newvals will also be non-NULL in the case where we're adding a
5848 : * column with a default. We choose to forbid that case as well,
5849 : * since composite types might eventually support defaults.
5850 : *
5851 : * (Eventually we'll probably need to check for composite type
5852 : * dependencies even when we're just scanning the table without a
5853 : * rewrite, but at the moment a composite type does not enforce any
5854 : * constraints, so it's not necessary/appropriate to enforce them just
5855 : * during ALTER.)
5856 : */
5857 27528 : if (tab->newvals != NIL || tab->rewrite > 0)
5858 : {
5859 : Relation rel;
5860 :
5861 1868 : rel = table_open(tab->relid, NoLock);
5862 1868 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5863 1814 : table_close(rel, NoLock);
5864 : }
5865 :
5866 : /*
5867 : * We only need to rewrite the table if at least one column needs to
5868 : * be recomputed, or we are changing its persistence or access method.
5869 : *
5870 : * There are two reasons for requiring a rewrite when changing
5871 : * persistence: on one hand, we need to ensure that the buffers
5872 : * belonging to each of the two relations are marked with or without
5873 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5874 : * and assigns a new relfilenumber, we automatically create or drop an
5875 : * init fork for the relation as appropriate.
5876 : */
5877 27474 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5878 1048 : {
5879 : /* Build a temporary relation and copy data */
5880 : Relation OldHeap;
5881 : Oid OIDNewHeap;
5882 : Oid NewAccessMethod;
5883 : Oid NewTableSpace;
5884 : char persistence;
5885 :
5886 1104 : OldHeap = table_open(tab->relid, NoLock);
5887 :
5888 : /*
5889 : * We don't support rewriting of system catalogs; there are too
5890 : * many corner cases and too little benefit. In particular this
5891 : * is certainly not going to work for mapped catalogs.
5892 : */
5893 1104 : if (IsSystemRelation(OldHeap))
5894 0 : ereport(ERROR,
5895 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5896 : errmsg("cannot rewrite system relation \"%s\"",
5897 : RelationGetRelationName(OldHeap))));
5898 :
5899 1104 : if (RelationIsUsedAsCatalogTable(OldHeap))
5900 2 : ereport(ERROR,
5901 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5902 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5903 : RelationGetRelationName(OldHeap))));
5904 :
5905 : /*
5906 : * Don't allow rewrite on temp tables of other backends ... their
5907 : * local buffer manager is not going to cope. (This is redundant
5908 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5909 : * check here too.)
5910 : */
5911 1102 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5912 0 : ereport(ERROR,
5913 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5914 : errmsg("cannot rewrite temporary tables of other sessions")));
5915 :
5916 : /*
5917 : * Select destination tablespace (same as original unless user
5918 : * requested a change)
5919 : */
5920 1102 : if (tab->newTableSpace)
5921 0 : NewTableSpace = tab->newTableSpace;
5922 : else
5923 1102 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5924 :
5925 : /*
5926 : * Select destination access method (same as original unless user
5927 : * requested a change)
5928 : */
5929 1102 : if (tab->chgAccessMethod)
5930 36 : NewAccessMethod = tab->newAccessMethod;
5931 : else
5932 1066 : NewAccessMethod = OldHeap->rd_rel->relam;
5933 :
5934 : /*
5935 : * Select persistence of transient table (same as original unless
5936 : * user requested a change)
5937 : */
5938 1102 : persistence = tab->chgPersistence ?
5939 1050 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5940 :
5941 1102 : table_close(OldHeap, NoLock);
5942 :
5943 : /*
5944 : * Fire off an Event Trigger now, before actually rewriting the
5945 : * table.
5946 : *
5947 : * We don't support Event Trigger for nested commands anywhere,
5948 : * here included, and parsetree is given NULL when coming from
5949 : * AlterTableInternal.
5950 : *
5951 : * And fire it only once.
5952 : */
5953 1102 : if (parsetree)
5954 1102 : EventTriggerTableRewrite((Node *) parsetree,
5955 : tab->relid,
5956 : tab->rewrite);
5957 :
5958 : /*
5959 : * Create transient table that will receive the modified data.
5960 : *
5961 : * Ensure it is marked correctly as logged or unlogged. We have
5962 : * to do this here so that buffers for the new relfilenumber will
5963 : * have the right persistence set, and at the same time ensure
5964 : * that the original filenumbers's buffers will get read in with
5965 : * the correct setting (i.e. the original one). Otherwise a
5966 : * rollback after the rewrite would possibly result with buffers
5967 : * for the original filenumbers having the wrong persistence
5968 : * setting.
5969 : *
5970 : * NB: This relies on swap_relation_files() also swapping the
5971 : * persistence. That wouldn't work for pg_class, but that can't be
5972 : * unlogged anyway.
5973 : */
5974 1096 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5975 : persistence, lockmode);
5976 :
5977 : /*
5978 : * Copy the heap data into the new table with the desired
5979 : * modifications, and test the current data within the table
5980 : * against new constraints generated by ALTER TABLE commands.
5981 : */
5982 1096 : ATRewriteTable(tab, OIDNewHeap);
5983 :
5984 : /*
5985 : * Swap the physical files of the old and new heaps, then rebuild
5986 : * indexes and discard the old heap. We can use RecentXmin for
5987 : * the table's new relfrozenxid because we rewrote all the tuples
5988 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5989 : * we never try to swap toast tables by content, since we have no
5990 : * interest in letting this code work on system catalogs.
5991 : */
5992 1054 : finish_heap_swap(tab->relid, OIDNewHeap,
5993 : false, false, true,
5994 1054 : !OidIsValid(tab->newTableSpace),
5995 : RecentXmin,
5996 : ReadNextMultiXactId(),
5997 : persistence);
5998 :
5999 1048 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
6000 : }
6001 26370 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6002 : {
6003 24 : if (tab->chgPersistence)
6004 24 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
6005 : }
6006 : else
6007 : {
6008 : /*
6009 : * If required, test the current data within the table against new
6010 : * constraints generated by ALTER TABLE commands, but don't
6011 : * rebuild data.
6012 : */
6013 26346 : if (tab->constraints != NIL || tab->verify_new_notnull ||
6014 23462 : tab->partition_constraint != NULL)
6015 4818 : ATRewriteTable(tab, InvalidOid);
6016 :
6017 : /*
6018 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
6019 : * just do a block-by-block copy.
6020 : */
6021 26074 : if (tab->newTableSpace)
6022 128 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6023 : }
6024 :
6025 : /*
6026 : * Also change persistence of owned sequences, so that it matches the
6027 : * table persistence.
6028 : */
6029 27146 : if (tab->chgPersistence)
6030 : {
6031 76 : List *seqlist = getOwnedSequences(tab->relid);
6032 : ListCell *lc;
6033 :
6034 124 : foreach(lc, seqlist)
6035 : {
6036 48 : Oid seq_relid = lfirst_oid(lc);
6037 :
6038 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
6039 : }
6040 : }
6041 : }
6042 :
6043 : /*
6044 : * Foreign key constraints are checked in a final pass, since (a) it's
6045 : * generally best to examine each one separately, and (b) it's at least
6046 : * theoretically possible that we have changed both relations of the
6047 : * foreign key, and we'd better have finished both rewrites before we try
6048 : * to read the tables.
6049 : */
6050 61606 : foreach(ltab, *wqueue)
6051 : {
6052 32814 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6053 32814 : Relation rel = NULL;
6054 : ListCell *lcon;
6055 :
6056 : /* Relations without storage may be ignored here too */
6057 32814 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6058 5730 : continue;
6059 :
6060 28888 : foreach(lcon, tab->constraints)
6061 : {
6062 1896 : NewConstraint *con = lfirst(lcon);
6063 :
6064 1896 : if (con->contype == CONSTR_FOREIGN)
6065 : {
6066 1166 : Constraint *fkconstraint = (Constraint *) con->qual;
6067 : Relation refrel;
6068 :
6069 1166 : if (rel == NULL)
6070 : {
6071 : /* Long since locked, no need for another */
6072 1154 : rel = table_open(tab->relid, NoLock);
6073 : }
6074 :
6075 1166 : refrel = table_open(con->refrelid, RowShareLock);
6076 :
6077 1166 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6078 : con->refindid,
6079 : con->conid,
6080 1166 : con->conwithperiod);
6081 :
6082 : /*
6083 : * No need to mark the constraint row as validated, we did
6084 : * that when we inserted the row earlier.
6085 : */
6086 :
6087 1074 : table_close(refrel, NoLock);
6088 : }
6089 : }
6090 :
6091 26992 : if (rel)
6092 1062 : table_close(rel, NoLock);
6093 : }
6094 :
6095 : /* Finally, run any afterStmts that were queued up */
6096 61470 : foreach(ltab, *wqueue)
6097 : {
6098 32678 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6099 : ListCell *lc;
6100 :
6101 32764 : foreach(lc, tab->afterStmts)
6102 : {
6103 86 : Node *stmt = (Node *) lfirst(lc);
6104 :
6105 86 : ProcessUtilityForAlterTable(stmt, context);
6106 86 : CommandCounterIncrement();
6107 : }
6108 : }
6109 28792 : }
6110 :
6111 : /*
6112 : * ATRewriteTable: scan or rewrite one table
6113 : *
6114 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6115 : * must already hold AccessExclusiveLock on it.
6116 : */
6117 : static void
6118 5914 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6119 : {
6120 : Relation oldrel;
6121 : Relation newrel;
6122 : TupleDesc oldTupDesc;
6123 : TupleDesc newTupDesc;
6124 5914 : bool needscan = false;
6125 : List *notnull_attrs;
6126 : List *notnull_virtual_attrs;
6127 : int i;
6128 : ListCell *l;
6129 : EState *estate;
6130 : CommandId mycid;
6131 : BulkInsertState bistate;
6132 : int ti_options;
6133 5914 : ExprState *partqualstate = NULL;
6134 :
6135 : /*
6136 : * Open the relation(s). We have surely already locked the existing
6137 : * table.
6138 : */
6139 5914 : oldrel = table_open(tab->relid, NoLock);
6140 5914 : oldTupDesc = tab->oldDesc;
6141 5914 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6142 :
6143 5914 : if (OidIsValid(OIDNewHeap))
6144 : {
6145 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6146 : false));
6147 1096 : newrel = table_open(OIDNewHeap, NoLock);
6148 : }
6149 : else
6150 4818 : newrel = NULL;
6151 :
6152 : /*
6153 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6154 : * is empty, so don't bother using it.
6155 : */
6156 5914 : if (newrel)
6157 : {
6158 1096 : mycid = GetCurrentCommandId(true);
6159 1096 : bistate = GetBulkInsertState();
6160 1096 : ti_options = TABLE_INSERT_SKIP_FSM;
6161 : }
6162 : else
6163 : {
6164 : /* keep compiler quiet about using these uninitialized */
6165 4818 : mycid = 0;
6166 4818 : bistate = NULL;
6167 4818 : ti_options = 0;
6168 : }
6169 :
6170 : /*
6171 : * Generate the constraint and default execution states
6172 : */
6173 :
6174 5914 : estate = CreateExecutorState();
6175 :
6176 : /* Build the needed expression execution states */
6177 7930 : foreach(l, tab->constraints)
6178 : {
6179 2016 : NewConstraint *con = lfirst(l);
6180 :
6181 2016 : switch (con->contype)
6182 : {
6183 844 : case CONSTR_CHECK:
6184 844 : needscan = true;
6185 844 : con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6186 844 : break;
6187 1172 : case CONSTR_FOREIGN:
6188 : /* Nothing to do here */
6189 1172 : break;
6190 0 : default:
6191 0 : elog(ERROR, "unrecognized constraint type: %d",
6192 : (int) con->contype);
6193 : }
6194 : }
6195 :
6196 : /* Build expression execution states for partition check quals */
6197 5914 : if (tab->partition_constraint)
6198 : {
6199 2082 : needscan = true;
6200 2082 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6201 : }
6202 :
6203 7072 : foreach(l, tab->newvals)
6204 : {
6205 1158 : NewColumnValue *ex = lfirst(l);
6206 :
6207 : /* expr already planned */
6208 1158 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6209 : }
6210 :
6211 5914 : notnull_attrs = notnull_virtual_attrs = NIL;
6212 5914 : if (newrel || tab->verify_new_notnull)
6213 : {
6214 : /*
6215 : * If we are rebuilding the tuples OR if we added any new but not
6216 : * verified not-null constraints, check all *valid* not-null
6217 : * constraints. This is a bit of overkill but it minimizes risk of
6218 : * bugs.
6219 : *
6220 : * notnull_attrs does *not* collect attribute numbers for valid
6221 : * not-null constraints over virtual generated columns; instead, they
6222 : * are collected in notnull_virtual_attrs for verification elsewhere.
6223 : */
6224 7766 : for (i = 0; i < newTupDesc->natts; i++)
6225 : {
6226 5632 : CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6227 :
6228 5632 : if (attr->attnullability == ATTNULLABLE_VALID &&
6229 2118 : !attr->attisdropped)
6230 : {
6231 2118 : Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6232 :
6233 2118 : if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6234 2028 : notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6235 : else
6236 90 : notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6237 90 : wholeatt->attnum);
6238 : }
6239 : }
6240 2134 : if (notnull_attrs || notnull_virtual_attrs)
6241 1556 : needscan = true;
6242 : }
6243 :
6244 5914 : if (newrel || needscan)
6245 : {
6246 : ExprContext *econtext;
6247 : TupleTableSlot *oldslot;
6248 : TupleTableSlot *newslot;
6249 : TableScanDesc scan;
6250 : MemoryContext oldCxt;
6251 4944 : List *dropped_attrs = NIL;
6252 : ListCell *lc;
6253 : Snapshot snapshot;
6254 4944 : ResultRelInfo *rInfo = NULL;
6255 :
6256 : /*
6257 : * When adding or changing a virtual generated column with a not-null
6258 : * constraint, we need to evaluate whether the generation expression
6259 : * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6260 : * prepare a dummy ResultRelInfo.
6261 : */
6262 4944 : if (notnull_virtual_attrs != NIL)
6263 : {
6264 : MemoryContext oldcontext;
6265 :
6266 : Assert(newTupDesc->constr->has_generated_virtual);
6267 : Assert(newTupDesc->constr->has_not_null);
6268 60 : oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6269 60 : rInfo = makeNode(ResultRelInfo);
6270 60 : InitResultRelInfo(rInfo,
6271 : oldrel,
6272 : 0, /* dummy rangetable index */
6273 : NULL,
6274 : estate->es_instrument);
6275 60 : MemoryContextSwitchTo(oldcontext);
6276 : }
6277 :
6278 4944 : if (newrel)
6279 1096 : ereport(DEBUG1,
6280 : (errmsg_internal("rewriting table \"%s\"",
6281 : RelationGetRelationName(oldrel))));
6282 : else
6283 3848 : ereport(DEBUG1,
6284 : (errmsg_internal("verifying table \"%s\"",
6285 : RelationGetRelationName(oldrel))));
6286 :
6287 4944 : if (newrel)
6288 : {
6289 : /*
6290 : * All predicate locks on the tuples or pages are about to be made
6291 : * invalid, because we move tuples around. Promote them to
6292 : * relation locks.
6293 : */
6294 1096 : TransferPredicateLocksToHeapRelation(oldrel);
6295 : }
6296 :
6297 4944 : econtext = GetPerTupleExprContext(estate);
6298 :
6299 : /*
6300 : * Create necessary tuple slots. When rewriting, two slots are needed,
6301 : * otherwise one suffices. In the case where one slot suffices, we
6302 : * need to use the new tuple descriptor, otherwise some constraints
6303 : * can't be evaluated. Note that even when the tuple layout is the
6304 : * same and no rewrite is required, the tupDescs might not be
6305 : * (consider ADD COLUMN without a default).
6306 : */
6307 4944 : if (tab->rewrite)
6308 : {
6309 : Assert(newrel != NULL);
6310 1096 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6311 : table_slot_callbacks(oldrel));
6312 1096 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6313 : table_slot_callbacks(newrel));
6314 :
6315 : /*
6316 : * Set all columns in the new slot to NULL initially, to ensure
6317 : * columns added as part of the rewrite are initialized to NULL.
6318 : * That is necessary as tab->newvals will not contain an
6319 : * expression for columns with a NULL default, e.g. when adding a
6320 : * column without a default together with a column with a default
6321 : * requiring an actual rewrite.
6322 : */
6323 1096 : ExecStoreAllNullTuple(newslot);
6324 : }
6325 : else
6326 : {
6327 3848 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6328 : table_slot_callbacks(oldrel));
6329 3848 : newslot = NULL;
6330 : }
6331 :
6332 : /*
6333 : * Any attributes that are dropped according to the new tuple
6334 : * descriptor can be set to NULL. We precompute the list of dropped
6335 : * attributes to avoid needing to do so in the per-tuple loop.
6336 : */
6337 17306 : for (i = 0; i < newTupDesc->natts; i++)
6338 : {
6339 12362 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6340 790 : dropped_attrs = lappend_int(dropped_attrs, i);
6341 : }
6342 :
6343 : /*
6344 : * Scan through the rows, generating a new row if needed and then
6345 : * checking all the constraints.
6346 : */
6347 4944 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6348 4944 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6349 :
6350 : /*
6351 : * Switch to per-tuple memory context and reset it for each tuple
6352 : * produced, so we don't leak memory.
6353 : */
6354 4944 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6355 :
6356 775090 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6357 : {
6358 : TupleTableSlot *insertslot;
6359 :
6360 765516 : if (tab->rewrite > 0)
6361 : {
6362 : /* Extract data from old tuple */
6363 100122 : slot_getallattrs(oldslot);
6364 100122 : ExecClearTuple(newslot);
6365 :
6366 : /* copy attributes */
6367 100122 : memcpy(newslot->tts_values, oldslot->tts_values,
6368 100122 : sizeof(Datum) * oldslot->tts_nvalid);
6369 100122 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6370 100122 : sizeof(bool) * oldslot->tts_nvalid);
6371 :
6372 : /* Set dropped attributes to null in new tuple */
6373 100238 : foreach(lc, dropped_attrs)
6374 116 : newslot->tts_isnull[lfirst_int(lc)] = true;
6375 :
6376 : /*
6377 : * Constraints and GENERATED expressions might reference the
6378 : * tableoid column, so fill tts_tableOid with the desired
6379 : * value. (We must do this each time, because it gets
6380 : * overwritten with newrel's OID during storing.)
6381 : */
6382 100122 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6383 :
6384 : /*
6385 : * Process supplied expressions to replace selected columns.
6386 : *
6387 : * First, evaluate expressions whose inputs come from the old
6388 : * tuple.
6389 : */
6390 100122 : econtext->ecxt_scantuple = oldslot;
6391 :
6392 206196 : foreach(l, tab->newvals)
6393 : {
6394 106086 : NewColumnValue *ex = lfirst(l);
6395 :
6396 106086 : if (ex->is_generated)
6397 312 : continue;
6398 :
6399 105774 : newslot->tts_values[ex->attnum - 1]
6400 105762 : = ExecEvalExpr(ex->exprstate,
6401 : econtext,
6402 105774 : &newslot->tts_isnull[ex->attnum - 1]);
6403 : }
6404 :
6405 100110 : ExecStoreVirtualTuple(newslot);
6406 :
6407 : /*
6408 : * Now, evaluate any expressions whose inputs come from the
6409 : * new tuple. We assume these columns won't reference each
6410 : * other, so that there's no ordering dependency.
6411 : */
6412 100110 : econtext->ecxt_scantuple = newslot;
6413 :
6414 206184 : foreach(l, tab->newvals)
6415 : {
6416 106074 : NewColumnValue *ex = lfirst(l);
6417 :
6418 106074 : if (!ex->is_generated)
6419 105762 : continue;
6420 :
6421 312 : newslot->tts_values[ex->attnum - 1]
6422 312 : = ExecEvalExpr(ex->exprstate,
6423 : econtext,
6424 312 : &newslot->tts_isnull[ex->attnum - 1]);
6425 : }
6426 :
6427 100110 : insertslot = newslot;
6428 : }
6429 : else
6430 : {
6431 : /*
6432 : * If there's no rewrite, old and new table are guaranteed to
6433 : * have the same AM, so we can just use the old slot to verify
6434 : * new constraints etc.
6435 : */
6436 665394 : insertslot = oldslot;
6437 : }
6438 :
6439 : /* Now check any constraints on the possibly-changed tuple */
6440 765504 : econtext->ecxt_scantuple = insertslot;
6441 :
6442 4107278 : foreach_int(attn, notnull_attrs)
6443 : {
6444 2576474 : if (slot_attisnull(insertslot, attn))
6445 : {
6446 102 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6447 :
6448 102 : ereport(ERROR,
6449 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6450 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6451 : NameStr(attr->attname),
6452 : RelationGetRelationName(oldrel)),
6453 : errtablecol(oldrel, attn)));
6454 : }
6455 : }
6456 :
6457 765402 : if (notnull_virtual_attrs != NIL)
6458 : {
6459 : AttrNumber attnum;
6460 :
6461 84 : attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6462 : estate,
6463 : notnull_virtual_attrs);
6464 84 : if (attnum != InvalidAttrNumber)
6465 : {
6466 30 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6467 :
6468 30 : ereport(ERROR,
6469 : errcode(ERRCODE_NOT_NULL_VIOLATION),
6470 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6471 : NameStr(attr->attname),
6472 : RelationGetRelationName(oldrel)),
6473 : errtablecol(oldrel, attnum));
6474 : }
6475 : }
6476 :
6477 773532 : foreach(l, tab->constraints)
6478 : {
6479 8256 : NewConstraint *con = lfirst(l);
6480 :
6481 8256 : switch (con->contype)
6482 : {
6483 8150 : case CONSTR_CHECK:
6484 8150 : if (!ExecCheck(con->qualstate, econtext))
6485 96 : ereport(ERROR,
6486 : (errcode(ERRCODE_CHECK_VIOLATION),
6487 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6488 : con->name,
6489 : RelationGetRelationName(oldrel)),
6490 : errtableconstraint(oldrel, con->name)));
6491 8054 : break;
6492 106 : case CONSTR_NOTNULL:
6493 : case CONSTR_FOREIGN:
6494 : /* Nothing to do here */
6495 106 : break;
6496 0 : default:
6497 0 : elog(ERROR, "unrecognized constraint type: %d",
6498 : (int) con->contype);
6499 : }
6500 : }
6501 :
6502 765276 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6503 : {
6504 74 : if (tab->validate_default)
6505 26 : ereport(ERROR,
6506 : (errcode(ERRCODE_CHECK_VIOLATION),
6507 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6508 : RelationGetRelationName(oldrel)),
6509 : errtable(oldrel)));
6510 : else
6511 48 : ereport(ERROR,
6512 : (errcode(ERRCODE_CHECK_VIOLATION),
6513 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6514 : RelationGetRelationName(oldrel)),
6515 : errtable(oldrel)));
6516 : }
6517 :
6518 : /* Write the tuple out to the new relation */
6519 765202 : if (newrel)
6520 100080 : table_tuple_insert(newrel, insertslot, mycid,
6521 : ti_options, bistate);
6522 :
6523 765202 : ResetExprContext(econtext);
6524 :
6525 765202 : CHECK_FOR_INTERRUPTS();
6526 : }
6527 :
6528 4630 : MemoryContextSwitchTo(oldCxt);
6529 4630 : table_endscan(scan);
6530 4630 : UnregisterSnapshot(snapshot);
6531 :
6532 4630 : ExecDropSingleTupleTableSlot(oldslot);
6533 4630 : if (newslot)
6534 1054 : ExecDropSingleTupleTableSlot(newslot);
6535 : }
6536 :
6537 5600 : FreeExecutorState(estate);
6538 :
6539 5600 : table_close(oldrel, NoLock);
6540 5600 : if (newrel)
6541 : {
6542 1054 : FreeBulkInsertState(bistate);
6543 :
6544 1054 : table_finish_bulk_insert(newrel, ti_options);
6545 :
6546 1054 : table_close(newrel, NoLock);
6547 : }
6548 5600 : }
6549 :
6550 : /*
6551 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6552 : */
6553 : static AlteredTableInfo *
6554 41732 : ATGetQueueEntry(List **wqueue, Relation rel)
6555 : {
6556 41732 : Oid relid = RelationGetRelid(rel);
6557 : AlteredTableInfo *tab;
6558 : ListCell *ltab;
6559 :
6560 51344 : foreach(ltab, *wqueue)
6561 : {
6562 14538 : tab = (AlteredTableInfo *) lfirst(ltab);
6563 14538 : if (tab->relid == relid)
6564 4926 : return tab;
6565 : }
6566 :
6567 : /*
6568 : * Not there, so add it. Note that we make a copy of the relation's
6569 : * existing descriptor before anything interesting can happen to it.
6570 : */
6571 36806 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6572 36806 : tab->relid = relid;
6573 36806 : tab->rel = NULL; /* set later */
6574 36806 : tab->relkind = rel->rd_rel->relkind;
6575 36806 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6576 36806 : tab->newAccessMethod = InvalidOid;
6577 36806 : tab->chgAccessMethod = false;
6578 36806 : tab->newTableSpace = InvalidOid;
6579 36806 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6580 36806 : tab->chgPersistence = false;
6581 :
6582 36806 : *wqueue = lappend(*wqueue, tab);
6583 :
6584 36806 : return tab;
6585 : }
6586 :
6587 : static const char *
6588 80 : alter_table_type_to_string(AlterTableType cmdtype)
6589 : {
6590 80 : switch (cmdtype)
6591 : {
6592 0 : case AT_AddColumn:
6593 : case AT_AddColumnToView:
6594 0 : return "ADD COLUMN";
6595 0 : case AT_ColumnDefault:
6596 : case AT_CookedColumnDefault:
6597 0 : return "ALTER COLUMN ... SET DEFAULT";
6598 6 : case AT_DropNotNull:
6599 6 : return "ALTER COLUMN ... DROP NOT NULL";
6600 6 : case AT_SetNotNull:
6601 6 : return "ALTER COLUMN ... SET NOT NULL";
6602 0 : case AT_SetExpression:
6603 0 : return "ALTER COLUMN ... SET EXPRESSION";
6604 0 : case AT_DropExpression:
6605 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6606 0 : case AT_SetStatistics:
6607 0 : return "ALTER COLUMN ... SET STATISTICS";
6608 12 : case AT_SetOptions:
6609 12 : return "ALTER COLUMN ... SET";
6610 0 : case AT_ResetOptions:
6611 0 : return "ALTER COLUMN ... RESET";
6612 0 : case AT_SetStorage:
6613 0 : return "ALTER COLUMN ... SET STORAGE";
6614 0 : case AT_SetCompression:
6615 0 : return "ALTER COLUMN ... SET COMPRESSION";
6616 6 : case AT_DropColumn:
6617 6 : return "DROP COLUMN";
6618 0 : case AT_AddIndex:
6619 : case AT_ReAddIndex:
6620 0 : return NULL; /* not real grammar */
6621 0 : case AT_AddConstraint:
6622 : case AT_ReAddConstraint:
6623 : case AT_ReAddDomainConstraint:
6624 : case AT_AddIndexConstraint:
6625 0 : return "ADD CONSTRAINT";
6626 6 : case AT_AlterConstraint:
6627 6 : return "ALTER CONSTRAINT";
6628 0 : case AT_ValidateConstraint:
6629 0 : return "VALIDATE CONSTRAINT";
6630 0 : case AT_DropConstraint:
6631 0 : return "DROP CONSTRAINT";
6632 0 : case AT_ReAddComment:
6633 0 : return NULL; /* not real grammar */
6634 0 : case AT_AlterColumnType:
6635 0 : return "ALTER COLUMN ... SET DATA TYPE";
6636 0 : case AT_AlterColumnGenericOptions:
6637 0 : return "ALTER COLUMN ... OPTIONS";
6638 0 : case AT_ChangeOwner:
6639 0 : return "OWNER TO";
6640 0 : case AT_ClusterOn:
6641 0 : return "CLUSTER ON";
6642 0 : case AT_DropCluster:
6643 0 : return "SET WITHOUT CLUSTER";
6644 0 : case AT_SetAccessMethod:
6645 0 : return "SET ACCESS METHOD";
6646 6 : case AT_SetLogged:
6647 6 : return "SET LOGGED";
6648 6 : case AT_SetUnLogged:
6649 6 : return "SET UNLOGGED";
6650 0 : case AT_DropOids:
6651 0 : return "SET WITHOUT OIDS";
6652 0 : case AT_SetTableSpace:
6653 0 : return "SET TABLESPACE";
6654 2 : case AT_SetRelOptions:
6655 2 : return "SET";
6656 0 : case AT_ResetRelOptions:
6657 0 : return "RESET";
6658 0 : case AT_ReplaceRelOptions:
6659 0 : return NULL; /* not real grammar */
6660 0 : case AT_EnableTrig:
6661 0 : return "ENABLE TRIGGER";
6662 0 : case AT_EnableAlwaysTrig:
6663 0 : return "ENABLE ALWAYS TRIGGER";
6664 0 : case AT_EnableReplicaTrig:
6665 0 : return "ENABLE REPLICA TRIGGER";
6666 0 : case AT_DisableTrig:
6667 0 : return "DISABLE TRIGGER";
6668 0 : case AT_EnableTrigAll:
6669 0 : return "ENABLE TRIGGER ALL";
6670 0 : case AT_DisableTrigAll:
6671 0 : return "DISABLE TRIGGER ALL";
6672 0 : case AT_EnableTrigUser:
6673 0 : return "ENABLE TRIGGER USER";
6674 0 : case AT_DisableTrigUser:
6675 0 : return "DISABLE TRIGGER USER";
6676 0 : case AT_EnableRule:
6677 0 : return "ENABLE RULE";
6678 0 : case AT_EnableAlwaysRule:
6679 0 : return "ENABLE ALWAYS RULE";
6680 0 : case AT_EnableReplicaRule:
6681 0 : return "ENABLE REPLICA RULE";
6682 0 : case AT_DisableRule:
6683 0 : return "DISABLE RULE";
6684 0 : case AT_AddInherit:
6685 0 : return "INHERIT";
6686 0 : case AT_DropInherit:
6687 0 : return "NO INHERIT";
6688 0 : case AT_AddOf:
6689 0 : return "OF";
6690 0 : case AT_DropOf:
6691 0 : return "NOT OF";
6692 0 : case AT_ReplicaIdentity:
6693 0 : return "REPLICA IDENTITY";
6694 0 : case AT_EnableRowSecurity:
6695 0 : return "ENABLE ROW SECURITY";
6696 0 : case AT_DisableRowSecurity:
6697 0 : return "DISABLE ROW SECURITY";
6698 0 : case AT_ForceRowSecurity:
6699 0 : return "FORCE ROW SECURITY";
6700 0 : case AT_NoForceRowSecurity:
6701 0 : return "NO FORCE ROW SECURITY";
6702 0 : case AT_GenericOptions:
6703 0 : return "OPTIONS";
6704 6 : case AT_AttachPartition:
6705 6 : return "ATTACH PARTITION";
6706 18 : case AT_DetachPartition:
6707 18 : return "DETACH PARTITION";
6708 6 : case AT_DetachPartitionFinalize:
6709 6 : return "DETACH PARTITION ... FINALIZE";
6710 0 : case AT_AddIdentity:
6711 0 : return "ALTER COLUMN ... ADD IDENTITY";
6712 0 : case AT_SetIdentity:
6713 0 : return "ALTER COLUMN ... SET";
6714 0 : case AT_DropIdentity:
6715 0 : return "ALTER COLUMN ... DROP IDENTITY";
6716 0 : case AT_ReAddStatistics:
6717 0 : return NULL; /* not real grammar */
6718 : }
6719 :
6720 0 : return NULL;
6721 : }
6722 :
6723 : /*
6724 : * ATSimplePermissions
6725 : *
6726 : * - Ensure that it is a relation (or possibly a view)
6727 : * - Ensure this user is the owner
6728 : * - Ensure that it is not a system table
6729 : */
6730 : static void
6731 38084 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6732 : {
6733 : int actual_target;
6734 :
6735 38084 : switch (rel->rd_rel->relkind)
6736 : {
6737 30142 : case RELKIND_RELATION:
6738 30142 : actual_target = ATT_TABLE;
6739 30142 : break;
6740 5670 : case RELKIND_PARTITIONED_TABLE:
6741 5670 : actual_target = ATT_PARTITIONED_TABLE;
6742 5670 : break;
6743 402 : case RELKIND_VIEW:
6744 402 : actual_target = ATT_VIEW;
6745 402 : break;
6746 46 : case RELKIND_MATVIEW:
6747 46 : actual_target = ATT_MATVIEW;
6748 46 : break;
6749 228 : case RELKIND_INDEX:
6750 228 : actual_target = ATT_INDEX;
6751 228 : break;
6752 422 : case RELKIND_PARTITIONED_INDEX:
6753 422 : actual_target = ATT_PARTITIONED_INDEX;
6754 422 : break;
6755 216 : case RELKIND_COMPOSITE_TYPE:
6756 216 : actual_target = ATT_COMPOSITE_TYPE;
6757 216 : break;
6758 932 : case RELKIND_FOREIGN_TABLE:
6759 932 : actual_target = ATT_FOREIGN_TABLE;
6760 932 : break;
6761 24 : case RELKIND_SEQUENCE:
6762 24 : actual_target = ATT_SEQUENCE;
6763 24 : break;
6764 2 : default:
6765 2 : actual_target = 0;
6766 2 : break;
6767 : }
6768 :
6769 : /* Wrong target type? */
6770 38084 : if ((actual_target & allowed_targets) == 0)
6771 : {
6772 80 : const char *action_str = alter_table_type_to_string(cmdtype);
6773 :
6774 80 : if (action_str)
6775 80 : ereport(ERROR,
6776 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6777 : /* translator: %s is a group of some SQL keywords */
6778 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6779 : action_str, RelationGetRelationName(rel)),
6780 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6781 : else
6782 : /* internal error? */
6783 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6784 : RelationGetRelationName(rel));
6785 : }
6786 :
6787 : /* Permissions checks */
6788 38004 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6789 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6790 12 : RelationGetRelationName(rel));
6791 :
6792 37992 : if (!allowSystemTableMods && IsSystemRelation(rel))
6793 0 : ereport(ERROR,
6794 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6795 : errmsg("permission denied: \"%s\" is a system catalog",
6796 : RelationGetRelationName(rel))));
6797 37992 : }
6798 :
6799 : /*
6800 : * ATSimpleRecursion
6801 : *
6802 : * Simple table recursion sufficient for most ALTER TABLE operations.
6803 : * All direct and indirect children are processed in an unspecified order.
6804 : * Note that if a child inherits from the original table via multiple
6805 : * inheritance paths, it will be visited just once.
6806 : */
6807 : static void
6808 1352 : ATSimpleRecursion(List **wqueue, Relation rel,
6809 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6810 : AlterTableUtilityContext *context)
6811 : {
6812 : /*
6813 : * Propagate to children, if desired and if there are (or might be) any
6814 : * children.
6815 : */
6816 1352 : if (recurse && rel->rd_rel->relhassubclass)
6817 : {
6818 84 : Oid relid = RelationGetRelid(rel);
6819 : ListCell *child;
6820 : List *children;
6821 :
6822 84 : children = find_all_inheritors(relid, lockmode, NULL);
6823 :
6824 : /*
6825 : * find_all_inheritors does the recursive search of the inheritance
6826 : * hierarchy, so all we have to do is process all of the relids in the
6827 : * list that it returns.
6828 : */
6829 366 : foreach(child, children)
6830 : {
6831 282 : Oid childrelid = lfirst_oid(child);
6832 : Relation childrel;
6833 :
6834 282 : if (childrelid == relid)
6835 84 : continue;
6836 : /* find_all_inheritors already got lock */
6837 198 : childrel = relation_open(childrelid, NoLock);
6838 198 : CheckAlterTableIsSafe(childrel);
6839 198 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6840 198 : relation_close(childrel, NoLock);
6841 : }
6842 : }
6843 1352 : }
6844 :
6845 : /*
6846 : * Obtain list of partitions of the given table, locking them all at the given
6847 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6848 : *
6849 : * This function is a no-op if the given relation is not a partitioned table;
6850 : * in particular, nothing is done if it's a legacy inheritance parent.
6851 : */
6852 : static void
6853 818 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6854 : {
6855 818 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6856 : {
6857 : List *inh;
6858 : ListCell *cell;
6859 :
6860 176 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6861 : /* first element is the parent rel; must ignore it */
6862 574 : for_each_from(cell, inh, 1)
6863 : {
6864 : Relation childrel;
6865 :
6866 : /* find_all_inheritors already got lock */
6867 404 : childrel = table_open(lfirst_oid(cell), NoLock);
6868 404 : CheckAlterTableIsSafe(childrel);
6869 398 : table_close(childrel, NoLock);
6870 : }
6871 170 : list_free(inh);
6872 : }
6873 812 : }
6874 :
6875 : /*
6876 : * ATTypedTableRecursion
6877 : *
6878 : * Propagate ALTER TYPE operations to the typed tables of that type.
6879 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6880 : * recursion to inheritance children of the typed tables.
6881 : */
6882 : static void
6883 192 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6884 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6885 : {
6886 : ListCell *child;
6887 : List *children;
6888 :
6889 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6890 :
6891 192 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6892 192 : RelationGetRelationName(rel),
6893 : cmd->behavior);
6894 :
6895 204 : foreach(child, children)
6896 : {
6897 30 : Oid childrelid = lfirst_oid(child);
6898 : Relation childrel;
6899 :
6900 30 : childrel = relation_open(childrelid, lockmode);
6901 30 : CheckAlterTableIsSafe(childrel);
6902 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6903 30 : relation_close(childrel, NoLock);
6904 : }
6905 174 : }
6906 :
6907 :
6908 : /*
6909 : * find_composite_type_dependencies
6910 : *
6911 : * Check to see if the type "typeOid" is being used as a column in some table
6912 : * (possibly nested several levels deep in composite types, arrays, etc!).
6913 : * Eventually, we'd like to propagate the check or rewrite operation
6914 : * into such tables, but for now, just error out if we find any.
6915 : *
6916 : * Caller should provide either the associated relation of a rowtype,
6917 : * or a type name (not both) for use in the error message, if any.
6918 : *
6919 : * Note that "typeOid" is not necessarily a composite type; it could also be
6920 : * another container type such as an array or range, or a domain over one of
6921 : * these things. The name of this function is therefore somewhat historical,
6922 : * but it's not worth changing.
6923 : *
6924 : * We assume that functions and views depending on the type are not reasons
6925 : * to reject the ALTER. (How safe is this really?)
6926 : */
6927 : void
6928 4792 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6929 : const char *origTypeName)
6930 : {
6931 : Relation depRel;
6932 : ScanKeyData key[2];
6933 : SysScanDesc depScan;
6934 : HeapTuple depTup;
6935 :
6936 : /* since this function recurses, it could be driven to stack overflow */
6937 4792 : check_stack_depth();
6938 :
6939 : /*
6940 : * We scan pg_depend to find those things that depend on the given type.
6941 : * (We assume we can ignore refobjsubid for a type.)
6942 : */
6943 4792 : depRel = table_open(DependRelationId, AccessShareLock);
6944 :
6945 4792 : ScanKeyInit(&key[0],
6946 : Anum_pg_depend_refclassid,
6947 : BTEqualStrategyNumber, F_OIDEQ,
6948 : ObjectIdGetDatum(TypeRelationId));
6949 4792 : ScanKeyInit(&key[1],
6950 : Anum_pg_depend_refobjid,
6951 : BTEqualStrategyNumber, F_OIDEQ,
6952 : ObjectIdGetDatum(typeOid));
6953 :
6954 4792 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6955 : NULL, 2, key);
6956 :
6957 7356 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6958 : {
6959 2720 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6960 : Relation rel;
6961 : TupleDesc tupleDesc;
6962 : Form_pg_attribute att;
6963 :
6964 : /* Check for directly dependent types */
6965 2720 : if (pg_depend->classid == TypeRelationId)
6966 : {
6967 : /*
6968 : * This must be an array, domain, or range containing the given
6969 : * type, so recursively check for uses of this type. Note that
6970 : * any error message will mention the original type not the
6971 : * container; this is intentional.
6972 : */
6973 2314 : find_composite_type_dependencies(pg_depend->objid,
6974 : origRelation, origTypeName);
6975 2290 : continue;
6976 : }
6977 :
6978 : /* Else, ignore dependees that aren't relations */
6979 406 : if (pg_depend->classid != RelationRelationId)
6980 122 : continue;
6981 :
6982 284 : rel = relation_open(pg_depend->objid, AccessShareLock);
6983 284 : tupleDesc = RelationGetDescr(rel);
6984 :
6985 : /*
6986 : * If objsubid identifies a specific column, refer to that in error
6987 : * messages. Otherwise, search to see if there's a user column of the
6988 : * type. (We assume system columns are never of interesting types.)
6989 : * The search is needed because an index containing an expression
6990 : * column of the target type will just be recorded as a whole-relation
6991 : * dependency. If we do not find a column of the type, the dependency
6992 : * must indicate that the type is transiently referenced in an index
6993 : * expression but not stored on disk, which we assume is OK, just as
6994 : * we do for references in views. (It could also be that the target
6995 : * type is embedded in some container type that is stored in an index
6996 : * column, but the previous recursion should catch such cases.)
6997 : */
6998 284 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6999 126 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7000 : else
7001 : {
7002 158 : att = NULL;
7003 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
7004 : {
7005 254 : att = TupleDescAttr(tupleDesc, attno - 1);
7006 254 : if (att->atttypid == typeOid && !att->attisdropped)
7007 6 : break;
7008 248 : att = NULL;
7009 : }
7010 158 : if (att == NULL)
7011 : {
7012 : /* No such column, so assume OK */
7013 152 : relation_close(rel, AccessShareLock);
7014 152 : continue;
7015 : }
7016 : }
7017 :
7018 : /*
7019 : * We definitely should reject if the relation has storage. If it's
7020 : * partitioned, then perhaps we don't have to reject: if there are
7021 : * partitions then we'll fail when we find one, else there is no
7022 : * stored data to worry about. However, it's possible that the type
7023 : * change would affect conclusions about whether the type is sortable
7024 : * or hashable and thus (if it's a partitioning column) break the
7025 : * partitioning rule. For now, reject for partitioned rels too.
7026 : */
7027 132 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7028 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7029 : {
7030 132 : if (origTypeName)
7031 30 : ereport(ERROR,
7032 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7033 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7034 : origTypeName,
7035 : RelationGetRelationName(rel),
7036 : NameStr(att->attname))));
7037 102 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7038 18 : ereport(ERROR,
7039 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7040 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7041 : RelationGetRelationName(origRelation),
7042 : RelationGetRelationName(rel),
7043 : NameStr(att->attname))));
7044 84 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7045 6 : ereport(ERROR,
7046 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7047 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7048 : RelationGetRelationName(origRelation),
7049 : RelationGetRelationName(rel),
7050 : NameStr(att->attname))));
7051 : else
7052 78 : ereport(ERROR,
7053 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7054 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7055 : RelationGetRelationName(origRelation),
7056 : RelationGetRelationName(rel),
7057 : NameStr(att->attname))));
7058 : }
7059 0 : else if (OidIsValid(rel->rd_rel->reltype))
7060 : {
7061 : /*
7062 : * A view or composite type itself isn't a problem, but we must
7063 : * recursively check for indirect dependencies via its rowtype.
7064 : */
7065 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
7066 : origRelation, origTypeName);
7067 : }
7068 :
7069 0 : relation_close(rel, AccessShareLock);
7070 : }
7071 :
7072 4636 : systable_endscan(depScan);
7073 :
7074 4636 : relation_close(depRel, AccessShareLock);
7075 4636 : }
7076 :
7077 :
7078 : /*
7079 : * find_typed_table_dependencies
7080 : *
7081 : * Check to see if a composite type is being used as the type of a
7082 : * typed table. Abort if any are found and behavior is RESTRICT.
7083 : * Else return the list of tables.
7084 : */
7085 : static List *
7086 216 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7087 : {
7088 : Relation classRel;
7089 : ScanKeyData key[1];
7090 : TableScanDesc scan;
7091 : HeapTuple tuple;
7092 216 : List *result = NIL;
7093 :
7094 216 : classRel = table_open(RelationRelationId, AccessShareLock);
7095 :
7096 216 : ScanKeyInit(&key[0],
7097 : Anum_pg_class_reloftype,
7098 : BTEqualStrategyNumber, F_OIDEQ,
7099 : ObjectIdGetDatum(typeOid));
7100 :
7101 216 : scan = table_beginscan_catalog(classRel, 1, key);
7102 :
7103 252 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7104 : {
7105 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7106 :
7107 60 : if (behavior == DROP_RESTRICT)
7108 24 : ereport(ERROR,
7109 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7110 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7111 : typeName),
7112 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7113 : else
7114 36 : result = lappend_oid(result, classform->oid);
7115 : }
7116 :
7117 192 : table_endscan(scan);
7118 192 : table_close(classRel, AccessShareLock);
7119 :
7120 192 : return result;
7121 : }
7122 :
7123 :
7124 : /*
7125 : * check_of_type
7126 : *
7127 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7128 : * isn't suitable, throw an error. Currently, we require that the type
7129 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7130 : * would require handling a number of extra corner cases in the DDL commands.
7131 : * (Also, allowing domain-over-composite would open up a can of worms about
7132 : * whether and how the domain's constraints should apply to derived tables.)
7133 : */
7134 : void
7135 182 : check_of_type(HeapTuple typetuple)
7136 : {
7137 182 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7138 182 : bool typeOk = false;
7139 :
7140 182 : if (typ->typtype == TYPTYPE_COMPOSITE)
7141 : {
7142 : Relation typeRelation;
7143 :
7144 : Assert(OidIsValid(typ->typrelid));
7145 176 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7146 176 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7147 :
7148 : /*
7149 : * Close the parent rel, but keep our AccessShareLock on it until xact
7150 : * commit. That will prevent someone else from deleting or ALTERing
7151 : * the type before the typed table creation/conversion commits.
7152 : */
7153 176 : relation_close(typeRelation, NoLock);
7154 :
7155 176 : if (!typeOk)
7156 6 : ereport(ERROR,
7157 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7158 : errmsg("type %s is the row type of another table",
7159 : format_type_be(typ->oid)),
7160 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7161 : }
7162 : else
7163 6 : ereport(ERROR,
7164 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7165 : errmsg("type %s is not a composite type",
7166 : format_type_be(typ->oid))));
7167 170 : }
7168 :
7169 :
7170 : /*
7171 : * ALTER TABLE ADD COLUMN
7172 : *
7173 : * Adds an additional attribute to a relation making the assumption that
7174 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7175 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7176 : * AlterTableCmd's.
7177 : *
7178 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7179 : * have to decide at runtime whether to recurse or not depending on whether we
7180 : * actually add a column or merely merge with an existing column. (We can't
7181 : * check this in a static pre-pass because it won't handle multiple inheritance
7182 : * situations correctly.)
7183 : */
7184 : static void
7185 2214 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7186 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7187 : AlterTableUtilityContext *context)
7188 : {
7189 2214 : if (rel->rd_rel->reloftype && !recursing)
7190 6 : ereport(ERROR,
7191 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7192 : errmsg("cannot add column to typed table")));
7193 :
7194 2208 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7195 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7196 :
7197 2202 : if (recurse && !is_view)
7198 2102 : cmd->recurse = true;
7199 2202 : }
7200 :
7201 : /*
7202 : * Add a column to a table. The return value is the address of the
7203 : * new column in the parent relation.
7204 : *
7205 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7206 : * copy (but that happens only after we check for IF NOT EXISTS).
7207 : */
7208 : static ObjectAddress
7209 2934 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7210 : AlterTableCmd **cmd, bool recurse, bool recursing,
7211 : LOCKMODE lockmode, AlterTablePass cur_pass,
7212 : AlterTableUtilityContext *context)
7213 : {
7214 2934 : Oid myrelid = RelationGetRelid(rel);
7215 2934 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7216 2934 : bool if_not_exists = (*cmd)->missing_ok;
7217 : Relation pgclass,
7218 : attrdesc;
7219 : HeapTuple reltup;
7220 : Form_pg_class relform;
7221 : Form_pg_attribute attribute;
7222 : int newattnum;
7223 : char relkind;
7224 : Expr *defval;
7225 : List *children;
7226 : ListCell *child;
7227 : AlterTableCmd *childcmd;
7228 : ObjectAddress address;
7229 : TupleDesc tupdesc;
7230 :
7231 : /* since this function recurses, it could be driven to stack overflow */
7232 2934 : check_stack_depth();
7233 :
7234 : /* At top level, permission check was done in ATPrepCmd, else do it */
7235 2934 : if (recursing)
7236 738 : ATSimplePermissions((*cmd)->subtype, rel,
7237 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7238 :
7239 2934 : if (rel->rd_rel->relispartition && !recursing)
7240 12 : ereport(ERROR,
7241 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7242 : errmsg("cannot add column to a partition")));
7243 :
7244 2922 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7245 :
7246 : /*
7247 : * Are we adding the column to a recursion child? If so, check whether to
7248 : * merge with an existing definition for the column. If we do merge, we
7249 : * must not recurse. Children will already have the column, and recursing
7250 : * into them would mess up attinhcount.
7251 : */
7252 2922 : if (colDef->inhcount > 0)
7253 : {
7254 : HeapTuple tuple;
7255 :
7256 : /* Does child already have a column by this name? */
7257 738 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7258 738 : if (HeapTupleIsValid(tuple))
7259 : {
7260 60 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7261 : Oid ctypeId;
7262 : int32 ctypmod;
7263 : Oid ccollid;
7264 :
7265 : /* Child column must match on type, typmod, and collation */
7266 60 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7267 60 : if (ctypeId != childatt->atttypid ||
7268 60 : ctypmod != childatt->atttypmod)
7269 0 : ereport(ERROR,
7270 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7271 : errmsg("child table \"%s\" has different type for column \"%s\"",
7272 : RelationGetRelationName(rel), colDef->colname)));
7273 60 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7274 60 : if (ccollid != childatt->attcollation)
7275 0 : ereport(ERROR,
7276 : (errcode(ERRCODE_COLLATION_MISMATCH),
7277 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7278 : RelationGetRelationName(rel), colDef->colname),
7279 : errdetail("\"%s\" versus \"%s\"",
7280 : get_collation_name(ccollid),
7281 : get_collation_name(childatt->attcollation))));
7282 :
7283 : /* Bump the existing child att's inhcount */
7284 60 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7285 : &childatt->attinhcount))
7286 0 : ereport(ERROR,
7287 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7288 : errmsg("too many inheritance parents"));
7289 60 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7290 :
7291 60 : heap_freetuple(tuple);
7292 :
7293 : /* Inform the user about the merge */
7294 60 : ereport(NOTICE,
7295 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7296 : colDef->colname, RelationGetRelationName(rel))));
7297 :
7298 60 : table_close(attrdesc, RowExclusiveLock);
7299 :
7300 : /* Make the child column change visible */
7301 60 : CommandCounterIncrement();
7302 :
7303 60 : return InvalidObjectAddress;
7304 : }
7305 : }
7306 :
7307 : /* skip if the name already exists and if_not_exists is true */
7308 2862 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7309 : {
7310 54 : table_close(attrdesc, RowExclusiveLock);
7311 54 : return InvalidObjectAddress;
7312 : }
7313 :
7314 : /*
7315 : * Okay, we need to add the column, so go ahead and do parse
7316 : * transformation. This can result in queueing up, or even immediately
7317 : * executing, subsidiary operations (such as creation of unique indexes);
7318 : * so we mustn't do it until we have made the if_not_exists check.
7319 : *
7320 : * When recursing, the command was already transformed and we needn't do
7321 : * so again. Also, if context isn't given we can't transform. (That
7322 : * currently happens only for AT_AddColumnToView; we expect that view.c
7323 : * passed us a ColumnDef that doesn't need work.)
7324 : */
7325 2778 : if (context != NULL && !recursing)
7326 : {
7327 2076 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7328 : cur_pass, context);
7329 : Assert(*cmd != NULL);
7330 2070 : colDef = castNode(ColumnDef, (*cmd)->def);
7331 : }
7332 :
7333 : /*
7334 : * Regular inheritance children are independent enough not to inherit the
7335 : * identity column from parent hence cannot recursively add identity
7336 : * column if the table has inheritance children.
7337 : *
7338 : * Partitions, on the other hand, are integral part of a partitioned table
7339 : * and inherit identity column. Hence propagate identity column down the
7340 : * partition hierarchy.
7341 : */
7342 2772 : if (colDef->identity &&
7343 54 : recurse &&
7344 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7345 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7346 6 : ereport(ERROR,
7347 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7348 : errmsg("cannot recursively add identity column to table that has child tables")));
7349 :
7350 2766 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7351 :
7352 2766 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7353 2766 : if (!HeapTupleIsValid(reltup))
7354 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7355 2766 : relform = (Form_pg_class) GETSTRUCT(reltup);
7356 2766 : relkind = relform->relkind;
7357 :
7358 : /* Determine the new attribute's number */
7359 2766 : newattnum = relform->relnatts + 1;
7360 2766 : if (newattnum > MaxHeapAttributeNumber)
7361 0 : ereport(ERROR,
7362 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7363 : errmsg("tables can have at most %d columns",
7364 : MaxHeapAttributeNumber)));
7365 :
7366 : /*
7367 : * Construct new attribute's pg_attribute entry.
7368 : */
7369 2766 : tupdesc = BuildDescForRelation(list_make1(colDef));
7370 :
7371 2754 : attribute = TupleDescAttr(tupdesc, 0);
7372 :
7373 : /* Fix up attribute number */
7374 2754 : attribute->attnum = newattnum;
7375 :
7376 : /* make sure datatype is legal for a column */
7377 5508 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7378 2754 : list_make1_oid(rel->rd_rel->reltype),
7379 2754 : (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7380 :
7381 2718 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7382 :
7383 2718 : table_close(attrdesc, RowExclusiveLock);
7384 :
7385 : /*
7386 : * Update pg_class tuple as appropriate
7387 : */
7388 2718 : relform->relnatts = newattnum;
7389 :
7390 2718 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7391 :
7392 2718 : heap_freetuple(reltup);
7393 :
7394 : /* Post creation hook for new attribute */
7395 2718 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7396 :
7397 2718 : table_close(pgclass, RowExclusiveLock);
7398 :
7399 : /* Make the attribute's catalog entry visible */
7400 2718 : CommandCounterIncrement();
7401 :
7402 : /*
7403 : * Store the DEFAULT, if any, in the catalogs
7404 : */
7405 2718 : if (colDef->raw_default)
7406 : {
7407 : RawColumnDefault *rawEnt;
7408 :
7409 950 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7410 950 : rawEnt->attnum = attribute->attnum;
7411 950 : rawEnt->raw_default = copyObject(colDef->raw_default);
7412 950 : rawEnt->generated = colDef->generated;
7413 :
7414 : /*
7415 : * This function is intended for CREATE TABLE, so it processes a
7416 : * _list_ of defaults, but we just do one.
7417 : */
7418 950 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7419 : false, true, false, NULL);
7420 :
7421 : /* Make the additional catalog changes visible */
7422 926 : CommandCounterIncrement();
7423 : }
7424 :
7425 : /*
7426 : * Tell Phase 3 to fill in the default expression, if there is one.
7427 : *
7428 : * If there is no default, Phase 3 doesn't have to do anything, because
7429 : * that effectively means that the default is NULL. The heap tuple access
7430 : * routines always check for attnum > # of attributes in tuple, and return
7431 : * NULL if so, so without any modification of the tuple data we will get
7432 : * the effect of NULL values in the new column.
7433 : *
7434 : * An exception occurs when the new column is of a domain type: the domain
7435 : * might have a not-null constraint, or a check constraint that indirectly
7436 : * rejects nulls. If there are any domain constraints then we construct
7437 : * an explicit NULL default value that will be passed through
7438 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7439 : * rewriting the table which we really wouldn't have to do; but we do it
7440 : * to preserve the historical behavior that such a failure will be raised
7441 : * only if the table currently contains some rows.)
7442 : *
7443 : * Note: we use build_column_default, and not just the cooked default
7444 : * returned by AddRelationNewConstraints, so that the right thing happens
7445 : * when a datatype's default applies.
7446 : *
7447 : * Note: it might seem that this should happen at the end of Phase 2, so
7448 : * that the effects of subsequent subcommands can be taken into account.
7449 : * It's intentional that we do it now, though. The new column should be
7450 : * filled according to what is said in the ADD COLUMN subcommand, so that
7451 : * the effects are the same as if this subcommand had been run by itself
7452 : * and the later subcommands had been issued in new ALTER TABLE commands.
7453 : *
7454 : * We can skip this entirely for relations without storage, since Phase 3
7455 : * is certainly not going to touch them.
7456 : */
7457 2694 : if (RELKIND_HAS_STORAGE(relkind))
7458 : {
7459 : bool has_domain_constraints;
7460 2318 : bool has_missing = false;
7461 :
7462 : /*
7463 : * For an identity column, we can't use build_column_default(),
7464 : * because the sequence ownership isn't set yet. So do it manually.
7465 : */
7466 2318 : if (colDef->identity)
7467 : {
7468 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7469 :
7470 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7471 42 : nve->typeId = attribute->atttypid;
7472 :
7473 42 : defval = (Expr *) nve;
7474 : }
7475 : else
7476 2276 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7477 :
7478 : /* Build CoerceToDomain(NULL) expression if needed */
7479 2318 : has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7480 2318 : if (!defval && has_domain_constraints)
7481 : {
7482 : Oid baseTypeId;
7483 : int32 baseTypeMod;
7484 : Oid baseTypeColl;
7485 :
7486 6 : baseTypeMod = attribute->atttypmod;
7487 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7488 6 : baseTypeColl = get_typcollation(baseTypeId);
7489 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7490 6 : defval = (Expr *) coerce_to_target_type(NULL,
7491 : (Node *) defval,
7492 : baseTypeId,
7493 : attribute->atttypid,
7494 : attribute->atttypmod,
7495 : COERCION_ASSIGNMENT,
7496 : COERCE_IMPLICIT_CAST,
7497 : -1);
7498 6 : if (defval == NULL) /* should not happen */
7499 0 : elog(ERROR, "failed to coerce base type to domain");
7500 : }
7501 :
7502 2318 : if (defval)
7503 : {
7504 : NewColumnValue *newval;
7505 :
7506 : /* Prepare defval for execution, either here or in Phase 3 */
7507 822 : defval = expression_planner(defval);
7508 :
7509 : /* Add the new default to the newvals list */
7510 822 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7511 822 : newval->attnum = attribute->attnum;
7512 822 : newval->expr = defval;
7513 822 : newval->is_generated = (colDef->generated != '\0');
7514 :
7515 822 : tab->newvals = lappend(tab->newvals, newval);
7516 :
7517 : /*
7518 : * Attempt to skip a complete table rewrite by storing the
7519 : * specified DEFAULT value outside of the heap. This is only
7520 : * allowed for plain relations and non-generated columns, and the
7521 : * default expression can't be volatile (stable is OK). Note that
7522 : * contain_volatile_functions deems CoerceToDomain immutable, but
7523 : * here we consider that coercion to a domain with constraints is
7524 : * volatile; else it might fail even when the table is empty.
7525 : */
7526 822 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
7527 822 : !colDef->generated &&
7528 694 : !has_domain_constraints &&
7529 682 : !contain_volatile_functions((Node *) defval))
7530 514 : {
7531 : EState *estate;
7532 : ExprState *exprState;
7533 : Datum missingval;
7534 : bool missingIsNull;
7535 :
7536 : /* Evaluate the default expression */
7537 514 : estate = CreateExecutorState();
7538 514 : exprState = ExecPrepareExpr(defval, estate);
7539 514 : missingval = ExecEvalExpr(exprState,
7540 514 : GetPerTupleExprContext(estate),
7541 : &missingIsNull);
7542 : /* If it turns out NULL, nothing to do; else store it */
7543 514 : if (!missingIsNull)
7544 : {
7545 514 : StoreAttrMissingVal(rel, attribute->attnum, missingval);
7546 : /* Make the additional catalog change visible */
7547 514 : CommandCounterIncrement();
7548 514 : has_missing = true;
7549 : }
7550 514 : FreeExecutorState(estate);
7551 : }
7552 : else
7553 : {
7554 : /*
7555 : * Failed to use missing mode. We have to do a table rewrite
7556 : * to install the value --- unless it's a virtual generated
7557 : * column.
7558 : */
7559 308 : if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7560 216 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7561 : }
7562 : }
7563 :
7564 2318 : if (!has_missing)
7565 : {
7566 : /*
7567 : * If the new column is NOT NULL, and there is no missing value,
7568 : * tell Phase 3 it needs to check for NULLs.
7569 : */
7570 1804 : tab->verify_new_notnull |= colDef->is_not_null;
7571 : }
7572 : }
7573 :
7574 : /*
7575 : * Add needed dependency entries for the new column.
7576 : */
7577 2694 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7578 2694 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7579 :
7580 : /*
7581 : * Propagate to children as appropriate. Unlike most other ALTER
7582 : * routines, we have to do this one level of recursion at a time; we can't
7583 : * use find_all_inheritors to do it in one pass.
7584 : */
7585 : children =
7586 2694 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7587 :
7588 : /*
7589 : * If we are told not to recurse, there had better not be any child
7590 : * tables; else the addition would put them out of step.
7591 : */
7592 2694 : if (children && !recurse)
7593 12 : ereport(ERROR,
7594 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7595 : errmsg("column must be added to child tables too")));
7596 :
7597 : /* Children should see column as singly inherited */
7598 2682 : if (!recursing)
7599 : {
7600 2004 : childcmd = copyObject(*cmd);
7601 2004 : colDef = castNode(ColumnDef, childcmd->def);
7602 2004 : colDef->inhcount = 1;
7603 2004 : colDef->is_local = false;
7604 : }
7605 : else
7606 678 : childcmd = *cmd; /* no need to copy again */
7607 :
7608 3420 : foreach(child, children)
7609 : {
7610 738 : Oid childrelid = lfirst_oid(child);
7611 : Relation childrel;
7612 : AlteredTableInfo *childtab;
7613 :
7614 : /* find_inheritance_children already got lock */
7615 738 : childrel = table_open(childrelid, NoLock);
7616 738 : CheckAlterTableIsSafe(childrel);
7617 :
7618 : /* Find or create work queue entry for this table */
7619 738 : childtab = ATGetQueueEntry(wqueue, childrel);
7620 :
7621 : /* Recurse to child; return value is ignored */
7622 738 : ATExecAddColumn(wqueue, childtab, childrel,
7623 : &childcmd, recurse, true,
7624 : lockmode, cur_pass, context);
7625 :
7626 738 : table_close(childrel, NoLock);
7627 : }
7628 :
7629 2682 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7630 2682 : return address;
7631 : }
7632 :
7633 : /*
7634 : * If a new or renamed column will collide with the name of an existing
7635 : * column and if_not_exists is false then error out, else do nothing.
7636 : */
7637 : static bool
7638 3312 : check_for_column_name_collision(Relation rel, const char *colname,
7639 : bool if_not_exists)
7640 : {
7641 : HeapTuple attTuple;
7642 : int attnum;
7643 :
7644 : /*
7645 : * this test is deliberately not attisdropped-aware, since if one tries to
7646 : * add a column matching a dropped column name, it's gonna fail anyway.
7647 : */
7648 3312 : attTuple = SearchSysCache2(ATTNAME,
7649 : ObjectIdGetDatum(RelationGetRelid(rel)),
7650 : PointerGetDatum(colname));
7651 3312 : if (!HeapTupleIsValid(attTuple))
7652 3216 : return true;
7653 :
7654 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7655 96 : ReleaseSysCache(attTuple);
7656 :
7657 : /*
7658 : * We throw a different error message for conflicts with system column
7659 : * names, since they are normally not shown and the user might otherwise
7660 : * be confused about the reason for the conflict.
7661 : */
7662 96 : if (attnum <= 0)
7663 12 : ereport(ERROR,
7664 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7665 : errmsg("column name \"%s\" conflicts with a system column name",
7666 : colname)));
7667 : else
7668 : {
7669 84 : if (if_not_exists)
7670 : {
7671 54 : ereport(NOTICE,
7672 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7673 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7674 : colname, RelationGetRelationName(rel))));
7675 54 : return false;
7676 : }
7677 :
7678 30 : ereport(ERROR,
7679 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7680 : errmsg("column \"%s\" of relation \"%s\" already exists",
7681 : colname, RelationGetRelationName(rel))));
7682 : }
7683 :
7684 : return true;
7685 : }
7686 :
7687 : /*
7688 : * Install a column's dependency on its datatype.
7689 : */
7690 : static void
7691 3844 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7692 : {
7693 : ObjectAddress myself,
7694 : referenced;
7695 :
7696 3844 : myself.classId = RelationRelationId;
7697 3844 : myself.objectId = relid;
7698 3844 : myself.objectSubId = attnum;
7699 3844 : referenced.classId = TypeRelationId;
7700 3844 : referenced.objectId = typid;
7701 3844 : referenced.objectSubId = 0;
7702 3844 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7703 3844 : }
7704 :
7705 : /*
7706 : * Install a column's dependency on its collation.
7707 : */
7708 : static void
7709 3844 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7710 : {
7711 : ObjectAddress myself,
7712 : referenced;
7713 :
7714 : /* We know the default collation is pinned, so don't bother recording it */
7715 3844 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7716 : {
7717 18 : myself.classId = RelationRelationId;
7718 18 : myself.objectId = relid;
7719 18 : myself.objectSubId = attnum;
7720 18 : referenced.classId = CollationRelationId;
7721 18 : referenced.objectId = collid;
7722 18 : referenced.objectSubId = 0;
7723 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7724 : }
7725 3844 : }
7726 :
7727 : /*
7728 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7729 : *
7730 : * Return the address of the modified column. If the column was already
7731 : * nullable, InvalidObjectAddress is returned.
7732 : */
7733 : static ObjectAddress
7734 268 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7735 : LOCKMODE lockmode)
7736 : {
7737 : HeapTuple tuple;
7738 : HeapTuple conTup;
7739 : Form_pg_attribute attTup;
7740 : AttrNumber attnum;
7741 : Relation attr_rel;
7742 : ObjectAddress address;
7743 :
7744 : /*
7745 : * lookup the attribute
7746 : */
7747 268 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7748 :
7749 268 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7750 268 : if (!HeapTupleIsValid(tuple))
7751 18 : ereport(ERROR,
7752 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7753 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7754 : colName, RelationGetRelationName(rel))));
7755 250 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7756 250 : attnum = attTup->attnum;
7757 250 : ObjectAddressSubSet(address, RelationRelationId,
7758 : RelationGetRelid(rel), attnum);
7759 :
7760 : /* If the column is already nullable there's nothing to do. */
7761 250 : if (!attTup->attnotnull)
7762 : {
7763 0 : table_close(attr_rel, RowExclusiveLock);
7764 0 : return InvalidObjectAddress;
7765 : }
7766 :
7767 : /* Prevent them from altering a system attribute */
7768 250 : if (attnum <= 0)
7769 0 : ereport(ERROR,
7770 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7771 : errmsg("cannot alter system column \"%s\"",
7772 : colName)));
7773 :
7774 250 : if (attTup->attidentity)
7775 18 : ereport(ERROR,
7776 : (errcode(ERRCODE_SYNTAX_ERROR),
7777 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7778 : colName, RelationGetRelationName(rel))));
7779 :
7780 : /*
7781 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7782 : */
7783 232 : if (rel->rd_rel->relispartition)
7784 : {
7785 12 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7786 12 : Relation parent = table_open(parentId, AccessShareLock);
7787 12 : TupleDesc tupDesc = RelationGetDescr(parent);
7788 : AttrNumber parent_attnum;
7789 :
7790 12 : parent_attnum = get_attnum(parentId, colName);
7791 12 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7792 12 : ereport(ERROR,
7793 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7794 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7795 : colName)));
7796 0 : table_close(parent, AccessShareLock);
7797 : }
7798 :
7799 : /*
7800 : * Find the constraint that makes this column NOT NULL, and drop it.
7801 : * dropconstraint_internal() resets attnotnull.
7802 : */
7803 220 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7804 220 : if (conTup == NULL)
7805 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7806 : colName, RelationGetRelationName(rel));
7807 :
7808 : /* The normal case: we have a pg_constraint row, remove it */
7809 220 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7810 : false, lockmode);
7811 166 : heap_freetuple(conTup);
7812 :
7813 166 : InvokeObjectPostAlterHook(RelationRelationId,
7814 : RelationGetRelid(rel), attnum);
7815 :
7816 166 : table_close(attr_rel, RowExclusiveLock);
7817 :
7818 166 : return address;
7819 : }
7820 :
7821 : /*
7822 : * set_attnotnull
7823 : * Helper to update/validate the pg_attribute status of a not-null
7824 : * constraint
7825 : *
7826 : * pg_attribute.attnotnull is set true, if it isn't already.
7827 : * If queue_validation is true, also set up wqueue to validate the constraint.
7828 : * wqueue may be given as NULL when validation is not needed (e.g., on table
7829 : * creation).
7830 : */
7831 : static void
7832 25310 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7833 : bool is_valid, bool queue_validation)
7834 : {
7835 : Form_pg_attribute attr;
7836 : CompactAttribute *thisatt;
7837 :
7838 : Assert(!queue_validation || wqueue);
7839 :
7840 25310 : CheckAlterTableIsSafe(rel);
7841 :
7842 : /*
7843 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7844 : * attribute.
7845 : */
7846 25310 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7847 25310 : if (attr->attisdropped)
7848 0 : return;
7849 :
7850 25310 : if (!attr->attnotnull)
7851 : {
7852 : Relation attr_rel;
7853 : HeapTuple tuple;
7854 :
7855 1462 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7856 :
7857 1462 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7858 1462 : if (!HeapTupleIsValid(tuple))
7859 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7860 : attnum, RelationGetRelid(rel));
7861 :
7862 1462 : thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7863 1462 : thisatt->attnullability = ATTNULLABLE_VALID;
7864 :
7865 1462 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7866 :
7867 1462 : attr->attnotnull = true;
7868 1462 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7869 :
7870 : /*
7871 : * If the nullness isn't already proven by validated constraints, have
7872 : * ALTER TABLE phase 3 test for it.
7873 : */
7874 1462 : if (queue_validation && wqueue &&
7875 1244 : !NotNullImpliedByRelConstraints(rel, attr))
7876 : {
7877 : AlteredTableInfo *tab;
7878 :
7879 1194 : tab = ATGetQueueEntry(wqueue, rel);
7880 1194 : tab->verify_new_notnull = true;
7881 : }
7882 :
7883 1462 : CommandCounterIncrement();
7884 :
7885 1462 : table_close(attr_rel, RowExclusiveLock);
7886 1462 : heap_freetuple(tuple);
7887 : }
7888 : else
7889 : {
7890 23848 : CacheInvalidateRelcache(rel);
7891 : }
7892 : }
7893 :
7894 : /*
7895 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7896 : *
7897 : * Add a not-null constraint to a single table and its children. Returns
7898 : * the address of the constraint added to the parent relation, if one gets
7899 : * added, or InvalidObjectAddress otherwise.
7900 : *
7901 : * We must recurse to child tables during execution, rather than using
7902 : * ALTER TABLE's normal prep-time recursion.
7903 : */
7904 : static ObjectAddress
7905 712 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7906 : bool recurse, bool recursing, LOCKMODE lockmode)
7907 : {
7908 : HeapTuple tuple;
7909 : AttrNumber attnum;
7910 : ObjectAddress address;
7911 : Constraint *constraint;
7912 : CookedConstraint *ccon;
7913 : List *cooked;
7914 712 : bool is_no_inherit = false;
7915 :
7916 : /* Guard against stack overflow due to overly deep inheritance tree. */
7917 712 : check_stack_depth();
7918 :
7919 : /* At top level, permission check was done in ATPrepCmd, else do it */
7920 712 : if (recursing)
7921 : {
7922 298 : ATSimplePermissions(AT_AddConstraint, rel,
7923 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7924 : Assert(conName != NULL);
7925 : }
7926 :
7927 712 : attnum = get_attnum(RelationGetRelid(rel), colName);
7928 712 : if (attnum == InvalidAttrNumber)
7929 18 : ereport(ERROR,
7930 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7931 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7932 : colName, RelationGetRelationName(rel))));
7933 :
7934 : /* Prevent them from altering a system attribute */
7935 694 : if (attnum <= 0)
7936 0 : ereport(ERROR,
7937 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7938 : errmsg("cannot alter system column \"%s\"",
7939 : colName)));
7940 :
7941 : /* See if there's already a constraint */
7942 694 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7943 694 : if (HeapTupleIsValid(tuple))
7944 : {
7945 158 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7946 158 : bool changed = false;
7947 :
7948 : /*
7949 : * Don't let a NO INHERIT constraint be changed into inherit.
7950 : */
7951 158 : if (conForm->connoinherit && recurse)
7952 12 : ereport(ERROR,
7953 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7954 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7955 : NameStr(conForm->conname),
7956 : RelationGetRelationName(rel)));
7957 :
7958 : /*
7959 : * If we find an appropriate constraint, we're almost done, but just
7960 : * need to change some properties on it: if we're recursing, increment
7961 : * coninhcount; if not, set conislocal if not already set.
7962 : */
7963 146 : if (recursing)
7964 : {
7965 102 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
7966 : &conForm->coninhcount))
7967 0 : ereport(ERROR,
7968 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7969 : errmsg("too many inheritance parents"));
7970 102 : changed = true;
7971 : }
7972 44 : else if (!conForm->conislocal)
7973 : {
7974 0 : conForm->conislocal = true;
7975 0 : changed = true;
7976 : }
7977 44 : else if (!conForm->convalidated)
7978 : {
7979 : /*
7980 : * Flip attnotnull and convalidated, and also validate the
7981 : * constraint.
7982 : */
7983 24 : return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
7984 : recurse, recursing, lockmode);
7985 : }
7986 :
7987 122 : if (changed)
7988 : {
7989 : Relation constr_rel;
7990 :
7991 102 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7992 :
7993 102 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7994 102 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7995 102 : table_close(constr_rel, RowExclusiveLock);
7996 : }
7997 :
7998 122 : if (changed)
7999 102 : return address;
8000 : else
8001 20 : return InvalidObjectAddress;
8002 : }
8003 :
8004 : /*
8005 : * If we're asked not to recurse, and children exist, raise an error for
8006 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
8007 : * specified.
8008 : */
8009 566 : if (!recurse &&
8010 30 : find_inheritance_children(RelationGetRelid(rel),
8011 : NoLock) != NIL)
8012 : {
8013 18 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8014 6 : ereport(ERROR,
8015 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8016 : errmsg("constraint must be added to child tables too"),
8017 : errhint("Do not specify the ONLY keyword."));
8018 : else
8019 12 : is_no_inherit = true;
8020 : }
8021 :
8022 : /*
8023 : * No constraint exists; we must add one. First determine a name to use,
8024 : * if we haven't already.
8025 : */
8026 530 : if (!recursing)
8027 : {
8028 : Assert(conName == NULL);
8029 340 : conName = ChooseConstraintName(RelationGetRelationName(rel),
8030 : colName, "not_null",
8031 340 : RelationGetNamespace(rel),
8032 : NIL);
8033 : }
8034 :
8035 530 : constraint = makeNotNullConstraint(makeString(colName));
8036 530 : constraint->is_no_inherit = is_no_inherit;
8037 530 : constraint->conname = conName;
8038 :
8039 : /* and do it */
8040 530 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8041 530 : false, !recursing, false, NULL);
8042 530 : ccon = linitial(cooked);
8043 530 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8044 :
8045 530 : InvokeObjectPostAlterHook(RelationRelationId,
8046 : RelationGetRelid(rel), attnum);
8047 :
8048 : /* Mark pg_attribute.attnotnull for the column and queue validation */
8049 530 : set_attnotnull(wqueue, rel, attnum, true, true);
8050 :
8051 : /*
8052 : * Recurse to propagate the constraint to children that don't have one.
8053 : */
8054 530 : if (recurse)
8055 : {
8056 : List *children;
8057 :
8058 506 : children = find_inheritance_children(RelationGetRelid(rel),
8059 : lockmode);
8060 :
8061 1244 : foreach_oid(childoid, children)
8062 : {
8063 244 : Relation childrel = table_open(childoid, NoLock);
8064 :
8065 244 : CommandCounterIncrement();
8066 :
8067 244 : ATExecSetNotNull(wqueue, childrel, conName, colName,
8068 : recurse, true, lockmode);
8069 238 : table_close(childrel, NoLock);
8070 : }
8071 : }
8072 :
8073 524 : return address;
8074 : }
8075 :
8076 : /*
8077 : * NotNullImpliedByRelConstraints
8078 : * Does rel's existing constraints imply NOT NULL for the given attribute?
8079 : */
8080 : static bool
8081 1244 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8082 : {
8083 1244 : NullTest *nnulltest = makeNode(NullTest);
8084 :
8085 2488 : nnulltest->arg = (Expr *) makeVar(1,
8086 1244 : attr->attnum,
8087 : attr->atttypid,
8088 : attr->atttypmod,
8089 : attr->attcollation,
8090 : 0);
8091 1244 : nnulltest->nulltesttype = IS_NOT_NULL;
8092 :
8093 : /*
8094 : * argisrow = false is correct even for a composite column, because
8095 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8096 : * case, just IS DISTINCT FROM NULL.
8097 : */
8098 1244 : nnulltest->argisrow = false;
8099 1244 : nnulltest->location = -1;
8100 :
8101 1244 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8102 : {
8103 50 : ereport(DEBUG1,
8104 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8105 : RelationGetRelationName(rel), NameStr(attr->attname))));
8106 50 : return true;
8107 : }
8108 :
8109 1194 : return false;
8110 : }
8111 :
8112 : /*
8113 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8114 : *
8115 : * Return the address of the affected column.
8116 : */
8117 : static ObjectAddress
8118 584 : ATExecColumnDefault(Relation rel, const char *colName,
8119 : Node *newDefault, LOCKMODE lockmode)
8120 : {
8121 584 : TupleDesc tupdesc = RelationGetDescr(rel);
8122 : AttrNumber attnum;
8123 : ObjectAddress address;
8124 :
8125 : /*
8126 : * get the number of the attribute
8127 : */
8128 584 : attnum = get_attnum(RelationGetRelid(rel), colName);
8129 584 : if (attnum == InvalidAttrNumber)
8130 30 : ereport(ERROR,
8131 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8132 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8133 : colName, RelationGetRelationName(rel))));
8134 :
8135 : /* Prevent them from altering a system attribute */
8136 554 : if (attnum <= 0)
8137 0 : ereport(ERROR,
8138 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8139 : errmsg("cannot alter system column \"%s\"",
8140 : colName)));
8141 :
8142 554 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8143 18 : ereport(ERROR,
8144 : (errcode(ERRCODE_SYNTAX_ERROR),
8145 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8146 : colName, RelationGetRelationName(rel)),
8147 : /* translator: %s is an SQL ALTER command */
8148 : newDefault ? 0 : errhint("Use %s instead.",
8149 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8150 :
8151 536 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8152 12 : ereport(ERROR,
8153 : (errcode(ERRCODE_SYNTAX_ERROR),
8154 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8155 : colName, RelationGetRelationName(rel)),
8156 : newDefault ?
8157 : /* translator: %s is an SQL ALTER command */
8158 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8159 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8160 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8161 :
8162 : /*
8163 : * Remove any old default for the column. We use RESTRICT here for
8164 : * safety, but at present we do not expect anything to depend on the
8165 : * default.
8166 : *
8167 : * We treat removing the existing default as an internal operation when it
8168 : * is preparatory to adding a new default, but as a user-initiated
8169 : * operation when the user asked for a drop.
8170 : */
8171 524 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8172 : newDefault != NULL);
8173 :
8174 524 : if (newDefault)
8175 : {
8176 : /* SET DEFAULT */
8177 : RawColumnDefault *rawEnt;
8178 :
8179 350 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8180 350 : rawEnt->attnum = attnum;
8181 350 : rawEnt->raw_default = newDefault;
8182 350 : rawEnt->generated = '\0';
8183 :
8184 : /*
8185 : * This function is intended for CREATE TABLE, so it processes a
8186 : * _list_ of defaults, but we just do one.
8187 : */
8188 350 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8189 : false, true, false, NULL);
8190 : }
8191 :
8192 518 : ObjectAddressSubSet(address, RelationRelationId,
8193 : RelationGetRelid(rel), attnum);
8194 518 : return address;
8195 : }
8196 :
8197 : /*
8198 : * Add a pre-cooked default expression.
8199 : *
8200 : * Return the address of the affected column.
8201 : */
8202 : static ObjectAddress
8203 80 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8204 : Node *newDefault)
8205 : {
8206 : ObjectAddress address;
8207 :
8208 : /* We assume no checking is required */
8209 :
8210 : /*
8211 : * Remove any old default for the column. We use RESTRICT here for
8212 : * safety, but at present we do not expect anything to depend on the
8213 : * default. (In ordinary cases, there could not be a default in place
8214 : * anyway, but it's possible when combining LIKE with inheritance.)
8215 : */
8216 80 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8217 : true);
8218 :
8219 80 : (void) StoreAttrDefault(rel, attnum, newDefault, true);
8220 :
8221 80 : ObjectAddressSubSet(address, RelationRelationId,
8222 : RelationGetRelid(rel), attnum);
8223 80 : return address;
8224 : }
8225 :
8226 : /*
8227 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8228 : *
8229 : * Return the address of the affected column.
8230 : */
8231 : static ObjectAddress
8232 166 : ATExecAddIdentity(Relation rel, const char *colName,
8233 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8234 : {
8235 : Relation attrelation;
8236 : HeapTuple tuple;
8237 : Form_pg_attribute attTup;
8238 : AttrNumber attnum;
8239 : ObjectAddress address;
8240 166 : ColumnDef *cdef = castNode(ColumnDef, def);
8241 : bool ispartitioned;
8242 :
8243 166 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8244 166 : if (ispartitioned && !recurse)
8245 6 : ereport(ERROR,
8246 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8247 : errmsg("cannot add identity to a column of only the partitioned table"),
8248 : errhint("Do not specify the ONLY keyword.")));
8249 :
8250 160 : if (rel->rd_rel->relispartition && !recursing)
8251 12 : ereport(ERROR,
8252 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8253 : errmsg("cannot add identity to a column of a partition"));
8254 :
8255 148 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8256 :
8257 148 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8258 148 : if (!HeapTupleIsValid(tuple))
8259 0 : ereport(ERROR,
8260 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8261 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8262 : colName, RelationGetRelationName(rel))));
8263 148 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8264 148 : attnum = attTup->attnum;
8265 :
8266 : /* Can't alter a system attribute */
8267 148 : if (attnum <= 0)
8268 0 : ereport(ERROR,
8269 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8270 : errmsg("cannot alter system column \"%s\"",
8271 : colName)));
8272 :
8273 : /*
8274 : * Creating a column as identity implies NOT NULL, so adding the identity
8275 : * to an existing column that is not NOT NULL would create a state that
8276 : * cannot be reproduced without contortions.
8277 : */
8278 148 : if (!attTup->attnotnull)
8279 6 : ereport(ERROR,
8280 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8281 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8282 : colName, RelationGetRelationName(rel))));
8283 :
8284 : /*
8285 : * On the other hand, if a not-null constraint exists, then verify that
8286 : * it's compatible.
8287 : */
8288 142 : if (attTup->attnotnull)
8289 : {
8290 : HeapTuple contup;
8291 : Form_pg_constraint conForm;
8292 :
8293 142 : contup = findNotNullConstraintAttnum(RelationGetRelid(rel),
8294 : attnum);
8295 142 : if (!HeapTupleIsValid(contup))
8296 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8297 : colName, RelationGetRelationName(rel));
8298 :
8299 142 : conForm = (Form_pg_constraint) GETSTRUCT(contup);
8300 142 : if (!conForm->convalidated)
8301 6 : ereport(ERROR,
8302 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8303 : errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8304 : NameStr(conForm->conname), RelationGetRelationName(rel)),
8305 : errhint("You might need to validate it using %s.",
8306 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
8307 : }
8308 :
8309 136 : if (attTup->attidentity)
8310 18 : ereport(ERROR,
8311 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8312 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8313 : colName, RelationGetRelationName(rel))));
8314 :
8315 118 : if (attTup->atthasdef)
8316 6 : ereport(ERROR,
8317 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8318 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8319 : colName, RelationGetRelationName(rel))));
8320 :
8321 112 : attTup->attidentity = cdef->identity;
8322 112 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8323 :
8324 112 : InvokeObjectPostAlterHook(RelationRelationId,
8325 : RelationGetRelid(rel),
8326 : attTup->attnum);
8327 112 : ObjectAddressSubSet(address, RelationRelationId,
8328 : RelationGetRelid(rel), attnum);
8329 112 : heap_freetuple(tuple);
8330 :
8331 112 : table_close(attrelation, RowExclusiveLock);
8332 :
8333 : /*
8334 : * Recurse to propagate the identity column to partitions. Identity is
8335 : * not inherited in regular inheritance children.
8336 : */
8337 112 : if (recurse && ispartitioned)
8338 : {
8339 : List *children;
8340 : ListCell *lc;
8341 :
8342 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8343 :
8344 16 : foreach(lc, children)
8345 : {
8346 : Relation childrel;
8347 :
8348 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8349 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8350 6 : table_close(childrel, NoLock);
8351 : }
8352 : }
8353 :
8354 112 : return address;
8355 : }
8356 :
8357 : /*
8358 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8359 : *
8360 : * Return the address of the affected column.
8361 : */
8362 : static ObjectAddress
8363 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8364 : LOCKMODE lockmode, bool recurse, bool recursing)
8365 : {
8366 : ListCell *option;
8367 74 : DefElem *generatedEl = NULL;
8368 : HeapTuple tuple;
8369 : Form_pg_attribute attTup;
8370 : AttrNumber attnum;
8371 : Relation attrelation;
8372 : ObjectAddress address;
8373 : bool ispartitioned;
8374 :
8375 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8376 74 : if (ispartitioned && !recurse)
8377 6 : ereport(ERROR,
8378 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8379 : errmsg("cannot change identity column of only the partitioned table"),
8380 : errhint("Do not specify the ONLY keyword.")));
8381 :
8382 68 : if (rel->rd_rel->relispartition && !recursing)
8383 12 : ereport(ERROR,
8384 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8385 : errmsg("cannot change identity column of a partition"));
8386 :
8387 100 : foreach(option, castNode(List, def))
8388 : {
8389 44 : DefElem *defel = lfirst_node(DefElem, option);
8390 :
8391 44 : if (strcmp(defel->defname, "generated") == 0)
8392 : {
8393 44 : if (generatedEl)
8394 0 : ereport(ERROR,
8395 : (errcode(ERRCODE_SYNTAX_ERROR),
8396 : errmsg("conflicting or redundant options")));
8397 44 : generatedEl = defel;
8398 : }
8399 : else
8400 0 : elog(ERROR, "option \"%s\" not recognized",
8401 : defel->defname);
8402 : }
8403 :
8404 : /*
8405 : * Even if there is nothing to change here, we run all the checks. There
8406 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8407 : * there.
8408 : */
8409 :
8410 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8411 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8412 56 : if (!HeapTupleIsValid(tuple))
8413 0 : ereport(ERROR,
8414 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8415 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8416 : colName, RelationGetRelationName(rel))));
8417 :
8418 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8419 56 : attnum = attTup->attnum;
8420 :
8421 56 : if (attnum <= 0)
8422 0 : ereport(ERROR,
8423 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8424 : errmsg("cannot alter system column \"%s\"",
8425 : colName)));
8426 :
8427 56 : if (!attTup->attidentity)
8428 6 : ereport(ERROR,
8429 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8430 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8431 : colName, RelationGetRelationName(rel))));
8432 :
8433 50 : if (generatedEl)
8434 : {
8435 44 : attTup->attidentity = defGetInt32(generatedEl);
8436 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8437 :
8438 44 : InvokeObjectPostAlterHook(RelationRelationId,
8439 : RelationGetRelid(rel),
8440 : attTup->attnum);
8441 44 : ObjectAddressSubSet(address, RelationRelationId,
8442 : RelationGetRelid(rel), attnum);
8443 : }
8444 : else
8445 6 : address = InvalidObjectAddress;
8446 :
8447 50 : heap_freetuple(tuple);
8448 50 : table_close(attrelation, RowExclusiveLock);
8449 :
8450 : /*
8451 : * Recurse to propagate the identity change to partitions. Identity is not
8452 : * inherited in regular inheritance children.
8453 : */
8454 50 : if (generatedEl && recurse && ispartitioned)
8455 : {
8456 : List *children;
8457 : ListCell *lc;
8458 :
8459 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8460 :
8461 18 : foreach(lc, children)
8462 : {
8463 : Relation childrel;
8464 :
8465 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8466 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8467 12 : table_close(childrel, NoLock);
8468 : }
8469 : }
8470 :
8471 50 : return address;
8472 : }
8473 :
8474 : /*
8475 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8476 : *
8477 : * Return the address of the affected column.
8478 : */
8479 : static ObjectAddress
8480 68 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8481 : bool recurse, bool recursing)
8482 : {
8483 : HeapTuple tuple;
8484 : Form_pg_attribute attTup;
8485 : AttrNumber attnum;
8486 : Relation attrelation;
8487 : ObjectAddress address;
8488 : Oid seqid;
8489 : ObjectAddress seqaddress;
8490 : bool ispartitioned;
8491 :
8492 68 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8493 68 : if (ispartitioned && !recurse)
8494 6 : ereport(ERROR,
8495 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8496 : errmsg("cannot drop identity from a column of only the partitioned table"),
8497 : errhint("Do not specify the ONLY keyword.")));
8498 :
8499 62 : if (rel->rd_rel->relispartition && !recursing)
8500 6 : ereport(ERROR,
8501 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8502 : errmsg("cannot drop identity from a column of a partition"));
8503 :
8504 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8505 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8506 56 : if (!HeapTupleIsValid(tuple))
8507 0 : ereport(ERROR,
8508 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8509 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8510 : colName, RelationGetRelationName(rel))));
8511 :
8512 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8513 56 : attnum = attTup->attnum;
8514 :
8515 56 : if (attnum <= 0)
8516 0 : ereport(ERROR,
8517 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8518 : errmsg("cannot alter system column \"%s\"",
8519 : colName)));
8520 :
8521 56 : if (!attTup->attidentity)
8522 : {
8523 12 : if (!missing_ok)
8524 6 : ereport(ERROR,
8525 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8526 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8527 : colName, RelationGetRelationName(rel))));
8528 : else
8529 : {
8530 6 : ereport(NOTICE,
8531 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8532 : colName, RelationGetRelationName(rel))));
8533 6 : heap_freetuple(tuple);
8534 6 : table_close(attrelation, RowExclusiveLock);
8535 6 : return InvalidObjectAddress;
8536 : }
8537 : }
8538 :
8539 44 : attTup->attidentity = '\0';
8540 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8541 :
8542 44 : InvokeObjectPostAlterHook(RelationRelationId,
8543 : RelationGetRelid(rel),
8544 : attTup->attnum);
8545 44 : ObjectAddressSubSet(address, RelationRelationId,
8546 : RelationGetRelid(rel), attnum);
8547 44 : heap_freetuple(tuple);
8548 :
8549 44 : table_close(attrelation, RowExclusiveLock);
8550 :
8551 : /*
8552 : * Recurse to drop the identity from column in partitions. Identity is
8553 : * not inherited in regular inheritance children so ignore them.
8554 : */
8555 44 : if (recurse && ispartitioned)
8556 : {
8557 : List *children;
8558 : ListCell *lc;
8559 :
8560 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8561 :
8562 12 : foreach(lc, children)
8563 : {
8564 : Relation childrel;
8565 :
8566 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8567 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8568 6 : table_close(childrel, NoLock);
8569 : }
8570 : }
8571 :
8572 44 : if (!recursing)
8573 : {
8574 : /* drop the internal sequence */
8575 32 : seqid = getIdentitySequence(rel, attnum, false);
8576 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8577 : RelationRelationId, DEPENDENCY_INTERNAL);
8578 32 : CommandCounterIncrement();
8579 32 : seqaddress.classId = RelationRelationId;
8580 32 : seqaddress.objectId = seqid;
8581 32 : seqaddress.objectSubId = 0;
8582 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8583 : }
8584 :
8585 44 : return address;
8586 : }
8587 :
8588 : /*
8589 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8590 : *
8591 : * Return the address of the affected column.
8592 : */
8593 : static ObjectAddress
8594 222 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8595 : Node *newExpr, LOCKMODE lockmode)
8596 : {
8597 : HeapTuple tuple;
8598 : Form_pg_attribute attTup;
8599 : AttrNumber attnum;
8600 : char attgenerated;
8601 : bool rewrite;
8602 : Oid attrdefoid;
8603 : ObjectAddress address;
8604 : Expr *defval;
8605 : NewColumnValue *newval;
8606 : RawColumnDefault *rawEnt;
8607 :
8608 222 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8609 222 : if (!HeapTupleIsValid(tuple))
8610 0 : ereport(ERROR,
8611 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8612 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8613 : colName, RelationGetRelationName(rel))));
8614 :
8615 222 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8616 :
8617 222 : attnum = attTup->attnum;
8618 222 : if (attnum <= 0)
8619 0 : ereport(ERROR,
8620 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8621 : errmsg("cannot alter system column \"%s\"",
8622 : colName)));
8623 :
8624 222 : attgenerated = attTup->attgenerated;
8625 222 : if (!attgenerated)
8626 12 : ereport(ERROR,
8627 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8628 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8629 : colName, RelationGetRelationName(rel))));
8630 :
8631 : /*
8632 : * TODO: This could be done, just need to recheck any constraints
8633 : * afterwards.
8634 : */
8635 210 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8636 108 : rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8637 12 : ereport(ERROR,
8638 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8639 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
8640 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8641 : colName, RelationGetRelationName(rel))));
8642 :
8643 198 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8644 24 : tab->verify_new_notnull = true;
8645 :
8646 : /*
8647 : * We need to prevent this because a change of expression could affect a
8648 : * row filter and inject expressions that are not permitted in a row
8649 : * filter. XXX We could try to have a more precise check to catch only
8650 : * publications with row filters, or even re-verify the row filter
8651 : * expressions.
8652 : */
8653 294 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8654 96 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
8655 6 : ereport(ERROR,
8656 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8657 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8658 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8659 : colName, RelationGetRelationName(rel))));
8660 :
8661 192 : rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8662 :
8663 192 : ReleaseSysCache(tuple);
8664 :
8665 192 : if (rewrite)
8666 : {
8667 : /*
8668 : * Clear all the missing values if we're rewriting the table, since
8669 : * this renders them pointless.
8670 : */
8671 102 : RelationClearMissing(rel);
8672 :
8673 : /* make sure we don't conflict with later attribute modifications */
8674 102 : CommandCounterIncrement();
8675 :
8676 : /*
8677 : * Find everything that depends on the column (constraints, indexes,
8678 : * etc), and record enough information to let us recreate the objects
8679 : * after rewrite.
8680 : */
8681 102 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8682 : }
8683 :
8684 : /*
8685 : * Drop the dependency records of the GENERATED expression, in particular
8686 : * its INTERNAL dependency on the column, which would otherwise cause
8687 : * dependency.c to refuse to perform the deletion.
8688 : */
8689 192 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8690 192 : if (!OidIsValid(attrdefoid))
8691 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8692 : RelationGetRelid(rel), attnum);
8693 192 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8694 :
8695 : /* Make above changes visible */
8696 192 : CommandCounterIncrement();
8697 :
8698 : /*
8699 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8700 : * safety, but at present we do not expect anything to depend on the
8701 : * expression.
8702 : */
8703 192 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8704 : false, false);
8705 :
8706 : /* Prepare to store the new expression, in the catalogs */
8707 192 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8708 192 : rawEnt->attnum = attnum;
8709 192 : rawEnt->raw_default = newExpr;
8710 192 : rawEnt->generated = attgenerated;
8711 :
8712 : /* Store the generated expression */
8713 192 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8714 : false, true, false, NULL);
8715 :
8716 : /* Make above new expression visible */
8717 192 : CommandCounterIncrement();
8718 :
8719 192 : if (rewrite)
8720 : {
8721 : /* Prepare for table rewrite */
8722 102 : defval = (Expr *) build_column_default(rel, attnum);
8723 :
8724 102 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8725 102 : newval->attnum = attnum;
8726 102 : newval->expr = expression_planner(defval);
8727 102 : newval->is_generated = true;
8728 :
8729 102 : tab->newvals = lappend(tab->newvals, newval);
8730 102 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8731 : }
8732 :
8733 : /* Drop any pg_statistic entry for the column */
8734 192 : RemoveStatistics(RelationGetRelid(rel), attnum);
8735 :
8736 192 : InvokeObjectPostAlterHook(RelationRelationId,
8737 : RelationGetRelid(rel), attnum);
8738 :
8739 192 : ObjectAddressSubSet(address, RelationRelationId,
8740 : RelationGetRelid(rel), attnum);
8741 192 : return address;
8742 : }
8743 :
8744 : /*
8745 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8746 : */
8747 : static void
8748 86 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8749 : {
8750 : /*
8751 : * Reject ONLY if there are child tables. We could implement this, but it
8752 : * is a bit complicated. GENERATED clauses must be attached to the column
8753 : * definition and cannot be added later like DEFAULT, so if a child table
8754 : * has a generation expression that the parent does not have, the child
8755 : * column will necessarily be an attislocal column. So to implement ONLY
8756 : * here, we'd need extra code to update attislocal of the direct child
8757 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8758 : * resulting state can be properly dumped and restored.
8759 : */
8760 110 : if (!recurse &&
8761 24 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8762 12 : ereport(ERROR,
8763 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8764 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8765 :
8766 : /*
8767 : * Cannot drop generation expression from inherited columns.
8768 : */
8769 74 : if (!recursing)
8770 : {
8771 : HeapTuple tuple;
8772 : Form_pg_attribute attTup;
8773 :
8774 62 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8775 62 : if (!HeapTupleIsValid(tuple))
8776 0 : ereport(ERROR,
8777 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8778 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8779 : cmd->name, RelationGetRelationName(rel))));
8780 :
8781 62 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8782 :
8783 62 : if (attTup->attinhcount > 0)
8784 12 : ereport(ERROR,
8785 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8786 : errmsg("cannot drop generation expression from inherited column")));
8787 : }
8788 62 : }
8789 :
8790 : /*
8791 : * Return the address of the affected column.
8792 : */
8793 : static ObjectAddress
8794 56 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8795 : {
8796 : HeapTuple tuple;
8797 : Form_pg_attribute attTup;
8798 : AttrNumber attnum;
8799 : Relation attrelation;
8800 : Oid attrdefoid;
8801 : ObjectAddress address;
8802 :
8803 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8804 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8805 56 : if (!HeapTupleIsValid(tuple))
8806 0 : ereport(ERROR,
8807 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8808 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8809 : colName, RelationGetRelationName(rel))));
8810 :
8811 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8812 56 : attnum = attTup->attnum;
8813 :
8814 56 : if (attnum <= 0)
8815 0 : ereport(ERROR,
8816 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8817 : errmsg("cannot alter system column \"%s\"",
8818 : colName)));
8819 :
8820 : /*
8821 : * TODO: This could be done, but it would need a table rewrite to
8822 : * materialize the generated values. Note that for the time being, we
8823 : * still error with missing_ok, so that we don't silently leave the column
8824 : * as generated.
8825 : */
8826 56 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8827 12 : ereport(ERROR,
8828 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8829 : errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8830 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8831 : colName, RelationGetRelationName(rel))));
8832 :
8833 44 : if (!attTup->attgenerated)
8834 : {
8835 24 : if (!missing_ok)
8836 12 : ereport(ERROR,
8837 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8838 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8839 : colName, RelationGetRelationName(rel))));
8840 : else
8841 : {
8842 12 : ereport(NOTICE,
8843 : (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8844 : colName, RelationGetRelationName(rel))));
8845 12 : heap_freetuple(tuple);
8846 12 : table_close(attrelation, RowExclusiveLock);
8847 12 : return InvalidObjectAddress;
8848 : }
8849 : }
8850 :
8851 : /*
8852 : * Mark the column as no longer generated. (The atthasdef flag needs to
8853 : * get cleared too, but RemoveAttrDefault will handle that.)
8854 : */
8855 20 : attTup->attgenerated = '\0';
8856 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8857 :
8858 20 : InvokeObjectPostAlterHook(RelationRelationId,
8859 : RelationGetRelid(rel),
8860 : attnum);
8861 20 : heap_freetuple(tuple);
8862 :
8863 20 : table_close(attrelation, RowExclusiveLock);
8864 :
8865 : /*
8866 : * Drop the dependency records of the GENERATED expression, in particular
8867 : * its INTERNAL dependency on the column, which would otherwise cause
8868 : * dependency.c to refuse to perform the deletion.
8869 : */
8870 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8871 20 : if (!OidIsValid(attrdefoid))
8872 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8873 : RelationGetRelid(rel), attnum);
8874 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8875 :
8876 : /* Make above changes visible */
8877 20 : CommandCounterIncrement();
8878 :
8879 : /*
8880 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8881 : * safety, but at present we do not expect anything to depend on the
8882 : * default.
8883 : */
8884 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8885 : false, false);
8886 :
8887 20 : ObjectAddressSubSet(address, RelationRelationId,
8888 : RelationGetRelid(rel), attnum);
8889 20 : return address;
8890 : }
8891 :
8892 : /*
8893 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8894 : *
8895 : * Return value is the address of the modified column
8896 : */
8897 : static ObjectAddress
8898 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8899 : {
8900 164 : int newtarget = 0;
8901 : bool newtarget_default;
8902 : Relation attrelation;
8903 : HeapTuple tuple,
8904 : newtuple;
8905 : Form_pg_attribute attrtuple;
8906 : AttrNumber attnum;
8907 : ObjectAddress address;
8908 : Datum repl_val[Natts_pg_attribute];
8909 : bool repl_null[Natts_pg_attribute];
8910 : bool repl_repl[Natts_pg_attribute];
8911 :
8912 : /*
8913 : * We allow referencing columns by numbers only for indexes, since table
8914 : * column numbers could contain gaps if columns are later dropped.
8915 : */
8916 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8917 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8918 : !colName)
8919 0 : ereport(ERROR,
8920 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8921 : errmsg("cannot refer to non-index column by number")));
8922 :
8923 : /* -1 was used in previous versions for the default setting */
8924 164 : if (newValue && intVal(newValue) != -1)
8925 : {
8926 120 : newtarget = intVal(newValue);
8927 120 : newtarget_default = false;
8928 : }
8929 : else
8930 44 : newtarget_default = true;
8931 :
8932 164 : if (!newtarget_default)
8933 : {
8934 : /*
8935 : * Limit target to a sane range
8936 : */
8937 120 : if (newtarget < 0)
8938 : {
8939 0 : ereport(ERROR,
8940 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8941 : errmsg("statistics target %d is too low",
8942 : newtarget)));
8943 : }
8944 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8945 : {
8946 0 : newtarget = MAX_STATISTICS_TARGET;
8947 0 : ereport(WARNING,
8948 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8949 : errmsg("lowering statistics target to %d",
8950 : newtarget)));
8951 : }
8952 : }
8953 :
8954 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8955 :
8956 164 : if (colName)
8957 : {
8958 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8959 :
8960 100 : if (!HeapTupleIsValid(tuple))
8961 12 : ereport(ERROR,
8962 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8963 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8964 : colName, RelationGetRelationName(rel))));
8965 : }
8966 : else
8967 : {
8968 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8969 :
8970 64 : if (!HeapTupleIsValid(tuple))
8971 12 : ereport(ERROR,
8972 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8973 : errmsg("column number %d of relation \"%s\" does not exist",
8974 : colNum, RelationGetRelationName(rel))));
8975 : }
8976 :
8977 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8978 :
8979 140 : attnum = attrtuple->attnum;
8980 140 : if (attnum <= 0)
8981 0 : ereport(ERROR,
8982 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8983 : errmsg("cannot alter system column \"%s\"",
8984 : colName)));
8985 :
8986 : /*
8987 : * Prevent this as long as the ANALYZE code skips virtual generated
8988 : * columns.
8989 : */
8990 140 : if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8991 0 : ereport(ERROR,
8992 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8993 : errmsg("cannot alter statistics on virtual generated column \"%s\"",
8994 : colName)));
8995 :
8996 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8997 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8998 : {
8999 52 : if (attnum > rel->rd_index->indnkeyatts)
9000 6 : ereport(ERROR,
9001 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9002 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9003 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9004 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9005 18 : ereport(ERROR,
9006 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9007 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9008 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9009 : errhint("Alter statistics on table column instead.")));
9010 : }
9011 :
9012 : /* Build new tuple. */
9013 116 : memset(repl_null, false, sizeof(repl_null));
9014 116 : memset(repl_repl, false, sizeof(repl_repl));
9015 116 : if (!newtarget_default)
9016 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
9017 : else
9018 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
9019 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
9020 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9021 : repl_val, repl_null, repl_repl);
9022 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9023 :
9024 116 : InvokeObjectPostAlterHook(RelationRelationId,
9025 : RelationGetRelid(rel),
9026 : attrtuple->attnum);
9027 116 : ObjectAddressSubSet(address, RelationRelationId,
9028 : RelationGetRelid(rel), attnum);
9029 :
9030 116 : heap_freetuple(newtuple);
9031 :
9032 116 : ReleaseSysCache(tuple);
9033 :
9034 116 : table_close(attrelation, RowExclusiveLock);
9035 :
9036 116 : return address;
9037 : }
9038 :
9039 : /*
9040 : * Return value is the address of the modified column
9041 : */
9042 : static ObjectAddress
9043 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
9044 : bool isReset, LOCKMODE lockmode)
9045 : {
9046 : Relation attrelation;
9047 : HeapTuple tuple,
9048 : newtuple;
9049 : Form_pg_attribute attrtuple;
9050 : AttrNumber attnum;
9051 : Datum datum,
9052 : newOptions;
9053 : bool isnull;
9054 : ObjectAddress address;
9055 : Datum repl_val[Natts_pg_attribute];
9056 : bool repl_null[Natts_pg_attribute];
9057 : bool repl_repl[Natts_pg_attribute];
9058 :
9059 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9060 :
9061 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9062 :
9063 32 : if (!HeapTupleIsValid(tuple))
9064 0 : ereport(ERROR,
9065 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9066 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9067 : colName, RelationGetRelationName(rel))));
9068 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9069 :
9070 32 : attnum = attrtuple->attnum;
9071 32 : if (attnum <= 0)
9072 0 : ereport(ERROR,
9073 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9074 : errmsg("cannot alter system column \"%s\"",
9075 : colName)));
9076 :
9077 : /* Generate new proposed attoptions (text array) */
9078 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9079 : &isnull);
9080 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9081 : castNode(List, options), NULL, NULL,
9082 : false, isReset);
9083 : /* Validate new options */
9084 32 : (void) attribute_reloptions(newOptions, true);
9085 :
9086 : /* Build new tuple. */
9087 32 : memset(repl_null, false, sizeof(repl_null));
9088 32 : memset(repl_repl, false, sizeof(repl_repl));
9089 32 : if (newOptions != (Datum) 0)
9090 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9091 : else
9092 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
9093 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9094 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9095 : repl_val, repl_null, repl_repl);
9096 :
9097 : /* Update system catalog. */
9098 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9099 :
9100 32 : InvokeObjectPostAlterHook(RelationRelationId,
9101 : RelationGetRelid(rel),
9102 : attrtuple->attnum);
9103 32 : ObjectAddressSubSet(address, RelationRelationId,
9104 : RelationGetRelid(rel), attnum);
9105 :
9106 32 : heap_freetuple(newtuple);
9107 :
9108 32 : ReleaseSysCache(tuple);
9109 :
9110 32 : table_close(attrelation, RowExclusiveLock);
9111 :
9112 32 : return address;
9113 : }
9114 :
9115 : /*
9116 : * Helper function for ATExecSetStorage and ATExecSetCompression
9117 : *
9118 : * Set the attstorage and/or attcompression fields for index columns
9119 : * associated with the specified table column.
9120 : */
9121 : static void
9122 320 : SetIndexStorageProperties(Relation rel, Relation attrelation,
9123 : AttrNumber attnum,
9124 : bool setstorage, char newstorage,
9125 : bool setcompression, char newcompression,
9126 : LOCKMODE lockmode)
9127 : {
9128 : ListCell *lc;
9129 :
9130 412 : foreach(lc, RelationGetIndexList(rel))
9131 : {
9132 92 : Oid indexoid = lfirst_oid(lc);
9133 : Relation indrel;
9134 92 : AttrNumber indattnum = 0;
9135 : HeapTuple tuple;
9136 :
9137 92 : indrel = index_open(indexoid, lockmode);
9138 :
9139 154 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9140 : {
9141 98 : if (indrel->rd_index->indkey.values[i] == attnum)
9142 : {
9143 36 : indattnum = i + 1;
9144 36 : break;
9145 : }
9146 : }
9147 :
9148 92 : if (indattnum == 0)
9149 : {
9150 56 : index_close(indrel, lockmode);
9151 56 : continue;
9152 : }
9153 :
9154 36 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9155 :
9156 36 : if (HeapTupleIsValid(tuple))
9157 : {
9158 36 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9159 :
9160 36 : if (setstorage)
9161 24 : attrtuple->attstorage = newstorage;
9162 :
9163 36 : if (setcompression)
9164 12 : attrtuple->attcompression = newcompression;
9165 :
9166 36 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9167 :
9168 36 : InvokeObjectPostAlterHook(RelationRelationId,
9169 : RelationGetRelid(rel),
9170 : attrtuple->attnum);
9171 :
9172 36 : heap_freetuple(tuple);
9173 : }
9174 :
9175 36 : index_close(indrel, lockmode);
9176 : }
9177 320 : }
9178 :
9179 : /*
9180 : * ALTER TABLE ALTER COLUMN SET STORAGE
9181 : *
9182 : * Return value is the address of the modified column
9183 : */
9184 : static ObjectAddress
9185 260 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9186 : {
9187 : Relation attrelation;
9188 : HeapTuple tuple;
9189 : Form_pg_attribute attrtuple;
9190 : AttrNumber attnum;
9191 : ObjectAddress address;
9192 :
9193 260 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9194 :
9195 260 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9196 :
9197 260 : if (!HeapTupleIsValid(tuple))
9198 12 : ereport(ERROR,
9199 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9200 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9201 : colName, RelationGetRelationName(rel))));
9202 248 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9203 :
9204 248 : attnum = attrtuple->attnum;
9205 248 : if (attnum <= 0)
9206 0 : ereport(ERROR,
9207 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9208 : errmsg("cannot alter system column \"%s\"",
9209 : colName)));
9210 :
9211 248 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9212 :
9213 248 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9214 :
9215 248 : InvokeObjectPostAlterHook(RelationRelationId,
9216 : RelationGetRelid(rel),
9217 : attrtuple->attnum);
9218 :
9219 : /*
9220 : * Apply the change to indexes as well (only for simple index columns,
9221 : * matching behavior of index.c ConstructTupleDescriptor()).
9222 : */
9223 248 : SetIndexStorageProperties(rel, attrelation, attnum,
9224 248 : true, attrtuple->attstorage,
9225 : false, 0,
9226 : lockmode);
9227 :
9228 248 : heap_freetuple(tuple);
9229 :
9230 248 : table_close(attrelation, RowExclusiveLock);
9231 :
9232 248 : ObjectAddressSubSet(address, RelationRelationId,
9233 : RelationGetRelid(rel), attnum);
9234 248 : return address;
9235 : }
9236 :
9237 :
9238 : /*
9239 : * ALTER TABLE DROP COLUMN
9240 : *
9241 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9242 : * because we have to decide at runtime whether to recurse or not depending
9243 : * on whether attinhcount goes to zero or not. (We can't check this in a
9244 : * static pre-pass because it won't handle multiple inheritance situations
9245 : * correctly.)
9246 : */
9247 : static void
9248 1658 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9249 : AlterTableCmd *cmd, LOCKMODE lockmode,
9250 : AlterTableUtilityContext *context)
9251 : {
9252 1658 : if (rel->rd_rel->reloftype && !recursing)
9253 6 : ereport(ERROR,
9254 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9255 : errmsg("cannot drop column from typed table")));
9256 :
9257 1652 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9258 84 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9259 :
9260 1646 : if (recurse)
9261 1366 : cmd->recurse = true;
9262 1646 : }
9263 :
9264 : /*
9265 : * Drops column 'colName' from relation 'rel' and returns the address of the
9266 : * dropped column. The column is also dropped (or marked as no longer
9267 : * inherited from relation) from the relation's inheritance children, if any.
9268 : *
9269 : * In the recursive invocations for inheritance child relations, instead of
9270 : * dropping the column directly (if to be dropped at all), its object address
9271 : * is added to 'addrs', which must be non-NULL in such invocations. All
9272 : * columns are dropped at the same time after all the children have been
9273 : * checked recursively.
9274 : */
9275 : static ObjectAddress
9276 2208 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9277 : DropBehavior behavior,
9278 : bool recurse, bool recursing,
9279 : bool missing_ok, LOCKMODE lockmode,
9280 : ObjectAddresses *addrs)
9281 : {
9282 : HeapTuple tuple;
9283 : Form_pg_attribute targetatt;
9284 : AttrNumber attnum;
9285 : List *children;
9286 : ObjectAddress object;
9287 : bool is_expr;
9288 :
9289 : /* At top level, permission check was done in ATPrepCmd, else do it */
9290 2208 : if (recursing)
9291 562 : ATSimplePermissions(AT_DropColumn, rel,
9292 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9293 :
9294 : /* Initialize addrs on the first invocation */
9295 : Assert(!recursing || addrs != NULL);
9296 :
9297 : /* since this function recurses, it could be driven to stack overflow */
9298 2208 : check_stack_depth();
9299 :
9300 2208 : if (!recursing)
9301 1646 : addrs = new_object_addresses();
9302 :
9303 : /*
9304 : * get the number of the attribute
9305 : */
9306 2208 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9307 2208 : if (!HeapTupleIsValid(tuple))
9308 : {
9309 54 : if (!missing_ok)
9310 : {
9311 36 : ereport(ERROR,
9312 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9313 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9314 : colName, RelationGetRelationName(rel))));
9315 : }
9316 : else
9317 : {
9318 18 : ereport(NOTICE,
9319 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9320 : colName, RelationGetRelationName(rel))));
9321 18 : return InvalidObjectAddress;
9322 : }
9323 : }
9324 2154 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9325 :
9326 2154 : attnum = targetatt->attnum;
9327 :
9328 : /* Can't drop a system attribute */
9329 2154 : if (attnum <= 0)
9330 6 : ereport(ERROR,
9331 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9332 : errmsg("cannot drop system column \"%s\"",
9333 : colName)));
9334 :
9335 : /*
9336 : * Don't drop inherited columns, unless recursing (presumably from a drop
9337 : * of the parent column)
9338 : */
9339 2148 : if (targetatt->attinhcount > 0 && !recursing)
9340 48 : ereport(ERROR,
9341 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9342 : errmsg("cannot drop inherited column \"%s\"",
9343 : colName)));
9344 :
9345 : /*
9346 : * Don't drop columns used in the partition key, either. (If we let this
9347 : * go through, the key column's dependencies would cause a cascaded drop
9348 : * of the whole table, which is surely not what the user expected.)
9349 : */
9350 2100 : if (has_partition_attrs(rel,
9351 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9352 : &is_expr))
9353 30 : ereport(ERROR,
9354 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9355 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9356 : colName, RelationGetRelationName(rel))));
9357 :
9358 2070 : ReleaseSysCache(tuple);
9359 :
9360 : /*
9361 : * Propagate to children as appropriate. Unlike most other ALTER
9362 : * routines, we have to do this one level of recursion at a time; we can't
9363 : * use find_all_inheritors to do it in one pass.
9364 : */
9365 : children =
9366 2070 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9367 :
9368 2070 : if (children)
9369 : {
9370 : Relation attr_rel;
9371 : ListCell *child;
9372 :
9373 : /*
9374 : * In case of a partitioned table, the column must be dropped from the
9375 : * partitions as well.
9376 : */
9377 308 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9378 6 : ereport(ERROR,
9379 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9380 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9381 : errhint("Do not specify the ONLY keyword.")));
9382 :
9383 302 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9384 894 : foreach(child, children)
9385 : {
9386 598 : Oid childrelid = lfirst_oid(child);
9387 : Relation childrel;
9388 : Form_pg_attribute childatt;
9389 :
9390 : /* find_inheritance_children already got lock */
9391 598 : childrel = table_open(childrelid, NoLock);
9392 598 : CheckAlterTableIsSafe(childrel);
9393 :
9394 598 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9395 598 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9396 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9397 : colName, childrelid);
9398 598 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9399 :
9400 598 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9401 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9402 : childrelid, colName);
9403 :
9404 598 : if (recurse)
9405 : {
9406 : /*
9407 : * If the child column has other definition sources, just
9408 : * decrement its inheritance count; if not, recurse to delete
9409 : * it.
9410 : */
9411 574 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9412 : {
9413 : /* Time to delete this child column, too */
9414 562 : ATExecDropColumn(wqueue, childrel, colName,
9415 : behavior, true, true,
9416 : false, lockmode, addrs);
9417 : }
9418 : else
9419 : {
9420 : /* Child column must survive my deletion */
9421 12 : childatt->attinhcount--;
9422 :
9423 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9424 :
9425 : /* Make update visible */
9426 12 : CommandCounterIncrement();
9427 : }
9428 : }
9429 : else
9430 : {
9431 : /*
9432 : * If we were told to drop ONLY in this table (no recursion),
9433 : * we need to mark the inheritors' attributes as locally
9434 : * defined rather than inherited.
9435 : */
9436 24 : childatt->attinhcount--;
9437 24 : childatt->attislocal = true;
9438 :
9439 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9440 :
9441 : /* Make update visible */
9442 24 : CommandCounterIncrement();
9443 : }
9444 :
9445 592 : heap_freetuple(tuple);
9446 :
9447 592 : table_close(childrel, NoLock);
9448 : }
9449 296 : table_close(attr_rel, RowExclusiveLock);
9450 : }
9451 :
9452 : /* Add object to delete */
9453 2058 : object.classId = RelationRelationId;
9454 2058 : object.objectId = RelationGetRelid(rel);
9455 2058 : object.objectSubId = attnum;
9456 2058 : add_exact_object_address(&object, addrs);
9457 :
9458 2058 : if (!recursing)
9459 : {
9460 : /* Recursion has ended, drop everything that was collected */
9461 1502 : performMultipleDeletions(addrs, behavior, 0);
9462 1448 : free_object_addresses(addrs);
9463 : }
9464 :
9465 2004 : return object;
9466 : }
9467 :
9468 : /*
9469 : * Prepare to add a primary key on a table, by adding not-null constraints
9470 : * on all columns.
9471 : *
9472 : * The not-null constraints for a primary key must cover the whole inheritance
9473 : * hierarchy (failing to ensure that leads to funny corner cases). For the
9474 : * normal case where we're asked to recurse, this routine checks if the
9475 : * not-null constraints exist already, and if not queues a requirement for
9476 : * them to be created by phase 2.
9477 : *
9478 : * For the case where we're asked not to recurse, we verify that a not-null
9479 : * constraint exists on each column of each (direct) child table, throwing an
9480 : * error if not. Not throwing an error would also work, because a not-null
9481 : * constraint would be created anyway, but it'd cause a silent scan of the
9482 : * child table to verify absence of nulls. We prefer to let the user know so
9483 : * that they can add the constraint manually without having to hold
9484 : * AccessExclusiveLock while at it.
9485 : *
9486 : * However, it's also important that we do not acquire locks on children if
9487 : * the not-null constraints already exist on the parent, to avoid risking
9488 : * deadlocks during parallel pg_restore of PKs on partitioned tables.
9489 : */
9490 : static void
9491 16078 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9492 : bool recurse, LOCKMODE lockmode,
9493 : AlterTableUtilityContext *context)
9494 : {
9495 : Constraint *pkconstr;
9496 16078 : List *children = NIL;
9497 16078 : bool got_children = false;
9498 :
9499 16078 : pkconstr = castNode(Constraint, cmd->def);
9500 16078 : if (pkconstr->contype != CONSTR_PRIMARY)
9501 9224 : return;
9502 :
9503 : /* Verify that columns are not-null, or request that they be made so */
9504 14676 : foreach_node(String, column, pkconstr->keys)
9505 : {
9506 : AlterTableCmd *newcmd;
9507 : Constraint *nnconstr;
9508 : HeapTuple tuple;
9509 :
9510 : /*
9511 : * First check if a suitable constraint exists. If it does, we don't
9512 : * need to request another one. We do need to bail out if it's not
9513 : * valid, though.
9514 : */
9515 1028 : tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9516 1028 : if (tuple != NULL)
9517 : {
9518 518 : verifyNotNullPKCompatible(tuple, strVal(column));
9519 :
9520 : /* All good with this one; don't request another */
9521 506 : heap_freetuple(tuple);
9522 506 : continue;
9523 : }
9524 510 : else if (!recurse)
9525 : {
9526 : /*
9527 : * No constraint on this column. Asked not to recurse, we won't
9528 : * create one here, but verify that all children have one.
9529 : */
9530 36 : if (!got_children)
9531 : {
9532 36 : children = find_inheritance_children(RelationGetRelid(rel),
9533 : lockmode);
9534 : /* only search for children on the first time through */
9535 36 : got_children = true;
9536 : }
9537 :
9538 72 : foreach_oid(childrelid, children)
9539 : {
9540 : HeapTuple tup;
9541 :
9542 36 : tup = findNotNullConstraint(childrelid, strVal(column));
9543 36 : if (!tup)
9544 6 : ereport(ERROR,
9545 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9546 : strVal(column), get_rel_name(childrelid)));
9547 : /* verify it's good enough */
9548 30 : verifyNotNullPKCompatible(tup, strVal(column));
9549 : }
9550 : }
9551 :
9552 : /* This column is not already not-null, so add it to the queue */
9553 492 : nnconstr = makeNotNullConstraint(column);
9554 :
9555 492 : newcmd = makeNode(AlterTableCmd);
9556 492 : newcmd->subtype = AT_AddConstraint;
9557 : /* note we force recurse=true here; see above */
9558 492 : newcmd->recurse = true;
9559 492 : newcmd->def = (Node *) nnconstr;
9560 :
9561 492 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9562 : }
9563 : }
9564 :
9565 : /*
9566 : * Verify whether the given not-null constraint is compatible with a
9567 : * primary key. If not, an error is thrown.
9568 : */
9569 : static void
9570 548 : verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9571 : {
9572 548 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
9573 :
9574 548 : if (conForm->contype != CONSTRAINT_NOTNULL)
9575 0 : elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9576 :
9577 : /* a NO INHERIT constraint is no good */
9578 548 : if (conForm->connoinherit)
9579 12 : ereport(ERROR,
9580 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9581 : errmsg("cannot create primary key on column \"%s\"", colname),
9582 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9583 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9584 : NameStr(conForm->conname), colname,
9585 : get_rel_name(conForm->conrelid), "NO INHERIT"),
9586 : errhint("You might need to make the existing constraint inheritable using %s.",
9587 : "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9588 :
9589 : /* an unvalidated constraint is no good */
9590 536 : if (!conForm->convalidated)
9591 12 : ereport(ERROR,
9592 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9593 : errmsg("cannot create primary key on column \"%s\"", colname),
9594 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9595 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9596 : NameStr(conForm->conname), colname,
9597 : get_rel_name(conForm->conrelid), "NOT VALID"),
9598 : errhint("You might need to validate it using %s.",
9599 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
9600 524 : }
9601 :
9602 : /*
9603 : * ALTER TABLE ADD INDEX
9604 : *
9605 : * There is no such command in the grammar, but parse_utilcmd.c converts
9606 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9607 : * us schedule creation of the index at the appropriate time during ALTER.
9608 : *
9609 : * Return value is the address of the new index.
9610 : */
9611 : static ObjectAddress
9612 1634 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9613 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9614 : {
9615 : bool check_rights;
9616 : bool skip_build;
9617 : bool quiet;
9618 : ObjectAddress address;
9619 :
9620 : Assert(IsA(stmt, IndexStmt));
9621 : Assert(!stmt->concurrent);
9622 :
9623 : /* The IndexStmt has already been through transformIndexStmt */
9624 : Assert(stmt->transformed);
9625 :
9626 : /* suppress schema rights check when rebuilding existing index */
9627 1634 : check_rights = !is_rebuild;
9628 : /* skip index build if phase 3 will do it or we're reusing an old one */
9629 1634 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9630 : /* suppress notices when rebuilding existing index */
9631 1634 : quiet = is_rebuild;
9632 :
9633 1634 : address = DefineIndex(RelationGetRelid(rel),
9634 : stmt,
9635 : InvalidOid, /* no predefined OID */
9636 : InvalidOid, /* no parent index */
9637 : InvalidOid, /* no parent constraint */
9638 : -1, /* total_parts unknown */
9639 : true, /* is_alter_table */
9640 : check_rights,
9641 : false, /* check_not_in_use - we did it already */
9642 : skip_build,
9643 : quiet);
9644 :
9645 : /*
9646 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9647 : * new index instead of building from scratch. Restore associated fields.
9648 : * This may store InvalidSubTransactionId in both fields, in which case
9649 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9650 : * this after the CCI that made catalog rows visible to any rebuild. The
9651 : * DROP of the old edition of this index will have scheduled the storage
9652 : * for deletion at commit, so cancel that pending deletion.
9653 : */
9654 1464 : if (RelFileNumberIsValid(stmt->oldNumber))
9655 : {
9656 74 : Relation irel = index_open(address.objectId, NoLock);
9657 :
9658 74 : irel->rd_createSubid = stmt->oldCreateSubid;
9659 74 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9660 74 : RelationPreserveStorage(irel->rd_locator, true);
9661 74 : index_close(irel, NoLock);
9662 : }
9663 :
9664 1464 : return address;
9665 : }
9666 :
9667 : /*
9668 : * ALTER TABLE ADD STATISTICS
9669 : *
9670 : * This is no such command in the grammar, but we use this internally to add
9671 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9672 : * column type change.
9673 : */
9674 : static ObjectAddress
9675 74 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9676 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9677 : {
9678 : ObjectAddress address;
9679 :
9680 : Assert(IsA(stmt, CreateStatsStmt));
9681 :
9682 : /* The CreateStatsStmt has already been through transformStatsStmt */
9683 : Assert(stmt->transformed);
9684 :
9685 74 : address = CreateStatistics(stmt, !is_rebuild);
9686 :
9687 74 : return address;
9688 : }
9689 :
9690 : /*
9691 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9692 : *
9693 : * Returns the address of the new constraint.
9694 : */
9695 : static ObjectAddress
9696 10640 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9697 : IndexStmt *stmt, LOCKMODE lockmode)
9698 : {
9699 10640 : Oid index_oid = stmt->indexOid;
9700 : Relation indexRel;
9701 : char *indexName;
9702 : IndexInfo *indexInfo;
9703 : char *constraintName;
9704 : char constraintType;
9705 : ObjectAddress address;
9706 : bits16 flags;
9707 :
9708 : Assert(IsA(stmt, IndexStmt));
9709 : Assert(OidIsValid(index_oid));
9710 : Assert(stmt->isconstraint);
9711 :
9712 : /*
9713 : * Doing this on partitioned tables is not a simple feature to implement,
9714 : * so let's punt for now.
9715 : */
9716 10640 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9717 6 : ereport(ERROR,
9718 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9719 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9720 :
9721 10634 : indexRel = index_open(index_oid, AccessShareLock);
9722 :
9723 10634 : indexName = pstrdup(RelationGetRelationName(indexRel));
9724 :
9725 10634 : indexInfo = BuildIndexInfo(indexRel);
9726 :
9727 : /* this should have been checked at parse time */
9728 10634 : if (!indexInfo->ii_Unique)
9729 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9730 :
9731 : /*
9732 : * Determine name to assign to constraint. We require a constraint to
9733 : * have the same name as the underlying index; therefore, use the index's
9734 : * existing name as the default constraint name, and if the user
9735 : * explicitly gives some other name for the constraint, rename the index
9736 : * to match.
9737 : */
9738 10634 : constraintName = stmt->idxname;
9739 10634 : if (constraintName == NULL)
9740 10608 : constraintName = indexName;
9741 26 : else if (strcmp(constraintName, indexName) != 0)
9742 : {
9743 20 : ereport(NOTICE,
9744 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9745 : indexName, constraintName)));
9746 20 : RenameRelationInternal(index_oid, constraintName, false, true);
9747 : }
9748 :
9749 : /* Extra checks needed if making primary key */
9750 10634 : if (stmt->primary)
9751 6006 : index_check_primary_key(rel, indexInfo, true, stmt);
9752 :
9753 : /* Note we currently don't support EXCLUSION constraints here */
9754 10628 : if (stmt->primary)
9755 6000 : constraintType = CONSTRAINT_PRIMARY;
9756 : else
9757 4628 : constraintType = CONSTRAINT_UNIQUE;
9758 :
9759 : /* Create the catalog entries for the constraint */
9760 10628 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9761 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9762 21256 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9763 10628 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9764 10628 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9765 :
9766 10628 : address = index_constraint_create(rel,
9767 : index_oid,
9768 : InvalidOid,
9769 : indexInfo,
9770 : constraintName,
9771 : constraintType,
9772 : flags,
9773 : allowSystemTableMods,
9774 : false); /* is_internal */
9775 :
9776 10628 : index_close(indexRel, NoLock);
9777 :
9778 10628 : return address;
9779 : }
9780 :
9781 : /*
9782 : * ALTER TABLE ADD CONSTRAINT
9783 : *
9784 : * Return value is the address of the new constraint; if no constraint was
9785 : * added, InvalidObjectAddress is returned.
9786 : */
9787 : static ObjectAddress
9788 12848 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9789 : Constraint *newConstraint, bool recurse, bool is_readd,
9790 : LOCKMODE lockmode)
9791 : {
9792 12848 : ObjectAddress address = InvalidObjectAddress;
9793 :
9794 : Assert(IsA(newConstraint, Constraint));
9795 :
9796 : /*
9797 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9798 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9799 : * parse_utilcmd.c).
9800 : */
9801 12848 : switch (newConstraint->contype)
9802 : {
9803 10176 : case CONSTR_CHECK:
9804 : case CONSTR_NOTNULL:
9805 : address =
9806 10176 : ATAddCheckNNConstraint(wqueue, tab, rel,
9807 : newConstraint, recurse, false, is_readd,
9808 : lockmode);
9809 10032 : break;
9810 :
9811 2672 : case CONSTR_FOREIGN:
9812 :
9813 : /*
9814 : * Assign or validate constraint name
9815 : */
9816 2672 : if (newConstraint->conname)
9817 : {
9818 1200 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9819 : RelationGetRelid(rel),
9820 1200 : newConstraint->conname))
9821 0 : ereport(ERROR,
9822 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9823 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9824 : newConstraint->conname,
9825 : RelationGetRelationName(rel))));
9826 : }
9827 : else
9828 1472 : newConstraint->conname =
9829 1472 : ChooseConstraintName(RelationGetRelationName(rel),
9830 1472 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9831 : "fkey",
9832 1472 : RelationGetNamespace(rel),
9833 : NIL);
9834 :
9835 2672 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9836 : newConstraint,
9837 : recurse, false,
9838 : lockmode);
9839 2124 : break;
9840 :
9841 0 : default:
9842 0 : elog(ERROR, "unrecognized constraint type: %d",
9843 : (int) newConstraint->contype);
9844 : }
9845 :
9846 12156 : return address;
9847 : }
9848 :
9849 : /*
9850 : * Generate the column-name portion of the constraint name for a new foreign
9851 : * key given the list of column names that reference the referenced
9852 : * table. This will be passed to ChooseConstraintName along with the parent
9853 : * table name and the "fkey" suffix.
9854 : *
9855 : * We know that less than NAMEDATALEN characters will actually be used, so we
9856 : * can truncate the result once we've generated that many.
9857 : *
9858 : * XXX see also ChooseExtendedStatisticNameAddition and
9859 : * ChooseIndexNameAddition.
9860 : */
9861 : static char *
9862 1472 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9863 : {
9864 : char buf[NAMEDATALEN * 2];
9865 1472 : int buflen = 0;
9866 : ListCell *lc;
9867 :
9868 1472 : buf[0] = '\0';
9869 3372 : foreach(lc, colnames)
9870 : {
9871 1900 : const char *name = strVal(lfirst(lc));
9872 :
9873 1900 : if (buflen > 0)
9874 428 : buf[buflen++] = '_'; /* insert _ between names */
9875 :
9876 : /*
9877 : * At this point we have buflen <= NAMEDATALEN. name should be less
9878 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9879 : */
9880 1900 : strlcpy(buf + buflen, name, NAMEDATALEN);
9881 1900 : buflen += strlen(buf + buflen);
9882 1900 : if (buflen >= NAMEDATALEN)
9883 0 : break;
9884 : }
9885 1472 : return pstrdup(buf);
9886 : }
9887 :
9888 : /*
9889 : * Add a check or not-null constraint to a single table and its children.
9890 : * Returns the address of the constraint added to the parent relation,
9891 : * if one gets added, or InvalidObjectAddress otherwise.
9892 : *
9893 : * Subroutine for ATExecAddConstraint.
9894 : *
9895 : * We must recurse to child tables during execution, rather than using
9896 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9897 : * constraints *must* be given the same name, else they won't be seen as
9898 : * related later. If the user didn't explicitly specify a name, then
9899 : * AddRelationNewConstraints would normally assign different names to the
9900 : * child constraints. To fix that, we must capture the name assigned at
9901 : * the parent table and pass that down.
9902 : */
9903 : static ObjectAddress
9904 10966 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9905 : Constraint *constr, bool recurse, bool recursing,
9906 : bool is_readd, LOCKMODE lockmode)
9907 : {
9908 : List *newcons;
9909 : ListCell *lcon;
9910 : List *children;
9911 : ListCell *child;
9912 10966 : ObjectAddress address = InvalidObjectAddress;
9913 :
9914 : /* Guard against stack overflow due to overly deep inheritance tree. */
9915 10966 : check_stack_depth();
9916 :
9917 : /* At top level, permission check was done in ATPrepCmd, else do it */
9918 10966 : if (recursing)
9919 790 : ATSimplePermissions(AT_AddConstraint, rel,
9920 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9921 :
9922 : /*
9923 : * Call AddRelationNewConstraints to do the work, making sure it works on
9924 : * a copy of the Constraint so transformExpr can't modify the original. It
9925 : * returns a list of cooked constraints.
9926 : *
9927 : * If the constraint ends up getting merged with a pre-existing one, it's
9928 : * omitted from the returned list, which is what we want: we do not need
9929 : * to do any validation work. That can only happen at child tables,
9930 : * though, since we disallow merging at the top level.
9931 : */
9932 10966 : newcons = AddRelationNewConstraints(rel, NIL,
9933 10966 : list_make1(copyObject(constr)),
9934 10966 : recursing || is_readd, /* allow_merge */
9935 10966 : !recursing, /* is_local */
9936 : is_readd, /* is_internal */
9937 10966 : NULL); /* queryString not available
9938 : * here */
9939 :
9940 : /* we don't expect more than one constraint here */
9941 : Assert(list_length(newcons) <= 1);
9942 :
9943 : /* Add each to-be-validated constraint to Phase 3's queue */
9944 21456 : foreach(lcon, newcons)
9945 : {
9946 10628 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9947 :
9948 10628 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9949 : {
9950 : NewConstraint *newcon;
9951 :
9952 910 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9953 910 : newcon->name = ccon->name;
9954 910 : newcon->contype = ccon->contype;
9955 910 : newcon->qual = ccon->expr;
9956 :
9957 910 : tab->constraints = lappend(tab->constraints, newcon);
9958 : }
9959 :
9960 : /* Save the actually assigned name if it was defaulted */
9961 10628 : if (constr->conname == NULL)
9962 8898 : constr->conname = ccon->name;
9963 :
9964 : /*
9965 : * If adding a valid not-null constraint, set the pg_attribute flag
9966 : * and tell phase 3 to verify existing rows, if needed. For an
9967 : * invalid constraint, just set attnotnull, without queueing
9968 : * verification.
9969 : */
9970 10628 : if (constr->contype == CONSTR_NOTNULL)
9971 9314 : set_attnotnull(wqueue, rel, ccon->attnum,
9972 9314 : !constr->skip_validation,
9973 9314 : !constr->skip_validation);
9974 :
9975 10628 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9976 : }
9977 :
9978 : /* At this point we must have a locked-down name to use */
9979 : Assert(newcons == NIL || constr->conname != NULL);
9980 :
9981 : /* Advance command counter in case same table is visited multiple times */
9982 10828 : CommandCounterIncrement();
9983 :
9984 : /*
9985 : * If the constraint got merged with an existing constraint, we're done.
9986 : * We mustn't recurse to child tables in this case, because they've
9987 : * already got the constraint, and visiting them again would lead to an
9988 : * incorrect value for coninhcount.
9989 : */
9990 10828 : if (newcons == NIL)
9991 200 : return address;
9992 :
9993 : /*
9994 : * If adding a NO INHERIT constraint, no need to find our children.
9995 : */
9996 10628 : if (constr->is_no_inherit)
9997 84 : return address;
9998 :
9999 : /*
10000 : * Propagate to children as appropriate. Unlike most other ALTER
10001 : * routines, we have to do this one level of recursion at a time; we can't
10002 : * use find_all_inheritors to do it in one pass.
10003 : */
10004 : children =
10005 10544 : find_inheritance_children(RelationGetRelid(rel), lockmode);
10006 :
10007 : /*
10008 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
10009 : * constraint creation only if there are no children currently. Error out
10010 : * otherwise.
10011 : */
10012 10544 : if (!recurse && children != NIL)
10013 6 : ereport(ERROR,
10014 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10015 : errmsg("constraint must be added to child tables too")));
10016 :
10017 : /*
10018 : * Recurse to create the constraint on each child.
10019 : */
10020 11298 : foreach(child, children)
10021 : {
10022 790 : Oid childrelid = lfirst_oid(child);
10023 : Relation childrel;
10024 : AlteredTableInfo *childtab;
10025 :
10026 : /* find_inheritance_children already got lock */
10027 790 : childrel = table_open(childrelid, NoLock);
10028 790 : CheckAlterTableIsSafe(childrel);
10029 :
10030 : /* Find or create work queue entry for this table */
10031 790 : childtab = ATGetQueueEntry(wqueue, childrel);
10032 :
10033 : /* Recurse to this child */
10034 790 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
10035 : constr, recurse, true, is_readd, lockmode);
10036 :
10037 760 : table_close(childrel, NoLock);
10038 : }
10039 :
10040 10508 : return address;
10041 : }
10042 :
10043 : /*
10044 : * Add a foreign-key constraint to a single table; return the new constraint's
10045 : * address.
10046 : *
10047 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
10048 : * lock on the rel, and have done appropriate validity checks for it.
10049 : * We do permissions checks here, however.
10050 : *
10051 : * When the referenced or referencing tables (or both) are partitioned,
10052 : * multiple pg_constraint rows are required -- one for each partitioned table
10053 : * and each partition on each side (fortunately, not one for every combination
10054 : * thereof). We also need action triggers on each leaf partition on the
10055 : * referenced side, and check triggers on each leaf partition on the
10056 : * referencing side.
10057 : */
10058 : static ObjectAddress
10059 2672 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
10060 : Constraint *fkconstraint,
10061 : bool recurse, bool recursing, LOCKMODE lockmode)
10062 : {
10063 : Relation pkrel;
10064 2672 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
10065 2672 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
10066 2672 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
10067 2672 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
10068 2672 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10069 2672 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10070 2672 : Oid opclasses[INDEX_MAX_KEYS] = {0};
10071 2672 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10072 2672 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10073 2672 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10074 2672 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10075 : bool with_period;
10076 : bool pk_has_without_overlaps;
10077 : int i;
10078 : int numfks,
10079 : numpks,
10080 : numfkdelsetcols;
10081 : Oid indexOid;
10082 : bool old_check_ok;
10083 : ObjectAddress address;
10084 2672 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10085 :
10086 : /*
10087 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10088 : * delete rows out from under us.
10089 : */
10090 2672 : if (OidIsValid(fkconstraint->old_pktable_oid))
10091 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10092 : else
10093 2600 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10094 :
10095 : /*
10096 : * Validity checks (permission checks wait till we have the column
10097 : * numbers)
10098 : */
10099 2666 : if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10100 6 : ereport(ERROR,
10101 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
10102 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10103 : RelationGetRelationName(rel),
10104 : RelationGetRelationName(pkrel)));
10105 :
10106 2660 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10107 350 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10108 0 : ereport(ERROR,
10109 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10110 : errmsg("referenced relation \"%s\" is not a table",
10111 : RelationGetRelationName(pkrel))));
10112 :
10113 2660 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
10114 2 : ereport(ERROR,
10115 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10116 : errmsg("permission denied: \"%s\" is a system catalog",
10117 : RelationGetRelationName(pkrel))));
10118 :
10119 : /*
10120 : * References from permanent or unlogged tables to temp tables, and from
10121 : * permanent tables to unlogged tables, are disallowed because the
10122 : * referenced data can vanish out from under us. References from temp
10123 : * tables to any other table type are also disallowed, because other
10124 : * backends might need to run the RI triggers on the perm table, but they
10125 : * can't reliably see tuples in the local buffers of other backends.
10126 : */
10127 2658 : switch (rel->rd_rel->relpersistence)
10128 : {
10129 2368 : case RELPERSISTENCE_PERMANENT:
10130 2368 : if (!RelationIsPermanent(pkrel))
10131 0 : ereport(ERROR,
10132 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10133 : errmsg("constraints on permanent tables may reference only permanent tables")));
10134 2368 : break;
10135 12 : case RELPERSISTENCE_UNLOGGED:
10136 12 : if (!RelationIsPermanent(pkrel)
10137 12 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10138 0 : ereport(ERROR,
10139 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10140 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10141 12 : break;
10142 278 : case RELPERSISTENCE_TEMP:
10143 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10144 0 : ereport(ERROR,
10145 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10146 : errmsg("constraints on temporary tables may reference only temporary tables")));
10147 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10148 0 : ereport(ERROR,
10149 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10150 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
10151 278 : break;
10152 : }
10153 :
10154 : /*
10155 : * Look up the referencing attributes to make sure they exist, and record
10156 : * their attnums and type and collation OIDs.
10157 : */
10158 2658 : numfks = transformColumnNameList(RelationGetRelid(rel),
10159 : fkconstraint->fk_attrs,
10160 : fkattnum, fktypoid, fkcolloid);
10161 2628 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10162 2628 : if (with_period && !fkconstraint->fk_with_period)
10163 24 : ereport(ERROR,
10164 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10165 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10166 :
10167 2604 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10168 : fkconstraint->fk_del_set_cols,
10169 : fkdelsetcols, NULL, NULL);
10170 2598 : numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10171 : numfkdelsetcols,
10172 : fkdelsetcols,
10173 : fkconstraint->fk_del_set_cols);
10174 :
10175 : /*
10176 : * If the attribute list for the referenced table was omitted, lookup the
10177 : * definition of the primary key and use it. Otherwise, validate the
10178 : * supplied attribute list. In either case, discover the index OID and
10179 : * index opclasses, and the attnums and type and collation OIDs of the
10180 : * attributes.
10181 : */
10182 2592 : if (fkconstraint->pk_attrs == NIL)
10183 : {
10184 1256 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10185 : &fkconstraint->pk_attrs,
10186 : pkattnum, pktypoid, pkcolloid,
10187 : opclasses, &pk_has_without_overlaps);
10188 :
10189 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10190 1256 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10191 24 : ereport(ERROR,
10192 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10193 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10194 : }
10195 : else
10196 : {
10197 1336 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
10198 : fkconstraint->pk_attrs,
10199 : pkattnum, pktypoid, pkcolloid);
10200 :
10201 : /* Since we got pk_attrs, one should be a period. */
10202 1306 : if (with_period && !fkconstraint->pk_with_period)
10203 24 : ereport(ERROR,
10204 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10205 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10206 :
10207 : /* Look for an index matching the column list */
10208 1282 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10209 : with_period, opclasses, &pk_has_without_overlaps);
10210 : }
10211 :
10212 : /*
10213 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10214 : * must use PERIOD.
10215 : */
10216 2478 : if (pk_has_without_overlaps && !with_period)
10217 12 : ereport(ERROR,
10218 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10219 : errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10220 :
10221 : /*
10222 : * Now we can check permissions.
10223 : */
10224 2466 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10225 :
10226 : /*
10227 : * Check some things for generated columns.
10228 : */
10229 5796 : for (i = 0; i < numfks; i++)
10230 : {
10231 3360 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10232 :
10233 3360 : if (attgenerated)
10234 : {
10235 : /*
10236 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10237 : */
10238 48 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10239 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10240 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10241 12 : ereport(ERROR,
10242 : (errcode(ERRCODE_SYNTAX_ERROR),
10243 : errmsg("invalid %s action for foreign key constraint containing generated column",
10244 : "ON UPDATE")));
10245 36 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10246 24 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10247 12 : ereport(ERROR,
10248 : (errcode(ERRCODE_SYNTAX_ERROR),
10249 : errmsg("invalid %s action for foreign key constraint containing generated column",
10250 : "ON DELETE")));
10251 : }
10252 :
10253 : /*
10254 : * FKs on virtual columns are not supported. This would require
10255 : * various additional support in ri_triggers.c, including special
10256 : * handling in ri_NullCheck(), ri_KeysEqual(),
10257 : * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10258 : * as NULL there). Also not really practical as long as you can't
10259 : * index virtual columns.
10260 : */
10261 3336 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10262 6 : ereport(ERROR,
10263 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10264 : errmsg("foreign key constraints on virtual generated columns are not supported")));
10265 : }
10266 :
10267 : /*
10268 : * Some actions are currently unsupported for foreign keys using PERIOD.
10269 : */
10270 2436 : if (fkconstraint->fk_with_period)
10271 : {
10272 278 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10273 266 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10274 248 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10275 230 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10276 66 : ereport(ERROR,
10277 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10278 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10279 : "ON UPDATE"));
10280 :
10281 212 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10282 206 : fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10283 206 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10284 206 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10285 6 : ereport(ERROR,
10286 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10287 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10288 : "ON DELETE"));
10289 : }
10290 :
10291 : /*
10292 : * Look up the equality operators to use in the constraint.
10293 : *
10294 : * Note that we have to be careful about the difference between the actual
10295 : * PK column type and the opclass' declared input type, which might be
10296 : * only binary-compatible with it. The declared opcintype is the right
10297 : * thing to probe pg_amop with.
10298 : */
10299 2364 : if (numfks != numpks)
10300 0 : ereport(ERROR,
10301 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10302 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10303 :
10304 : /*
10305 : * On the strength of a previous constraint, we might avoid scanning
10306 : * tables to validate this one. See below.
10307 : */
10308 2364 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10309 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10310 :
10311 5166 : for (i = 0; i < numpks; i++)
10312 : {
10313 3042 : Oid pktype = pktypoid[i];
10314 3042 : Oid fktype = fktypoid[i];
10315 : Oid fktyped;
10316 3042 : Oid pkcoll = pkcolloid[i];
10317 3042 : Oid fkcoll = fkcolloid[i];
10318 : HeapTuple cla_ht;
10319 : Form_pg_opclass cla_tup;
10320 : Oid amid;
10321 : Oid opfamily;
10322 : Oid opcintype;
10323 : bool for_overlaps;
10324 : CompareType cmptype;
10325 : Oid pfeqop;
10326 : Oid ppeqop;
10327 : Oid ffeqop;
10328 : int16 eqstrategy;
10329 : Oid pfeqop_right;
10330 :
10331 : /* We need several fields out of the pg_opclass entry */
10332 3042 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10333 3042 : if (!HeapTupleIsValid(cla_ht))
10334 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10335 3042 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10336 3042 : amid = cla_tup->opcmethod;
10337 3042 : opfamily = cla_tup->opcfamily;
10338 3042 : opcintype = cla_tup->opcintype;
10339 3042 : ReleaseSysCache(cla_ht);
10340 :
10341 : /*
10342 : * Get strategy number from index AM.
10343 : *
10344 : * For a normal foreign-key constraint, this should not fail, since we
10345 : * already checked that the index is unique and should therefore have
10346 : * appropriate equal operators. For a period foreign key, this could
10347 : * fail if we selected a non-matching exclusion constraint earlier.
10348 : * (XXX Maybe we should do these lookups earlier so we don't end up
10349 : * doing that.)
10350 : */
10351 3042 : for_overlaps = with_period && i == numpks - 1;
10352 3042 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10353 3042 : eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10354 3042 : if (eqstrategy == InvalidStrategy)
10355 0 : ereport(ERROR,
10356 : errcode(ERRCODE_UNDEFINED_OBJECT),
10357 : for_overlaps
10358 : ? errmsg("could not identify an overlaps operator for foreign key")
10359 : : errmsg("could not identify an equality operator for foreign key"),
10360 : errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10361 : cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10362 :
10363 : /*
10364 : * There had better be a primary equality operator for the index.
10365 : * We'll use it for PK = PK comparisons.
10366 : */
10367 3042 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10368 : eqstrategy);
10369 :
10370 3042 : if (!OidIsValid(ppeqop))
10371 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10372 : eqstrategy, opcintype, opcintype, opfamily);
10373 :
10374 : /*
10375 : * Are there equality operators that take exactly the FK type? Assume
10376 : * we should look through any domain here.
10377 : */
10378 3042 : fktyped = getBaseType(fktype);
10379 :
10380 3042 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10381 : eqstrategy);
10382 3042 : if (OidIsValid(pfeqop))
10383 : {
10384 2346 : pfeqop_right = fktyped;
10385 2346 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10386 : eqstrategy);
10387 : }
10388 : else
10389 : {
10390 : /* keep compiler quiet */
10391 696 : pfeqop_right = InvalidOid;
10392 696 : ffeqop = InvalidOid;
10393 : }
10394 :
10395 3042 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10396 : {
10397 : /*
10398 : * Otherwise, look for an implicit cast from the FK type to the
10399 : * opcintype, and if found, use the primary equality operator.
10400 : * This is a bit tricky because opcintype might be a polymorphic
10401 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10402 : * whether the two actual column types can be concurrently cast to
10403 : * that type. (Otherwise, we'd fail to reject combinations such
10404 : * as int[] and point[].)
10405 : */
10406 : Oid input_typeids[2];
10407 : Oid target_typeids[2];
10408 :
10409 696 : input_typeids[0] = pktype;
10410 696 : input_typeids[1] = fktype;
10411 696 : target_typeids[0] = opcintype;
10412 696 : target_typeids[1] = opcintype;
10413 696 : if (can_coerce_type(2, input_typeids, target_typeids,
10414 : COERCION_IMPLICIT))
10415 : {
10416 468 : pfeqop = ffeqop = ppeqop;
10417 468 : pfeqop_right = opcintype;
10418 : }
10419 : }
10420 :
10421 3042 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10422 228 : ereport(ERROR,
10423 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10424 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10425 : fkconstraint->conname),
10426 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10427 : "are of incompatible types: %s and %s.",
10428 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10429 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10430 : format_type_be(fktype),
10431 : format_type_be(pktype))));
10432 :
10433 : /*
10434 : * This shouldn't be possible, but better check to make sure we have a
10435 : * consistent state for the check below.
10436 : */
10437 2814 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10438 0 : elog(ERROR, "key columns are not both collatable");
10439 :
10440 2814 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10441 : {
10442 : bool pkcolldet;
10443 : bool fkcolldet;
10444 :
10445 104 : pkcolldet = get_collation_isdeterministic(pkcoll);
10446 104 : fkcolldet = get_collation_isdeterministic(fkcoll);
10447 :
10448 : /*
10449 : * SQL requires that both collations are the same. This is
10450 : * because we need a consistent notion of equality on both
10451 : * columns. We relax this by allowing different collations if
10452 : * they are both deterministic. (This is also for backward
10453 : * compatibility, because PostgreSQL has always allowed this.)
10454 : */
10455 104 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10456 12 : ereport(ERROR,
10457 : (errcode(ERRCODE_COLLATION_MISMATCH),
10458 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10459 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10460 : "have incompatible collations: \"%s\" and \"%s\". "
10461 : "If either collation is nondeterministic, then both collations have to be the same.",
10462 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10463 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10464 : get_collation_name(fkcoll),
10465 : get_collation_name(pkcoll))));
10466 : }
10467 :
10468 2802 : if (old_check_ok)
10469 : {
10470 : /*
10471 : * When a pfeqop changes, revalidate the constraint. We could
10472 : * permit intra-opfamily changes, but that adds subtle complexity
10473 : * without any concrete benefit for core types. We need not
10474 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10475 : */
10476 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10477 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10478 : old_pfeqop_item);
10479 : }
10480 2802 : if (old_check_ok)
10481 : {
10482 : Oid old_fktype;
10483 : Oid new_fktype;
10484 : CoercionPathType old_pathtype;
10485 : CoercionPathType new_pathtype;
10486 : Oid old_castfunc;
10487 : Oid new_castfunc;
10488 : Oid old_fkcoll;
10489 : Oid new_fkcoll;
10490 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10491 6 : fkattnum[i] - 1);
10492 :
10493 : /*
10494 : * Identify coercion pathways from each of the old and new FK-side
10495 : * column types to the right (foreign) operand type of the pfeqop.
10496 : * We may assume that pg_constraint.conkey is not changing.
10497 : */
10498 6 : old_fktype = attr->atttypid;
10499 6 : new_fktype = fktype;
10500 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10501 : &old_castfunc);
10502 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10503 : &new_castfunc);
10504 :
10505 6 : old_fkcoll = attr->attcollation;
10506 6 : new_fkcoll = fkcoll;
10507 :
10508 : /*
10509 : * Upon a change to the cast from the FK column to its pfeqop
10510 : * operand, revalidate the constraint. For this evaluation, a
10511 : * binary coercion cast is equivalent to no cast at all. While
10512 : * type implementors should design implicit casts with an eye
10513 : * toward consistency of operations like equality, we cannot
10514 : * assume here that they have done so.
10515 : *
10516 : * A function with a polymorphic argument could change behavior
10517 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10518 : * when the cast destination is polymorphic, we only avoid
10519 : * revalidation if the input type has not changed at all. Given
10520 : * just the core data types and operator classes, this requirement
10521 : * prevents no would-be optimizations.
10522 : *
10523 : * If the cast converts from a base type to a domain thereon, then
10524 : * that domain type must be the opcintype of the unique index.
10525 : * Necessarily, the primary key column must then be of the domain
10526 : * type. Since the constraint was previously valid, all values on
10527 : * the foreign side necessarily exist on the primary side and in
10528 : * turn conform to the domain. Consequently, we need not treat
10529 : * domains specially here.
10530 : *
10531 : * If the collation changes, revalidation is required, unless both
10532 : * collations are deterministic, because those share the same
10533 : * notion of equality (because texteq reduces to bitwise
10534 : * equality).
10535 : *
10536 : * We need not directly consider the PK type. It's necessarily
10537 : * binary coercible to the opcintype of the unique index column,
10538 : * and ri_triggers.c will only deal with PK datums in terms of
10539 : * that opcintype. Changing the opcintype also changes pfeqop.
10540 : */
10541 6 : old_check_ok = (new_pathtype == old_pathtype &&
10542 6 : new_castfunc == old_castfunc &&
10543 6 : (!IsPolymorphicType(pfeqop_right) ||
10544 12 : new_fktype == old_fktype) &&
10545 0 : (new_fkcoll == old_fkcoll ||
10546 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10547 : }
10548 :
10549 2802 : pfeqoperators[i] = pfeqop;
10550 2802 : ppeqoperators[i] = ppeqop;
10551 2802 : ffeqoperators[i] = ffeqop;
10552 : }
10553 :
10554 : /*
10555 : * For FKs with PERIOD we need additional operators to check whether the
10556 : * referencing row's range is contained by the aggregated ranges of the
10557 : * referenced row(s). For rangetypes and multirangetypes this is
10558 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10559 : * support for now. FKs will look these up at "runtime", but we should
10560 : * make sure the lookup works here, even if we don't use the values.
10561 : */
10562 2124 : if (with_period)
10563 : {
10564 : Oid periodoperoid;
10565 : Oid aggedperiodoperoid;
10566 : Oid intersectoperoid;
10567 :
10568 188 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10569 : &intersectoperoid);
10570 : }
10571 :
10572 : /* First, create the constraint catalog entry itself. */
10573 2124 : address = addFkConstraint(addFkBothSides,
10574 : fkconstraint->conname, fkconstraint, rel, pkrel,
10575 : indexOid,
10576 : InvalidOid, /* no parent constraint */
10577 : numfks,
10578 : pkattnum,
10579 : fkattnum,
10580 : pfeqoperators,
10581 : ppeqoperators,
10582 : ffeqoperators,
10583 : numfkdelsetcols,
10584 : fkdelsetcols,
10585 : false,
10586 : with_period);
10587 :
10588 : /* Next process the action triggers at the referenced side and recurse */
10589 2124 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10590 : indexOid,
10591 : address.objectId,
10592 : numfks,
10593 : pkattnum,
10594 : fkattnum,
10595 : pfeqoperators,
10596 : ppeqoperators,
10597 : ffeqoperators,
10598 : numfkdelsetcols,
10599 : fkdelsetcols,
10600 : old_check_ok,
10601 : InvalidOid, InvalidOid,
10602 : with_period);
10603 :
10604 : /* Lastly create the check triggers at the referencing side and recurse */
10605 2124 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10606 : indexOid,
10607 : address.objectId,
10608 : numfks,
10609 : pkattnum,
10610 : fkattnum,
10611 : pfeqoperators,
10612 : ppeqoperators,
10613 : ffeqoperators,
10614 : numfkdelsetcols,
10615 : fkdelsetcols,
10616 : old_check_ok,
10617 : lockmode,
10618 : InvalidOid, InvalidOid,
10619 : with_period);
10620 :
10621 : /*
10622 : * Done. Close pk table, but keep lock until we've committed.
10623 : */
10624 2124 : table_close(pkrel, NoLock);
10625 :
10626 2124 : return address;
10627 : }
10628 :
10629 : /*
10630 : * validateFkOnDeleteSetColumns
10631 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10632 : * column lists are valid.
10633 : *
10634 : * If there are duplicates in the fksetcolsattnums[] array, this silently
10635 : * removes the dups. The new count of numfksetcols is returned.
10636 : */
10637 : static int
10638 2598 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10639 : int numfksetcols, int16 *fksetcolsattnums,
10640 : List *fksetcols)
10641 : {
10642 2598 : int numcolsout = 0;
10643 :
10644 2628 : for (int i = 0; i < numfksetcols; i++)
10645 : {
10646 36 : int16 setcol_attnum = fksetcolsattnums[i];
10647 36 : bool seen = false;
10648 :
10649 : /* Make sure it's in fkattnums[] */
10650 66 : for (int j = 0; j < numfks; j++)
10651 : {
10652 60 : if (fkattnums[j] == setcol_attnum)
10653 : {
10654 30 : seen = true;
10655 30 : break;
10656 : }
10657 : }
10658 :
10659 36 : if (!seen)
10660 : {
10661 6 : char *col = strVal(list_nth(fksetcols, i));
10662 :
10663 6 : ereport(ERROR,
10664 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10665 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10666 : }
10667 :
10668 : /* Now check for dups */
10669 30 : seen = false;
10670 30 : for (int j = 0; j < numcolsout; j++)
10671 : {
10672 6 : if (fksetcolsattnums[j] == setcol_attnum)
10673 : {
10674 6 : seen = true;
10675 6 : break;
10676 : }
10677 : }
10678 30 : if (!seen)
10679 24 : fksetcolsattnums[numcolsout++] = setcol_attnum;
10680 : }
10681 2592 : return numcolsout;
10682 : }
10683 :
10684 : /*
10685 : * addFkConstraint
10686 : * Install pg_constraint entries to implement a foreign key constraint.
10687 : * Caller must separately invoke addFkRecurseReferenced and
10688 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10689 : * and (for partitioned tables) recurse to partitions.
10690 : *
10691 : * fkside: the side of the FK (or both) to create. Caller should
10692 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10693 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10694 : * addFkBothSides.
10695 : * constraintname: the base name for the constraint being added,
10696 : * copied to fkconstraint->conname if the latter is not set
10697 : * fkconstraint: the constraint being added
10698 : * rel: the root referencing relation
10699 : * pkrel: the referenced relation; might be a partition, if recursing
10700 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10701 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10702 : * top-level constraint
10703 : * numfks: the number of columns in the foreign key
10704 : * pkattnum: the attnum array of referenced attributes
10705 : * fkattnum: the attnum array of referencing attributes
10706 : * pf/pp/ffeqoperators: OID array of operators between columns
10707 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10708 : * (...) clause
10709 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10710 : * NULL/DEFAULT clause
10711 : * with_period: true if this is a temporal FK
10712 : */
10713 : static ObjectAddress
10714 4100 : addFkConstraint(addFkConstraintSides fkside,
10715 : char *constraintname, Constraint *fkconstraint,
10716 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10717 : int numfks, int16 *pkattnum,
10718 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10719 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10720 : bool is_internal, bool with_period)
10721 : {
10722 : ObjectAddress address;
10723 : Oid constrOid;
10724 : char *conname;
10725 : bool conislocal;
10726 : int16 coninhcount;
10727 : bool connoinherit;
10728 :
10729 : /*
10730 : * Verify relkind for each referenced partition. At the top level, this
10731 : * is redundant with a previous check, but we need it when recursing.
10732 : */
10733 4100 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10734 868 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10735 0 : ereport(ERROR,
10736 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10737 : errmsg("referenced relation \"%s\" is not a table",
10738 : RelationGetRelationName(pkrel))));
10739 :
10740 : /*
10741 : * Caller supplies us with a constraint name; however, it may be used in
10742 : * this partition, so come up with a different one in that case. Unless
10743 : * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10744 : * supplied name with an underscore and digit(s) appended.
10745 : */
10746 4100 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10747 : RelationGetRelid(rel),
10748 : constraintname))
10749 1176 : conname = ChooseConstraintName(constraintname,
10750 : NULL,
10751 : "",
10752 1176 : RelationGetNamespace(rel), NIL);
10753 : else
10754 2924 : conname = constraintname;
10755 :
10756 4100 : if (fkconstraint->conname == NULL)
10757 436 : fkconstraint->conname = pstrdup(conname);
10758 :
10759 4100 : if (OidIsValid(parentConstr))
10760 : {
10761 1976 : conislocal = false;
10762 1976 : coninhcount = 1;
10763 1976 : connoinherit = false;
10764 : }
10765 : else
10766 : {
10767 2124 : conislocal = true;
10768 2124 : coninhcount = 0;
10769 :
10770 : /*
10771 : * always inherit for partitioned tables, never for legacy inheritance
10772 : */
10773 2124 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10774 : }
10775 :
10776 : /*
10777 : * Record the FK constraint in pg_constraint.
10778 : */
10779 4100 : constrOid = CreateConstraintEntry(conname,
10780 4100 : RelationGetNamespace(rel),
10781 : CONSTRAINT_FOREIGN,
10782 4100 : fkconstraint->deferrable,
10783 4100 : fkconstraint->initdeferred,
10784 4100 : fkconstraint->is_enforced,
10785 4100 : fkconstraint->initially_valid,
10786 : parentConstr,
10787 : RelationGetRelid(rel),
10788 : fkattnum,
10789 : numfks,
10790 : numfks,
10791 : InvalidOid, /* not a domain constraint */
10792 : indexOid,
10793 : RelationGetRelid(pkrel),
10794 : pkattnum,
10795 : pfeqoperators,
10796 : ppeqoperators,
10797 : ffeqoperators,
10798 : numfks,
10799 4100 : fkconstraint->fk_upd_action,
10800 4100 : fkconstraint->fk_del_action,
10801 : fkdelsetcols,
10802 : numfkdelsetcols,
10803 4100 : fkconstraint->fk_matchtype,
10804 : NULL, /* no exclusion constraint */
10805 : NULL, /* no check constraint */
10806 : NULL,
10807 : conislocal, /* islocal */
10808 : coninhcount, /* inhcount */
10809 : connoinherit, /* conNoInherit */
10810 : with_period, /* conPeriod */
10811 : is_internal); /* is_internal */
10812 :
10813 4100 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10814 :
10815 : /*
10816 : * In partitioning cases, create the dependency entries for this
10817 : * constraint. (For non-partitioned cases, relevant entries were created
10818 : * by CreateConstraintEntry.)
10819 : *
10820 : * On the referenced side, we need the constraint to have an internal
10821 : * dependency on its parent constraint; this means that this constraint
10822 : * cannot be dropped on its own -- only through the parent constraint. It
10823 : * also means the containing partition cannot be dropped on its own, but
10824 : * it can be detached, at which point this dependency is removed (after
10825 : * verifying that no rows are referenced via this FK.)
10826 : *
10827 : * When processing the referencing side, we link the constraint via the
10828 : * special partitioning dependencies: the parent constraint is the primary
10829 : * dependent, and the partition on which the foreign key exists is the
10830 : * secondary dependency. That way, this constraint is dropped if either
10831 : * of these objects is.
10832 : *
10833 : * Note that this is only necessary for the subsidiary pg_constraint rows
10834 : * in partitions; the topmost row doesn't need any of this.
10835 : */
10836 4100 : if (OidIsValid(parentConstr))
10837 : {
10838 : ObjectAddress referenced;
10839 :
10840 1976 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10841 :
10842 : Assert(fkside != addFkBothSides);
10843 1976 : if (fkside == addFkReferencedSide)
10844 1170 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10845 : else
10846 : {
10847 806 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10848 806 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10849 806 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10850 : }
10851 : }
10852 :
10853 : /* make new constraint visible, in case we add more */
10854 4100 : CommandCounterIncrement();
10855 :
10856 4100 : return address;
10857 : }
10858 :
10859 : /*
10860 : * addFkRecurseReferenced
10861 : * Recursive helper for the referenced side of foreign key creation,
10862 : * which creates the action triggers and recurses
10863 : *
10864 : * If the referenced relation is a plain relation, create the necessary action
10865 : * triggers that implement the constraint. If the referenced relation is a
10866 : * partitioned table, then we create a pg_constraint row referencing the parent
10867 : * of the referencing side for it and recurse on this routine for each
10868 : * partition.
10869 : *
10870 : * fkconstraint: the constraint being added
10871 : * rel: the root referencing relation
10872 : * pkrel: the referenced relation; might be a partition, if recursing
10873 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10874 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10875 : * top-level constraint
10876 : * numfks: the number of columns in the foreign key
10877 : * pkattnum: the attnum array of referenced attributes
10878 : * fkattnum: the attnum array of referencing attributes
10879 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10880 : * NULL/DEFAULT (...) clause
10881 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10882 : * NULL/DEFAULT clause
10883 : * pf/pp/ffeqoperators: OID array of operators between columns
10884 : * old_check_ok: true if this constraint replaces an existing one that
10885 : * was already validated (thus this one doesn't need validation)
10886 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10887 : * partition, the OIDs of the parent action triggers for DELETE and
10888 : * UPDATE respectively.
10889 : * with_period: true if this is a temporal FK
10890 : */
10891 : static void
10892 3396 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10893 : Relation pkrel, Oid indexOid, Oid parentConstr,
10894 : int numfks,
10895 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10896 : Oid *ppeqoperators, Oid *ffeqoperators,
10897 : int numfkdelsetcols, int16 *fkdelsetcols,
10898 : bool old_check_ok,
10899 : Oid parentDelTrigger, Oid parentUpdTrigger,
10900 : bool with_period)
10901 : {
10902 3396 : Oid deleteTriggerOid = InvalidOid,
10903 3396 : updateTriggerOid = InvalidOid;
10904 :
10905 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10906 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10907 :
10908 : /*
10909 : * Create action triggers to enforce the constraint, or skip them if the
10910 : * constraint is NOT ENFORCED.
10911 : */
10912 3396 : if (fkconstraint->is_enforced)
10913 3348 : createForeignKeyActionTriggers(RelationGetRelid(rel),
10914 : RelationGetRelid(pkrel),
10915 : fkconstraint,
10916 : parentConstr, indexOid,
10917 : parentDelTrigger, parentUpdTrigger,
10918 : &deleteTriggerOid, &updateTriggerOid);
10919 :
10920 : /*
10921 : * If the referenced table is partitioned, recurse on ourselves to handle
10922 : * each partition. We need one pg_constraint row created for each
10923 : * partition in addition to the pg_constraint row for the parent table.
10924 : */
10925 3396 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10926 : {
10927 558 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10928 :
10929 1506 : for (int i = 0; i < pd->nparts; i++)
10930 : {
10931 : Relation partRel;
10932 : AttrMap *map;
10933 : AttrNumber *mapped_pkattnum;
10934 : Oid partIndexId;
10935 : ObjectAddress address;
10936 :
10937 : /* XXX would it be better to acquire these locks beforehand? */
10938 948 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10939 :
10940 : /*
10941 : * Map the attribute numbers in the referenced side of the FK
10942 : * definition to match the partition's column layout.
10943 : */
10944 948 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10945 : RelationGetDescr(pkrel),
10946 : false);
10947 948 : if (map)
10948 : {
10949 136 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10950 284 : for (int j = 0; j < numfks; j++)
10951 148 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10952 : }
10953 : else
10954 812 : mapped_pkattnum = pkattnum;
10955 :
10956 : /* Determine the index to use at this level */
10957 948 : partIndexId = index_get_partition(partRel, indexOid);
10958 948 : if (!OidIsValid(partIndexId))
10959 0 : elog(ERROR, "index for %u not found in partition %s",
10960 : indexOid, RelationGetRelationName(partRel));
10961 :
10962 : /* Create entry at this level ... */
10963 948 : address = addFkConstraint(addFkReferencedSide,
10964 : fkconstraint->conname, fkconstraint, rel,
10965 : partRel, partIndexId, parentConstr,
10966 : numfks, mapped_pkattnum,
10967 : fkattnum, pfeqoperators, ppeqoperators,
10968 : ffeqoperators, numfkdelsetcols,
10969 : fkdelsetcols, true, with_period);
10970 : /* ... and recurse to our children */
10971 948 : addFkRecurseReferenced(fkconstraint, rel, partRel,
10972 : partIndexId, address.objectId, numfks,
10973 : mapped_pkattnum, fkattnum,
10974 : pfeqoperators, ppeqoperators, ffeqoperators,
10975 : numfkdelsetcols, fkdelsetcols,
10976 : old_check_ok,
10977 : deleteTriggerOid, updateTriggerOid,
10978 : with_period);
10979 :
10980 : /* Done -- clean up (but keep the lock) */
10981 948 : table_close(partRel, NoLock);
10982 948 : if (map)
10983 : {
10984 136 : pfree(mapped_pkattnum);
10985 136 : free_attrmap(map);
10986 : }
10987 : }
10988 : }
10989 3396 : }
10990 :
10991 : /*
10992 : * addFkRecurseReferencing
10993 : * Recursive helper for the referencing side of foreign key creation,
10994 : * which creates the check triggers and recurses
10995 : *
10996 : * If the referencing relation is a plain relation, create the necessary check
10997 : * triggers that implement the constraint, and set up for Phase 3 constraint
10998 : * verification. If the referencing relation is a partitioned table, then
10999 : * we create a pg_constraint row for it and recurse on this routine for each
11000 : * partition.
11001 : *
11002 : * We assume that the referenced relation is locked against concurrent
11003 : * deletions. If it's a partitioned relation, every partition must be so
11004 : * locked.
11005 : *
11006 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
11007 : * of an ALTER TABLE sequence.
11008 : * fkconstraint: the constraint being added
11009 : * rel: the referencing relation; might be a partition, if recursing
11010 : * pkrel: the root referenced relation
11011 : * indexOid: the OID of the index (on pkrel) implementing this constraint
11012 : * parentConstr: the OID of the parent constraint (there is always one)
11013 : * numfks: the number of columns in the foreign key
11014 : * pkattnum: the attnum array of referenced attributes
11015 : * fkattnum: the attnum array of referencing attributes
11016 : * pf/pp/ffeqoperators: OID array of operators between columns
11017 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
11018 : * (...) clause
11019 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
11020 : * NULL/DEFAULT clause
11021 : * old_check_ok: true if this constraint replaces an existing one that
11022 : * was already validated (thus this one doesn't need validation)
11023 : * lockmode: the lockmode to acquire on partitions when recursing
11024 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
11025 : * a partition, the OIDs of the parent check triggers for INSERT and
11026 : * UPDATE respectively.
11027 : * with_period: true if this is a temporal FK
11028 : */
11029 : static void
11030 2930 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
11031 : Relation pkrel, Oid indexOid, Oid parentConstr,
11032 : int numfks, int16 *pkattnum, int16 *fkattnum,
11033 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11034 : int numfkdelsetcols, int16 *fkdelsetcols,
11035 : bool old_check_ok, LOCKMODE lockmode,
11036 : Oid parentInsTrigger, Oid parentUpdTrigger,
11037 : bool with_period)
11038 : {
11039 2930 : Oid insertTriggerOid = InvalidOid,
11040 2930 : updateTriggerOid = InvalidOid;
11041 :
11042 : Assert(OidIsValid(parentConstr));
11043 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
11044 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
11045 :
11046 2930 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11047 0 : ereport(ERROR,
11048 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11049 : errmsg("foreign key constraints are not supported on foreign tables")));
11050 :
11051 : /*
11052 : * Add check triggers if the constraint is ENFORCED, and if needed,
11053 : * schedule them to be checked in Phase 3.
11054 : *
11055 : * If the relation is partitioned, drill down to do it to its partitions.
11056 : */
11057 2930 : if (fkconstraint->is_enforced)
11058 2888 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
11059 : RelationGetRelid(pkrel),
11060 : fkconstraint,
11061 : parentConstr,
11062 : indexOid,
11063 : parentInsTrigger, parentUpdTrigger,
11064 : &insertTriggerOid, &updateTriggerOid);
11065 :
11066 2930 : if (rel->rd_rel->relkind == RELKIND_RELATION)
11067 : {
11068 : /*
11069 : * Tell Phase 3 to check that the constraint is satisfied by existing
11070 : * rows. We can skip this during table creation, when constraint is
11071 : * specified as NOT ENFORCED, or when requested explicitly by
11072 : * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11073 : * recreating a constraint following a SET DATA TYPE operation that
11074 : * did not impugn its validity.
11075 : */
11076 2446 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11077 772 : fkconstraint->is_enforced)
11078 : {
11079 : NewConstraint *newcon;
11080 : AlteredTableInfo *tab;
11081 :
11082 772 : tab = ATGetQueueEntry(wqueue, rel);
11083 :
11084 772 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11085 772 : newcon->name = get_constraint_name(parentConstr);
11086 772 : newcon->contype = CONSTR_FOREIGN;
11087 772 : newcon->refrelid = RelationGetRelid(pkrel);
11088 772 : newcon->refindid = indexOid;
11089 772 : newcon->conid = parentConstr;
11090 772 : newcon->conwithperiod = fkconstraint->fk_with_period;
11091 772 : newcon->qual = (Node *) fkconstraint;
11092 :
11093 772 : tab->constraints = lappend(tab->constraints, newcon);
11094 : }
11095 : }
11096 484 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11097 : {
11098 484 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
11099 : Relation trigrel;
11100 :
11101 : /*
11102 : * Triggers of the foreign keys will be manipulated a bunch of times
11103 : * in the loop below. To avoid repeatedly opening/closing the trigger
11104 : * catalog relation, we open it here and pass it to the subroutines
11105 : * called below.
11106 : */
11107 484 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11108 :
11109 : /*
11110 : * Recurse to take appropriate action on each partition; either we
11111 : * find an existing constraint to reparent to ours, or we create a new
11112 : * one.
11113 : */
11114 866 : for (int i = 0; i < pd->nparts; i++)
11115 : {
11116 388 : Relation partition = table_open(pd->oids[i], lockmode);
11117 : List *partFKs;
11118 : AttrMap *attmap;
11119 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11120 : bool attached;
11121 : ObjectAddress address;
11122 :
11123 388 : CheckAlterTableIsSafe(partition);
11124 :
11125 382 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
11126 : RelationGetDescr(rel),
11127 : false);
11128 986 : for (int j = 0; j < numfks; j++)
11129 604 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11130 :
11131 : /* Check whether an existing constraint can be repurposed */
11132 382 : partFKs = copyObject(RelationGetFKeyList(partition));
11133 382 : attached = false;
11134 782 : foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11135 : {
11136 30 : if (tryAttachPartitionForeignKey(wqueue,
11137 : fk,
11138 : partition,
11139 : parentConstr,
11140 : numfks,
11141 : mapped_fkattnum,
11142 : pkattnum,
11143 : pfeqoperators,
11144 : insertTriggerOid,
11145 : updateTriggerOid,
11146 : trigrel))
11147 : {
11148 12 : attached = true;
11149 12 : break;
11150 : }
11151 : }
11152 382 : if (attached)
11153 : {
11154 12 : table_close(partition, NoLock);
11155 12 : continue;
11156 : }
11157 :
11158 : /*
11159 : * No luck finding a good constraint to reuse; create our own.
11160 : */
11161 370 : address = addFkConstraint(addFkReferencingSide,
11162 : fkconstraint->conname, fkconstraint,
11163 : partition, pkrel, indexOid, parentConstr,
11164 : numfks, pkattnum,
11165 : mapped_fkattnum, pfeqoperators,
11166 : ppeqoperators, ffeqoperators,
11167 : numfkdelsetcols, fkdelsetcols, true,
11168 : with_period);
11169 :
11170 : /* call ourselves to finalize the creation and we're done */
11171 370 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11172 : indexOid,
11173 : address.objectId,
11174 : numfks,
11175 : pkattnum,
11176 : mapped_fkattnum,
11177 : pfeqoperators,
11178 : ppeqoperators,
11179 : ffeqoperators,
11180 : numfkdelsetcols,
11181 : fkdelsetcols,
11182 : old_check_ok,
11183 : lockmode,
11184 : insertTriggerOid,
11185 : updateTriggerOid,
11186 : with_period);
11187 :
11188 370 : table_close(partition, NoLock);
11189 : }
11190 :
11191 478 : table_close(trigrel, RowExclusiveLock);
11192 : }
11193 2924 : }
11194 :
11195 : /*
11196 : * CloneForeignKeyConstraints
11197 : * Clone foreign keys from a partitioned table to a newly acquired
11198 : * partition.
11199 : *
11200 : * partitionRel is a partition of parentRel, so we can be certain that it has
11201 : * the same columns with the same datatypes. The columns may be in different
11202 : * order, though.
11203 : *
11204 : * wqueue must be passed to set up phase 3 constraint checking, unless the
11205 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
11206 : * PARTITION OF).
11207 : */
11208 : static void
11209 9848 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
11210 : Relation partitionRel)
11211 : {
11212 : /* This only works for declarative partitioning */
11213 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11214 :
11215 : /*
11216 : * First, clone constraints where the parent is on the referencing side.
11217 : */
11218 9848 : CloneFkReferencing(wqueue, parentRel, partitionRel);
11219 :
11220 : /*
11221 : * Clone constraints for which the parent is on the referenced side.
11222 : */
11223 9830 : CloneFkReferenced(parentRel, partitionRel);
11224 9830 : }
11225 :
11226 : /*
11227 : * CloneFkReferenced
11228 : * Subroutine for CloneForeignKeyConstraints
11229 : *
11230 : * Find all the FKs that have the parent relation on the referenced side;
11231 : * clone those constraints to the given partition. This is to be called
11232 : * when the partition is being created or attached.
11233 : *
11234 : * This recurses to partitions, if the relation being attached is partitioned.
11235 : * Recursion is done by calling addFkRecurseReferenced.
11236 : */
11237 : static void
11238 9830 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
11239 : {
11240 : Relation pg_constraint;
11241 : AttrMap *attmap;
11242 : ListCell *cell;
11243 : SysScanDesc scan;
11244 : ScanKeyData key[2];
11245 : HeapTuple tuple;
11246 9830 : List *clone = NIL;
11247 : Relation trigrel;
11248 :
11249 : /*
11250 : * Search for any constraints where this partition's parent is in the
11251 : * referenced side. However, we must not clone any constraint whose
11252 : * parent constraint is also going to be cloned, to avoid duplicates. So
11253 : * do it in two steps: first construct the list of constraints to clone,
11254 : * then go over that list cloning those whose parents are not in the list.
11255 : * (We must not rely on the parent being seen first, since the catalog
11256 : * scan could return children first.)
11257 : */
11258 9830 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11259 9830 : ScanKeyInit(&key[0],
11260 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11261 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11262 9830 : ScanKeyInit(&key[1],
11263 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11264 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11265 : /* This is a seqscan, as we don't have a usable index ... */
11266 9830 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11267 : NULL, 2, key);
11268 10274 : while ((tuple = systable_getnext(scan)) != NULL)
11269 : {
11270 444 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11271 :
11272 444 : clone = lappend_oid(clone, constrForm->oid);
11273 : }
11274 9830 : systable_endscan(scan);
11275 9830 : table_close(pg_constraint, RowShareLock);
11276 :
11277 : /*
11278 : * Triggers of the foreign keys will be manipulated a bunch of times in
11279 : * the loop below. To avoid repeatedly opening/closing the trigger
11280 : * catalog relation, we open it here and pass it to the subroutines called
11281 : * below.
11282 : */
11283 9830 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11284 :
11285 9830 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11286 : RelationGetDescr(parentRel),
11287 : false);
11288 10274 : foreach(cell, clone)
11289 : {
11290 444 : Oid constrOid = lfirst_oid(cell);
11291 : Form_pg_constraint constrForm;
11292 : Relation fkRel;
11293 : Oid indexOid;
11294 : Oid partIndexId;
11295 : int numfks;
11296 : AttrNumber conkey[INDEX_MAX_KEYS];
11297 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11298 : AttrNumber confkey[INDEX_MAX_KEYS];
11299 : Oid conpfeqop[INDEX_MAX_KEYS];
11300 : Oid conppeqop[INDEX_MAX_KEYS];
11301 : Oid conffeqop[INDEX_MAX_KEYS];
11302 : int numfkdelsetcols;
11303 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11304 : Constraint *fkconstraint;
11305 : ObjectAddress address;
11306 444 : Oid deleteTriggerOid = InvalidOid,
11307 444 : updateTriggerOid = InvalidOid;
11308 :
11309 444 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11310 444 : if (!HeapTupleIsValid(tuple))
11311 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11312 444 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11313 :
11314 : /*
11315 : * As explained above: don't try to clone a constraint for which we're
11316 : * going to clone the parent.
11317 : */
11318 444 : if (list_member_oid(clone, constrForm->conparentid))
11319 : {
11320 222 : ReleaseSysCache(tuple);
11321 222 : continue;
11322 : }
11323 :
11324 : /* We need the same lock level that CreateTrigger will acquire */
11325 222 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11326 :
11327 222 : indexOid = constrForm->conindid;
11328 222 : DeconstructFkConstraintRow(tuple,
11329 : &numfks,
11330 : conkey,
11331 : confkey,
11332 : conpfeqop,
11333 : conppeqop,
11334 : conffeqop,
11335 : &numfkdelsetcols,
11336 : confdelsetcols);
11337 :
11338 486 : for (int i = 0; i < numfks; i++)
11339 264 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11340 :
11341 222 : fkconstraint = makeNode(Constraint);
11342 222 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11343 222 : fkconstraint->conname = NameStr(constrForm->conname);
11344 222 : fkconstraint->deferrable = constrForm->condeferrable;
11345 222 : fkconstraint->initdeferred = constrForm->condeferred;
11346 222 : fkconstraint->location = -1;
11347 222 : fkconstraint->pktable = NULL;
11348 : /* ->fk_attrs determined below */
11349 222 : fkconstraint->pk_attrs = NIL;
11350 222 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11351 222 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11352 222 : fkconstraint->fk_del_action = constrForm->confdeltype;
11353 222 : fkconstraint->fk_del_set_cols = NIL;
11354 222 : fkconstraint->old_conpfeqop = NIL;
11355 222 : fkconstraint->old_pktable_oid = InvalidOid;
11356 222 : fkconstraint->is_enforced = constrForm->conenforced;
11357 222 : fkconstraint->skip_validation = false;
11358 222 : fkconstraint->initially_valid = constrForm->convalidated;
11359 :
11360 : /* set up colnames that are used to generate the constraint name */
11361 486 : for (int i = 0; i < numfks; i++)
11362 : {
11363 : Form_pg_attribute att;
11364 :
11365 264 : att = TupleDescAttr(RelationGetDescr(fkRel),
11366 264 : conkey[i] - 1);
11367 264 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11368 264 : makeString(NameStr(att->attname)));
11369 : }
11370 :
11371 : /*
11372 : * Add the new foreign key constraint pointing to the new partition.
11373 : * Because this new partition appears in the referenced side of the
11374 : * constraint, we don't need to set up for Phase 3 check.
11375 : */
11376 222 : partIndexId = index_get_partition(partitionRel, indexOid);
11377 222 : if (!OidIsValid(partIndexId))
11378 0 : elog(ERROR, "index for %u not found in partition %s",
11379 : indexOid, RelationGetRelationName(partitionRel));
11380 :
11381 : /*
11382 : * Get the "action" triggers belonging to the constraint to pass as
11383 : * parent OIDs for similar triggers that will be created on the
11384 : * partition in addFkRecurseReferenced().
11385 : */
11386 222 : if (constrForm->conenforced)
11387 222 : GetForeignKeyActionTriggers(trigrel, constrOid,
11388 : constrForm->confrelid, constrForm->conrelid,
11389 : &deleteTriggerOid, &updateTriggerOid);
11390 :
11391 : /* Add this constraint ... */
11392 222 : address = addFkConstraint(addFkReferencedSide,
11393 : fkconstraint->conname, fkconstraint, fkRel,
11394 : partitionRel, partIndexId, constrOid,
11395 : numfks, mapped_confkey,
11396 : conkey, conpfeqop, conppeqop, conffeqop,
11397 : numfkdelsetcols, confdelsetcols, false,
11398 222 : constrForm->conperiod);
11399 : /* ... and recurse */
11400 222 : addFkRecurseReferenced(fkconstraint,
11401 : fkRel,
11402 : partitionRel,
11403 : partIndexId,
11404 : address.objectId,
11405 : numfks,
11406 : mapped_confkey,
11407 : conkey,
11408 : conpfeqop,
11409 : conppeqop,
11410 : conffeqop,
11411 : numfkdelsetcols,
11412 : confdelsetcols,
11413 : true,
11414 : deleteTriggerOid,
11415 : updateTriggerOid,
11416 222 : constrForm->conperiod);
11417 :
11418 222 : table_close(fkRel, NoLock);
11419 222 : ReleaseSysCache(tuple);
11420 : }
11421 :
11422 9830 : table_close(trigrel, RowExclusiveLock);
11423 9830 : }
11424 :
11425 : /*
11426 : * CloneFkReferencing
11427 : * Subroutine for CloneForeignKeyConstraints
11428 : *
11429 : * For each FK constraint of the parent relation in the given list, find an
11430 : * equivalent constraint in its partition relation that can be reparented;
11431 : * if one cannot be found, create a new constraint in the partition as its
11432 : * child.
11433 : *
11434 : * If wqueue is given, it is used to set up phase-3 verification for each
11435 : * cloned constraint; omit it if such verification is not needed
11436 : * (example: the partition is being created anew).
11437 : */
11438 : static void
11439 9848 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11440 : {
11441 : AttrMap *attmap;
11442 : List *partFKs;
11443 9848 : List *clone = NIL;
11444 : ListCell *cell;
11445 : Relation trigrel;
11446 :
11447 : /* obtain a list of constraints that we need to clone */
11448 11164 : foreach(cell, RelationGetFKeyList(parentRel))
11449 : {
11450 1322 : ForeignKeyCacheInfo *fk = lfirst(cell);
11451 :
11452 : /*
11453 : * Refuse to attach a table as partition that this partitioned table
11454 : * already has a foreign key to. This isn't useful schema, which is
11455 : * proven by the fact that there have been no user complaints that
11456 : * it's already impossible to achieve this in the opposite direction,
11457 : * i.e., creating a foreign key that references a partition. This
11458 : * restriction allows us to dodge some complexities around
11459 : * pg_constraint and pg_trigger row creations that would be needed
11460 : * during ATTACH/DETACH for this kind of relationship.
11461 : */
11462 1322 : if (fk->confrelid == RelationGetRelid(partRel))
11463 6 : ereport(ERROR,
11464 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11465 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11466 : RelationGetRelationName(partRel),
11467 : get_constraint_name(fk->conoid))));
11468 :
11469 1316 : clone = lappend_oid(clone, fk->conoid);
11470 : }
11471 :
11472 : /*
11473 : * Silently do nothing if there's nothing to do. In particular, this
11474 : * avoids throwing a spurious error for foreign tables.
11475 : */
11476 9842 : if (clone == NIL)
11477 9286 : return;
11478 :
11479 556 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11480 0 : ereport(ERROR,
11481 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11482 : errmsg("foreign key constraints are not supported on foreign tables")));
11483 :
11484 : /*
11485 : * Triggers of the foreign keys will be manipulated a bunch of times in
11486 : * the loop below. To avoid repeatedly opening/closing the trigger
11487 : * catalog relation, we open it here and pass it to the subroutines called
11488 : * below.
11489 : */
11490 556 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11491 :
11492 : /*
11493 : * The constraint key may differ, if the columns in the partition are
11494 : * different. This map is used to convert them.
11495 : */
11496 556 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11497 : RelationGetDescr(parentRel),
11498 : false);
11499 :
11500 556 : partFKs = copyObject(RelationGetFKeyList(partRel));
11501 :
11502 1860 : foreach(cell, clone)
11503 : {
11504 1316 : Oid parentConstrOid = lfirst_oid(cell);
11505 : Form_pg_constraint constrForm;
11506 : Relation pkrel;
11507 : HeapTuple tuple;
11508 : int numfks;
11509 : AttrNumber conkey[INDEX_MAX_KEYS];
11510 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11511 : AttrNumber confkey[INDEX_MAX_KEYS];
11512 : Oid conpfeqop[INDEX_MAX_KEYS];
11513 : Oid conppeqop[INDEX_MAX_KEYS];
11514 : Oid conffeqop[INDEX_MAX_KEYS];
11515 : int numfkdelsetcols;
11516 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11517 : Constraint *fkconstraint;
11518 : bool attached;
11519 : Oid indexOid;
11520 : ObjectAddress address;
11521 : ListCell *lc;
11522 1316 : Oid insertTriggerOid = InvalidOid,
11523 1316 : updateTriggerOid = InvalidOid;
11524 : bool with_period;
11525 :
11526 1316 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11527 1316 : if (!HeapTupleIsValid(tuple))
11528 0 : elog(ERROR, "cache lookup failed for constraint %u",
11529 : parentConstrOid);
11530 1316 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11531 :
11532 : /* Don't clone constraints whose parents are being cloned */
11533 1316 : if (list_member_oid(clone, constrForm->conparentid))
11534 : {
11535 724 : ReleaseSysCache(tuple);
11536 874 : continue;
11537 : }
11538 :
11539 : /*
11540 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11541 : * relation, that means to lock all partitions.
11542 : */
11543 592 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11544 592 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11545 250 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11546 : ShareRowExclusiveLock, NULL);
11547 :
11548 592 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11549 : conpfeqop, conppeqop, conffeqop,
11550 : &numfkdelsetcols, confdelsetcols);
11551 1418 : for (int i = 0; i < numfks; i++)
11552 826 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11553 :
11554 : /*
11555 : * Get the "check" triggers belonging to the constraint, if it is
11556 : * ENFORCED, to pass as parent OIDs for similar triggers that will be
11557 : * created on the partition in addFkRecurseReferencing(). They are
11558 : * also passed to tryAttachPartitionForeignKey() below to simply
11559 : * assign as parents to the partition's existing "check" triggers,
11560 : * that is, if the corresponding constraints is deemed attachable to
11561 : * the parent constraint.
11562 : */
11563 592 : if (constrForm->conenforced)
11564 580 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11565 : constrForm->confrelid, constrForm->conrelid,
11566 : &insertTriggerOid, &updateTriggerOid);
11567 :
11568 : /*
11569 : * Before creating a new constraint, see whether any existing FKs are
11570 : * fit for the purpose. If one is, attach the parent constraint to
11571 : * it, and don't clone anything. This way we avoid the expensive
11572 : * verification step and don't end up with a duplicate FK, and we
11573 : * don't need to recurse to partitions for this constraint.
11574 : */
11575 592 : attached = false;
11576 682 : foreach(lc, partFKs)
11577 : {
11578 246 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11579 :
11580 246 : if (tryAttachPartitionForeignKey(wqueue,
11581 : fk,
11582 : partRel,
11583 : parentConstrOid,
11584 : numfks,
11585 : mapped_conkey,
11586 : confkey,
11587 : conpfeqop,
11588 : insertTriggerOid,
11589 : updateTriggerOid,
11590 : trigrel))
11591 : {
11592 150 : attached = true;
11593 150 : table_close(pkrel, NoLock);
11594 150 : break;
11595 : }
11596 : }
11597 586 : if (attached)
11598 : {
11599 150 : ReleaseSysCache(tuple);
11600 150 : continue;
11601 : }
11602 :
11603 : /* No dice. Set up to create our own constraint */
11604 436 : fkconstraint = makeNode(Constraint);
11605 436 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11606 : /* ->conname determined below */
11607 436 : fkconstraint->deferrable = constrForm->condeferrable;
11608 436 : fkconstraint->initdeferred = constrForm->condeferred;
11609 436 : fkconstraint->location = -1;
11610 436 : fkconstraint->pktable = NULL;
11611 : /* ->fk_attrs determined below */
11612 436 : fkconstraint->pk_attrs = NIL;
11613 436 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11614 436 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11615 436 : fkconstraint->fk_del_action = constrForm->confdeltype;
11616 436 : fkconstraint->fk_del_set_cols = NIL;
11617 436 : fkconstraint->old_conpfeqop = NIL;
11618 436 : fkconstraint->old_pktable_oid = InvalidOid;
11619 436 : fkconstraint->is_enforced = constrForm->conenforced;
11620 436 : fkconstraint->skip_validation = false;
11621 436 : fkconstraint->initially_valid = constrForm->convalidated;
11622 992 : for (int i = 0; i < numfks; i++)
11623 : {
11624 : Form_pg_attribute att;
11625 :
11626 556 : att = TupleDescAttr(RelationGetDescr(partRel),
11627 556 : mapped_conkey[i] - 1);
11628 556 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11629 556 : makeString(NameStr(att->attname)));
11630 : }
11631 :
11632 436 : indexOid = constrForm->conindid;
11633 436 : with_period = constrForm->conperiod;
11634 :
11635 : /* Create the pg_constraint entry at this level */
11636 436 : address = addFkConstraint(addFkReferencingSide,
11637 436 : NameStr(constrForm->conname), fkconstraint,
11638 : partRel, pkrel, indexOid, parentConstrOid,
11639 : numfks, confkey,
11640 : mapped_conkey, conpfeqop,
11641 : conppeqop, conffeqop,
11642 : numfkdelsetcols, confdelsetcols,
11643 : false, with_period);
11644 :
11645 : /* Done with the cloned constraint's tuple */
11646 436 : ReleaseSysCache(tuple);
11647 :
11648 : /* Create the check triggers, and recurse to partitions, if any */
11649 436 : addFkRecurseReferencing(wqueue,
11650 : fkconstraint,
11651 : partRel,
11652 : pkrel,
11653 : indexOid,
11654 : address.objectId,
11655 : numfks,
11656 : confkey,
11657 : mapped_conkey,
11658 : conpfeqop,
11659 : conppeqop,
11660 : conffeqop,
11661 : numfkdelsetcols,
11662 : confdelsetcols,
11663 : false, /* no old check exists */
11664 : AccessExclusiveLock,
11665 : insertTriggerOid,
11666 : updateTriggerOid,
11667 : with_period);
11668 430 : table_close(pkrel, NoLock);
11669 : }
11670 :
11671 544 : table_close(trigrel, RowExclusiveLock);
11672 : }
11673 :
11674 : /*
11675 : * When the parent of a partition receives [the referencing side of] a foreign
11676 : * key, we must propagate that foreign key to the partition. However, the
11677 : * partition might already have an equivalent foreign key; this routine
11678 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11679 : * by the other parameters. If they are equivalent, create the link between
11680 : * the two constraints and return true.
11681 : *
11682 : * If the given FK does not match the one defined by rest of the params,
11683 : * return false.
11684 : */
11685 : static bool
11686 276 : tryAttachPartitionForeignKey(List **wqueue,
11687 : ForeignKeyCacheInfo *fk,
11688 : Relation partition,
11689 : Oid parentConstrOid,
11690 : int numfks,
11691 : AttrNumber *mapped_conkey,
11692 : AttrNumber *confkey,
11693 : Oid *conpfeqop,
11694 : Oid parentInsTrigger,
11695 : Oid parentUpdTrigger,
11696 : Relation trigrel)
11697 : {
11698 : HeapTuple parentConstrTup;
11699 : Form_pg_constraint parentConstr;
11700 : HeapTuple partcontup;
11701 : Form_pg_constraint partConstr;
11702 :
11703 276 : parentConstrTup = SearchSysCache1(CONSTROID,
11704 : ObjectIdGetDatum(parentConstrOid));
11705 276 : if (!HeapTupleIsValid(parentConstrTup))
11706 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11707 276 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11708 :
11709 : /*
11710 : * Do some quick & easy initial checks. If any of these fail, we cannot
11711 : * use this constraint.
11712 : */
11713 276 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11714 : {
11715 0 : ReleaseSysCache(parentConstrTup);
11716 0 : return false;
11717 : }
11718 768 : for (int i = 0; i < numfks; i++)
11719 : {
11720 492 : if (fk->conkey[i] != mapped_conkey[i] ||
11721 492 : fk->confkey[i] != confkey[i] ||
11722 492 : fk->conpfeqop[i] != conpfeqop[i])
11723 : {
11724 0 : ReleaseSysCache(parentConstrTup);
11725 0 : return false;
11726 : }
11727 : }
11728 :
11729 : /* Looks good so far; perform more extensive checks. */
11730 276 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11731 276 : if (!HeapTupleIsValid(partcontup))
11732 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11733 276 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11734 :
11735 : /*
11736 : * An error should be raised if the constraint enforceability is
11737 : * different. Returning false without raising an error, as we do for other
11738 : * attributes, could lead to a duplicate constraint with the same
11739 : * enforceability as the parent. While this may be acceptable, it may not
11740 : * be ideal. Therefore, it's better to raise an error and allow the user
11741 : * to correct the enforceability before proceeding.
11742 : */
11743 276 : if (partConstr->conenforced != parentConstr->conenforced)
11744 6 : ereport(ERROR,
11745 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11746 : errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11747 : NameStr(parentConstr->conname),
11748 : NameStr(partConstr->conname),
11749 : RelationGetRelationName(partition))));
11750 :
11751 270 : if (OidIsValid(partConstr->conparentid) ||
11752 240 : partConstr->condeferrable != parentConstr->condeferrable ||
11753 212 : partConstr->condeferred != parentConstr->condeferred ||
11754 212 : partConstr->confupdtype != parentConstr->confupdtype ||
11755 176 : partConstr->confdeltype != parentConstr->confdeltype ||
11756 176 : partConstr->confmatchtype != parentConstr->confmatchtype)
11757 : {
11758 108 : ReleaseSysCache(parentConstrTup);
11759 108 : ReleaseSysCache(partcontup);
11760 108 : return false;
11761 : }
11762 :
11763 162 : ReleaseSysCache(parentConstrTup);
11764 162 : ReleaseSysCache(partcontup);
11765 :
11766 : /* Looks good! Attach this constraint. */
11767 162 : AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11768 : parentConstrOid, parentInsTrigger,
11769 : parentUpdTrigger, trigrel);
11770 :
11771 162 : return true;
11772 : }
11773 :
11774 : /*
11775 : * AttachPartitionForeignKey
11776 : *
11777 : * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11778 : * attaching the constraint, removing redundant triggers and entries from
11779 : * pg_constraint, and setting the constraint's parent.
11780 : */
11781 : static void
11782 162 : AttachPartitionForeignKey(List **wqueue,
11783 : Relation partition,
11784 : Oid partConstrOid,
11785 : Oid parentConstrOid,
11786 : Oid parentInsTrigger,
11787 : Oid parentUpdTrigger,
11788 : Relation trigrel)
11789 : {
11790 : HeapTuple parentConstrTup;
11791 : Form_pg_constraint parentConstr;
11792 : HeapTuple partcontup;
11793 : Form_pg_constraint partConstr;
11794 : bool queueValidation;
11795 : Oid partConstrFrelid;
11796 : Oid partConstrRelid;
11797 : bool parentConstrIsEnforced;
11798 :
11799 : /* Fetch the parent constraint tuple */
11800 162 : parentConstrTup = SearchSysCache1(CONSTROID,
11801 : ObjectIdGetDatum(parentConstrOid));
11802 162 : if (!HeapTupleIsValid(parentConstrTup))
11803 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11804 162 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11805 162 : parentConstrIsEnforced = parentConstr->conenforced;
11806 :
11807 : /* Fetch the child constraint tuple */
11808 162 : partcontup = SearchSysCache1(CONSTROID,
11809 : ObjectIdGetDatum(partConstrOid));
11810 162 : if (!HeapTupleIsValid(partcontup))
11811 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11812 162 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11813 162 : partConstrFrelid = partConstr->confrelid;
11814 162 : partConstrRelid = partConstr->conrelid;
11815 :
11816 : /*
11817 : * If the referenced table is partitioned, then the partition we're
11818 : * attaching now has extra pg_constraint rows and action triggers that are
11819 : * no longer needed. Remove those.
11820 : */
11821 162 : if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11822 : {
11823 36 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11824 :
11825 36 : RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11826 : partConstrRelid);
11827 :
11828 36 : table_close(pg_constraint, RowShareLock);
11829 : }
11830 :
11831 : /*
11832 : * Will we need to validate this constraint? A valid parent constraint
11833 : * implies that all child constraints have been validated, so if this one
11834 : * isn't, we must trigger phase 3 validation.
11835 : */
11836 162 : queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11837 :
11838 162 : ReleaseSysCache(partcontup);
11839 162 : ReleaseSysCache(parentConstrTup);
11840 :
11841 : /*
11842 : * The action triggers in the new partition become redundant -- the parent
11843 : * table already has equivalent ones, and those will be able to reach the
11844 : * partition. Remove the ones in the partition. We identify them because
11845 : * they have our constraint OID, as well as being on the referenced rel.
11846 : */
11847 162 : DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11848 : partConstrRelid);
11849 :
11850 162 : ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11851 : RelationGetRelid(partition));
11852 :
11853 : /*
11854 : * Like the constraint, attach partition's "check" triggers to the
11855 : * corresponding parent triggers if the constraint is ENFORCED. NOT
11856 : * ENFORCED constraints do not have these triggers.
11857 : */
11858 162 : if (parentConstrIsEnforced)
11859 : {
11860 : Oid insertTriggerOid,
11861 : updateTriggerOid;
11862 :
11863 150 : GetForeignKeyCheckTriggers(trigrel,
11864 : partConstrOid, partConstrFrelid, partConstrRelid,
11865 : &insertTriggerOid, &updateTriggerOid);
11866 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11867 150 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11868 : RelationGetRelid(partition));
11869 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11870 150 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11871 : RelationGetRelid(partition));
11872 : }
11873 :
11874 : /*
11875 : * We updated this pg_constraint row above to set its parent; validating
11876 : * it will cause its convalidated flag to change, so we need CCI here. In
11877 : * addition, we need it unconditionally for the rare case where the parent
11878 : * table has *two* identical constraints; when reaching this function for
11879 : * the second one, we must have made our changes visible, otherwise we
11880 : * would try to attach both to this one.
11881 : */
11882 162 : CommandCounterIncrement();
11883 :
11884 : /* If validation is needed, put it in the queue now. */
11885 162 : if (queueValidation)
11886 : {
11887 : Relation conrel;
11888 : Oid confrelid;
11889 :
11890 18 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11891 :
11892 18 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11893 18 : if (!HeapTupleIsValid(partcontup))
11894 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11895 :
11896 18 : confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11897 :
11898 : /* Use the same lock as for AT_ValidateConstraint */
11899 18 : QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11900 : partcontup, ShareUpdateExclusiveLock);
11901 18 : ReleaseSysCache(partcontup);
11902 18 : table_close(conrel, RowExclusiveLock);
11903 : }
11904 162 : }
11905 :
11906 : /*
11907 : * RemoveInheritedConstraint
11908 : *
11909 : * Removes the constraint and its associated trigger from the specified
11910 : * relation, which inherited the given constraint.
11911 : */
11912 : static void
11913 36 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
11914 : Oid conrelid)
11915 : {
11916 : ObjectAddresses *objs;
11917 : HeapTuple consttup;
11918 : ScanKeyData key;
11919 : SysScanDesc scan;
11920 : HeapTuple trigtup;
11921 :
11922 36 : ScanKeyInit(&key,
11923 : Anum_pg_constraint_conrelid,
11924 : BTEqualStrategyNumber, F_OIDEQ,
11925 : ObjectIdGetDatum(conrelid));
11926 :
11927 36 : scan = systable_beginscan(conrel,
11928 : ConstraintRelidTypidNameIndexId,
11929 : true, NULL, 1, &key);
11930 36 : objs = new_object_addresses();
11931 324 : while ((consttup = systable_getnext(scan)) != NULL)
11932 : {
11933 288 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11934 :
11935 288 : if (conform->conparentid != conoid)
11936 210 : continue;
11937 : else
11938 : {
11939 : ObjectAddress addr;
11940 : SysScanDesc scan2;
11941 : ScanKeyData key2;
11942 : int n PG_USED_FOR_ASSERTS_ONLY;
11943 :
11944 78 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11945 78 : add_exact_object_address(&addr, objs);
11946 :
11947 : /*
11948 : * First we must delete the dependency record that binds the
11949 : * constraint records together.
11950 : */
11951 78 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11952 : conform->oid,
11953 : DEPENDENCY_INTERNAL,
11954 : ConstraintRelationId,
11955 : conoid);
11956 : Assert(n == 1); /* actually only one is expected */
11957 :
11958 : /*
11959 : * Now search for the triggers for this constraint and set them up
11960 : * for deletion too
11961 : */
11962 78 : ScanKeyInit(&key2,
11963 : Anum_pg_trigger_tgconstraint,
11964 : BTEqualStrategyNumber, F_OIDEQ,
11965 : ObjectIdGetDatum(conform->oid));
11966 78 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11967 : true, NULL, 1, &key2);
11968 234 : while ((trigtup = systable_getnext(scan2)) != NULL)
11969 : {
11970 156 : ObjectAddressSet(addr, TriggerRelationId,
11971 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11972 156 : add_exact_object_address(&addr, objs);
11973 : }
11974 78 : systable_endscan(scan2);
11975 : }
11976 : }
11977 : /* make the dependency deletions visible */
11978 36 : CommandCounterIncrement();
11979 36 : performMultipleDeletions(objs, DROP_RESTRICT,
11980 : PERFORM_DELETION_INTERNAL);
11981 36 : systable_endscan(scan);
11982 36 : }
11983 :
11984 : /*
11985 : * DropForeignKeyConstraintTriggers
11986 : *
11987 : * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11988 : * action triggers for the foreign key constraint.
11989 : *
11990 : * If valid confrelid and conrelid values are not provided, the respective
11991 : * trigger check will be skipped, and the trigger will be considered for
11992 : * removal.
11993 : */
11994 : static void
11995 234 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
11996 : Oid conrelid)
11997 : {
11998 : ScanKeyData key;
11999 : SysScanDesc scan;
12000 : HeapTuple trigtup;
12001 :
12002 234 : ScanKeyInit(&key,
12003 : Anum_pg_trigger_tgconstraint,
12004 : BTEqualStrategyNumber, F_OIDEQ,
12005 : ObjectIdGetDatum(conoid));
12006 234 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12007 : NULL, 1, &key);
12008 1014 : while ((trigtup = systable_getnext(scan)) != NULL)
12009 : {
12010 780 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12011 : ObjectAddress trigger;
12012 :
12013 : /* Invalid if trigger is not for a referential integrity constraint */
12014 780 : if (!OidIsValid(trgform->tgconstrrelid))
12015 300 : continue;
12016 780 : if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12017 300 : continue;
12018 480 : if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12019 0 : continue;
12020 :
12021 : /* We should be dropping trigger related to foreign key constraint */
12022 : Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12023 : trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12024 : trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12025 : trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12026 : trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12027 : trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12028 : trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12029 : trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12030 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12031 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12032 : trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12033 : trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12034 :
12035 : /*
12036 : * The constraint is originally set up to contain this trigger as an
12037 : * implementation object, so there's a dependency record that links
12038 : * the two; however, since the trigger is no longer needed, we remove
12039 : * the dependency link in order to be able to drop the trigger while
12040 : * keeping the constraint intact.
12041 : */
12042 480 : deleteDependencyRecordsFor(TriggerRelationId,
12043 : trgform->oid,
12044 : false);
12045 : /* make dependency deletion visible to performDeletion */
12046 480 : CommandCounterIncrement();
12047 480 : ObjectAddressSet(trigger, TriggerRelationId,
12048 : trgform->oid);
12049 480 : performDeletion(&trigger, DROP_RESTRICT, 0);
12050 : /* make trigger drop visible, in case the loop iterates */
12051 480 : CommandCounterIncrement();
12052 : }
12053 :
12054 234 : systable_endscan(scan);
12055 234 : }
12056 :
12057 : /*
12058 : * GetForeignKeyActionTriggers
12059 : * Returns delete and update "action" triggers of the given relation
12060 : * belonging to the given constraint
12061 : */
12062 : static void
12063 222 : GetForeignKeyActionTriggers(Relation trigrel,
12064 : Oid conoid, Oid confrelid, Oid conrelid,
12065 : Oid *deleteTriggerOid,
12066 : Oid *updateTriggerOid)
12067 : {
12068 : ScanKeyData key;
12069 : SysScanDesc scan;
12070 : HeapTuple trigtup;
12071 :
12072 222 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12073 222 : ScanKeyInit(&key,
12074 : Anum_pg_trigger_tgconstraint,
12075 : BTEqualStrategyNumber, F_OIDEQ,
12076 : ObjectIdGetDatum(conoid));
12077 :
12078 222 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12079 : NULL, 1, &key);
12080 452 : while ((trigtup = systable_getnext(scan)) != NULL)
12081 : {
12082 452 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12083 :
12084 452 : if (trgform->tgconstrrelid != conrelid)
12085 4 : continue;
12086 448 : if (trgform->tgrelid != confrelid)
12087 0 : continue;
12088 : /* Only ever look at "action" triggers on the PK side. */
12089 448 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12090 4 : continue;
12091 444 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
12092 : {
12093 : Assert(*deleteTriggerOid == InvalidOid);
12094 222 : *deleteTriggerOid = trgform->oid;
12095 : }
12096 222 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12097 : {
12098 : Assert(*updateTriggerOid == InvalidOid);
12099 222 : *updateTriggerOid = trgform->oid;
12100 : }
12101 : #ifndef USE_ASSERT_CHECKING
12102 : /* In an assert-enabled build, continue looking to find duplicates */
12103 444 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12104 222 : break;
12105 : #endif
12106 : }
12107 :
12108 222 : if (!OidIsValid(*deleteTriggerOid))
12109 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12110 : conoid);
12111 222 : if (!OidIsValid(*updateTriggerOid))
12112 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12113 : conoid);
12114 :
12115 222 : systable_endscan(scan);
12116 222 : }
12117 :
12118 : /*
12119 : * GetForeignKeyCheckTriggers
12120 : * Returns insert and update "check" triggers of the given relation
12121 : * belonging to the given constraint
12122 : */
12123 : static void
12124 832 : GetForeignKeyCheckTriggers(Relation trigrel,
12125 : Oid conoid, Oid confrelid, Oid conrelid,
12126 : Oid *insertTriggerOid,
12127 : Oid *updateTriggerOid)
12128 : {
12129 : ScanKeyData key;
12130 : SysScanDesc scan;
12131 : HeapTuple trigtup;
12132 :
12133 832 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
12134 832 : ScanKeyInit(&key,
12135 : Anum_pg_trigger_tgconstraint,
12136 : BTEqualStrategyNumber, F_OIDEQ,
12137 : ObjectIdGetDatum(conoid));
12138 :
12139 832 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12140 : NULL, 1, &key);
12141 2670 : while ((trigtup = systable_getnext(scan)) != NULL)
12142 : {
12143 2670 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12144 :
12145 2670 : if (trgform->tgconstrrelid != confrelid)
12146 902 : continue;
12147 1768 : if (trgform->tgrelid != conrelid)
12148 0 : continue;
12149 : /* Only ever look at "check" triggers on the FK side. */
12150 1768 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12151 104 : continue;
12152 1664 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
12153 : {
12154 : Assert(*insertTriggerOid == InvalidOid);
12155 832 : *insertTriggerOid = trgform->oid;
12156 : }
12157 832 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12158 : {
12159 : Assert(*updateTriggerOid == InvalidOid);
12160 832 : *updateTriggerOid = trgform->oid;
12161 : }
12162 : #ifndef USE_ASSERT_CHECKING
12163 : /* In an assert-enabled build, continue looking to find duplicates. */
12164 1664 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12165 832 : break;
12166 : #endif
12167 : }
12168 :
12169 832 : if (!OidIsValid(*insertTriggerOid))
12170 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12171 : conoid);
12172 832 : if (!OidIsValid(*updateTriggerOid))
12173 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12174 : conoid);
12175 :
12176 832 : systable_endscan(scan);
12177 832 : }
12178 :
12179 : /*
12180 : * ALTER TABLE ALTER CONSTRAINT
12181 : *
12182 : * Update the attributes of a constraint.
12183 : *
12184 : * Currently only works for Foreign Key and not null constraints.
12185 : *
12186 : * If the constraint is modified, returns its address; otherwise, return
12187 : * InvalidObjectAddress.
12188 : */
12189 : static ObjectAddress
12190 288 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
12191 : bool recurse, LOCKMODE lockmode)
12192 : {
12193 : Relation conrel;
12194 : Relation tgrel;
12195 : SysScanDesc scan;
12196 : ScanKeyData skey[3];
12197 : HeapTuple contuple;
12198 : Form_pg_constraint currcon;
12199 : ObjectAddress address;
12200 :
12201 : /*
12202 : * Disallow altering ONLY a partitioned table, as it would make no sense.
12203 : * This is okay for legacy inheritance.
12204 : */
12205 288 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12206 0 : ereport(ERROR,
12207 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12208 : errmsg("constraint must be altered in child tables too"),
12209 : errhint("Do not specify the ONLY keyword."));
12210 :
12211 :
12212 288 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12213 288 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12214 :
12215 : /*
12216 : * Find and check the target constraint
12217 : */
12218 288 : ScanKeyInit(&skey[0],
12219 : Anum_pg_constraint_conrelid,
12220 : BTEqualStrategyNumber, F_OIDEQ,
12221 : ObjectIdGetDatum(RelationGetRelid(rel)));
12222 288 : ScanKeyInit(&skey[1],
12223 : Anum_pg_constraint_contypid,
12224 : BTEqualStrategyNumber, F_OIDEQ,
12225 : ObjectIdGetDatum(InvalidOid));
12226 288 : ScanKeyInit(&skey[2],
12227 : Anum_pg_constraint_conname,
12228 : BTEqualStrategyNumber, F_NAMEEQ,
12229 288 : CStringGetDatum(cmdcon->conname));
12230 288 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12231 : true, NULL, 3, skey);
12232 :
12233 : /* There can be at most one matching row */
12234 288 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12235 6 : ereport(ERROR,
12236 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12237 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12238 : cmdcon->conname, RelationGetRelationName(rel))));
12239 :
12240 282 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12241 282 : if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12242 0 : ereport(ERROR,
12243 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12244 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12245 : cmdcon->conname, RelationGetRelationName(rel))));
12246 282 : if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12247 12 : ereport(ERROR,
12248 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12249 : errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12250 : cmdcon->conname, RelationGetRelationName(rel))));
12251 270 : if (cmdcon->alterInheritability &&
12252 90 : currcon->contype != CONSTRAINT_NOTNULL)
12253 24 : ereport(ERROR,
12254 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12255 : errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12256 : cmdcon->conname, RelationGetRelationName(rel)));
12257 :
12258 : /* Refuse to modify inheritability of inherited constraints */
12259 246 : if (cmdcon->alterInheritability &&
12260 66 : cmdcon->noinherit && currcon->coninhcount > 0)
12261 6 : ereport(ERROR,
12262 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12263 : errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12264 : NameStr(currcon->conname),
12265 : RelationGetRelationName(rel)));
12266 :
12267 : /*
12268 : * If it's not the topmost constraint, raise an error.
12269 : *
12270 : * Altering a non-topmost constraint leaves some triggers untouched, since
12271 : * they are not directly connected to this constraint; also, pg_dump would
12272 : * ignore the deferrability status of the individual constraint, since it
12273 : * only dumps topmost constraints. Avoid these problems by refusing this
12274 : * operation and telling the user to alter the parent constraint instead.
12275 : */
12276 240 : if (OidIsValid(currcon->conparentid))
12277 : {
12278 : HeapTuple tp;
12279 12 : Oid parent = currcon->conparentid;
12280 12 : char *ancestorname = NULL;
12281 12 : char *ancestortable = NULL;
12282 :
12283 : /* Loop to find the topmost constraint */
12284 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12285 : {
12286 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
12287 :
12288 : /* If no parent, this is the constraint we want */
12289 24 : if (!OidIsValid(contup->conparentid))
12290 : {
12291 12 : ancestorname = pstrdup(NameStr(contup->conname));
12292 12 : ancestortable = get_rel_name(contup->conrelid);
12293 12 : ReleaseSysCache(tp);
12294 12 : break;
12295 : }
12296 :
12297 12 : parent = contup->conparentid;
12298 12 : ReleaseSysCache(tp);
12299 : }
12300 :
12301 12 : ereport(ERROR,
12302 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12303 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12304 : cmdcon->conname, RelationGetRelationName(rel)),
12305 : ancestorname && ancestortable ?
12306 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12307 : cmdcon->conname, ancestorname, ancestortable) : 0,
12308 : errhint("You may alter the constraint it derives from instead.")));
12309 : }
12310 :
12311 228 : address = InvalidObjectAddress;
12312 :
12313 : /*
12314 : * Do the actual catalog work, and recurse if necessary.
12315 : */
12316 228 : if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12317 : contuple, recurse, lockmode))
12318 216 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12319 :
12320 222 : systable_endscan(scan);
12321 :
12322 222 : table_close(tgrel, RowExclusiveLock);
12323 222 : table_close(conrel, RowExclusiveLock);
12324 :
12325 222 : return address;
12326 : }
12327 :
12328 : /*
12329 : * A subroutine of ATExecAlterConstraint that calls the respective routines for
12330 : * altering constraint's enforceability, deferrability or inheritability.
12331 : */
12332 : static bool
12333 228 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
12334 : Relation conrel, Relation tgrel, Relation rel,
12335 : HeapTuple contuple, bool recurse,
12336 : LOCKMODE lockmode)
12337 : {
12338 : Form_pg_constraint currcon;
12339 228 : bool changed = false;
12340 228 : List *otherrelids = NIL;
12341 :
12342 228 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12343 :
12344 : /*
12345 : * Do the catalog work for the enforceability or deferrability change,
12346 : * recurse if necessary.
12347 : *
12348 : * Note that even if deferrability is requested to be altered along with
12349 : * enforceability, we don't need to explicitly update multiple entries in
12350 : * pg_trigger related to deferrability.
12351 : *
12352 : * Modifying enforceability involves either creating or dropping the
12353 : * trigger, during which the deferrability setting will be adjusted
12354 : * automatically.
12355 : */
12356 300 : if (cmdcon->alterEnforceability &&
12357 72 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12358 : currcon->conrelid, currcon->confrelid,
12359 : contuple, lockmode, InvalidOid,
12360 : InvalidOid, InvalidOid, InvalidOid))
12361 66 : changed = true;
12362 :
12363 258 : else if (cmdcon->alterDeferrability &&
12364 96 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12365 : contuple, recurse, &otherrelids,
12366 : lockmode))
12367 : {
12368 : /*
12369 : * AlterConstrUpdateConstraintEntry already invalidated relcache for
12370 : * the relations having the constraint itself; here we also invalidate
12371 : * for relations that have any triggers that are part of the
12372 : * constraint.
12373 : */
12374 306 : foreach_oid(relid, otherrelids)
12375 114 : CacheInvalidateRelcacheByRelid(relid);
12376 :
12377 96 : changed = true;
12378 : }
12379 :
12380 : /*
12381 : * Do the catalog work for the inheritability change.
12382 : */
12383 282 : if (cmdcon->alterInheritability &&
12384 60 : ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12385 : lockmode))
12386 54 : changed = true;
12387 :
12388 222 : return changed;
12389 : }
12390 :
12391 : /*
12392 : * Returns true if the constraint's enforceability is altered.
12393 : *
12394 : * Depending on whether the constraint is being set to ENFORCED or NOT
12395 : * ENFORCED, it creates or drops the trigger accordingly.
12396 : *
12397 : * Note that we must recurse even when trying to change a constraint to not
12398 : * enforced if it is already not enforced, in case descendant constraints
12399 : * might be enforced and need to be changed to not enforced. Conversely, we
12400 : * should do nothing if a constraint is being set to enforced and is already
12401 : * enforced, as descendant constraints cannot be different in that case.
12402 : */
12403 : static bool
12404 168 : ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
12405 : Relation conrel, Relation tgrel,
12406 : Oid fkrelid, Oid pkrelid,
12407 : HeapTuple contuple, LOCKMODE lockmode,
12408 : Oid ReferencedParentDelTrigger,
12409 : Oid ReferencedParentUpdTrigger,
12410 : Oid ReferencingParentInsTrigger,
12411 : Oid ReferencingParentUpdTrigger)
12412 : {
12413 : Form_pg_constraint currcon;
12414 : Oid conoid;
12415 : Relation rel;
12416 168 : bool changed = false;
12417 :
12418 : /* Since this function recurses, it could be driven to stack overflow */
12419 168 : check_stack_depth();
12420 :
12421 : Assert(cmdcon->alterEnforceability);
12422 :
12423 168 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12424 168 : conoid = currcon->oid;
12425 :
12426 : /* Should be foreign key constraint */
12427 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12428 :
12429 168 : rel = table_open(currcon->conrelid, lockmode);
12430 :
12431 168 : if (currcon->conenforced != cmdcon->is_enforced)
12432 : {
12433 162 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12434 162 : changed = true;
12435 : }
12436 :
12437 : /* Drop triggers */
12438 168 : if (!cmdcon->is_enforced)
12439 : {
12440 : /*
12441 : * When setting a constraint to NOT ENFORCED, the constraint triggers
12442 : * need to be dropped. Therefore, we must process the child relations
12443 : * first, followed by the parent, to account for dependencies.
12444 : */
12445 126 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12446 54 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12447 18 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12448 : fkrelid, pkrelid, contuple,
12449 : lockmode, InvalidOid, InvalidOid,
12450 : InvalidOid, InvalidOid);
12451 :
12452 : /* Drop all the triggers */
12453 72 : DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
12454 : }
12455 96 : else if (changed) /* Create triggers */
12456 : {
12457 96 : Oid ReferencedDelTriggerOid = InvalidOid,
12458 96 : ReferencedUpdTriggerOid = InvalidOid,
12459 96 : ReferencingInsTriggerOid = InvalidOid,
12460 96 : ReferencingUpdTriggerOid = InvalidOid;
12461 :
12462 : /* Prepare the minimal information required for trigger creation. */
12463 96 : Constraint *fkconstraint = makeNode(Constraint);
12464 :
12465 96 : fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12466 96 : fkconstraint->fk_matchtype = currcon->confmatchtype;
12467 96 : fkconstraint->fk_upd_action = currcon->confupdtype;
12468 96 : fkconstraint->fk_del_action = currcon->confdeltype;
12469 :
12470 : /* Create referenced triggers */
12471 96 : if (currcon->conrelid == fkrelid)
12472 54 : createForeignKeyActionTriggers(currcon->conrelid,
12473 : currcon->confrelid,
12474 : fkconstraint,
12475 : conoid,
12476 : currcon->conindid,
12477 : ReferencedParentDelTrigger,
12478 : ReferencedParentUpdTrigger,
12479 : &ReferencedDelTriggerOid,
12480 : &ReferencedUpdTriggerOid);
12481 :
12482 : /* Create referencing triggers */
12483 96 : if (currcon->confrelid == pkrelid)
12484 84 : createForeignKeyCheckTriggers(currcon->conrelid,
12485 : pkrelid,
12486 : fkconstraint,
12487 : conoid,
12488 : currcon->conindid,
12489 : ReferencingParentInsTrigger,
12490 : ReferencingParentUpdTrigger,
12491 : &ReferencingInsTriggerOid,
12492 : &ReferencingUpdTriggerOid);
12493 :
12494 : /*
12495 : * Tell Phase 3 to check that the constraint is satisfied by existing
12496 : * rows. Only applies to leaf partitions, and (for constraints that
12497 : * reference a partitioned table) only if this is not one of the
12498 : * pg_constraint rows that exist solely to support action triggers.
12499 : */
12500 96 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
12501 78 : currcon->confrelid == pkrelid)
12502 : {
12503 : AlteredTableInfo *tab;
12504 : NewConstraint *newcon;
12505 :
12506 66 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12507 66 : newcon->name = fkconstraint->conname;
12508 66 : newcon->contype = CONSTR_FOREIGN;
12509 66 : newcon->refrelid = currcon->confrelid;
12510 66 : newcon->refindid = currcon->conindid;
12511 66 : newcon->conid = currcon->oid;
12512 66 : newcon->qual = (Node *) fkconstraint;
12513 :
12514 : /* Find or create work queue entry for this table */
12515 66 : tab = ATGetQueueEntry(wqueue, rel);
12516 66 : tab->constraints = lappend(tab->constraints, newcon);
12517 : }
12518 :
12519 : /*
12520 : * If the table at either end of the constraint is partitioned, we
12521 : * need to recurse and create triggers for each constraint that is a
12522 : * child of this one.
12523 : */
12524 174 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12525 78 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12526 24 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12527 : fkrelid, pkrelid, contuple,
12528 : lockmode, ReferencedDelTriggerOid,
12529 : ReferencedUpdTriggerOid,
12530 : ReferencingInsTriggerOid,
12531 : ReferencingUpdTriggerOid);
12532 : }
12533 :
12534 168 : table_close(rel, NoLock);
12535 :
12536 168 : return changed;
12537 : }
12538 :
12539 : /*
12540 : * Returns true if the constraint's deferrability is altered.
12541 : *
12542 : * *otherrelids is appended OIDs of relations containing affected triggers.
12543 : *
12544 : * Note that we must recurse even when the values are correct, in case
12545 : * indirect descendants have had their constraints altered locally.
12546 : * (This could be avoided if we forbade altering constraints in partitions
12547 : * but existing releases don't do that.)
12548 : */
12549 : static bool
12550 162 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
12551 : Relation conrel, Relation tgrel, Relation rel,
12552 : HeapTuple contuple, bool recurse,
12553 : List **otherrelids, LOCKMODE lockmode)
12554 : {
12555 : Form_pg_constraint currcon;
12556 : Oid refrelid;
12557 162 : bool changed = false;
12558 :
12559 : /* since this function recurses, it could be driven to stack overflow */
12560 162 : check_stack_depth();
12561 :
12562 : Assert(cmdcon->alterDeferrability);
12563 :
12564 162 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12565 162 : refrelid = currcon->confrelid;
12566 :
12567 : /* Should be foreign key constraint */
12568 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12569 :
12570 : /*
12571 : * If called to modify a constraint that's already in the desired state,
12572 : * silently do nothing.
12573 : */
12574 162 : if (currcon->condeferrable != cmdcon->deferrable ||
12575 6 : currcon->condeferred != cmdcon->initdeferred)
12576 : {
12577 162 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12578 162 : changed = true;
12579 :
12580 : /*
12581 : * Now we need to update the multiple entries in pg_trigger that
12582 : * implement the constraint.
12583 : */
12584 162 : AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12585 162 : cmdcon->deferrable,
12586 162 : cmdcon->initdeferred, otherrelids);
12587 : }
12588 :
12589 : /*
12590 : * If the table at either end of the constraint is partitioned, we need to
12591 : * handle every constraint that is a child of this one.
12592 : */
12593 162 : if (recurse && changed &&
12594 300 : (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12595 138 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12596 42 : AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12597 : contuple, recurse, otherrelids,
12598 : lockmode);
12599 :
12600 162 : return changed;
12601 : }
12602 :
12603 : /*
12604 : * Returns true if the constraint's inheritability is altered.
12605 : */
12606 : static bool
12607 60 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
12608 : Relation conrel, Relation rel,
12609 : HeapTuple contuple, LOCKMODE lockmode)
12610 : {
12611 : Form_pg_constraint currcon;
12612 : AttrNumber colNum;
12613 : char *colName;
12614 : List *children;
12615 :
12616 : Assert(cmdcon->alterInheritability);
12617 :
12618 60 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12619 :
12620 : /* The current implementation only works for NOT NULL constraints */
12621 : Assert(currcon->contype == CONSTRAINT_NOTNULL);
12622 :
12623 : /*
12624 : * If called to modify a constraint that's already in the desired state,
12625 : * silently do nothing.
12626 : */
12627 60 : if (cmdcon->noinherit == currcon->connoinherit)
12628 0 : return false;
12629 :
12630 60 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12631 60 : CommandCounterIncrement();
12632 :
12633 : /* Fetch the column number and name */
12634 60 : colNum = extractNotNullColumn(contuple);
12635 60 : colName = get_attname(currcon->conrelid, colNum, false);
12636 :
12637 : /*
12638 : * Propagate the change to children. For this subcommand type we don't
12639 : * recursively affect children, just the immediate level.
12640 : */
12641 60 : children = find_inheritance_children(RelationGetRelid(rel),
12642 : lockmode);
12643 192 : foreach_oid(childoid, children)
12644 : {
12645 : ObjectAddress addr;
12646 :
12647 84 : if (cmdcon->noinherit)
12648 : {
12649 : HeapTuple childtup;
12650 : Form_pg_constraint childcon;
12651 :
12652 30 : childtup = findNotNullConstraint(childoid, colName);
12653 30 : if (!childtup)
12654 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12655 : colName, childoid);
12656 30 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12657 : Assert(childcon->coninhcount > 0);
12658 30 : childcon->coninhcount--;
12659 30 : childcon->conislocal = true;
12660 30 : CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12661 30 : heap_freetuple(childtup);
12662 : }
12663 : else
12664 : {
12665 54 : Relation childrel = table_open(childoid, NoLock);
12666 :
12667 54 : addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12668 : colName, true, true, lockmode);
12669 48 : if (OidIsValid(addr.objectId))
12670 48 : CommandCounterIncrement();
12671 48 : table_close(childrel, NoLock);
12672 : }
12673 : }
12674 :
12675 54 : return true;
12676 : }
12677 :
12678 : /*
12679 : * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12680 : * trigger's deferrability.
12681 : *
12682 : * The arguments to this function have the same meaning as the arguments to
12683 : * ATExecAlterConstrDeferrability.
12684 : */
12685 : static void
12686 162 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12687 : bool deferrable, bool initdeferred,
12688 : List **otherrelids)
12689 : {
12690 : HeapTuple tgtuple;
12691 : ScanKeyData tgkey;
12692 : SysScanDesc tgscan;
12693 :
12694 162 : ScanKeyInit(&tgkey,
12695 : Anum_pg_trigger_tgconstraint,
12696 : BTEqualStrategyNumber, F_OIDEQ,
12697 : ObjectIdGetDatum(conoid));
12698 162 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12699 : NULL, 1, &tgkey);
12700 630 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12701 : {
12702 468 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12703 : Form_pg_trigger copy_tg;
12704 : HeapTuple tgCopyTuple;
12705 :
12706 : /*
12707 : * Remember OIDs of other relation(s) involved in FK constraint.
12708 : * (Note: it's likely that we could skip forcing a relcache inval for
12709 : * other rels that don't have a trigger whose properties change, but
12710 : * let's be conservative.)
12711 : */
12712 468 : if (tgform->tgrelid != RelationGetRelid(rel))
12713 228 : *otherrelids = list_append_unique_oid(*otherrelids,
12714 : tgform->tgrelid);
12715 :
12716 : /*
12717 : * Update enable status and deferrability of RI_FKey_noaction_del,
12718 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12719 : * triggers, but not others; see createForeignKeyActionTriggers and
12720 : * CreateFKCheckTrigger.
12721 : */
12722 468 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12723 372 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12724 258 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12725 138 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12726 18 : continue;
12727 :
12728 450 : tgCopyTuple = heap_copytuple(tgtuple);
12729 450 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12730 :
12731 450 : copy_tg->tgdeferrable = deferrable;
12732 450 : copy_tg->tginitdeferred = initdeferred;
12733 450 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12734 :
12735 450 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12736 :
12737 450 : heap_freetuple(tgCopyTuple);
12738 : }
12739 :
12740 162 : systable_endscan(tgscan);
12741 162 : }
12742 :
12743 : /*
12744 : * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12745 : * the specified constraint.
12746 : *
12747 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12748 : * list of child relations and recursing; instead it uses the conparentid
12749 : * relationships. This may need to be reconsidered.
12750 : *
12751 : * The arguments to this function have the same meaning as the arguments to
12752 : * ATExecAlterConstrEnforceability.
12753 : */
12754 : static void
12755 42 : AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12756 : Relation conrel, Relation tgrel,
12757 : Oid fkrelid, Oid pkrelid,
12758 : HeapTuple contuple, LOCKMODE lockmode,
12759 : Oid ReferencedParentDelTrigger,
12760 : Oid ReferencedParentUpdTrigger,
12761 : Oid ReferencingParentInsTrigger,
12762 : Oid ReferencingParentUpdTrigger)
12763 : {
12764 : Form_pg_constraint currcon;
12765 : Oid conoid;
12766 : ScanKeyData pkey;
12767 : SysScanDesc pscan;
12768 : HeapTuple childtup;
12769 :
12770 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12771 42 : conoid = currcon->oid;
12772 :
12773 42 : ScanKeyInit(&pkey,
12774 : Anum_pg_constraint_conparentid,
12775 : BTEqualStrategyNumber, F_OIDEQ,
12776 : ObjectIdGetDatum(conoid));
12777 :
12778 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12779 : true, NULL, 1, &pkey);
12780 :
12781 138 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12782 96 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12783 : pkrelid, childtup, lockmode,
12784 : ReferencedParentDelTrigger,
12785 : ReferencedParentUpdTrigger,
12786 : ReferencingParentInsTrigger,
12787 : ReferencingParentUpdTrigger);
12788 :
12789 42 : systable_endscan(pscan);
12790 42 : }
12791 :
12792 : /*
12793 : * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12794 : * the specified constraint.
12795 : *
12796 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12797 : * list of child relations and recursing; instead it uses the conparentid
12798 : * relationships. This may need to be reconsidered.
12799 : *
12800 : * The arguments to this function have the same meaning as the arguments to
12801 : * ATExecAlterConstrDeferrability.
12802 : */
12803 : static void
12804 42 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12805 : Relation conrel, Relation tgrel, Relation rel,
12806 : HeapTuple contuple, bool recurse,
12807 : List **otherrelids, LOCKMODE lockmode)
12808 : {
12809 : Form_pg_constraint currcon;
12810 : Oid conoid;
12811 : ScanKeyData pkey;
12812 : SysScanDesc pscan;
12813 : HeapTuple childtup;
12814 :
12815 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12816 42 : conoid = currcon->oid;
12817 :
12818 42 : ScanKeyInit(&pkey,
12819 : Anum_pg_constraint_conparentid,
12820 : BTEqualStrategyNumber, F_OIDEQ,
12821 : ObjectIdGetDatum(conoid));
12822 :
12823 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12824 : true, NULL, 1, &pkey);
12825 :
12826 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12827 : {
12828 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12829 : Relation childrel;
12830 :
12831 66 : childrel = table_open(childcon->conrelid, lockmode);
12832 :
12833 66 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12834 : childtup, recurse, otherrelids, lockmode);
12835 66 : table_close(childrel, NoLock);
12836 : }
12837 :
12838 42 : systable_endscan(pscan);
12839 42 : }
12840 :
12841 : /*
12842 : * Update the constraint entry for the given ATAlterConstraint command, and
12843 : * invoke the appropriate hooks.
12844 : */
12845 : static void
12846 384 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
12847 : HeapTuple contuple)
12848 : {
12849 : HeapTuple copyTuple;
12850 : Form_pg_constraint copy_con;
12851 :
12852 : Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12853 : cmdcon->alterInheritability);
12854 :
12855 384 : copyTuple = heap_copytuple(contuple);
12856 384 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12857 :
12858 384 : if (cmdcon->alterEnforceability)
12859 : {
12860 162 : copy_con->conenforced = cmdcon->is_enforced;
12861 :
12862 : /*
12863 : * NB: The convalidated status is irrelevant when the constraint is
12864 : * set to NOT ENFORCED, but for consistency, it should still be set
12865 : * appropriately. Similarly, if the constraint is later changed to
12866 : * ENFORCED, validation will be performed during phase 3, so it makes
12867 : * sense to mark it as valid in that case.
12868 : */
12869 162 : copy_con->convalidated = cmdcon->is_enforced;
12870 : }
12871 384 : if (cmdcon->alterDeferrability)
12872 : {
12873 168 : copy_con->condeferrable = cmdcon->deferrable;
12874 168 : copy_con->condeferred = cmdcon->initdeferred;
12875 : }
12876 384 : if (cmdcon->alterInheritability)
12877 60 : copy_con->connoinherit = cmdcon->noinherit;
12878 :
12879 384 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12880 384 : InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12881 :
12882 : /* Make new constraint flags visible to others */
12883 384 : CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12884 :
12885 384 : heap_freetuple(copyTuple);
12886 384 : }
12887 :
12888 : /*
12889 : * ALTER TABLE VALIDATE CONSTRAINT
12890 : *
12891 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12892 : * there's no good way to skip recursing when handling foreign keys: there is
12893 : * no need to lock children in that case, yet we wouldn't be able to avoid
12894 : * doing so at that level.
12895 : *
12896 : * Return value is the address of the validated constraint. If the constraint
12897 : * was already validated, InvalidObjectAddress is returned.
12898 : */
12899 : static ObjectAddress
12900 584 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12901 : bool recurse, bool recursing, LOCKMODE lockmode)
12902 : {
12903 : Relation conrel;
12904 : SysScanDesc scan;
12905 : ScanKeyData skey[3];
12906 : HeapTuple tuple;
12907 : Form_pg_constraint con;
12908 : ObjectAddress address;
12909 :
12910 584 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12911 :
12912 : /*
12913 : * Find and check the target constraint
12914 : */
12915 584 : ScanKeyInit(&skey[0],
12916 : Anum_pg_constraint_conrelid,
12917 : BTEqualStrategyNumber, F_OIDEQ,
12918 : ObjectIdGetDatum(RelationGetRelid(rel)));
12919 584 : ScanKeyInit(&skey[1],
12920 : Anum_pg_constraint_contypid,
12921 : BTEqualStrategyNumber, F_OIDEQ,
12922 : ObjectIdGetDatum(InvalidOid));
12923 584 : ScanKeyInit(&skey[2],
12924 : Anum_pg_constraint_conname,
12925 : BTEqualStrategyNumber, F_NAMEEQ,
12926 : CStringGetDatum(constrName));
12927 584 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12928 : true, NULL, 3, skey);
12929 :
12930 : /* There can be at most one matching row */
12931 584 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12932 0 : ereport(ERROR,
12933 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12934 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12935 : constrName, RelationGetRelationName(rel))));
12936 :
12937 584 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12938 584 : if (con->contype != CONSTRAINT_FOREIGN &&
12939 256 : con->contype != CONSTRAINT_CHECK &&
12940 112 : con->contype != CONSTRAINT_NOTNULL)
12941 0 : ereport(ERROR,
12942 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12943 : errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12944 : constrName, RelationGetRelationName(rel)),
12945 : errdetail("This operation is not supported for this type of constraint."));
12946 :
12947 584 : if (!con->conenforced)
12948 6 : ereport(ERROR,
12949 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12950 : errmsg("cannot validate NOT ENFORCED constraint")));
12951 :
12952 578 : if (!con->convalidated)
12953 : {
12954 560 : if (con->contype == CONSTRAINT_FOREIGN)
12955 : {
12956 322 : QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12957 : tuple, lockmode);
12958 : }
12959 238 : else if (con->contype == CONSTRAINT_CHECK)
12960 : {
12961 126 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12962 : tuple, recurse, recursing, lockmode);
12963 : }
12964 112 : else if (con->contype == CONSTRAINT_NOTNULL)
12965 : {
12966 112 : QueueNNConstraintValidation(wqueue, conrel, rel,
12967 : tuple, recurse, recursing, lockmode);
12968 : }
12969 :
12970 560 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12971 : }
12972 : else
12973 18 : address = InvalidObjectAddress; /* already validated */
12974 :
12975 578 : systable_endscan(scan);
12976 :
12977 578 : table_close(conrel, RowExclusiveLock);
12978 :
12979 578 : return address;
12980 : }
12981 :
12982 : /*
12983 : * QueueFKConstraintValidation
12984 : *
12985 : * Add an entry to the wqueue to validate the given foreign key constraint in
12986 : * Phase 3 and update the convalidated field in the pg_constraint catalog
12987 : * for the specified relation and all its children.
12988 : */
12989 : static void
12990 394 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
12991 : Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
12992 : {
12993 : Form_pg_constraint con;
12994 : AlteredTableInfo *tab;
12995 : HeapTuple copyTuple;
12996 : Form_pg_constraint copy_con;
12997 :
12998 394 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12999 : Assert(con->contype == CONSTRAINT_FOREIGN);
13000 : Assert(!con->convalidated);
13001 :
13002 : /*
13003 : * Add the validation to phase 3's queue; not needed for partitioned
13004 : * tables themselves, only for their partitions.
13005 : *
13006 : * When the referenced table (pkrelid) is partitioned, the referencing
13007 : * table (fkrel) has one pg_constraint row pointing to each partition
13008 : * thereof. These rows are there only to support action triggers and no
13009 : * table scan is needed, therefore skip this for them as well.
13010 : */
13011 394 : if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13012 346 : con->confrelid == pkrelid)
13013 : {
13014 : NewConstraint *newcon;
13015 : Constraint *fkconstraint;
13016 :
13017 : /* Queue validation for phase 3 */
13018 334 : fkconstraint = makeNode(Constraint);
13019 : /* for now this is all we need */
13020 334 : fkconstraint->conname = pstrdup(NameStr(con->conname));
13021 :
13022 334 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13023 334 : newcon->name = fkconstraint->conname;
13024 334 : newcon->contype = CONSTR_FOREIGN;
13025 334 : newcon->refrelid = con->confrelid;
13026 334 : newcon->refindid = con->conindid;
13027 334 : newcon->conid = con->oid;
13028 334 : newcon->qual = (Node *) fkconstraint;
13029 :
13030 : /* Find or create work queue entry for this table */
13031 334 : tab = ATGetQueueEntry(wqueue, fkrel);
13032 334 : tab->constraints = lappend(tab->constraints, newcon);
13033 : }
13034 :
13035 : /*
13036 : * If the table at either end of the constraint is partitioned, we need to
13037 : * recurse and handle every unvalidate constraint that is a child of this
13038 : * constraint.
13039 : */
13040 740 : if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13041 346 : get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13042 : {
13043 : ScanKeyData pkey;
13044 : SysScanDesc pscan;
13045 : HeapTuple childtup;
13046 :
13047 72 : ScanKeyInit(&pkey,
13048 : Anum_pg_constraint_conparentid,
13049 : BTEqualStrategyNumber, F_OIDEQ,
13050 : ObjectIdGetDatum(con->oid));
13051 :
13052 72 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13053 : true, NULL, 1, &pkey);
13054 :
13055 144 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13056 : {
13057 : Form_pg_constraint childcon;
13058 : Relation childrel;
13059 :
13060 72 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13061 :
13062 : /*
13063 : * If the child constraint has already been validated, no further
13064 : * action is required for it or its descendants, as they are all
13065 : * valid.
13066 : */
13067 72 : if (childcon->convalidated)
13068 18 : continue;
13069 :
13070 54 : childrel = table_open(childcon->conrelid, lockmode);
13071 :
13072 : /*
13073 : * NB: Note that pkrelid should be passed as-is during recursion,
13074 : * as it is required to identify the root referenced table.
13075 : */
13076 54 : QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13077 : childtup, lockmode);
13078 54 : table_close(childrel, NoLock);
13079 : }
13080 :
13081 72 : systable_endscan(pscan);
13082 : }
13083 :
13084 : /*
13085 : * Now mark the pg_constraint row as validated (even if we didn't check,
13086 : * notably the ones for partitions on the referenced side).
13087 : *
13088 : * We rely on transaction abort to roll back this change if phase 3
13089 : * ultimately finds violating rows. This is a bit ugly.
13090 : */
13091 394 : copyTuple = heap_copytuple(contuple);
13092 394 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13093 394 : copy_con->convalidated = true;
13094 394 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13095 :
13096 394 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13097 :
13098 394 : heap_freetuple(copyTuple);
13099 394 : }
13100 :
13101 : /*
13102 : * QueueCheckConstraintValidation
13103 : *
13104 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
13105 : * and update the convalidated field in the pg_constraint catalog for the
13106 : * specified relation and all its inheriting children.
13107 : */
13108 : static void
13109 126 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13110 : char *constrName, HeapTuple contuple,
13111 : bool recurse, bool recursing, LOCKMODE lockmode)
13112 : {
13113 : Form_pg_constraint con;
13114 : AlteredTableInfo *tab;
13115 : HeapTuple copyTuple;
13116 : Form_pg_constraint copy_con;
13117 :
13118 126 : List *children = NIL;
13119 : ListCell *child;
13120 : NewConstraint *newcon;
13121 : Datum val;
13122 : char *conbin;
13123 :
13124 126 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13125 : Assert(con->contype == CONSTRAINT_CHECK);
13126 :
13127 : /*
13128 : * If we're recursing, the parent has already done this, so skip it. Also,
13129 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13130 : * for it in the children.
13131 : */
13132 126 : if (!recursing && !con->connoinherit)
13133 72 : children = find_all_inheritors(RelationGetRelid(rel),
13134 : lockmode, NULL);
13135 :
13136 : /*
13137 : * For CHECK constraints, we must ensure that we only mark the constraint
13138 : * as validated on the parent if it's already validated on the children.
13139 : *
13140 : * We recurse before validating on the parent, to reduce risk of
13141 : * deadlocks.
13142 : */
13143 246 : foreach(child, children)
13144 : {
13145 120 : Oid childoid = lfirst_oid(child);
13146 : Relation childrel;
13147 :
13148 120 : if (childoid == RelationGetRelid(rel))
13149 72 : continue;
13150 :
13151 : /*
13152 : * If we are told not to recurse, there had better not be any child
13153 : * tables, because we can't mark the constraint on the parent valid
13154 : * unless it is valid for all child tables.
13155 : */
13156 48 : if (!recurse)
13157 0 : ereport(ERROR,
13158 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13159 : errmsg("constraint must be validated on child tables too")));
13160 :
13161 : /* find_all_inheritors already got lock */
13162 48 : childrel = table_open(childoid, NoLock);
13163 :
13164 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
13165 : true, lockmode);
13166 48 : table_close(childrel, NoLock);
13167 : }
13168 :
13169 : /* Queue validation for phase 3 */
13170 126 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13171 126 : newcon->name = constrName;
13172 126 : newcon->contype = CONSTR_CHECK;
13173 126 : newcon->refrelid = InvalidOid;
13174 126 : newcon->refindid = InvalidOid;
13175 126 : newcon->conid = con->oid;
13176 :
13177 126 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13178 : Anum_pg_constraint_conbin);
13179 126 : conbin = TextDatumGetCString(val);
13180 126 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13181 :
13182 : /* Find or create work queue entry for this table */
13183 126 : tab = ATGetQueueEntry(wqueue, rel);
13184 126 : tab->constraints = lappend(tab->constraints, newcon);
13185 :
13186 : /*
13187 : * Invalidate relcache so that others see the new validated constraint.
13188 : */
13189 126 : CacheInvalidateRelcache(rel);
13190 :
13191 : /*
13192 : * Now update the catalog, while we have the door open.
13193 : */
13194 126 : copyTuple = heap_copytuple(contuple);
13195 126 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13196 126 : copy_con->convalidated = true;
13197 126 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13198 :
13199 126 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13200 :
13201 126 : heap_freetuple(copyTuple);
13202 126 : }
13203 :
13204 : /*
13205 : * QueueNNConstraintValidation
13206 : *
13207 : * Add an entry to the wqueue to validate the given not-null constraint in
13208 : * Phase 3 and update the convalidated field in the pg_constraint catalog for
13209 : * the specified relation and all its inheriting children.
13210 : */
13211 : static void
13212 112 : QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13213 : HeapTuple contuple, bool recurse, bool recursing,
13214 : LOCKMODE lockmode)
13215 : {
13216 : Form_pg_constraint con;
13217 : AlteredTableInfo *tab;
13218 : HeapTuple copyTuple;
13219 : Form_pg_constraint copy_con;
13220 112 : List *children = NIL;
13221 : AttrNumber attnum;
13222 : char *colname;
13223 :
13224 112 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13225 : Assert(con->contype == CONSTRAINT_NOTNULL);
13226 :
13227 112 : attnum = extractNotNullColumn(contuple);
13228 :
13229 : /*
13230 : * If we're recursing, we've already done this for parent, so skip it.
13231 : * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13232 : * look for it in the children.
13233 : *
13234 : * We recurse before validating on the parent, to reduce risk of
13235 : * deadlocks.
13236 : */
13237 112 : if (!recursing && !con->connoinherit)
13238 76 : children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13239 :
13240 112 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13241 378 : foreach_oid(childoid, children)
13242 : {
13243 : Relation childrel;
13244 : HeapTuple contup;
13245 : Form_pg_constraint childcon;
13246 : char *conname;
13247 :
13248 154 : if (childoid == RelationGetRelid(rel))
13249 76 : continue;
13250 :
13251 : /*
13252 : * If we are told not to recurse, there had better not be any child
13253 : * tables, because we can't mark the constraint on the parent valid
13254 : * unless it is valid for all child tables.
13255 : */
13256 78 : if (!recurse)
13257 0 : ereport(ERROR,
13258 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13259 : errmsg("constraint must be validated on child tables too"));
13260 :
13261 : /*
13262 : * The column on child might have a different attnum, so search by
13263 : * column name.
13264 : */
13265 78 : contup = findNotNullConstraint(childoid, colname);
13266 78 : if (!contup)
13267 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13268 : colname, get_rel_name(childoid));
13269 78 : childcon = (Form_pg_constraint) GETSTRUCT(contup);
13270 78 : if (childcon->convalidated)
13271 42 : continue;
13272 :
13273 : /* find_all_inheritors already got lock */
13274 36 : childrel = table_open(childoid, NoLock);
13275 36 : conname = pstrdup(NameStr(childcon->conname));
13276 :
13277 : /* XXX improve ATExecValidateConstraint API to avoid double search */
13278 36 : ATExecValidateConstraint(wqueue, childrel, conname,
13279 : false, true, lockmode);
13280 36 : table_close(childrel, NoLock);
13281 : }
13282 :
13283 : /* Set attnotnull appropriately without queueing another validation */
13284 112 : set_attnotnull(NULL, rel, attnum, true, false);
13285 :
13286 112 : tab = ATGetQueueEntry(wqueue, rel);
13287 112 : tab->verify_new_notnull = true;
13288 :
13289 : /*
13290 : * Invalidate relcache so that others see the new validated constraint.
13291 : */
13292 112 : CacheInvalidateRelcache(rel);
13293 :
13294 : /*
13295 : * Now update the catalogs, while we have the door open.
13296 : */
13297 112 : copyTuple = heap_copytuple(contuple);
13298 112 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13299 112 : copy_con->convalidated = true;
13300 112 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13301 :
13302 112 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13303 :
13304 112 : heap_freetuple(copyTuple);
13305 112 : }
13306 :
13307 : /*
13308 : * transformColumnNameList - transform list of column names
13309 : *
13310 : * Lookup each name and return its attnum and, optionally, type and collation
13311 : * OIDs
13312 : *
13313 : * Note: the name of this function suggests that it's general-purpose,
13314 : * but actually it's only used to look up names appearing in foreign-key
13315 : * clauses. The error messages would need work to use it in other cases,
13316 : * and perhaps the validity checks as well.
13317 : */
13318 : static int
13319 6598 : transformColumnNameList(Oid relId, List *colList,
13320 : int16 *attnums, Oid *atttypids, Oid *attcollids)
13321 : {
13322 : ListCell *l;
13323 : int attnum;
13324 :
13325 6598 : attnum = 0;
13326 12046 : foreach(l, colList)
13327 : {
13328 5514 : char *attname = strVal(lfirst(l));
13329 : HeapTuple atttuple;
13330 : Form_pg_attribute attform;
13331 :
13332 5514 : atttuple = SearchSysCacheAttName(relId, attname);
13333 5514 : if (!HeapTupleIsValid(atttuple))
13334 54 : ereport(ERROR,
13335 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13336 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13337 : attname)));
13338 5460 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13339 5460 : if (attform->attnum < 0)
13340 12 : ereport(ERROR,
13341 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13342 : errmsg("system columns cannot be used in foreign keys")));
13343 5448 : if (attnum >= INDEX_MAX_KEYS)
13344 0 : ereport(ERROR,
13345 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
13346 : errmsg("cannot have more than %d keys in a foreign key",
13347 : INDEX_MAX_KEYS)));
13348 5448 : attnums[attnum] = attform->attnum;
13349 5448 : if (atttypids != NULL)
13350 5412 : atttypids[attnum] = attform->atttypid;
13351 5448 : if (attcollids != NULL)
13352 5412 : attcollids[attnum] = attform->attcollation;
13353 5448 : ReleaseSysCache(atttuple);
13354 5448 : attnum++;
13355 : }
13356 :
13357 6532 : return attnum;
13358 : }
13359 :
13360 : /*
13361 : * transformFkeyGetPrimaryKey -
13362 : *
13363 : * Look up the names, attnums, types, and collations of the primary key attributes
13364 : * for the pkrel. Also return the index OID and index opclasses of the
13365 : * index supporting the primary key. Also return whether the index has
13366 : * WITHOUT OVERLAPS.
13367 : *
13368 : * All parameters except pkrel are output parameters. Also, the function
13369 : * return value is the number of attributes in the primary key.
13370 : *
13371 : * Used when the column list in the REFERENCES specification is omitted.
13372 : */
13373 : static int
13374 1256 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
13375 : List **attnamelist,
13376 : int16 *attnums, Oid *atttypids, Oid *attcollids,
13377 : Oid *opclasses, bool *pk_has_without_overlaps)
13378 : {
13379 : List *indexoidlist;
13380 : ListCell *indexoidscan;
13381 1256 : HeapTuple indexTuple = NULL;
13382 1256 : Form_pg_index indexStruct = NULL;
13383 : Datum indclassDatum;
13384 : oidvector *indclass;
13385 : int i;
13386 :
13387 : /*
13388 : * Get the list of index OIDs for the table from the relcache, and look up
13389 : * each one in the pg_index syscache until we find one marked primary key
13390 : * (hopefully there isn't more than one such). Insist it's valid, too.
13391 : */
13392 1256 : *indexOid = InvalidOid;
13393 :
13394 1256 : indexoidlist = RelationGetIndexList(pkrel);
13395 :
13396 1262 : foreach(indexoidscan, indexoidlist)
13397 : {
13398 1262 : Oid indexoid = lfirst_oid(indexoidscan);
13399 :
13400 1262 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13401 1262 : if (!HeapTupleIsValid(indexTuple))
13402 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13403 1262 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13404 1262 : if (indexStruct->indisprimary && indexStruct->indisvalid)
13405 : {
13406 : /*
13407 : * Refuse to use a deferrable primary key. This is per SQL spec,
13408 : * and there would be a lot of interesting semantic problems if we
13409 : * tried to allow it.
13410 : */
13411 1256 : if (!indexStruct->indimmediate)
13412 0 : ereport(ERROR,
13413 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13414 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13415 : RelationGetRelationName(pkrel))));
13416 :
13417 1256 : *indexOid = indexoid;
13418 1256 : break;
13419 : }
13420 6 : ReleaseSysCache(indexTuple);
13421 : }
13422 :
13423 1256 : list_free(indexoidlist);
13424 :
13425 : /*
13426 : * Check that we found it
13427 : */
13428 1256 : if (!OidIsValid(*indexOid))
13429 0 : ereport(ERROR,
13430 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13431 : errmsg("there is no primary key for referenced table \"%s\"",
13432 : RelationGetRelationName(pkrel))));
13433 :
13434 : /* Must get indclass the hard way */
13435 1256 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13436 : Anum_pg_index_indclass);
13437 1256 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13438 :
13439 : /*
13440 : * Now build the list of PK attributes from the indkey definition (we
13441 : * assume a primary key cannot have expressional elements)
13442 : */
13443 1256 : *attnamelist = NIL;
13444 2990 : for (i = 0; i < indexStruct->indnkeyatts; i++)
13445 : {
13446 1734 : int pkattno = indexStruct->indkey.values[i];
13447 :
13448 1734 : attnums[i] = pkattno;
13449 1734 : atttypids[i] = attnumTypeId(pkrel, pkattno);
13450 1734 : attcollids[i] = attnumCollationId(pkrel, pkattno);
13451 1734 : opclasses[i] = indclass->values[i];
13452 1734 : *attnamelist = lappend(*attnamelist,
13453 1734 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13454 : }
13455 :
13456 1256 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13457 :
13458 1256 : ReleaseSysCache(indexTuple);
13459 :
13460 1256 : return i;
13461 : }
13462 :
13463 : /*
13464 : * transformFkeyCheckAttrs -
13465 : *
13466 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13467 : * reference as part of a foreign key constraint.
13468 : *
13469 : * Returns the OID of the unique index supporting the constraint and
13470 : * populates the caller-provided 'opclasses' array with the opclasses
13471 : * associated with the index columns. Also sets whether the index
13472 : * uses WITHOUT OVERLAPS.
13473 : *
13474 : * Raises an ERROR on validation failure.
13475 : */
13476 : static Oid
13477 1282 : transformFkeyCheckAttrs(Relation pkrel,
13478 : int numattrs, int16 *attnums,
13479 : bool with_period, Oid *opclasses,
13480 : bool *pk_has_without_overlaps)
13481 : {
13482 1282 : Oid indexoid = InvalidOid;
13483 1282 : bool found = false;
13484 1282 : bool found_deferrable = false;
13485 : List *indexoidlist;
13486 : ListCell *indexoidscan;
13487 : int i,
13488 : j;
13489 :
13490 : /*
13491 : * Reject duplicate appearances of columns in the referenced-columns list.
13492 : * Such a case is forbidden by the SQL standard, and even if we thought it
13493 : * useful to allow it, there would be ambiguity about how to match the
13494 : * list to unique indexes (in particular, it'd be unclear which index
13495 : * opclass goes with which FK column).
13496 : */
13497 2992 : for (i = 0; i < numattrs; i++)
13498 : {
13499 2256 : for (j = i + 1; j < numattrs; j++)
13500 : {
13501 546 : if (attnums[i] == attnums[j])
13502 24 : ereport(ERROR,
13503 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13504 : errmsg("foreign key referenced-columns list must not contain duplicates")));
13505 : }
13506 : }
13507 :
13508 : /*
13509 : * Get the list of index OIDs for the table from the relcache, and look up
13510 : * each one in the pg_index syscache, and match unique indexes to the list
13511 : * of attnums we are given.
13512 : */
13513 1258 : indexoidlist = RelationGetIndexList(pkrel);
13514 :
13515 1438 : foreach(indexoidscan, indexoidlist)
13516 : {
13517 : HeapTuple indexTuple;
13518 : Form_pg_index indexStruct;
13519 :
13520 1426 : indexoid = lfirst_oid(indexoidscan);
13521 1426 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13522 1426 : if (!HeapTupleIsValid(indexTuple))
13523 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13524 1426 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13525 :
13526 : /*
13527 : * Must have the right number of columns; must be unique (or if
13528 : * temporal then exclusion instead) and not a partial index; forget it
13529 : * if there are any expressions, too. Invalid indexes are out as well.
13530 : */
13531 2744 : if (indexStruct->indnkeyatts == numattrs &&
13532 1318 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13533 2608 : indexStruct->indisvalid &&
13534 2608 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13535 1304 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13536 : {
13537 : Datum indclassDatum;
13538 : oidvector *indclass;
13539 :
13540 : /* Must get indclass the hard way */
13541 1304 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13542 : Anum_pg_index_indclass);
13543 1304 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13544 :
13545 : /*
13546 : * The given attnum list may match the index columns in any order.
13547 : * Check for a match, and extract the appropriate opclasses while
13548 : * we're at it.
13549 : *
13550 : * We know that attnums[] is duplicate-free per the test at the
13551 : * start of this function, and we checked above that the number of
13552 : * index columns agrees, so if we find a match for each attnums[]
13553 : * entry then we must have a one-to-one match in some order.
13554 : */
13555 3002 : for (i = 0; i < numattrs; i++)
13556 : {
13557 1756 : found = false;
13558 2336 : for (j = 0; j < numattrs; j++)
13559 : {
13560 2278 : if (attnums[i] == indexStruct->indkey.values[j])
13561 : {
13562 1698 : opclasses[i] = indclass->values[j];
13563 1698 : found = true;
13564 1698 : break;
13565 : }
13566 : }
13567 1756 : if (!found)
13568 58 : break;
13569 : }
13570 : /* The last attribute in the index must be the PERIOD FK part */
13571 1304 : if (found && with_period)
13572 : {
13573 122 : int16 periodattnum = attnums[numattrs - 1];
13574 :
13575 122 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13576 : }
13577 :
13578 : /*
13579 : * Refuse to use a deferrable unique/primary key. This is per SQL
13580 : * spec, and there would be a lot of interesting semantic problems
13581 : * if we tried to allow it.
13582 : */
13583 1304 : if (found && !indexStruct->indimmediate)
13584 : {
13585 : /*
13586 : * Remember that we found an otherwise matching index, so that
13587 : * we can generate a more appropriate error message.
13588 : */
13589 0 : found_deferrable = true;
13590 0 : found = false;
13591 : }
13592 :
13593 : /* We need to know whether the index has WITHOUT OVERLAPS */
13594 1304 : if (found)
13595 1246 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13596 : }
13597 1426 : ReleaseSysCache(indexTuple);
13598 1426 : if (found)
13599 1246 : break;
13600 : }
13601 :
13602 1258 : if (!found)
13603 : {
13604 12 : if (found_deferrable)
13605 0 : ereport(ERROR,
13606 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13607 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13608 : RelationGetRelationName(pkrel))));
13609 : else
13610 12 : ereport(ERROR,
13611 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13612 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13613 : RelationGetRelationName(pkrel))));
13614 : }
13615 :
13616 1246 : list_free(indexoidlist);
13617 :
13618 1246 : return indexoid;
13619 : }
13620 :
13621 : /*
13622 : * findFkeyCast -
13623 : *
13624 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13625 : * Caller has equal regard for binary coercibility and for an exact match.
13626 : */
13627 : static CoercionPathType
13628 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13629 : {
13630 : CoercionPathType ret;
13631 :
13632 12 : if (targetTypeId == sourceTypeId)
13633 : {
13634 12 : ret = COERCION_PATH_RELABELTYPE;
13635 12 : *funcid = InvalidOid;
13636 : }
13637 : else
13638 : {
13639 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13640 : COERCION_IMPLICIT, funcid);
13641 0 : if (ret == COERCION_PATH_NONE)
13642 : /* A previously-relied-upon cast is now gone. */
13643 0 : elog(ERROR, "could not find cast from %u to %u",
13644 : sourceTypeId, targetTypeId);
13645 : }
13646 :
13647 12 : return ret;
13648 : }
13649 :
13650 : /*
13651 : * Permissions checks on the referenced table for ADD FOREIGN KEY
13652 : *
13653 : * Note: we have already checked that the user owns the referencing table,
13654 : * else we'd have failed much earlier; no additional checks are needed for it.
13655 : */
13656 : static void
13657 2466 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13658 : {
13659 2466 : Oid roleid = GetUserId();
13660 : AclResult aclresult;
13661 : int i;
13662 :
13663 : /* Okay if we have relation-level REFERENCES permission */
13664 2466 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13665 : ACL_REFERENCES);
13666 2466 : if (aclresult == ACLCHECK_OK)
13667 2466 : return;
13668 : /* Else we must have REFERENCES on each column */
13669 0 : for (i = 0; i < natts; i++)
13670 : {
13671 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13672 : roleid, ACL_REFERENCES);
13673 0 : if (aclresult != ACLCHECK_OK)
13674 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13675 0 : RelationGetRelationName(rel));
13676 : }
13677 : }
13678 :
13679 : /*
13680 : * Scan the existing rows in a table to verify they meet a proposed FK
13681 : * constraint.
13682 : *
13683 : * Caller must have opened and locked both relations appropriately.
13684 : */
13685 : static void
13686 1166 : validateForeignKeyConstraint(char *conname,
13687 : Relation rel,
13688 : Relation pkrel,
13689 : Oid pkindOid,
13690 : Oid constraintOid,
13691 : bool hasperiod)
13692 : {
13693 : TupleTableSlot *slot;
13694 : TableScanDesc scan;
13695 1166 : Trigger trig = {0};
13696 : Snapshot snapshot;
13697 : MemoryContext oldcxt;
13698 : MemoryContext perTupCxt;
13699 :
13700 1166 : ereport(DEBUG1,
13701 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13702 :
13703 : /*
13704 : * Build a trigger call structure; we'll need it either way.
13705 : */
13706 1166 : trig.tgoid = InvalidOid;
13707 1166 : trig.tgname = conname;
13708 1166 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13709 1166 : trig.tgisinternal = true;
13710 1166 : trig.tgconstrrelid = RelationGetRelid(pkrel);
13711 1166 : trig.tgconstrindid = pkindOid;
13712 1166 : trig.tgconstraint = constraintOid;
13713 1166 : trig.tgdeferrable = false;
13714 1166 : trig.tginitdeferred = false;
13715 : /* we needn't fill in remaining fields */
13716 :
13717 : /*
13718 : * See if we can do it with a single LEFT JOIN query. A false result
13719 : * indicates we must proceed with the fire-the-trigger method. We can't do
13720 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13721 : * left joins.
13722 : */
13723 1166 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13724 984 : return;
13725 :
13726 : /*
13727 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13728 : * if that tuple had just been inserted. If any of those fail, it should
13729 : * ereport(ERROR) and that's that.
13730 : */
13731 108 : snapshot = RegisterSnapshot(GetLatestSnapshot());
13732 108 : slot = table_slot_create(rel, NULL);
13733 108 : scan = table_beginscan(rel, snapshot, 0, NULL);
13734 :
13735 108 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
13736 : "validateForeignKeyConstraint",
13737 : ALLOCSET_SMALL_SIZES);
13738 108 : oldcxt = MemoryContextSwitchTo(perTupCxt);
13739 :
13740 192 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13741 : {
13742 102 : LOCAL_FCINFO(fcinfo, 0);
13743 102 : TriggerData trigdata = {0};
13744 :
13745 102 : CHECK_FOR_INTERRUPTS();
13746 :
13747 : /*
13748 : * Make a call to the trigger function
13749 : *
13750 : * No parameters are passed, but we do set a context
13751 : */
13752 510 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13753 :
13754 : /*
13755 : * We assume RI_FKey_check_ins won't look at flinfo...
13756 : */
13757 102 : trigdata.type = T_TriggerData;
13758 102 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
13759 102 : trigdata.tg_relation = rel;
13760 102 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13761 102 : trigdata.tg_trigslot = slot;
13762 102 : trigdata.tg_trigger = &trig;
13763 :
13764 102 : fcinfo->context = (Node *) &trigdata;
13765 :
13766 102 : RI_FKey_check_ins(fcinfo);
13767 :
13768 84 : MemoryContextReset(perTupCxt);
13769 : }
13770 :
13771 90 : MemoryContextSwitchTo(oldcxt);
13772 90 : MemoryContextDelete(perTupCxt);
13773 90 : table_endscan(scan);
13774 90 : UnregisterSnapshot(snapshot);
13775 90 : ExecDropSingleTupleTableSlot(slot);
13776 : }
13777 :
13778 : /*
13779 : * CreateFKCheckTrigger
13780 : * Creates the insert (on_insert=true) or update "check" trigger that
13781 : * implements a given foreign key
13782 : *
13783 : * Returns the OID of the so created trigger.
13784 : */
13785 : static Oid
13786 5944 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13787 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13788 : bool on_insert)
13789 : {
13790 : ObjectAddress trigAddress;
13791 : CreateTrigStmt *fk_trigger;
13792 :
13793 : /*
13794 : * Note: for a self-referential FK (referencing and referenced tables are
13795 : * the same), it is important that the ON UPDATE action fires before the
13796 : * CHECK action, since both triggers will fire on the same row during an
13797 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13798 : * state of the row. Triggers fire in name order, so we ensure this by
13799 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13800 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13801 : */
13802 5944 : fk_trigger = makeNode(CreateTrigStmt);
13803 5944 : fk_trigger->replace = false;
13804 5944 : fk_trigger->isconstraint = true;
13805 5944 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
13806 5944 : fk_trigger->relation = NULL;
13807 :
13808 : /* Either ON INSERT or ON UPDATE */
13809 5944 : if (on_insert)
13810 : {
13811 2972 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13812 2972 : fk_trigger->events = TRIGGER_TYPE_INSERT;
13813 : }
13814 : else
13815 : {
13816 2972 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13817 2972 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13818 : }
13819 :
13820 5944 : fk_trigger->args = NIL;
13821 5944 : fk_trigger->row = true;
13822 5944 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13823 5944 : fk_trigger->columns = NIL;
13824 5944 : fk_trigger->whenClause = NULL;
13825 5944 : fk_trigger->transitionRels = NIL;
13826 5944 : fk_trigger->deferrable = fkconstraint->deferrable;
13827 5944 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13828 5944 : fk_trigger->constrrel = NULL;
13829 :
13830 5944 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13831 : constraintOid, indexOid, InvalidOid,
13832 : parentTrigOid, NULL, true, false);
13833 :
13834 : /* Make changes-so-far visible */
13835 5944 : CommandCounterIncrement();
13836 :
13837 5944 : return trigAddress.objectId;
13838 : }
13839 :
13840 : /*
13841 : * createForeignKeyActionTriggers
13842 : * Create the referenced-side "action" triggers that implement a foreign
13843 : * key.
13844 : *
13845 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
13846 : * *updateTrigOid.
13847 : */
13848 : static void
13849 3402 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13850 : Oid constraintOid, Oid indexOid,
13851 : Oid parentDelTrigger, Oid parentUpdTrigger,
13852 : Oid *deleteTrigOid, Oid *updateTrigOid)
13853 : {
13854 : CreateTrigStmt *fk_trigger;
13855 : ObjectAddress trigAddress;
13856 :
13857 : /*
13858 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13859 : * DELETE action on the referenced table.
13860 : */
13861 3402 : fk_trigger = makeNode(CreateTrigStmt);
13862 3402 : fk_trigger->replace = false;
13863 3402 : fk_trigger->isconstraint = true;
13864 3402 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13865 3402 : fk_trigger->relation = NULL;
13866 3402 : fk_trigger->args = NIL;
13867 3402 : fk_trigger->row = true;
13868 3402 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13869 3402 : fk_trigger->events = TRIGGER_TYPE_DELETE;
13870 3402 : fk_trigger->columns = NIL;
13871 3402 : fk_trigger->whenClause = NULL;
13872 3402 : fk_trigger->transitionRels = NIL;
13873 3402 : fk_trigger->constrrel = NULL;
13874 :
13875 3402 : switch (fkconstraint->fk_del_action)
13876 : {
13877 2750 : case FKCONSTR_ACTION_NOACTION:
13878 2750 : fk_trigger->deferrable = fkconstraint->deferrable;
13879 2750 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13880 2750 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13881 2750 : break;
13882 30 : case FKCONSTR_ACTION_RESTRICT:
13883 30 : fk_trigger->deferrable = false;
13884 30 : fk_trigger->initdeferred = false;
13885 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13886 30 : break;
13887 464 : case FKCONSTR_ACTION_CASCADE:
13888 464 : fk_trigger->deferrable = false;
13889 464 : fk_trigger->initdeferred = false;
13890 464 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13891 464 : break;
13892 98 : case FKCONSTR_ACTION_SETNULL:
13893 98 : fk_trigger->deferrable = false;
13894 98 : fk_trigger->initdeferred = false;
13895 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13896 98 : break;
13897 60 : case FKCONSTR_ACTION_SETDEFAULT:
13898 60 : fk_trigger->deferrable = false;
13899 60 : fk_trigger->initdeferred = false;
13900 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13901 60 : break;
13902 0 : default:
13903 0 : elog(ERROR, "unrecognized FK action type: %d",
13904 : (int) fkconstraint->fk_del_action);
13905 : break;
13906 : }
13907 :
13908 3402 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13909 : constraintOid, indexOid, InvalidOid,
13910 : parentDelTrigger, NULL, true, false);
13911 3402 : if (deleteTrigOid)
13912 3402 : *deleteTrigOid = trigAddress.objectId;
13913 :
13914 : /* Make changes-so-far visible */
13915 3402 : CommandCounterIncrement();
13916 :
13917 : /*
13918 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13919 : * UPDATE action on the referenced table.
13920 : */
13921 3402 : fk_trigger = makeNode(CreateTrigStmt);
13922 3402 : fk_trigger->replace = false;
13923 3402 : fk_trigger->isconstraint = true;
13924 3402 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13925 3402 : fk_trigger->relation = NULL;
13926 3402 : fk_trigger->args = NIL;
13927 3402 : fk_trigger->row = true;
13928 3402 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13929 3402 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13930 3402 : fk_trigger->columns = NIL;
13931 3402 : fk_trigger->whenClause = NULL;
13932 3402 : fk_trigger->transitionRels = NIL;
13933 3402 : fk_trigger->constrrel = NULL;
13934 :
13935 3402 : switch (fkconstraint->fk_upd_action)
13936 : {
13937 2944 : case FKCONSTR_ACTION_NOACTION:
13938 2944 : fk_trigger->deferrable = fkconstraint->deferrable;
13939 2944 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13940 2944 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13941 2944 : break;
13942 36 : case FKCONSTR_ACTION_RESTRICT:
13943 36 : fk_trigger->deferrable = false;
13944 36 : fk_trigger->initdeferred = false;
13945 36 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13946 36 : break;
13947 318 : case FKCONSTR_ACTION_CASCADE:
13948 318 : fk_trigger->deferrable = false;
13949 318 : fk_trigger->initdeferred = false;
13950 318 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13951 318 : break;
13952 62 : case FKCONSTR_ACTION_SETNULL:
13953 62 : fk_trigger->deferrable = false;
13954 62 : fk_trigger->initdeferred = false;
13955 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13956 62 : break;
13957 42 : case FKCONSTR_ACTION_SETDEFAULT:
13958 42 : fk_trigger->deferrable = false;
13959 42 : fk_trigger->initdeferred = false;
13960 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13961 42 : break;
13962 0 : default:
13963 0 : elog(ERROR, "unrecognized FK action type: %d",
13964 : (int) fkconstraint->fk_upd_action);
13965 : break;
13966 : }
13967 :
13968 3402 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13969 : constraintOid, indexOid, InvalidOid,
13970 : parentUpdTrigger, NULL, true, false);
13971 3402 : if (updateTrigOid)
13972 3402 : *updateTrigOid = trigAddress.objectId;
13973 3402 : }
13974 :
13975 : /*
13976 : * createForeignKeyCheckTriggers
13977 : * Create the referencing-side "check" triggers that implement a foreign
13978 : * key.
13979 : *
13980 : * Returns the OIDs of the so created triggers in *insertTrigOid and
13981 : * *updateTrigOid.
13982 : */
13983 : static void
13984 2972 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
13985 : Constraint *fkconstraint, Oid constraintOid,
13986 : Oid indexOid,
13987 : Oid parentInsTrigger, Oid parentUpdTrigger,
13988 : Oid *insertTrigOid, Oid *updateTrigOid)
13989 : {
13990 2972 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13991 : constraintOid, indexOid,
13992 : parentInsTrigger, true);
13993 2972 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13994 : constraintOid, indexOid,
13995 : parentUpdTrigger, false);
13996 2972 : }
13997 :
13998 : /*
13999 : * ALTER TABLE DROP CONSTRAINT
14000 : *
14001 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
14002 : */
14003 : static void
14004 812 : ATExecDropConstraint(Relation rel, const char *constrName,
14005 : DropBehavior behavior, bool recurse,
14006 : bool missing_ok, LOCKMODE lockmode)
14007 : {
14008 : Relation conrel;
14009 : SysScanDesc scan;
14010 : ScanKeyData skey[3];
14011 : HeapTuple tuple;
14012 812 : bool found = false;
14013 :
14014 812 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14015 :
14016 : /*
14017 : * Find and drop the target constraint
14018 : */
14019 812 : ScanKeyInit(&skey[0],
14020 : Anum_pg_constraint_conrelid,
14021 : BTEqualStrategyNumber, F_OIDEQ,
14022 : ObjectIdGetDatum(RelationGetRelid(rel)));
14023 812 : ScanKeyInit(&skey[1],
14024 : Anum_pg_constraint_contypid,
14025 : BTEqualStrategyNumber, F_OIDEQ,
14026 : ObjectIdGetDatum(InvalidOid));
14027 812 : ScanKeyInit(&skey[2],
14028 : Anum_pg_constraint_conname,
14029 : BTEqualStrategyNumber, F_NAMEEQ,
14030 : CStringGetDatum(constrName));
14031 812 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14032 : true, NULL, 3, skey);
14033 :
14034 : /* There can be at most one matching row */
14035 812 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14036 : {
14037 776 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
14038 : missing_ok, lockmode);
14039 590 : found = true;
14040 : }
14041 :
14042 626 : systable_endscan(scan);
14043 :
14044 626 : if (!found)
14045 : {
14046 36 : if (!missing_ok)
14047 24 : ereport(ERROR,
14048 : errcode(ERRCODE_UNDEFINED_OBJECT),
14049 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14050 : constrName, RelationGetRelationName(rel)));
14051 : else
14052 12 : ereport(NOTICE,
14053 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14054 : constrName, RelationGetRelationName(rel)));
14055 : }
14056 :
14057 602 : table_close(conrel, RowExclusiveLock);
14058 602 : }
14059 :
14060 : /*
14061 : * Remove a constraint, using its pg_constraint tuple
14062 : *
14063 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14064 : * DROP NOT NULL.
14065 : *
14066 : * Returns the address of the constraint being removed.
14067 : */
14068 : static ObjectAddress
14069 1206 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
14070 : bool recurse, bool recursing, bool missing_ok,
14071 : LOCKMODE lockmode)
14072 : {
14073 : Relation conrel;
14074 : Form_pg_constraint con;
14075 : ObjectAddress conobj;
14076 : List *children;
14077 1206 : bool is_no_inherit_constraint = false;
14078 : char *constrName;
14079 1206 : char *colname = NULL;
14080 :
14081 : /* Guard against stack overflow due to overly deep inheritance tree. */
14082 1206 : check_stack_depth();
14083 :
14084 : /* At top level, permission check was done in ATPrepCmd, else do it */
14085 1206 : if (recursing)
14086 210 : ATSimplePermissions(AT_DropConstraint, rel,
14087 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
14088 :
14089 1200 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14090 :
14091 1200 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14092 1200 : constrName = NameStr(con->conname);
14093 :
14094 : /* Don't allow drop of inherited constraints */
14095 1200 : if (con->coninhcount > 0 && !recursing)
14096 156 : ereport(ERROR,
14097 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14098 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14099 : constrName, RelationGetRelationName(rel))));
14100 :
14101 : /*
14102 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14103 : *
14104 : * While doing that, we're in a good position to disallow dropping a not-
14105 : * null constraint underneath a primary key, a replica identity index, or
14106 : * a generated identity column.
14107 : */
14108 1044 : if (con->contype == CONSTRAINT_NOTNULL)
14109 : {
14110 314 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14111 314 : AttrNumber attnum = extractNotNullColumn(constraintTup);
14112 : Bitmapset *pkattrs;
14113 : Bitmapset *irattrs;
14114 : HeapTuple atttup;
14115 : Form_pg_attribute attForm;
14116 :
14117 : /* save column name for recursion step */
14118 314 : colname = get_attname(RelationGetRelid(rel), attnum, false);
14119 :
14120 : /*
14121 : * Disallow if it's in the primary key. For partitioned tables we
14122 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14123 : * return NULL if the primary key is invalid; but we still need to
14124 : * protect not-null constraints under such a constraint, so check the
14125 : * slow way.
14126 : */
14127 314 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
14128 :
14129 314 : if (pkattrs == NULL &&
14130 278 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14131 : {
14132 18 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14133 :
14134 18 : if (OidIsValid(pkindex))
14135 : {
14136 0 : Relation pk = relation_open(pkindex, AccessShareLock);
14137 :
14138 0 : pkattrs = NULL;
14139 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14140 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14141 :
14142 0 : relation_close(pk, AccessShareLock);
14143 : }
14144 : }
14145 :
14146 350 : if (pkattrs &&
14147 36 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
14148 24 : ereport(ERROR,
14149 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14150 : errmsg("column \"%s\" is in a primary key",
14151 : get_attname(RelationGetRelid(rel), attnum, false)));
14152 :
14153 : /* Disallow if it's in the replica identity */
14154 290 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
14155 290 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
14156 12 : ereport(ERROR,
14157 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14158 : errmsg("column \"%s\" is in index used as replica identity",
14159 : get_attname(RelationGetRelid(rel), attnum, false)));
14160 :
14161 : /* Disallow if it's a GENERATED AS IDENTITY column */
14162 278 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
14163 278 : if (!HeapTupleIsValid(atttup))
14164 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14165 : attnum, RelationGetRelid(rel));
14166 278 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14167 278 : if (attForm->attidentity != '\0')
14168 0 : ereport(ERROR,
14169 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14170 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
14171 : get_attname(RelationGetRelid(rel), attnum,
14172 : false),
14173 : RelationGetRelationName(rel)));
14174 :
14175 : /* All good -- reset attnotnull if needed */
14176 278 : if (attForm->attnotnull)
14177 : {
14178 278 : attForm->attnotnull = false;
14179 278 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14180 : }
14181 :
14182 278 : table_close(attrel, RowExclusiveLock);
14183 : }
14184 :
14185 1008 : is_no_inherit_constraint = con->connoinherit;
14186 :
14187 : /*
14188 : * If it's a foreign-key constraint, we'd better lock the referenced table
14189 : * and check that that's not in use, just as we've already done for the
14190 : * constrained table (else we might, eg, be dropping a trigger that has
14191 : * unfired events). But we can/must skip that in the self-referential
14192 : * case.
14193 : */
14194 1008 : if (con->contype == CONSTRAINT_FOREIGN &&
14195 168 : con->confrelid != RelationGetRelid(rel))
14196 : {
14197 : Relation frel;
14198 :
14199 : /* Must match lock taken by RemoveTriggerById: */
14200 168 : frel = table_open(con->confrelid, AccessExclusiveLock);
14201 168 : CheckAlterTableIsSafe(frel);
14202 162 : table_close(frel, NoLock);
14203 : }
14204 :
14205 : /*
14206 : * Perform the actual constraint deletion
14207 : */
14208 1002 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14209 1002 : performDeletion(&conobj, behavior, 0);
14210 :
14211 : /*
14212 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14213 : * are dropped via the dependency mechanism, so we're done here.
14214 : */
14215 966 : if (con->contype != CONSTRAINT_CHECK &&
14216 630 : con->contype != CONSTRAINT_NOTNULL &&
14217 352 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14218 : {
14219 78 : table_close(conrel, RowExclusiveLock);
14220 78 : return conobj;
14221 : }
14222 :
14223 : /*
14224 : * Propagate to children as appropriate. Unlike most other ALTER
14225 : * routines, we have to do this one level of recursion at a time; we can't
14226 : * use find_all_inheritors to do it in one pass.
14227 : */
14228 888 : if (!is_no_inherit_constraint)
14229 602 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14230 : else
14231 286 : children = NIL;
14232 :
14233 2148 : foreach_oid(childrelid, children)
14234 : {
14235 : Relation childrel;
14236 : HeapTuple tuple;
14237 : Form_pg_constraint childcon;
14238 :
14239 : /* find_inheritance_children already got lock */
14240 384 : childrel = table_open(childrelid, NoLock);
14241 384 : CheckAlterTableIsSafe(childrel);
14242 :
14243 : /*
14244 : * We search for not-null constraints by column name, and others by
14245 : * constraint name.
14246 : */
14247 384 : if (con->contype == CONSTRAINT_NOTNULL)
14248 : {
14249 148 : tuple = findNotNullConstraint(childrelid, colname);
14250 148 : if (!HeapTupleIsValid(tuple))
14251 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14252 : colname, RelationGetRelid(childrel));
14253 : }
14254 : else
14255 : {
14256 : SysScanDesc scan;
14257 : ScanKeyData skey[3];
14258 :
14259 236 : ScanKeyInit(&skey[0],
14260 : Anum_pg_constraint_conrelid,
14261 : BTEqualStrategyNumber, F_OIDEQ,
14262 : ObjectIdGetDatum(childrelid));
14263 236 : ScanKeyInit(&skey[1],
14264 : Anum_pg_constraint_contypid,
14265 : BTEqualStrategyNumber, F_OIDEQ,
14266 : ObjectIdGetDatum(InvalidOid));
14267 236 : ScanKeyInit(&skey[2],
14268 : Anum_pg_constraint_conname,
14269 : BTEqualStrategyNumber, F_NAMEEQ,
14270 : CStringGetDatum(constrName));
14271 236 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14272 : true, NULL, 3, skey);
14273 : /* There can only be one, so no need to loop */
14274 236 : tuple = systable_getnext(scan);
14275 236 : if (!HeapTupleIsValid(tuple))
14276 0 : ereport(ERROR,
14277 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14278 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14279 : constrName,
14280 : RelationGetRelationName(childrel))));
14281 236 : tuple = heap_copytuple(tuple);
14282 236 : systable_endscan(scan);
14283 : }
14284 :
14285 384 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14286 :
14287 : /* Right now only CHECK and not-null constraints can be inherited */
14288 384 : if (childcon->contype != CONSTRAINT_CHECK &&
14289 148 : childcon->contype != CONSTRAINT_NOTNULL)
14290 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14291 :
14292 384 : if (childcon->coninhcount <= 0) /* shouldn't happen */
14293 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14294 : childrelid, NameStr(childcon->conname));
14295 :
14296 384 : if (recurse)
14297 : {
14298 : /*
14299 : * If the child constraint has other definition sources, just
14300 : * decrement its inheritance count; if not, recurse to delete it.
14301 : */
14302 282 : if (childcon->coninhcount == 1 && !childcon->conislocal)
14303 : {
14304 : /* Time to delete this child constraint, too */
14305 210 : dropconstraint_internal(childrel, tuple, behavior,
14306 : recurse, true, missing_ok,
14307 : lockmode);
14308 : }
14309 : else
14310 : {
14311 : /* Child constraint must survive my deletion */
14312 72 : childcon->coninhcount--;
14313 72 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14314 :
14315 : /* Make update visible */
14316 72 : CommandCounterIncrement();
14317 : }
14318 : }
14319 : else
14320 : {
14321 : /*
14322 : * If we were told to drop ONLY in this table (no recursion) and
14323 : * there are no further parents for this constraint, we need to
14324 : * mark the inheritors' constraints as locally defined rather than
14325 : * inherited.
14326 : */
14327 102 : childcon->coninhcount--;
14328 102 : if (childcon->coninhcount == 0)
14329 102 : childcon->conislocal = true;
14330 :
14331 102 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14332 :
14333 : /* Make update visible */
14334 102 : CommandCounterIncrement();
14335 : }
14336 :
14337 378 : heap_freetuple(tuple);
14338 :
14339 378 : table_close(childrel, NoLock);
14340 : }
14341 :
14342 882 : table_close(conrel, RowExclusiveLock);
14343 :
14344 882 : return conobj;
14345 : }
14346 :
14347 : /*
14348 : * ALTER COLUMN TYPE
14349 : *
14350 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14351 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14352 : * transformed (and must be, because we rely on some transformed fields).
14353 : *
14354 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14355 : * table will be done "in parallel" during phase 3, so all the USING
14356 : * expressions should be parsed assuming the original column types. Also,
14357 : * this allows a USING expression to refer to a field that will be dropped.
14358 : *
14359 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14360 : * the first two execution steps in phase 2; they must not see the effects
14361 : * of any other subcommand types, since the USING expressions are parsed
14362 : * against the unmodified table's state.
14363 : */
14364 : static void
14365 1426 : ATPrepAlterColumnType(List **wqueue,
14366 : AlteredTableInfo *tab, Relation rel,
14367 : bool recurse, bool recursing,
14368 : AlterTableCmd *cmd, LOCKMODE lockmode,
14369 : AlterTableUtilityContext *context)
14370 : {
14371 1426 : char *colName = cmd->name;
14372 1426 : ColumnDef *def = (ColumnDef *) cmd->def;
14373 1426 : TypeName *typeName = def->typeName;
14374 1426 : Node *transform = def->cooked_default;
14375 : HeapTuple tuple;
14376 : Form_pg_attribute attTup;
14377 : AttrNumber attnum;
14378 : Oid targettype;
14379 : int32 targettypmod;
14380 : Oid targetcollid;
14381 : NewColumnValue *newval;
14382 1426 : ParseState *pstate = make_parsestate(NULL);
14383 : AclResult aclresult;
14384 : bool is_expr;
14385 :
14386 1426 : pstate->p_sourcetext = context->queryString;
14387 :
14388 1426 : if (rel->rd_rel->reloftype && !recursing)
14389 6 : ereport(ERROR,
14390 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14391 : errmsg("cannot alter column type of typed table"),
14392 : parser_errposition(pstate, def->location)));
14393 :
14394 : /* lookup the attribute so we can check inheritance status */
14395 1420 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14396 1420 : if (!HeapTupleIsValid(tuple))
14397 0 : ereport(ERROR,
14398 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14399 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14400 : colName, RelationGetRelationName(rel)),
14401 : parser_errposition(pstate, def->location)));
14402 1420 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14403 1420 : attnum = attTup->attnum;
14404 :
14405 : /* Can't alter a system attribute */
14406 1420 : if (attnum <= 0)
14407 6 : ereport(ERROR,
14408 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14409 : errmsg("cannot alter system column \"%s\"", colName),
14410 : parser_errposition(pstate, def->location)));
14411 :
14412 : /*
14413 : * Cannot specify USING when altering type of a generated column, because
14414 : * that would violate the generation expression.
14415 : */
14416 1414 : if (attTup->attgenerated && def->cooked_default)
14417 12 : ereport(ERROR,
14418 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14419 : errmsg("cannot specify USING when altering type of generated column"),
14420 : errdetail("Column \"%s\" is a generated column.", colName),
14421 : parser_errposition(pstate, def->location)));
14422 :
14423 : /*
14424 : * Don't alter inherited columns. At outer level, there had better not be
14425 : * any inherited definition; when recursing, we assume this was checked at
14426 : * the parent level (see below).
14427 : */
14428 1402 : if (attTup->attinhcount > 0 && !recursing)
14429 6 : ereport(ERROR,
14430 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14431 : errmsg("cannot alter inherited column \"%s\"", colName),
14432 : parser_errposition(pstate, def->location)));
14433 :
14434 : /* Don't alter columns used in the partition key */
14435 1396 : if (has_partition_attrs(rel,
14436 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
14437 : &is_expr))
14438 18 : ereport(ERROR,
14439 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14440 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14441 : colName, RelationGetRelationName(rel)),
14442 : parser_errposition(pstate, def->location)));
14443 :
14444 : /* Look up the target type */
14445 1378 : typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14446 :
14447 1372 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14448 1372 : if (aclresult != ACLCHECK_OK)
14449 12 : aclcheck_error_type(aclresult, targettype);
14450 :
14451 : /* And the collation */
14452 1360 : targetcollid = GetColumnDefCollation(pstate, def, targettype);
14453 :
14454 : /* make sure datatype is legal for a column */
14455 2708 : CheckAttributeType(colName, targettype, targetcollid,
14456 1354 : list_make1_oid(rel->rd_rel->reltype),
14457 1354 : (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14458 :
14459 1342 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14460 : {
14461 : /* do nothing */
14462 : }
14463 1306 : else if (tab->relkind == RELKIND_RELATION ||
14464 202 : tab->relkind == RELKIND_PARTITIONED_TABLE)
14465 : {
14466 : /*
14467 : * Set up an expression to transform the old data value to the new
14468 : * type. If a USING option was given, use the expression as
14469 : * transformed by transformAlterTableStmt, else just take the old
14470 : * value and try to coerce it. We do this first so that type
14471 : * incompatibility can be detected before we waste effort, and because
14472 : * we need the expression to be parsed against the original table row
14473 : * type.
14474 : */
14475 1170 : if (!transform)
14476 : {
14477 942 : transform = (Node *) makeVar(1, attnum,
14478 : attTup->atttypid, attTup->atttypmod,
14479 : attTup->attcollation,
14480 : 0);
14481 : }
14482 :
14483 1170 : transform = coerce_to_target_type(pstate,
14484 : transform, exprType(transform),
14485 : targettype, targettypmod,
14486 : COERCION_ASSIGNMENT,
14487 : COERCE_IMPLICIT_CAST,
14488 : -1);
14489 1170 : if (transform == NULL)
14490 : {
14491 : /* error text depends on whether USING was specified or not */
14492 24 : if (def->cooked_default != NULL)
14493 6 : ereport(ERROR,
14494 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14495 : errmsg("result of USING clause for column \"%s\""
14496 : " cannot be cast automatically to type %s",
14497 : colName, format_type_be(targettype)),
14498 : errhint("You might need to add an explicit cast.")));
14499 : else
14500 18 : ereport(ERROR,
14501 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14502 : errmsg("column \"%s\" cannot be cast automatically to type %s",
14503 : colName, format_type_be(targettype)),
14504 : !attTup->attgenerated ?
14505 : /* translator: USING is SQL, don't translate it */
14506 : errhint("You might need to specify \"USING %s::%s\".",
14507 : quote_identifier(colName),
14508 : format_type_with_typemod(targettype,
14509 : targettypmod)) : 0));
14510 : }
14511 :
14512 : /* Fix collations after all else */
14513 1146 : assign_expr_collations(pstate, transform);
14514 :
14515 : /* Expand virtual generated columns in the expr. */
14516 1146 : transform = expand_generated_columns_in_expr(transform, rel, 1);
14517 :
14518 : /* Plan the expr now so we can accurately assess the need to rewrite. */
14519 1146 : transform = (Node *) expression_planner((Expr *) transform);
14520 :
14521 : /*
14522 : * Add a work queue item to make ATRewriteTable update the column
14523 : * contents.
14524 : */
14525 1146 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
14526 1146 : newval->attnum = attnum;
14527 1146 : newval->expr = (Expr *) transform;
14528 1146 : newval->is_generated = false;
14529 :
14530 1146 : tab->newvals = lappend(tab->newvals, newval);
14531 1146 : if (ATColumnChangeRequiresRewrite(transform, attnum))
14532 944 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
14533 : }
14534 136 : else if (transform)
14535 12 : ereport(ERROR,
14536 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14537 : errmsg("\"%s\" is not a table",
14538 : RelationGetRelationName(rel))));
14539 :
14540 1306 : if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14541 : {
14542 : /*
14543 : * For relations or columns without storage, do this check now.
14544 : * Regular tables will check it later when the table is being
14545 : * rewritten.
14546 : */
14547 226 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14548 : }
14549 :
14550 1258 : ReleaseSysCache(tuple);
14551 :
14552 : /*
14553 : * Recurse manually by queueing a new command for each child, if
14554 : * necessary. We cannot apply ATSimpleRecursion here because we need to
14555 : * remap attribute numbers in the USING expression, if any.
14556 : *
14557 : * If we are told not to recurse, there had better not be any child
14558 : * tables; else the alter would put them out of step.
14559 : */
14560 1258 : if (recurse)
14561 : {
14562 1000 : Oid relid = RelationGetRelid(rel);
14563 : List *child_oids,
14564 : *child_numparents;
14565 : ListCell *lo,
14566 : *li;
14567 :
14568 1000 : child_oids = find_all_inheritors(relid, lockmode,
14569 : &child_numparents);
14570 :
14571 : /*
14572 : * find_all_inheritors does the recursive search of the inheritance
14573 : * hierarchy, so all we have to do is process all of the relids in the
14574 : * list that it returns.
14575 : */
14576 2208 : forboth(lo, child_oids, li, child_numparents)
14577 : {
14578 1232 : Oid childrelid = lfirst_oid(lo);
14579 1232 : int numparents = lfirst_int(li);
14580 : Relation childrel;
14581 : HeapTuple childtuple;
14582 : Form_pg_attribute childattTup;
14583 :
14584 1232 : if (childrelid == relid)
14585 1000 : continue;
14586 :
14587 : /* find_all_inheritors already got lock */
14588 232 : childrel = relation_open(childrelid, NoLock);
14589 232 : CheckAlterTableIsSafe(childrel);
14590 :
14591 : /*
14592 : * Verify that the child doesn't have any inherited definitions of
14593 : * this column that came from outside this inheritance hierarchy.
14594 : * (renameatt makes a similar test, though in a different way
14595 : * because of its different recursion mechanism.)
14596 : */
14597 232 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14598 : colName);
14599 232 : if (!HeapTupleIsValid(childtuple))
14600 0 : ereport(ERROR,
14601 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14602 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14603 : colName, RelationGetRelationName(childrel))));
14604 232 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14605 :
14606 232 : if (childattTup->attinhcount > numparents)
14607 6 : ereport(ERROR,
14608 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14609 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14610 : colName, RelationGetRelationName(childrel))));
14611 :
14612 226 : ReleaseSysCache(childtuple);
14613 :
14614 : /*
14615 : * Remap the attribute numbers. If no USING expression was
14616 : * specified, there is no need for this step.
14617 : */
14618 226 : if (def->cooked_default)
14619 : {
14620 : AttrMap *attmap;
14621 : bool found_whole_row;
14622 :
14623 : /* create a copy to scribble on */
14624 78 : cmd = copyObject(cmd);
14625 :
14626 78 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14627 : RelationGetDescr(rel),
14628 : false);
14629 156 : ((ColumnDef *) cmd->def)->cooked_default =
14630 78 : map_variable_attnos(def->cooked_default,
14631 : 1, 0,
14632 : attmap,
14633 : InvalidOid, &found_whole_row);
14634 78 : if (found_whole_row)
14635 6 : ereport(ERROR,
14636 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14637 : errmsg("cannot convert whole-row table reference"),
14638 : errdetail("USING expression contains a whole-row table reference.")));
14639 72 : pfree(attmap);
14640 : }
14641 220 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14642 208 : relation_close(childrel, NoLock);
14643 : }
14644 : }
14645 308 : else if (!recursing &&
14646 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
14647 0 : ereport(ERROR,
14648 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14649 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
14650 : colName)));
14651 :
14652 1234 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14653 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14654 1228 : }
14655 :
14656 : /*
14657 : * When the data type of a column is changed, a rewrite might not be required
14658 : * if the new type is sufficiently identical to the old one, and the USING
14659 : * clause isn't trying to insert some other value. It's safe to skip the
14660 : * rewrite in these cases:
14661 : *
14662 : * - the old type is binary coercible to the new type
14663 : * - the new type is an unconstrained domain over the old type
14664 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14665 : *
14666 : * In the case of a constrained domain, we could get by with scanning the
14667 : * table and checking the constraint rather than actually rewriting it, but we
14668 : * don't currently try to do that.
14669 : */
14670 : static bool
14671 1146 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
14672 : {
14673 : Assert(expr != NULL);
14674 :
14675 : for (;;)
14676 : {
14677 : /* only one varno, so no need to check that */
14678 1264 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14679 202 : return false;
14680 1062 : else if (IsA(expr, RelabelType))
14681 106 : expr = (Node *) ((RelabelType *) expr)->arg;
14682 956 : else if (IsA(expr, CoerceToDomain))
14683 : {
14684 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
14685 :
14686 0 : if (DomainHasConstraints(d->resulttype))
14687 0 : return true;
14688 0 : expr = (Node *) d->arg;
14689 : }
14690 956 : else if (IsA(expr, FuncExpr))
14691 : {
14692 750 : FuncExpr *f = (FuncExpr *) expr;
14693 :
14694 750 : switch (f->funcid)
14695 : {
14696 18 : case F_TIMESTAMPTZ_TIMESTAMP:
14697 : case F_TIMESTAMP_TIMESTAMPTZ:
14698 18 : if (TimestampTimestampTzRequiresRewrite())
14699 6 : return true;
14700 : else
14701 12 : expr = linitial(f->args);
14702 12 : break;
14703 732 : default:
14704 732 : return true;
14705 : }
14706 : }
14707 : else
14708 206 : return true;
14709 : }
14710 : }
14711 :
14712 : /*
14713 : * ALTER COLUMN .. SET DATA TYPE
14714 : *
14715 : * Return the address of the modified column.
14716 : */
14717 : static ObjectAddress
14718 1192 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
14719 : AlterTableCmd *cmd, LOCKMODE lockmode)
14720 : {
14721 1192 : char *colName = cmd->name;
14722 1192 : ColumnDef *def = (ColumnDef *) cmd->def;
14723 1192 : TypeName *typeName = def->typeName;
14724 : HeapTuple heapTup;
14725 : Form_pg_attribute attTup,
14726 : attOldTup;
14727 : AttrNumber attnum;
14728 : HeapTuple typeTuple;
14729 : Form_pg_type tform;
14730 : Oid targettype;
14731 : int32 targettypmod;
14732 : Oid targetcollid;
14733 : Node *defaultexpr;
14734 : Relation attrelation;
14735 : Relation depRel;
14736 : ScanKeyData key[3];
14737 : SysScanDesc scan;
14738 : HeapTuple depTup;
14739 : ObjectAddress address;
14740 :
14741 : /*
14742 : * Clear all the missing values if we're rewriting the table, since this
14743 : * renders them pointless.
14744 : */
14745 1192 : if (tab->rewrite)
14746 : {
14747 : Relation newrel;
14748 :
14749 884 : newrel = table_open(RelationGetRelid(rel), NoLock);
14750 884 : RelationClearMissing(newrel);
14751 884 : relation_close(newrel, NoLock);
14752 : /* make sure we don't conflict with later attribute modifications */
14753 884 : CommandCounterIncrement();
14754 : }
14755 :
14756 1192 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14757 :
14758 : /* Look up the target column */
14759 1192 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14760 1192 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14761 0 : ereport(ERROR,
14762 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14763 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14764 : colName, RelationGetRelationName(rel))));
14765 1192 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14766 1192 : attnum = attTup->attnum;
14767 1192 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14768 :
14769 : /* Check for multiple ALTER TYPE on same column --- can't cope */
14770 1192 : if (attTup->atttypid != attOldTup->atttypid ||
14771 1192 : attTup->atttypmod != attOldTup->atttypmod)
14772 0 : ereport(ERROR,
14773 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14774 : errmsg("cannot alter type of column \"%s\" twice",
14775 : colName)));
14776 :
14777 : /* Look up the target type (should not fail, since prep found it) */
14778 1192 : typeTuple = typenameType(NULL, typeName, &targettypmod);
14779 1192 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
14780 1192 : targettype = tform->oid;
14781 : /* And the collation */
14782 1192 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
14783 :
14784 : /*
14785 : * If there is a default expression for the column, get it and ensure we
14786 : * can coerce it to the new datatype. (We must do this before changing
14787 : * the column type, because build_column_default itself will try to
14788 : * coerce, and will not issue the error message we want if it fails.)
14789 : *
14790 : * We remove any implicit coercion steps at the top level of the old
14791 : * default expression; this has been agreed to satisfy the principle of
14792 : * least surprise. (The conversion to the new column type should act like
14793 : * it started from what the user sees as the stored expression, and the
14794 : * implicit coercions aren't going to be shown.)
14795 : */
14796 1192 : if (attTup->atthasdef)
14797 : {
14798 92 : defaultexpr = build_column_default(rel, attnum);
14799 : Assert(defaultexpr);
14800 92 : defaultexpr = strip_implicit_coercions(defaultexpr);
14801 92 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14802 : defaultexpr, exprType(defaultexpr),
14803 : targettype, targettypmod,
14804 : COERCION_ASSIGNMENT,
14805 : COERCE_IMPLICIT_CAST,
14806 : -1);
14807 92 : if (defaultexpr == NULL)
14808 : {
14809 6 : if (attTup->attgenerated)
14810 0 : ereport(ERROR,
14811 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14812 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14813 : colName, format_type_be(targettype))));
14814 : else
14815 6 : ereport(ERROR,
14816 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14817 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14818 : colName, format_type_be(targettype))));
14819 : }
14820 : }
14821 : else
14822 1100 : defaultexpr = NULL;
14823 :
14824 : /*
14825 : * Find everything that depends on the column (constraints, indexes, etc),
14826 : * and record enough information to let us recreate the objects.
14827 : *
14828 : * The actual recreation does not happen here, but only after we have
14829 : * performed all the individual ALTER TYPE operations. We have to save
14830 : * the info before executing ALTER TYPE, though, else the deparser will
14831 : * get confused.
14832 : */
14833 1186 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
14834 :
14835 : /*
14836 : * Now scan for dependencies of this column on other things. The only
14837 : * things we should find are the dependency on the column datatype and
14838 : * possibly a collation dependency. Those can be removed.
14839 : */
14840 1150 : depRel = table_open(DependRelationId, RowExclusiveLock);
14841 :
14842 1150 : ScanKeyInit(&key[0],
14843 : Anum_pg_depend_classid,
14844 : BTEqualStrategyNumber, F_OIDEQ,
14845 : ObjectIdGetDatum(RelationRelationId));
14846 1150 : ScanKeyInit(&key[1],
14847 : Anum_pg_depend_objid,
14848 : BTEqualStrategyNumber, F_OIDEQ,
14849 : ObjectIdGetDatum(RelationGetRelid(rel)));
14850 1150 : ScanKeyInit(&key[2],
14851 : Anum_pg_depend_objsubid,
14852 : BTEqualStrategyNumber, F_INT4EQ,
14853 : Int32GetDatum((int32) attnum));
14854 :
14855 1150 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
14856 : NULL, 3, key);
14857 :
14858 1154 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14859 : {
14860 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14861 : ObjectAddress foundObject;
14862 :
14863 4 : foundObject.classId = foundDep->refclassid;
14864 4 : foundObject.objectId = foundDep->refobjid;
14865 4 : foundObject.objectSubId = foundDep->refobjsubid;
14866 :
14867 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
14868 0 : elog(ERROR, "found unexpected dependency type '%c'",
14869 : foundDep->deptype);
14870 4 : if (!(foundDep->refclassid == TypeRelationId &&
14871 4 : foundDep->refobjid == attTup->atttypid) &&
14872 0 : !(foundDep->refclassid == CollationRelationId &&
14873 0 : foundDep->refobjid == attTup->attcollation))
14874 0 : elog(ERROR, "found unexpected dependency for column: %s",
14875 : getObjectDescription(&foundObject, false));
14876 :
14877 4 : CatalogTupleDelete(depRel, &depTup->t_self);
14878 : }
14879 :
14880 1150 : systable_endscan(scan);
14881 :
14882 1150 : table_close(depRel, RowExclusiveLock);
14883 :
14884 : /*
14885 : * Here we go --- change the recorded column type and collation. (Note
14886 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14887 : * fix up the missing value if any.
14888 : */
14889 1150 : if (attTup->atthasmissing)
14890 : {
14891 : Datum missingval;
14892 : bool missingNull;
14893 :
14894 : /* if rewrite is true the missing value should already be cleared */
14895 : Assert(tab->rewrite == 0);
14896 :
14897 : /* Get the missing value datum */
14898 6 : missingval = heap_getattr(heapTup,
14899 : Anum_pg_attribute_attmissingval,
14900 : attrelation->rd_att,
14901 : &missingNull);
14902 :
14903 : /* if it's a null array there is nothing to do */
14904 :
14905 6 : if (!missingNull)
14906 : {
14907 : /*
14908 : * Get the datum out of the array and repack it in a new array
14909 : * built with the new type data. We assume that since the table
14910 : * doesn't need rewriting, the actual Datum doesn't need to be
14911 : * changed, only the array metadata.
14912 : */
14913 :
14914 6 : int one = 1;
14915 : bool isNull;
14916 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
14917 6 : bool nullsAtt[Natts_pg_attribute] = {0};
14918 6 : bool replacesAtt[Natts_pg_attribute] = {0};
14919 : HeapTuple newTup;
14920 :
14921 12 : missingval = array_get_element(missingval,
14922 : 1,
14923 : &one,
14924 : 0,
14925 6 : attTup->attlen,
14926 6 : attTup->attbyval,
14927 6 : attTup->attalign,
14928 : &isNull);
14929 6 : missingval = PointerGetDatum(construct_array(&missingval,
14930 : 1,
14931 : targettype,
14932 6 : tform->typlen,
14933 6 : tform->typbyval,
14934 6 : tform->typalign));
14935 :
14936 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14937 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14938 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14939 :
14940 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14941 : valuesAtt, nullsAtt, replacesAtt);
14942 6 : heap_freetuple(heapTup);
14943 6 : heapTup = newTup;
14944 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14945 : }
14946 : }
14947 :
14948 1150 : attTup->atttypid = targettype;
14949 1150 : attTup->atttypmod = targettypmod;
14950 1150 : attTup->attcollation = targetcollid;
14951 1150 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14952 0 : ereport(ERROR,
14953 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14954 : errmsg("too many array dimensions"));
14955 1150 : attTup->attndims = list_length(typeName->arrayBounds);
14956 1150 : attTup->attlen = tform->typlen;
14957 1150 : attTup->attbyval = tform->typbyval;
14958 1150 : attTup->attalign = tform->typalign;
14959 1150 : attTup->attstorage = tform->typstorage;
14960 1150 : attTup->attcompression = InvalidCompressionMethod;
14961 :
14962 1150 : ReleaseSysCache(typeTuple);
14963 :
14964 1150 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14965 :
14966 1150 : table_close(attrelation, RowExclusiveLock);
14967 :
14968 : /* Install dependencies on new datatype and collation */
14969 1150 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
14970 1150 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
14971 :
14972 : /*
14973 : * Drop any pg_statistic entry for the column, since it's now wrong type
14974 : */
14975 1150 : RemoveStatistics(RelationGetRelid(rel), attnum);
14976 :
14977 1150 : InvokeObjectPostAlterHook(RelationRelationId,
14978 : RelationGetRelid(rel), attnum);
14979 :
14980 : /*
14981 : * Update the default, if present, by brute force --- remove and re-add
14982 : * the default. Probably unsafe to take shortcuts, since the new version
14983 : * may well have additional dependencies. (It's okay to do this now,
14984 : * rather than after other ALTER TYPE commands, since the default won't
14985 : * depend on other column types.)
14986 : */
14987 1150 : if (defaultexpr)
14988 : {
14989 : /*
14990 : * If it's a GENERATED default, drop its dependency records, in
14991 : * particular its INTERNAL dependency on the column, which would
14992 : * otherwise cause dependency.c to refuse to perform the deletion.
14993 : */
14994 86 : if (attTup->attgenerated)
14995 : {
14996 36 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14997 :
14998 36 : if (!OidIsValid(attrdefoid))
14999 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15000 : RelationGetRelid(rel), attnum);
15001 36 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
15002 : }
15003 :
15004 : /*
15005 : * Make updates-so-far visible, particularly the new pg_attribute row
15006 : * which will be updated again.
15007 : */
15008 86 : CommandCounterIncrement();
15009 :
15010 : /*
15011 : * We use RESTRICT here for safety, but at present we do not expect
15012 : * anything to depend on the default.
15013 : */
15014 86 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
15015 : true);
15016 :
15017 86 : (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15018 : }
15019 :
15020 1150 : ObjectAddressSubSet(address, RelationRelationId,
15021 : RelationGetRelid(rel), attnum);
15022 :
15023 : /* Cleanup */
15024 1150 : heap_freetuple(heapTup);
15025 :
15026 1150 : return address;
15027 : }
15028 :
15029 : /*
15030 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
15031 : * that depends on the column (constraints, indexes, etc), and record enough
15032 : * information to let us recreate the objects.
15033 : */
15034 : static void
15035 1288 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
15036 : Relation rel, AttrNumber attnum, const char *colName)
15037 : {
15038 : Relation depRel;
15039 : ScanKeyData key[3];
15040 : SysScanDesc scan;
15041 : HeapTuple depTup;
15042 :
15043 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15044 :
15045 1288 : depRel = table_open(DependRelationId, RowExclusiveLock);
15046 :
15047 1288 : ScanKeyInit(&key[0],
15048 : Anum_pg_depend_refclassid,
15049 : BTEqualStrategyNumber, F_OIDEQ,
15050 : ObjectIdGetDatum(RelationRelationId));
15051 1288 : ScanKeyInit(&key[1],
15052 : Anum_pg_depend_refobjid,
15053 : BTEqualStrategyNumber, F_OIDEQ,
15054 : ObjectIdGetDatum(RelationGetRelid(rel)));
15055 1288 : ScanKeyInit(&key[2],
15056 : Anum_pg_depend_refobjsubid,
15057 : BTEqualStrategyNumber, F_INT4EQ,
15058 : Int32GetDatum((int32) attnum));
15059 :
15060 1288 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15061 : NULL, 3, key);
15062 :
15063 2522 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15064 : {
15065 1270 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15066 : ObjectAddress foundObject;
15067 :
15068 1270 : foundObject.classId = foundDep->classid;
15069 1270 : foundObject.objectId = foundDep->objid;
15070 1270 : foundObject.objectSubId = foundDep->objsubid;
15071 :
15072 1270 : switch (foundObject.classId)
15073 : {
15074 286 : case RelationRelationId:
15075 : {
15076 286 : char relKind = get_rel_relkind(foundObject.objectId);
15077 :
15078 286 : if (relKind == RELKIND_INDEX ||
15079 : relKind == RELKIND_PARTITIONED_INDEX)
15080 : {
15081 : Assert(foundObject.objectSubId == 0);
15082 248 : RememberIndexForRebuilding(foundObject.objectId, tab);
15083 : }
15084 38 : else if (relKind == RELKIND_SEQUENCE)
15085 : {
15086 : /*
15087 : * This must be a SERIAL column's sequence. We need
15088 : * not do anything to it.
15089 : */
15090 : Assert(foundObject.objectSubId == 0);
15091 : }
15092 : else
15093 : {
15094 : /* Not expecting any other direct dependencies... */
15095 0 : elog(ERROR, "unexpected object depending on column: %s",
15096 : getObjectDescription(&foundObject, false));
15097 : }
15098 286 : break;
15099 : }
15100 :
15101 686 : case ConstraintRelationId:
15102 : Assert(foundObject.objectSubId == 0);
15103 686 : RememberConstraintForRebuilding(foundObject.objectId, tab);
15104 686 : break;
15105 :
15106 0 : case ProcedureRelationId:
15107 :
15108 : /*
15109 : * A new-style SQL function can depend on a column, if that
15110 : * column is referenced in the parsed function body. Ideally
15111 : * we'd automatically update the function by deparsing and
15112 : * reparsing it, but that's risky and might well fail anyhow.
15113 : * FIXME someday.
15114 : *
15115 : * This is only a problem for AT_AlterColumnType, not
15116 : * AT_SetExpression.
15117 : */
15118 0 : if (subtype == AT_AlterColumnType)
15119 0 : ereport(ERROR,
15120 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15121 : errmsg("cannot alter type of a column used by a function or procedure"),
15122 : errdetail("%s depends on column \"%s\"",
15123 : getObjectDescription(&foundObject, false),
15124 : colName)));
15125 0 : break;
15126 :
15127 12 : case RewriteRelationId:
15128 :
15129 : /*
15130 : * View/rule bodies have pretty much the same issues as
15131 : * function bodies. FIXME someday.
15132 : */
15133 12 : if (subtype == AT_AlterColumnType)
15134 12 : ereport(ERROR,
15135 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15136 : errmsg("cannot alter type of a column used by a view or rule"),
15137 : errdetail("%s depends on column \"%s\"",
15138 : getObjectDescription(&foundObject, false),
15139 : colName)));
15140 0 : break;
15141 :
15142 0 : case TriggerRelationId:
15143 :
15144 : /*
15145 : * A trigger can depend on a column because the column is
15146 : * specified as an update target, or because the column is
15147 : * used in the trigger's WHEN condition. The first case would
15148 : * not require any extra work, but the second case would
15149 : * require updating the WHEN expression, which has the same
15150 : * issues as above. Since we can't easily tell which case
15151 : * applies, we punt for both. FIXME someday.
15152 : */
15153 0 : if (subtype == AT_AlterColumnType)
15154 0 : ereport(ERROR,
15155 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15156 : errmsg("cannot alter type of a column used in a trigger definition"),
15157 : errdetail("%s depends on column \"%s\"",
15158 : getObjectDescription(&foundObject, false),
15159 : colName)));
15160 0 : break;
15161 :
15162 0 : case PolicyRelationId:
15163 :
15164 : /*
15165 : * A policy can depend on a column because the column is
15166 : * specified in the policy's USING or WITH CHECK qual
15167 : * expressions. It might be possible to rewrite and recheck
15168 : * the policy expression, but punt for now. It's certainly
15169 : * easy enough to remove and recreate the policy; still, FIXME
15170 : * someday.
15171 : */
15172 0 : if (subtype == AT_AlterColumnType)
15173 0 : ereport(ERROR,
15174 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15175 : errmsg("cannot alter type of a column used in a policy definition"),
15176 : errdetail("%s depends on column \"%s\"",
15177 : getObjectDescription(&foundObject, false),
15178 : colName)));
15179 0 : break;
15180 :
15181 212 : case AttrDefaultRelationId:
15182 : {
15183 212 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
15184 :
15185 212 : if (col.objectId == RelationGetRelid(rel) &&
15186 212 : col.objectSubId == attnum)
15187 : {
15188 : /*
15189 : * Ignore the column's own default expression. The
15190 : * caller deals with it.
15191 : */
15192 : }
15193 : else
15194 : {
15195 : /*
15196 : * This must be a reference from the expression of a
15197 : * generated column elsewhere in the same table.
15198 : * Changing the type/generated expression of a column
15199 : * that is used by a generated column is not allowed
15200 : * by SQL standard, so just punt for now. It might be
15201 : * doable with some thinking and effort.
15202 : */
15203 24 : if (subtype == AT_AlterColumnType)
15204 24 : ereport(ERROR,
15205 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15206 : errmsg("cannot alter type of a column used by a generated column"),
15207 : errdetail("Column \"%s\" is used by generated column \"%s\".",
15208 : colName,
15209 : get_attname(col.objectId,
15210 : col.objectSubId,
15211 : false))));
15212 : }
15213 188 : break;
15214 : }
15215 :
15216 74 : case StatisticExtRelationId:
15217 :
15218 : /*
15219 : * Give the extended-stats machinery a chance to fix anything
15220 : * that this column type change would break.
15221 : */
15222 74 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
15223 74 : break;
15224 :
15225 0 : case PublicationRelRelationId:
15226 :
15227 : /*
15228 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15229 : * clause. Same issues as above. FIXME someday.
15230 : */
15231 0 : if (subtype == AT_AlterColumnType)
15232 0 : ereport(ERROR,
15233 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15234 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
15235 : errdetail("%s depends on column \"%s\"",
15236 : getObjectDescription(&foundObject, false),
15237 : colName)));
15238 0 : break;
15239 :
15240 0 : default:
15241 :
15242 : /*
15243 : * We don't expect any other sorts of objects to depend on a
15244 : * column.
15245 : */
15246 0 : elog(ERROR, "unexpected object depending on column: %s",
15247 : getObjectDescription(&foundObject, false));
15248 : break;
15249 : }
15250 : }
15251 :
15252 1252 : systable_endscan(scan);
15253 1252 : table_close(depRel, NoLock);
15254 1252 : }
15255 :
15256 : /*
15257 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
15258 : * needs to be reset.
15259 : */
15260 : static void
15261 456 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
15262 : {
15263 456 : if (!get_index_isreplident(indoid))
15264 438 : return;
15265 :
15266 18 : if (tab->replicaIdentityIndex)
15267 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15268 :
15269 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
15270 : }
15271 :
15272 : /*
15273 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
15274 : */
15275 : static void
15276 456 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
15277 : {
15278 456 : if (!get_index_isclustered(indoid))
15279 438 : return;
15280 :
15281 18 : if (tab->clusterOnIndex)
15282 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15283 :
15284 18 : tab->clusterOnIndex = get_rel_name(indoid);
15285 : }
15286 :
15287 : /*
15288 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15289 : * to be rebuilt (which we might already know).
15290 : */
15291 : static void
15292 698 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
15293 : {
15294 : /*
15295 : * This de-duplication check is critical for two independent reasons: we
15296 : * mustn't try to recreate the same constraint twice, and if a constraint
15297 : * depends on more than one column whose type is to be altered, we must
15298 : * capture its definition string before applying any of the column type
15299 : * changes. ruleutils.c will get confused if we ask again later.
15300 : */
15301 698 : if (!list_member_oid(tab->changedConstraintOids, conoid))
15302 : {
15303 : /* OK, capture the constraint's existing definition string */
15304 608 : char *defstring = pg_get_constraintdef_command(conoid);
15305 : Oid indoid;
15306 :
15307 : /*
15308 : * It is critical to create not-null constraints ahead of primary key
15309 : * indexes; otherwise, the not-null constraint would be created by the
15310 : * primary key, and the constraint name would be wrong.
15311 : */
15312 608 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15313 : {
15314 198 : tab->changedConstraintOids = lcons_oid(conoid,
15315 : tab->changedConstraintOids);
15316 198 : tab->changedConstraintDefs = lcons(defstring,
15317 : tab->changedConstraintDefs);
15318 : }
15319 : else
15320 : {
15321 :
15322 410 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
15323 : conoid);
15324 410 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
15325 : defstring);
15326 : }
15327 :
15328 : /*
15329 : * For the index of a constraint, if any, remember if it is used for
15330 : * the table's replica identity or if it is a clustered index, so that
15331 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15332 : * those properties.
15333 : */
15334 608 : indoid = get_constraint_index(conoid);
15335 608 : if (OidIsValid(indoid))
15336 : {
15337 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
15338 228 : RememberClusterOnForRebuilding(indoid, tab);
15339 : }
15340 : }
15341 698 : }
15342 :
15343 : /*
15344 : * Subroutine for ATExecAlterColumnType: remember that an index needs
15345 : * to be rebuilt (which we might already know).
15346 : */
15347 : static void
15348 248 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
15349 : {
15350 : /*
15351 : * This de-duplication check is critical for two independent reasons: we
15352 : * mustn't try to recreate the same index twice, and if an index depends
15353 : * on more than one column whose type is to be altered, we must capture
15354 : * its definition string before applying any of the column type changes.
15355 : * ruleutils.c will get confused if we ask again later.
15356 : */
15357 248 : if (!list_member_oid(tab->changedIndexOids, indoid))
15358 : {
15359 : /*
15360 : * Before adding it as an index-to-rebuild, we'd better see if it
15361 : * belongs to a constraint, and if so rebuild the constraint instead.
15362 : * Typically this check fails, because constraint indexes normally
15363 : * have only dependencies on their constraint. But it's possible for
15364 : * such an index to also have direct dependencies on table columns,
15365 : * for example with a partial exclusion constraint.
15366 : */
15367 240 : Oid conoid = get_index_constraint(indoid);
15368 :
15369 240 : if (OidIsValid(conoid))
15370 : {
15371 12 : RememberConstraintForRebuilding(conoid, tab);
15372 : }
15373 : else
15374 : {
15375 : /* OK, capture the index's existing definition string */
15376 228 : char *defstring = pg_get_indexdef_string(indoid);
15377 :
15378 228 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
15379 : indoid);
15380 228 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
15381 : defstring);
15382 :
15383 : /*
15384 : * Remember if this index is used for the table's replica identity
15385 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15386 : * can queue up commands necessary to restore those properties.
15387 : */
15388 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
15389 228 : RememberClusterOnForRebuilding(indoid, tab);
15390 : }
15391 : }
15392 248 : }
15393 :
15394 : /*
15395 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
15396 : * needs to be rebuilt (which we might already know).
15397 : */
15398 : static void
15399 74 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
15400 : {
15401 : /*
15402 : * This de-duplication check is critical for two independent reasons: we
15403 : * mustn't try to recreate the same statistics object twice, and if the
15404 : * statistics object depends on more than one column whose type is to be
15405 : * altered, we must capture its definition string before applying any of
15406 : * the type changes. ruleutils.c will get confused if we ask again later.
15407 : */
15408 74 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15409 : {
15410 : /* OK, capture the statistics object's existing definition string */
15411 74 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
15412 :
15413 74 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
15414 : stxoid);
15415 74 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
15416 : defstring);
15417 : }
15418 74 : }
15419 :
15420 : /*
15421 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15422 : * operations for a particular relation. We have to drop and recreate all the
15423 : * indexes and constraints that depend on the altered columns. We do the
15424 : * actual dropping here, but re-creation is managed by adding work queue
15425 : * entries to do those steps later.
15426 : */
15427 : static void
15428 1300 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
15429 : {
15430 : ObjectAddress obj;
15431 : ObjectAddresses *objects;
15432 : ListCell *def_item;
15433 : ListCell *oid_item;
15434 :
15435 : /*
15436 : * Collect all the constraints and indexes to drop so we can process them
15437 : * in a single call. That way we don't have to worry about dependencies
15438 : * among them.
15439 : */
15440 1300 : objects = new_object_addresses();
15441 :
15442 : /*
15443 : * Re-parse the index and constraint definitions, and attach them to the
15444 : * appropriate work queue entries. We do this before dropping because in
15445 : * the case of a constraint on another table, we might not yet have
15446 : * exclusive lock on the table the constraint is attached to, and we need
15447 : * to get that before reparsing/dropping. (That's possible at least for
15448 : * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15449 : * requires a dependency on the target table's composite type in the other
15450 : * table's constraint expressions.)
15451 : *
15452 : * We can't rely on the output of deparsing to tell us which relation to
15453 : * operate on, because concurrent activity might have made the name
15454 : * resolve differently. Instead, we've got to use the OID of the
15455 : * constraint or index we're processing to figure out which relation to
15456 : * operate on.
15457 : */
15458 1908 : forboth(oid_item, tab->changedConstraintOids,
15459 : def_item, tab->changedConstraintDefs)
15460 : {
15461 608 : Oid oldId = lfirst_oid(oid_item);
15462 : HeapTuple tup;
15463 : Form_pg_constraint con;
15464 : Oid relid;
15465 : Oid confrelid;
15466 : bool conislocal;
15467 :
15468 608 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15469 608 : if (!HeapTupleIsValid(tup)) /* should not happen */
15470 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15471 608 : con = (Form_pg_constraint) GETSTRUCT(tup);
15472 608 : if (OidIsValid(con->conrelid))
15473 594 : relid = con->conrelid;
15474 : else
15475 : {
15476 : /* must be a domain constraint */
15477 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
15478 14 : if (!OidIsValid(relid))
15479 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15480 : }
15481 608 : confrelid = con->confrelid;
15482 608 : conislocal = con->conislocal;
15483 608 : ReleaseSysCache(tup);
15484 :
15485 608 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
15486 608 : add_exact_object_address(&obj, objects);
15487 :
15488 : /*
15489 : * If the constraint is inherited (only), we don't want to inject a
15490 : * new definition here; it'll get recreated when
15491 : * ATAddCheckNNConstraint recurses from adding the parent table's
15492 : * constraint. But we had to carry the info this far so that we can
15493 : * drop the constraint below.
15494 : */
15495 608 : if (!conislocal)
15496 28 : continue;
15497 :
15498 : /*
15499 : * When rebuilding another table's constraint that references the
15500 : * table we're modifying, we might not yet have any lock on the other
15501 : * table, so get one now. We'll need AccessExclusiveLock for the DROP
15502 : * CONSTRAINT step, so there's no value in asking for anything weaker.
15503 : */
15504 580 : if (relid != tab->relid)
15505 48 : LockRelationOid(relid, AccessExclusiveLock);
15506 :
15507 580 : ATPostAlterTypeParse(oldId, relid, confrelid,
15508 580 : (char *) lfirst(def_item),
15509 580 : wqueue, lockmode, tab->rewrite);
15510 : }
15511 1528 : forboth(oid_item, tab->changedIndexOids,
15512 : def_item, tab->changedIndexDefs)
15513 : {
15514 228 : Oid oldId = lfirst_oid(oid_item);
15515 : Oid relid;
15516 :
15517 228 : relid = IndexGetRelation(oldId, false);
15518 :
15519 : /*
15520 : * As above, make sure we have lock on the index's table if it's not
15521 : * the same table.
15522 : */
15523 228 : if (relid != tab->relid)
15524 12 : LockRelationOid(relid, AccessExclusiveLock);
15525 :
15526 228 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15527 228 : (char *) lfirst(def_item),
15528 228 : wqueue, lockmode, tab->rewrite);
15529 :
15530 228 : ObjectAddressSet(obj, RelationRelationId, oldId);
15531 228 : add_exact_object_address(&obj, objects);
15532 : }
15533 :
15534 : /* add dependencies for new statistics */
15535 1374 : forboth(oid_item, tab->changedStatisticsOids,
15536 : def_item, tab->changedStatisticsDefs)
15537 : {
15538 74 : Oid oldId = lfirst_oid(oid_item);
15539 : Oid relid;
15540 :
15541 74 : relid = StatisticsGetRelation(oldId, false);
15542 :
15543 : /*
15544 : * As above, make sure we have lock on the statistics object's table
15545 : * if it's not the same table. However, we take
15546 : * ShareUpdateExclusiveLock here, aligning with the lock level used in
15547 : * CreateStatistics and RemoveStatisticsById.
15548 : *
15549 : * CAUTION: this should be done after all cases that grab
15550 : * AccessExclusiveLock, else we risk causing deadlock due to needing
15551 : * to promote our table lock.
15552 : */
15553 74 : if (relid != tab->relid)
15554 12 : LockRelationOid(relid, ShareUpdateExclusiveLock);
15555 :
15556 74 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15557 74 : (char *) lfirst(def_item),
15558 74 : wqueue, lockmode, tab->rewrite);
15559 :
15560 74 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15561 74 : add_exact_object_address(&obj, objects);
15562 : }
15563 :
15564 : /*
15565 : * Queue up command to restore replica identity index marking
15566 : */
15567 1300 : if (tab->replicaIdentityIndex)
15568 : {
15569 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15570 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
15571 :
15572 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15573 18 : subcmd->name = tab->replicaIdentityIndex;
15574 18 : cmd->subtype = AT_ReplicaIdentity;
15575 18 : cmd->def = (Node *) subcmd;
15576 :
15577 : /* do it after indexes and constraints */
15578 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15579 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15580 : }
15581 :
15582 : /*
15583 : * Queue up command to restore marking of index used for cluster.
15584 : */
15585 1300 : if (tab->clusterOnIndex)
15586 : {
15587 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15588 :
15589 18 : cmd->subtype = AT_ClusterOn;
15590 18 : cmd->name = tab->clusterOnIndex;
15591 :
15592 : /* do it after indexes and constraints */
15593 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15594 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15595 : }
15596 :
15597 : /*
15598 : * It should be okay to use DROP_RESTRICT here, since nothing else should
15599 : * be depending on these objects.
15600 : */
15601 1300 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
15602 :
15603 1300 : free_object_addresses(objects);
15604 :
15605 : /*
15606 : * The objects will get recreated during subsequent passes over the work
15607 : * queue.
15608 : */
15609 1300 : }
15610 :
15611 : /*
15612 : * Parse the previously-saved definition string for a constraint, index or
15613 : * statistics object against the newly-established column data type(s), and
15614 : * queue up the resulting command parsetrees for execution.
15615 : *
15616 : * This might fail if, for example, you have a WHERE clause that uses an
15617 : * operator that's not available for the new column type.
15618 : */
15619 : static void
15620 882 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15621 : List **wqueue, LOCKMODE lockmode, bool rewrite)
15622 : {
15623 : List *raw_parsetree_list;
15624 : List *querytree_list;
15625 : ListCell *list_item;
15626 : Relation rel;
15627 :
15628 : /*
15629 : * We expect that we will get only ALTER TABLE and CREATE INDEX
15630 : * statements. Hence, there is no need to pass them through
15631 : * parse_analyze_*() or the rewriter, but instead we need to pass them
15632 : * through parse_utilcmd.c to make them ready for execution.
15633 : */
15634 882 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15635 882 : querytree_list = NIL;
15636 1764 : foreach(list_item, raw_parsetree_list)
15637 : {
15638 882 : RawStmt *rs = lfirst_node(RawStmt, list_item);
15639 882 : Node *stmt = rs->stmt;
15640 :
15641 882 : if (IsA(stmt, IndexStmt))
15642 228 : querytree_list = lappend(querytree_list,
15643 228 : transformIndexStmt(oldRelId,
15644 : (IndexStmt *) stmt,
15645 : cmd));
15646 654 : else if (IsA(stmt, AlterTableStmt))
15647 : {
15648 : List *beforeStmts;
15649 : List *afterStmts;
15650 :
15651 566 : stmt = (Node *) transformAlterTableStmt(oldRelId,
15652 : (AlterTableStmt *) stmt,
15653 : cmd,
15654 : &beforeStmts,
15655 : &afterStmts);
15656 566 : querytree_list = list_concat(querytree_list, beforeStmts);
15657 566 : querytree_list = lappend(querytree_list, stmt);
15658 566 : querytree_list = list_concat(querytree_list, afterStmts);
15659 : }
15660 88 : else if (IsA(stmt, CreateStatsStmt))
15661 74 : querytree_list = lappend(querytree_list,
15662 74 : transformStatsStmt(oldRelId,
15663 : (CreateStatsStmt *) stmt,
15664 : cmd));
15665 : else
15666 14 : querytree_list = lappend(querytree_list, stmt);
15667 : }
15668 :
15669 : /* Caller should already have acquired whatever lock we need. */
15670 882 : rel = relation_open(oldRelId, NoLock);
15671 :
15672 : /*
15673 : * Attach each generated command to the proper place in the work queue.
15674 : * Note this could result in creation of entirely new work-queue entries.
15675 : *
15676 : * Also note that we have to tweak the command subtypes, because it turns
15677 : * out that re-creation of indexes and constraints has to act a bit
15678 : * differently from initial creation.
15679 : */
15680 1764 : foreach(list_item, querytree_list)
15681 : {
15682 882 : Node *stm = (Node *) lfirst(list_item);
15683 : AlteredTableInfo *tab;
15684 :
15685 882 : tab = ATGetQueueEntry(wqueue, rel);
15686 :
15687 882 : if (IsA(stm, IndexStmt))
15688 : {
15689 228 : IndexStmt *stmt = (IndexStmt *) stm;
15690 : AlterTableCmd *newcmd;
15691 :
15692 228 : if (!rewrite)
15693 56 : TryReuseIndex(oldId, stmt);
15694 228 : stmt->reset_default_tblspc = true;
15695 : /* keep the index's comment */
15696 228 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15697 :
15698 228 : newcmd = makeNode(AlterTableCmd);
15699 228 : newcmd->subtype = AT_ReAddIndex;
15700 228 : newcmd->def = (Node *) stmt;
15701 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
15702 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15703 : }
15704 654 : else if (IsA(stm, AlterTableStmt))
15705 : {
15706 566 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
15707 : ListCell *lcmd;
15708 :
15709 1132 : foreach(lcmd, stmt->cmds)
15710 : {
15711 566 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
15712 :
15713 566 : if (cmd->subtype == AT_AddIndex)
15714 : {
15715 : IndexStmt *indstmt;
15716 : Oid indoid;
15717 :
15718 228 : indstmt = castNode(IndexStmt, cmd->def);
15719 228 : indoid = get_constraint_index(oldId);
15720 :
15721 228 : if (!rewrite)
15722 48 : TryReuseIndex(indoid, indstmt);
15723 : /* keep any comment on the index */
15724 228 : indstmt->idxcomment = GetComment(indoid,
15725 : RelationRelationId, 0);
15726 228 : indstmt->reset_default_tblspc = true;
15727 :
15728 228 : cmd->subtype = AT_ReAddIndex;
15729 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
15730 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15731 :
15732 : /* recreate any comment on the constraint */
15733 228 : RebuildConstraintComment(tab,
15734 : AT_PASS_OLD_INDEX,
15735 : oldId,
15736 : rel,
15737 : NIL,
15738 228 : indstmt->idxname);
15739 : }
15740 338 : else if (cmd->subtype == AT_AddConstraint)
15741 : {
15742 338 : Constraint *con = castNode(Constraint, cmd->def);
15743 :
15744 338 : con->old_pktable_oid = refRelId;
15745 : /* rewriting neither side of a FK */
15746 338 : if (con->contype == CONSTR_FOREIGN &&
15747 72 : !rewrite && tab->rewrite == 0)
15748 6 : TryReuseForeignKey(oldId, con);
15749 338 : con->reset_default_tblspc = true;
15750 338 : cmd->subtype = AT_ReAddConstraint;
15751 338 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15752 338 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15753 :
15754 : /*
15755 : * Recreate any comment on the constraint. If we have
15756 : * recreated a primary key, then transformTableConstraint
15757 : * has added an unnamed not-null constraint here; skip
15758 : * this in that case.
15759 : */
15760 338 : if (con->conname)
15761 338 : RebuildConstraintComment(tab,
15762 : AT_PASS_OLD_CONSTR,
15763 : oldId,
15764 : rel,
15765 : NIL,
15766 338 : con->conname);
15767 : else
15768 : Assert(con->contype == CONSTR_NOTNULL);
15769 : }
15770 : else
15771 0 : elog(ERROR, "unexpected statement subtype: %d",
15772 : (int) cmd->subtype);
15773 : }
15774 : }
15775 88 : else if (IsA(stm, AlterDomainStmt))
15776 : {
15777 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
15778 :
15779 14 : if (stmt->subtype == AD_AddConstraint)
15780 : {
15781 14 : Constraint *con = castNode(Constraint, stmt->def);
15782 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15783 :
15784 14 : cmd->subtype = AT_ReAddDomainConstraint;
15785 14 : cmd->def = (Node *) stmt;
15786 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15787 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15788 :
15789 : /* recreate any comment on the constraint */
15790 14 : RebuildConstraintComment(tab,
15791 : AT_PASS_OLD_CONSTR,
15792 : oldId,
15793 : NULL,
15794 : stmt->typeName,
15795 14 : con->conname);
15796 : }
15797 : else
15798 0 : elog(ERROR, "unexpected statement subtype: %d",
15799 : (int) stmt->subtype);
15800 : }
15801 74 : else if (IsA(stm, CreateStatsStmt))
15802 : {
15803 74 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
15804 : AlterTableCmd *newcmd;
15805 :
15806 : /* keep the statistics object's comment */
15807 74 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15808 :
15809 74 : newcmd = makeNode(AlterTableCmd);
15810 74 : newcmd->subtype = AT_ReAddStatistics;
15811 74 : newcmd->def = (Node *) stmt;
15812 74 : tab->subcmds[AT_PASS_MISC] =
15813 74 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15814 : }
15815 : else
15816 0 : elog(ERROR, "unexpected statement type: %d",
15817 : (int) nodeTag(stm));
15818 : }
15819 :
15820 882 : relation_close(rel, NoLock);
15821 882 : }
15822 :
15823 : /*
15824 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15825 : * for a table or domain constraint that is being rebuilt.
15826 : *
15827 : * objid is the OID of the constraint.
15828 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15829 : * as a string list) for a domain constraint.
15830 : * (We could dig that info, as well as the conname, out of the pg_constraint
15831 : * entry; but callers already have them so might as well pass them.)
15832 : */
15833 : static void
15834 580 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
15835 : Relation rel, List *domname,
15836 : const char *conname)
15837 : {
15838 : CommentStmt *cmd;
15839 : char *comment_str;
15840 : AlterTableCmd *newcmd;
15841 :
15842 : /* Look for comment for object wanted, and leave if none */
15843 580 : comment_str = GetComment(objid, ConstraintRelationId, 0);
15844 580 : if (comment_str == NULL)
15845 490 : return;
15846 :
15847 : /* Build CommentStmt node, copying all input data for safety */
15848 90 : cmd = makeNode(CommentStmt);
15849 90 : if (rel)
15850 : {
15851 78 : cmd->objtype = OBJECT_TABCONSTRAINT;
15852 78 : cmd->object = (Node *)
15853 78 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
15854 : makeString(pstrdup(RelationGetRelationName(rel))),
15855 : makeString(pstrdup(conname)));
15856 : }
15857 : else
15858 : {
15859 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
15860 12 : cmd->object = (Node *)
15861 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
15862 : makeString(pstrdup(conname)));
15863 : }
15864 90 : cmd->comment = comment_str;
15865 :
15866 : /* Append it to list of commands */
15867 90 : newcmd = makeNode(AlterTableCmd);
15868 90 : newcmd->subtype = AT_ReAddComment;
15869 90 : newcmd->def = (Node *) cmd;
15870 90 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15871 : }
15872 :
15873 : /*
15874 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15875 : * for the real analysis, then mutates the IndexStmt based on that verdict.
15876 : */
15877 : static void
15878 104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
15879 : {
15880 104 : if (CheckIndexCompatible(oldId,
15881 104 : stmt->accessMethod,
15882 104 : stmt->indexParams,
15883 104 : stmt->excludeOpNames,
15884 104 : stmt->iswithoutoverlaps))
15885 : {
15886 104 : Relation irel = index_open(oldId, NoLock);
15887 :
15888 : /* If it's a partitioned index, there is no storage to share. */
15889 104 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15890 : {
15891 74 : stmt->oldNumber = irel->rd_locator.relNumber;
15892 74 : stmt->oldCreateSubid = irel->rd_createSubid;
15893 74 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15894 : }
15895 104 : index_close(irel, NoLock);
15896 : }
15897 104 : }
15898 :
15899 : /*
15900 : * Subroutine for ATPostAlterTypeParse().
15901 : *
15902 : * Stash the old P-F equality operator into the Constraint node, for possible
15903 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15904 : * this constraint can be skipped.
15905 : */
15906 : static void
15907 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
15908 : {
15909 : HeapTuple tup;
15910 : Datum adatum;
15911 : ArrayType *arr;
15912 : Oid *rawarr;
15913 : int numkeys;
15914 : int i;
15915 :
15916 : Assert(con->contype == CONSTR_FOREIGN);
15917 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15918 :
15919 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15920 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
15921 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15922 :
15923 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15924 : Anum_pg_constraint_conpfeqop);
15925 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15926 6 : numkeys = ARR_DIMS(arr)[0];
15927 : /* test follows the one in ri_FetchConstraintInfo() */
15928 6 : if (ARR_NDIM(arr) != 1 ||
15929 6 : ARR_HASNULL(arr) ||
15930 6 : ARR_ELEMTYPE(arr) != OIDOID)
15931 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
15932 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
15933 :
15934 : /* stash a List of the operator Oids in our Constraint node */
15935 12 : for (i = 0; i < numkeys; i++)
15936 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15937 :
15938 6 : ReleaseSysCache(tup);
15939 6 : }
15940 :
15941 : /*
15942 : * ALTER COLUMN .. OPTIONS ( ... )
15943 : *
15944 : * Returns the address of the modified column
15945 : */
15946 : static ObjectAddress
15947 172 : ATExecAlterColumnGenericOptions(Relation rel,
15948 : const char *colName,
15949 : List *options,
15950 : LOCKMODE lockmode)
15951 : {
15952 : Relation ftrel;
15953 : Relation attrel;
15954 : ForeignServer *server;
15955 : ForeignDataWrapper *fdw;
15956 : HeapTuple tuple;
15957 : HeapTuple newtuple;
15958 : bool isnull;
15959 : Datum repl_val[Natts_pg_attribute];
15960 : bool repl_null[Natts_pg_attribute];
15961 : bool repl_repl[Natts_pg_attribute];
15962 : Datum datum;
15963 : Form_pg_foreign_table fttableform;
15964 : Form_pg_attribute atttableform;
15965 : AttrNumber attnum;
15966 : ObjectAddress address;
15967 :
15968 172 : if (options == NIL)
15969 0 : return InvalidObjectAddress;
15970 :
15971 : /* First, determine FDW validator associated to the foreign table. */
15972 172 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15973 172 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15974 172 : if (!HeapTupleIsValid(tuple))
15975 0 : ereport(ERROR,
15976 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15977 : errmsg("foreign table \"%s\" does not exist",
15978 : RelationGetRelationName(rel))));
15979 172 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15980 172 : server = GetForeignServer(fttableform->ftserver);
15981 172 : fdw = GetForeignDataWrapper(server->fdwid);
15982 :
15983 172 : table_close(ftrel, AccessShareLock);
15984 172 : ReleaseSysCache(tuple);
15985 :
15986 172 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
15987 172 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15988 172 : if (!HeapTupleIsValid(tuple))
15989 0 : ereport(ERROR,
15990 : (errcode(ERRCODE_UNDEFINED_COLUMN),
15991 : errmsg("column \"%s\" of relation \"%s\" does not exist",
15992 : colName, RelationGetRelationName(rel))));
15993 :
15994 : /* Prevent them from altering a system attribute */
15995 172 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15996 172 : attnum = atttableform->attnum;
15997 172 : if (attnum <= 0)
15998 6 : ereport(ERROR,
15999 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16000 : errmsg("cannot alter system column \"%s\"", colName)));
16001 :
16002 :
16003 : /* Initialize buffers for new tuple values */
16004 166 : memset(repl_val, 0, sizeof(repl_val));
16005 166 : memset(repl_null, false, sizeof(repl_null));
16006 166 : memset(repl_repl, false, sizeof(repl_repl));
16007 :
16008 : /* Extract the current options */
16009 166 : datum = SysCacheGetAttr(ATTNAME,
16010 : tuple,
16011 : Anum_pg_attribute_attfdwoptions,
16012 : &isnull);
16013 166 : if (isnull)
16014 156 : datum = PointerGetDatum(NULL);
16015 :
16016 : /* Transform the options */
16017 166 : datum = transformGenericOptions(AttributeRelationId,
16018 : datum,
16019 : options,
16020 : fdw->fdwvalidator);
16021 :
16022 166 : if (DatumGetPointer(datum) != NULL)
16023 166 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16024 : else
16025 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16026 :
16027 166 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16028 :
16029 : /* Everything looks good - update the tuple */
16030 :
16031 166 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16032 : repl_val, repl_null, repl_repl);
16033 :
16034 166 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16035 :
16036 166 : InvokeObjectPostAlterHook(RelationRelationId,
16037 : RelationGetRelid(rel),
16038 : atttableform->attnum);
16039 166 : ObjectAddressSubSet(address, RelationRelationId,
16040 : RelationGetRelid(rel), attnum);
16041 :
16042 166 : ReleaseSysCache(tuple);
16043 :
16044 166 : table_close(attrel, RowExclusiveLock);
16045 :
16046 166 : heap_freetuple(newtuple);
16047 :
16048 166 : return address;
16049 : }
16050 :
16051 : /*
16052 : * ALTER TABLE OWNER
16053 : *
16054 : * recursing is true if we are recursing from a table to its indexes,
16055 : * sequences, or toast table. We don't allow the ownership of those things to
16056 : * be changed separately from the parent table. Also, we can skip permission
16057 : * checks (this is necessary not just an optimization, else we'd fail to
16058 : * handle toast tables properly).
16059 : *
16060 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
16061 : * free-standing composite type.
16062 : */
16063 : void
16064 2232 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
16065 : {
16066 : Relation target_rel;
16067 : Relation class_rel;
16068 : HeapTuple tuple;
16069 : Form_pg_class tuple_class;
16070 :
16071 : /*
16072 : * Get exclusive lock till end of transaction on the target table. Use
16073 : * relation_open so that we can work on indexes and sequences.
16074 : */
16075 2232 : target_rel = relation_open(relationOid, lockmode);
16076 :
16077 : /* Get its pg_class tuple, too */
16078 2232 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
16079 :
16080 2232 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16081 2232 : if (!HeapTupleIsValid(tuple))
16082 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
16083 2232 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16084 :
16085 : /* Can we change the ownership of this tuple? */
16086 2232 : switch (tuple_class->relkind)
16087 : {
16088 1948 : case RELKIND_RELATION:
16089 : case RELKIND_VIEW:
16090 : case RELKIND_MATVIEW:
16091 : case RELKIND_FOREIGN_TABLE:
16092 : case RELKIND_PARTITIONED_TABLE:
16093 : /* ok to change owner */
16094 1948 : break;
16095 96 : case RELKIND_INDEX:
16096 96 : if (!recursing)
16097 : {
16098 : /*
16099 : * Because ALTER INDEX OWNER used to be allowed, and in fact
16100 : * is generated by old versions of pg_dump, we give a warning
16101 : * and do nothing rather than erroring out. Also, to avoid
16102 : * unnecessary chatter while restoring those old dumps, say
16103 : * nothing at all if the command would be a no-op anyway.
16104 : */
16105 0 : if (tuple_class->relowner != newOwnerId)
16106 0 : ereport(WARNING,
16107 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16108 : errmsg("cannot change owner of index \"%s\"",
16109 : NameStr(tuple_class->relname)),
16110 : errhint("Change the ownership of the index's table instead.")));
16111 : /* quick hack to exit via the no-op path */
16112 0 : newOwnerId = tuple_class->relowner;
16113 : }
16114 96 : break;
16115 20 : case RELKIND_PARTITIONED_INDEX:
16116 20 : if (recursing)
16117 20 : break;
16118 0 : ereport(ERROR,
16119 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16120 : errmsg("cannot change owner of index \"%s\"",
16121 : NameStr(tuple_class->relname)),
16122 : errhint("Change the ownership of the index's table instead.")));
16123 : break;
16124 118 : case RELKIND_SEQUENCE:
16125 118 : if (!recursing &&
16126 70 : tuple_class->relowner != newOwnerId)
16127 : {
16128 : /* if it's an owned sequence, disallow changing it by itself */
16129 : Oid tableId;
16130 : int32 colId;
16131 :
16132 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16133 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16134 0 : ereport(ERROR,
16135 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16136 : errmsg("cannot change owner of sequence \"%s\"",
16137 : NameStr(tuple_class->relname)),
16138 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
16139 : NameStr(tuple_class->relname),
16140 : get_rel_name(tableId))));
16141 : }
16142 118 : break;
16143 8 : case RELKIND_COMPOSITE_TYPE:
16144 8 : if (recursing)
16145 8 : break;
16146 0 : ereport(ERROR,
16147 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16148 : errmsg("\"%s\" is a composite type",
16149 : NameStr(tuple_class->relname)),
16150 : /* translator: %s is an SQL ALTER command */
16151 : errhint("Use %s instead.",
16152 : "ALTER TYPE")));
16153 : break;
16154 42 : case RELKIND_TOASTVALUE:
16155 42 : if (recursing)
16156 42 : break;
16157 : /* FALL THRU */
16158 : default:
16159 0 : ereport(ERROR,
16160 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16161 : errmsg("cannot change owner of relation \"%s\"",
16162 : NameStr(tuple_class->relname)),
16163 : errdetail_relkind_not_supported(tuple_class->relkind)));
16164 : }
16165 :
16166 : /*
16167 : * If the new owner is the same as the existing owner, consider the
16168 : * command to have succeeded. This is for dump restoration purposes.
16169 : */
16170 2232 : if (tuple_class->relowner != newOwnerId)
16171 : {
16172 : Datum repl_val[Natts_pg_class];
16173 : bool repl_null[Natts_pg_class];
16174 : bool repl_repl[Natts_pg_class];
16175 : Acl *newAcl;
16176 : Datum aclDatum;
16177 : bool isNull;
16178 : HeapTuple newtuple;
16179 :
16180 : /* skip permission checks when recursing to index or toast table */
16181 504 : if (!recursing)
16182 : {
16183 : /* Superusers can always do it */
16184 286 : if (!superuser())
16185 : {
16186 42 : Oid namespaceOid = tuple_class->relnamespace;
16187 : AclResult aclresult;
16188 :
16189 : /* Otherwise, must be owner of the existing object */
16190 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16191 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
16192 0 : RelationGetRelationName(target_rel));
16193 :
16194 : /* Must be able to become new owner */
16195 42 : check_can_set_role(GetUserId(), newOwnerId);
16196 :
16197 : /* New owner must have CREATE privilege on namespace */
16198 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16199 : ACL_CREATE);
16200 30 : if (aclresult != ACLCHECK_OK)
16201 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
16202 0 : get_namespace_name(namespaceOid));
16203 : }
16204 : }
16205 :
16206 492 : memset(repl_null, false, sizeof(repl_null));
16207 492 : memset(repl_repl, false, sizeof(repl_repl));
16208 :
16209 492 : repl_repl[Anum_pg_class_relowner - 1] = true;
16210 492 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16211 :
16212 : /*
16213 : * Determine the modified ACL for the new owner. This is only
16214 : * necessary when the ACL is non-null.
16215 : */
16216 492 : aclDatum = SysCacheGetAttr(RELOID, tuple,
16217 : Anum_pg_class_relacl,
16218 : &isNull);
16219 492 : if (!isNull)
16220 : {
16221 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16222 : tuple_class->relowner, newOwnerId);
16223 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
16224 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16225 : }
16226 :
16227 492 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16228 :
16229 492 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16230 :
16231 492 : heap_freetuple(newtuple);
16232 :
16233 : /*
16234 : * We must similarly update any per-column ACLs to reflect the new
16235 : * owner; for neatness reasons that's split out as a subroutine.
16236 : */
16237 492 : change_owner_fix_column_acls(relationOid,
16238 : tuple_class->relowner,
16239 : newOwnerId);
16240 :
16241 : /*
16242 : * Update owner dependency reference, if any. A composite type has
16243 : * none, because it's tracked for the pg_type entry instead of here;
16244 : * indexes and TOAST tables don't have their own entries either.
16245 : */
16246 492 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16247 484 : tuple_class->relkind != RELKIND_INDEX &&
16248 388 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16249 368 : tuple_class->relkind != RELKIND_TOASTVALUE)
16250 326 : changeDependencyOnOwner(RelationRelationId, relationOid,
16251 : newOwnerId);
16252 :
16253 : /*
16254 : * Also change the ownership of the table's row type, if it has one
16255 : */
16256 492 : if (OidIsValid(tuple_class->reltype))
16257 300 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16258 :
16259 : /*
16260 : * If we are operating on a table or materialized view, also change
16261 : * the ownership of any indexes and sequences that belong to the
16262 : * relation, as well as its toast table (if it has one).
16263 : */
16264 492 : if (tuple_class->relkind == RELKIND_RELATION ||
16265 262 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16266 224 : tuple_class->relkind == RELKIND_MATVIEW ||
16267 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
16268 : {
16269 : List *index_oid_list;
16270 : ListCell *i;
16271 :
16272 : /* Find all the indexes belonging to this relation */
16273 310 : index_oid_list = RelationGetIndexList(target_rel);
16274 :
16275 : /* For each index, recursively change its ownership */
16276 426 : foreach(i, index_oid_list)
16277 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16278 :
16279 310 : list_free(index_oid_list);
16280 : }
16281 :
16282 : /* If it has a toast table, recurse to change its ownership */
16283 492 : if (tuple_class->reltoastrelid != InvalidOid)
16284 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16285 : true, lockmode);
16286 :
16287 : /* If it has dependent sequences, recurse to change them too */
16288 492 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16289 : }
16290 :
16291 2220 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16292 :
16293 2220 : ReleaseSysCache(tuple);
16294 2220 : table_close(class_rel, RowExclusiveLock);
16295 2220 : relation_close(target_rel, NoLock);
16296 2220 : }
16297 :
16298 : /*
16299 : * change_owner_fix_column_acls
16300 : *
16301 : * Helper function for ATExecChangeOwner. Scan the columns of the table
16302 : * and fix any non-null column ACLs to reflect the new owner.
16303 : */
16304 : static void
16305 492 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16306 : {
16307 : Relation attRelation;
16308 : SysScanDesc scan;
16309 : ScanKeyData key[1];
16310 : HeapTuple attributeTuple;
16311 :
16312 492 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16313 492 : ScanKeyInit(&key[0],
16314 : Anum_pg_attribute_attrelid,
16315 : BTEqualStrategyNumber, F_OIDEQ,
16316 : ObjectIdGetDatum(relationOid));
16317 492 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16318 : true, NULL, 1, key);
16319 3432 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16320 : {
16321 2940 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16322 : Datum repl_val[Natts_pg_attribute];
16323 : bool repl_null[Natts_pg_attribute];
16324 : bool repl_repl[Natts_pg_attribute];
16325 : Acl *newAcl;
16326 : Datum aclDatum;
16327 : bool isNull;
16328 : HeapTuple newtuple;
16329 :
16330 : /* Ignore dropped columns */
16331 2940 : if (att->attisdropped)
16332 2938 : continue;
16333 :
16334 2940 : aclDatum = heap_getattr(attributeTuple,
16335 : Anum_pg_attribute_attacl,
16336 : RelationGetDescr(attRelation),
16337 : &isNull);
16338 : /* Null ACLs do not require changes */
16339 2940 : if (isNull)
16340 2938 : continue;
16341 :
16342 2 : memset(repl_null, false, sizeof(repl_null));
16343 2 : memset(repl_repl, false, sizeof(repl_repl));
16344 :
16345 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16346 : oldOwnerId, newOwnerId);
16347 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
16348 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16349 :
16350 2 : newtuple = heap_modify_tuple(attributeTuple,
16351 : RelationGetDescr(attRelation),
16352 : repl_val, repl_null, repl_repl);
16353 :
16354 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16355 :
16356 2 : heap_freetuple(newtuple);
16357 : }
16358 492 : systable_endscan(scan);
16359 492 : table_close(attRelation, RowExclusiveLock);
16360 492 : }
16361 :
16362 : /*
16363 : * change_owner_recurse_to_sequences
16364 : *
16365 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
16366 : * for sequences that are dependent on serial columns, and changes their
16367 : * ownership.
16368 : */
16369 : static void
16370 492 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16371 : {
16372 : Relation depRel;
16373 : SysScanDesc scan;
16374 : ScanKeyData key[2];
16375 : HeapTuple tup;
16376 :
16377 : /*
16378 : * SERIAL sequences are those having an auto dependency on one of the
16379 : * table's columns (we don't care *which* column, exactly).
16380 : */
16381 492 : depRel = table_open(DependRelationId, AccessShareLock);
16382 :
16383 492 : ScanKeyInit(&key[0],
16384 : Anum_pg_depend_refclassid,
16385 : BTEqualStrategyNumber, F_OIDEQ,
16386 : ObjectIdGetDatum(RelationRelationId));
16387 492 : ScanKeyInit(&key[1],
16388 : Anum_pg_depend_refobjid,
16389 : BTEqualStrategyNumber, F_OIDEQ,
16390 : ObjectIdGetDatum(relationOid));
16391 : /* we leave refobjsubid unspecified */
16392 :
16393 492 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16394 : NULL, 2, key);
16395 :
16396 1398 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
16397 : {
16398 906 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16399 : Relation seqRel;
16400 :
16401 : /* skip dependencies other than auto dependencies on columns */
16402 906 : if (depForm->refobjsubid == 0 ||
16403 364 : depForm->classid != RelationRelationId ||
16404 142 : depForm->objsubid != 0 ||
16405 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16406 764 : continue;
16407 :
16408 : /* Use relation_open just in case it's an index */
16409 142 : seqRel = relation_open(depForm->objid, lockmode);
16410 :
16411 : /* skip non-sequence relations */
16412 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16413 : {
16414 : /* No need to keep the lock */
16415 116 : relation_close(seqRel, lockmode);
16416 116 : continue;
16417 : }
16418 :
16419 : /* We don't need to close the sequence while we alter it. */
16420 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16421 :
16422 : /* Now we can close it. Keep the lock till end of transaction. */
16423 26 : relation_close(seqRel, NoLock);
16424 : }
16425 :
16426 492 : systable_endscan(scan);
16427 :
16428 492 : relation_close(depRel, AccessShareLock);
16429 492 : }
16430 :
16431 : /*
16432 : * ALTER TABLE CLUSTER ON
16433 : *
16434 : * The only thing we have to do is to change the indisclustered bits.
16435 : *
16436 : * Return the address of the new clustering index.
16437 : */
16438 : static ObjectAddress
16439 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16440 : {
16441 : Oid indexOid;
16442 : ObjectAddress address;
16443 :
16444 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16445 :
16446 64 : if (!OidIsValid(indexOid))
16447 0 : ereport(ERROR,
16448 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16449 : errmsg("index \"%s\" for table \"%s\" does not exist",
16450 : indexName, RelationGetRelationName(rel))));
16451 :
16452 : /* Check index is valid to cluster on */
16453 64 : check_index_is_clusterable(rel, indexOid, lockmode);
16454 :
16455 : /* And do the work */
16456 64 : mark_index_clustered(rel, indexOid, false);
16457 :
16458 58 : ObjectAddressSet(address,
16459 : RelationRelationId, indexOid);
16460 :
16461 58 : return address;
16462 : }
16463 :
16464 : /*
16465 : * ALTER TABLE SET WITHOUT CLUSTER
16466 : *
16467 : * We have to find any indexes on the table that have indisclustered bit
16468 : * set and turn it off.
16469 : */
16470 : static void
16471 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
16472 : {
16473 18 : mark_index_clustered(rel, InvalidOid, false);
16474 12 : }
16475 :
16476 : /*
16477 : * Preparation phase for SET ACCESS METHOD
16478 : *
16479 : * Check that the access method exists and determine whether a change is
16480 : * actually needed.
16481 : */
16482 : static void
16483 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
16484 : {
16485 : Oid amoid;
16486 :
16487 : /*
16488 : * Look up the access method name and check that it differs from the
16489 : * table's current AM. If DEFAULT was specified for a partitioned table
16490 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16491 : */
16492 110 : if (amname != NULL)
16493 74 : amoid = get_table_am_oid(amname, false);
16494 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16495 18 : amoid = InvalidOid;
16496 : else
16497 18 : amoid = get_table_am_oid(default_table_access_method, false);
16498 :
16499 : /* if it's a match, phase 3 doesn't need to do anything */
16500 110 : if (rel->rd_rel->relam == amoid)
16501 12 : return;
16502 :
16503 : /* Save info for Phase 3 to do the real work */
16504 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
16505 98 : tab->newAccessMethod = amoid;
16506 98 : tab->chgAccessMethod = true;
16507 : }
16508 :
16509 : /*
16510 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16511 : * storage that have an interest in preserving AM.
16512 : *
16513 : * Since these have no storage, setting the access method is a catalog only
16514 : * operation.
16515 : */
16516 : static void
16517 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
16518 : {
16519 : Relation pg_class;
16520 : Oid oldAccessMethodId;
16521 : HeapTuple tuple;
16522 : Form_pg_class rd_rel;
16523 44 : Oid reloid = RelationGetRelid(rel);
16524 :
16525 : /*
16526 : * Shouldn't be called on relations having storage; these are processed in
16527 : * phase 3.
16528 : */
16529 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16530 :
16531 : /* Get a modifiable copy of the relation's pg_class row. */
16532 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
16533 :
16534 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16535 44 : if (!HeapTupleIsValid(tuple))
16536 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
16537 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16538 :
16539 : /* Update the pg_class row. */
16540 44 : oldAccessMethodId = rd_rel->relam;
16541 44 : rd_rel->relam = newAccessMethodId;
16542 :
16543 : /* Leave if no update required */
16544 44 : if (rd_rel->relam == oldAccessMethodId)
16545 : {
16546 0 : heap_freetuple(tuple);
16547 0 : table_close(pg_class, RowExclusiveLock);
16548 0 : return;
16549 : }
16550 :
16551 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16552 :
16553 : /*
16554 : * Update the dependency on the new access method. No dependency is added
16555 : * if the new access method is InvalidOid (default case). Be very careful
16556 : * that this has to compare the previous value stored in pg_class with the
16557 : * new one.
16558 : */
16559 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16560 20 : {
16561 : ObjectAddress relobj,
16562 : referenced;
16563 :
16564 : /*
16565 : * New access method is defined and there was no dependency
16566 : * previously, so record a new one.
16567 : */
16568 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
16569 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16570 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16571 : }
16572 24 : else if (OidIsValid(oldAccessMethodId) &&
16573 24 : !OidIsValid(rd_rel->relam))
16574 : {
16575 : /*
16576 : * There was an access method defined, and no new one, so just remove
16577 : * the existing dependency.
16578 : */
16579 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
16580 : AccessMethodRelationId,
16581 : DEPENDENCY_NORMAL);
16582 : }
16583 : else
16584 : {
16585 : Assert(OidIsValid(oldAccessMethodId) &&
16586 : OidIsValid(rd_rel->relam));
16587 :
16588 : /* Both are valid, so update the dependency */
16589 12 : changeDependencyFor(RelationRelationId, reloid,
16590 : AccessMethodRelationId,
16591 : oldAccessMethodId, rd_rel->relam);
16592 : }
16593 :
16594 : /* make the relam and dependency changes visible */
16595 44 : CommandCounterIncrement();
16596 :
16597 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16598 :
16599 44 : heap_freetuple(tuple);
16600 44 : table_close(pg_class, RowExclusiveLock);
16601 : }
16602 :
16603 : /*
16604 : * ALTER TABLE SET TABLESPACE
16605 : */
16606 : static void
16607 164 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16608 : {
16609 : Oid tablespaceId;
16610 :
16611 : /* Check that the tablespace exists */
16612 164 : tablespaceId = get_tablespace_oid(tablespacename, false);
16613 :
16614 : /* Check permissions except when moving to database's default */
16615 164 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16616 : {
16617 : AclResult aclresult;
16618 :
16619 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16620 66 : if (aclresult != ACLCHECK_OK)
16621 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16622 : }
16623 :
16624 : /* Save info for Phase 3 to do the real work */
16625 164 : if (OidIsValid(tab->newTableSpace))
16626 0 : ereport(ERROR,
16627 : (errcode(ERRCODE_SYNTAX_ERROR),
16628 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
16629 :
16630 164 : tab->newTableSpace = tablespaceId;
16631 164 : }
16632 :
16633 : /*
16634 : * Set, reset, or replace reloptions.
16635 : */
16636 : static void
16637 960 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
16638 : LOCKMODE lockmode)
16639 : {
16640 : Oid relid;
16641 : Relation pgclass;
16642 : HeapTuple tuple;
16643 : HeapTuple newtuple;
16644 : Datum datum;
16645 : Datum newOptions;
16646 : Datum repl_val[Natts_pg_class];
16647 : bool repl_null[Natts_pg_class];
16648 : bool repl_repl[Natts_pg_class];
16649 960 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16650 :
16651 960 : if (defList == NIL && operation != AT_ReplaceRelOptions)
16652 0 : return; /* nothing to do */
16653 :
16654 960 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
16655 :
16656 : /* Fetch heap tuple */
16657 960 : relid = RelationGetRelid(rel);
16658 960 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16659 960 : if (!HeapTupleIsValid(tuple))
16660 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16661 :
16662 960 : if (operation == AT_ReplaceRelOptions)
16663 : {
16664 : /*
16665 : * If we're supposed to replace the reloptions list, we just pretend
16666 : * there were none before.
16667 : */
16668 194 : datum = (Datum) 0;
16669 : }
16670 : else
16671 : {
16672 : bool isnull;
16673 :
16674 : /* Get the old reloptions */
16675 766 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16676 : &isnull);
16677 766 : if (isnull)
16678 478 : datum = (Datum) 0;
16679 : }
16680 :
16681 : /* Generate new proposed reloptions (text array) */
16682 960 : newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16683 : operation == AT_ResetRelOptions);
16684 :
16685 : /* Validate */
16686 954 : switch (rel->rd_rel->relkind)
16687 : {
16688 536 : case RELKIND_RELATION:
16689 : case RELKIND_MATVIEW:
16690 536 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16691 536 : break;
16692 6 : case RELKIND_PARTITIONED_TABLE:
16693 6 : (void) partitioned_table_reloptions(newOptions, true);
16694 0 : break;
16695 296 : case RELKIND_VIEW:
16696 296 : (void) view_reloptions(newOptions, true);
16697 278 : break;
16698 116 : case RELKIND_INDEX:
16699 : case RELKIND_PARTITIONED_INDEX:
16700 116 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16701 94 : break;
16702 0 : case RELKIND_TOASTVALUE:
16703 : /* fall through to error -- shouldn't ever get here */
16704 : default:
16705 0 : ereport(ERROR,
16706 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16707 : errmsg("cannot set options for relation \"%s\"",
16708 : RelationGetRelationName(rel)),
16709 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16710 : break;
16711 : }
16712 :
16713 : /* Special-case validation of view options */
16714 908 : if (rel->rd_rel->relkind == RELKIND_VIEW)
16715 : {
16716 278 : Query *view_query = get_view_query(rel);
16717 278 : List *view_options = untransformRelOptions(newOptions);
16718 : ListCell *cell;
16719 278 : bool check_option = false;
16720 :
16721 380 : foreach(cell, view_options)
16722 : {
16723 102 : DefElem *defel = (DefElem *) lfirst(cell);
16724 :
16725 102 : if (strcmp(defel->defname, "check_option") == 0)
16726 24 : check_option = true;
16727 : }
16728 :
16729 : /*
16730 : * If the check option is specified, look to see if the view is
16731 : * actually auto-updatable or not.
16732 : */
16733 278 : if (check_option)
16734 : {
16735 : const char *view_updatable_error =
16736 24 : view_query_is_auto_updatable(view_query, true);
16737 :
16738 24 : if (view_updatable_error)
16739 0 : ereport(ERROR,
16740 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16741 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16742 : errhint("%s", _(view_updatable_error))));
16743 : }
16744 : }
16745 :
16746 : /*
16747 : * All we need do here is update the pg_class row; the new options will be
16748 : * propagated into relcaches during post-commit cache inval.
16749 : */
16750 908 : memset(repl_val, 0, sizeof(repl_val));
16751 908 : memset(repl_null, false, sizeof(repl_null));
16752 908 : memset(repl_repl, false, sizeof(repl_repl));
16753 :
16754 908 : if (newOptions != (Datum) 0)
16755 614 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16756 : else
16757 294 : repl_null[Anum_pg_class_reloptions - 1] = true;
16758 :
16759 908 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16760 :
16761 908 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16762 : repl_val, repl_null, repl_repl);
16763 :
16764 908 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16765 908 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16766 :
16767 908 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16768 :
16769 908 : heap_freetuple(newtuple);
16770 :
16771 908 : ReleaseSysCache(tuple);
16772 :
16773 : /* repeat the whole exercise for the toast table, if there's one */
16774 908 : if (OidIsValid(rel->rd_rel->reltoastrelid))
16775 : {
16776 : Relation toastrel;
16777 268 : Oid toastid = rel->rd_rel->reltoastrelid;
16778 :
16779 268 : toastrel = table_open(toastid, lockmode);
16780 :
16781 : /* Fetch heap tuple */
16782 268 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16783 268 : if (!HeapTupleIsValid(tuple))
16784 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
16785 :
16786 268 : if (operation == AT_ReplaceRelOptions)
16787 : {
16788 : /*
16789 : * If we're supposed to replace the reloptions list, we just
16790 : * pretend there were none before.
16791 : */
16792 0 : datum = (Datum) 0;
16793 : }
16794 : else
16795 : {
16796 : bool isnull;
16797 :
16798 : /* Get the old reloptions */
16799 268 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16800 : &isnull);
16801 268 : if (isnull)
16802 232 : datum = (Datum) 0;
16803 : }
16804 :
16805 268 : newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16806 : false, operation == AT_ResetRelOptions);
16807 :
16808 268 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16809 :
16810 268 : memset(repl_val, 0, sizeof(repl_val));
16811 268 : memset(repl_null, false, sizeof(repl_null));
16812 268 : memset(repl_repl, false, sizeof(repl_repl));
16813 :
16814 268 : if (newOptions != (Datum) 0)
16815 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16816 : else
16817 226 : repl_null[Anum_pg_class_reloptions - 1] = true;
16818 :
16819 268 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16820 :
16821 268 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16822 : repl_val, repl_null, repl_repl);
16823 :
16824 268 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16825 :
16826 268 : InvokeObjectPostAlterHookArg(RelationRelationId,
16827 : RelationGetRelid(toastrel), 0,
16828 : InvalidOid, true);
16829 :
16830 268 : heap_freetuple(newtuple);
16831 :
16832 268 : ReleaseSysCache(tuple);
16833 :
16834 268 : table_close(toastrel, NoLock);
16835 : }
16836 :
16837 908 : table_close(pgclass, RowExclusiveLock);
16838 : }
16839 :
16840 : /*
16841 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16842 : * rewriting to be done, so we just want to copy the data as fast as possible.
16843 : */
16844 : static void
16845 168 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16846 : {
16847 : Relation rel;
16848 : Oid reltoastrelid;
16849 : RelFileNumber newrelfilenumber;
16850 : RelFileLocator newrlocator;
16851 168 : List *reltoastidxids = NIL;
16852 : ListCell *lc;
16853 :
16854 : /*
16855 : * Need lock here in case we are recursing to toast table or index
16856 : */
16857 168 : rel = relation_open(tableOid, lockmode);
16858 :
16859 : /* Check first if relation can be moved to new tablespace */
16860 168 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16861 : {
16862 8 : InvokeObjectPostAlterHook(RelationRelationId,
16863 : RelationGetRelid(rel), 0);
16864 8 : relation_close(rel, NoLock);
16865 8 : return;
16866 : }
16867 :
16868 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
16869 : /* Fetch the list of indexes on toast relation if necessary */
16870 160 : if (OidIsValid(reltoastrelid))
16871 : {
16872 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
16873 :
16874 20 : reltoastidxids = RelationGetIndexList(toastRel);
16875 20 : relation_close(toastRel, lockmode);
16876 : }
16877 :
16878 : /*
16879 : * Relfilenumbers are not unique in databases across tablespaces, so we
16880 : * need to allocate a new one in the new tablespace.
16881 : */
16882 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16883 160 : rel->rd_rel->relpersistence);
16884 :
16885 : /* Open old and new relation */
16886 160 : newrlocator = rel->rd_locator;
16887 160 : newrlocator.relNumber = newrelfilenumber;
16888 160 : newrlocator.spcOid = newTableSpace;
16889 :
16890 : /* hand off to AM to actually create new rel storage and copy the data */
16891 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
16892 : {
16893 62 : index_copy_data(rel, newrlocator);
16894 : }
16895 : else
16896 : {
16897 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16898 98 : table_relation_copy_data(rel, &newrlocator);
16899 : }
16900 :
16901 : /*
16902 : * Update the pg_class row.
16903 : *
16904 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16905 : * executed on pg_class or its indexes (the above copy wouldn't contain
16906 : * the updated pg_class entry), but that's forbidden with
16907 : * CheckRelationTableSpaceMove().
16908 : */
16909 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16910 :
16911 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16912 :
16913 160 : RelationAssumeNewRelfilelocator(rel);
16914 :
16915 160 : relation_close(rel, NoLock);
16916 :
16917 : /* Make sure the reltablespace change is visible */
16918 160 : CommandCounterIncrement();
16919 :
16920 : /* Move associated toast relation and/or indexes, too */
16921 160 : if (OidIsValid(reltoastrelid))
16922 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16923 180 : foreach(lc, reltoastidxids)
16924 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16925 :
16926 : /* Clean up */
16927 160 : list_free(reltoastidxids);
16928 : }
16929 :
16930 : /*
16931 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16932 : * storage that have an interest in preserving tablespace.
16933 : *
16934 : * Since these have no storage the tablespace can be updated with a simple
16935 : * metadata only operation to update the tablespace.
16936 : */
16937 : static void
16938 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
16939 : {
16940 : /*
16941 : * Shouldn't be called on relations having storage; these are processed in
16942 : * phase 3.
16943 : */
16944 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16945 :
16946 : /* check if relation can be moved to its new tablespace */
16947 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16948 : {
16949 0 : InvokeObjectPostAlterHook(RelationRelationId,
16950 : RelationGetRelid(rel),
16951 : 0);
16952 0 : return;
16953 : }
16954 :
16955 : /* Update can be done, so change reltablespace */
16956 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16957 :
16958 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16959 :
16960 : /* Make sure the reltablespace change is visible */
16961 30 : CommandCounterIncrement();
16962 : }
16963 :
16964 : /*
16965 : * Alter Table ALL ... SET TABLESPACE
16966 : *
16967 : * Allows a user to move all objects of some type in a given tablespace in the
16968 : * current database to another tablespace. Objects can be chosen based on the
16969 : * owner of the object also, to allow users to move only their objects.
16970 : * The user must have CREATE rights on the new tablespace, as usual. The main
16971 : * permissions handling is done by the lower-level table move function.
16972 : *
16973 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
16974 : * lock can't be acquired then we ereport(ERROR).
16975 : */
16976 : Oid
16977 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
16978 : {
16979 30 : List *relations = NIL;
16980 : ListCell *l;
16981 : ScanKeyData key[1];
16982 : Relation rel;
16983 : TableScanDesc scan;
16984 : HeapTuple tuple;
16985 : Oid orig_tablespaceoid;
16986 : Oid new_tablespaceoid;
16987 30 : List *role_oids = roleSpecsToIds(stmt->roles);
16988 :
16989 : /* Ensure we were not asked to move something we can't */
16990 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16991 12 : stmt->objtype != OBJECT_MATVIEW)
16992 0 : ereport(ERROR,
16993 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16994 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16995 :
16996 : /* Get the orig and new tablespace OIDs */
16997 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16998 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16999 :
17000 : /* Can't move shared relations in to or out of pg_global */
17001 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17002 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
17003 : new_tablespaceoid == GLOBALTABLESPACE_OID)
17004 0 : ereport(ERROR,
17005 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17006 : errmsg("cannot move relations in to or out of pg_global tablespace")));
17007 :
17008 : /*
17009 : * Must have CREATE rights on the new tablespace, unless it is the
17010 : * database default tablespace (which all users implicitly have CREATE
17011 : * rights on).
17012 : */
17013 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
17014 : {
17015 : AclResult aclresult;
17016 :
17017 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
17018 : ACL_CREATE);
17019 0 : if (aclresult != ACLCHECK_OK)
17020 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
17021 0 : get_tablespace_name(new_tablespaceoid));
17022 : }
17023 :
17024 : /*
17025 : * Now that the checks are done, check if we should set either to
17026 : * InvalidOid because it is our database's default tablespace.
17027 : */
17028 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
17029 0 : orig_tablespaceoid = InvalidOid;
17030 :
17031 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
17032 30 : new_tablespaceoid = InvalidOid;
17033 :
17034 : /* no-op */
17035 30 : if (orig_tablespaceoid == new_tablespaceoid)
17036 0 : return new_tablespaceoid;
17037 :
17038 : /*
17039 : * Walk the list of objects in the tablespace and move them. This will
17040 : * only find objects in our database, of course.
17041 : */
17042 30 : ScanKeyInit(&key[0],
17043 : Anum_pg_class_reltablespace,
17044 : BTEqualStrategyNumber, F_OIDEQ,
17045 : ObjectIdGetDatum(orig_tablespaceoid));
17046 :
17047 30 : rel = table_open(RelationRelationId, AccessShareLock);
17048 30 : scan = table_beginscan_catalog(rel, 1, key);
17049 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17050 : {
17051 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17052 102 : Oid relOid = relForm->oid;
17053 :
17054 : /*
17055 : * Do not move objects in pg_catalog as part of this, if an admin
17056 : * really wishes to do so, they can issue the individual ALTER
17057 : * commands directly.
17058 : *
17059 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
17060 : * (TOAST will be moved with the main table).
17061 : */
17062 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
17063 204 : relForm->relisshared ||
17064 204 : isAnyTempNamespace(relForm->relnamespace) ||
17065 102 : IsToastNamespace(relForm->relnamespace))
17066 0 : continue;
17067 :
17068 : /* Only move the object type requested */
17069 102 : if ((stmt->objtype == OBJECT_TABLE &&
17070 60 : relForm->relkind != RELKIND_RELATION &&
17071 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17072 66 : (stmt->objtype == OBJECT_INDEX &&
17073 36 : relForm->relkind != RELKIND_INDEX &&
17074 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17075 60 : (stmt->objtype == OBJECT_MATVIEW &&
17076 6 : relForm->relkind != RELKIND_MATVIEW))
17077 42 : continue;
17078 :
17079 : /* Check if we are only moving objects owned by certain roles */
17080 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17081 0 : continue;
17082 :
17083 : /*
17084 : * Handle permissions-checking here since we are locking the tables
17085 : * and also to avoid doing a bunch of work only to fail part-way. Note
17086 : * that permissions will also be checked by AlterTableInternal().
17087 : *
17088 : * Caller must be considered an owner on the table to move it.
17089 : */
17090 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17091 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
17092 0 : NameStr(relForm->relname));
17093 :
17094 60 : if (stmt->nowait &&
17095 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
17096 0 : ereport(ERROR,
17097 : (errcode(ERRCODE_OBJECT_IN_USE),
17098 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
17099 : get_namespace_name(relForm->relnamespace),
17100 : NameStr(relForm->relname))));
17101 : else
17102 60 : LockRelationOid(relOid, AccessExclusiveLock);
17103 :
17104 : /* Add to our list of objects to move */
17105 60 : relations = lappend_oid(relations, relOid);
17106 : }
17107 :
17108 30 : table_endscan(scan);
17109 30 : table_close(rel, AccessShareLock);
17110 :
17111 30 : if (relations == NIL)
17112 12 : ereport(NOTICE,
17113 : (errcode(ERRCODE_NO_DATA_FOUND),
17114 : errmsg("no matching relations in tablespace \"%s\" found",
17115 : orig_tablespaceoid == InvalidOid ? "(database default)" :
17116 : get_tablespace_name(orig_tablespaceoid))));
17117 :
17118 : /* Everything is locked, loop through and move all of the relations. */
17119 90 : foreach(l, relations)
17120 : {
17121 60 : List *cmds = NIL;
17122 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
17123 :
17124 60 : cmd->subtype = AT_SetTableSpace;
17125 60 : cmd->name = stmt->new_tablespacename;
17126 :
17127 60 : cmds = lappend(cmds, cmd);
17128 :
17129 60 : EventTriggerAlterTableStart((Node *) stmt);
17130 : /* OID is set by AlterTableInternal */
17131 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
17132 60 : EventTriggerAlterTableEnd();
17133 : }
17134 :
17135 30 : return new_tablespaceoid;
17136 : }
17137 :
17138 : static void
17139 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
17140 : {
17141 : SMgrRelation dstrel;
17142 :
17143 : /*
17144 : * Since we copy the file directly without looking at the shared buffers,
17145 : * we'd better first flush out any pages of the source relation that are
17146 : * in shared buffers. We assume no new changes will be made while we are
17147 : * holding exclusive lock on the rel.
17148 : */
17149 62 : FlushRelationBuffers(rel);
17150 :
17151 : /*
17152 : * Create and copy all forks of the relation, and schedule unlinking of
17153 : * old physical files.
17154 : *
17155 : * NOTE: any conflict in relfilenumber value will be caught in
17156 : * RelationCreateStorage().
17157 : */
17158 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17159 :
17160 : /* copy main fork */
17161 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
17162 62 : rel->rd_rel->relpersistence);
17163 :
17164 : /* copy those extra forks that exist */
17165 62 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17166 248 : forkNum <= MAX_FORKNUM; forkNum++)
17167 : {
17168 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
17169 : {
17170 0 : smgrcreate(dstrel, forkNum, false);
17171 :
17172 : /*
17173 : * WAL log creation if the relation is persistent, or this is the
17174 : * init fork of an unlogged relation.
17175 : */
17176 0 : if (RelationIsPermanent(rel) ||
17177 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17178 : forkNum == INIT_FORKNUM))
17179 0 : log_smgrcreate(&newrlocator, forkNum);
17180 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17181 0 : rel->rd_rel->relpersistence);
17182 : }
17183 : }
17184 :
17185 : /* drop old relation, and close new one */
17186 62 : RelationDropStorage(rel);
17187 62 : smgrclose(dstrel);
17188 62 : }
17189 :
17190 : /*
17191 : * ALTER TABLE ENABLE/DISABLE TRIGGER
17192 : *
17193 : * We just pass this off to trigger.c.
17194 : */
17195 : static void
17196 342 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17197 : char fires_when, bool skip_system, bool recurse,
17198 : LOCKMODE lockmode)
17199 : {
17200 342 : EnableDisableTrigger(rel, trigname, InvalidOid,
17201 : fires_when, skip_system, recurse,
17202 : lockmode);
17203 :
17204 342 : InvokeObjectPostAlterHook(RelationRelationId,
17205 : RelationGetRelid(rel), 0);
17206 342 : }
17207 :
17208 : /*
17209 : * ALTER TABLE ENABLE/DISABLE RULE
17210 : *
17211 : * We just pass this off to rewriteDefine.c.
17212 : */
17213 : static void
17214 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
17215 : char fires_when, LOCKMODE lockmode)
17216 : {
17217 46 : EnableDisableRule(rel, rulename, fires_when);
17218 :
17219 46 : InvokeObjectPostAlterHook(RelationRelationId,
17220 : RelationGetRelid(rel), 0);
17221 46 : }
17222 :
17223 : /*
17224 : * ALTER TABLE INHERIT
17225 : *
17226 : * Add a parent to the child's parents. This verifies that all the columns and
17227 : * check constraints of the parent appear in the child and that they have the
17228 : * same data types and expressions.
17229 : */
17230 : static void
17231 464 : ATPrepAddInherit(Relation child_rel)
17232 : {
17233 464 : if (child_rel->rd_rel->reloftype)
17234 6 : ereport(ERROR,
17235 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17236 : errmsg("cannot change inheritance of typed table")));
17237 :
17238 458 : if (child_rel->rd_rel->relispartition)
17239 6 : ereport(ERROR,
17240 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17241 : errmsg("cannot change inheritance of a partition")));
17242 :
17243 452 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17244 6 : ereport(ERROR,
17245 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17246 : errmsg("cannot change inheritance of partitioned table")));
17247 446 : }
17248 :
17249 : /*
17250 : * Return the address of the new parent relation.
17251 : */
17252 : static ObjectAddress
17253 446 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17254 : {
17255 : Relation parent_rel;
17256 : List *children;
17257 : ObjectAddress address;
17258 : const char *trigger_name;
17259 :
17260 : /*
17261 : * A self-exclusive lock is needed here. See the similar case in
17262 : * MergeAttributes() for a full explanation.
17263 : */
17264 446 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17265 :
17266 : /*
17267 : * Must be owner of both parent and child -- child was checked by
17268 : * ATSimplePermissions call in ATPrepCmd
17269 : */
17270 446 : ATSimplePermissions(AT_AddInherit, parent_rel,
17271 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
17272 :
17273 : /* Permanent rels cannot inherit from temporary ones */
17274 446 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17275 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17276 0 : ereport(ERROR,
17277 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17278 : errmsg("cannot inherit from temporary relation \"%s\"",
17279 : RelationGetRelationName(parent_rel))));
17280 :
17281 : /* If parent rel is temp, it must belong to this session */
17282 446 : if (RELATION_IS_OTHER_TEMP(parent_rel))
17283 0 : ereport(ERROR,
17284 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17285 : errmsg("cannot inherit from temporary relation of another session")));
17286 :
17287 : /* Ditto for the child */
17288 446 : if (RELATION_IS_OTHER_TEMP(child_rel))
17289 0 : ereport(ERROR,
17290 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17291 : errmsg("cannot inherit to temporary relation of another session")));
17292 :
17293 : /* Prevent partitioned tables from becoming inheritance parents */
17294 446 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17295 6 : ereport(ERROR,
17296 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17297 : errmsg("cannot inherit from partitioned table \"%s\"",
17298 : parent->relname)));
17299 :
17300 : /* Likewise for partitions */
17301 440 : if (parent_rel->rd_rel->relispartition)
17302 6 : ereport(ERROR,
17303 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17304 : errmsg("cannot inherit from a partition")));
17305 :
17306 : /*
17307 : * Prevent circularity by seeing if proposed parent inherits from child.
17308 : * (In particular, this disallows making a rel inherit from itself.)
17309 : *
17310 : * This is not completely bulletproof because of race conditions: in
17311 : * multi-level inheritance trees, someone else could concurrently be
17312 : * making another inheritance link that closes the loop but does not join
17313 : * either of the rels we have locked. Preventing that seems to require
17314 : * exclusive locks on the entire inheritance tree, which is a cure worse
17315 : * than the disease. find_all_inheritors() will cope with circularity
17316 : * anyway, so don't sweat it too much.
17317 : *
17318 : * We use weakest lock we can on child's children, namely AccessShareLock.
17319 : */
17320 434 : children = find_all_inheritors(RelationGetRelid(child_rel),
17321 : AccessShareLock, NULL);
17322 :
17323 434 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
17324 12 : ereport(ERROR,
17325 : (errcode(ERRCODE_DUPLICATE_TABLE),
17326 : errmsg("circular inheritance not allowed"),
17327 : errdetail("\"%s\" is already a child of \"%s\".",
17328 : parent->relname,
17329 : RelationGetRelationName(child_rel))));
17330 :
17331 : /*
17332 : * If child_rel has row-level triggers with transition tables, we
17333 : * currently don't allow it to become an inheritance child. See also
17334 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
17335 : */
17336 422 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17337 422 : if (trigger_name != NULL)
17338 6 : ereport(ERROR,
17339 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17340 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17341 : trigger_name, RelationGetRelationName(child_rel)),
17342 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17343 :
17344 : /* OK to create inheritance */
17345 416 : CreateInheritance(child_rel, parent_rel, false);
17346 :
17347 326 : ObjectAddressSet(address, RelationRelationId,
17348 : RelationGetRelid(parent_rel));
17349 :
17350 : /* keep our lock on the parent relation until commit */
17351 326 : table_close(parent_rel, NoLock);
17352 :
17353 326 : return address;
17354 : }
17355 :
17356 : /*
17357 : * CreateInheritance
17358 : * Catalog manipulation portion of creating inheritance between a child
17359 : * table and a parent table.
17360 : *
17361 : * Common to ATExecAddInherit() and ATExecAttachPartition().
17362 : */
17363 : static void
17364 2684 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17365 : {
17366 : Relation catalogRelation;
17367 : SysScanDesc scan;
17368 : ScanKeyData key;
17369 : HeapTuple inheritsTuple;
17370 : int32 inhseqno;
17371 :
17372 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17373 2684 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17374 :
17375 : /*
17376 : * Check for duplicates in the list of parents, and determine the highest
17377 : * inhseqno already present; we'll use the next one for the new parent.
17378 : * Also, if proposed child is a partition, it cannot already be
17379 : * inheriting.
17380 : *
17381 : * Note: we do not reject the case where the child already inherits from
17382 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17383 : */
17384 2684 : ScanKeyInit(&key,
17385 : Anum_pg_inherits_inhrelid,
17386 : BTEqualStrategyNumber, F_OIDEQ,
17387 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17388 2684 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17389 : true, NULL, 1, &key);
17390 :
17391 : /* inhseqno sequences start at 1 */
17392 2684 : inhseqno = 0;
17393 2754 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17394 : {
17395 76 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17396 :
17397 76 : if (inh->inhparent == RelationGetRelid(parent_rel))
17398 6 : ereport(ERROR,
17399 : (errcode(ERRCODE_DUPLICATE_TABLE),
17400 : errmsg("relation \"%s\" would be inherited from more than once",
17401 : RelationGetRelationName(parent_rel))));
17402 :
17403 70 : if (inh->inhseqno > inhseqno)
17404 70 : inhseqno = inh->inhseqno;
17405 : }
17406 2678 : systable_endscan(scan);
17407 :
17408 : /* Match up the columns and bump attinhcount as needed */
17409 2678 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17410 :
17411 : /* Match up the constraints and bump coninhcount as needed */
17412 2546 : MergeConstraintsIntoExisting(child_rel, parent_rel);
17413 :
17414 : /*
17415 : * OK, it looks valid. Make the catalog entries that show inheritance.
17416 : */
17417 2486 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
17418 : RelationGetRelid(parent_rel),
17419 : inhseqno + 1,
17420 : catalogRelation,
17421 2486 : parent_rel->rd_rel->relkind ==
17422 : RELKIND_PARTITIONED_TABLE);
17423 :
17424 : /* Now we're done with pg_inherits */
17425 2486 : table_close(catalogRelation, RowExclusiveLock);
17426 2486 : }
17427 :
17428 : /*
17429 : * Obtain the source-text form of the constraint expression for a check
17430 : * constraint, given its pg_constraint tuple
17431 : */
17432 : static char *
17433 184 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
17434 : {
17435 : Form_pg_constraint con;
17436 : bool isnull;
17437 : Datum attr;
17438 : Datum expr;
17439 :
17440 184 : con = (Form_pg_constraint) GETSTRUCT(contup);
17441 184 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17442 184 : if (isnull)
17443 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
17444 :
17445 184 : expr = DirectFunctionCall2(pg_get_expr, attr,
17446 : ObjectIdGetDatum(con->conrelid));
17447 184 : return TextDatumGetCString(expr);
17448 : }
17449 :
17450 : /*
17451 : * Determine whether two check constraints are functionally equivalent
17452 : *
17453 : * The test we apply is to see whether they reverse-compile to the same
17454 : * source string. This insulates us from issues like whether attributes
17455 : * have the same physical column numbers in parent and child relations.
17456 : *
17457 : * Note that we ignore enforceability as there are cases where constraints
17458 : * with differing enforceability are allowed.
17459 : */
17460 : static bool
17461 92 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
17462 : {
17463 92 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
17464 92 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
17465 :
17466 92 : if (acon->condeferrable != bcon->condeferrable ||
17467 92 : acon->condeferred != bcon->condeferred ||
17468 92 : strcmp(decompile_conbin(a, tupleDesc),
17469 92 : decompile_conbin(b, tupleDesc)) != 0)
17470 6 : return false;
17471 : else
17472 86 : return true;
17473 : }
17474 :
17475 : /*
17476 : * Check columns in child table match up with columns in parent, and increment
17477 : * their attinhcount.
17478 : *
17479 : * Called by CreateInheritance
17480 : *
17481 : * Currently all parent columns must be found in child. Missing columns are an
17482 : * error. One day we might consider creating new columns like CREATE TABLE
17483 : * does. However, that is widely unpopular --- in the common use case of
17484 : * partitioned tables it's a foot-gun.
17485 : *
17486 : * The data type must match exactly. If the parent column is NOT NULL then
17487 : * the child must be as well. Defaults are not compared, however.
17488 : */
17489 : static void
17490 2678 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17491 : {
17492 : Relation attrrel;
17493 : TupleDesc parent_desc;
17494 :
17495 2678 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17496 2678 : parent_desc = RelationGetDescr(parent_rel);
17497 :
17498 8540 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17499 : {
17500 5994 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17501 5994 : char *parent_attname = NameStr(parent_att->attname);
17502 : HeapTuple tuple;
17503 :
17504 : /* Ignore dropped columns in the parent. */
17505 5994 : if (parent_att->attisdropped)
17506 296 : continue;
17507 :
17508 : /* Find same column in child (matching on column name). */
17509 5698 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17510 5698 : if (HeapTupleIsValid(tuple))
17511 : {
17512 5686 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17513 :
17514 5686 : if (parent_att->atttypid != child_att->atttypid ||
17515 5680 : parent_att->atttypmod != child_att->atttypmod)
17516 12 : ereport(ERROR,
17517 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17518 : errmsg("child table \"%s\" has different type for column \"%s\"",
17519 : RelationGetRelationName(child_rel), parent_attname)));
17520 :
17521 5674 : if (parent_att->attcollation != child_att->attcollation)
17522 6 : ereport(ERROR,
17523 : (errcode(ERRCODE_COLLATION_MISMATCH),
17524 : errmsg("child table \"%s\" has different collation for column \"%s\"",
17525 : RelationGetRelationName(child_rel), parent_attname)));
17526 :
17527 : /*
17528 : * If the parent has a not-null constraint that's not NO INHERIT,
17529 : * make sure the child has one too.
17530 : *
17531 : * Other constraints are checked elsewhere.
17532 : */
17533 5668 : if (parent_att->attnotnull && !child_att->attnotnull)
17534 : {
17535 : HeapTuple contup;
17536 :
17537 48 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17538 48 : parent_att->attnum);
17539 48 : if (HeapTupleIsValid(contup) &&
17540 48 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17541 30 : ereport(ERROR,
17542 : errcode(ERRCODE_DATATYPE_MISMATCH),
17543 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17544 : parent_attname, RelationGetRelationName(child_rel)));
17545 : }
17546 :
17547 : /*
17548 : * Child column must be generated if and only if parent column is.
17549 : */
17550 5638 : if (parent_att->attgenerated && !child_att->attgenerated)
17551 36 : ereport(ERROR,
17552 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17553 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17554 5602 : if (child_att->attgenerated && !parent_att->attgenerated)
17555 24 : ereport(ERROR,
17556 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17557 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17558 :
17559 5578 : if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17560 12 : ereport(ERROR,
17561 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17562 : errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17563 : errdetail("Parent column is %s, child column is %s.",
17564 : parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17565 : child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17566 :
17567 : /*
17568 : * Regular inheritance children are independent enough not to
17569 : * inherit identity columns. But partitions are integral part of
17570 : * a partitioned table and inherit identity column.
17571 : */
17572 5566 : if (ispartition)
17573 4838 : child_att->attidentity = parent_att->attidentity;
17574 :
17575 : /*
17576 : * OK, bump the child column's inheritance count. (If we fail
17577 : * later on, this change will just roll back.)
17578 : */
17579 5566 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
17580 : &child_att->attinhcount))
17581 0 : ereport(ERROR,
17582 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17583 : errmsg("too many inheritance parents"));
17584 :
17585 : /*
17586 : * In case of partitions, we must enforce that value of attislocal
17587 : * is same in all partitions. (Note: there are only inherited
17588 : * attributes in partitions)
17589 : */
17590 5566 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17591 : {
17592 : Assert(child_att->attinhcount == 1);
17593 4838 : child_att->attislocal = false;
17594 : }
17595 :
17596 5566 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17597 5566 : heap_freetuple(tuple);
17598 : }
17599 : else
17600 : {
17601 12 : ereport(ERROR,
17602 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17603 : errmsg("child table is missing column \"%s\"", parent_attname)));
17604 : }
17605 : }
17606 :
17607 2546 : table_close(attrrel, RowExclusiveLock);
17608 2546 : }
17609 :
17610 : /*
17611 : * Check constraints in child table match up with constraints in parent,
17612 : * and increment their coninhcount.
17613 : *
17614 : * Constraints that are marked ONLY in the parent are ignored.
17615 : *
17616 : * Called by CreateInheritance
17617 : *
17618 : * Currently all constraints in parent must be present in the child. One day we
17619 : * may consider adding new constraints like CREATE TABLE does.
17620 : *
17621 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
17622 : * constraints. As long as tables have more like 10 constraints it shouldn't be
17623 : * a problem though. Even 100 constraints ought not be the end of the world.
17624 : *
17625 : * XXX See MergeWithExistingConstraint too if you change this code.
17626 : */
17627 : static void
17628 2546 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
17629 : {
17630 : Relation constraintrel;
17631 : SysScanDesc parent_scan;
17632 : ScanKeyData parent_key;
17633 : HeapTuple parent_tuple;
17634 2546 : Oid parent_relid = RelationGetRelid(parent_rel);
17635 : AttrMap *attmap;
17636 :
17637 2546 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17638 :
17639 : /* Outer loop scans through the parent's constraint definitions */
17640 2546 : ScanKeyInit(&parent_key,
17641 : Anum_pg_constraint_conrelid,
17642 : BTEqualStrategyNumber, F_OIDEQ,
17643 : ObjectIdGetDatum(parent_relid));
17644 2546 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17645 : true, NULL, 1, &parent_key);
17646 :
17647 2546 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17648 : RelationGetDescr(child_rel),
17649 : true);
17650 :
17651 4496 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17652 : {
17653 2010 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17654 : SysScanDesc child_scan;
17655 : ScanKeyData child_key;
17656 : HeapTuple child_tuple;
17657 : AttrNumber parent_attno;
17658 2010 : bool found = false;
17659 :
17660 2010 : if (parent_con->contype != CONSTRAINT_CHECK &&
17661 1874 : parent_con->contype != CONSTRAINT_NOTNULL)
17662 994 : continue;
17663 :
17664 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17665 1060 : if (parent_con->connoinherit)
17666 44 : continue;
17667 :
17668 1016 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17669 900 : parent_attno = extractNotNullColumn(parent_tuple);
17670 : else
17671 116 : parent_attno = InvalidAttrNumber;
17672 :
17673 : /* Search for a child constraint matching this one */
17674 1016 : ScanKeyInit(&child_key,
17675 : Anum_pg_constraint_conrelid,
17676 : BTEqualStrategyNumber, F_OIDEQ,
17677 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17678 1016 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17679 : true, NULL, 1, &child_key);
17680 :
17681 1594 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17682 : {
17683 1570 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17684 : HeapTuple child_copy;
17685 :
17686 1570 : if (child_con->contype != parent_con->contype)
17687 280 : continue;
17688 :
17689 : /*
17690 : * CHECK constraint are matched by constraint name, NOT NULL ones
17691 : * by attribute number.
17692 : */
17693 1290 : if (child_con->contype == CONSTRAINT_CHECK)
17694 : {
17695 152 : if (strcmp(NameStr(parent_con->conname),
17696 122 : NameStr(child_con->conname)) != 0)
17697 30 : continue;
17698 : }
17699 1168 : else if (child_con->contype == CONSTRAINT_NOTNULL)
17700 : {
17701 : Form_pg_attribute parent_attr;
17702 : Form_pg_attribute child_attr;
17703 : AttrNumber child_attno;
17704 :
17705 1168 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17706 1168 : child_attno = extractNotNullColumn(child_tuple);
17707 1168 : if (parent_attno != attmap->attnums[child_attno - 1])
17708 268 : continue;
17709 :
17710 900 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17711 : /* there shouldn't be constraints on dropped columns */
17712 900 : if (parent_attr->attisdropped || child_attr->attisdropped)
17713 0 : elog(ERROR, "found not-null constraint on dropped columns");
17714 : }
17715 :
17716 992 : if (child_con->contype == CONSTRAINT_CHECK &&
17717 92 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17718 6 : ereport(ERROR,
17719 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17720 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17721 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17722 :
17723 : /*
17724 : * If the child constraint is "no inherit" then cannot merge
17725 : */
17726 986 : if (child_con->connoinherit)
17727 12 : ereport(ERROR,
17728 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17729 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17730 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17731 :
17732 : /*
17733 : * If the child constraint is "not valid" then cannot merge with a
17734 : * valid parent constraint
17735 : */
17736 974 : if (parent_con->convalidated && child_con->conenforced &&
17737 920 : !child_con->convalidated)
17738 12 : ereport(ERROR,
17739 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17740 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17741 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17742 :
17743 : /*
17744 : * A NOT ENFORCED child constraint cannot be merged with an
17745 : * ENFORCED parent constraint. However, the reverse is allowed,
17746 : * where the child constraint is ENFORCED.
17747 : */
17748 962 : if (parent_con->conenforced && !child_con->conenforced)
17749 6 : ereport(ERROR,
17750 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17751 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17752 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17753 :
17754 : /*
17755 : * OK, bump the child constraint's inheritance count. (If we fail
17756 : * later on, this change will just roll back.)
17757 : */
17758 956 : child_copy = heap_copytuple(child_tuple);
17759 956 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17760 :
17761 956 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
17762 : &child_con->coninhcount))
17763 0 : ereport(ERROR,
17764 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17765 : errmsg("too many inheritance parents"));
17766 :
17767 : /*
17768 : * In case of partitions, an inherited constraint must be
17769 : * inherited only once since it cannot have multiple parents and
17770 : * it is never considered local.
17771 : */
17772 956 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17773 : {
17774 : Assert(child_con->coninhcount == 1);
17775 808 : child_con->conislocal = false;
17776 : }
17777 :
17778 956 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17779 956 : heap_freetuple(child_copy);
17780 :
17781 956 : found = true;
17782 956 : break;
17783 : }
17784 :
17785 980 : systable_endscan(child_scan);
17786 :
17787 980 : if (!found)
17788 : {
17789 24 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17790 0 : ereport(ERROR,
17791 : errcode(ERRCODE_DATATYPE_MISMATCH),
17792 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17793 : get_attname(parent_relid,
17794 : extractNotNullColumn(parent_tuple),
17795 : false),
17796 : RelationGetRelationName(child_rel)));
17797 :
17798 24 : ereport(ERROR,
17799 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17800 : errmsg("child table is missing constraint \"%s\"",
17801 : NameStr(parent_con->conname))));
17802 : }
17803 : }
17804 :
17805 2486 : systable_endscan(parent_scan);
17806 2486 : table_close(constraintrel, RowExclusiveLock);
17807 2486 : }
17808 :
17809 : /*
17810 : * ALTER TABLE NO INHERIT
17811 : *
17812 : * Return value is the address of the relation that is no longer parent.
17813 : */
17814 : static ObjectAddress
17815 94 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
17816 : {
17817 : ObjectAddress address;
17818 : Relation parent_rel;
17819 :
17820 94 : if (rel->rd_rel->relispartition)
17821 0 : ereport(ERROR,
17822 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17823 : errmsg("cannot change inheritance of a partition")));
17824 :
17825 : /*
17826 : * AccessShareLock on the parent is probably enough, seeing that DROP
17827 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
17828 : * be inspecting the parent's schema.
17829 : */
17830 94 : parent_rel = table_openrv(parent, AccessShareLock);
17831 :
17832 : /*
17833 : * We don't bother to check ownership of the parent table --- ownership of
17834 : * the child is presumed enough rights.
17835 : */
17836 :
17837 : /* Off to RemoveInheritance() where most of the work happens */
17838 94 : RemoveInheritance(rel, parent_rel, false);
17839 :
17840 88 : ObjectAddressSet(address, RelationRelationId,
17841 : RelationGetRelid(parent_rel));
17842 :
17843 : /* keep our lock on the parent relation until commit */
17844 88 : table_close(parent_rel, NoLock);
17845 :
17846 88 : return address;
17847 : }
17848 :
17849 : /*
17850 : * MarkInheritDetached
17851 : *
17852 : * Set inhdetachpending for a partition, for ATExecDetachPartition
17853 : * in concurrent mode. While at it, verify that no other partition is
17854 : * already pending detach.
17855 : */
17856 : static void
17857 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
17858 : {
17859 : Relation catalogRelation;
17860 : SysScanDesc scan;
17861 : ScanKeyData key;
17862 : HeapTuple inheritsTuple;
17863 146 : bool found = false;
17864 :
17865 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17866 :
17867 : /*
17868 : * Find pg_inherits entries by inhparent. (We need to scan them all in
17869 : * order to verify that no other partition is pending detach.)
17870 : */
17871 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17872 146 : ScanKeyInit(&key,
17873 : Anum_pg_inherits_inhparent,
17874 : BTEqualStrategyNumber, F_OIDEQ,
17875 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17876 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17877 : true, NULL, 1, &key);
17878 :
17879 576 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17880 : {
17881 : Form_pg_inherits inhForm;
17882 :
17883 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17884 286 : if (inhForm->inhdetachpending)
17885 2 : ereport(ERROR,
17886 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17887 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17888 : get_rel_name(inhForm->inhrelid),
17889 : get_namespace_name(parent_rel->rd_rel->relnamespace),
17890 : RelationGetRelationName(parent_rel)),
17891 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17892 :
17893 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
17894 : {
17895 : HeapTuple newtup;
17896 :
17897 144 : newtup = heap_copytuple(inheritsTuple);
17898 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17899 :
17900 144 : CatalogTupleUpdate(catalogRelation,
17901 144 : &inheritsTuple->t_self,
17902 : newtup);
17903 144 : found = true;
17904 144 : heap_freetuple(newtup);
17905 : /* keep looking, to ensure we catch others pending detach */
17906 : }
17907 : }
17908 :
17909 : /* Done */
17910 144 : systable_endscan(scan);
17911 144 : table_close(catalogRelation, RowExclusiveLock);
17912 :
17913 144 : if (!found)
17914 0 : ereport(ERROR,
17915 : (errcode(ERRCODE_UNDEFINED_TABLE),
17916 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17917 : RelationGetRelationName(child_rel),
17918 : RelationGetRelationName(parent_rel))));
17919 144 : }
17920 :
17921 : /*
17922 : * RemoveInheritance
17923 : *
17924 : * Drop a parent from the child's parents. This just adjusts the attinhcount
17925 : * and attislocal of the columns and removes the pg_inherit and pg_depend
17926 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17927 : *
17928 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17929 : * up attislocal stays true, which means if a child is ever removed from a
17930 : * parent then its columns will never be automatically dropped which may
17931 : * surprise. But at least we'll never surprise by dropping columns someone
17932 : * isn't expecting to be dropped which would actually mean data loss.
17933 : *
17934 : * coninhcount and conislocal for inherited constraints are adjusted in
17935 : * exactly the same way.
17936 : *
17937 : * Common to ATExecDropInherit() and ATExecDetachPartition().
17938 : */
17939 : static void
17940 606 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17941 : {
17942 : Relation catalogRelation;
17943 : SysScanDesc scan;
17944 : ScanKeyData key[3];
17945 : HeapTuple attributeTuple,
17946 : constraintTuple;
17947 : AttrMap *attmap;
17948 : List *connames;
17949 : List *nncolumns;
17950 : bool found;
17951 : bool is_partitioning;
17952 :
17953 606 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17954 :
17955 606 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17956 : RelationGetRelid(parent_rel),
17957 : expect_detached,
17958 606 : RelationGetRelationName(child_rel));
17959 606 : if (!found)
17960 : {
17961 24 : if (is_partitioning)
17962 18 : ereport(ERROR,
17963 : (errcode(ERRCODE_UNDEFINED_TABLE),
17964 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17965 : RelationGetRelationName(child_rel),
17966 : RelationGetRelationName(parent_rel))));
17967 : else
17968 6 : ereport(ERROR,
17969 : (errcode(ERRCODE_UNDEFINED_TABLE),
17970 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17971 : RelationGetRelationName(parent_rel),
17972 : RelationGetRelationName(child_rel))));
17973 : }
17974 :
17975 : /*
17976 : * Search through child columns looking for ones matching parent rel
17977 : */
17978 582 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17979 582 : ScanKeyInit(&key[0],
17980 : Anum_pg_attribute_attrelid,
17981 : BTEqualStrategyNumber, F_OIDEQ,
17982 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17983 582 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17984 : true, NULL, 1, key);
17985 5198 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17986 : {
17987 4616 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17988 :
17989 : /* Ignore if dropped or not inherited */
17990 4616 : if (att->attisdropped)
17991 6 : continue;
17992 4610 : if (att->attinhcount <= 0)
17993 3522 : continue;
17994 :
17995 1088 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
17996 1088 : NameStr(att->attname)))
17997 : {
17998 : /* Decrement inhcount and possibly set islocal to true */
17999 1034 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
18000 1034 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
18001 :
18002 1034 : copy_att->attinhcount--;
18003 1034 : if (copy_att->attinhcount == 0)
18004 1004 : copy_att->attislocal = true;
18005 :
18006 1034 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18007 1034 : heap_freetuple(copyTuple);
18008 : }
18009 : }
18010 582 : systable_endscan(scan);
18011 582 : table_close(catalogRelation, RowExclusiveLock);
18012 :
18013 : /*
18014 : * Likewise, find inherited check and not-null constraints and disinherit
18015 : * them. To do this, we first need a list of the names of the parent's
18016 : * check constraints. (We cheat a bit by only checking for name matches,
18017 : * assuming that the expressions will match.)
18018 : *
18019 : * For NOT NULL columns, we store column numbers to match, mapping them in
18020 : * to the child rel's attribute numbers.
18021 : */
18022 582 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
18023 : RelationGetDescr(parent_rel),
18024 : false);
18025 :
18026 582 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
18027 582 : ScanKeyInit(&key[0],
18028 : Anum_pg_constraint_conrelid,
18029 : BTEqualStrategyNumber, F_OIDEQ,
18030 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18031 582 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18032 : true, NULL, 1, key);
18033 :
18034 582 : connames = NIL;
18035 582 : nncolumns = NIL;
18036 :
18037 1236 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18038 : {
18039 654 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18040 :
18041 654 : if (con->connoinherit)
18042 110 : continue;
18043 :
18044 544 : if (con->contype == CONSTRAINT_CHECK)
18045 12 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
18046 544 : if (con->contype == CONSTRAINT_NOTNULL)
18047 : {
18048 208 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
18049 :
18050 208 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18051 : }
18052 : }
18053 :
18054 582 : systable_endscan(scan);
18055 :
18056 : /* Now scan the child's constraints to find matches */
18057 582 : ScanKeyInit(&key[0],
18058 : Anum_pg_constraint_conrelid,
18059 : BTEqualStrategyNumber, F_OIDEQ,
18060 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
18061 582 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18062 : true, NULL, 1, key);
18063 :
18064 1232 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18065 : {
18066 650 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18067 650 : bool match = false;
18068 :
18069 : /*
18070 : * Match CHECK constraints by name, not-null constraints by column
18071 : * number, and ignore all others.
18072 : */
18073 650 : if (con->contype == CONSTRAINT_CHECK)
18074 : {
18075 142 : foreach_ptr(char, chkname, connames)
18076 : {
18077 18 : if (con->contype == CONSTRAINT_CHECK &&
18078 18 : strcmp(NameStr(con->conname), chkname) == 0)
18079 : {
18080 12 : match = true;
18081 12 : connames = foreach_delete_current(connames, chkname);
18082 12 : break;
18083 : }
18084 : }
18085 : }
18086 582 : else if (con->contype == CONSTRAINT_NOTNULL)
18087 : {
18088 268 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18089 :
18090 542 : foreach_int(prevattno, nncolumns)
18091 : {
18092 214 : if (prevattno == child_attno)
18093 : {
18094 208 : match = true;
18095 208 : nncolumns = foreach_delete_current(nncolumns, prevattno);
18096 208 : break;
18097 : }
18098 : }
18099 : }
18100 : else
18101 314 : continue;
18102 :
18103 336 : if (match)
18104 : {
18105 : /* Decrement inhcount and possibly set islocal to true */
18106 220 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
18107 220 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18108 :
18109 220 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
18110 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18111 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
18112 :
18113 220 : copy_con->coninhcount--;
18114 220 : if (copy_con->coninhcount == 0)
18115 202 : copy_con->conislocal = true;
18116 :
18117 220 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18118 220 : heap_freetuple(copyTuple);
18119 : }
18120 : }
18121 :
18122 : /* We should have matched all constraints */
18123 582 : if (connames != NIL || nncolumns != NIL)
18124 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18125 : list_length(connames) + list_length(nncolumns),
18126 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18127 :
18128 582 : systable_endscan(scan);
18129 582 : table_close(catalogRelation, RowExclusiveLock);
18130 :
18131 582 : drop_parent_dependency(RelationGetRelid(child_rel),
18132 : RelationRelationId,
18133 : RelationGetRelid(parent_rel),
18134 : child_dependency_type(is_partitioning));
18135 :
18136 : /*
18137 : * Post alter hook of this inherits. Since object_access_hook doesn't take
18138 : * multiple object identifiers, we relay oid of parent relation using
18139 : * auxiliary_id argument.
18140 : */
18141 582 : InvokeObjectPostAlterHookArg(InheritsRelationId,
18142 : RelationGetRelid(child_rel), 0,
18143 : RelationGetRelid(parent_rel), false);
18144 582 : }
18145 :
18146 : /*
18147 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18148 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18149 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18150 : * be TypeRelationId). There's no convenient way to do this, so go trawling
18151 : * through pg_depend.
18152 : */
18153 : static void
18154 594 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18155 : DependencyType deptype)
18156 : {
18157 : Relation catalogRelation;
18158 : SysScanDesc scan;
18159 : ScanKeyData key[3];
18160 : HeapTuple depTuple;
18161 :
18162 594 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18163 :
18164 594 : ScanKeyInit(&key[0],
18165 : Anum_pg_depend_classid,
18166 : BTEqualStrategyNumber, F_OIDEQ,
18167 : ObjectIdGetDatum(RelationRelationId));
18168 594 : ScanKeyInit(&key[1],
18169 : Anum_pg_depend_objid,
18170 : BTEqualStrategyNumber, F_OIDEQ,
18171 : ObjectIdGetDatum(relid));
18172 594 : ScanKeyInit(&key[2],
18173 : Anum_pg_depend_objsubid,
18174 : BTEqualStrategyNumber, F_INT4EQ,
18175 : Int32GetDatum(0));
18176 :
18177 594 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18178 : NULL, 3, key);
18179 :
18180 1846 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18181 : {
18182 1252 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18183 :
18184 1252 : if (dep->refclassid == refclassid &&
18185 636 : dep->refobjid == refobjid &&
18186 594 : dep->refobjsubid == 0 &&
18187 594 : dep->deptype == deptype)
18188 594 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18189 : }
18190 :
18191 594 : systable_endscan(scan);
18192 594 : table_close(catalogRelation, RowExclusiveLock);
18193 594 : }
18194 :
18195 : /*
18196 : * ALTER TABLE OF
18197 : *
18198 : * Attach a table to a composite type, as though it had been created with CREATE
18199 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18200 : * subject table must not have inheritance parents. These restrictions ensure
18201 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18202 : *
18203 : * The address of the type is returned.
18204 : */
18205 : static ObjectAddress
18206 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18207 : {
18208 66 : Oid relid = RelationGetRelid(rel);
18209 : Type typetuple;
18210 : Form_pg_type typeform;
18211 : Oid typeid;
18212 : Relation inheritsRelation,
18213 : relationRelation;
18214 : SysScanDesc scan;
18215 : ScanKeyData key;
18216 : AttrNumber table_attno,
18217 : type_attno;
18218 : TupleDesc typeTupleDesc,
18219 : tableTupleDesc;
18220 : ObjectAddress tableobj,
18221 : typeobj;
18222 : HeapTuple classtuple;
18223 :
18224 : /* Validate the type. */
18225 66 : typetuple = typenameType(NULL, ofTypename, NULL);
18226 66 : check_of_type(typetuple);
18227 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
18228 66 : typeid = typeform->oid;
18229 :
18230 : /* Fail if the table has any inheritance parents. */
18231 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18232 66 : ScanKeyInit(&key,
18233 : Anum_pg_inherits_inhrelid,
18234 : BTEqualStrategyNumber, F_OIDEQ,
18235 : ObjectIdGetDatum(relid));
18236 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18237 : true, NULL, 1, &key);
18238 66 : if (HeapTupleIsValid(systable_getnext(scan)))
18239 6 : ereport(ERROR,
18240 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18241 : errmsg("typed tables cannot inherit")));
18242 60 : systable_endscan(scan);
18243 60 : table_close(inheritsRelation, AccessShareLock);
18244 :
18245 : /*
18246 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
18247 : * require that the order also match. However, attnotnull need not match.
18248 : */
18249 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18250 60 : tableTupleDesc = RelationGetDescr(rel);
18251 60 : table_attno = 1;
18252 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18253 : {
18254 : Form_pg_attribute type_attr,
18255 : table_attr;
18256 : const char *type_attname,
18257 : *table_attname;
18258 :
18259 : /* Get the next non-dropped type attribute. */
18260 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18261 154 : if (type_attr->attisdropped)
18262 44 : continue;
18263 110 : type_attname = NameStr(type_attr->attname);
18264 :
18265 : /* Get the next non-dropped table attribute. */
18266 : do
18267 : {
18268 122 : if (table_attno > tableTupleDesc->natts)
18269 6 : ereport(ERROR,
18270 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18271 : errmsg("table is missing column \"%s\"",
18272 : type_attname)));
18273 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18274 116 : table_attno++;
18275 116 : } while (table_attr->attisdropped);
18276 104 : table_attname = NameStr(table_attr->attname);
18277 :
18278 : /* Compare name. */
18279 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18280 6 : ereport(ERROR,
18281 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18282 : errmsg("table has column \"%s\" where type requires \"%s\"",
18283 : table_attname, type_attname)));
18284 :
18285 : /* Compare type. */
18286 98 : if (table_attr->atttypid != type_attr->atttypid ||
18287 92 : table_attr->atttypmod != type_attr->atttypmod ||
18288 86 : table_attr->attcollation != type_attr->attcollation)
18289 12 : ereport(ERROR,
18290 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18291 : errmsg("table \"%s\" has different type for column \"%s\"",
18292 : RelationGetRelationName(rel), type_attname)));
18293 : }
18294 36 : ReleaseTupleDesc(typeTupleDesc);
18295 :
18296 : /* Any remaining columns at the end of the table had better be dropped. */
18297 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
18298 : {
18299 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18300 : table_attno - 1);
18301 :
18302 6 : if (!table_attr->attisdropped)
18303 6 : ereport(ERROR,
18304 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18305 : errmsg("table has extra column \"%s\"",
18306 : NameStr(table_attr->attname))));
18307 : }
18308 :
18309 : /* If the table was already typed, drop the existing dependency. */
18310 30 : if (rel->rd_rel->reloftype)
18311 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18312 : DEPENDENCY_NORMAL);
18313 :
18314 : /* Record a dependency on the new type. */
18315 30 : tableobj.classId = RelationRelationId;
18316 30 : tableobj.objectId = relid;
18317 30 : tableobj.objectSubId = 0;
18318 30 : typeobj.classId = TypeRelationId;
18319 30 : typeobj.objectId = typeid;
18320 30 : typeobj.objectSubId = 0;
18321 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18322 :
18323 : /* Update pg_class.reloftype */
18324 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18325 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18326 30 : if (!HeapTupleIsValid(classtuple))
18327 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18328 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18329 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18330 :
18331 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18332 :
18333 30 : heap_freetuple(classtuple);
18334 30 : table_close(relationRelation, RowExclusiveLock);
18335 :
18336 30 : ReleaseSysCache(typetuple);
18337 :
18338 30 : return typeobj;
18339 : }
18340 :
18341 : /*
18342 : * ALTER TABLE NOT OF
18343 : *
18344 : * Detach a typed table from its originating type. Just clear reloftype and
18345 : * remove the dependency.
18346 : */
18347 : static void
18348 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
18349 : {
18350 6 : Oid relid = RelationGetRelid(rel);
18351 : Relation relationRelation;
18352 : HeapTuple tuple;
18353 :
18354 6 : if (!OidIsValid(rel->rd_rel->reloftype))
18355 0 : ereport(ERROR,
18356 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18357 : errmsg("\"%s\" is not a typed table",
18358 : RelationGetRelationName(rel))));
18359 :
18360 : /*
18361 : * We don't bother to check ownership of the type --- ownership of the
18362 : * table is presumed enough rights. No lock required on the type, either.
18363 : */
18364 :
18365 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18366 : DEPENDENCY_NORMAL);
18367 :
18368 : /* Clear pg_class.reloftype */
18369 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18370 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18371 6 : if (!HeapTupleIsValid(tuple))
18372 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18373 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18374 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18375 :
18376 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18377 :
18378 6 : heap_freetuple(tuple);
18379 6 : table_close(relationRelation, RowExclusiveLock);
18380 6 : }
18381 :
18382 : /*
18383 : * relation_mark_replica_identity: Update a table's replica identity
18384 : *
18385 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18386 : * index. Otherwise, it must be InvalidOid.
18387 : *
18388 : * Caller had better hold an exclusive lock on the relation, as the results
18389 : * of running two of these concurrently wouldn't be pretty.
18390 : */
18391 : static void
18392 464 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18393 : bool is_internal)
18394 : {
18395 : Relation pg_index;
18396 : Relation pg_class;
18397 : HeapTuple pg_class_tuple;
18398 : HeapTuple pg_index_tuple;
18399 : Form_pg_class pg_class_form;
18400 : Form_pg_index pg_index_form;
18401 : ListCell *index;
18402 :
18403 : /*
18404 : * Check whether relreplident has changed, and update it if so.
18405 : */
18406 464 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18407 464 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
18408 : ObjectIdGetDatum(RelationGetRelid(rel)));
18409 464 : if (!HeapTupleIsValid(pg_class_tuple))
18410 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
18411 : RelationGetRelationName(rel));
18412 464 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18413 464 : if (pg_class_form->relreplident != ri_type)
18414 : {
18415 414 : pg_class_form->relreplident = ri_type;
18416 414 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18417 : }
18418 464 : table_close(pg_class, RowExclusiveLock);
18419 464 : heap_freetuple(pg_class_tuple);
18420 :
18421 : /*
18422 : * Update the per-index indisreplident flags correctly.
18423 : */
18424 464 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
18425 1188 : foreach(index, RelationGetIndexList(rel))
18426 : {
18427 724 : Oid thisIndexOid = lfirst_oid(index);
18428 724 : bool dirty = false;
18429 :
18430 724 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18431 : ObjectIdGetDatum(thisIndexOid));
18432 724 : if (!HeapTupleIsValid(pg_index_tuple))
18433 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18434 724 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18435 :
18436 724 : if (thisIndexOid == indexOid)
18437 : {
18438 : /* Set the bit if not already set. */
18439 240 : if (!pg_index_form->indisreplident)
18440 : {
18441 222 : dirty = true;
18442 222 : pg_index_form->indisreplident = true;
18443 : }
18444 : }
18445 : else
18446 : {
18447 : /* Unset the bit if set. */
18448 484 : if (pg_index_form->indisreplident)
18449 : {
18450 52 : dirty = true;
18451 52 : pg_index_form->indisreplident = false;
18452 : }
18453 : }
18454 :
18455 724 : if (dirty)
18456 : {
18457 274 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18458 274 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18459 : InvalidOid, is_internal);
18460 :
18461 : /*
18462 : * Invalidate the relcache for the table, so that after we commit
18463 : * all sessions will refresh the table's replica identity index
18464 : * before attempting any UPDATE or DELETE on the table. (If we
18465 : * changed the table's pg_class row above, then a relcache inval
18466 : * is already queued due to that; but we might not have.)
18467 : */
18468 274 : CacheInvalidateRelcache(rel);
18469 : }
18470 724 : heap_freetuple(pg_index_tuple);
18471 : }
18472 :
18473 464 : table_close(pg_index, RowExclusiveLock);
18474 464 : }
18475 :
18476 : /*
18477 : * ALTER TABLE <name> REPLICA IDENTITY ...
18478 : */
18479 : static void
18480 512 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
18481 : {
18482 : Oid indexOid;
18483 : Relation indexRel;
18484 : int key;
18485 :
18486 512 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18487 : {
18488 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18489 6 : return;
18490 : }
18491 506 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18492 : {
18493 170 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18494 170 : return;
18495 : }
18496 336 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18497 : {
18498 48 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18499 48 : return;
18500 : }
18501 288 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18502 : {
18503 : /* fallthrough */ ;
18504 : }
18505 : else
18506 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18507 :
18508 : /* Check that the index exists */
18509 288 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18510 288 : if (!OidIsValid(indexOid))
18511 0 : ereport(ERROR,
18512 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18513 : errmsg("index \"%s\" for table \"%s\" does not exist",
18514 : stmt->name, RelationGetRelationName(rel))));
18515 :
18516 288 : indexRel = index_open(indexOid, ShareLock);
18517 :
18518 : /* Check that the index is on the relation we're altering. */
18519 288 : if (indexRel->rd_index == NULL ||
18520 288 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
18521 6 : ereport(ERROR,
18522 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18523 : errmsg("\"%s\" is not an index for table \"%s\"",
18524 : RelationGetRelationName(indexRel),
18525 : RelationGetRelationName(rel))));
18526 :
18527 : /*
18528 : * The AM must support uniqueness, and the index must in fact be unique.
18529 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18530 : * exclusion), we can use that too.
18531 : */
18532 282 : if ((!indexRel->rd_indam->amcanunique ||
18533 262 : !indexRel->rd_index->indisunique) &&
18534 26 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18535 12 : ereport(ERROR,
18536 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18537 : errmsg("cannot use non-unique index \"%s\" as replica identity",
18538 : RelationGetRelationName(indexRel))));
18539 : /* Deferred indexes are not guaranteed to be always unique. */
18540 270 : if (!indexRel->rd_index->indimmediate)
18541 12 : ereport(ERROR,
18542 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18543 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
18544 : RelationGetRelationName(indexRel))));
18545 : /* Expression indexes aren't supported. */
18546 258 : if (RelationGetIndexExpressions(indexRel) != NIL)
18547 6 : ereport(ERROR,
18548 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18549 : errmsg("cannot use expression index \"%s\" as replica identity",
18550 : RelationGetRelationName(indexRel))));
18551 : /* Predicate indexes aren't supported. */
18552 252 : if (RelationGetIndexPredicate(indexRel) != NIL)
18553 6 : ereport(ERROR,
18554 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18555 : errmsg("cannot use partial index \"%s\" as replica identity",
18556 : RelationGetRelationName(indexRel))));
18557 :
18558 : /* Check index for nullable columns. */
18559 552 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18560 : {
18561 312 : int16 attno = indexRel->rd_index->indkey.values[key];
18562 : Form_pg_attribute attr;
18563 :
18564 : /*
18565 : * Reject any other system columns. (Going forward, we'll disallow
18566 : * indexes containing such columns in the first place, but they might
18567 : * exist in older branches.)
18568 : */
18569 312 : if (attno <= 0)
18570 0 : ereport(ERROR,
18571 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18572 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18573 : RelationGetRelationName(indexRel), attno)));
18574 :
18575 312 : attr = TupleDescAttr(rel->rd_att, attno - 1);
18576 312 : if (!attr->attnotnull)
18577 6 : ereport(ERROR,
18578 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18579 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18580 : RelationGetRelationName(indexRel),
18581 : NameStr(attr->attname))));
18582 : }
18583 :
18584 : /* This index is suitable for use as a replica identity. Mark it. */
18585 240 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18586 :
18587 240 : index_close(indexRel, NoLock);
18588 : }
18589 :
18590 : /*
18591 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18592 : */
18593 : static void
18594 348 : ATExecSetRowSecurity(Relation rel, bool rls)
18595 : {
18596 : Relation pg_class;
18597 : Oid relid;
18598 : HeapTuple tuple;
18599 :
18600 348 : relid = RelationGetRelid(rel);
18601 :
18602 : /* Pull the record for this relation and update it */
18603 348 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18604 :
18605 348 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18606 :
18607 348 : if (!HeapTupleIsValid(tuple))
18608 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18609 :
18610 348 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18611 348 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18612 :
18613 348 : InvokeObjectPostAlterHook(RelationRelationId,
18614 : RelationGetRelid(rel), 0);
18615 :
18616 348 : table_close(pg_class, RowExclusiveLock);
18617 348 : heap_freetuple(tuple);
18618 348 : }
18619 :
18620 : /*
18621 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18622 : */
18623 : static void
18624 132 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
18625 : {
18626 : Relation pg_class;
18627 : Oid relid;
18628 : HeapTuple tuple;
18629 :
18630 132 : relid = RelationGetRelid(rel);
18631 :
18632 132 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18633 :
18634 132 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18635 :
18636 132 : if (!HeapTupleIsValid(tuple))
18637 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18638 :
18639 132 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18640 132 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18641 :
18642 132 : InvokeObjectPostAlterHook(RelationRelationId,
18643 : RelationGetRelid(rel), 0);
18644 :
18645 132 : table_close(pg_class, RowExclusiveLock);
18646 132 : heap_freetuple(tuple);
18647 132 : }
18648 :
18649 : /*
18650 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
18651 : */
18652 : static void
18653 58 : ATExecGenericOptions(Relation rel, List *options)
18654 : {
18655 : Relation ftrel;
18656 : ForeignServer *server;
18657 : ForeignDataWrapper *fdw;
18658 : HeapTuple tuple;
18659 : bool isnull;
18660 : Datum repl_val[Natts_pg_foreign_table];
18661 : bool repl_null[Natts_pg_foreign_table];
18662 : bool repl_repl[Natts_pg_foreign_table];
18663 : Datum datum;
18664 : Form_pg_foreign_table tableform;
18665 :
18666 58 : if (options == NIL)
18667 0 : return;
18668 :
18669 58 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18670 :
18671 58 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18672 : ObjectIdGetDatum(rel->rd_id));
18673 58 : if (!HeapTupleIsValid(tuple))
18674 0 : ereport(ERROR,
18675 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18676 : errmsg("foreign table \"%s\" does not exist",
18677 : RelationGetRelationName(rel))));
18678 58 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18679 58 : server = GetForeignServer(tableform->ftserver);
18680 58 : fdw = GetForeignDataWrapper(server->fdwid);
18681 :
18682 58 : memset(repl_val, 0, sizeof(repl_val));
18683 58 : memset(repl_null, false, sizeof(repl_null));
18684 58 : memset(repl_repl, false, sizeof(repl_repl));
18685 :
18686 : /* Extract the current options */
18687 58 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
18688 : tuple,
18689 : Anum_pg_foreign_table_ftoptions,
18690 : &isnull);
18691 58 : if (isnull)
18692 4 : datum = PointerGetDatum(NULL);
18693 :
18694 : /* Transform the options */
18695 58 : datum = transformGenericOptions(ForeignTableRelationId,
18696 : datum,
18697 : options,
18698 : fdw->fdwvalidator);
18699 :
18700 56 : if (DatumGetPointer(datum) != NULL)
18701 56 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18702 : else
18703 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18704 :
18705 56 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18706 :
18707 : /* Everything looks good - update the tuple */
18708 :
18709 56 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18710 : repl_val, repl_null, repl_repl);
18711 :
18712 56 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18713 :
18714 : /*
18715 : * Invalidate relcache so that all sessions will refresh any cached plans
18716 : * that might depend on the old options.
18717 : */
18718 56 : CacheInvalidateRelcache(rel);
18719 :
18720 56 : InvokeObjectPostAlterHook(ForeignTableRelationId,
18721 : RelationGetRelid(rel), 0);
18722 :
18723 56 : table_close(ftrel, RowExclusiveLock);
18724 :
18725 56 : heap_freetuple(tuple);
18726 : }
18727 :
18728 : /*
18729 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
18730 : *
18731 : * Return value is the address of the modified column
18732 : */
18733 : static ObjectAddress
18734 78 : ATExecSetCompression(Relation rel,
18735 : const char *column,
18736 : Node *newValue,
18737 : LOCKMODE lockmode)
18738 : {
18739 : Relation attrel;
18740 : HeapTuple tuple;
18741 : Form_pg_attribute atttableform;
18742 : AttrNumber attnum;
18743 : char *compression;
18744 : char cmethod;
18745 : ObjectAddress address;
18746 :
18747 78 : compression = strVal(newValue);
18748 :
18749 78 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
18750 :
18751 : /* copy the cache entry so we can scribble on it below */
18752 78 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18753 78 : if (!HeapTupleIsValid(tuple))
18754 0 : ereport(ERROR,
18755 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18756 : errmsg("column \"%s\" of relation \"%s\" does not exist",
18757 : column, RelationGetRelationName(rel))));
18758 :
18759 : /* prevent them from altering a system attribute */
18760 78 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18761 78 : attnum = atttableform->attnum;
18762 78 : if (attnum <= 0)
18763 0 : ereport(ERROR,
18764 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18765 : errmsg("cannot alter system column \"%s\"", column)));
18766 :
18767 : /*
18768 : * Check that column type is compressible, then get the attribute
18769 : * compression method code
18770 : */
18771 78 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18772 :
18773 : /* update pg_attribute entry */
18774 72 : atttableform->attcompression = cmethod;
18775 72 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18776 :
18777 72 : InvokeObjectPostAlterHook(RelationRelationId,
18778 : RelationGetRelid(rel),
18779 : attnum);
18780 :
18781 : /*
18782 : * Apply the change to indexes as well (only for simple index columns,
18783 : * matching behavior of index.c ConstructTupleDescriptor()).
18784 : */
18785 72 : SetIndexStorageProperties(rel, attrel, attnum,
18786 : false, 0,
18787 : true, cmethod,
18788 : lockmode);
18789 :
18790 72 : heap_freetuple(tuple);
18791 :
18792 72 : table_close(attrel, RowExclusiveLock);
18793 :
18794 : /* make changes visible */
18795 72 : CommandCounterIncrement();
18796 :
18797 72 : ObjectAddressSubSet(address, RelationRelationId,
18798 : RelationGetRelid(rel), attnum);
18799 72 : return address;
18800 : }
18801 :
18802 :
18803 : /*
18804 : * Preparation phase for SET LOGGED/UNLOGGED
18805 : *
18806 : * This verifies that we're not trying to change a temp table. Also,
18807 : * existing foreign key constraints are checked to avoid ending up with
18808 : * permanent tables referencing unlogged tables.
18809 : */
18810 : static void
18811 100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
18812 : {
18813 : Relation pg_constraint;
18814 : HeapTuple tuple;
18815 : SysScanDesc scan;
18816 : ScanKeyData skey[1];
18817 :
18818 : /*
18819 : * Disallow changing status for a temp table. Also verify whether we can
18820 : * get away with doing nothing; in such cases we don't need to run the
18821 : * checks below, either.
18822 : */
18823 100 : switch (rel->rd_rel->relpersistence)
18824 : {
18825 0 : case RELPERSISTENCE_TEMP:
18826 0 : ereport(ERROR,
18827 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18828 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
18829 : RelationGetRelationName(rel)),
18830 : errtable(rel)));
18831 : break;
18832 56 : case RELPERSISTENCE_PERMANENT:
18833 56 : if (toLogged)
18834 : /* nothing to do */
18835 12 : return;
18836 50 : break;
18837 44 : case RELPERSISTENCE_UNLOGGED:
18838 44 : if (!toLogged)
18839 : /* nothing to do */
18840 6 : return;
18841 38 : break;
18842 : }
18843 :
18844 : /*
18845 : * Check that the table is not part of any publication when changing to
18846 : * UNLOGGED, as UNLOGGED tables can't be published.
18847 : */
18848 138 : if (!toLogged &&
18849 50 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
18850 0 : ereport(ERROR,
18851 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18852 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18853 : RelationGetRelationName(rel)),
18854 : errdetail("Unlogged relations cannot be replicated.")));
18855 :
18856 : /*
18857 : * Check existing foreign key constraints to preserve the invariant that
18858 : * permanent tables cannot reference unlogged ones. Self-referencing
18859 : * foreign keys can safely be ignored.
18860 : */
18861 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18862 :
18863 : /*
18864 : * Scan conrelid if changing to permanent, else confrelid. This also
18865 : * determines whether a useful index exists.
18866 : */
18867 88 : ScanKeyInit(&skey[0],
18868 : toLogged ? Anum_pg_constraint_conrelid :
18869 : Anum_pg_constraint_confrelid,
18870 : BTEqualStrategyNumber, F_OIDEQ,
18871 : ObjectIdGetDatum(RelationGetRelid(rel)));
18872 88 : scan = systable_beginscan(pg_constraint,
18873 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18874 : true, NULL, 1, skey);
18875 :
18876 142 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18877 : {
18878 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
18879 :
18880 66 : if (con->contype == CONSTRAINT_FOREIGN)
18881 : {
18882 : Oid foreignrelid;
18883 : Relation foreignrel;
18884 :
18885 : /* the opposite end of what we used as scankey */
18886 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
18887 :
18888 : /* ignore if self-referencing */
18889 30 : if (RelationGetRelid(rel) == foreignrelid)
18890 12 : continue;
18891 :
18892 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
18893 :
18894 18 : if (toLogged)
18895 : {
18896 6 : if (!RelationIsPermanent(foreignrel))
18897 6 : ereport(ERROR,
18898 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18899 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18900 : RelationGetRelationName(rel),
18901 : RelationGetRelationName(foreignrel)),
18902 : errtableconstraint(rel, NameStr(con->conname))));
18903 : }
18904 : else
18905 : {
18906 12 : if (RelationIsPermanent(foreignrel))
18907 6 : ereport(ERROR,
18908 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18909 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18910 : RelationGetRelationName(rel),
18911 : RelationGetRelationName(foreignrel)),
18912 : errtableconstraint(rel, NameStr(con->conname))));
18913 : }
18914 :
18915 6 : relation_close(foreignrel, AccessShareLock);
18916 : }
18917 : }
18918 :
18919 76 : systable_endscan(scan);
18920 :
18921 76 : table_close(pg_constraint, AccessShareLock);
18922 :
18923 : /* force rewrite if necessary; see comment in ATRewriteTables */
18924 76 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
18925 76 : if (toLogged)
18926 32 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18927 : else
18928 44 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18929 76 : tab->chgPersistence = true;
18930 : }
18931 :
18932 : /*
18933 : * Execute ALTER TABLE SET SCHEMA
18934 : */
18935 : ObjectAddress
18936 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
18937 : {
18938 : Relation rel;
18939 : Oid relid;
18940 : Oid oldNspOid;
18941 : Oid nspOid;
18942 : RangeVar *newrv;
18943 : ObjectAddresses *objsMoved;
18944 : ObjectAddress myself;
18945 :
18946 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
18947 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
18948 : RangeVarCallbackForAlterRelation,
18949 : stmt);
18950 :
18951 102 : if (!OidIsValid(relid))
18952 : {
18953 12 : ereport(NOTICE,
18954 : (errmsg("relation \"%s\" does not exist, skipping",
18955 : stmt->relation->relname)));
18956 12 : return InvalidObjectAddress;
18957 : }
18958 :
18959 90 : rel = relation_open(relid, NoLock);
18960 :
18961 90 : oldNspOid = RelationGetNamespace(rel);
18962 :
18963 : /* If it's an owned sequence, disallow moving it by itself. */
18964 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18965 : {
18966 : Oid tableId;
18967 : int32 colId;
18968 :
18969 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18970 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18971 6 : ereport(ERROR,
18972 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18973 : errmsg("cannot move an owned sequence into another schema"),
18974 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
18975 : RelationGetRelationName(rel),
18976 : get_rel_name(tableId))));
18977 : }
18978 :
18979 : /* Get and lock schema OID and check its permissions. */
18980 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18981 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18982 :
18983 : /* common checks on switching namespaces */
18984 84 : CheckSetNamespace(oldNspOid, nspOid);
18985 :
18986 84 : objsMoved = new_object_addresses();
18987 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18988 84 : free_object_addresses(objsMoved);
18989 :
18990 84 : ObjectAddressSet(myself, RelationRelationId, relid);
18991 :
18992 84 : if (oldschema)
18993 84 : *oldschema = oldNspOid;
18994 :
18995 : /* close rel, but keep lock until commit */
18996 84 : relation_close(rel, NoLock);
18997 :
18998 84 : return myself;
18999 : }
19000 :
19001 : /*
19002 : * The guts of relocating a table or materialized view to another namespace:
19003 : * besides moving the relation itself, its dependent objects are relocated to
19004 : * the new schema.
19005 : */
19006 : void
19007 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
19008 : ObjectAddresses *objsMoved)
19009 : {
19010 : Relation classRel;
19011 :
19012 : Assert(objsMoved != NULL);
19013 :
19014 : /* OK, modify the pg_class row and pg_depend entry */
19015 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
19016 :
19017 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
19018 : nspOid, true, objsMoved);
19019 :
19020 : /* Fix the table's row type too, if it has one */
19021 86 : if (OidIsValid(rel->rd_rel->reltype))
19022 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
19023 : false, /* isImplicitArray */
19024 : false, /* ignoreDependent */
19025 : false, /* errorOnTableType */
19026 : objsMoved);
19027 :
19028 : /* Fix other dependent stuff */
19029 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19030 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19031 : objsMoved, AccessExclusiveLock);
19032 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19033 : false, objsMoved);
19034 :
19035 86 : table_close(classRel, RowExclusiveLock);
19036 86 : }
19037 :
19038 : /*
19039 : * The guts of relocating a relation to another namespace: fix the pg_class
19040 : * entry, and the pg_depend entry if any. Caller must already have
19041 : * opened and write-locked pg_class.
19042 : */
19043 : void
19044 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
19045 : Oid oldNspOid, Oid newNspOid,
19046 : bool hasDependEntry,
19047 : ObjectAddresses *objsMoved)
19048 : {
19049 : HeapTuple classTup;
19050 : Form_pg_class classForm;
19051 : ObjectAddress thisobj;
19052 188 : bool already_done = false;
19053 :
19054 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19055 188 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19056 188 : if (!HeapTupleIsValid(classTup))
19057 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
19058 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
19059 :
19060 : Assert(classForm->relnamespace == oldNspOid);
19061 :
19062 188 : thisobj.classId = RelationRelationId;
19063 188 : thisobj.objectId = relOid;
19064 188 : thisobj.objectSubId = 0;
19065 :
19066 : /*
19067 : * If the object has already been moved, don't move it again. If it's
19068 : * already in the right place, don't move it, but still fire the object
19069 : * access hook.
19070 : */
19071 188 : already_done = object_address_present(&thisobj, objsMoved);
19072 188 : if (!already_done && oldNspOid != newNspOid)
19073 146 : {
19074 146 : ItemPointerData otid = classTup->t_self;
19075 :
19076 : /* check for duplicate name (more friendly than unique-index failure) */
19077 146 : if (get_relname_relid(NameStr(classForm->relname),
19078 : newNspOid) != InvalidOid)
19079 0 : ereport(ERROR,
19080 : (errcode(ERRCODE_DUPLICATE_TABLE),
19081 : errmsg("relation \"%s\" already exists in schema \"%s\"",
19082 : NameStr(classForm->relname),
19083 : get_namespace_name(newNspOid))));
19084 :
19085 : /* classTup is a copy, so OK to scribble on */
19086 146 : classForm->relnamespace = newNspOid;
19087 :
19088 146 : CatalogTupleUpdate(classRel, &otid, classTup);
19089 146 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19090 :
19091 :
19092 : /* Update dependency on schema if caller said so */
19093 250 : if (hasDependEntry &&
19094 104 : changeDependencyFor(RelationRelationId,
19095 : relOid,
19096 : NamespaceRelationId,
19097 : oldNspOid,
19098 : newNspOid) != 1)
19099 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
19100 : NameStr(classForm->relname));
19101 : }
19102 : else
19103 42 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19104 188 : if (!already_done)
19105 : {
19106 188 : add_exact_object_address(&thisobj, objsMoved);
19107 :
19108 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19109 : }
19110 :
19111 188 : heap_freetuple(classTup);
19112 188 : }
19113 :
19114 : /*
19115 : * Move all indexes for the specified relation to another namespace.
19116 : *
19117 : * Note: we assume adequate permission checking was done by the caller,
19118 : * and that the caller has a suitable lock on the owning relation.
19119 : */
19120 : static void
19121 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
19122 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19123 : {
19124 : List *indexList;
19125 : ListCell *l;
19126 :
19127 86 : indexList = RelationGetIndexList(rel);
19128 :
19129 132 : foreach(l, indexList)
19130 : {
19131 46 : Oid indexOid = lfirst_oid(l);
19132 : ObjectAddress thisobj;
19133 :
19134 46 : thisobj.classId = RelationRelationId;
19135 46 : thisobj.objectId = indexOid;
19136 46 : thisobj.objectSubId = 0;
19137 :
19138 : /*
19139 : * Note: currently, the index will not have its own dependency on the
19140 : * namespace, so we don't need to do changeDependencyFor(). There's no
19141 : * row type in pg_type, either.
19142 : *
19143 : * XXX this objsMoved test may be pointless -- surely we have a single
19144 : * dependency link from a relation to each index?
19145 : */
19146 46 : if (!object_address_present(&thisobj, objsMoved))
19147 : {
19148 46 : AlterRelationNamespaceInternal(classRel, indexOid,
19149 : oldNspOid, newNspOid,
19150 : false, objsMoved);
19151 46 : add_exact_object_address(&thisobj, objsMoved);
19152 : }
19153 : }
19154 :
19155 86 : list_free(indexList);
19156 86 : }
19157 :
19158 : /*
19159 : * Move all identity and SERIAL-column sequences of the specified relation to another
19160 : * namespace.
19161 : *
19162 : * Note: we assume adequate permission checking was done by the caller,
19163 : * and that the caller has a suitable lock on the owning relation.
19164 : */
19165 : static void
19166 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
19167 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19168 : LOCKMODE lockmode)
19169 : {
19170 : Relation depRel;
19171 : SysScanDesc scan;
19172 : ScanKeyData key[2];
19173 : HeapTuple tup;
19174 :
19175 : /*
19176 : * SERIAL sequences are those having an auto dependency on one of the
19177 : * table's columns (we don't care *which* column, exactly).
19178 : */
19179 86 : depRel = table_open(DependRelationId, AccessShareLock);
19180 :
19181 86 : ScanKeyInit(&key[0],
19182 : Anum_pg_depend_refclassid,
19183 : BTEqualStrategyNumber, F_OIDEQ,
19184 : ObjectIdGetDatum(RelationRelationId));
19185 86 : ScanKeyInit(&key[1],
19186 : Anum_pg_depend_refobjid,
19187 : BTEqualStrategyNumber, F_OIDEQ,
19188 : ObjectIdGetDatum(RelationGetRelid(rel)));
19189 : /* we leave refobjsubid unspecified */
19190 :
19191 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19192 : NULL, 2, key);
19193 :
19194 616 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
19195 : {
19196 530 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19197 : Relation seqRel;
19198 :
19199 : /* skip dependencies other than auto dependencies on columns */
19200 530 : if (depForm->refobjsubid == 0 ||
19201 382 : depForm->classid != RelationRelationId ||
19202 42 : depForm->objsubid != 0 ||
19203 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19204 488 : continue;
19205 :
19206 : /* Use relation_open just in case it's an index */
19207 42 : seqRel = relation_open(depForm->objid, lockmode);
19208 :
19209 : /* skip non-sequence relations */
19210 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19211 : {
19212 : /* No need to keep the lock */
19213 0 : relation_close(seqRel, lockmode);
19214 0 : continue;
19215 : }
19216 :
19217 : /* Fix the pg_class and pg_depend entries */
19218 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
19219 : oldNspOid, newNspOid,
19220 : true, objsMoved);
19221 :
19222 : /*
19223 : * Sequences used to have entries in pg_type, but no longer do. If we
19224 : * ever re-instate that, we'll need to move the pg_type entry to the
19225 : * new namespace, too (using AlterTypeNamespaceInternal).
19226 : */
19227 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19228 :
19229 : /* Now we can close it. Keep the lock till end of transaction. */
19230 42 : relation_close(seqRel, NoLock);
19231 : }
19232 :
19233 86 : systable_endscan(scan);
19234 :
19235 86 : relation_close(depRel, AccessShareLock);
19236 86 : }
19237 :
19238 :
19239 : /*
19240 : * This code supports
19241 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19242 : *
19243 : * Because we only support this for TEMP tables, it's sufficient to remember
19244 : * the state in a backend-local data structure.
19245 : */
19246 :
19247 : /*
19248 : * Register a newly-created relation's ON COMMIT action.
19249 : */
19250 : void
19251 176 : register_on_commit_action(Oid relid, OnCommitAction action)
19252 : {
19253 : OnCommitItem *oc;
19254 : MemoryContext oldcxt;
19255 :
19256 : /*
19257 : * We needn't bother registering the relation unless there is an ON COMMIT
19258 : * action we need to take.
19259 : */
19260 176 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19261 24 : return;
19262 :
19263 152 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
19264 :
19265 152 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19266 152 : oc->relid = relid;
19267 152 : oc->oncommit = action;
19268 152 : oc->creating_subid = GetCurrentSubTransactionId();
19269 152 : oc->deleting_subid = InvalidSubTransactionId;
19270 :
19271 : /*
19272 : * We use lcons() here so that ON COMMIT actions are processed in reverse
19273 : * order of registration. That might not be essential but it seems
19274 : * reasonable.
19275 : */
19276 152 : on_commits = lcons(oc, on_commits);
19277 :
19278 152 : MemoryContextSwitchTo(oldcxt);
19279 : }
19280 :
19281 : /*
19282 : * Unregister any ON COMMIT action when a relation is deleted.
19283 : *
19284 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19285 : */
19286 : void
19287 49268 : remove_on_commit_action(Oid relid)
19288 : {
19289 : ListCell *l;
19290 :
19291 49414 : foreach(l, on_commits)
19292 : {
19293 286 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19294 :
19295 286 : if (oc->relid == relid)
19296 : {
19297 140 : oc->deleting_subid = GetCurrentSubTransactionId();
19298 140 : break;
19299 : }
19300 : }
19301 49268 : }
19302 :
19303 : /*
19304 : * Perform ON COMMIT actions.
19305 : *
19306 : * This is invoked just before actually committing, since it's possible
19307 : * to encounter errors.
19308 : */
19309 : void
19310 1026978 : PreCommit_on_commit_actions(void)
19311 : {
19312 : ListCell *l;
19313 1026978 : List *oids_to_truncate = NIL;
19314 1026978 : List *oids_to_drop = NIL;
19315 :
19316 1027800 : foreach(l, on_commits)
19317 : {
19318 822 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19319 :
19320 : /* Ignore entry if already dropped in this xact */
19321 822 : if (oc->deleting_subid != InvalidSubTransactionId)
19322 74 : continue;
19323 :
19324 748 : switch (oc->oncommit)
19325 : {
19326 0 : case ONCOMMIT_NOOP:
19327 : case ONCOMMIT_PRESERVE_ROWS:
19328 : /* Do nothing (there shouldn't be such entries, actually) */
19329 0 : break;
19330 694 : case ONCOMMIT_DELETE_ROWS:
19331 :
19332 : /*
19333 : * If this transaction hasn't accessed any temporary
19334 : * relations, we can skip truncating ON COMMIT DELETE ROWS
19335 : * tables, as they must still be empty.
19336 : */
19337 694 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
19338 448 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19339 694 : break;
19340 54 : case ONCOMMIT_DROP:
19341 54 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19342 54 : break;
19343 : }
19344 : }
19345 :
19346 : /*
19347 : * Truncate relations before dropping so that all dependencies between
19348 : * relations are removed after they are worked on. Doing it like this
19349 : * might be a waste as it is possible that a relation being truncated will
19350 : * be dropped anyway due to its parent being dropped, but this makes the
19351 : * code more robust because of not having to re-check that the relation
19352 : * exists at truncation time.
19353 : */
19354 1026978 : if (oids_to_truncate != NIL)
19355 382 : heap_truncate(oids_to_truncate);
19356 :
19357 1026972 : if (oids_to_drop != NIL)
19358 : {
19359 48 : ObjectAddresses *targetObjects = new_object_addresses();
19360 :
19361 102 : foreach(l, oids_to_drop)
19362 : {
19363 : ObjectAddress object;
19364 :
19365 54 : object.classId = RelationRelationId;
19366 54 : object.objectId = lfirst_oid(l);
19367 54 : object.objectSubId = 0;
19368 :
19369 : Assert(!object_address_present(&object, targetObjects));
19370 :
19371 54 : add_exact_object_address(&object, targetObjects);
19372 : }
19373 :
19374 : /*
19375 : * Object deletion might involve toast table access (to clean up
19376 : * toasted catalog entries), so ensure we have a valid snapshot.
19377 : */
19378 48 : PushActiveSnapshot(GetTransactionSnapshot());
19379 :
19380 : /*
19381 : * Since this is an automatic drop, rather than one directly initiated
19382 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19383 : */
19384 48 : performMultipleDeletions(targetObjects, DROP_CASCADE,
19385 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
19386 :
19387 48 : PopActiveSnapshot();
19388 :
19389 : #ifdef USE_ASSERT_CHECKING
19390 :
19391 : /*
19392 : * Note that table deletion will call remove_on_commit_action, so the
19393 : * entry should get marked as deleted.
19394 : */
19395 : foreach(l, on_commits)
19396 : {
19397 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19398 :
19399 : if (oc->oncommit != ONCOMMIT_DROP)
19400 : continue;
19401 :
19402 : Assert(oc->deleting_subid != InvalidSubTransactionId);
19403 : }
19404 : #endif
19405 : }
19406 1026972 : }
19407 :
19408 : /*
19409 : * Post-commit or post-abort cleanup for ON COMMIT management.
19410 : *
19411 : * All we do here is remove no-longer-needed OnCommitItem entries.
19412 : *
19413 : * During commit, remove entries that were deleted during this transaction;
19414 : * during abort, remove those created during this transaction.
19415 : */
19416 : void
19417 1076974 : AtEOXact_on_commit_actions(bool isCommit)
19418 : {
19419 : ListCell *cur_item;
19420 :
19421 1077826 : foreach(cur_item, on_commits)
19422 : {
19423 852 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19424 :
19425 954 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19426 102 : oc->creating_subid != InvalidSubTransactionId)
19427 : {
19428 : /* cur_item must be removed */
19429 152 : on_commits = foreach_delete_current(on_commits, cur_item);
19430 152 : pfree(oc);
19431 : }
19432 : else
19433 : {
19434 : /* cur_item must be preserved */
19435 700 : oc->creating_subid = InvalidSubTransactionId;
19436 700 : oc->deleting_subid = InvalidSubTransactionId;
19437 : }
19438 : }
19439 1076974 : }
19440 :
19441 : /*
19442 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19443 : *
19444 : * During subabort, we can immediately remove entries created during this
19445 : * subtransaction. During subcommit, just relabel entries marked during
19446 : * this subtransaction as being the parent's responsibility.
19447 : */
19448 : void
19449 20128 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
19450 : SubTransactionId parentSubid)
19451 : {
19452 : ListCell *cur_item;
19453 :
19454 20128 : foreach(cur_item, on_commits)
19455 : {
19456 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19457 :
19458 0 : if (!isCommit && oc->creating_subid == mySubid)
19459 : {
19460 : /* cur_item must be removed */
19461 0 : on_commits = foreach_delete_current(on_commits, cur_item);
19462 0 : pfree(oc);
19463 : }
19464 : else
19465 : {
19466 : /* cur_item must be preserved */
19467 0 : if (oc->creating_subid == mySubid)
19468 0 : oc->creating_subid = parentSubid;
19469 0 : if (oc->deleting_subid == mySubid)
19470 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19471 : }
19472 : }
19473 20128 : }
19474 :
19475 : /*
19476 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19477 : * the relation to be locked only if (1) it's a plain or partitioned table,
19478 : * materialized view, or TOAST table and (2) the current user is the owner (or
19479 : * the superuser) or has been granted MAINTAIN. This meets the
19480 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19481 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19482 : */
19483 : void
19484 1014 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
19485 : Oid relId, Oid oldRelId, void *arg)
19486 : {
19487 : char relkind;
19488 : AclResult aclresult;
19489 :
19490 : /* Nothing to do if the relation was not found. */
19491 1014 : if (!OidIsValid(relId))
19492 6 : return;
19493 :
19494 : /*
19495 : * If the relation does exist, check whether it's an index. But note that
19496 : * the relation might have been dropped between the time we did the name
19497 : * lookup and now. In that case, there's nothing to do.
19498 : */
19499 1008 : relkind = get_rel_relkind(relId);
19500 1008 : if (!relkind)
19501 0 : return;
19502 1008 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19503 140 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19504 28 : ereport(ERROR,
19505 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19506 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19507 :
19508 : /* Check permissions */
19509 980 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19510 980 : if (aclresult != ACLCHECK_OK)
19511 30 : aclcheck_error(aclresult,
19512 30 : get_relkind_objtype(get_rel_relkind(relId)),
19513 30 : relation->relname);
19514 : }
19515 :
19516 : /*
19517 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19518 : */
19519 : static void
19520 2156 : RangeVarCallbackForTruncate(const RangeVar *relation,
19521 : Oid relId, Oid oldRelId, void *arg)
19522 : {
19523 : HeapTuple tuple;
19524 :
19525 : /* Nothing to do if the relation was not found. */
19526 2156 : if (!OidIsValid(relId))
19527 0 : return;
19528 :
19529 2156 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19530 2156 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19531 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19532 :
19533 2156 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
19534 2150 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
19535 :
19536 2118 : ReleaseSysCache(tuple);
19537 : }
19538 :
19539 : /*
19540 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19541 : * the owner of the relation, or superuser.
19542 : */
19543 : void
19544 15940 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
19545 : Oid relId, Oid oldRelId, void *arg)
19546 : {
19547 : HeapTuple tuple;
19548 :
19549 : /* Nothing to do if the relation was not found. */
19550 15940 : if (!OidIsValid(relId))
19551 6 : return;
19552 :
19553 15934 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19554 15934 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19555 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19556 :
19557 15934 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19558 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
19559 6 : relation->relname);
19560 :
19561 31736 : if (!allowSystemTableMods &&
19562 15808 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19563 2 : ereport(ERROR,
19564 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19565 : errmsg("permission denied: \"%s\" is a system catalog",
19566 : relation->relname)));
19567 :
19568 15926 : ReleaseSysCache(tuple);
19569 : }
19570 :
19571 : /*
19572 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
19573 : * processing.
19574 : */
19575 : static void
19576 33938 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
19577 : void *arg)
19578 : {
19579 33938 : Node *stmt = (Node *) arg;
19580 : ObjectType reltype;
19581 : HeapTuple tuple;
19582 : Form_pg_class classform;
19583 : AclResult aclresult;
19584 : char relkind;
19585 :
19586 33938 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19587 33938 : if (!HeapTupleIsValid(tuple))
19588 218 : return; /* concurrently dropped */
19589 33720 : classform = (Form_pg_class) GETSTRUCT(tuple);
19590 33720 : relkind = classform->relkind;
19591 :
19592 : /* Must own relation. */
19593 33720 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19594 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
19595 :
19596 : /* No system table modifications unless explicitly allowed. */
19597 33660 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
19598 30 : ereport(ERROR,
19599 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19600 : errmsg("permission denied: \"%s\" is a system catalog",
19601 : rv->relname)));
19602 :
19603 : /*
19604 : * Extract the specified relation type from the statement parse tree.
19605 : *
19606 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
19607 : * have CREATE rights on the containing namespace.
19608 : */
19609 33630 : if (IsA(stmt, RenameStmt))
19610 : {
19611 510 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19612 : GetUserId(), ACL_CREATE);
19613 510 : if (aclresult != ACLCHECK_OK)
19614 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
19615 0 : get_namespace_name(classform->relnamespace));
19616 510 : reltype = ((RenameStmt *) stmt)->renameType;
19617 : }
19618 33120 : else if (IsA(stmt, AlterObjectSchemaStmt))
19619 92 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19620 :
19621 33028 : else if (IsA(stmt, AlterTableStmt))
19622 33028 : reltype = ((AlterTableStmt *) stmt)->objtype;
19623 : else
19624 : {
19625 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19626 : reltype = OBJECT_TABLE; /* placate compiler */
19627 : }
19628 :
19629 : /*
19630 : * For compatibility with prior releases, we allow ALTER TABLE to be used
19631 : * with most other types of relations (but not composite types). We allow
19632 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
19633 : * otherwise. Otherwise, the user must select the correct form of the
19634 : * command for the relation at issue.
19635 : */
19636 33630 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19637 0 : ereport(ERROR,
19638 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19639 : errmsg("\"%s\" is not a sequence", rv->relname)));
19640 :
19641 33630 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19642 0 : ereport(ERROR,
19643 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19644 : errmsg("\"%s\" is not a view", rv->relname)));
19645 :
19646 33630 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19647 0 : ereport(ERROR,
19648 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19649 : errmsg("\"%s\" is not a materialized view", rv->relname)));
19650 :
19651 33630 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19652 0 : ereport(ERROR,
19653 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19654 : errmsg("\"%s\" is not a foreign table", rv->relname)));
19655 :
19656 33630 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19657 0 : ereport(ERROR,
19658 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19659 : errmsg("\"%s\" is not a composite type", rv->relname)));
19660 :
19661 33630 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19662 : relkind != RELKIND_PARTITIONED_INDEX
19663 40 : && !IsA(stmt, RenameStmt))
19664 6 : ereport(ERROR,
19665 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19666 : errmsg("\"%s\" is not an index", rv->relname)));
19667 :
19668 : /*
19669 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19670 : * TYPE for that.
19671 : */
19672 33624 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19673 0 : ereport(ERROR,
19674 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19675 : errmsg("\"%s\" is a composite type", rv->relname),
19676 : /* translator: %s is an SQL ALTER command */
19677 : errhint("Use %s instead.",
19678 : "ALTER TYPE")));
19679 :
19680 : /*
19681 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19682 : * to a different schema, such as indexes and TOAST tables.
19683 : */
19684 33624 : if (IsA(stmt, AlterObjectSchemaStmt))
19685 : {
19686 92 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19687 0 : ereport(ERROR,
19688 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19689 : errmsg("cannot change schema of index \"%s\"",
19690 : rv->relname),
19691 : errhint("Change the schema of the table instead.")));
19692 92 : else if (relkind == RELKIND_COMPOSITE_TYPE)
19693 0 : ereport(ERROR,
19694 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19695 : errmsg("cannot change schema of composite type \"%s\"",
19696 : rv->relname),
19697 : /* translator: %s is an SQL ALTER command */
19698 : errhint("Use %s instead.",
19699 : "ALTER TYPE")));
19700 92 : else if (relkind == RELKIND_TOASTVALUE)
19701 0 : ereport(ERROR,
19702 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19703 : errmsg("cannot change schema of TOAST table \"%s\"",
19704 : rv->relname),
19705 : errhint("Change the schema of the table instead.")));
19706 : }
19707 :
19708 33624 : ReleaseSysCache(tuple);
19709 : }
19710 :
19711 : /*
19712 : * Transform any expressions present in the partition key
19713 : *
19714 : * Returns a transformed PartitionSpec.
19715 : */
19716 : static PartitionSpec *
19717 5106 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
19718 : {
19719 : PartitionSpec *newspec;
19720 : ParseState *pstate;
19721 : ParseNamespaceItem *nsitem;
19722 : ListCell *l;
19723 :
19724 5106 : newspec = makeNode(PartitionSpec);
19725 :
19726 5106 : newspec->strategy = partspec->strategy;
19727 5106 : newspec->partParams = NIL;
19728 5106 : newspec->location = partspec->location;
19729 :
19730 : /* Check valid number of columns for strategy */
19731 7640 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19732 2534 : list_length(partspec->partParams) != 1)
19733 6 : ereport(ERROR,
19734 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19735 : errmsg("cannot use \"list\" partition strategy with more than one column")));
19736 :
19737 : /*
19738 : * Create a dummy ParseState and insert the target relation as its sole
19739 : * rangetable entry. We need a ParseState for transformExpr.
19740 : */
19741 5100 : pstate = make_parsestate(NULL);
19742 5100 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19743 : NULL, false, true);
19744 5100 : addNSItemToQuery(pstate, nsitem, true, true, true);
19745 :
19746 : /* take care of any partition expressions */
19747 10626 : foreach(l, partspec->partParams)
19748 : {
19749 5550 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
19750 :
19751 5550 : if (pelem->expr)
19752 : {
19753 : /* Copy, to avoid scribbling on the input */
19754 340 : pelem = copyObject(pelem);
19755 :
19756 : /* Now do parse transformation of the expression */
19757 340 : pelem->expr = transformExpr(pstate, pelem->expr,
19758 : EXPR_KIND_PARTITION_EXPRESSION);
19759 :
19760 : /* we have to fix its collations too */
19761 316 : assign_expr_collations(pstate, pelem->expr);
19762 : }
19763 :
19764 5526 : newspec->partParams = lappend(newspec->partParams, pelem);
19765 : }
19766 :
19767 5076 : return newspec;
19768 : }
19769 :
19770 : /*
19771 : * Compute per-partition-column information from a list of PartitionElems.
19772 : * Expressions in the PartitionElems must be parse-analyzed already.
19773 : */
19774 : static void
19775 5076 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19776 : List **partexprs, Oid *partopclass, Oid *partcollation,
19777 : PartitionStrategy strategy)
19778 : {
19779 : int attn;
19780 : ListCell *lc;
19781 : Oid am_oid;
19782 :
19783 5076 : attn = 0;
19784 10470 : foreach(lc, partParams)
19785 : {
19786 5526 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
19787 : Oid atttype;
19788 : Oid attcollation;
19789 :
19790 5526 : if (pelem->name != NULL)
19791 : {
19792 : /* Simple attribute reference */
19793 : HeapTuple atttuple;
19794 : Form_pg_attribute attform;
19795 :
19796 5210 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
19797 5210 : pelem->name);
19798 5210 : if (!HeapTupleIsValid(atttuple))
19799 12 : ereport(ERROR,
19800 : (errcode(ERRCODE_UNDEFINED_COLUMN),
19801 : errmsg("column \"%s\" named in partition key does not exist",
19802 : pelem->name),
19803 : parser_errposition(pstate, pelem->location)));
19804 5198 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19805 :
19806 5198 : if (attform->attnum <= 0)
19807 6 : ereport(ERROR,
19808 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19809 : errmsg("cannot use system column \"%s\" in partition key",
19810 : pelem->name),
19811 : parser_errposition(pstate, pelem->location)));
19812 :
19813 : /*
19814 : * Stored generated columns cannot work: They are computed after
19815 : * BEFORE triggers, but partition routing is done before all
19816 : * triggers. Maybe virtual generated columns could be made to
19817 : * work, but then they would need to be handled as an expression
19818 : * below.
19819 : */
19820 5192 : if (attform->attgenerated)
19821 12 : ereport(ERROR,
19822 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19823 : errmsg("cannot use generated column in partition key"),
19824 : errdetail("Column \"%s\" is a generated column.",
19825 : pelem->name),
19826 : parser_errposition(pstate, pelem->location)));
19827 :
19828 5180 : partattrs[attn] = attform->attnum;
19829 5180 : atttype = attform->atttypid;
19830 5180 : attcollation = attform->attcollation;
19831 5180 : ReleaseSysCache(atttuple);
19832 : }
19833 : else
19834 : {
19835 : /* Expression */
19836 316 : Node *expr = pelem->expr;
19837 : char partattname[16];
19838 316 : Bitmapset *expr_attrs = NULL;
19839 : int i;
19840 :
19841 : Assert(expr != NULL);
19842 316 : atttype = exprType(expr);
19843 316 : attcollation = exprCollation(expr);
19844 :
19845 : /*
19846 : * The expression must be of a storable type (e.g., not RECORD).
19847 : * The test is the same as for whether a table column is of a safe
19848 : * type (which is why we needn't check for the non-expression
19849 : * case).
19850 : */
19851 316 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19852 316 : CheckAttributeType(partattname,
19853 : atttype, attcollation,
19854 : NIL, CHKATYPE_IS_PARTKEY);
19855 :
19856 : /*
19857 : * Strip any top-level COLLATE clause. This ensures that we treat
19858 : * "x COLLATE y" and "(x COLLATE y)" alike.
19859 : */
19860 304 : while (IsA(expr, CollateExpr))
19861 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
19862 :
19863 : /*
19864 : * Examine all the columns in the partition key expression. When
19865 : * the whole-row reference is present, examine all the columns of
19866 : * the partitioned table.
19867 : */
19868 304 : pull_varattnos(expr, 1, &expr_attrs);
19869 304 : if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
19870 : {
19871 60 : expr_attrs = bms_add_range(expr_attrs,
19872 : 1 - FirstLowInvalidHeapAttributeNumber,
19873 30 : RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
19874 30 : expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
19875 : }
19876 :
19877 304 : i = -1;
19878 672 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
19879 : {
19880 416 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
19881 :
19882 : Assert(attno != 0);
19883 :
19884 : /*
19885 : * Cannot allow system column references, since that would
19886 : * make partition routing impossible: their values won't be
19887 : * known yet when we need to do that.
19888 : */
19889 416 : if (attno < 0)
19890 0 : ereport(ERROR,
19891 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19892 : errmsg("partition key expressions cannot contain system column references")));
19893 :
19894 : /*
19895 : * Stored generated columns cannot work: They are computed
19896 : * after BEFORE triggers, but partition routing is done before
19897 : * all triggers. Virtual generated columns could probably
19898 : * work, but it would require more work elsewhere (for example
19899 : * SET EXPRESSION would need to check whether the column is
19900 : * used in partition keys). Seems safer to prohibit for now.
19901 : */
19902 416 : if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19903 48 : ereport(ERROR,
19904 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19905 : errmsg("cannot use generated column in partition key"),
19906 : errdetail("Column \"%s\" is a generated column.",
19907 : get_attname(RelationGetRelid(rel), attno, false)),
19908 : parser_errposition(pstate, pelem->location)));
19909 : }
19910 :
19911 256 : if (IsA(expr, Var) &&
19912 12 : ((Var *) expr)->varattno > 0)
19913 : {
19914 :
19915 : /*
19916 : * User wrote "(column)" or "(column COLLATE something)".
19917 : * Treat it like simple attribute anyway.
19918 : */
19919 6 : partattrs[attn] = ((Var *) expr)->varattno;
19920 : }
19921 : else
19922 : {
19923 250 : partattrs[attn] = 0; /* marks the column as expression */
19924 250 : *partexprs = lappend(*partexprs, expr);
19925 :
19926 : /*
19927 : * transformPartitionSpec() should have already rejected
19928 : * subqueries, aggregates, window functions, and SRFs, based
19929 : * on the EXPR_KIND_ for partition expressions.
19930 : */
19931 :
19932 : /*
19933 : * Preprocess the expression before checking for mutability.
19934 : * This is essential for the reasons described in
19935 : * contain_mutable_functions_after_planning. However, we call
19936 : * expression_planner for ourselves rather than using that
19937 : * function, because if constant-folding reduces the
19938 : * expression to a constant, we'd like to know that so we can
19939 : * complain below.
19940 : *
19941 : * Like contain_mutable_functions_after_planning, assume that
19942 : * expression_planner won't scribble on its input, so this
19943 : * won't affect the partexprs entry we saved above.
19944 : */
19945 250 : expr = (Node *) expression_planner((Expr *) expr);
19946 :
19947 : /*
19948 : * Partition expressions cannot contain mutable functions,
19949 : * because a given row must always map to the same partition
19950 : * as long as there is no change in the partition boundary
19951 : * structure.
19952 : */
19953 250 : if (contain_mutable_functions(expr))
19954 6 : ereport(ERROR,
19955 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19956 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
19957 :
19958 : /*
19959 : * While it is not exactly *wrong* for a partition expression
19960 : * to be a constant, it seems better to reject such keys.
19961 : */
19962 244 : if (IsA(expr, Const))
19963 12 : ereport(ERROR,
19964 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19965 : errmsg("cannot use constant expression as partition key")));
19966 : }
19967 : }
19968 :
19969 : /*
19970 : * Apply collation override if any
19971 : */
19972 5418 : if (pelem->collation)
19973 54 : attcollation = get_collation_oid(pelem->collation, false);
19974 :
19975 : /*
19976 : * Check we have a collation iff it's a collatable type. The only
19977 : * expected failures here are (1) COLLATE applied to a noncollatable
19978 : * type, or (2) partition expression had an unresolved collation. But
19979 : * we might as well code this to be a complete consistency check.
19980 : */
19981 5418 : if (type_is_collatable(atttype))
19982 : {
19983 606 : if (!OidIsValid(attcollation))
19984 0 : ereport(ERROR,
19985 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
19986 : errmsg("could not determine which collation to use for partition expression"),
19987 : errhint("Use the COLLATE clause to set the collation explicitly.")));
19988 : }
19989 : else
19990 : {
19991 4812 : if (OidIsValid(attcollation))
19992 0 : ereport(ERROR,
19993 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19994 : errmsg("collations are not supported by type %s",
19995 : format_type_be(atttype))));
19996 : }
19997 :
19998 5418 : partcollation[attn] = attcollation;
19999 :
20000 : /*
20001 : * Identify the appropriate operator class. For list and range
20002 : * partitioning, we use a btree operator class; hash partitioning uses
20003 : * a hash operator class.
20004 : */
20005 5418 : if (strategy == PARTITION_STRATEGY_HASH)
20006 320 : am_oid = HASH_AM_OID;
20007 : else
20008 5098 : am_oid = BTREE_AM_OID;
20009 :
20010 5418 : if (!pelem->opclass)
20011 : {
20012 5280 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20013 :
20014 5280 : if (!OidIsValid(partopclass[attn]))
20015 : {
20016 12 : if (strategy == PARTITION_STRATEGY_HASH)
20017 0 : ereport(ERROR,
20018 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20019 : errmsg("data type %s has no default operator class for access method \"%s\"",
20020 : format_type_be(atttype), "hash"),
20021 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20022 : else
20023 12 : ereport(ERROR,
20024 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20025 : errmsg("data type %s has no default operator class for access method \"%s\"",
20026 : format_type_be(atttype), "btree"),
20027 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20028 : }
20029 : }
20030 : else
20031 138 : partopclass[attn] = ResolveOpClass(pelem->opclass,
20032 : atttype,
20033 : am_oid == HASH_AM_OID ? "hash" : "btree",
20034 : am_oid);
20035 :
20036 5394 : attn++;
20037 : }
20038 4944 : }
20039 :
20040 : /*
20041 : * PartConstraintImpliedByRelConstraint
20042 : * Do scanrel's existing constraints imply the partition constraint?
20043 : *
20044 : * "Existing constraints" include its check constraints and column-level
20045 : * not-null constraints. partConstraint describes the partition constraint,
20046 : * in implicit-AND form.
20047 : */
20048 : bool
20049 3084 : PartConstraintImpliedByRelConstraint(Relation scanrel,
20050 : List *partConstraint)
20051 : {
20052 3084 : List *existConstraint = NIL;
20053 3084 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20054 : int i;
20055 :
20056 3084 : if (constr && constr->has_not_null)
20057 : {
20058 790 : int natts = scanrel->rd_att->natts;
20059 :
20060 2614 : for (i = 1; i <= natts; i++)
20061 : {
20062 1824 : CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20063 :
20064 : /* invalid not-null constraint must be ignored here */
20065 1824 : if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20066 : {
20067 1086 : Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20068 1086 : NullTest *ntest = makeNode(NullTest);
20069 :
20070 1086 : ntest->arg = (Expr *) makeVar(1,
20071 : i,
20072 : wholeatt->atttypid,
20073 : wholeatt->atttypmod,
20074 : wholeatt->attcollation,
20075 : 0);
20076 1086 : ntest->nulltesttype = IS_NOT_NULL;
20077 :
20078 : /*
20079 : * argisrow=false is correct even for a composite column,
20080 : * because attnotnull does not represent a SQL-spec IS NOT
20081 : * NULL test in such a case, just IS DISTINCT FROM NULL.
20082 : */
20083 1086 : ntest->argisrow = false;
20084 1086 : ntest->location = -1;
20085 1086 : existConstraint = lappend(existConstraint, ntest);
20086 : }
20087 : }
20088 : }
20089 :
20090 3084 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20091 : }
20092 :
20093 : /*
20094 : * ConstraintImpliedByRelConstraint
20095 : * Do scanrel's existing constraints imply the given constraint?
20096 : *
20097 : * testConstraint is the constraint to validate. provenConstraint is a
20098 : * caller-provided list of conditions which this function may assume
20099 : * to be true. Both provenConstraint and testConstraint must be in
20100 : * implicit-AND form, must only contain immutable clauses, and must
20101 : * contain only Vars with varno = 1.
20102 : */
20103 : bool
20104 4328 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20105 : {
20106 4328 : List *existConstraint = list_copy(provenConstraint);
20107 4328 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20108 : int num_check,
20109 : i;
20110 :
20111 4328 : num_check = (constr != NULL) ? constr->num_check : 0;
20112 4836 : for (i = 0; i < num_check; i++)
20113 : {
20114 : Node *cexpr;
20115 :
20116 : /*
20117 : * If this constraint hasn't been fully validated yet, we must ignore
20118 : * it here.
20119 : */
20120 508 : if (!constr->check[i].ccvalid)
20121 6 : continue;
20122 :
20123 : /*
20124 : * NOT ENFORCED constraints are always marked as invalid, which should
20125 : * have been ignored.
20126 : */
20127 : Assert(constr->check[i].ccenforced);
20128 :
20129 502 : cexpr = stringToNode(constr->check[i].ccbin);
20130 :
20131 : /*
20132 : * Run each expression through const-simplification and
20133 : * canonicalization. It is necessary, because we will be comparing it
20134 : * to similarly-processed partition constraint expressions, and may
20135 : * fail to detect valid matches without this.
20136 : */
20137 502 : cexpr = eval_const_expressions(NULL, cexpr);
20138 502 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20139 :
20140 502 : existConstraint = list_concat(existConstraint,
20141 502 : make_ands_implicit((Expr *) cexpr));
20142 : }
20143 :
20144 : /*
20145 : * Try to make the proof. Since we are comparing CHECK constraints, we
20146 : * need to use weak implication, i.e., we assume existConstraint is
20147 : * not-false and try to prove the same for testConstraint.
20148 : *
20149 : * Note that predicate_implied_by assumes its first argument is known
20150 : * immutable. That should always be true for both NOT NULL and partition
20151 : * constraints, so we don't test it here.
20152 : */
20153 4328 : return predicate_implied_by(testConstraint, existConstraint, true);
20154 : }
20155 :
20156 : /*
20157 : * QueuePartitionConstraintValidation
20158 : *
20159 : * Add an entry to wqueue to have the given partition constraint validated by
20160 : * Phase 3, for the given relation, and all its children.
20161 : *
20162 : * We first verify whether the given constraint is implied by pre-existing
20163 : * relation constraints; if it is, there's no need to scan the table to
20164 : * validate, so don't queue in that case.
20165 : */
20166 : static void
20167 2594 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
20168 : List *partConstraint,
20169 : bool validate_default)
20170 : {
20171 : /*
20172 : * Based on the table's existing constraints, determine whether or not we
20173 : * may skip scanning the table.
20174 : */
20175 2594 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20176 : {
20177 88 : if (!validate_default)
20178 66 : ereport(DEBUG1,
20179 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20180 : RelationGetRelationName(scanrel))));
20181 : else
20182 22 : ereport(DEBUG1,
20183 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20184 : RelationGetRelationName(scanrel))));
20185 88 : return;
20186 : }
20187 :
20188 : /*
20189 : * Constraints proved insufficient. For plain relations, queue a
20190 : * validation item now; for partitioned tables, recurse to process each
20191 : * partition.
20192 : */
20193 2506 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20194 : {
20195 : AlteredTableInfo *tab;
20196 :
20197 : /* Grab a work queue entry. */
20198 2088 : tab = ATGetQueueEntry(wqueue, scanrel);
20199 : Assert(tab->partition_constraint == NULL);
20200 2088 : tab->partition_constraint = (Expr *) linitial(partConstraint);
20201 2088 : tab->validate_default = validate_default;
20202 : }
20203 418 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20204 : {
20205 366 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20206 : int i;
20207 :
20208 756 : for (i = 0; i < partdesc->nparts; i++)
20209 : {
20210 : Relation part_rel;
20211 : List *thisPartConstraint;
20212 :
20213 : /*
20214 : * This is the minimum lock we need to prevent deadlocks.
20215 : */
20216 390 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20217 :
20218 : /*
20219 : * Adjust the constraint for scanrel so that it matches this
20220 : * partition's attribute numbers.
20221 : */
20222 : thisPartConstraint =
20223 390 : map_partition_varattnos(partConstraint, 1,
20224 : part_rel, scanrel);
20225 :
20226 390 : QueuePartitionConstraintValidation(wqueue, part_rel,
20227 : thisPartConstraint,
20228 : validate_default);
20229 390 : table_close(part_rel, NoLock); /* keep lock till commit */
20230 : }
20231 : }
20232 : }
20233 :
20234 : /*
20235 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20236 : *
20237 : * Return the address of the newly attached partition.
20238 : */
20239 : static ObjectAddress
20240 2424 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
20241 : AlterTableUtilityContext *context)
20242 : {
20243 : Relation attachrel,
20244 : catalog;
20245 : List *attachrel_children;
20246 : List *partConstraint;
20247 : SysScanDesc scan;
20248 : ScanKeyData skey;
20249 : AttrNumber attno;
20250 : int natts;
20251 : TupleDesc tupleDesc;
20252 : ObjectAddress address;
20253 : const char *trigger_name;
20254 : Oid defaultPartOid;
20255 : List *partBoundConstraint;
20256 2424 : ParseState *pstate = make_parsestate(NULL);
20257 :
20258 2424 : pstate->p_sourcetext = context->queryString;
20259 :
20260 : /*
20261 : * We must lock the default partition if one exists, because attaching a
20262 : * new partition will change its partition constraint.
20263 : */
20264 : defaultPartOid =
20265 2424 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20266 2424 : if (OidIsValid(defaultPartOid))
20267 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20268 :
20269 2424 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20270 :
20271 : /*
20272 : * XXX I think it'd be a good idea to grab locks on all tables referenced
20273 : * by FKs at this point also.
20274 : */
20275 :
20276 : /*
20277 : * Must be owner of both parent and source table -- parent was checked by
20278 : * ATSimplePermissions call in ATPrepCmd
20279 : */
20280 2418 : ATSimplePermissions(AT_AttachPartition, attachrel,
20281 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
20282 :
20283 : /* A partition can only have one parent */
20284 2412 : if (attachrel->rd_rel->relispartition)
20285 6 : ereport(ERROR,
20286 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20287 : errmsg("\"%s\" is already a partition",
20288 : RelationGetRelationName(attachrel))));
20289 :
20290 2406 : if (OidIsValid(attachrel->rd_rel->reloftype))
20291 6 : ereport(ERROR,
20292 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20293 : errmsg("cannot attach a typed table as partition")));
20294 :
20295 : /*
20296 : * Table being attached should not already be part of inheritance; either
20297 : * as a child table...
20298 : */
20299 2400 : catalog = table_open(InheritsRelationId, AccessShareLock);
20300 2400 : ScanKeyInit(&skey,
20301 : Anum_pg_inherits_inhrelid,
20302 : BTEqualStrategyNumber, F_OIDEQ,
20303 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20304 2400 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20305 : NULL, 1, &skey);
20306 2400 : if (HeapTupleIsValid(systable_getnext(scan)))
20307 6 : ereport(ERROR,
20308 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20309 : errmsg("cannot attach inheritance child as partition")));
20310 2394 : systable_endscan(scan);
20311 :
20312 : /* ...or as a parent table (except the case when it is partitioned) */
20313 2394 : ScanKeyInit(&skey,
20314 : Anum_pg_inherits_inhparent,
20315 : BTEqualStrategyNumber, F_OIDEQ,
20316 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20317 2394 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20318 : 1, &skey);
20319 2394 : if (HeapTupleIsValid(systable_getnext(scan)) &&
20320 262 : attachrel->rd_rel->relkind == RELKIND_RELATION)
20321 6 : ereport(ERROR,
20322 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20323 : errmsg("cannot attach inheritance parent as partition")));
20324 2388 : systable_endscan(scan);
20325 2388 : table_close(catalog, AccessShareLock);
20326 :
20327 : /*
20328 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
20329 : * particular, this disallows making a rel a partition of itself.)
20330 : *
20331 : * We do that by checking if rel is a member of the list of attachrel's
20332 : * partitions provided the latter is partitioned at all. We want to avoid
20333 : * having to construct this list again, so we request the strongest lock
20334 : * on all partitions. We need the strongest lock, because we may decide
20335 : * to scan them if we find out that the table being attached (or its leaf
20336 : * partitions) may contain rows that violate the partition constraint. If
20337 : * the table has a constraint that would prevent such rows, which by
20338 : * definition is present in all the partitions, we need not scan the
20339 : * table, nor its partitions. But we cannot risk a deadlock by taking a
20340 : * weaker lock now and the stronger one only when needed.
20341 : */
20342 2388 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20343 : AccessExclusiveLock, NULL);
20344 2388 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20345 12 : ereport(ERROR,
20346 : (errcode(ERRCODE_DUPLICATE_TABLE),
20347 : errmsg("circular inheritance not allowed"),
20348 : errdetail("\"%s\" is already a child of \"%s\".",
20349 : RelationGetRelationName(rel),
20350 : RelationGetRelationName(attachrel))));
20351 :
20352 : /* If the parent is permanent, so must be all of its partitions. */
20353 2376 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20354 2334 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20355 6 : ereport(ERROR,
20356 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20357 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20358 : RelationGetRelationName(rel))));
20359 :
20360 : /* Temp parent cannot have a partition that is itself not a temp */
20361 2370 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20362 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20363 18 : ereport(ERROR,
20364 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20365 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20366 : RelationGetRelationName(rel))));
20367 :
20368 : /* If the parent is temp, it must belong to this session */
20369 2352 : if (RELATION_IS_OTHER_TEMP(rel))
20370 0 : ereport(ERROR,
20371 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20372 : errmsg("cannot attach as partition of temporary relation of another session")));
20373 :
20374 : /* Ditto for the partition */
20375 2352 : if (RELATION_IS_OTHER_TEMP(attachrel))
20376 0 : ereport(ERROR,
20377 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20378 : errmsg("cannot attach temporary relation of another session as partition")));
20379 :
20380 : /*
20381 : * Check if attachrel has any identity columns or any columns that aren't
20382 : * in the parent.
20383 : */
20384 2352 : tupleDesc = RelationGetDescr(attachrel);
20385 2352 : natts = tupleDesc->natts;
20386 8016 : for (attno = 1; attno <= natts; attno++)
20387 : {
20388 5706 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20389 5706 : char *attributeName = NameStr(attribute->attname);
20390 :
20391 : /* Ignore dropped */
20392 5706 : if (attribute->attisdropped)
20393 580 : continue;
20394 :
20395 5126 : if (attribute->attidentity)
20396 24 : ereport(ERROR,
20397 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20398 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20399 : RelationGetRelationName(attachrel), attributeName),
20400 : errdetail("The new partition may not contain an identity column."));
20401 :
20402 : /* Try to find the column in parent (matching on column name) */
20403 5102 : if (!SearchSysCacheExists2(ATTNAME,
20404 : ObjectIdGetDatum(RelationGetRelid(rel)),
20405 : CStringGetDatum(attributeName)))
20406 18 : ereport(ERROR,
20407 : (errcode(ERRCODE_DATATYPE_MISMATCH),
20408 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20409 : RelationGetRelationName(attachrel), attributeName,
20410 : RelationGetRelationName(rel)),
20411 : errdetail("The new partition may contain only the columns present in parent.")));
20412 : }
20413 :
20414 : /*
20415 : * If child_rel has row-level triggers with transition tables, we
20416 : * currently don't allow it to become a partition. See also prohibitions
20417 : * in ATExecAddInherit() and CreateTrigger().
20418 : */
20419 2310 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20420 2310 : if (trigger_name != NULL)
20421 6 : ereport(ERROR,
20422 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20423 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20424 : trigger_name, RelationGetRelationName(attachrel)),
20425 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
20426 :
20427 : /*
20428 : * Check that the new partition's bound is valid and does not overlap any
20429 : * of existing partitions of the parent - note that it does not return on
20430 : * error.
20431 : */
20432 2304 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
20433 : cmd->bound, pstate);
20434 :
20435 : /* OK to create inheritance. Rest of the checks performed there */
20436 2268 : CreateInheritance(attachrel, rel, true);
20437 :
20438 : /* Update the pg_class entry. */
20439 2160 : StorePartitionBound(attachrel, rel, cmd->bound);
20440 :
20441 : /* Ensure there exists a correct set of indexes in the partition. */
20442 2160 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20443 :
20444 : /* and triggers */
20445 2130 : CloneRowTriggersToPartition(rel, attachrel);
20446 :
20447 : /*
20448 : * Clone foreign key constraints. Callee is responsible for setting up
20449 : * for phase 3 constraint verification.
20450 : */
20451 2124 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
20452 :
20453 : /*
20454 : * Generate partition constraint from the partition bound specification.
20455 : * If the parent itself is a partition, make sure to include its
20456 : * constraint as well.
20457 : */
20458 2106 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20459 :
20460 : /*
20461 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20462 : * since it's needed later to construct the constraint expression for
20463 : * validating against the default partition, if any.
20464 : */
20465 2106 : partConstraint = list_concat_copy(partBoundConstraint,
20466 2106 : RelationGetPartitionQual(rel));
20467 :
20468 : /* Skip validation if there are no constraints to validate. */
20469 2106 : if (partConstraint)
20470 : {
20471 : /*
20472 : * Run the partition quals through const-simplification similar to
20473 : * check constraints. We skip canonicalize_qual, though, because
20474 : * partition quals should be in canonical form already.
20475 : */
20476 : partConstraint =
20477 2058 : (List *) eval_const_expressions(NULL,
20478 : (Node *) partConstraint);
20479 :
20480 : /* XXX this sure looks wrong */
20481 2058 : partConstraint = list_make1(make_ands_explicit(partConstraint));
20482 :
20483 : /*
20484 : * Adjust the generated constraint to match this partition's attribute
20485 : * numbers.
20486 : */
20487 2058 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20488 : rel);
20489 :
20490 : /* Validate partition constraints against the table being attached. */
20491 2058 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20492 : false);
20493 : }
20494 :
20495 : /*
20496 : * If we're attaching a partition other than the default partition and a
20497 : * default one exists, then that partition's partition constraint changes,
20498 : * so add an entry to the work queue to validate it, too. (We must not do
20499 : * this when the partition being attached is the default one; we already
20500 : * did it above!)
20501 : */
20502 2106 : if (OidIsValid(defaultPartOid))
20503 : {
20504 : Relation defaultrel;
20505 : List *defPartConstraint;
20506 :
20507 : Assert(!cmd->bound->is_default);
20508 :
20509 : /* we already hold a lock on the default partition */
20510 146 : defaultrel = table_open(defaultPartOid, NoLock);
20511 : defPartConstraint =
20512 146 : get_proposed_default_constraint(partBoundConstraint);
20513 :
20514 : /*
20515 : * Map the Vars in the constraint expression from rel's attnos to
20516 : * defaultrel's.
20517 : */
20518 : defPartConstraint =
20519 146 : map_partition_varattnos(defPartConstraint,
20520 : 1, defaultrel, rel);
20521 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
20522 : defPartConstraint, true);
20523 :
20524 : /* keep our lock until commit. */
20525 146 : table_close(defaultrel, NoLock);
20526 : }
20527 :
20528 2106 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20529 :
20530 : /*
20531 : * If the partition we just attached is partitioned itself, invalidate
20532 : * relcache for all descendent partitions too to ensure that their
20533 : * rd_partcheck expression trees are rebuilt; partitions already locked at
20534 : * the beginning of this function.
20535 : */
20536 2106 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20537 : {
20538 : ListCell *l;
20539 :
20540 1032 : foreach(l, attachrel_children)
20541 : {
20542 690 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
20543 : }
20544 : }
20545 :
20546 : /* keep our lock until commit */
20547 2106 : table_close(attachrel, NoLock);
20548 :
20549 2106 : return address;
20550 : }
20551 :
20552 : /*
20553 : * AttachPartitionEnsureIndexes
20554 : * subroutine for ATExecAttachPartition to create/match indexes
20555 : *
20556 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20557 : * PARTITION: every partition must have an index attached to each index on the
20558 : * partitioned table.
20559 : */
20560 : static void
20561 2160 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
20562 : {
20563 : List *idxes;
20564 : List *attachRelIdxs;
20565 : Relation *attachrelIdxRels;
20566 : IndexInfo **attachInfos;
20567 : ListCell *cell;
20568 : MemoryContext cxt;
20569 : MemoryContext oldcxt;
20570 :
20571 2160 : cxt = AllocSetContextCreate(CurrentMemoryContext,
20572 : "AttachPartitionEnsureIndexes",
20573 : ALLOCSET_DEFAULT_SIZES);
20574 2160 : oldcxt = MemoryContextSwitchTo(cxt);
20575 :
20576 2160 : idxes = RelationGetIndexList(rel);
20577 2160 : attachRelIdxs = RelationGetIndexList(attachrel);
20578 2160 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20579 2160 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20580 :
20581 : /* Build arrays of all existing indexes and their IndexInfos */
20582 4702 : foreach_oid(cldIdxId, attachRelIdxs)
20583 : {
20584 382 : int i = foreach_current_index(cldIdxId);
20585 :
20586 382 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20587 382 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20588 : }
20589 :
20590 : /*
20591 : * If we're attaching a foreign table, we must fail if any of the indexes
20592 : * is a constraint index; otherwise, there's nothing to do here. Do this
20593 : * before starting work, to avoid wasting the effort of building a few
20594 : * non-unique indexes before coming across a unique one.
20595 : */
20596 2160 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20597 : {
20598 88 : foreach(cell, idxes)
20599 : {
20600 36 : Oid idx = lfirst_oid(cell);
20601 36 : Relation idxRel = index_open(idx, AccessShareLock);
20602 :
20603 36 : if (idxRel->rd_index->indisunique ||
20604 24 : idxRel->rd_index->indisprimary)
20605 12 : ereport(ERROR,
20606 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20607 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20608 : RelationGetRelationName(attachrel),
20609 : RelationGetRelationName(rel)),
20610 : errdetail("Partitioned table \"%s\" contains unique indexes.",
20611 : RelationGetRelationName(rel))));
20612 24 : index_close(idxRel, AccessShareLock);
20613 : }
20614 :
20615 52 : goto out;
20616 : }
20617 :
20618 : /*
20619 : * For each index on the partitioned table, find a matching one in the
20620 : * partition-to-be; if one is not found, create one.
20621 : */
20622 2528 : foreach(cell, idxes)
20623 : {
20624 450 : Oid idx = lfirst_oid(cell);
20625 450 : Relation idxRel = index_open(idx, AccessShareLock);
20626 : IndexInfo *info;
20627 : AttrMap *attmap;
20628 450 : bool found = false;
20629 : Oid constraintOid;
20630 :
20631 : /*
20632 : * Ignore indexes in the partitioned table other than partitioned
20633 : * indexes.
20634 : */
20635 450 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20636 : {
20637 0 : index_close(idxRel, AccessShareLock);
20638 0 : continue;
20639 : }
20640 :
20641 : /* construct an indexinfo to compare existing indexes against */
20642 450 : info = BuildIndexInfo(idxRel);
20643 450 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20644 : RelationGetDescr(rel),
20645 : false);
20646 450 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
20647 :
20648 : /*
20649 : * Scan the list of existing indexes in the partition-to-be, and mark
20650 : * the first matching, valid, unattached one we find, if any, as
20651 : * partition of the parent index. If we find one, we're done.
20652 : */
20653 510 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20654 : {
20655 274 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20656 274 : Oid cldConstrOid = InvalidOid;
20657 :
20658 : /* does this index have a parent? if so, can't use it */
20659 274 : if (attachrelIdxRels[i]->rd_rel->relispartition)
20660 12 : continue;
20661 :
20662 : /* If this index is invalid, can't use it */
20663 262 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
20664 6 : continue;
20665 :
20666 256 : if (CompareIndexInfo(attachInfos[i], info,
20667 256 : attachrelIdxRels[i]->rd_indcollation,
20668 256 : idxRel->rd_indcollation,
20669 256 : attachrelIdxRels[i]->rd_opfamily,
20670 256 : idxRel->rd_opfamily,
20671 : attmap))
20672 : {
20673 : /*
20674 : * If this index is being created in the parent because of a
20675 : * constraint, then the child needs to have a constraint also,
20676 : * so look for one. If there is no such constraint, this
20677 : * index is no good, so keep looking.
20678 : */
20679 220 : if (OidIsValid(constraintOid))
20680 : {
20681 : cldConstrOid =
20682 122 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
20683 : cldIdxId);
20684 : /* no dice */
20685 122 : if (!OidIsValid(cldConstrOid))
20686 6 : continue;
20687 :
20688 : /* Ensure they're both the same type of constraint */
20689 232 : if (get_constraint_type(constraintOid) !=
20690 116 : get_constraint_type(cldConstrOid))
20691 0 : continue;
20692 : }
20693 :
20694 : /* bingo. */
20695 214 : IndexSetParentIndex(attachrelIdxRels[i], idx);
20696 214 : if (OidIsValid(constraintOid))
20697 116 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20698 : RelationGetRelid(attachrel));
20699 214 : found = true;
20700 :
20701 214 : CommandCounterIncrement();
20702 214 : break;
20703 : }
20704 : }
20705 :
20706 : /*
20707 : * If no suitable index was found in the partition-to-be, create one
20708 : * now. Note that if this is a PK, not-null constraints must already
20709 : * exist.
20710 : */
20711 450 : if (!found)
20712 : {
20713 : IndexStmt *stmt;
20714 : Oid conOid;
20715 :
20716 236 : stmt = generateClonedIndexStmt(NULL,
20717 : idxRel, attmap,
20718 : &conOid);
20719 236 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
20720 : RelationGetRelid(idxRel),
20721 : conOid,
20722 : -1,
20723 : true, false, false, false, false);
20724 : }
20725 :
20726 432 : index_close(idxRel, AccessShareLock);
20727 : }
20728 :
20729 2130 : out:
20730 : /* Clean up. */
20731 2500 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20732 370 : index_close(attachrelIdxRels[i], AccessShareLock);
20733 2130 : MemoryContextSwitchTo(oldcxt);
20734 2130 : MemoryContextDelete(cxt);
20735 2130 : }
20736 :
20737 : /*
20738 : * CloneRowTriggersToPartition
20739 : * subroutine for ATExecAttachPartition/DefineRelation to create row
20740 : * triggers on partitions
20741 : */
20742 : static void
20743 2574 : CloneRowTriggersToPartition(Relation parent, Relation partition)
20744 : {
20745 : Relation pg_trigger;
20746 : ScanKeyData key;
20747 : SysScanDesc scan;
20748 : HeapTuple tuple;
20749 : MemoryContext perTupCxt;
20750 :
20751 2574 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20752 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20753 2574 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20754 2574 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20755 : true, NULL, 1, &key);
20756 :
20757 2574 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
20758 : "clone trig", ALLOCSET_SMALL_SIZES);
20759 :
20760 4436 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20761 : {
20762 1868 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20763 : CreateTrigStmt *trigStmt;
20764 1868 : Node *qual = NULL;
20765 : Datum value;
20766 : bool isnull;
20767 1868 : List *cols = NIL;
20768 1868 : List *trigargs = NIL;
20769 : MemoryContext oldcxt;
20770 :
20771 : /*
20772 : * Ignore statement-level triggers; those are not cloned.
20773 : */
20774 1868 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20775 1712 : continue;
20776 :
20777 : /*
20778 : * Don't clone internal triggers, because the constraint cloning code
20779 : * will.
20780 : */
20781 1850 : if (trigForm->tgisinternal)
20782 1694 : continue;
20783 :
20784 : /*
20785 : * Complain if we find an unexpected trigger type.
20786 : */
20787 156 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20788 138 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
20789 0 : elog(ERROR, "unexpected trigger \"%s\" found",
20790 : NameStr(trigForm->tgname));
20791 :
20792 : /* Use short-lived context for CREATE TRIGGER */
20793 156 : oldcxt = MemoryContextSwitchTo(perTupCxt);
20794 :
20795 : /*
20796 : * If there is a WHEN clause, generate a 'cooked' version of it that's
20797 : * appropriate for the partition.
20798 : */
20799 156 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20800 : RelationGetDescr(pg_trigger), &isnull);
20801 156 : if (!isnull)
20802 : {
20803 6 : qual = stringToNode(TextDatumGetCString(value));
20804 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20805 : partition, parent);
20806 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20807 : partition, parent);
20808 : }
20809 :
20810 : /*
20811 : * If there is a column list, transform it to a list of column names.
20812 : * Note we don't need to map this list in any way ...
20813 : */
20814 156 : if (trigForm->tgattr.dim1 > 0)
20815 : {
20816 : int i;
20817 :
20818 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
20819 : {
20820 : Form_pg_attribute col;
20821 :
20822 6 : col = TupleDescAttr(parent->rd_att,
20823 6 : trigForm->tgattr.values[i] - 1);
20824 6 : cols = lappend(cols,
20825 6 : makeString(pstrdup(NameStr(col->attname))));
20826 : }
20827 : }
20828 :
20829 : /* Reconstruct trigger arguments list. */
20830 156 : if (trigForm->tgnargs > 0)
20831 : {
20832 : char *p;
20833 :
20834 12 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20835 : RelationGetDescr(pg_trigger), &isnull);
20836 12 : if (isnull)
20837 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20838 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
20839 :
20840 12 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20841 :
20842 36 : for (int i = 0; i < trigForm->tgnargs; i++)
20843 : {
20844 24 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
20845 24 : p += strlen(p) + 1;
20846 : }
20847 : }
20848 :
20849 156 : trigStmt = makeNode(CreateTrigStmt);
20850 156 : trigStmt->replace = false;
20851 156 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20852 156 : trigStmt->trigname = NameStr(trigForm->tgname);
20853 156 : trigStmt->relation = NULL;
20854 156 : trigStmt->funcname = NULL; /* passed separately */
20855 156 : trigStmt->args = trigargs;
20856 156 : trigStmt->row = true;
20857 156 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20858 156 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20859 156 : trigStmt->columns = cols;
20860 156 : trigStmt->whenClause = NULL; /* passed separately */
20861 156 : trigStmt->transitionRels = NIL; /* not supported at present */
20862 156 : trigStmt->deferrable = trigForm->tgdeferrable;
20863 156 : trigStmt->initdeferred = trigForm->tginitdeferred;
20864 156 : trigStmt->constrrel = NULL; /* passed separately */
20865 :
20866 156 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20867 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20868 : trigForm->tgfoid, trigForm->oid, qual,
20869 156 : false, true, trigForm->tgenabled);
20870 :
20871 150 : MemoryContextSwitchTo(oldcxt);
20872 150 : MemoryContextReset(perTupCxt);
20873 : }
20874 :
20875 2568 : MemoryContextDelete(perTupCxt);
20876 :
20877 2568 : systable_endscan(scan);
20878 2568 : table_close(pg_trigger, RowExclusiveLock);
20879 2568 : }
20880 :
20881 : /*
20882 : * ALTER TABLE DETACH PARTITION
20883 : *
20884 : * Return the address of the relation that is no longer a partition of rel.
20885 : *
20886 : * If concurrent mode is requested, we run in two transactions. A side-
20887 : * effect is that this command cannot run in a multi-part ALTER TABLE.
20888 : * Currently, that's enforced by the grammar.
20889 : *
20890 : * The strategy for concurrency is to first modify the partition's
20891 : * pg_inherit catalog row to make it visible to everyone that the
20892 : * partition is detached, lock the partition against writes, and commit
20893 : * the transaction; anyone who requests the partition descriptor from
20894 : * that point onwards has to ignore such a partition. In a second
20895 : * transaction, we wait until all transactions that could have seen the
20896 : * partition as attached are gone, then we remove the rest of partition
20897 : * metadata (pg_inherits and pg_class.relpartbounds).
20898 : */
20899 : static ObjectAddress
20900 578 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
20901 : RangeVar *name, bool concurrent)
20902 : {
20903 : Relation partRel;
20904 : ObjectAddress address;
20905 : Oid defaultPartOid;
20906 :
20907 : /*
20908 : * We must lock the default partition, because detaching this partition
20909 : * will change its partition constraint.
20910 : */
20911 : defaultPartOid =
20912 578 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20913 578 : if (OidIsValid(defaultPartOid))
20914 : {
20915 : /*
20916 : * Concurrent detaching when a default partition exists is not
20917 : * supported. The main problem is that the default partition
20918 : * constraint would change. And there's a definitional problem: what
20919 : * should happen to the tuples that are being inserted that belong to
20920 : * the partition being detached? Putting them on the partition being
20921 : * detached would be wrong, since they'd become "lost" after the
20922 : * detaching completes but we cannot put them in the default partition
20923 : * either until we alter its partition constraint.
20924 : *
20925 : * I think we could solve this problem if we effected the constraint
20926 : * change before committing the first transaction. But the lock would
20927 : * have to remain AEL and it would cause concurrent query planning to
20928 : * be blocked, so changing it that way would be even worse.
20929 : */
20930 106 : if (concurrent)
20931 12 : ereport(ERROR,
20932 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20933 : errmsg("cannot detach partitions concurrently when a default partition exists")));
20934 94 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20935 : }
20936 :
20937 : /*
20938 : * In concurrent mode, the partition is locked with share-update-exclusive
20939 : * in the first transaction. This allows concurrent transactions to be
20940 : * doing DML to the partition.
20941 : */
20942 566 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20943 : AccessExclusiveLock);
20944 :
20945 : /*
20946 : * Check inheritance conditions and either delete the pg_inherits row (in
20947 : * non-concurrent mode) or just set the inhdetachpending flag.
20948 : */
20949 554 : if (!concurrent)
20950 408 : RemoveInheritance(partRel, rel, false);
20951 : else
20952 146 : MarkInheritDetached(partRel, rel);
20953 :
20954 : /*
20955 : * Ensure that foreign keys still hold after this detach. This keeps
20956 : * locks on the referencing tables, which prevents concurrent transactions
20957 : * from adding rows that we wouldn't see. For this to work in concurrent
20958 : * mode, it is critical that the partition appears as no longer attached
20959 : * for the RI queries as soon as the first transaction commits.
20960 : */
20961 534 : ATDetachCheckNoForeignKeyRefs(partRel);
20962 :
20963 : /*
20964 : * Concurrent mode has to work harder; first we add a new constraint to
20965 : * the partition that matches the partition constraint. Then we close our
20966 : * existing transaction, and in a new one wait for all processes to catch
20967 : * up on the catalog updates we've done so far; at that point we can
20968 : * complete the operation.
20969 : */
20970 500 : if (concurrent)
20971 : {
20972 : Oid partrelid,
20973 : parentrelid;
20974 : LOCKTAG tag;
20975 : char *parentrelname;
20976 : char *partrelname;
20977 :
20978 : /*
20979 : * We're almost done now; the only traces that remain are the
20980 : * pg_inherits tuple and the partition's relpartbounds. Before we can
20981 : * remove those, we need to wait until all transactions that know that
20982 : * this is a partition are gone.
20983 : */
20984 :
20985 : /*
20986 : * Remember relation OIDs to re-acquire them later; and relation names
20987 : * too, for error messages if something is dropped in between.
20988 : */
20989 140 : partrelid = RelationGetRelid(partRel);
20990 140 : parentrelid = RelationGetRelid(rel);
20991 140 : parentrelname = MemoryContextStrdup(PortalContext,
20992 140 : RelationGetRelationName(rel));
20993 140 : partrelname = MemoryContextStrdup(PortalContext,
20994 140 : RelationGetRelationName(partRel));
20995 :
20996 : /* Invalidate relcache entries for the parent -- must be before close */
20997 140 : CacheInvalidateRelcache(rel);
20998 :
20999 140 : table_close(partRel, NoLock);
21000 140 : table_close(rel, NoLock);
21001 140 : tab->rel = NULL;
21002 :
21003 : /* Make updated catalog entry visible */
21004 140 : PopActiveSnapshot();
21005 140 : CommitTransactionCommand();
21006 :
21007 140 : StartTransactionCommand();
21008 :
21009 : /*
21010 : * Now wait. This ensures that all queries that were planned
21011 : * including the partition are finished before we remove the rest of
21012 : * catalog entries. We don't need or indeed want to acquire this
21013 : * lock, though -- that would block later queries.
21014 : *
21015 : * We don't need to concern ourselves with waiting for a lock on the
21016 : * partition itself, since we will acquire AccessExclusiveLock below.
21017 : */
21018 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
21019 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
21020 :
21021 : /*
21022 : * Now acquire locks in both relations again. Note they may have been
21023 : * removed in the meantime, so care is required.
21024 : */
21025 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
21026 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
21027 :
21028 : /* If the relations aren't there, something bad happened; bail out */
21029 90 : if (rel == NULL)
21030 : {
21031 0 : if (partRel != NULL) /* shouldn't happen */
21032 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21033 : partrelname);
21034 0 : ereport(ERROR,
21035 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21036 : errmsg("partitioned table \"%s\" was removed concurrently",
21037 : parentrelname)));
21038 : }
21039 90 : if (partRel == NULL)
21040 0 : ereport(ERROR,
21041 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21042 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
21043 :
21044 90 : tab->rel = rel;
21045 : }
21046 :
21047 : /*
21048 : * Detaching the partition might involve TOAST table access, so ensure we
21049 : * have a valid snapshot.
21050 : */
21051 450 : PushActiveSnapshot(GetTransactionSnapshot());
21052 :
21053 : /* Do the final part of detaching */
21054 450 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21055 :
21056 448 : PopActiveSnapshot();
21057 :
21058 448 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21059 :
21060 : /* keep our lock until commit */
21061 448 : table_close(partRel, NoLock);
21062 :
21063 448 : return address;
21064 : }
21065 :
21066 : /*
21067 : * Second part of ALTER TABLE .. DETACH.
21068 : *
21069 : * This is separate so that it can be run independently when the second
21070 : * transaction of the concurrent algorithm fails (crash or abort).
21071 : */
21072 : static void
21073 464 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
21074 : Oid defaultPartOid)
21075 : {
21076 : Relation classRel;
21077 : List *fks;
21078 : ListCell *cell;
21079 : List *indexes;
21080 : Datum new_val[Natts_pg_class];
21081 : bool new_null[Natts_pg_class],
21082 : new_repl[Natts_pg_class];
21083 : HeapTuple tuple,
21084 : newtuple;
21085 464 : Relation trigrel = NULL;
21086 464 : List *fkoids = NIL;
21087 :
21088 464 : if (concurrent)
21089 : {
21090 : /*
21091 : * We can remove the pg_inherits row now. (In the non-concurrent case,
21092 : * this was already done).
21093 : */
21094 104 : RemoveInheritance(partRel, rel, true);
21095 : }
21096 :
21097 : /* Drop any triggers that were cloned on creation/attach. */
21098 464 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
21099 :
21100 : /*
21101 : * Detach any foreign keys that are inherited. This includes creating
21102 : * additional action triggers.
21103 : */
21104 464 : fks = copyObject(RelationGetFKeyList(partRel));
21105 464 : if (fks != NIL)
21106 84 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21107 :
21108 : /*
21109 : * It's possible that the partition being detached has a foreign key that
21110 : * references a partitioned table. When that happens, there are multiple
21111 : * pg_constraint rows for the partition: one points to the partitioned
21112 : * table itself, while the others point to each of its partitions. Only
21113 : * the topmost one is to be considered here; the child constraints must be
21114 : * left alone, because conceptually those aren't coming from our parent
21115 : * partitioned table, but from this partition itself.
21116 : *
21117 : * We implement this by collecting all the constraint OIDs in a first scan
21118 : * of the FK array, and skipping in the loop below those constraints whose
21119 : * parents are listed here.
21120 : */
21121 1096 : foreach_node(ForeignKeyCacheInfo, fk, fks)
21122 168 : fkoids = lappend_oid(fkoids, fk->conoid);
21123 :
21124 632 : foreach(cell, fks)
21125 : {
21126 168 : ForeignKeyCacheInfo *fk = lfirst(cell);
21127 : HeapTuple contup;
21128 : Form_pg_constraint conform;
21129 :
21130 168 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21131 168 : if (!HeapTupleIsValid(contup))
21132 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21133 168 : conform = (Form_pg_constraint) GETSTRUCT(contup);
21134 :
21135 : /*
21136 : * Consider only inherited foreign keys, and only if their parents
21137 : * aren't in the list.
21138 : */
21139 168 : if (conform->contype != CONSTRAINT_FOREIGN ||
21140 312 : !OidIsValid(conform->conparentid) ||
21141 144 : list_member_oid(fkoids, conform->conparentid))
21142 : {
21143 66 : ReleaseSysCache(contup);
21144 66 : continue;
21145 : }
21146 :
21147 : /*
21148 : * The constraint on this table must be marked no longer a child of
21149 : * the parent's constraint, as do its check triggers.
21150 : */
21151 102 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
21152 :
21153 : /*
21154 : * Also, look up the partition's "check" triggers corresponding to the
21155 : * ENFORCED constraint being detached and detach them from the parent
21156 : * triggers. NOT ENFORCED constraints do not have these triggers;
21157 : * therefore, this step is not needed.
21158 : */
21159 102 : if (fk->conenforced)
21160 : {
21161 : Oid insertTriggerOid,
21162 : updateTriggerOid;
21163 :
21164 102 : GetForeignKeyCheckTriggers(trigrel,
21165 : fk->conoid, fk->confrelid, fk->conrelid,
21166 : &insertTriggerOid, &updateTriggerOid);
21167 : Assert(OidIsValid(insertTriggerOid));
21168 102 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21169 : RelationGetRelid(partRel));
21170 : Assert(OidIsValid(updateTriggerOid));
21171 102 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21172 : RelationGetRelid(partRel));
21173 : }
21174 :
21175 : /*
21176 : * Lastly, create the action triggers on the referenced table, using
21177 : * addFkRecurseReferenced, which requires some elaborate setup (so put
21178 : * it in a separate block). While at it, if the table is partitioned,
21179 : * that function will recurse to create the pg_constraint rows and
21180 : * action triggers for each partition.
21181 : *
21182 : * Note there's no need to do addFkConstraint() here, because the
21183 : * pg_constraint row already exists.
21184 : */
21185 : {
21186 : Constraint *fkconstraint;
21187 : int numfks;
21188 : AttrNumber conkey[INDEX_MAX_KEYS];
21189 : AttrNumber confkey[INDEX_MAX_KEYS];
21190 : Oid conpfeqop[INDEX_MAX_KEYS];
21191 : Oid conppeqop[INDEX_MAX_KEYS];
21192 : Oid conffeqop[INDEX_MAX_KEYS];
21193 : int numfkdelsetcols;
21194 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21195 : Relation refdRel;
21196 :
21197 102 : DeconstructFkConstraintRow(contup,
21198 : &numfks,
21199 : conkey,
21200 : confkey,
21201 : conpfeqop,
21202 : conppeqop,
21203 : conffeqop,
21204 : &numfkdelsetcols,
21205 : confdelsetcols);
21206 :
21207 : /* Create a synthetic node we'll use throughout */
21208 102 : fkconstraint = makeNode(Constraint);
21209 102 : fkconstraint->contype = CONSTRAINT_FOREIGN;
21210 102 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
21211 102 : fkconstraint->deferrable = conform->condeferrable;
21212 102 : fkconstraint->initdeferred = conform->condeferred;
21213 102 : fkconstraint->is_enforced = conform->conenforced;
21214 102 : fkconstraint->skip_validation = true;
21215 102 : fkconstraint->initially_valid = conform->convalidated;
21216 : /* a few irrelevant fields omitted here */
21217 102 : fkconstraint->pktable = NULL;
21218 102 : fkconstraint->fk_attrs = NIL;
21219 102 : fkconstraint->pk_attrs = NIL;
21220 102 : fkconstraint->fk_matchtype = conform->confmatchtype;
21221 102 : fkconstraint->fk_upd_action = conform->confupdtype;
21222 102 : fkconstraint->fk_del_action = conform->confdeltype;
21223 102 : fkconstraint->fk_del_set_cols = NIL;
21224 102 : fkconstraint->old_conpfeqop = NIL;
21225 102 : fkconstraint->old_pktable_oid = InvalidOid;
21226 102 : fkconstraint->location = -1;
21227 :
21228 : /* set up colnames, used to generate the constraint name */
21229 252 : for (int i = 0; i < numfks; i++)
21230 : {
21231 : Form_pg_attribute att;
21232 :
21233 150 : att = TupleDescAttr(RelationGetDescr(partRel),
21234 150 : conkey[i] - 1);
21235 :
21236 150 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21237 150 : makeString(NameStr(att->attname)));
21238 : }
21239 :
21240 102 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
21241 :
21242 102 : addFkRecurseReferenced(fkconstraint, partRel,
21243 : refdRel,
21244 : conform->conindid,
21245 : fk->conoid,
21246 : numfks,
21247 : confkey,
21248 : conkey,
21249 : conpfeqop,
21250 : conppeqop,
21251 : conffeqop,
21252 : numfkdelsetcols,
21253 : confdelsetcols,
21254 : true,
21255 : InvalidOid, InvalidOid,
21256 102 : conform->conperiod);
21257 102 : table_close(refdRel, NoLock); /* keep lock till end of xact */
21258 : }
21259 :
21260 102 : ReleaseSysCache(contup);
21261 : }
21262 464 : list_free_deep(fks);
21263 464 : if (trigrel)
21264 84 : table_close(trigrel, RowExclusiveLock);
21265 :
21266 : /*
21267 : * Any sub-constraints that are in the referenced-side of a larger
21268 : * constraint have to be removed. This partition is no longer part of the
21269 : * key space of the constraint.
21270 : */
21271 524 : foreach(cell, GetParentedForeignKeyRefs(partRel))
21272 : {
21273 62 : Oid constrOid = lfirst_oid(cell);
21274 : ObjectAddress constraint;
21275 :
21276 62 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21277 62 : deleteDependencyRecordsForClass(ConstraintRelationId,
21278 : constrOid,
21279 : ConstraintRelationId,
21280 : DEPENDENCY_INTERNAL);
21281 62 : CommandCounterIncrement();
21282 :
21283 62 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21284 62 : performDeletion(&constraint, DROP_RESTRICT, 0);
21285 : }
21286 :
21287 : /* Now we can detach indexes */
21288 462 : indexes = RelationGetIndexList(partRel);
21289 656 : foreach(cell, indexes)
21290 : {
21291 194 : Oid idxid = lfirst_oid(cell);
21292 : Oid parentidx;
21293 : Relation idx;
21294 : Oid constrOid;
21295 : Oid parentConstrOid;
21296 :
21297 194 : if (!has_superclass(idxid))
21298 12 : continue;
21299 :
21300 182 : parentidx = get_partition_parent(idxid, false);
21301 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21302 :
21303 182 : idx = index_open(idxid, AccessExclusiveLock);
21304 182 : IndexSetParentIndex(idx, InvalidOid);
21305 :
21306 : /*
21307 : * If there's a constraint associated with the index, detach it too.
21308 : * Careful: it is possible for a constraint index in a partition to be
21309 : * the child of a non-constraint index, so verify whether the parent
21310 : * index does actually have a constraint.
21311 : */
21312 182 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
21313 : idxid);
21314 182 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
21315 : parentidx);
21316 182 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21317 84 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21318 :
21319 182 : index_close(idx, NoLock);
21320 : }
21321 :
21322 : /* Update pg_class tuple */
21323 462 : classRel = table_open(RelationRelationId, RowExclusiveLock);
21324 462 : tuple = SearchSysCacheCopy1(RELOID,
21325 : ObjectIdGetDatum(RelationGetRelid(partRel)));
21326 462 : if (!HeapTupleIsValid(tuple))
21327 0 : elog(ERROR, "cache lookup failed for relation %u",
21328 : RelationGetRelid(partRel));
21329 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21330 :
21331 : /* Clear relpartbound and reset relispartition */
21332 462 : memset(new_val, 0, sizeof(new_val));
21333 462 : memset(new_null, false, sizeof(new_null));
21334 462 : memset(new_repl, false, sizeof(new_repl));
21335 462 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21336 462 : new_null[Anum_pg_class_relpartbound - 1] = true;
21337 462 : new_repl[Anum_pg_class_relpartbound - 1] = true;
21338 462 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21339 : new_val, new_null, new_repl);
21340 :
21341 462 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21342 462 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21343 462 : heap_freetuple(newtuple);
21344 462 : table_close(classRel, RowExclusiveLock);
21345 :
21346 : /*
21347 : * Drop identity property from all identity columns of partition.
21348 : */
21349 1318 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21350 : {
21351 856 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21352 :
21353 856 : if (!attr->attisdropped && attr->attidentity)
21354 6 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21355 : AccessExclusiveLock, true, true);
21356 : }
21357 :
21358 462 : if (OidIsValid(defaultPartOid))
21359 : {
21360 : /*
21361 : * If the relation being detached is the default partition itself,
21362 : * remove it from the parent's pg_partitioned_table entry.
21363 : *
21364 : * If not, we must invalidate default partition's relcache entry, as
21365 : * in StorePartitionBound: its partition constraint depends on every
21366 : * other partition's partition constraint.
21367 : */
21368 46 : if (RelationGetRelid(partRel) == defaultPartOid)
21369 2 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
21370 : else
21371 44 : CacheInvalidateRelcacheByRelid(defaultPartOid);
21372 : }
21373 :
21374 : /*
21375 : * Invalidate the parent's relcache so that the partition is no longer
21376 : * included in its partition descriptor.
21377 : */
21378 462 : CacheInvalidateRelcache(rel);
21379 :
21380 : /*
21381 : * If the partition we just detached is partitioned itself, invalidate
21382 : * relcache for all descendent partitions too to ensure that their
21383 : * rd_partcheck expression trees are rebuilt; must lock partitions before
21384 : * doing so, using the same lockmode as what partRel has been locked with
21385 : * by the caller.
21386 : */
21387 462 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21388 : {
21389 : List *children;
21390 :
21391 62 : children = find_all_inheritors(RelationGetRelid(partRel),
21392 : AccessExclusiveLock, NULL);
21393 204 : foreach(cell, children)
21394 : {
21395 142 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
21396 : }
21397 : }
21398 462 : }
21399 :
21400 : /*
21401 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21402 : *
21403 : * To use when a DETACH PARTITION command previously did not run to
21404 : * completion; this completes the detaching process.
21405 : */
21406 : static ObjectAddress
21407 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
21408 : {
21409 : Relation partRel;
21410 : ObjectAddress address;
21411 14 : Snapshot snap = GetActiveSnapshot();
21412 :
21413 14 : partRel = table_openrv(name, AccessExclusiveLock);
21414 :
21415 : /*
21416 : * Wait until existing snapshots are gone. This is important if the
21417 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21418 : * user could immediately run DETACH FINALIZE without actually waiting for
21419 : * existing transactions. We must not complete the detach action until
21420 : * all such queries are complete (otherwise we would present them with an
21421 : * inconsistent view of catalogs).
21422 : */
21423 14 : WaitForOlderSnapshots(snap->xmin, false);
21424 :
21425 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21426 :
21427 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21428 :
21429 14 : table_close(partRel, NoLock);
21430 :
21431 14 : return address;
21432 : }
21433 :
21434 : /*
21435 : * DropClonedTriggersFromPartition
21436 : * subroutine for ATExecDetachPartition to remove any triggers that were
21437 : * cloned to the partition when it was created-as-partition or attached.
21438 : * This undoes what CloneRowTriggersToPartition did.
21439 : */
21440 : static void
21441 464 : DropClonedTriggersFromPartition(Oid partitionId)
21442 : {
21443 : ScanKeyData skey;
21444 : SysScanDesc scan;
21445 : HeapTuple trigtup;
21446 : Relation tgrel;
21447 : ObjectAddresses *objects;
21448 :
21449 464 : objects = new_object_addresses();
21450 :
21451 : /*
21452 : * Scan pg_trigger to search for all triggers on this rel.
21453 : */
21454 464 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21455 : F_OIDEQ, ObjectIdGetDatum(partitionId));
21456 464 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21457 464 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21458 : true, NULL, 1, &skey);
21459 876 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21460 : {
21461 412 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21462 : ObjectAddress trig;
21463 :
21464 : /* Ignore triggers that weren't cloned */
21465 412 : if (!OidIsValid(pg_trigger->tgparentid))
21466 394 : continue;
21467 :
21468 : /*
21469 : * Ignore internal triggers that are implementation objects of foreign
21470 : * keys, because these will be detached when the foreign keys
21471 : * themselves are.
21472 : */
21473 346 : if (OidIsValid(pg_trigger->tgconstrrelid))
21474 328 : continue;
21475 :
21476 : /*
21477 : * This is ugly, but necessary: remove the dependency markings on the
21478 : * trigger so that it can be removed.
21479 : */
21480 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21481 : TriggerRelationId,
21482 : DEPENDENCY_PARTITION_PRI);
21483 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21484 : RelationRelationId,
21485 : DEPENDENCY_PARTITION_SEC);
21486 :
21487 : /* remember this trigger to remove it below */
21488 18 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21489 18 : add_exact_object_address(&trig, objects);
21490 : }
21491 :
21492 : /* make the dependency removal visible to the deletion below */
21493 464 : CommandCounterIncrement();
21494 464 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
21495 :
21496 : /* done */
21497 464 : free_object_addresses(objects);
21498 464 : systable_endscan(scan);
21499 464 : table_close(tgrel, RowExclusiveLock);
21500 464 : }
21501 :
21502 : /*
21503 : * Before acquiring lock on an index, acquire the same lock on the owning
21504 : * table.
21505 : */
21506 : struct AttachIndexCallbackState
21507 : {
21508 : Oid partitionOid;
21509 : Oid parentTblOid;
21510 : bool lockedParentTbl;
21511 : };
21512 :
21513 : static void
21514 412 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21515 : void *arg)
21516 : {
21517 : struct AttachIndexCallbackState *state;
21518 : Form_pg_class classform;
21519 : HeapTuple tuple;
21520 :
21521 412 : state = (struct AttachIndexCallbackState *) arg;
21522 :
21523 412 : if (!state->lockedParentTbl)
21524 : {
21525 380 : LockRelationOid(state->parentTblOid, AccessShareLock);
21526 380 : state->lockedParentTbl = true;
21527 : }
21528 :
21529 : /*
21530 : * If we previously locked some other heap, and the name we're looking up
21531 : * no longer refers to an index on that relation, release the now-useless
21532 : * lock. XXX maybe we should do *after* we verify whether the index does
21533 : * not actually belong to the same relation ...
21534 : */
21535 412 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21536 : {
21537 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
21538 0 : state->partitionOid = InvalidOid;
21539 : }
21540 :
21541 : /* Didn't find a relation, so no need for locking or permission checks. */
21542 412 : if (!OidIsValid(relOid))
21543 6 : return;
21544 :
21545 406 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21546 406 : if (!HeapTupleIsValid(tuple))
21547 0 : return; /* concurrently dropped, so nothing to do */
21548 406 : classform = (Form_pg_class) GETSTRUCT(tuple);
21549 406 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21550 310 : classform->relkind != RELKIND_INDEX)
21551 6 : ereport(ERROR,
21552 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21553 : errmsg("\"%s\" is not an index", rv->relname)));
21554 400 : ReleaseSysCache(tuple);
21555 :
21556 : /*
21557 : * Since we need only examine the heap's tupledesc, an access share lock
21558 : * on it (preventing any DDL) is sufficient.
21559 : */
21560 400 : state->partitionOid = IndexGetRelation(relOid, false);
21561 400 : LockRelationOid(state->partitionOid, AccessShareLock);
21562 : }
21563 :
21564 : /*
21565 : * ALTER INDEX i1 ATTACH PARTITION i2
21566 : */
21567 : static ObjectAddress
21568 380 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
21569 : {
21570 : Relation partIdx;
21571 : Relation partTbl;
21572 : Relation parentTbl;
21573 : ObjectAddress address;
21574 : Oid partIdxId;
21575 : Oid currParent;
21576 : struct AttachIndexCallbackState state;
21577 :
21578 : /*
21579 : * We need to obtain lock on the index 'name' to modify it, but we also
21580 : * need to read its owning table's tuple descriptor -- so we need to lock
21581 : * both. To avoid deadlocks, obtain lock on the table before doing so on
21582 : * the index. Furthermore, we need to examine the parent table of the
21583 : * partition, so lock that one too.
21584 : */
21585 380 : state.partitionOid = InvalidOid;
21586 380 : state.parentTblOid = parentIdx->rd_index->indrelid;
21587 380 : state.lockedParentTbl = false;
21588 : partIdxId =
21589 380 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
21590 : RangeVarCallbackForAttachIndex,
21591 : &state);
21592 : /* Not there? */
21593 368 : if (!OidIsValid(partIdxId))
21594 0 : ereport(ERROR,
21595 : (errcode(ERRCODE_UNDEFINED_OBJECT),
21596 : errmsg("index \"%s\" does not exist", name->relname)));
21597 :
21598 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21599 368 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
21600 :
21601 : /* we already hold locks on both tables, so this is safe: */
21602 368 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21603 368 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21604 :
21605 368 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21606 :
21607 : /* Silently do nothing if already in the right state */
21608 736 : currParent = partIdx->rd_rel->relispartition ?
21609 368 : get_partition_parent(partIdxId, false) : InvalidOid;
21610 368 : if (currParent != RelationGetRelid(parentIdx))
21611 : {
21612 : IndexInfo *childInfo;
21613 : IndexInfo *parentInfo;
21614 : AttrMap *attmap;
21615 : bool found;
21616 : int i;
21617 : PartitionDesc partDesc;
21618 : Oid constraintOid,
21619 344 : cldConstrId = InvalidOid;
21620 :
21621 : /*
21622 : * If this partition already has an index attached, refuse the
21623 : * operation.
21624 : */
21625 344 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21626 :
21627 338 : if (OidIsValid(currParent))
21628 0 : ereport(ERROR,
21629 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21630 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21631 : RelationGetRelationName(partIdx),
21632 : RelationGetRelationName(parentIdx)),
21633 : errdetail("Index \"%s\" is already attached to another index.",
21634 : RelationGetRelationName(partIdx))));
21635 :
21636 : /* Make sure it indexes a partition of the other index's table */
21637 338 : partDesc = RelationGetPartitionDesc(parentTbl, true);
21638 338 : found = false;
21639 528 : for (i = 0; i < partDesc->nparts; i++)
21640 : {
21641 522 : if (partDesc->oids[i] == state.partitionOid)
21642 : {
21643 332 : found = true;
21644 332 : break;
21645 : }
21646 : }
21647 338 : if (!found)
21648 6 : ereport(ERROR,
21649 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21650 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21651 : RelationGetRelationName(partIdx),
21652 : RelationGetRelationName(parentIdx)),
21653 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21654 : RelationGetRelationName(partIdx),
21655 : RelationGetRelationName(parentTbl))));
21656 :
21657 : /* Ensure the indexes are compatible */
21658 332 : childInfo = BuildIndexInfo(partIdx);
21659 332 : parentInfo = BuildIndexInfo(parentIdx);
21660 332 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21661 : RelationGetDescr(parentTbl),
21662 : false);
21663 332 : if (!CompareIndexInfo(childInfo, parentInfo,
21664 332 : partIdx->rd_indcollation,
21665 332 : parentIdx->rd_indcollation,
21666 332 : partIdx->rd_opfamily,
21667 332 : parentIdx->rd_opfamily,
21668 : attmap))
21669 42 : ereport(ERROR,
21670 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21671 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21672 : RelationGetRelationName(partIdx),
21673 : RelationGetRelationName(parentIdx)),
21674 : errdetail("The index definitions do not match.")));
21675 :
21676 : /*
21677 : * If there is a constraint in the parent, make sure there is one in
21678 : * the child too.
21679 : */
21680 290 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21681 : RelationGetRelid(parentIdx));
21682 :
21683 290 : if (OidIsValid(constraintOid))
21684 : {
21685 110 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
21686 : partIdxId);
21687 110 : if (!OidIsValid(cldConstrId))
21688 6 : ereport(ERROR,
21689 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21690 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21691 : RelationGetRelationName(partIdx),
21692 : RelationGetRelationName(parentIdx)),
21693 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21694 : RelationGetRelationName(parentIdx),
21695 : RelationGetRelationName(parentTbl),
21696 : RelationGetRelationName(partIdx))));
21697 : }
21698 :
21699 : /*
21700 : * If it's a primary key, make sure the columns in the partition are
21701 : * NOT NULL.
21702 : */
21703 284 : if (parentIdx->rd_index->indisprimary)
21704 92 : verifyPartitionIndexNotNull(childInfo, partTbl);
21705 :
21706 : /* All good -- do it */
21707 284 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21708 284 : if (OidIsValid(constraintOid))
21709 104 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
21710 : RelationGetRelid(partTbl));
21711 :
21712 284 : free_attrmap(attmap);
21713 :
21714 284 : validatePartitionedIndex(parentIdx, parentTbl);
21715 : }
21716 :
21717 308 : relation_close(parentTbl, AccessShareLock);
21718 : /* keep these locks till commit */
21719 308 : relation_close(partTbl, NoLock);
21720 308 : relation_close(partIdx, NoLock);
21721 :
21722 308 : return address;
21723 : }
21724 :
21725 : /*
21726 : * Verify whether the given partition already contains an index attached
21727 : * to the given partitioned index. If so, raise an error.
21728 : */
21729 : static void
21730 344 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21731 : {
21732 : Oid existingIdx;
21733 :
21734 344 : existingIdx = index_get_partition(partitionTbl,
21735 : RelationGetRelid(parentIdx));
21736 344 : if (OidIsValid(existingIdx))
21737 6 : ereport(ERROR,
21738 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21739 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21740 : RelationGetRelationName(partIdx),
21741 : RelationGetRelationName(parentIdx)),
21742 : errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21743 : get_rel_name(existingIdx),
21744 : RelationGetRelationName(partitionTbl))));
21745 338 : }
21746 :
21747 : /*
21748 : * Verify whether the set of attached partition indexes to a parent index on
21749 : * a partitioned table is complete. If it is, mark the parent index valid.
21750 : *
21751 : * This should be called each time a partition index is attached.
21752 : */
21753 : static void
21754 326 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
21755 : {
21756 : Relation inheritsRel;
21757 : SysScanDesc scan;
21758 : ScanKeyData key;
21759 326 : int tuples = 0;
21760 : HeapTuple inhTup;
21761 326 : bool updated = false;
21762 :
21763 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21764 :
21765 : /*
21766 : * Scan pg_inherits for this parent index. Count each valid index we find
21767 : * (verifying the pg_index entry for each), and if we reach the total
21768 : * amount we expect, we can mark this parent index as valid.
21769 : */
21770 326 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21771 326 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21772 : BTEqualStrategyNumber, F_OIDEQ,
21773 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21774 326 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21775 : NULL, 1, &key);
21776 844 : while ((inhTup = systable_getnext(scan)) != NULL)
21777 : {
21778 518 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21779 : HeapTuple indTup;
21780 : Form_pg_index indexForm;
21781 :
21782 518 : indTup = SearchSysCache1(INDEXRELID,
21783 : ObjectIdGetDatum(inhForm->inhrelid));
21784 518 : if (!HeapTupleIsValid(indTup))
21785 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21786 518 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21787 518 : if (indexForm->indisvalid)
21788 460 : tuples += 1;
21789 518 : ReleaseSysCache(indTup);
21790 : }
21791 :
21792 : /* Done with pg_inherits */
21793 326 : systable_endscan(scan);
21794 326 : table_close(inheritsRel, AccessShareLock);
21795 :
21796 : /*
21797 : * If we found as many inherited indexes as the partitioned table has
21798 : * partitions, we're good; update pg_index to set indisvalid.
21799 : */
21800 326 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21801 : {
21802 : Relation idxRel;
21803 : HeapTuple indTup;
21804 : Form_pg_index indexForm;
21805 :
21806 164 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
21807 164 : indTup = SearchSysCacheCopy1(INDEXRELID,
21808 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21809 164 : if (!HeapTupleIsValid(indTup))
21810 0 : elog(ERROR, "cache lookup failed for index %u",
21811 : RelationGetRelid(partedIdx));
21812 164 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21813 :
21814 164 : indexForm->indisvalid = true;
21815 164 : updated = true;
21816 :
21817 164 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21818 :
21819 164 : table_close(idxRel, RowExclusiveLock);
21820 164 : heap_freetuple(indTup);
21821 : }
21822 :
21823 : /*
21824 : * If this index is in turn a partition of a larger index, validating it
21825 : * might cause the parent to become valid also. Try that.
21826 : */
21827 326 : if (updated && partedIdx->rd_rel->relispartition)
21828 : {
21829 : Oid parentIdxId,
21830 : parentTblId;
21831 : Relation parentIdx,
21832 : parentTbl;
21833 :
21834 : /* make sure we see the validation we just did */
21835 42 : CommandCounterIncrement();
21836 :
21837 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21838 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21839 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21840 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21841 : Assert(!parentIdx->rd_index->indisvalid);
21842 :
21843 42 : validatePartitionedIndex(parentIdx, parentTbl);
21844 :
21845 42 : relation_close(parentIdx, AccessExclusiveLock);
21846 42 : relation_close(parentTbl, AccessExclusiveLock);
21847 : }
21848 326 : }
21849 :
21850 : /*
21851 : * When attaching an index as a partition of a partitioned index which is a
21852 : * primary key, verify that all the columns in the partition are marked NOT
21853 : * NULL.
21854 : */
21855 : static void
21856 92 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
21857 : {
21858 186 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21859 : {
21860 94 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
21861 94 : iinfo->ii_IndexAttrNumbers[i] - 1);
21862 :
21863 94 : if (!att->attnotnull)
21864 0 : ereport(ERROR,
21865 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21866 : errmsg("invalid primary key definition"),
21867 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21868 : NameStr(att->attname),
21869 : RelationGetRelationName(partition)));
21870 : }
21871 92 : }
21872 :
21873 : /*
21874 : * Return an OID list of constraints that reference the given relation
21875 : * that are marked as having a parent constraints.
21876 : */
21877 : static List *
21878 998 : GetParentedForeignKeyRefs(Relation partition)
21879 : {
21880 : Relation pg_constraint;
21881 : HeapTuple tuple;
21882 : SysScanDesc scan;
21883 : ScanKeyData key[2];
21884 998 : List *constraints = NIL;
21885 :
21886 : /*
21887 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
21888 : * scan.
21889 : */
21890 1426 : if (RelationGetIndexList(partition) == NIL ||
21891 428 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
21892 : INDEX_ATTR_BITMAP_KEY)))
21893 726 : return NIL;
21894 :
21895 : /* Search for constraints referencing this table */
21896 272 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21897 272 : ScanKeyInit(&key[0],
21898 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21899 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21900 272 : ScanKeyInit(&key[1],
21901 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
21902 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21903 :
21904 : /* XXX This is a seqscan, as we don't have a usable index */
21905 272 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21906 444 : while ((tuple = systable_getnext(scan)) != NULL)
21907 : {
21908 172 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21909 :
21910 : /*
21911 : * We only need to process constraints that are part of larger ones.
21912 : */
21913 172 : if (!OidIsValid(constrForm->conparentid))
21914 0 : continue;
21915 :
21916 172 : constraints = lappend_oid(constraints, constrForm->oid);
21917 : }
21918 :
21919 272 : systable_endscan(scan);
21920 272 : table_close(pg_constraint, AccessShareLock);
21921 :
21922 272 : return constraints;
21923 : }
21924 :
21925 : /*
21926 : * During DETACH PARTITION, verify that any foreign keys pointing to the
21927 : * partitioned table would not become invalid. An error is raised if any
21928 : * referenced values exist.
21929 : */
21930 : static void
21931 534 : ATDetachCheckNoForeignKeyRefs(Relation partition)
21932 : {
21933 : List *constraints;
21934 : ListCell *cell;
21935 :
21936 534 : constraints = GetParentedForeignKeyRefs(partition);
21937 :
21938 610 : foreach(cell, constraints)
21939 : {
21940 110 : Oid constrOid = lfirst_oid(cell);
21941 : HeapTuple tuple;
21942 : Form_pg_constraint constrForm;
21943 : Relation rel;
21944 110 : Trigger trig = {0};
21945 :
21946 110 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21947 110 : if (!HeapTupleIsValid(tuple))
21948 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21949 110 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21950 :
21951 : Assert(OidIsValid(constrForm->conparentid));
21952 : Assert(constrForm->confrelid == RelationGetRelid(partition));
21953 :
21954 : /* prevent data changes into the referencing table until commit */
21955 110 : rel = table_open(constrForm->conrelid, ShareLock);
21956 :
21957 110 : trig.tgoid = InvalidOid;
21958 110 : trig.tgname = NameStr(constrForm->conname);
21959 110 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
21960 110 : trig.tgisinternal = true;
21961 110 : trig.tgconstrrelid = RelationGetRelid(partition);
21962 110 : trig.tgconstrindid = constrForm->conindid;
21963 110 : trig.tgconstraint = constrForm->oid;
21964 110 : trig.tgdeferrable = false;
21965 110 : trig.tginitdeferred = false;
21966 : /* we needn't fill in remaining fields */
21967 :
21968 110 : RI_PartitionRemove_Check(&trig, rel, partition);
21969 :
21970 76 : ReleaseSysCache(tuple);
21971 :
21972 76 : table_close(rel, NoLock);
21973 : }
21974 500 : }
21975 :
21976 : /*
21977 : * resolve column compression specification to compression method.
21978 : */
21979 : static char
21980 259922 : GetAttributeCompression(Oid atttypid, const char *compression)
21981 : {
21982 : char cmethod;
21983 :
21984 259922 : if (compression == NULL || strcmp(compression, "default") == 0)
21985 259738 : return InvalidCompressionMethod;
21986 :
21987 : /*
21988 : * To specify a nondefault method, the column data type must be toastable.
21989 : * Note this says nothing about whether the column's attstorage setting
21990 : * permits compression; we intentionally allow attstorage and
21991 : * attcompression to be independent. But with a non-toastable type,
21992 : * attstorage could not be set to a value that would permit compression.
21993 : *
21994 : * We don't actually need to enforce this, since nothing bad would happen
21995 : * if attcompression were non-default; it would never be consulted. But
21996 : * it seems more user-friendly to complain about a certainly-useless
21997 : * attempt to set the property.
21998 : */
21999 184 : if (!TypeIsToastable(atttypid))
22000 6 : ereport(ERROR,
22001 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22002 : errmsg("column data type %s does not support compression",
22003 : format_type_be(atttypid))));
22004 :
22005 178 : cmethod = CompressionNameToMethod(compression);
22006 178 : if (!CompressionMethodIsValid(cmethod))
22007 12 : ereport(ERROR,
22008 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22009 : errmsg("invalid compression method \"%s\"", compression)));
22010 :
22011 166 : return cmethod;
22012 : }
22013 :
22014 : /*
22015 : * resolve column storage specification
22016 : */
22017 : static char
22018 274 : GetAttributeStorage(Oid atttypid, const char *storagemode)
22019 : {
22020 274 : char cstorage = 0;
22021 :
22022 274 : if (pg_strcasecmp(storagemode, "plain") == 0)
22023 56 : cstorage = TYPSTORAGE_PLAIN;
22024 218 : else if (pg_strcasecmp(storagemode, "external") == 0)
22025 176 : cstorage = TYPSTORAGE_EXTERNAL;
22026 42 : else if (pg_strcasecmp(storagemode, "extended") == 0)
22027 16 : cstorage = TYPSTORAGE_EXTENDED;
22028 26 : else if (pg_strcasecmp(storagemode, "main") == 0)
22029 20 : cstorage = TYPSTORAGE_MAIN;
22030 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
22031 6 : cstorage = get_typstorage(atttypid);
22032 : else
22033 0 : ereport(ERROR,
22034 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22035 : errmsg("invalid storage type \"%s\"",
22036 : storagemode)));
22037 :
22038 : /*
22039 : * safety check: do not allow toasted storage modes unless column datatype
22040 : * is TOAST-aware.
22041 : */
22042 274 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22043 6 : ereport(ERROR,
22044 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22045 : errmsg("column data type %s can only have storage PLAIN",
22046 : format_type_be(atttypid))));
22047 :
22048 268 : return cstorage;
22049 : }
|