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 DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
726 : static void DropClonedTriggersFromPartition(Oid partitionId);
727 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
728 : Relation rel, RangeVar *name,
729 : bool concurrent);
730 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
731 : bool concurrent, Oid defaultPartOid);
732 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
733 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
734 : RangeVar *name);
735 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
736 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
737 : Relation partitionTbl);
738 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
739 : static List *GetParentedForeignKeyRefs(Relation partition);
740 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
741 : static char GetAttributeCompression(Oid atttypid, const char *compression);
742 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
743 :
744 :
745 : /* ----------------------------------------------------------------
746 : * DefineRelation
747 : * Creates a new relation.
748 : *
749 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
750 : * The other arguments are used to extend the behavior for other cases:
751 : * relkind: relkind to assign to the new relation
752 : * ownerId: if not InvalidOid, use this as the new relation's owner.
753 : * typaddress: if not null, it's set to the pg_type entry's address.
754 : * queryString: for error reporting
755 : *
756 : * Note that permissions checks are done against current user regardless of
757 : * ownerId. A nonzero ownerId is used when someone is creating a relation
758 : * "on behalf of" someone else, so we still want to see that the current user
759 : * has permissions to do it.
760 : *
761 : * If successful, returns the address of the new relation.
762 : * ----------------------------------------------------------------
763 : */
764 : ObjectAddress
765 62660 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
766 : ObjectAddress *typaddress, const char *queryString)
767 : {
768 : char relname[NAMEDATALEN];
769 : Oid namespaceId;
770 : Oid relationId;
771 : Oid tablespaceId;
772 : Relation rel;
773 : TupleDesc descriptor;
774 : List *inheritOids;
775 : List *old_constraints;
776 : List *old_notnulls;
777 : List *rawDefaults;
778 : List *cookedDefaults;
779 : List *nncols;
780 : Datum reloptions;
781 : ListCell *listptr;
782 : AttrNumber attnum;
783 : bool partitioned;
784 62660 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
785 : Oid ofTypeId;
786 : ObjectAddress address;
787 : LOCKMODE parentLockmode;
788 62660 : Oid accessMethodId = InvalidOid;
789 :
790 : /*
791 : * Truncate relname to appropriate length (probably a waste of time, as
792 : * parser should have done this already).
793 : */
794 62660 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
795 :
796 : /*
797 : * Check consistency of arguments
798 : */
799 62660 : if (stmt->oncommit != ONCOMMIT_NOOP
800 188 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
801 12 : ereport(ERROR,
802 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
803 : errmsg("ON COMMIT can only be used on temporary tables")));
804 :
805 62648 : if (stmt->partspec != NULL)
806 : {
807 5010 : if (relkind != RELKIND_RELATION)
808 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
809 :
810 5010 : relkind = RELKIND_PARTITIONED_TABLE;
811 5010 : partitioned = true;
812 : }
813 : else
814 57638 : partitioned = false;
815 :
816 62648 : if (relkind == RELKIND_PARTITIONED_TABLE &&
817 5010 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
818 6 : ereport(ERROR,
819 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
820 : errmsg("partitioned tables cannot be unlogged")));
821 :
822 : /*
823 : * Look up the namespace in which we are supposed to create the relation,
824 : * check we have permission to create there, lock it against concurrent
825 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
826 : * namespace is selected.
827 : */
828 : namespaceId =
829 62642 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
830 :
831 : /*
832 : * Security check: disallow creating temp tables from security-restricted
833 : * code. This is needed because calling code might not expect untrusted
834 : * tables to appear in pg_temp at the front of its search path.
835 : */
836 62642 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
837 3190 : && InSecurityRestrictedOperation())
838 0 : ereport(ERROR,
839 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
840 : errmsg("cannot create temporary table within security-restricted operation")));
841 :
842 : /*
843 : * Determine the lockmode to use when scanning parents. A self-exclusive
844 : * lock is needed here.
845 : *
846 : * For regular inheritance, if two backends attempt to add children to the
847 : * same parent simultaneously, and that parent has no pre-existing
848 : * children, then both will attempt to update the parent's relhassubclass
849 : * field, leading to a "tuple concurrently updated" error. Also, this
850 : * interlocks against a concurrent ANALYZE on the parent table, which
851 : * might otherwise be attempting to clear the parent's relhassubclass
852 : * field, if its previous children were recently dropped.
853 : *
854 : * If the child table is a partition, then we instead grab an exclusive
855 : * lock on the parent because its partition descriptor will be changed by
856 : * addition of the new partition.
857 : */
858 62642 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
859 : ShareUpdateExclusiveLock);
860 :
861 : /* Determine the list of OIDs of the parents. */
862 62642 : inheritOids = NIL;
863 73156 : foreach(listptr, stmt->inhRelations)
864 : {
865 10514 : RangeVar *rv = (RangeVar *) lfirst(listptr);
866 : Oid parentOid;
867 :
868 10514 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
869 :
870 : /*
871 : * Reject duplications in the list of parents.
872 : */
873 10514 : if (list_member_oid(inheritOids, parentOid))
874 0 : ereport(ERROR,
875 : (errcode(ERRCODE_DUPLICATE_TABLE),
876 : errmsg("relation \"%s\" would be inherited from more than once",
877 : get_rel_name(parentOid))));
878 :
879 10514 : inheritOids = lappend_oid(inheritOids, parentOid);
880 : }
881 :
882 : /*
883 : * Select tablespace to use: an explicitly indicated one, or (in the case
884 : * of a partitioned table) the parent's, if it has one.
885 : */
886 62642 : if (stmt->tablespacename)
887 : {
888 128 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
889 :
890 122 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
891 6 : ereport(ERROR,
892 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
893 : errmsg("cannot specify default tablespace for partitioned relations")));
894 : }
895 62514 : else if (stmt->partbound)
896 : {
897 : Assert(list_length(inheritOids) == 1);
898 8030 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
899 : }
900 : else
901 54484 : tablespaceId = InvalidOid;
902 :
903 : /* still nothing? use the default */
904 62630 : if (!OidIsValid(tablespaceId))
905 62492 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
906 : partitioned);
907 :
908 : /* Check permissions except when using database's default */
909 62624 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
910 : {
911 : AclResult aclresult;
912 :
913 164 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
914 : ACL_CREATE);
915 164 : if (aclresult != ACLCHECK_OK)
916 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
917 6 : get_tablespace_name(tablespaceId));
918 : }
919 :
920 : /* In all cases disallow placing user relations in pg_global */
921 62618 : if (tablespaceId == GLOBALTABLESPACE_OID)
922 18 : ereport(ERROR,
923 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
924 : errmsg("only shared relations can be placed in pg_global tablespace")));
925 :
926 : /* Identify user ID that will own the table */
927 62600 : if (!OidIsValid(ownerId))
928 62360 : ownerId = GetUserId();
929 :
930 : /*
931 : * Parse and validate reloptions, if any.
932 : */
933 62600 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
934 : true, false);
935 :
936 62582 : switch (relkind)
937 : {
938 16678 : case RELKIND_VIEW:
939 16678 : (void) view_reloptions(reloptions, true);
940 16660 : break;
941 4986 : case RELKIND_PARTITIONED_TABLE:
942 4986 : (void) partitioned_table_reloptions(reloptions, true);
943 4980 : break;
944 40918 : default:
945 40918 : (void) heap_reloptions(relkind, reloptions, true);
946 : }
947 :
948 62462 : if (stmt->ofTypename)
949 : {
950 : AclResult aclresult;
951 :
952 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
953 :
954 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
955 86 : if (aclresult != ACLCHECK_OK)
956 6 : aclcheck_error_type(aclresult, ofTypeId);
957 : }
958 : else
959 62376 : ofTypeId = InvalidOid;
960 :
961 : /*
962 : * Look up inheritance ancestors and generate relation schema, including
963 : * inherited attributes. (Note that stmt->tableElts is destructively
964 : * modified by MergeAttributes.)
965 : */
966 62216 : stmt->tableElts =
967 62456 : MergeAttributes(stmt->tableElts, inheritOids,
968 62456 : stmt->relation->relpersistence,
969 62456 : stmt->partbound != NULL,
970 : &old_constraints, &old_notnulls);
971 :
972 : /*
973 : * Create a tuple descriptor from the relation schema. Note that this
974 : * deals with column names, types, and in-descriptor NOT NULL flags, but
975 : * not default values, NOT NULL or CHECK constraints; we handle those
976 : * below.
977 : */
978 62216 : descriptor = BuildDescForRelation(stmt->tableElts);
979 :
980 : /*
981 : * Find columns with default values and prepare for insertion of the
982 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
983 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
984 : * while raw defaults go into a list of RawColumnDefault structs that will
985 : * be processed by AddRelationNewConstraints. (We can't deal with raw
986 : * expressions until we can do transformExpr.)
987 : */
988 62168 : rawDefaults = NIL;
989 62168 : cookedDefaults = NIL;
990 62168 : attnum = 0;
991 :
992 315368 : foreach(listptr, stmt->tableElts)
993 : {
994 253200 : ColumnDef *colDef = lfirst(listptr);
995 :
996 253200 : attnum++;
997 253200 : if (colDef->raw_default != NULL)
998 : {
999 : RawColumnDefault *rawEnt;
1000 :
1001 : Assert(colDef->cooked_default == NULL);
1002 :
1003 3184 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
1004 3184 : rawEnt->attnum = attnum;
1005 3184 : rawEnt->raw_default = colDef->raw_default;
1006 3184 : rawEnt->generated = colDef->generated;
1007 3184 : rawDefaults = lappend(rawDefaults, rawEnt);
1008 : }
1009 250016 : else if (colDef->cooked_default != NULL)
1010 : {
1011 : CookedConstraint *cooked;
1012 :
1013 390 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
1014 390 : cooked->contype = CONSTR_DEFAULT;
1015 390 : cooked->conoid = InvalidOid; /* until created */
1016 390 : cooked->name = NULL;
1017 390 : cooked->attnum = attnum;
1018 390 : cooked->expr = colDef->cooked_default;
1019 390 : cooked->is_enforced = true;
1020 390 : cooked->skip_validation = false;
1021 390 : cooked->is_local = true; /* not used for defaults */
1022 390 : cooked->inhcount = 0; /* ditto */
1023 390 : cooked->is_no_inherit = false;
1024 390 : cookedDefaults = lappend(cookedDefaults, cooked);
1025 : }
1026 : }
1027 :
1028 : /*
1029 : * For relations with table AM and partitioned tables, select access
1030 : * method to use: an explicitly indicated one, or (in the case of a
1031 : * partitioned table) the parent's, if it has one.
1032 : */
1033 62168 : if (stmt->accessMethod != NULL)
1034 : {
1035 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1036 122 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1037 : }
1038 62046 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1039 : {
1040 38614 : if (stmt->partbound)
1041 : {
1042 : Assert(list_length(inheritOids) == 1);
1043 7848 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1044 : }
1045 :
1046 38614 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1047 33620 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1048 : }
1049 :
1050 : /*
1051 : * Create the relation. Inherited defaults and CHECK constraints are
1052 : * passed in for immediate handling --- since they don't need parsing,
1053 : * they can be stored immediately.
1054 : */
1055 62150 : relationId = heap_create_with_catalog(relname,
1056 : namespaceId,
1057 : tablespaceId,
1058 : InvalidOid,
1059 : InvalidOid,
1060 : ofTypeId,
1061 : ownerId,
1062 : accessMethodId,
1063 : descriptor,
1064 : list_concat(cookedDefaults,
1065 : old_constraints),
1066 : relkind,
1067 62150 : stmt->relation->relpersistence,
1068 : false,
1069 : false,
1070 : stmt->oncommit,
1071 : reloptions,
1072 : true,
1073 : allowSystemTableMods,
1074 : false,
1075 : InvalidOid,
1076 : typaddress);
1077 :
1078 : /*
1079 : * We must bump the command counter to make the newly-created relation
1080 : * tuple visible for opening.
1081 : */
1082 62102 : CommandCounterIncrement();
1083 :
1084 : /*
1085 : * Open the new relation and acquire exclusive lock on it. This isn't
1086 : * really necessary for locking out other backends (since they can't see
1087 : * the new rel anyway until we commit), but it keeps the lock manager from
1088 : * complaining about deadlock risks.
1089 : */
1090 62102 : rel = relation_open(relationId, AccessExclusiveLock);
1091 :
1092 : /*
1093 : * Now add any newly specified column default and generation expressions
1094 : * to the new relation. These are passed to us in the form of raw
1095 : * parsetrees; we need to transform them to executable expression trees
1096 : * before they can be added. The most convenient way to do that is to
1097 : * apply the parser's transformExpr routine, but transformExpr doesn't
1098 : * work unless we have a pre-existing relation. So, the transformation has
1099 : * to be postponed to this final step of CREATE TABLE.
1100 : *
1101 : * This needs to be before processing the partitioning clauses because
1102 : * those could refer to generated columns.
1103 : */
1104 62102 : if (rawDefaults)
1105 2718 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1106 : true, true, false, queryString);
1107 :
1108 : /*
1109 : * Make column generation expressions visible for use by partitioning.
1110 : */
1111 61910 : CommandCounterIncrement();
1112 :
1113 : /* Process and store partition bound, if any. */
1114 61910 : if (stmt->partbound)
1115 : {
1116 : PartitionBoundSpec *bound;
1117 : ParseState *pstate;
1118 7952 : Oid parentId = linitial_oid(inheritOids),
1119 : defaultPartOid;
1120 : Relation parent,
1121 7952 : defaultRel = NULL;
1122 : ParseNamespaceItem *nsitem;
1123 :
1124 : /* Already have strong enough lock on the parent */
1125 7952 : parent = table_open(parentId, NoLock);
1126 :
1127 : /*
1128 : * We are going to try to validate the partition bound specification
1129 : * against the partition key of parentRel, so it better have one.
1130 : */
1131 7952 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1132 18 : ereport(ERROR,
1133 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1134 : errmsg("\"%s\" is not partitioned",
1135 : RelationGetRelationName(parent))));
1136 :
1137 : /*
1138 : * The partition constraint of the default partition depends on the
1139 : * partition bounds of every other partition. It is possible that
1140 : * another backend might be about to execute a query on the default
1141 : * partition table, and that the query relies on previously cached
1142 : * default partition constraints. We must therefore take a table lock
1143 : * strong enough to prevent all queries on the default partition from
1144 : * proceeding until we commit and send out a shared-cache-inval notice
1145 : * that will make them update their index lists.
1146 : *
1147 : * Order of locking: The relation being added won't be visible to
1148 : * other backends until it is committed, hence here in
1149 : * DefineRelation() the order of locking the default partition and the
1150 : * relation being added does not matter. But at all other places we
1151 : * need to lock the default relation before we lock the relation being
1152 : * added or removed i.e. we should take the lock in same order at all
1153 : * the places such that lock parent, lock default partition and then
1154 : * lock the partition so as to avoid a deadlock.
1155 : */
1156 : defaultPartOid =
1157 7934 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1158 : true));
1159 7934 : if (OidIsValid(defaultPartOid))
1160 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1161 :
1162 : /* Transform the bound values */
1163 7934 : pstate = make_parsestate(NULL);
1164 7934 : pstate->p_sourcetext = queryString;
1165 :
1166 : /*
1167 : * Add an nsitem containing this relation, so that transformExpr
1168 : * called on partition bound expressions is able to report errors
1169 : * using a proper context.
1170 : */
1171 7934 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1172 : NULL, false, false);
1173 7934 : addNSItemToQuery(pstate, nsitem, false, true, true);
1174 :
1175 7934 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1176 :
1177 : /*
1178 : * Check first that the new partition's bound is valid and does not
1179 : * overlap with any of existing partitions of the parent.
1180 : */
1181 7730 : check_new_partition_bound(relname, parent, bound, pstate);
1182 :
1183 : /*
1184 : * If the default partition exists, its partition constraints will
1185 : * change after the addition of this new partition such that it won't
1186 : * allow any row that qualifies for this new partition. So, check that
1187 : * the existing data in the default partition satisfies the constraint
1188 : * as it will exist after adding this partition.
1189 : */
1190 7616 : if (OidIsValid(defaultPartOid))
1191 : {
1192 348 : check_default_partition_contents(parent, defaultRel, bound);
1193 : /* Keep the lock until commit. */
1194 330 : table_close(defaultRel, NoLock);
1195 : }
1196 :
1197 : /* Update the pg_class entry. */
1198 7598 : StorePartitionBound(rel, parent, bound);
1199 :
1200 7598 : table_close(parent, NoLock);
1201 : }
1202 :
1203 : /* Store inheritance information for new rel. */
1204 61556 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1205 :
1206 : /*
1207 : * Process the partitioning specification (if any) and store the partition
1208 : * key information into the catalog.
1209 : */
1210 61556 : if (partitioned)
1211 : {
1212 : ParseState *pstate;
1213 : int partnatts;
1214 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1215 : Oid partopclass[PARTITION_MAX_KEYS];
1216 : Oid partcollation[PARTITION_MAX_KEYS];
1217 4980 : List *partexprs = NIL;
1218 :
1219 4980 : pstate = make_parsestate(NULL);
1220 4980 : pstate->p_sourcetext = queryString;
1221 :
1222 4980 : partnatts = list_length(stmt->partspec->partParams);
1223 :
1224 : /* Protect fixed-size arrays here and in executor */
1225 4980 : if (partnatts > PARTITION_MAX_KEYS)
1226 0 : ereport(ERROR,
1227 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1228 : errmsg("cannot partition using more than %d columns",
1229 : PARTITION_MAX_KEYS)));
1230 :
1231 : /*
1232 : * We need to transform the raw parsetrees corresponding to partition
1233 : * expressions into executable expression trees. Like column defaults
1234 : * and CHECK constraints, we could not have done the transformation
1235 : * earlier.
1236 : */
1237 4980 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1238 :
1239 4950 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1240 : partattrs, &partexprs, partopclass,
1241 4950 : partcollation, stmt->partspec->strategy);
1242 :
1243 4854 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1244 : partexprs,
1245 : partopclass, partcollation);
1246 :
1247 : /* make it all visible */
1248 4854 : CommandCounterIncrement();
1249 : }
1250 :
1251 : /*
1252 : * If we're creating a partition, create now all the indexes, triggers,
1253 : * FKs defined in the parent.
1254 : *
1255 : * We can't do it earlier, because DefineIndex wants to know the partition
1256 : * key which we just stored.
1257 : */
1258 61430 : if (stmt->partbound)
1259 : {
1260 7592 : Oid parentId = linitial_oid(inheritOids);
1261 : Relation parent;
1262 : List *idxlist;
1263 : ListCell *cell;
1264 :
1265 : /* Already have strong enough lock on the parent */
1266 7592 : parent = table_open(parentId, NoLock);
1267 7592 : idxlist = RelationGetIndexList(parent);
1268 :
1269 : /*
1270 : * For each index in the parent table, create one in the partition
1271 : */
1272 9006 : foreach(cell, idxlist)
1273 : {
1274 1432 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1275 : AttrMap *attmap;
1276 : IndexStmt *idxstmt;
1277 : Oid constraintOid;
1278 :
1279 1432 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1280 : {
1281 36 : if (idxRel->rd_index->indisunique)
1282 12 : ereport(ERROR,
1283 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1284 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1285 : RelationGetRelationName(parent)),
1286 : errdetail("Table \"%s\" contains indexes that are unique.",
1287 : RelationGetRelationName(parent))));
1288 : else
1289 : {
1290 24 : index_close(idxRel, AccessShareLock);
1291 24 : continue;
1292 : }
1293 : }
1294 :
1295 1396 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1296 : RelationGetDescr(parent),
1297 : false);
1298 : idxstmt =
1299 1396 : generateClonedIndexStmt(NULL, idxRel,
1300 : attmap, &constraintOid);
1301 1396 : DefineIndex(RelationGetRelid(rel),
1302 : idxstmt,
1303 : InvalidOid,
1304 : RelationGetRelid(idxRel),
1305 : constraintOid,
1306 : -1,
1307 : false, false, false, false, false);
1308 :
1309 1390 : index_close(idxRel, AccessShareLock);
1310 : }
1311 :
1312 7574 : list_free(idxlist);
1313 :
1314 : /*
1315 : * If there are any row-level triggers, clone them to the new
1316 : * partition.
1317 : */
1318 7574 : if (parent->trigdesc != NULL)
1319 432 : CloneRowTriggersToPartition(parent, rel);
1320 :
1321 : /*
1322 : * And foreign keys too. Note that because we're freshly creating the
1323 : * table, there is no need to verify these new constraints.
1324 : */
1325 7574 : CloneForeignKeyConstraints(NULL, parent, rel);
1326 :
1327 7574 : table_close(parent, NoLock);
1328 : }
1329 :
1330 : /*
1331 : * Now add any newly specified CHECK constraints to the new relation. Same
1332 : * as for defaults above, but these need to come after partitioning is set
1333 : * up.
1334 : */
1335 61412 : if (stmt->constraints)
1336 746 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1337 : true, true, false, queryString);
1338 :
1339 : /*
1340 : * Finally, merge the not-null constraints that are declared directly with
1341 : * those that come from parent relations (making sure to count inheritance
1342 : * appropriately for each), create them, and set the attnotnull flag on
1343 : * columns that don't yet have it.
1344 : */
1345 61382 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1346 : old_notnulls);
1347 137864 : foreach_int(attrnum, nncols)
1348 15256 : set_attnotnull(NULL, rel, attrnum, true, false);
1349 :
1350 61304 : ObjectAddressSet(address, RelationRelationId, relationId);
1351 :
1352 : /*
1353 : * Clean up. We keep lock on new relation (although it shouldn't be
1354 : * visible to anyone else anyway, until commit).
1355 : */
1356 61304 : relation_close(rel, NoLock);
1357 :
1358 61304 : return address;
1359 : }
1360 :
1361 : /*
1362 : * BuildDescForRelation
1363 : *
1364 : * Given a list of ColumnDef nodes, build a TupleDesc.
1365 : *
1366 : * Note: This is only for the limited purpose of table and view creation. Not
1367 : * everything is filled in. A real tuple descriptor should be obtained from
1368 : * the relcache.
1369 : */
1370 : TupleDesc
1371 65182 : BuildDescForRelation(const List *columns)
1372 : {
1373 : int natts;
1374 : AttrNumber attnum;
1375 : ListCell *l;
1376 : TupleDesc desc;
1377 : char *attname;
1378 : Oid atttypid;
1379 : int32 atttypmod;
1380 : Oid attcollation;
1381 : int attdim;
1382 :
1383 : /*
1384 : * allocate a new tuple descriptor
1385 : */
1386 65182 : natts = list_length(columns);
1387 65182 : desc = CreateTemplateTupleDesc(natts);
1388 :
1389 65182 : attnum = 0;
1390 :
1391 321612 : foreach(l, columns)
1392 : {
1393 256490 : ColumnDef *entry = lfirst(l);
1394 : AclResult aclresult;
1395 : Form_pg_attribute att;
1396 :
1397 : /*
1398 : * for each entry in the list, get the name and type information from
1399 : * the list and have TupleDescInitEntry fill in the attribute
1400 : * information we need.
1401 : */
1402 256490 : attnum++;
1403 :
1404 256490 : attname = entry->colname;
1405 256490 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1406 :
1407 256490 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1408 256490 : if (aclresult != ACLCHECK_OK)
1409 42 : aclcheck_error_type(aclresult, atttypid);
1410 :
1411 256448 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1412 256448 : attdim = list_length(entry->typeName->arrayBounds);
1413 256448 : if (attdim > PG_INT16_MAX)
1414 0 : ereport(ERROR,
1415 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1416 : errmsg("too many array dimensions"));
1417 :
1418 256448 : if (entry->typeName->setof)
1419 0 : ereport(ERROR,
1420 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1421 : errmsg("column \"%s\" cannot be declared SETOF",
1422 : attname)));
1423 :
1424 256448 : TupleDescInitEntry(desc, attnum, attname,
1425 : atttypid, atttypmod, attdim);
1426 256448 : att = TupleDescAttr(desc, attnum - 1);
1427 :
1428 : /* Override TupleDescInitEntry's settings as requested */
1429 256448 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1430 :
1431 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1432 256448 : att->attnotnull = entry->is_not_null;
1433 256448 : att->attislocal = entry->is_local;
1434 256448 : att->attinhcount = entry->inhcount;
1435 256448 : att->attidentity = entry->identity;
1436 256448 : att->attgenerated = entry->generated;
1437 256448 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1438 256436 : if (entry->storage)
1439 20144 : att->attstorage = entry->storage;
1440 236292 : else if (entry->storage_name)
1441 26 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1442 :
1443 256430 : populate_compact_attribute(desc, attnum - 1);
1444 : }
1445 :
1446 65122 : return desc;
1447 : }
1448 :
1449 : /*
1450 : * Emit the right error or warning message for a "DROP" command issued on a
1451 : * non-existent relation
1452 : */
1453 : static void
1454 1088 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1455 : {
1456 : const struct dropmsgstrings *rentry;
1457 :
1458 1208 : if (rel->schemaname != NULL &&
1459 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1460 : {
1461 42 : if (!missing_ok)
1462 : {
1463 0 : ereport(ERROR,
1464 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1465 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1466 : }
1467 : else
1468 : {
1469 42 : ereport(NOTICE,
1470 : (errmsg("schema \"%s\" does not exist, skipping",
1471 : rel->schemaname)));
1472 : }
1473 42 : return;
1474 : }
1475 :
1476 1366 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1477 : {
1478 1366 : if (rentry->kind == rightkind)
1479 : {
1480 1046 : if (!missing_ok)
1481 : {
1482 138 : ereport(ERROR,
1483 : (errcode(rentry->nonexistent_code),
1484 : errmsg(rentry->nonexistent_msg, rel->relname)));
1485 : }
1486 : else
1487 : {
1488 908 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1489 908 : break;
1490 : }
1491 : }
1492 : }
1493 :
1494 : Assert(rentry->kind != '\0'); /* Should be impossible */
1495 : }
1496 :
1497 : /*
1498 : * Emit the right error message for a "DROP" command issued on a
1499 : * relation of the wrong type
1500 : */
1501 : static void
1502 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1503 : {
1504 : const struct dropmsgstrings *rentry;
1505 : const struct dropmsgstrings *wentry;
1506 :
1507 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1508 0 : if (rentry->kind == rightkind)
1509 0 : break;
1510 : Assert(rentry->kind != '\0');
1511 :
1512 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1513 0 : if (wentry->kind == wrongkind)
1514 0 : break;
1515 : /* wrongkind could be something we don't have in our table... */
1516 :
1517 0 : ereport(ERROR,
1518 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1519 : errmsg(rentry->nota_msg, relname),
1520 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1521 : }
1522 :
1523 : /*
1524 : * RemoveRelations
1525 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1526 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1527 : */
1528 : void
1529 17362 : RemoveRelations(DropStmt *drop)
1530 : {
1531 : ObjectAddresses *objects;
1532 : char relkind;
1533 : ListCell *cell;
1534 17362 : int flags = 0;
1535 17362 : LOCKMODE lockmode = AccessExclusiveLock;
1536 :
1537 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1538 17362 : if (drop->concurrent)
1539 : {
1540 : /*
1541 : * Note that for temporary relations this lock may get upgraded later
1542 : * on, but as no other session can access a temporary relation, this
1543 : * is actually fine.
1544 : */
1545 184 : lockmode = ShareUpdateExclusiveLock;
1546 : Assert(drop->removeType == OBJECT_INDEX);
1547 184 : if (list_length(drop->objects) != 1)
1548 6 : ereport(ERROR,
1549 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1550 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1551 178 : if (drop->behavior == DROP_CASCADE)
1552 0 : ereport(ERROR,
1553 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1554 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1555 : }
1556 :
1557 : /*
1558 : * First we identify all the relations, then we delete them in a single
1559 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1560 : * RESTRICT errors if one of the relations depends on another.
1561 : */
1562 :
1563 : /* Determine required relkind */
1564 17356 : switch (drop->removeType)
1565 : {
1566 15076 : case OBJECT_TABLE:
1567 15076 : relkind = RELKIND_RELATION;
1568 15076 : break;
1569 :
1570 866 : case OBJECT_INDEX:
1571 866 : relkind = RELKIND_INDEX;
1572 866 : break;
1573 :
1574 170 : case OBJECT_SEQUENCE:
1575 170 : relkind = RELKIND_SEQUENCE;
1576 170 : break;
1577 :
1578 954 : case OBJECT_VIEW:
1579 954 : relkind = RELKIND_VIEW;
1580 954 : break;
1581 :
1582 126 : case OBJECT_MATVIEW:
1583 126 : relkind = RELKIND_MATVIEW;
1584 126 : break;
1585 :
1586 164 : case OBJECT_FOREIGN_TABLE:
1587 164 : relkind = RELKIND_FOREIGN_TABLE;
1588 164 : break;
1589 :
1590 0 : default:
1591 0 : elog(ERROR, "unrecognized drop object type: %d",
1592 : (int) drop->removeType);
1593 : relkind = 0; /* keep compiler quiet */
1594 : break;
1595 : }
1596 :
1597 : /* Lock and validate each relation; build a list of object addresses */
1598 17356 : objects = new_object_addresses();
1599 :
1600 38686 : foreach(cell, drop->objects)
1601 : {
1602 21494 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1603 : Oid relOid;
1604 : ObjectAddress obj;
1605 : struct DropRelationCallbackState state;
1606 :
1607 : /*
1608 : * These next few steps are a great deal like relation_openrv, but we
1609 : * don't bother building a relcache entry since we don't need it.
1610 : *
1611 : * Check for shared-cache-inval messages before trying to access the
1612 : * relation. This is needed to cover the case where the name
1613 : * identifies a rel that has been dropped and recreated since the
1614 : * start of our transaction: if we don't flush the old syscache entry,
1615 : * then we'll latch onto that entry and suffer an error later.
1616 : */
1617 21494 : AcceptInvalidationMessages();
1618 :
1619 : /* Look up the appropriate relation using namespace search. */
1620 21494 : state.expected_relkind = relkind;
1621 42988 : state.heap_lockmode = drop->concurrent ?
1622 21494 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1623 : /* We must initialize these fields to show that no locks are held: */
1624 21494 : state.heapOid = InvalidOid;
1625 21494 : state.partParentOid = InvalidOid;
1626 :
1627 21494 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1628 : RangeVarCallbackForDropRelation,
1629 : &state);
1630 :
1631 : /* Not there? */
1632 21474 : if (!OidIsValid(relOid))
1633 : {
1634 1088 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1635 950 : continue;
1636 : }
1637 :
1638 : /*
1639 : * Decide if concurrent mode needs to be used here or not. The
1640 : * callback retrieved the rel's persistence for us.
1641 : */
1642 20386 : if (drop->concurrent &&
1643 172 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1644 : {
1645 : Assert(list_length(drop->objects) == 1 &&
1646 : drop->removeType == OBJECT_INDEX);
1647 154 : flags |= PERFORM_DELETION_CONCURRENTLY;
1648 : }
1649 :
1650 : /*
1651 : * Concurrent index drop cannot be used with partitioned indexes,
1652 : * either.
1653 : */
1654 20386 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1655 154 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1656 6 : ereport(ERROR,
1657 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1658 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1659 : rel->relname)));
1660 :
1661 : /*
1662 : * If we're told to drop a partitioned index, we must acquire lock on
1663 : * all the children of its parent partitioned table before proceeding.
1664 : * Otherwise we'd try to lock the child index partitions before their
1665 : * tables, leading to potential deadlock against other sessions that
1666 : * will lock those objects in the other order.
1667 : */
1668 20380 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1669 76 : (void) find_all_inheritors(state.heapOid,
1670 : state.heap_lockmode,
1671 : NULL);
1672 :
1673 : /* OK, we're ready to delete this one */
1674 20380 : obj.classId = RelationRelationId;
1675 20380 : obj.objectId = relOid;
1676 20380 : obj.objectSubId = 0;
1677 :
1678 20380 : add_exact_object_address(&obj, objects);
1679 : }
1680 :
1681 17192 : performMultipleDeletions(objects, drop->behavior, flags);
1682 :
1683 17050 : free_object_addresses(objects);
1684 17050 : }
1685 :
1686 : /*
1687 : * Before acquiring a table lock, check whether we have sufficient rights.
1688 : * In the case of DROP INDEX, also try to lock the table before the index.
1689 : * Also, if the table to be dropped is a partition, we try to lock the parent
1690 : * first.
1691 : */
1692 : static void
1693 21858 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1694 : void *arg)
1695 : {
1696 : HeapTuple tuple;
1697 : struct DropRelationCallbackState *state;
1698 : char expected_relkind;
1699 : bool is_partition;
1700 : Form_pg_class classform;
1701 : LOCKMODE heap_lockmode;
1702 21858 : bool invalid_system_index = false;
1703 :
1704 21858 : state = (struct DropRelationCallbackState *) arg;
1705 21858 : heap_lockmode = state->heap_lockmode;
1706 :
1707 : /*
1708 : * If we previously locked some other index's heap, and the name we're
1709 : * looking up no longer refers to that relation, release the now-useless
1710 : * lock.
1711 : */
1712 21858 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1713 : {
1714 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1715 0 : state->heapOid = InvalidOid;
1716 : }
1717 :
1718 : /*
1719 : * Similarly, if we previously locked some other partition's heap, and the
1720 : * name we're looking up no longer refers to that relation, release the
1721 : * now-useless lock.
1722 : */
1723 21858 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1724 : {
1725 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1726 0 : state->partParentOid = InvalidOid;
1727 : }
1728 :
1729 : /* Didn't find a relation, so no need for locking or permission checks. */
1730 21858 : if (!OidIsValid(relOid))
1731 1092 : return;
1732 :
1733 20766 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1734 20766 : if (!HeapTupleIsValid(tuple))
1735 0 : return; /* concurrently dropped, so nothing to do */
1736 20766 : classform = (Form_pg_class) GETSTRUCT(tuple);
1737 20766 : is_partition = classform->relispartition;
1738 :
1739 : /* Pass back some data to save lookups in RemoveRelations */
1740 20766 : state->actual_relkind = classform->relkind;
1741 20766 : state->actual_relpersistence = classform->relpersistence;
1742 :
1743 : /*
1744 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1745 : * but RemoveRelations() can only pass one relkind for a given relation.
1746 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1747 : * That means we must be careful before giving the wrong type error when
1748 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1749 : * exists with indexes.
1750 : */
1751 20766 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1752 2976 : expected_relkind = RELKIND_RELATION;
1753 17790 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1754 90 : expected_relkind = RELKIND_INDEX;
1755 : else
1756 17700 : expected_relkind = classform->relkind;
1757 :
1758 20766 : if (state->expected_relkind != expected_relkind)
1759 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1760 0 : state->expected_relkind);
1761 :
1762 : /* Allow DROP to either table owner or schema owner */
1763 20766 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1764 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1765 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1766 18 : get_relkind_objtype(classform->relkind),
1767 18 : rel->relname);
1768 :
1769 : /*
1770 : * Check the case of a system index that might have been invalidated by a
1771 : * failed concurrent process and allow its drop. For the time being, this
1772 : * only concerns indexes of toast relations that became invalid during a
1773 : * REINDEX CONCURRENTLY process.
1774 : */
1775 20748 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1776 : {
1777 : HeapTuple locTuple;
1778 : Form_pg_index indexform;
1779 : bool indisvalid;
1780 :
1781 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1782 0 : if (!HeapTupleIsValid(locTuple))
1783 : {
1784 0 : ReleaseSysCache(tuple);
1785 0 : return;
1786 : }
1787 :
1788 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1789 0 : indisvalid = indexform->indisvalid;
1790 0 : ReleaseSysCache(locTuple);
1791 :
1792 : /* Mark object as being an invalid index of system catalogs */
1793 0 : if (!indisvalid)
1794 0 : invalid_system_index = true;
1795 : }
1796 :
1797 : /* In the case of an invalid index, it is fine to bypass this check */
1798 20748 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1799 2 : ereport(ERROR,
1800 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1801 : errmsg("permission denied: \"%s\" is a system catalog",
1802 : rel->relname)));
1803 :
1804 20746 : ReleaseSysCache(tuple);
1805 :
1806 : /*
1807 : * In DROP INDEX, attempt to acquire lock on the parent table before
1808 : * locking the index. index_drop() will need this anyway, and since
1809 : * regular queries lock tables before their indexes, we risk deadlock if
1810 : * we do it the other way around. No error if we don't find a pg_index
1811 : * entry, though --- the relation may have been dropped. Note that this
1812 : * code will execute for either plain or partitioned indexes.
1813 : */
1814 20746 : if (expected_relkind == RELKIND_INDEX &&
1815 : relOid != oldRelOid)
1816 : {
1817 854 : state->heapOid = IndexGetRelation(relOid, true);
1818 854 : if (OidIsValid(state->heapOid))
1819 854 : LockRelationOid(state->heapOid, heap_lockmode);
1820 : }
1821 :
1822 : /*
1823 : * Similarly, if the relation is a partition, we must acquire lock on its
1824 : * parent before locking the partition. That's because queries lock the
1825 : * parent before its partitions, so we risk deadlock if we do it the other
1826 : * way around.
1827 : */
1828 20746 : if (is_partition && relOid != oldRelOid)
1829 : {
1830 618 : state->partParentOid = get_partition_parent(relOid, true);
1831 618 : if (OidIsValid(state->partParentOid))
1832 618 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1833 : }
1834 : }
1835 :
1836 : /*
1837 : * ExecuteTruncate
1838 : * Executes a TRUNCATE command.
1839 : *
1840 : * This is a multi-relation truncate. We first open and grab exclusive
1841 : * lock on all relations involved, checking permissions and otherwise
1842 : * verifying that the relation is OK for truncation. Note that if relations
1843 : * are foreign tables, at this stage, we have not yet checked that their
1844 : * foreign data in external data sources are OK for truncation. These are
1845 : * checked when foreign data are actually truncated later. In CASCADE mode,
1846 : * relations having FK references to the targeted relations are automatically
1847 : * added to the group; in RESTRICT mode, we check that all FK references are
1848 : * internal to the group that's being truncated. Finally all the relations
1849 : * are truncated and reindexed.
1850 : */
1851 : void
1852 1744 : ExecuteTruncate(TruncateStmt *stmt)
1853 : {
1854 1744 : List *rels = NIL;
1855 1744 : List *relids = NIL;
1856 1744 : List *relids_logged = NIL;
1857 : ListCell *cell;
1858 :
1859 : /*
1860 : * Open, exclusive-lock, and check all the explicitly-specified relations
1861 : */
1862 3700 : foreach(cell, stmt->relations)
1863 : {
1864 2012 : RangeVar *rv = lfirst(cell);
1865 : Relation rel;
1866 2012 : bool recurse = rv->inh;
1867 : Oid myrelid;
1868 2012 : LOCKMODE lockmode = AccessExclusiveLock;
1869 :
1870 2012 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1871 : 0, RangeVarCallbackForTruncate,
1872 : NULL);
1873 :
1874 : /* don't throw error for "TRUNCATE foo, foo" */
1875 1974 : if (list_member_oid(relids, myrelid))
1876 2 : continue;
1877 :
1878 : /* open the relation, we already hold a lock on it */
1879 1972 : rel = table_open(myrelid, NoLock);
1880 :
1881 : /*
1882 : * RangeVarGetRelidExtended() has done most checks with its callback,
1883 : * but other checks with the now-opened Relation remain.
1884 : */
1885 1972 : truncate_check_activity(rel);
1886 :
1887 1966 : rels = lappend(rels, rel);
1888 1966 : relids = lappend_oid(relids, myrelid);
1889 :
1890 : /* Log this relation only if needed for logical decoding */
1891 1966 : if (RelationIsLogicallyLogged(rel))
1892 74 : relids_logged = lappend_oid(relids_logged, myrelid);
1893 :
1894 1966 : if (recurse)
1895 : {
1896 : ListCell *child;
1897 : List *children;
1898 :
1899 1912 : children = find_all_inheritors(myrelid, lockmode, NULL);
1900 :
1901 5628 : foreach(child, children)
1902 : {
1903 3716 : Oid childrelid = lfirst_oid(child);
1904 :
1905 3716 : if (list_member_oid(relids, childrelid))
1906 1912 : continue;
1907 :
1908 : /* find_all_inheritors already got lock */
1909 1804 : rel = table_open(childrelid, NoLock);
1910 :
1911 : /*
1912 : * It is possible that the parent table has children that are
1913 : * temp tables of other backends. We cannot safely access
1914 : * such tables (because of buffering issues), and the best
1915 : * thing to do is to silently ignore them. Note that this
1916 : * check is the same as one of the checks done in
1917 : * truncate_check_activity() called below, still it is kept
1918 : * here for simplicity.
1919 : */
1920 1804 : if (RELATION_IS_OTHER_TEMP(rel))
1921 : {
1922 8 : table_close(rel, lockmode);
1923 8 : continue;
1924 : }
1925 :
1926 : /*
1927 : * Inherited TRUNCATE commands perform access permission
1928 : * checks on the parent table only. So we skip checking the
1929 : * children's permissions and don't call
1930 : * truncate_check_perms() here.
1931 : */
1932 1796 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1933 1796 : truncate_check_activity(rel);
1934 :
1935 1796 : rels = lappend(rels, rel);
1936 1796 : relids = lappend_oid(relids, childrelid);
1937 :
1938 : /* Log this relation only if needed for logical decoding */
1939 1796 : if (RelationIsLogicallyLogged(rel))
1940 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1941 : }
1942 : }
1943 54 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1944 12 : ereport(ERROR,
1945 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1946 : errmsg("cannot truncate only a partitioned table"),
1947 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1948 : }
1949 :
1950 1688 : ExecuteTruncateGuts(rels, relids, relids_logged,
1951 1688 : stmt->behavior, stmt->restart_seqs, false);
1952 :
1953 : /* And close the rels */
1954 5202 : foreach(cell, rels)
1955 : {
1956 3596 : Relation rel = (Relation) lfirst(cell);
1957 :
1958 3596 : table_close(rel, NoLock);
1959 : }
1960 1606 : }
1961 :
1962 : /*
1963 : * ExecuteTruncateGuts
1964 : *
1965 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1966 : * command (see above) as well as replication subscribers that execute a
1967 : * replicated TRUNCATE action.
1968 : *
1969 : * explicit_rels is the list of Relations to truncate that the command
1970 : * specified. relids is the list of Oids corresponding to explicit_rels.
1971 : * relids_logged is the list of Oids (a subset of relids) that require
1972 : * WAL-logging. This is all a bit redundant, but the existing callers have
1973 : * this information handy in this form.
1974 : */
1975 : void
1976 1714 : ExecuteTruncateGuts(List *explicit_rels,
1977 : List *relids,
1978 : List *relids_logged,
1979 : DropBehavior behavior, bool restart_seqs,
1980 : bool run_as_table_owner)
1981 : {
1982 : List *rels;
1983 1714 : List *seq_relids = NIL;
1984 1714 : HTAB *ft_htab = NULL;
1985 : EState *estate;
1986 : ResultRelInfo *resultRelInfos;
1987 : ResultRelInfo *resultRelInfo;
1988 : SubTransactionId mySubid;
1989 : ListCell *cell;
1990 : Oid *logrelids;
1991 :
1992 : /*
1993 : * Check the explicitly-specified relations.
1994 : *
1995 : * In CASCADE mode, suck in all referencing relations as well. This
1996 : * requires multiple iterations to find indirectly-dependent relations. At
1997 : * each phase, we need to exclusive-lock new rels before looking for their
1998 : * dependencies, else we might miss something. Also, we check each rel as
1999 : * soon as we open it, to avoid a faux pas such as holding lock for a long
2000 : * time on a rel we have no permissions for.
2001 : */
2002 1714 : rels = list_copy(explicit_rels);
2003 1714 : if (behavior == DROP_CASCADE)
2004 : {
2005 : for (;;)
2006 40 : {
2007 : List *newrelids;
2008 :
2009 80 : newrelids = heap_truncate_find_FKs(relids);
2010 80 : if (newrelids == NIL)
2011 40 : break; /* nothing else to add */
2012 :
2013 134 : foreach(cell, newrelids)
2014 : {
2015 94 : Oid relid = lfirst_oid(cell);
2016 : Relation rel;
2017 :
2018 94 : rel = table_open(relid, AccessExclusiveLock);
2019 94 : ereport(NOTICE,
2020 : (errmsg("truncate cascades to table \"%s\"",
2021 : RelationGetRelationName(rel))));
2022 94 : truncate_check_rel(relid, rel->rd_rel);
2023 94 : truncate_check_perms(relid, rel->rd_rel);
2024 94 : truncate_check_activity(rel);
2025 94 : rels = lappend(rels, rel);
2026 94 : relids = lappend_oid(relids, relid);
2027 :
2028 : /* Log this relation only if needed for logical decoding */
2029 94 : if (RelationIsLogicallyLogged(rel))
2030 0 : relids_logged = lappend_oid(relids_logged, relid);
2031 : }
2032 : }
2033 : }
2034 :
2035 : /*
2036 : * Check foreign key references. In CASCADE mode, this should be
2037 : * unnecessary since we just pulled in all the references; but as a
2038 : * cross-check, do it anyway if in an Assert-enabled build.
2039 : */
2040 : #ifdef USE_ASSERT_CHECKING
2041 : heap_truncate_check_FKs(rels, false);
2042 : #else
2043 1714 : if (behavior == DROP_RESTRICT)
2044 1674 : heap_truncate_check_FKs(rels, false);
2045 : #endif
2046 :
2047 : /*
2048 : * If we are asked to restart sequences, find all the sequences, lock them
2049 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2050 : * We want to do this early since it's pointless to do all the truncation
2051 : * work only to fail on sequence permissions.
2052 : */
2053 1640 : if (restart_seqs)
2054 : {
2055 44 : foreach(cell, rels)
2056 : {
2057 22 : Relation rel = (Relation) lfirst(cell);
2058 22 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2059 : ListCell *seqcell;
2060 :
2061 54 : foreach(seqcell, seqlist)
2062 : {
2063 32 : Oid seq_relid = lfirst_oid(seqcell);
2064 : Relation seq_rel;
2065 :
2066 32 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2067 :
2068 : /* This check must match AlterSequence! */
2069 32 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2070 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2071 0 : RelationGetRelationName(seq_rel));
2072 :
2073 32 : seq_relids = lappend_oid(seq_relids, seq_relid);
2074 :
2075 32 : relation_close(seq_rel, NoLock);
2076 : }
2077 : }
2078 : }
2079 :
2080 : /* Prepare to catch AFTER triggers. */
2081 1640 : AfterTriggerBeginQuery();
2082 :
2083 : /*
2084 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2085 : * each relation. We don't need to call ExecOpenIndices, though.
2086 : *
2087 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2088 : * though we don't have a range table and don't populate the
2089 : * es_result_relations array. That's a bit bogus, but it's enough to make
2090 : * ExecGetTriggerResultRel() find them.
2091 : */
2092 1640 : estate = CreateExecutorState();
2093 : resultRelInfos = (ResultRelInfo *)
2094 1640 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2095 1640 : resultRelInfo = resultRelInfos;
2096 5394 : foreach(cell, rels)
2097 : {
2098 3754 : Relation rel = (Relation) lfirst(cell);
2099 :
2100 3754 : InitResultRelInfo(resultRelInfo,
2101 : rel,
2102 : 0, /* dummy rangetable index */
2103 : NULL,
2104 : 0);
2105 3754 : estate->es_opened_result_relations =
2106 3754 : lappend(estate->es_opened_result_relations, resultRelInfo);
2107 3754 : resultRelInfo++;
2108 : }
2109 :
2110 : /*
2111 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2112 : * truncating (this is because one of them might throw an error). Also, if
2113 : * we were to allow them to prevent statement execution, that would need
2114 : * to be handled here.
2115 : */
2116 1640 : resultRelInfo = resultRelInfos;
2117 5394 : foreach(cell, rels)
2118 : {
2119 : UserContext ucxt;
2120 :
2121 3754 : if (run_as_table_owner)
2122 56 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2123 : &ucxt);
2124 3754 : ExecBSTruncateTriggers(estate, resultRelInfo);
2125 3754 : if (run_as_table_owner)
2126 56 : RestoreUserContext(&ucxt);
2127 3754 : resultRelInfo++;
2128 : }
2129 :
2130 : /*
2131 : * OK, truncate each table.
2132 : */
2133 1640 : mySubid = GetCurrentSubTransactionId();
2134 :
2135 5394 : foreach(cell, rels)
2136 : {
2137 3754 : Relation rel = (Relation) lfirst(cell);
2138 :
2139 : /* Skip partitioned tables as there is nothing to do */
2140 3754 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2141 704 : continue;
2142 :
2143 : /*
2144 : * Build the lists of foreign tables belonging to each foreign server
2145 : * and pass each list to the foreign data wrapper's callback function,
2146 : * so that each server can truncate its all foreign tables in bulk.
2147 : * Each list is saved as a single entry in a hash table that uses the
2148 : * server OID as lookup key.
2149 : */
2150 3050 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2151 34 : {
2152 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2153 : bool found;
2154 : ForeignTruncateInfo *ft_info;
2155 :
2156 : /* First time through, initialize hashtable for foreign tables */
2157 34 : if (!ft_htab)
2158 : {
2159 : HASHCTL hctl;
2160 :
2161 30 : memset(&hctl, 0, sizeof(HASHCTL));
2162 30 : hctl.keysize = sizeof(Oid);
2163 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2164 30 : hctl.hcxt = CurrentMemoryContext;
2165 :
2166 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2167 : 32, /* start small and extend */
2168 : &hctl,
2169 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2170 : }
2171 :
2172 : /* Find or create cached entry for the foreign table */
2173 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2174 34 : if (!found)
2175 30 : ft_info->rels = NIL;
2176 :
2177 : /*
2178 : * Save the foreign table in the entry of the server that the
2179 : * foreign table belongs to.
2180 : */
2181 34 : ft_info->rels = lappend(ft_info->rels, rel);
2182 34 : continue;
2183 : }
2184 :
2185 : /*
2186 : * Normally, we need a transaction-safe truncation here. However, if
2187 : * the table was either created in the current (sub)transaction or has
2188 : * a new relfilenumber in the current (sub)transaction, then we can
2189 : * just truncate it in-place, because a rollback would cause the whole
2190 : * table or the current physical file to be thrown away anyway.
2191 : */
2192 3016 : if (rel->rd_createSubid == mySubid ||
2193 2990 : rel->rd_newRelfilelocatorSubid == mySubid)
2194 : {
2195 : /* Immediate, non-rollbackable truncation is OK */
2196 90 : heap_truncate_one_rel(rel);
2197 : }
2198 : else
2199 : {
2200 : Oid heap_relid;
2201 : Oid toast_relid;
2202 2926 : ReindexParams reindex_params = {0};
2203 :
2204 : /*
2205 : * This effectively deletes all rows in the table, and may be done
2206 : * in a serializable transaction. In that case we must record a
2207 : * rw-conflict in to this transaction from each transaction
2208 : * holding a predicate lock on the table.
2209 : */
2210 2926 : CheckTableForSerializableConflictIn(rel);
2211 :
2212 : /*
2213 : * Need the full transaction-safe pushups.
2214 : *
2215 : * Create a new empty storage file for the relation, and assign it
2216 : * as the relfilenumber value. The old storage file is scheduled
2217 : * for deletion at commit.
2218 : */
2219 2926 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2220 :
2221 2926 : heap_relid = RelationGetRelid(rel);
2222 :
2223 : /*
2224 : * The same for the toast table, if any.
2225 : */
2226 2926 : toast_relid = rel->rd_rel->reltoastrelid;
2227 2926 : if (OidIsValid(toast_relid))
2228 : {
2229 1766 : Relation toastrel = relation_open(toast_relid,
2230 : AccessExclusiveLock);
2231 :
2232 1766 : RelationSetNewRelfilenumber(toastrel,
2233 1766 : toastrel->rd_rel->relpersistence);
2234 1766 : table_close(toastrel, NoLock);
2235 : }
2236 :
2237 : /*
2238 : * Reconstruct the indexes to match, and we're done.
2239 : */
2240 2926 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2241 : &reindex_params);
2242 : }
2243 :
2244 3016 : pgstat_count_truncate(rel);
2245 : }
2246 :
2247 : /* Now go through the hash table, and truncate foreign tables */
2248 1640 : if (ft_htab)
2249 : {
2250 : ForeignTruncateInfo *ft_info;
2251 : HASH_SEQ_STATUS seq;
2252 :
2253 30 : hash_seq_init(&seq, ft_htab);
2254 :
2255 30 : PG_TRY();
2256 : {
2257 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2258 : {
2259 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2260 :
2261 : /* truncate_check_rel() has checked that already */
2262 : Assert(routine->ExecForeignTruncate != NULL);
2263 :
2264 30 : routine->ExecForeignTruncate(ft_info->rels,
2265 : behavior,
2266 : restart_seqs);
2267 : }
2268 : }
2269 8 : PG_FINALLY();
2270 : {
2271 30 : hash_destroy(ft_htab);
2272 : }
2273 30 : PG_END_TRY();
2274 : }
2275 :
2276 : /*
2277 : * Restart owned sequences if we were asked to.
2278 : */
2279 1664 : foreach(cell, seq_relids)
2280 : {
2281 32 : Oid seq_relid = lfirst_oid(cell);
2282 :
2283 32 : ResetSequence(seq_relid);
2284 : }
2285 :
2286 : /*
2287 : * Write a WAL record to allow this set of actions to be logically
2288 : * decoded.
2289 : *
2290 : * Assemble an array of relids so we can write a single WAL record for the
2291 : * whole action.
2292 : */
2293 1632 : if (relids_logged != NIL)
2294 : {
2295 : xl_heap_truncate xlrec;
2296 62 : int i = 0;
2297 :
2298 : /* should only get here if wal_level >= logical */
2299 : Assert(XLogLogicalInfoActive());
2300 :
2301 62 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2302 160 : foreach(cell, relids_logged)
2303 98 : logrelids[i++] = lfirst_oid(cell);
2304 :
2305 62 : xlrec.dbId = MyDatabaseId;
2306 62 : xlrec.nrelids = list_length(relids_logged);
2307 62 : xlrec.flags = 0;
2308 62 : if (behavior == DROP_CASCADE)
2309 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2310 62 : if (restart_seqs)
2311 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2312 :
2313 62 : XLogBeginInsert();
2314 62 : XLogRegisterData(&xlrec, SizeOfHeapTruncate);
2315 62 : XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2316 :
2317 62 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2318 :
2319 62 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2320 : }
2321 :
2322 : /*
2323 : * Process all AFTER STATEMENT TRUNCATE triggers.
2324 : */
2325 1632 : resultRelInfo = resultRelInfos;
2326 5378 : foreach(cell, rels)
2327 : {
2328 : UserContext ucxt;
2329 :
2330 3746 : if (run_as_table_owner)
2331 56 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2332 : &ucxt);
2333 3746 : ExecASTruncateTriggers(estate, resultRelInfo);
2334 3746 : if (run_as_table_owner)
2335 56 : RestoreUserContext(&ucxt);
2336 3746 : resultRelInfo++;
2337 : }
2338 :
2339 : /* Handle queued AFTER triggers */
2340 1632 : AfterTriggerEndQuery(estate);
2341 :
2342 : /* We can clean up the EState now */
2343 1632 : FreeExecutorState(estate);
2344 :
2345 : /*
2346 : * Close any rels opened by CASCADE (can't do this while EState still
2347 : * holds refs)
2348 : */
2349 1632 : rels = list_difference_ptr(rels, explicit_rels);
2350 1726 : foreach(cell, rels)
2351 : {
2352 94 : Relation rel = (Relation) lfirst(cell);
2353 :
2354 94 : table_close(rel, NoLock);
2355 : }
2356 1632 : }
2357 :
2358 : /*
2359 : * Check that a given relation is safe to truncate. Subroutine for
2360 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2361 : */
2362 : static void
2363 4012 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2364 : {
2365 4012 : char *relname = NameStr(reltuple->relname);
2366 :
2367 : /*
2368 : * Only allow truncate on regular tables, foreign tables using foreign
2369 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2370 : * latter are only being included here for the following checks; no
2371 : * physical truncation will occur in their case.).
2372 : */
2373 4012 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2374 : {
2375 38 : Oid serverid = GetForeignServerIdByRelId(relid);
2376 38 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2377 :
2378 36 : if (!fdwroutine->ExecForeignTruncate)
2379 2 : ereport(ERROR,
2380 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2381 : errmsg("cannot truncate foreign table \"%s\"",
2382 : relname)));
2383 : }
2384 3974 : else if (reltuple->relkind != RELKIND_RELATION &&
2385 720 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2386 0 : ereport(ERROR,
2387 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2388 : errmsg("\"%s\" is not a table", relname)));
2389 :
2390 : /*
2391 : * Most system catalogs can't be truncated at all, or at least not unless
2392 : * allow_system_table_mods=on. As an exception, however, we allow
2393 : * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2394 : * pg_upgrade, because we need to change its relfilenode to match the old
2395 : * cluster, and allowing a TRUNCATE command to be executed is the easiest
2396 : * way of doing that.
2397 : */
2398 4008 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2399 114 : && (!IsBinaryUpgrade ||
2400 56 : (relid != LargeObjectRelationId &&
2401 : relid != LargeObjectMetadataRelationId)))
2402 2 : ereport(ERROR,
2403 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2404 : errmsg("permission denied: \"%s\" is a system catalog",
2405 : relname)));
2406 :
2407 4006 : InvokeObjectTruncateHook(relid);
2408 4006 : }
2409 :
2410 : /*
2411 : * Check that current user has the permission to truncate given relation.
2412 : */
2413 : static void
2414 2210 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2415 : {
2416 2210 : char *relname = NameStr(reltuple->relname);
2417 : AclResult aclresult;
2418 :
2419 : /* Permissions checks */
2420 2210 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2421 2210 : if (aclresult != ACLCHECK_OK)
2422 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2423 : relname);
2424 2178 : }
2425 :
2426 : /*
2427 : * Set of extra sanity checks to check if a given relation is safe to
2428 : * truncate. This is split with truncate_check_rel() as
2429 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2430 : */
2431 : static void
2432 3862 : truncate_check_activity(Relation rel)
2433 : {
2434 : /*
2435 : * Don't allow truncate on temp tables of other backends ... their local
2436 : * buffer manager is not going to cope.
2437 : */
2438 3862 : if (RELATION_IS_OTHER_TEMP(rel))
2439 0 : ereport(ERROR,
2440 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2441 : errmsg("cannot truncate temporary tables of other sessions")));
2442 :
2443 : /*
2444 : * Also check for active uses of the relation in the current transaction,
2445 : * including open scans and pending AFTER trigger events.
2446 : */
2447 3862 : CheckTableNotInUse(rel, "TRUNCATE");
2448 3856 : }
2449 :
2450 : /*
2451 : * storage_name
2452 : * returns the name corresponding to a typstorage/attstorage enum value
2453 : */
2454 : static const char *
2455 24 : storage_name(char c)
2456 : {
2457 24 : switch (c)
2458 : {
2459 0 : case TYPSTORAGE_PLAIN:
2460 0 : return "PLAIN";
2461 0 : case TYPSTORAGE_EXTERNAL:
2462 0 : return "EXTERNAL";
2463 12 : case TYPSTORAGE_EXTENDED:
2464 12 : return "EXTENDED";
2465 12 : case TYPSTORAGE_MAIN:
2466 12 : return "MAIN";
2467 0 : default:
2468 0 : return "???";
2469 : }
2470 : }
2471 :
2472 : /*----------
2473 : * MergeAttributes
2474 : * Returns new schema given initial schema and superclasses.
2475 : *
2476 : * Input arguments:
2477 : * 'columns' is the column/attribute definition for the table. (It's a list
2478 : * of ColumnDef's.) It is destructively changed.
2479 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2480 : * 'relpersistence' is the persistence type of the table.
2481 : * 'is_partition' tells if the table is a partition.
2482 : *
2483 : * Output arguments:
2484 : * 'supconstr' receives a list of CookedConstraint representing
2485 : * CHECK constraints belonging to parent relations, updated as
2486 : * necessary to be valid for the child.
2487 : * 'supnotnulls' receives a list of CookedConstraint representing
2488 : * not-null constraints based on those from parent relations.
2489 : *
2490 : * Return value:
2491 : * Completed schema list.
2492 : *
2493 : * Notes:
2494 : * The order in which the attributes are inherited is very important.
2495 : * Intuitively, the inherited attributes should come first. If a table
2496 : * inherits from multiple parents, the order of those attributes are
2497 : * according to the order of the parents specified in CREATE TABLE.
2498 : *
2499 : * Here's an example:
2500 : *
2501 : * create table person (name text, age int4, location point);
2502 : * create table emp (salary int4, manager text) inherits(person);
2503 : * create table student (gpa float8) inherits (person);
2504 : * create table stud_emp (percent int4) inherits (emp, student);
2505 : *
2506 : * The order of the attributes of stud_emp is:
2507 : *
2508 : * person {1:name, 2:age, 3:location}
2509 : * / \
2510 : * {6:gpa} student emp {4:salary, 5:manager}
2511 : * \ /
2512 : * stud_emp {7:percent}
2513 : *
2514 : * If the same attribute name appears multiple times, then it appears
2515 : * in the result table in the proper location for its first appearance.
2516 : *
2517 : * Constraints (including not-null constraints) for the child table
2518 : * are the union of all relevant constraints, from both the child schema
2519 : * and parent tables. In addition, in legacy inheritance, each column that
2520 : * appears in a primary key in any of the parents also gets a NOT NULL
2521 : * constraint (partitioning doesn't need this, because the PK itself gets
2522 : * inherited.)
2523 : *
2524 : * The default value for a child column is defined as:
2525 : * (1) If the child schema specifies a default, that value is used.
2526 : * (2) If neither the child nor any parent specifies a default, then
2527 : * the column will not have a default.
2528 : * (3) If conflicting defaults are inherited from different parents
2529 : * (and not overridden by the child), an error is raised.
2530 : * (4) Otherwise the inherited default is used.
2531 : *
2532 : * Note that the default-value infrastructure is used for generated
2533 : * columns' expressions too, so most of the preceding paragraph applies
2534 : * to generation expressions too. We insist that a child column be
2535 : * generated if and only if its parent(s) are, but it need not have
2536 : * the same generation expression.
2537 : *----------
2538 : */
2539 : static List *
2540 62456 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2541 : bool is_partition, List **supconstr, List **supnotnulls)
2542 : {
2543 62456 : List *inh_columns = NIL;
2544 62456 : List *constraints = NIL;
2545 62456 : List *nnconstraints = NIL;
2546 62456 : bool have_bogus_defaults = false;
2547 : int child_attno;
2548 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2549 62456 : List *saved_columns = NIL;
2550 : ListCell *lc;
2551 :
2552 : /*
2553 : * Check for and reject tables with too many columns. We perform this
2554 : * check relatively early for two reasons: (a) we don't run the risk of
2555 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2556 : * okay if we're processing <= 1600 columns, but could take minutes to
2557 : * execute if the user attempts to create a table with hundreds of
2558 : * thousands of columns.
2559 : *
2560 : * Note that we also need to check that we do not exceed this figure after
2561 : * including columns from inherited relations.
2562 : */
2563 62456 : if (list_length(columns) > MaxHeapAttributeNumber)
2564 0 : ereport(ERROR,
2565 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2566 : errmsg("tables can have at most %d columns",
2567 : MaxHeapAttributeNumber)));
2568 :
2569 : /*
2570 : * Check for duplicate names in the explicit list of attributes.
2571 : *
2572 : * Although we might consider merging such entries in the same way that we
2573 : * handle name conflicts for inherited attributes, it seems to make more
2574 : * sense to assume such conflicts are errors.
2575 : *
2576 : * We don't use foreach() here because we have two nested loops over the
2577 : * columns list, with possible element deletions in the inner one. If we
2578 : * used foreach_delete_current() it could only fix up the state of one of
2579 : * the loops, so it seems cleaner to use looping over list indexes for
2580 : * both loops. Note that any deletion will happen beyond where the outer
2581 : * loop is, so its index never needs adjustment.
2582 : */
2583 296416 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2584 : {
2585 233984 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2586 :
2587 233984 : if (!is_partition && coldef->typeName == NULL)
2588 : {
2589 : /*
2590 : * Typed table column option that does not belong to a column from
2591 : * the type. This works because the columns from the type come
2592 : * first in the list. (We omit this check for partition column
2593 : * lists; those are processed separately below.)
2594 : */
2595 6 : ereport(ERROR,
2596 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2597 : errmsg("column \"%s\" does not exist",
2598 : coldef->colname)));
2599 : }
2600 :
2601 : /* restpos scans all entries beyond coldef; incr is in loop body */
2602 6564320 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2603 : {
2604 6330360 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2605 :
2606 6330360 : if (strcmp(coldef->colname, restdef->colname) == 0)
2607 : {
2608 50 : if (coldef->is_from_type)
2609 : {
2610 : /*
2611 : * merge the column options into the column from the type
2612 : */
2613 32 : coldef->is_not_null = restdef->is_not_null;
2614 32 : coldef->raw_default = restdef->raw_default;
2615 32 : coldef->cooked_default = restdef->cooked_default;
2616 32 : coldef->constraints = restdef->constraints;
2617 32 : coldef->is_from_type = false;
2618 32 : columns = list_delete_nth_cell(columns, restpos);
2619 : }
2620 : else
2621 18 : ereport(ERROR,
2622 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2623 : errmsg("column \"%s\" specified more than once",
2624 : coldef->colname)));
2625 : }
2626 : else
2627 6330310 : restpos++;
2628 : }
2629 : }
2630 :
2631 : /*
2632 : * In case of a partition, there are no new column definitions, only dummy
2633 : * ColumnDefs created for column constraints. Set them aside for now and
2634 : * process them at the end.
2635 : */
2636 62432 : if (is_partition)
2637 : {
2638 8018 : saved_columns = columns;
2639 8018 : columns = NIL;
2640 : }
2641 :
2642 : /*
2643 : * Scan the parents left-to-right, and merge their attributes to form a
2644 : * list of inherited columns (inh_columns).
2645 : */
2646 62432 : child_attno = 0;
2647 72838 : foreach(lc, supers)
2648 : {
2649 10490 : Oid parent = lfirst_oid(lc);
2650 : Relation relation;
2651 : TupleDesc tupleDesc;
2652 : TupleConstr *constr;
2653 : AttrMap *newattmap;
2654 : List *inherited_defaults;
2655 : List *cols_with_defaults;
2656 : List *nnconstrs;
2657 : ListCell *lc1;
2658 : ListCell *lc2;
2659 10490 : Bitmapset *nncols = NULL;
2660 :
2661 : /* caller already got lock */
2662 10490 : relation = table_open(parent, NoLock);
2663 :
2664 : /*
2665 : * Check for active uses of the parent partitioned table in the
2666 : * current transaction, such as being used in some manner by an
2667 : * enclosing command.
2668 : */
2669 10490 : if (is_partition)
2670 8018 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2671 :
2672 : /*
2673 : * We do not allow partitioned tables and partitions to participate in
2674 : * regular inheritance.
2675 : */
2676 10484 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2677 6 : ereport(ERROR,
2678 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2679 : errmsg("cannot inherit from partitioned table \"%s\"",
2680 : RelationGetRelationName(relation))));
2681 10478 : if (relation->rd_rel->relispartition && !is_partition)
2682 6 : ereport(ERROR,
2683 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2684 : errmsg("cannot inherit from partition \"%s\"",
2685 : RelationGetRelationName(relation))));
2686 :
2687 10472 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2688 8014 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2689 7994 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2690 0 : ereport(ERROR,
2691 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2692 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2693 : RelationGetRelationName(relation))));
2694 :
2695 : /*
2696 : * If the parent is permanent, so must be all of its partitions. Note
2697 : * that inheritance allows that case.
2698 : */
2699 10472 : if (is_partition &&
2700 8012 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2701 : relpersistence == RELPERSISTENCE_TEMP)
2702 6 : ereport(ERROR,
2703 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2704 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2705 : RelationGetRelationName(relation))));
2706 :
2707 : /* Permanent rels cannot inherit from temporary ones */
2708 10466 : if (relpersistence != RELPERSISTENCE_TEMP &&
2709 10100 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2710 24 : ereport(ERROR,
2711 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2712 : errmsg(!is_partition
2713 : ? "cannot inherit from temporary relation \"%s\""
2714 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2715 : RelationGetRelationName(relation))));
2716 :
2717 : /* If existing rel is temp, it must belong to this session */
2718 10442 : if (RELATION_IS_OTHER_TEMP(relation))
2719 0 : ereport(ERROR,
2720 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2721 : errmsg(!is_partition
2722 : ? "cannot inherit from temporary relation of another session"
2723 : : "cannot create as partition of temporary relation of another session")));
2724 :
2725 : /*
2726 : * We should have an UNDER permission flag for this, but for now,
2727 : * demand that creator of a child table own the parent.
2728 : */
2729 10442 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2730 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2731 0 : RelationGetRelationName(relation));
2732 :
2733 10442 : tupleDesc = RelationGetDescr(relation);
2734 10442 : constr = tupleDesc->constr;
2735 :
2736 : /*
2737 : * newattmap->attnums[] will contain the child-table attribute numbers
2738 : * for the attributes of this parent table. (They are not the same
2739 : * for parents after the first one, nor if we have dropped columns.)
2740 : */
2741 10442 : newattmap = make_attrmap(tupleDesc->natts);
2742 :
2743 : /* We can't process inherited defaults until newattmap is complete. */
2744 10442 : inherited_defaults = cols_with_defaults = NIL;
2745 :
2746 : /*
2747 : * Request attnotnull on columns that have a not-null constraint
2748 : * that's not marked NO INHERIT (even if not valid).
2749 : */
2750 10442 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2751 : true, false);
2752 23266 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2753 2382 : nncols = bms_add_member(nncols, cc->attnum);
2754 :
2755 31370 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2756 20928 : parent_attno++)
2757 : {
2758 20964 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2759 : parent_attno - 1);
2760 20964 : char *attributeName = NameStr(attribute->attname);
2761 : int exist_attno;
2762 : ColumnDef *newdef;
2763 : ColumnDef *mergeddef;
2764 :
2765 : /*
2766 : * Ignore dropped columns in the parent.
2767 : */
2768 20964 : if (attribute->attisdropped)
2769 192 : continue; /* leave newattmap->attnums entry as zero */
2770 :
2771 : /*
2772 : * Create new column definition
2773 : */
2774 20772 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2775 : attribute->atttypmod, attribute->attcollation);
2776 20772 : newdef->storage = attribute->attstorage;
2777 20772 : newdef->generated = attribute->attgenerated;
2778 20772 : if (CompressionMethodIsValid(attribute->attcompression))
2779 36 : newdef->compression =
2780 36 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2781 :
2782 : /*
2783 : * Regular inheritance children are independent enough not to
2784 : * inherit identity columns. But partitions are integral part of
2785 : * a partitioned table and inherit identity column.
2786 : */
2787 20772 : if (is_partition)
2788 16276 : newdef->identity = attribute->attidentity;
2789 :
2790 : /*
2791 : * Does it match some previously considered column from another
2792 : * parent?
2793 : */
2794 20772 : exist_attno = findAttrByName(attributeName, inh_columns);
2795 20772 : if (exist_attno > 0)
2796 : {
2797 : /*
2798 : * Yes, try to merge the two column definitions.
2799 : */
2800 370 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2801 :
2802 334 : newattmap->attnums[parent_attno - 1] = exist_attno;
2803 :
2804 : /*
2805 : * Partitions have only one parent, so conflict should never
2806 : * occur.
2807 : */
2808 : Assert(!is_partition);
2809 : }
2810 : else
2811 : {
2812 : /*
2813 : * No, create a new inherited column
2814 : */
2815 20402 : newdef->inhcount = 1;
2816 20402 : newdef->is_local = false;
2817 20402 : inh_columns = lappend(inh_columns, newdef);
2818 :
2819 20402 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2820 20402 : mergeddef = newdef;
2821 : }
2822 :
2823 : /*
2824 : * mark attnotnull if parent has it
2825 : */
2826 20736 : if (bms_is_member(parent_attno, nncols))
2827 2382 : mergeddef->is_not_null = true;
2828 :
2829 : /*
2830 : * Locate default/generation expression if any
2831 : */
2832 20736 : if (attribute->atthasdef)
2833 : {
2834 : Node *this_default;
2835 :
2836 692 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2837 692 : if (this_default == NULL)
2838 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2839 : parent_attno, RelationGetRelationName(relation));
2840 :
2841 : /*
2842 : * If it's a GENERATED default, it might contain Vars that
2843 : * need to be mapped to the inherited column(s)' new numbers.
2844 : * We can't do that till newattmap is ready, so just remember
2845 : * all the inherited default expressions for the moment.
2846 : */
2847 692 : inherited_defaults = lappend(inherited_defaults, this_default);
2848 692 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2849 : }
2850 : }
2851 :
2852 : /*
2853 : * Now process any inherited default expressions, adjusting attnos
2854 : * using the completed newattmap map.
2855 : */
2856 11098 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2857 : {
2858 692 : Node *this_default = (Node *) lfirst(lc1);
2859 692 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2860 : bool found_whole_row;
2861 :
2862 : /* Adjust Vars to match new table's column numbering */
2863 692 : this_default = map_variable_attnos(this_default,
2864 : 1, 0,
2865 : newattmap,
2866 : InvalidOid, &found_whole_row);
2867 :
2868 : /*
2869 : * For the moment we have to reject whole-row variables. We could
2870 : * convert them, if we knew the new table's rowtype OID, but that
2871 : * hasn't been assigned yet. (A variable could only appear in a
2872 : * generation expression, so the error message is correct.)
2873 : */
2874 692 : if (found_whole_row)
2875 0 : ereport(ERROR,
2876 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2877 : errmsg("cannot convert whole-row table reference"),
2878 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2879 : def->colname,
2880 : RelationGetRelationName(relation))));
2881 :
2882 : /*
2883 : * If we already had a default from some prior parent, check to
2884 : * see if they are the same. If so, no problem; if not, mark the
2885 : * column as having a bogus default. Below, we will complain if
2886 : * the bogus default isn't overridden by the child columns.
2887 : */
2888 : Assert(def->raw_default == NULL);
2889 692 : if (def->cooked_default == NULL)
2890 650 : def->cooked_default = this_default;
2891 42 : else if (!equal(def->cooked_default, this_default))
2892 : {
2893 36 : def->cooked_default = &bogus_marker;
2894 36 : have_bogus_defaults = true;
2895 : }
2896 : }
2897 :
2898 : /*
2899 : * Now copy the CHECK constraints of this parent, adjusting attnos
2900 : * using the completed newattmap map. Identically named constraints
2901 : * are merged if possible, else we throw error.
2902 : */
2903 10406 : if (constr && constr->num_check > 0)
2904 : {
2905 334 : ConstrCheck *check = constr->check;
2906 :
2907 1070 : for (int i = 0; i < constr->num_check; i++)
2908 : {
2909 736 : char *name = check[i].ccname;
2910 : Node *expr;
2911 : bool found_whole_row;
2912 :
2913 : /* ignore if the constraint is non-inheritable */
2914 736 : if (check[i].ccnoinherit)
2915 48 : continue;
2916 :
2917 : /* Adjust Vars to match new table's column numbering */
2918 688 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2919 : 1, 0,
2920 : newattmap,
2921 : InvalidOid, &found_whole_row);
2922 :
2923 : /*
2924 : * For the moment we have to reject whole-row variables. We
2925 : * could convert them, if we knew the new table's rowtype OID,
2926 : * but that hasn't been assigned yet.
2927 : */
2928 688 : if (found_whole_row)
2929 0 : ereport(ERROR,
2930 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2931 : errmsg("cannot convert whole-row table reference"),
2932 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2933 : name,
2934 : RelationGetRelationName(relation))));
2935 :
2936 688 : constraints = MergeCheckConstraint(constraints, name, expr,
2937 688 : check[i].ccenforced);
2938 : }
2939 : }
2940 :
2941 : /*
2942 : * Also copy the not-null constraints from this parent. The
2943 : * attnotnull markings were already installed above.
2944 : */
2945 23194 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2946 : {
2947 : Assert(nn->contype == CONSTR_NOTNULL);
2948 :
2949 2382 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2950 :
2951 2382 : nnconstraints = lappend(nnconstraints, nn);
2952 : }
2953 :
2954 10406 : free_attrmap(newattmap);
2955 :
2956 : /*
2957 : * Close the parent rel, but keep our lock on it until xact commit.
2958 : * That will prevent someone else from deleting or ALTERing the parent
2959 : * before the child is committed.
2960 : */
2961 10406 : table_close(relation, NoLock);
2962 : }
2963 :
2964 : /*
2965 : * If we had no inherited attributes, the result columns are just the
2966 : * explicitly declared columns. Otherwise, we need to merge the declared
2967 : * columns into the inherited column list. Although, we never have any
2968 : * explicitly declared columns if the table is a partition.
2969 : */
2970 62348 : if (inh_columns != NIL)
2971 : {
2972 9970 : int newcol_attno = 0;
2973 :
2974 10944 : foreach(lc, columns)
2975 : {
2976 1052 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2977 1052 : char *attributeName = newdef->colname;
2978 : int exist_attno;
2979 :
2980 : /*
2981 : * Partitions have only one parent and have no column definitions
2982 : * of their own, so conflict should never occur.
2983 : */
2984 : Assert(!is_partition);
2985 :
2986 1052 : newcol_attno++;
2987 :
2988 : /*
2989 : * Does it match some inherited column?
2990 : */
2991 1052 : exist_attno = findAttrByName(attributeName, inh_columns);
2992 1052 : if (exist_attno > 0)
2993 : {
2994 : /*
2995 : * Yes, try to merge the two column definitions.
2996 : */
2997 380 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2998 : }
2999 : else
3000 : {
3001 : /*
3002 : * No, attach new column unchanged to result columns.
3003 : */
3004 672 : inh_columns = lappend(inh_columns, newdef);
3005 : }
3006 : }
3007 :
3008 9892 : columns = inh_columns;
3009 :
3010 : /*
3011 : * Check that we haven't exceeded the legal # of columns after merging
3012 : * in inherited columns.
3013 : */
3014 9892 : if (list_length(columns) > MaxHeapAttributeNumber)
3015 0 : ereport(ERROR,
3016 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
3017 : errmsg("tables can have at most %d columns",
3018 : MaxHeapAttributeNumber)));
3019 : }
3020 :
3021 : /*
3022 : * Now that we have the column definition list for a partition, we can
3023 : * check whether the columns referenced in the column constraint specs
3024 : * actually exist. Also, merge column defaults.
3025 : */
3026 62270 : if (is_partition)
3027 : {
3028 8194 : foreach(lc, saved_columns)
3029 : {
3030 242 : ColumnDef *restdef = lfirst(lc);
3031 242 : bool found = false;
3032 : ListCell *l;
3033 :
3034 900 : foreach(l, columns)
3035 : {
3036 694 : ColumnDef *coldef = lfirst(l);
3037 :
3038 694 : if (strcmp(coldef->colname, restdef->colname) == 0)
3039 : {
3040 242 : found = true;
3041 :
3042 : /*
3043 : * Check for conflicts related to generated columns.
3044 : *
3045 : * Same rules as above: generated-ness has to match the
3046 : * parent, but the contents of the generation expression
3047 : * can be different.
3048 : */
3049 242 : if (coldef->generated)
3050 : {
3051 134 : if (restdef->raw_default && !restdef->generated)
3052 12 : ereport(ERROR,
3053 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3054 : errmsg("column \"%s\" inherits from generated column but specifies default",
3055 : restdef->colname)));
3056 122 : if (restdef->identity)
3057 0 : ereport(ERROR,
3058 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3059 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3060 : restdef->colname)));
3061 : }
3062 : else
3063 : {
3064 108 : if (restdef->generated)
3065 12 : ereport(ERROR,
3066 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3067 : errmsg("child column \"%s\" specifies generation expression",
3068 : restdef->colname),
3069 : errhint("A child table column cannot be generated unless its parent column is.")));
3070 : }
3071 :
3072 218 : if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3073 12 : ereport(ERROR,
3074 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3075 : errmsg("column \"%s\" inherits from generated column of different kind",
3076 : restdef->colname),
3077 : errdetail("Parent column is %s, child column is %s.",
3078 : coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3079 : restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3080 :
3081 : /*
3082 : * Override the parent's default value for this column
3083 : * (coldef->cooked_default) with the partition's local
3084 : * definition (restdef->raw_default), if there's one. It
3085 : * should be physically impossible to get a cooked default
3086 : * in the local definition or a raw default in the
3087 : * inherited definition, but make sure they're nulls, for
3088 : * future-proofing.
3089 : */
3090 : Assert(restdef->cooked_default == NULL);
3091 : Assert(coldef->raw_default == NULL);
3092 206 : if (restdef->raw_default)
3093 : {
3094 134 : coldef->raw_default = restdef->raw_default;
3095 134 : coldef->cooked_default = NULL;
3096 : }
3097 : }
3098 : }
3099 :
3100 : /* complain for constraints on columns not in parent */
3101 206 : if (!found)
3102 0 : ereport(ERROR,
3103 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3104 : errmsg("column \"%s\" does not exist",
3105 : restdef->colname)));
3106 : }
3107 : }
3108 :
3109 : /*
3110 : * If we found any conflicting parent default values, check to make sure
3111 : * they were overridden by the child.
3112 : */
3113 62234 : if (have_bogus_defaults)
3114 : {
3115 90 : foreach(lc, columns)
3116 : {
3117 72 : ColumnDef *def = lfirst(lc);
3118 :
3119 72 : if (def->cooked_default == &bogus_marker)
3120 : {
3121 18 : if (def->generated)
3122 12 : ereport(ERROR,
3123 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3124 : errmsg("column \"%s\" inherits conflicting generation expressions",
3125 : def->colname),
3126 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3127 : else
3128 6 : ereport(ERROR,
3129 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3130 : errmsg("column \"%s\" inherits conflicting default values",
3131 : def->colname),
3132 : errhint("To resolve the conflict, specify a default explicitly.")));
3133 : }
3134 : }
3135 : }
3136 :
3137 62216 : *supconstr = constraints;
3138 62216 : *supnotnulls = nnconstraints;
3139 :
3140 62216 : return columns;
3141 : }
3142 :
3143 :
3144 : /*
3145 : * MergeCheckConstraint
3146 : * Try to merge an inherited CHECK constraint with previous ones
3147 : *
3148 : * If we inherit identically-named constraints from multiple parents, we must
3149 : * merge them, or throw an error if they don't have identical definitions.
3150 : *
3151 : * constraints is a list of CookedConstraint structs for previous constraints.
3152 : *
3153 : * If the new constraint matches an existing one, then the existing
3154 : * constraint's inheritance count is updated. If there is a conflict (same
3155 : * name but different expression), throw an error. If the constraint neither
3156 : * matches nor conflicts with an existing one, a new constraint is appended to
3157 : * the list.
3158 : */
3159 : static List *
3160 688 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3161 : {
3162 : ListCell *lc;
3163 : CookedConstraint *newcon;
3164 :
3165 2212 : foreach(lc, constraints)
3166 : {
3167 1674 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3168 :
3169 : Assert(ccon->contype == CONSTR_CHECK);
3170 :
3171 : /* Non-matching names never conflict */
3172 1674 : if (strcmp(ccon->name, name) != 0)
3173 1524 : continue;
3174 :
3175 150 : if (equal(expr, ccon->expr))
3176 : {
3177 : /* OK to merge constraint with existing */
3178 150 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3179 : &ccon->inhcount))
3180 0 : ereport(ERROR,
3181 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3182 : errmsg("too many inheritance parents"));
3183 :
3184 : /*
3185 : * When enforceability differs, the merged constraint should be
3186 : * marked as ENFORCED because one of the parents is ENFORCED.
3187 : */
3188 150 : if (!ccon->is_enforced && is_enforced)
3189 : {
3190 48 : ccon->is_enforced = true;
3191 48 : ccon->skip_validation = false;
3192 : }
3193 :
3194 150 : return constraints;
3195 : }
3196 :
3197 0 : ereport(ERROR,
3198 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3199 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3200 : name)));
3201 : }
3202 :
3203 : /*
3204 : * Constraint couldn't be merged with an existing one and also didn't
3205 : * conflict with an existing one, so add it as a new one to the list.
3206 : */
3207 538 : newcon = palloc0_object(CookedConstraint);
3208 538 : newcon->contype = CONSTR_CHECK;
3209 538 : newcon->name = pstrdup(name);
3210 538 : newcon->expr = expr;
3211 538 : newcon->inhcount = 1;
3212 538 : newcon->is_enforced = is_enforced;
3213 538 : newcon->skip_validation = !is_enforced;
3214 538 : return lappend(constraints, newcon);
3215 : }
3216 :
3217 : /*
3218 : * MergeChildAttribute
3219 : * Merge given child attribute definition into given inherited attribute.
3220 : *
3221 : * Input arguments:
3222 : * 'inh_columns' is the list of inherited ColumnDefs.
3223 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3224 : * 'newcol_attno' is the attribute number in child table's schema definition
3225 : * 'newdef' is the column/attribute definition from the child table.
3226 : *
3227 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3228 : * ColumnDef remains unchanged.
3229 : *
3230 : * Notes:
3231 : * - The attribute is merged according to the rules laid out in the prologue
3232 : * of MergeAttributes().
3233 : * - If matching inherited attribute exists but the child attribute can not be
3234 : * merged into it, the function throws respective errors.
3235 : * - A partition can not have its own column definitions. Hence this function
3236 : * is applicable only to a regular inheritance child.
3237 : */
3238 : static void
3239 380 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3240 : {
3241 380 : char *attributeName = newdef->colname;
3242 : ColumnDef *inhdef;
3243 : Oid inhtypeid,
3244 : newtypeid;
3245 : int32 inhtypmod,
3246 : newtypmod;
3247 : Oid inhcollid,
3248 : newcollid;
3249 :
3250 380 : if (exist_attno == newcol_attno)
3251 346 : ereport(NOTICE,
3252 : (errmsg("merging column \"%s\" with inherited definition",
3253 : attributeName)));
3254 : else
3255 34 : ereport(NOTICE,
3256 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3257 : errdetail("User-specified column moved to the position of the inherited column.")));
3258 :
3259 380 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3260 :
3261 : /*
3262 : * Must have the same type and typmod
3263 : */
3264 380 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3265 380 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3266 380 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3267 12 : ereport(ERROR,
3268 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3269 : errmsg("column \"%s\" has a type conflict",
3270 : attributeName),
3271 : errdetail("%s versus %s",
3272 : format_type_with_typemod(inhtypeid, inhtypmod),
3273 : format_type_with_typemod(newtypeid, newtypmod))));
3274 :
3275 : /*
3276 : * Must have the same collation
3277 : */
3278 368 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3279 368 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3280 368 : if (inhcollid != newcollid)
3281 6 : ereport(ERROR,
3282 : (errcode(ERRCODE_COLLATION_MISMATCH),
3283 : errmsg("column \"%s\" has a collation conflict",
3284 : attributeName),
3285 : errdetail("\"%s\" versus \"%s\"",
3286 : get_collation_name(inhcollid),
3287 : get_collation_name(newcollid))));
3288 :
3289 : /*
3290 : * Identity is never inherited by a regular inheritance child. Pick
3291 : * child's identity definition if there's one.
3292 : */
3293 362 : inhdef->identity = newdef->identity;
3294 :
3295 : /*
3296 : * Copy storage parameter
3297 : */
3298 362 : if (inhdef->storage == 0)
3299 0 : inhdef->storage = newdef->storage;
3300 362 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3301 6 : ereport(ERROR,
3302 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3303 : errmsg("column \"%s\" has a storage parameter conflict",
3304 : attributeName),
3305 : errdetail("%s versus %s",
3306 : storage_name(inhdef->storage),
3307 : storage_name(newdef->storage))));
3308 :
3309 : /*
3310 : * Copy compression parameter
3311 : */
3312 356 : if (inhdef->compression == NULL)
3313 350 : inhdef->compression = newdef->compression;
3314 6 : else if (newdef->compression != NULL)
3315 : {
3316 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3317 6 : ereport(ERROR,
3318 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3319 : errmsg("column \"%s\" has a compression method conflict",
3320 : attributeName),
3321 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3322 : }
3323 :
3324 : /*
3325 : * Merge of not-null constraints = OR 'em together
3326 : */
3327 350 : inhdef->is_not_null |= newdef->is_not_null;
3328 :
3329 : /*
3330 : * Check for conflicts related to generated columns.
3331 : *
3332 : * If the parent column is generated, the child column will be made a
3333 : * generated column if it isn't already. If it is a generated column,
3334 : * we'll take its generation expression in preference to the parent's. We
3335 : * must check that the child column doesn't specify a default value or
3336 : * identity, which matches the rules for a single column in
3337 : * parse_utilcmd.c.
3338 : *
3339 : * Conversely, if the parent column is not generated, the child column
3340 : * can't be either. (We used to allow that, but it results in being able
3341 : * to override the generation expression via UPDATEs through the parent.)
3342 : */
3343 350 : if (inhdef->generated)
3344 : {
3345 62 : if (newdef->raw_default && !newdef->generated)
3346 12 : ereport(ERROR,
3347 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3348 : errmsg("column \"%s\" inherits from generated column but specifies default",
3349 : inhdef->colname)));
3350 50 : if (newdef->identity)
3351 12 : ereport(ERROR,
3352 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3353 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3354 : inhdef->colname)));
3355 : }
3356 : else
3357 : {
3358 288 : if (newdef->generated)
3359 12 : ereport(ERROR,
3360 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3361 : errmsg("child column \"%s\" specifies generation expression",
3362 : inhdef->colname),
3363 : errhint("A child table column cannot be generated unless its parent column is.")));
3364 : }
3365 :
3366 314 : if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3367 12 : ereport(ERROR,
3368 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3369 : errmsg("column \"%s\" inherits from generated column of different kind",
3370 : inhdef->colname),
3371 : errdetail("Parent column is %s, child column is %s.",
3372 : inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3373 : newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3374 :
3375 : /*
3376 : * If new def has a default, override previous default
3377 : */
3378 302 : if (newdef->raw_default != NULL)
3379 : {
3380 30 : inhdef->raw_default = newdef->raw_default;
3381 30 : inhdef->cooked_default = newdef->cooked_default;
3382 : }
3383 :
3384 : /* Mark the column as locally defined */
3385 302 : inhdef->is_local = true;
3386 302 : }
3387 :
3388 : /*
3389 : * MergeInheritedAttribute
3390 : * Merge given parent attribute definition into specified attribute
3391 : * inherited from the previous parents.
3392 : *
3393 : * Input arguments:
3394 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3395 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3396 : * 'newdef' is the new parent column/attribute definition to be merged.
3397 : *
3398 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3399 : *
3400 : * Notes:
3401 : * - The attribute is merged according to the rules laid out in the prologue
3402 : * of MergeAttributes().
3403 : * - If matching inherited attribute exists but the new attribute can not be
3404 : * merged into it, the function throws respective errors.
3405 : * - A partition inherits from only a single parent. Hence this function is
3406 : * applicable only to a regular inheritance.
3407 : */
3408 : static ColumnDef *
3409 370 : MergeInheritedAttribute(List *inh_columns,
3410 : int exist_attno,
3411 : const ColumnDef *newdef)
3412 : {
3413 370 : char *attributeName = newdef->colname;
3414 : ColumnDef *prevdef;
3415 : Oid prevtypeid,
3416 : newtypeid;
3417 : int32 prevtypmod,
3418 : newtypmod;
3419 : Oid prevcollid,
3420 : newcollid;
3421 :
3422 370 : ereport(NOTICE,
3423 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3424 : attributeName)));
3425 370 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3426 :
3427 : /*
3428 : * Must have the same type and typmod
3429 : */
3430 370 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3431 370 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3432 370 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3433 0 : ereport(ERROR,
3434 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3435 : errmsg("inherited column \"%s\" has a type conflict",
3436 : attributeName),
3437 : errdetail("%s versus %s",
3438 : format_type_with_typemod(prevtypeid, prevtypmod),
3439 : format_type_with_typemod(newtypeid, newtypmod))));
3440 :
3441 : /*
3442 : * Must have the same collation
3443 : */
3444 370 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3445 370 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3446 370 : if (prevcollid != newcollid)
3447 0 : ereport(ERROR,
3448 : (errcode(ERRCODE_COLLATION_MISMATCH),
3449 : errmsg("inherited column \"%s\" has a collation conflict",
3450 : attributeName),
3451 : errdetail("\"%s\" versus \"%s\"",
3452 : get_collation_name(prevcollid),
3453 : get_collation_name(newcollid))));
3454 :
3455 : /*
3456 : * Copy/check storage parameter
3457 : */
3458 370 : if (prevdef->storage == 0)
3459 0 : prevdef->storage = newdef->storage;
3460 370 : else if (prevdef->storage != newdef->storage)
3461 6 : ereport(ERROR,
3462 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3463 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3464 : attributeName),
3465 : errdetail("%s versus %s",
3466 : storage_name(prevdef->storage),
3467 : storage_name(newdef->storage))));
3468 :
3469 : /*
3470 : * Copy/check compression parameter
3471 : */
3472 364 : if (prevdef->compression == NULL)
3473 346 : prevdef->compression = newdef->compression;
3474 18 : else if (newdef->compression != NULL)
3475 : {
3476 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3477 6 : ereport(ERROR,
3478 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3479 : errmsg("column \"%s\" has a compression method conflict",
3480 : attributeName),
3481 : errdetail("%s versus %s",
3482 : prevdef->compression, newdef->compression)));
3483 : }
3484 :
3485 : /*
3486 : * Check for GENERATED conflicts
3487 : */
3488 358 : if (prevdef->generated != newdef->generated)
3489 24 : ereport(ERROR,
3490 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3491 : errmsg("inherited column \"%s\" has a generation conflict",
3492 : attributeName)));
3493 :
3494 : /*
3495 : * Default and other constraints are handled by the caller.
3496 : */
3497 :
3498 334 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3499 : &prevdef->inhcount))
3500 0 : ereport(ERROR,
3501 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3502 : errmsg("too many inheritance parents"));
3503 :
3504 334 : return prevdef;
3505 : }
3506 :
3507 : /*
3508 : * StoreCatalogInheritance
3509 : * Updates the system catalogs with proper inheritance information.
3510 : *
3511 : * supers is a list of the OIDs of the new relation's direct ancestors.
3512 : */
3513 : static void
3514 61556 : StoreCatalogInheritance(Oid relationId, List *supers,
3515 : bool child_is_partition)
3516 : {
3517 : Relation relation;
3518 : int32 seqNumber;
3519 : ListCell *entry;
3520 :
3521 : /*
3522 : * sanity checks
3523 : */
3524 : Assert(OidIsValid(relationId));
3525 :
3526 61556 : if (supers == NIL)
3527 52024 : return;
3528 :
3529 : /*
3530 : * Store INHERITS information in pg_inherits using direct ancestors only.
3531 : * Also enter dependencies on the direct ancestors, and make sure they are
3532 : * marked with relhassubclass = true.
3533 : *
3534 : * (Once upon a time, both direct and indirect ancestors were found here
3535 : * and then entered into pg_ipl. Since that catalog doesn't exist
3536 : * anymore, there's no need to look for indirect ancestors.)
3537 : */
3538 9532 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3539 :
3540 9532 : seqNumber = 1;
3541 19398 : foreach(entry, supers)
3542 : {
3543 9866 : Oid parentOid = lfirst_oid(entry);
3544 :
3545 9866 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3546 : child_is_partition);
3547 9866 : seqNumber++;
3548 : }
3549 :
3550 9532 : table_close(relation, RowExclusiveLock);
3551 : }
3552 :
3553 : /*
3554 : * Make catalog entries showing relationId as being an inheritance child
3555 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3556 : */
3557 : static void
3558 12320 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3559 : int32 seqNumber, Relation inhRelation,
3560 : bool child_is_partition)
3561 : {
3562 : ObjectAddress childobject,
3563 : parentobject;
3564 :
3565 : /* store the pg_inherits row */
3566 12320 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3567 :
3568 : /*
3569 : * Store a dependency too
3570 : */
3571 12320 : parentobject.classId = RelationRelationId;
3572 12320 : parentobject.objectId = parentOid;
3573 12320 : parentobject.objectSubId = 0;
3574 12320 : childobject.classId = RelationRelationId;
3575 12320 : childobject.objectId = relationId;
3576 12320 : childobject.objectSubId = 0;
3577 :
3578 12320 : recordDependencyOn(&childobject, &parentobject,
3579 : child_dependency_type(child_is_partition));
3580 :
3581 : /*
3582 : * Post creation hook of this inheritance. Since object_access_hook
3583 : * doesn't take multiple object identifiers, we relay oid of parent
3584 : * relation using auxiliary_id argument.
3585 : */
3586 12320 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3587 : relationId, 0,
3588 : parentOid, false);
3589 :
3590 : /*
3591 : * Mark the parent as having subclasses.
3592 : */
3593 12320 : SetRelationHasSubclass(parentOid, true);
3594 12320 : }
3595 :
3596 : /*
3597 : * Look for an existing column entry with the given name.
3598 : *
3599 : * Returns the index (starting with 1) if attribute already exists in columns,
3600 : * 0 if it doesn't.
3601 : */
3602 : static int
3603 21824 : findAttrByName(const char *attributeName, const List *columns)
3604 : {
3605 : ListCell *lc;
3606 21824 : int i = 1;
3607 :
3608 38980 : foreach(lc, columns)
3609 : {
3610 17906 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3611 750 : return i;
3612 :
3613 17156 : i++;
3614 : }
3615 21074 : return 0;
3616 : }
3617 :
3618 :
3619 : /*
3620 : * SetRelationHasSubclass
3621 : * Set the value of the relation's relhassubclass field in pg_class.
3622 : *
3623 : * It's always safe to set this field to true, because all SQL commands are
3624 : * ready to see true and then find no children. On the other hand, commands
3625 : * generally assume zero children if this is false.
3626 : *
3627 : * Caller must hold any self-exclusive lock until end of transaction. If the
3628 : * new value is false, caller must have acquired that lock before reading the
3629 : * evidence that justified the false value. That way, it properly waits if
3630 : * another backend is simultaneously concluding no need to change the tuple
3631 : * (new and old values are true).
3632 : *
3633 : * NOTE: an important side-effect of this operation is that an SI invalidation
3634 : * message is sent out to all backends --- including me --- causing plans
3635 : * referencing the relation to be rebuilt with the new list of children.
3636 : * This must happen even if we find that no change is needed in the pg_class
3637 : * row.
3638 : */
3639 : void
3640 15358 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3641 : {
3642 : Relation relationRelation;
3643 : HeapTuple tuple;
3644 : Form_pg_class classtuple;
3645 :
3646 : Assert(CheckRelationOidLockedByMe(relationId,
3647 : ShareUpdateExclusiveLock, false) ||
3648 : CheckRelationOidLockedByMe(relationId,
3649 : ShareRowExclusiveLock, true));
3650 :
3651 : /*
3652 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3653 : */
3654 15358 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3655 15358 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3656 15358 : if (!HeapTupleIsValid(tuple))
3657 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3658 15358 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3659 :
3660 15358 : if (classtuple->relhassubclass != relhassubclass)
3661 : {
3662 7782 : classtuple->relhassubclass = relhassubclass;
3663 7782 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3664 : }
3665 : else
3666 : {
3667 : /* no need to change tuple, but force relcache rebuild anyway */
3668 7576 : CacheInvalidateRelcacheByTuple(tuple);
3669 : }
3670 :
3671 15358 : heap_freetuple(tuple);
3672 15358 : table_close(relationRelation, RowExclusiveLock);
3673 15358 : }
3674 :
3675 : /*
3676 : * CheckRelationTableSpaceMove
3677 : * Check if relation can be moved to new tablespace.
3678 : *
3679 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3680 : *
3681 : * Returns true if the relation can be moved to the new tablespace; raises
3682 : * an error if it is not possible to do the move; returns false if the move
3683 : * would have no effect.
3684 : */
3685 : bool
3686 226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3687 : {
3688 : Oid oldTableSpaceId;
3689 :
3690 : /*
3691 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3692 : * stored as 0.
3693 : */
3694 226 : oldTableSpaceId = rel->rd_rel->reltablespace;
3695 226 : if (newTableSpaceId == oldTableSpaceId ||
3696 218 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3697 10 : return false;
3698 :
3699 : /*
3700 : * We cannot support moving mapped relations into different tablespaces.
3701 : * (In particular this eliminates all shared catalogs.)
3702 : */
3703 216 : if (RelationIsMapped(rel))
3704 0 : ereport(ERROR,
3705 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3706 : errmsg("cannot move system relation \"%s\"",
3707 : RelationGetRelationName(rel))));
3708 :
3709 : /* Cannot move a non-shared relation into pg_global */
3710 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3711 12 : ereport(ERROR,
3712 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3713 : errmsg("only shared relations can be placed in pg_global tablespace")));
3714 :
3715 : /*
3716 : * Do not allow moving temp tables of other backends ... their local
3717 : * buffer manager is not going to cope.
3718 : */
3719 204 : if (RELATION_IS_OTHER_TEMP(rel))
3720 0 : ereport(ERROR,
3721 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3722 : errmsg("cannot move temporary tables of other sessions")));
3723 :
3724 204 : return true;
3725 : }
3726 :
3727 : /*
3728 : * SetRelationTableSpace
3729 : * Set new reltablespace and relfilenumber in pg_class entry.
3730 : *
3731 : * newTableSpaceId is the new tablespace for the relation, and
3732 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3733 : * InvalidRelFileNumber, this field is not updated.
3734 : *
3735 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3736 : *
3737 : * The caller of this routine had better check if a relation can be
3738 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3739 : * first, and is responsible for making the change visible with
3740 : * CommandCounterIncrement().
3741 : */
3742 : void
3743 204 : SetRelationTableSpace(Relation rel,
3744 : Oid newTableSpaceId,
3745 : RelFileNumber newRelFilenumber)
3746 : {
3747 : Relation pg_class;
3748 : HeapTuple tuple;
3749 : ItemPointerData otid;
3750 : Form_pg_class rd_rel;
3751 204 : Oid reloid = RelationGetRelid(rel);
3752 :
3753 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3754 :
3755 : /* Get a modifiable copy of the relation's pg_class row. */
3756 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3757 :
3758 204 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3759 204 : if (!HeapTupleIsValid(tuple))
3760 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3761 204 : otid = tuple->t_self;
3762 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3763 :
3764 : /* Update the pg_class row. */
3765 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3766 204 : InvalidOid : newTableSpaceId;
3767 204 : if (RelFileNumberIsValid(newRelFilenumber))
3768 160 : rd_rel->relfilenode = newRelFilenumber;
3769 204 : CatalogTupleUpdate(pg_class, &otid, tuple);
3770 204 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3771 :
3772 : /*
3773 : * Record dependency on tablespace. This is only required for relations
3774 : * that have no physical storage.
3775 : */
3776 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3777 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3778 : rd_rel->reltablespace);
3779 :
3780 204 : heap_freetuple(tuple);
3781 204 : table_close(pg_class, RowExclusiveLock);
3782 204 : }
3783 :
3784 : /*
3785 : * renameatt_check - basic sanity checks before attribute rename
3786 : */
3787 : static void
3788 1022 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3789 : {
3790 1022 : char relkind = classform->relkind;
3791 :
3792 1022 : if (classform->reloftype && !recursing)
3793 6 : ereport(ERROR,
3794 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3795 : errmsg("cannot rename column of typed table")));
3796 :
3797 : /*
3798 : * Renaming the columns of sequences or toast tables doesn't actually
3799 : * break anything from the system's point of view, since internal
3800 : * references are by attnum. But it doesn't seem right to allow users to
3801 : * change names that are hardcoded into the system, hence the following
3802 : * restriction.
3803 : */
3804 1016 : if (relkind != RELKIND_RELATION &&
3805 84 : relkind != RELKIND_VIEW &&
3806 84 : relkind != RELKIND_MATVIEW &&
3807 36 : relkind != RELKIND_COMPOSITE_TYPE &&
3808 36 : relkind != RELKIND_INDEX &&
3809 36 : relkind != RELKIND_PARTITIONED_INDEX &&
3810 0 : relkind != RELKIND_FOREIGN_TABLE &&
3811 : relkind != RELKIND_PARTITIONED_TABLE)
3812 0 : ereport(ERROR,
3813 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3814 : errmsg("cannot rename columns of relation \"%s\"",
3815 : NameStr(classform->relname)),
3816 : errdetail_relkind_not_supported(relkind)));
3817 :
3818 : /*
3819 : * permissions checking. only the owner of a class can change its schema.
3820 : */
3821 1016 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3822 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3823 0 : NameStr(classform->relname));
3824 1016 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3825 2 : ereport(ERROR,
3826 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3827 : errmsg("permission denied: \"%s\" is a system catalog",
3828 : NameStr(classform->relname))));
3829 1014 : }
3830 :
3831 : /*
3832 : * renameatt_internal - workhorse for renameatt
3833 : *
3834 : * Return value is the attribute number in the 'myrelid' relation.
3835 : */
3836 : static AttrNumber
3837 552 : renameatt_internal(Oid myrelid,
3838 : const char *oldattname,
3839 : const char *newattname,
3840 : bool recurse,
3841 : bool recursing,
3842 : int expected_parents,
3843 : DropBehavior behavior)
3844 : {
3845 : Relation targetrelation;
3846 : Relation attrelation;
3847 : HeapTuple atttup;
3848 : Form_pg_attribute attform;
3849 : AttrNumber attnum;
3850 :
3851 : /*
3852 : * Grab an exclusive lock on the target table, which we will NOT release
3853 : * until end of transaction.
3854 : */
3855 552 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3856 552 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3857 :
3858 : /*
3859 : * if the 'recurse' flag is set then we are supposed to rename this
3860 : * attribute in all classes that inherit from 'relname' (as well as in
3861 : * 'relname').
3862 : *
3863 : * any permissions or problems with duplicate attributes will cause the
3864 : * whole transaction to abort, which is what we want -- all or nothing.
3865 : */
3866 552 : if (recurse)
3867 : {
3868 : List *child_oids,
3869 : *child_numparents;
3870 : ListCell *lo,
3871 : *li;
3872 :
3873 : /*
3874 : * we need the number of parents for each child so that the recursive
3875 : * calls to renameatt() can determine whether there are any parents
3876 : * outside the inheritance hierarchy being processed.
3877 : */
3878 248 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3879 : &child_numparents);
3880 :
3881 : /*
3882 : * find_all_inheritors does the recursive search of the inheritance
3883 : * hierarchy, so all we have to do is process all of the relids in the
3884 : * list that it returns.
3885 : */
3886 734 : forboth(lo, child_oids, li, child_numparents)
3887 : {
3888 516 : Oid childrelid = lfirst_oid(lo);
3889 516 : int numparents = lfirst_int(li);
3890 :
3891 516 : if (childrelid == myrelid)
3892 248 : continue;
3893 : /* note we need not recurse again */
3894 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3895 : }
3896 : }
3897 : else
3898 : {
3899 : /*
3900 : * If we are told not to recurse, there had better not be any child
3901 : * tables; else the rename would put them out of step.
3902 : *
3903 : * expected_parents will only be 0 if we are not already recursing.
3904 : */
3905 340 : if (expected_parents == 0 &&
3906 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3907 12 : ereport(ERROR,
3908 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3909 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3910 : oldattname)));
3911 : }
3912 :
3913 : /* rename attributes in typed tables of composite type */
3914 510 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3915 : {
3916 : List *child_oids;
3917 : ListCell *lo;
3918 :
3919 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3920 24 : RelationGetRelationName(targetrelation),
3921 : behavior);
3922 :
3923 24 : foreach(lo, child_oids)
3924 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3925 : }
3926 :
3927 504 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3928 :
3929 504 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3930 504 : if (!HeapTupleIsValid(atttup))
3931 24 : ereport(ERROR,
3932 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3933 : errmsg("column \"%s\" does not exist",
3934 : oldattname)));
3935 480 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3936 :
3937 480 : attnum = attform->attnum;
3938 480 : if (attnum <= 0)
3939 0 : ereport(ERROR,
3940 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3941 : errmsg("cannot rename system column \"%s\"",
3942 : oldattname)));
3943 :
3944 : /*
3945 : * if the attribute is inherited, forbid the renaming. if this is a
3946 : * top-level call to renameatt(), then expected_parents will be 0, so the
3947 : * effect of this code will be to prohibit the renaming if the attribute
3948 : * is inherited at all. if this is a recursive call to renameatt(),
3949 : * expected_parents will be the number of parents the current relation has
3950 : * within the inheritance hierarchy being processed, so we'll prohibit the
3951 : * renaming only if there are additional parents from elsewhere.
3952 : */
3953 480 : if (attform->attinhcount > expected_parents)
3954 30 : ereport(ERROR,
3955 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3956 : errmsg("cannot rename inherited column \"%s\"",
3957 : oldattname)));
3958 :
3959 : /* new name should not already exist */
3960 450 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3961 :
3962 : /* apply the update */
3963 438 : namestrcpy(&(attform->attname), newattname);
3964 :
3965 438 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3966 :
3967 438 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3968 :
3969 438 : heap_freetuple(atttup);
3970 :
3971 438 : table_close(attrelation, RowExclusiveLock);
3972 :
3973 438 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3974 :
3975 438 : return attnum;
3976 : }
3977 :
3978 : /*
3979 : * Perform permissions and integrity checks before acquiring a relation lock.
3980 : */
3981 : static void
3982 422 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3983 : void *arg)
3984 : {
3985 : HeapTuple tuple;
3986 : Form_pg_class form;
3987 :
3988 422 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3989 422 : if (!HeapTupleIsValid(tuple))
3990 36 : return; /* concurrently dropped */
3991 386 : form = (Form_pg_class) GETSTRUCT(tuple);
3992 386 : renameatt_check(relid, form, false);
3993 378 : ReleaseSysCache(tuple);
3994 : }
3995 :
3996 : /*
3997 : * renameatt - changes the name of an attribute in a relation
3998 : *
3999 : * The returned ObjectAddress is that of the renamed column.
4000 : */
4001 : ObjectAddress
4002 316 : renameatt(RenameStmt *stmt)
4003 : {
4004 : Oid relid;
4005 : AttrNumber attnum;
4006 : ObjectAddress address;
4007 :
4008 : /* lock level taken here should match renameatt_internal */
4009 316 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4010 316 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4011 : RangeVarCallbackForRenameAttribute,
4012 : NULL);
4013 :
4014 302 : if (!OidIsValid(relid))
4015 : {
4016 24 : ereport(NOTICE,
4017 : (errmsg("relation \"%s\" does not exist, skipping",
4018 : stmt->relation->relname)));
4019 24 : return InvalidObjectAddress;
4020 : }
4021 :
4022 : attnum =
4023 278 : renameatt_internal(relid,
4024 278 : stmt->subname, /* old att name */
4025 278 : stmt->newname, /* new att name */
4026 278 : stmt->relation->inh, /* recursive? */
4027 : false, /* recursing? */
4028 : 0, /* expected inhcount */
4029 : stmt->behavior);
4030 :
4031 194 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4032 :
4033 194 : return address;
4034 : }
4035 :
4036 : /*
4037 : * same logic as renameatt_internal
4038 : */
4039 : static ObjectAddress
4040 90 : rename_constraint_internal(Oid myrelid,
4041 : Oid mytypid,
4042 : const char *oldconname,
4043 : const char *newconname,
4044 : bool recurse,
4045 : bool recursing,
4046 : int expected_parents)
4047 : {
4048 90 : Relation targetrelation = NULL;
4049 : Oid constraintOid;
4050 : HeapTuple tuple;
4051 : Form_pg_constraint con;
4052 : ObjectAddress address;
4053 :
4054 : Assert(!myrelid || !mytypid);
4055 :
4056 90 : if (mytypid)
4057 : {
4058 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4059 : }
4060 : else
4061 : {
4062 84 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4063 :
4064 : /*
4065 : * don't tell it whether we're recursing; we allow changing typed
4066 : * tables here
4067 : */
4068 84 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4069 :
4070 84 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4071 : }
4072 :
4073 90 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4074 90 : if (!HeapTupleIsValid(tuple))
4075 0 : elog(ERROR, "cache lookup failed for constraint %u",
4076 : constraintOid);
4077 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4078 :
4079 90 : if (myrelid &&
4080 84 : (con->contype == CONSTRAINT_CHECK ||
4081 24 : con->contype == CONSTRAINT_NOTNULL) &&
4082 66 : !con->connoinherit)
4083 : {
4084 54 : if (recurse)
4085 : {
4086 : List *child_oids,
4087 : *child_numparents;
4088 : ListCell *lo,
4089 : *li;
4090 :
4091 36 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4092 : &child_numparents);
4093 :
4094 84 : forboth(lo, child_oids, li, child_numparents)
4095 : {
4096 48 : Oid childrelid = lfirst_oid(lo);
4097 48 : int numparents = lfirst_int(li);
4098 :
4099 48 : if (childrelid == myrelid)
4100 36 : continue;
4101 :
4102 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4103 : }
4104 : }
4105 : else
4106 : {
4107 24 : if (expected_parents == 0 &&
4108 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4109 6 : ereport(ERROR,
4110 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4111 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4112 : oldconname)));
4113 : }
4114 :
4115 48 : if (con->coninhcount > expected_parents)
4116 6 : ereport(ERROR,
4117 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4118 : errmsg("cannot rename inherited constraint \"%s\"",
4119 : oldconname)));
4120 : }
4121 :
4122 78 : if (con->conindid
4123 18 : && (con->contype == CONSTRAINT_PRIMARY
4124 6 : || con->contype == CONSTRAINT_UNIQUE
4125 0 : || con->contype == CONSTRAINT_EXCLUSION))
4126 : /* rename the index; this renames the constraint as well */
4127 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4128 : else
4129 60 : RenameConstraintById(constraintOid, newconname);
4130 :
4131 78 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4132 :
4133 78 : ReleaseSysCache(tuple);
4134 :
4135 78 : if (targetrelation)
4136 : {
4137 : /*
4138 : * Invalidate relcache so as others can see the new constraint name.
4139 : */
4140 72 : CacheInvalidateRelcache(targetrelation);
4141 :
4142 72 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4143 : }
4144 :
4145 78 : return address;
4146 : }
4147 :
4148 : ObjectAddress
4149 84 : RenameConstraint(RenameStmt *stmt)
4150 : {
4151 84 : Oid relid = InvalidOid;
4152 84 : Oid typid = InvalidOid;
4153 :
4154 84 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4155 : {
4156 : Relation rel;
4157 : HeapTuple tup;
4158 :
4159 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4160 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4161 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4162 6 : if (!HeapTupleIsValid(tup))
4163 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4164 6 : checkDomainOwner(tup);
4165 6 : ReleaseSysCache(tup);
4166 6 : table_close(rel, NoLock);
4167 : }
4168 : else
4169 : {
4170 : /* lock level taken here should match rename_constraint_internal */
4171 78 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4172 78 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4173 : RangeVarCallbackForRenameAttribute,
4174 : NULL);
4175 78 : if (!OidIsValid(relid))
4176 : {
4177 6 : ereport(NOTICE,
4178 : (errmsg("relation \"%s\" does not exist, skipping",
4179 : stmt->relation->relname)));
4180 6 : return InvalidObjectAddress;
4181 : }
4182 : }
4183 :
4184 : return
4185 78 : rename_constraint_internal(relid, typid,
4186 78 : stmt->subname,
4187 78 : stmt->newname,
4188 150 : (stmt->relation &&
4189 72 : stmt->relation->inh), /* recursive? */
4190 : false, /* recursing? */
4191 : 0 /* expected inhcount */ );
4192 : }
4193 :
4194 : /*
4195 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4196 : * RENAME
4197 : */
4198 : ObjectAddress
4199 512 : RenameRelation(RenameStmt *stmt)
4200 : {
4201 512 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4202 : Oid relid;
4203 : ObjectAddress address;
4204 :
4205 : /*
4206 : * Grab an exclusive lock on the target table, index, sequence, view,
4207 : * materialized view, or foreign table, which we will NOT release until
4208 : * end of transaction.
4209 : *
4210 : * Lock level used here should match RenameRelationInternal, to avoid lock
4211 : * escalation. However, because ALTER INDEX can be used with any relation
4212 : * type, we mustn't believe without verification.
4213 : */
4214 : for (;;)
4215 12 : {
4216 : LOCKMODE lockmode;
4217 : char relkind;
4218 : bool obj_is_index;
4219 :
4220 524 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4221 :
4222 524 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4223 524 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4224 : RangeVarCallbackForAlterRelation,
4225 : stmt);
4226 :
4227 474 : if (!OidIsValid(relid))
4228 : {
4229 18 : ereport(NOTICE,
4230 : (errmsg("relation \"%s\" does not exist, skipping",
4231 : stmt->relation->relname)));
4232 18 : return InvalidObjectAddress;
4233 : }
4234 :
4235 : /*
4236 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4237 : * to rename a table), but we might've used the wrong lock level. If
4238 : * that happens, retry with the correct lock level. We don't bother
4239 : * if we already acquired AccessExclusiveLock with an index, however.
4240 : */
4241 456 : relkind = get_rel_relkind(relid);
4242 456 : obj_is_index = (relkind == RELKIND_INDEX ||
4243 : relkind == RELKIND_PARTITIONED_INDEX);
4244 456 : if (obj_is_index || is_index_stmt == obj_is_index)
4245 : break;
4246 :
4247 12 : UnlockRelationOid(relid, lockmode);
4248 12 : is_index_stmt = obj_is_index;
4249 : }
4250 :
4251 : /* Do the work */
4252 444 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4253 :
4254 432 : ObjectAddressSet(address, RelationRelationId, relid);
4255 :
4256 432 : return address;
4257 : }
4258 :
4259 : /*
4260 : * RenameRelationInternal - change the name of a relation
4261 : */
4262 : void
4263 1676 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4264 : {
4265 : Relation targetrelation;
4266 : Relation relrelation; /* for RELATION relation */
4267 : ItemPointerData otid;
4268 : HeapTuple reltup;
4269 : Form_pg_class relform;
4270 : Oid namespaceId;
4271 :
4272 : /*
4273 : * Grab a lock on the target relation, which we will NOT release until end
4274 : * of transaction. We need at least a self-exclusive lock so that
4275 : * concurrent DDL doesn't overwrite the rename if they start updating
4276 : * while still seeing the old version. The lock also guards against
4277 : * triggering relcache reloads in concurrent sessions, which might not
4278 : * handle this information changing under them. For indexes, we can use a
4279 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4280 : * specially.
4281 : */
4282 1676 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4283 1676 : namespaceId = RelationGetNamespace(targetrelation);
4284 :
4285 : /*
4286 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4287 : */
4288 1676 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4289 :
4290 1676 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4291 1676 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4292 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4293 1676 : otid = reltup->t_self;
4294 1676 : relform = (Form_pg_class) GETSTRUCT(reltup);
4295 :
4296 1676 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4297 12 : ereport(ERROR,
4298 : (errcode(ERRCODE_DUPLICATE_TABLE),
4299 : errmsg("relation \"%s\" already exists",
4300 : newrelname)));
4301 :
4302 : /*
4303 : * RenameRelation is careful not to believe the caller's idea of the
4304 : * relation kind being handled. We don't have to worry about this, but
4305 : * let's not be totally oblivious to it. We can process an index as
4306 : * not-an-index, but not the other way around.
4307 : */
4308 : Assert(!is_index ||
4309 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4310 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4311 :
4312 : /*
4313 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4314 : * because it's a copy...)
4315 : */
4316 1664 : namestrcpy(&(relform->relname), newrelname);
4317 :
4318 1664 : CatalogTupleUpdate(relrelation, &otid, reltup);
4319 1664 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4320 :
4321 1664 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4322 : InvalidOid, is_internal);
4323 :
4324 1664 : heap_freetuple(reltup);
4325 1664 : table_close(relrelation, RowExclusiveLock);
4326 :
4327 : /*
4328 : * Also rename the associated type, if any.
4329 : */
4330 1664 : if (OidIsValid(targetrelation->rd_rel->reltype))
4331 126 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4332 : newrelname, namespaceId);
4333 :
4334 : /*
4335 : * Also rename the associated constraint, if any.
4336 : */
4337 1664 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4338 874 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4339 : {
4340 808 : Oid constraintId = get_index_constraint(myrelid);
4341 :
4342 808 : if (OidIsValid(constraintId))
4343 36 : RenameConstraintById(constraintId, newrelname);
4344 : }
4345 :
4346 : /*
4347 : * Close rel, but keep lock!
4348 : */
4349 1664 : relation_close(targetrelation, NoLock);
4350 1664 : }
4351 :
4352 : /*
4353 : * ResetRelRewrite - reset relrewrite
4354 : */
4355 : void
4356 596 : ResetRelRewrite(Oid myrelid)
4357 : {
4358 : Relation relrelation; /* for RELATION relation */
4359 : HeapTuple reltup;
4360 : Form_pg_class relform;
4361 :
4362 : /*
4363 : * Find relation's pg_class tuple.
4364 : */
4365 596 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4366 :
4367 596 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4368 596 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4369 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4370 596 : relform = (Form_pg_class) GETSTRUCT(reltup);
4371 :
4372 : /*
4373 : * Update pg_class tuple.
4374 : */
4375 596 : relform->relrewrite = InvalidOid;
4376 :
4377 596 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4378 :
4379 596 : heap_freetuple(reltup);
4380 596 : table_close(relrelation, RowExclusiveLock);
4381 596 : }
4382 :
4383 : /*
4384 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4385 : * any open reference to the target table besides the one just acquired by
4386 : * the calling command; this implies there's an open cursor or active plan.
4387 : * We need this check because our lock doesn't protect us against stomping
4388 : * on our own foot, only other people's feet!
4389 : *
4390 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4391 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4392 : * possibly be relaxed to only error out for certain types of alterations.
4393 : * But the use-case for allowing any of these things is not obvious, so we
4394 : * won't work hard at it for now.
4395 : *
4396 : * We also reject these commands if there are any pending AFTER trigger events
4397 : * for the rel. This is certainly necessary for the rewriting variants of
4398 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4399 : * events would try to fetch the wrong tuples. It might be overly cautious
4400 : * in other cases, but again it seems better to err on the side of paranoia.
4401 : *
4402 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4403 : * we are worried about active indexscans on the index. The trigger-event
4404 : * check can be skipped, since we are doing no damage to the parent table.
4405 : *
4406 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4407 : */
4408 : void
4409 169184 : CheckTableNotInUse(Relation rel, const char *stmt)
4410 : {
4411 : int expected_refcnt;
4412 :
4413 169184 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4414 169184 : if (rel->rd_refcnt != expected_refcnt)
4415 42 : ereport(ERROR,
4416 : (errcode(ERRCODE_OBJECT_IN_USE),
4417 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4418 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4419 : stmt, RelationGetRelationName(rel))));
4420 :
4421 169142 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4422 275588 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4423 136756 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4424 18 : ereport(ERROR,
4425 : (errcode(ERRCODE_OBJECT_IN_USE),
4426 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4427 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4428 : stmt, RelationGetRelationName(rel))));
4429 169124 : }
4430 :
4431 : /*
4432 : * CheckAlterTableIsSafe
4433 : * Verify that it's safe to allow ALTER TABLE on this relation.
4434 : *
4435 : * This consists of CheckTableNotInUse() plus a check that the relation
4436 : * isn't another session's temp table. We must split out the temp-table
4437 : * check because there are callers of CheckTableNotInUse() that don't want
4438 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4439 : * an orphaned temp schema.) Compare truncate_check_activity().
4440 : */
4441 : static void
4442 61138 : CheckAlterTableIsSafe(Relation rel)
4443 : {
4444 : /*
4445 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4446 : * manager is not going to cope if we need to change the table's contents.
4447 : * Even if we don't, there may be optimizations that assume temp tables
4448 : * aren't subject to such interference.
4449 : */
4450 61138 : if (RELATION_IS_OTHER_TEMP(rel))
4451 0 : ereport(ERROR,
4452 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4453 : errmsg("cannot alter temporary tables of other sessions")));
4454 :
4455 : /*
4456 : * Also check for active uses of the relation in the current transaction,
4457 : * including open scans and pending AFTER trigger events.
4458 : */
4459 61138 : CheckTableNotInUse(rel, "ALTER TABLE");
4460 61102 : }
4461 :
4462 : /*
4463 : * AlterTableLookupRelation
4464 : * Look up, and lock, the OID for the relation named by an alter table
4465 : * statement.
4466 : */
4467 : Oid
4468 32234 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4469 : {
4470 64380 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4471 32234 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4472 : RangeVarCallbackForAlterRelation,
4473 : stmt);
4474 : }
4475 :
4476 : /*
4477 : * AlterTable
4478 : * Execute ALTER TABLE, which can be a list of subcommands
4479 : *
4480 : * ALTER TABLE is performed in three phases:
4481 : * 1. Examine subcommands and perform pre-transformation checking.
4482 : * 2. Validate and transform subcommands, and update system catalogs.
4483 : * 3. Scan table(s) to check new constraints, and optionally recopy
4484 : * the data into new table(s).
4485 : * Phase 3 is not performed unless one or more of the subcommands requires
4486 : * it. The intention of this design is to allow multiple independent
4487 : * updates of the table schema to be performed with only one pass over the
4488 : * data.
4489 : *
4490 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4491 : * each table to be affected (there may be multiple affected tables if the
4492 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4493 : * validation of the subcommands. Because earlier subcommands may change
4494 : * the catalog state seen by later commands, there are limits to what can
4495 : * be done in this phase. Generally, this phase acquires table locks,
4496 : * checks permissions and relkind, and recurses to find child tables.
4497 : *
4498 : * ATRewriteCatalogs performs phase 2 for each affected table.
4499 : * Certain subcommands need to be performed before others to avoid
4500 : * unnecessary conflicts; for example, DROP COLUMN should come before
4501 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4502 : * lists, one for each logical "pass" of phase 2.
4503 : *
4504 : * ATRewriteTables performs phase 3 for those tables that need it.
4505 : *
4506 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4507 : * since phase 1 already does it. However, for certain subcommand types
4508 : * it is only possible to determine how to recurse at phase 2 time; for
4509 : * those cases, phase 1 sets the cmd->recurse flag.
4510 : *
4511 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4512 : * the whole operation; we don't have to do anything special to clean up.
4513 : *
4514 : * The caller must lock the relation, with an appropriate lock level
4515 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4516 : * or higher. We pass the lock level down
4517 : * so that we can apply it recursively to inherited tables. Note that the
4518 : * lock level we want as we recurse might well be higher than required for
4519 : * that specific subcommand. So we pass down the overall lock requirement,
4520 : * rather than reassess it at lower levels.
4521 : *
4522 : * The caller also provides a "context" which is to be passed back to
4523 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4524 : * Some of the fields therein, such as the relid, are used here as well.
4525 : */
4526 : void
4527 32008 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4528 : AlterTableUtilityContext *context)
4529 : {
4530 : Relation rel;
4531 :
4532 : /* Caller is required to provide an adequate lock. */
4533 32008 : rel = relation_open(context->relid, NoLock);
4534 :
4535 32008 : CheckAlterTableIsSafe(rel);
4536 :
4537 31990 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4538 28288 : }
4539 :
4540 : /*
4541 : * AlterTableInternal
4542 : *
4543 : * ALTER TABLE with target specified by OID
4544 : *
4545 : * We do not reject if the relation is already open, because it's quite
4546 : * likely that one or more layers of caller have it open. That means it
4547 : * is unsafe to use this entry point for alterations that could break
4548 : * existing query plans. On the assumption it's not used for such, we
4549 : * don't have to reject pending AFTER triggers, either.
4550 : *
4551 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4552 : * used for any subcommand types that require parse transformation or
4553 : * could generate subcommands that have to be passed to ProcessUtility.
4554 : */
4555 : void
4556 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4557 : {
4558 : Relation rel;
4559 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4560 :
4561 278 : rel = relation_open(relid, lockmode);
4562 :
4563 278 : EventTriggerAlterTableRelid(relid);
4564 :
4565 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4566 278 : }
4567 :
4568 : /*
4569 : * AlterTableGetLockLevel
4570 : *
4571 : * Sets the overall lock level required for the supplied list of subcommands.
4572 : * Policy for doing this set according to needs of AlterTable(), see
4573 : * comments there for overall explanation.
4574 : *
4575 : * Function is called before and after parsing, so it must give same
4576 : * answer each time it is called. Some subcommands are transformed
4577 : * into other subcommand types, so the transform must never be made to a
4578 : * lower lock level than previously assigned. All transforms are noted below.
4579 : *
4580 : * Since this is called before we lock the table we cannot use table metadata
4581 : * to influence the type of lock we acquire.
4582 : *
4583 : * There should be no lockmodes hardcoded into the subcommand functions. All
4584 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4585 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4586 : * and does not travel through this section of code and cannot be combined with
4587 : * any of the subcommands given here.
4588 : *
4589 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4590 : * so any changes that might affect SELECTs running on standbys need to use
4591 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4592 : * have a solution for that also.
4593 : *
4594 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4595 : * that takes a lock less than AccessExclusiveLock can change object definitions
4596 : * while pg_dump is running. Be careful to check that the appropriate data is
4597 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4598 : * otherwise we might end up with an inconsistent dump that can't restore.
4599 : */
4600 : LOCKMODE
4601 32512 : AlterTableGetLockLevel(List *cmds)
4602 : {
4603 : /*
4604 : * This only works if we read catalog tables using MVCC snapshots.
4605 : */
4606 : ListCell *lcmd;
4607 32512 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4608 :
4609 66220 : foreach(lcmd, cmds)
4610 : {
4611 33708 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4612 33708 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4613 :
4614 33708 : switch (cmd->subtype)
4615 : {
4616 : /*
4617 : * These subcommands rewrite the heap, so require full locks.
4618 : */
4619 3598 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4620 : * to SELECT */
4621 : case AT_SetAccessMethod: /* must rewrite heap */
4622 : case AT_SetTableSpace: /* must rewrite heap */
4623 : case AT_AlterColumnType: /* must rewrite heap */
4624 3598 : cmd_lockmode = AccessExclusiveLock;
4625 3598 : break;
4626 :
4627 : /*
4628 : * These subcommands may require addition of toast tables. If
4629 : * we add a toast table to a table currently being scanned, we
4630 : * might miss data added to the new toast table by concurrent
4631 : * insert transactions.
4632 : */
4633 238 : case AT_SetStorage: /* may add toast tables, see
4634 : * ATRewriteCatalogs() */
4635 238 : cmd_lockmode = AccessExclusiveLock;
4636 238 : break;
4637 :
4638 : /*
4639 : * Removing constraints can affect SELECTs that have been
4640 : * optimized assuming the constraint holds true. See also
4641 : * CloneFkReferenced.
4642 : */
4643 1130 : case AT_DropConstraint: /* as DROP INDEX */
4644 : case AT_DropNotNull: /* may change some SQL plans */
4645 1130 : cmd_lockmode = AccessExclusiveLock;
4646 1130 : break;
4647 :
4648 : /*
4649 : * Subcommands that may be visible to concurrent SELECTs
4650 : */
4651 1752 : case AT_DropColumn: /* change visible to SELECT */
4652 : case AT_AddColumnToView: /* CREATE VIEW */
4653 : case AT_DropOids: /* used to equiv to DropColumn */
4654 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4655 : case AT_EnableReplicaRule: /* may change SELECT rules */
4656 : case AT_EnableRule: /* may change SELECT rules */
4657 : case AT_DisableRule: /* may change SELECT rules */
4658 1752 : cmd_lockmode = AccessExclusiveLock;
4659 1752 : break;
4660 :
4661 : /*
4662 : * Changing owner may remove implicit SELECT privileges
4663 : */
4664 2022 : case AT_ChangeOwner: /* change visible to SELECT */
4665 2022 : cmd_lockmode = AccessExclusiveLock;
4666 2022 : break;
4667 :
4668 : /*
4669 : * Changing foreign table options may affect optimization.
4670 : */
4671 254 : case AT_GenericOptions:
4672 : case AT_AlterColumnGenericOptions:
4673 254 : cmd_lockmode = AccessExclusiveLock;
4674 254 : break;
4675 :
4676 : /*
4677 : * These subcommands affect write operations only.
4678 : */
4679 342 : case AT_EnableTrig:
4680 : case AT_EnableAlwaysTrig:
4681 : case AT_EnableReplicaTrig:
4682 : case AT_EnableTrigAll:
4683 : case AT_EnableTrigUser:
4684 : case AT_DisableTrig:
4685 : case AT_DisableTrigAll:
4686 : case AT_DisableTrigUser:
4687 342 : cmd_lockmode = ShareRowExclusiveLock;
4688 342 : break;
4689 :
4690 : /*
4691 : * These subcommands affect write operations only. XXX
4692 : * Theoretically, these could be ShareRowExclusiveLock.
4693 : */
4694 2924 : case AT_ColumnDefault:
4695 : case AT_CookedColumnDefault:
4696 : case AT_AlterConstraint:
4697 : case AT_AddIndex: /* from ADD CONSTRAINT */
4698 : case AT_AddIndexConstraint:
4699 : case AT_ReplicaIdentity:
4700 : case AT_SetNotNull:
4701 : case AT_EnableRowSecurity:
4702 : case AT_DisableRowSecurity:
4703 : case AT_ForceRowSecurity:
4704 : case AT_NoForceRowSecurity:
4705 : case AT_AddIdentity:
4706 : case AT_DropIdentity:
4707 : case AT_SetIdentity:
4708 : case AT_SetExpression:
4709 : case AT_DropExpression:
4710 : case AT_SetCompression:
4711 2924 : cmd_lockmode = AccessExclusiveLock;
4712 2924 : break;
4713 :
4714 15602 : case AT_AddConstraint:
4715 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4716 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4717 15602 : if (IsA(cmd->def, Constraint))
4718 : {
4719 15602 : Constraint *con = (Constraint *) cmd->def;
4720 :
4721 15602 : switch (con->contype)
4722 : {
4723 11900 : case CONSTR_EXCLUSION:
4724 : case CONSTR_PRIMARY:
4725 : case CONSTR_UNIQUE:
4726 :
4727 : /*
4728 : * Cases essentially the same as CREATE INDEX. We
4729 : * could reduce the lock strength to ShareLock if
4730 : * we can work out how to allow concurrent catalog
4731 : * updates. XXX Might be set down to
4732 : * ShareRowExclusiveLock but requires further
4733 : * analysis.
4734 : */
4735 11900 : cmd_lockmode = AccessExclusiveLock;
4736 11900 : break;
4737 2582 : case CONSTR_FOREIGN:
4738 :
4739 : /*
4740 : * We add triggers to both tables when we add a
4741 : * Foreign Key, so the lock level must be at least
4742 : * as strong as CREATE TRIGGER.
4743 : */
4744 2582 : cmd_lockmode = ShareRowExclusiveLock;
4745 2582 : break;
4746 :
4747 1120 : default:
4748 1120 : cmd_lockmode = AccessExclusiveLock;
4749 : }
4750 : }
4751 15602 : break;
4752 :
4753 : /*
4754 : * These subcommands affect inheritance behaviour. Queries
4755 : * started before us will continue to see the old inheritance
4756 : * behaviour, while queries started after we commit will see
4757 : * new behaviour. No need to prevent reads or writes to the
4758 : * subtable while we hook it up though. Changing the TupDesc
4759 : * may be a problem, so keep highest lock.
4760 : */
4761 556 : case AT_AddInherit:
4762 : case AT_DropInherit:
4763 556 : cmd_lockmode = AccessExclusiveLock;
4764 556 : break;
4765 :
4766 : /*
4767 : * These subcommands affect implicit row type conversion. They
4768 : * have affects similar to CREATE/DROP CAST on queries. don't
4769 : * provide for invalidating parse trees as a result of such
4770 : * changes, so we keep these at AccessExclusiveLock.
4771 : */
4772 72 : case AT_AddOf:
4773 : case AT_DropOf:
4774 72 : cmd_lockmode = AccessExclusiveLock;
4775 72 : break;
4776 :
4777 : /*
4778 : * Only used by CREATE OR REPLACE VIEW which must conflict
4779 : * with an SELECTs currently using the view.
4780 : */
4781 194 : case AT_ReplaceRelOptions:
4782 194 : cmd_lockmode = AccessExclusiveLock;
4783 194 : break;
4784 :
4785 : /*
4786 : * These subcommands affect general strategies for performance
4787 : * and maintenance, though don't change the semantic results
4788 : * from normal data reads and writes. Delaying an ALTER TABLE
4789 : * behind currently active writes only delays the point where
4790 : * the new strategy begins to take effect, so there is no
4791 : * benefit in waiting. In this case the minimum restriction
4792 : * applies: we don't currently allow concurrent catalog
4793 : * updates.
4794 : */
4795 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4796 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4797 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4798 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4799 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4800 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4801 234 : break;
4802 :
4803 112 : case AT_SetLogged:
4804 : case AT_SetUnLogged:
4805 112 : cmd_lockmode = AccessExclusiveLock;
4806 112 : break;
4807 :
4808 476 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4809 476 : cmd_lockmode = ShareUpdateExclusiveLock;
4810 476 : break;
4811 :
4812 : /*
4813 : * Rel options are more complex than first appears. Options
4814 : * are set here for tables, views and indexes; for historical
4815 : * reasons these can all be used with ALTER TABLE, so we can't
4816 : * decide between them using the basic grammar.
4817 : */
4818 770 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4819 : * getTables() */
4820 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4821 : * getTables() */
4822 770 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4823 770 : break;
4824 :
4825 2816 : case AT_AttachPartition:
4826 2816 : cmd_lockmode = ShareUpdateExclusiveLock;
4827 2816 : break;
4828 :
4829 596 : case AT_DetachPartition:
4830 596 : if (((PartitionCmd *) cmd->def)->concurrent)
4831 164 : cmd_lockmode = ShareUpdateExclusiveLock;
4832 : else
4833 432 : cmd_lockmode = AccessExclusiveLock;
4834 596 : break;
4835 :
4836 20 : case AT_DetachPartitionFinalize:
4837 20 : cmd_lockmode = ShareUpdateExclusiveLock;
4838 20 : break;
4839 :
4840 0 : default: /* oops */
4841 0 : elog(ERROR, "unrecognized alter table type: %d",
4842 : (int) cmd->subtype);
4843 : break;
4844 : }
4845 :
4846 : /*
4847 : * Take the greatest lockmode from any subcommand
4848 : */
4849 33708 : if (cmd_lockmode > lockmode)
4850 28220 : lockmode = cmd_lockmode;
4851 : }
4852 :
4853 32512 : return lockmode;
4854 : }
4855 :
4856 : /*
4857 : * ATController provides top level control over the phases.
4858 : *
4859 : * parsetree is passed in to allow it to be passed to event triggers
4860 : * when requested.
4861 : */
4862 : static void
4863 32268 : ATController(AlterTableStmt *parsetree,
4864 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4865 : AlterTableUtilityContext *context)
4866 : {
4867 32268 : List *wqueue = NIL;
4868 : ListCell *lcmd;
4869 :
4870 : /* Phase 1: preliminary examination of commands, create work queue */
4871 65320 : foreach(lcmd, cmds)
4872 : {
4873 33458 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4874 :
4875 33458 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4876 : }
4877 :
4878 : /* Close the relation, but keep lock until commit */
4879 31862 : relation_close(rel, NoLock);
4880 :
4881 : /* Phase 2: update system catalogs */
4882 31862 : ATRewriteCatalogs(&wqueue, lockmode, context);
4883 :
4884 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4885 29040 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4886 28566 : }
4887 :
4888 : /*
4889 : * ATPrepCmd
4890 : *
4891 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4892 : * recursion and permission checks.
4893 : *
4894 : * Caller must have acquired appropriate lock type on relation already.
4895 : * This lock should be held until commit.
4896 : */
4897 : static void
4898 34398 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4899 : bool recurse, bool recursing, LOCKMODE lockmode,
4900 : AlterTableUtilityContext *context)
4901 : {
4902 : AlteredTableInfo *tab;
4903 34398 : AlterTablePass pass = AT_PASS_UNSET;
4904 :
4905 : /* Find or create work queue entry for this table */
4906 34398 : tab = ATGetQueueEntry(wqueue, rel);
4907 :
4908 : /*
4909 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4910 : * partitions that are pending detach.
4911 : */
4912 34398 : if (rel->rd_rel->relispartition &&
4913 2692 : cmd->subtype != AT_DetachPartitionFinalize &&
4914 1346 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4915 2 : ereport(ERROR,
4916 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4917 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4918 : RelationGetRelationName(rel)),
4919 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4920 :
4921 : /*
4922 : * Copy the original subcommand for each table, so we can scribble on it.
4923 : * This avoids conflicts when different child tables need to make
4924 : * different parse transformations (for example, the same column may have
4925 : * different column numbers in different children).
4926 : */
4927 34396 : cmd = copyObject(cmd);
4928 :
4929 : /*
4930 : * Do permissions and relkind checking, recursion to child tables if
4931 : * needed, and any additional phase-1 processing needed. (But beware of
4932 : * adding any processing that looks at table details that another
4933 : * subcommand could change. In some cases we reject multiple subcommands
4934 : * that could try to change the same state in contrary ways.)
4935 : */
4936 34396 : switch (cmd->subtype)
4937 : {
4938 2166 : case AT_AddColumn: /* ADD COLUMN */
4939 2166 : ATSimplePermissions(cmd->subtype, rel,
4940 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4941 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4942 2166 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4943 : lockmode, context);
4944 : /* Recursion occurs during execution phase */
4945 2154 : pass = AT_PASS_ADD_COL;
4946 2154 : break;
4947 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4948 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4949 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4950 : lockmode, context);
4951 : /* Recursion occurs during execution phase */
4952 24 : pass = AT_PASS_ADD_COL;
4953 24 : break;
4954 620 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4955 :
4956 : /*
4957 : * We allow defaults on views so that INSERT into a view can have
4958 : * default-ish behavior. This works because the rewriter
4959 : * substitutes default values into INSERTs before it expands
4960 : * rules.
4961 : */
4962 620 : ATSimplePermissions(cmd->subtype, rel,
4963 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4964 : ATT_FOREIGN_TABLE);
4965 620 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4966 : /* No command-specific prep needed */
4967 620 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4968 620 : break;
4969 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4970 : /* This is currently used only in CREATE TABLE */
4971 : /* (so the permission check really isn't necessary) */
4972 80 : ATSimplePermissions(cmd->subtype, rel,
4973 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4974 : /* This command never recurses */
4975 80 : pass = AT_PASS_ADD_OTHERCONSTR;
4976 80 : break;
4977 166 : case AT_AddIdentity:
4978 166 : ATSimplePermissions(cmd->subtype, rel,
4979 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4980 : ATT_FOREIGN_TABLE);
4981 : /* Set up recursion for phase 2; no other prep needed */
4982 166 : if (recurse)
4983 160 : cmd->recurse = true;
4984 166 : pass = AT_PASS_ADD_OTHERCONSTR;
4985 166 : break;
4986 62 : case AT_SetIdentity:
4987 62 : ATSimplePermissions(cmd->subtype, rel,
4988 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4989 : ATT_FOREIGN_TABLE);
4990 : /* Set up recursion for phase 2; no other prep needed */
4991 62 : if (recurse)
4992 56 : cmd->recurse = true;
4993 : /* This should run after AddIdentity, so do it in MISC pass */
4994 62 : pass = AT_PASS_MISC;
4995 62 : break;
4996 56 : case AT_DropIdentity:
4997 56 : ATSimplePermissions(cmd->subtype, rel,
4998 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4999 : ATT_FOREIGN_TABLE);
5000 : /* Set up recursion for phase 2; no other prep needed */
5001 56 : if (recurse)
5002 50 : cmd->recurse = true;
5003 56 : pass = AT_PASS_DROP;
5004 56 : break;
5005 274 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5006 274 : ATSimplePermissions(cmd->subtype, rel,
5007 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5008 : /* Set up recursion for phase 2; no other prep needed */
5009 268 : if (recurse)
5010 250 : cmd->recurse = true;
5011 268 : pass = AT_PASS_DROP;
5012 268 : break;
5013 414 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5014 414 : ATSimplePermissions(cmd->subtype, rel,
5015 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5016 : /* Set up recursion for phase 2; no other prep needed */
5017 408 : if (recurse)
5018 384 : cmd->recurse = true;
5019 408 : pass = AT_PASS_COL_ATTRS;
5020 408 : break;
5021 216 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5022 216 : ATSimplePermissions(cmd->subtype, rel,
5023 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5024 216 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5025 216 : pass = AT_PASS_SET_EXPRESSION;
5026 216 : break;
5027 86 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5028 86 : ATSimplePermissions(cmd->subtype, rel,
5029 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5030 86 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5031 86 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5032 62 : pass = AT_PASS_DROP;
5033 62 : break;
5034 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5035 164 : ATSimplePermissions(cmd->subtype, rel,
5036 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
5037 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
5038 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5039 : /* No command-specific prep needed */
5040 164 : pass = AT_PASS_MISC;
5041 164 : break;
5042 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5043 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5044 44 : ATSimplePermissions(cmd->subtype, rel,
5045 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5046 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5047 : /* This command never recurses */
5048 32 : pass = AT_PASS_MISC;
5049 32 : break;
5050 260 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5051 260 : ATSimplePermissions(cmd->subtype, rel,
5052 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5053 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5054 260 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5055 : /* No command-specific prep needed */
5056 260 : pass = AT_PASS_MISC;
5057 260 : break;
5058 78 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5059 78 : ATSimplePermissions(cmd->subtype, rel,
5060 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5061 : /* This command never recurses */
5062 : /* No command-specific prep needed */
5063 78 : pass = AT_PASS_MISC;
5064 78 : break;
5065 1658 : case AT_DropColumn: /* DROP COLUMN */
5066 1658 : ATSimplePermissions(cmd->subtype, rel,
5067 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5068 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5069 1652 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5070 : lockmode, context);
5071 : /* Recursion occurs during execution phase */
5072 1640 : pass = AT_PASS_DROP;
5073 1640 : break;
5074 0 : case AT_AddIndex: /* ADD INDEX */
5075 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5076 : /* This command never recurses */
5077 : /* No command-specific prep needed */
5078 0 : pass = AT_PASS_ADD_INDEX;
5079 0 : break;
5080 16066 : case AT_AddConstraint: /* ADD CONSTRAINT */
5081 16066 : ATSimplePermissions(cmd->subtype, rel,
5082 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5083 16066 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5084 16036 : if (recurse)
5085 : {
5086 : /* recurses at exec time; lock descendants and set flag */
5087 15668 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5088 15668 : cmd->recurse = true;
5089 : }
5090 16036 : pass = AT_PASS_ADD_CONSTR;
5091 16036 : break;
5092 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5093 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5094 : /* This command never recurses */
5095 : /* No command-specific prep needed */
5096 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5097 0 : break;
5098 818 : case AT_DropConstraint: /* DROP CONSTRAINT */
5099 818 : ATSimplePermissions(cmd->subtype, rel,
5100 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5101 818 : ATCheckPartitionsNotInUse(rel, lockmode);
5102 : /* Other recursion occurs during execution phase */
5103 : /* No command-specific prep needed except saving recurse flag */
5104 812 : if (recurse)
5105 776 : cmd->recurse = true;
5106 812 : pass = AT_PASS_DROP;
5107 812 : break;
5108 1318 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5109 1318 : ATSimplePermissions(cmd->subtype, rel,
5110 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5111 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5112 : /* See comments for ATPrepAlterColumnType */
5113 1318 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5114 : AT_PASS_UNSET, context);
5115 : Assert(cmd != NULL);
5116 : /* Performs own recursion */
5117 1312 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5118 : lockmode, context);
5119 1114 : pass = AT_PASS_ALTER_TYPE;
5120 1114 : break;
5121 172 : case AT_AlterColumnGenericOptions:
5122 172 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5123 : /* This command never recurses */
5124 : /* No command-specific prep needed */
5125 172 : pass = AT_PASS_MISC;
5126 172 : break;
5127 1998 : case AT_ChangeOwner: /* ALTER OWNER */
5128 : /* This command never recurses */
5129 : /* No command-specific prep needed */
5130 1998 : pass = AT_PASS_MISC;
5131 1998 : break;
5132 64 : case AT_ClusterOn: /* CLUSTER ON */
5133 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5134 64 : ATSimplePermissions(cmd->subtype, rel,
5135 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5136 : /* These commands never recurse */
5137 : /* No command-specific prep needed */
5138 64 : pass = AT_PASS_MISC;
5139 64 : break;
5140 112 : case AT_SetLogged: /* SET LOGGED */
5141 : case AT_SetUnLogged: /* SET UNLOGGED */
5142 112 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5143 100 : if (tab->chgPersistence)
5144 0 : ereport(ERROR,
5145 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5146 : errmsg("cannot change persistence setting twice")));
5147 100 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5148 88 : pass = AT_PASS_MISC;
5149 88 : break;
5150 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5151 6 : ATSimplePermissions(cmd->subtype, rel,
5152 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5153 6 : pass = AT_PASS_DROP;
5154 6 : break;
5155 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5156 128 : ATSimplePermissions(cmd->subtype, rel,
5157 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5158 :
5159 : /* check if another access method change was already requested */
5160 128 : if (tab->chgAccessMethod)
5161 18 : ereport(ERROR,
5162 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5163 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5164 :
5165 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5166 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5167 110 : break;
5168 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5169 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5170 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5171 : /* This command never recurses */
5172 158 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5173 158 : pass = AT_PASS_MISC; /* doesn't actually matter */
5174 158 : break;
5175 962 : case AT_SetRelOptions: /* SET (...) */
5176 : case AT_ResetRelOptions: /* RESET (...) */
5177 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5178 962 : ATSimplePermissions(cmd->subtype, rel,
5179 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5180 : ATT_MATVIEW | ATT_INDEX);
5181 : /* This command never recurses */
5182 : /* No command-specific prep needed */
5183 960 : pass = AT_PASS_MISC;
5184 960 : break;
5185 462 : case AT_AddInherit: /* INHERIT */
5186 462 : ATSimplePermissions(cmd->subtype, rel,
5187 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5188 : /* This command never recurses */
5189 462 : ATPrepAddInherit(rel);
5190 444 : pass = AT_PASS_MISC;
5191 444 : break;
5192 94 : case AT_DropInherit: /* NO INHERIT */
5193 94 : ATSimplePermissions(cmd->subtype, rel,
5194 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5195 : /* This command never recurses */
5196 : /* No command-specific prep needed */
5197 94 : pass = AT_PASS_MISC;
5198 94 : break;
5199 294 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5200 294 : ATSimplePermissions(cmd->subtype, rel,
5201 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5202 : /* Recursion occurs during execution phase */
5203 288 : if (recurse)
5204 288 : cmd->recurse = true;
5205 288 : pass = AT_PASS_MISC;
5206 288 : break;
5207 476 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5208 476 : ATSimplePermissions(cmd->subtype, rel,
5209 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5210 : /* Recursion occurs during execution phase */
5211 : /* No command-specific prep needed except saving recurse flag */
5212 476 : if (recurse)
5213 476 : cmd->recurse = true;
5214 476 : pass = AT_PASS_MISC;
5215 476 : break;
5216 494 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5217 494 : ATSimplePermissions(cmd->subtype, rel,
5218 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5219 494 : pass = AT_PASS_MISC;
5220 : /* This command never recurses */
5221 : /* No command-specific prep needed */
5222 494 : break;
5223 342 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5224 : case AT_EnableAlwaysTrig:
5225 : case AT_EnableReplicaTrig:
5226 : case AT_EnableTrigAll:
5227 : case AT_EnableTrigUser:
5228 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5229 : case AT_DisableTrigAll:
5230 : case AT_DisableTrigUser:
5231 342 : ATSimplePermissions(cmd->subtype, rel,
5232 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5233 : /* Set up recursion for phase 2; no other prep needed */
5234 342 : if (recurse)
5235 314 : cmd->recurse = true;
5236 342 : pass = AT_PASS_MISC;
5237 342 : break;
5238 586 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5239 : case AT_EnableAlwaysRule:
5240 : case AT_EnableReplicaRule:
5241 : case AT_DisableRule:
5242 : case AT_AddOf: /* OF */
5243 : case AT_DropOf: /* NOT OF */
5244 : case AT_EnableRowSecurity:
5245 : case AT_DisableRowSecurity:
5246 : case AT_ForceRowSecurity:
5247 : case AT_NoForceRowSecurity:
5248 586 : ATSimplePermissions(cmd->subtype, rel,
5249 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5250 : /* These commands never recurse */
5251 : /* No command-specific prep needed */
5252 586 : pass = AT_PASS_MISC;
5253 586 : break;
5254 58 : case AT_GenericOptions:
5255 58 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5256 : /* No command-specific prep needed */
5257 58 : pass = AT_PASS_MISC;
5258 58 : break;
5259 2804 : case AT_AttachPartition:
5260 2804 : ATSimplePermissions(cmd->subtype, rel,
5261 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5262 : /* No command-specific prep needed */
5263 2798 : pass = AT_PASS_MISC;
5264 2798 : break;
5265 596 : case AT_DetachPartition:
5266 596 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5267 : /* No command-specific prep needed */
5268 578 : pass = AT_PASS_MISC;
5269 578 : break;
5270 20 : case AT_DetachPartitionFinalize:
5271 20 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5272 : /* No command-specific prep needed */
5273 14 : pass = AT_PASS_MISC;
5274 14 : break;
5275 0 : default: /* oops */
5276 0 : elog(ERROR, "unrecognized alter table type: %d",
5277 : (int) cmd->subtype);
5278 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5279 : break;
5280 : }
5281 : Assert(pass > AT_PASS_UNSET);
5282 :
5283 : /* Add the subcommand to the appropriate list for phase 2 */
5284 33980 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5285 33980 : }
5286 :
5287 : /*
5288 : * ATRewriteCatalogs
5289 : *
5290 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5291 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5292 : * conflicts).
5293 : */
5294 : static void
5295 31862 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5296 : AlterTableUtilityContext *context)
5297 : {
5298 : ListCell *ltab;
5299 :
5300 : /*
5301 : * We process all the tables "in parallel", one pass at a time. This is
5302 : * needed because we may have to propagate work from one table to another
5303 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5304 : * re-adding of the foreign key constraint to the other table). Work can
5305 : * only be propagated into later passes, however.
5306 : */
5307 401610 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5308 : {
5309 : /* Go through each table that needs to be processed */
5310 758382 : foreach(ltab, *wqueue)
5311 : {
5312 388634 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5313 388634 : List *subcmds = tab->subcmds[pass];
5314 : ListCell *lcmd;
5315 :
5316 388634 : if (subcmds == NIL)
5317 333198 : continue;
5318 :
5319 : /*
5320 : * Open the relation and store it in tab. This allows subroutines
5321 : * close and reopen, if necessary. Appropriate lock was obtained
5322 : * by phase 1, needn't get it again.
5323 : */
5324 55436 : tab->rel = relation_open(tab->relid, NoLock);
5325 :
5326 111756 : foreach(lcmd, subcmds)
5327 59142 : ATExecCmd(wqueue, tab,
5328 59142 : lfirst_node(AlterTableCmd, lcmd),
5329 : lockmode, pass, context);
5330 :
5331 : /*
5332 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5333 : * (this is not done in ATExecAlterColumnType since it should be
5334 : * done only once if multiple columns of a table are altered).
5335 : */
5336 52614 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5337 1180 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5338 :
5339 52614 : if (tab->rel)
5340 : {
5341 52614 : relation_close(tab->rel, NoLock);
5342 52614 : tab->rel = NULL;
5343 : }
5344 : }
5345 : }
5346 :
5347 : /* Check to see if a toast table must be added. */
5348 62306 : foreach(ltab, *wqueue)
5349 : {
5350 33266 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5351 :
5352 : /*
5353 : * If the table is source table of ATTACH PARTITION command, we did
5354 : * not modify anything about it that will change its toasting
5355 : * requirement, so no need to check.
5356 : */
5357 33266 : if (((tab->relkind == RELKIND_RELATION ||
5358 6102 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5359 31362 : tab->partition_constraint == NULL) ||
5360 3974 : tab->relkind == RELKIND_MATVIEW)
5361 29342 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5362 : }
5363 29040 : }
5364 :
5365 : /*
5366 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5367 : */
5368 : static void
5369 59142 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5370 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5371 : AlterTableUtilityContext *context)
5372 : {
5373 59142 : ObjectAddress address = InvalidObjectAddress;
5374 59142 : Relation rel = tab->rel;
5375 :
5376 59142 : switch (cmd->subtype)
5377 : {
5378 2172 : case AT_AddColumn: /* ADD COLUMN */
5379 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5380 2172 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5381 2172 : cmd->recurse, false,
5382 : lockmode, cur_pass, context);
5383 2034 : break;
5384 584 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5385 584 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5386 518 : break;
5387 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5388 80 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5389 80 : break;
5390 166 : case AT_AddIdentity:
5391 166 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5392 : cur_pass, context);
5393 : Assert(cmd != NULL);
5394 154 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5395 106 : break;
5396 62 : case AT_SetIdentity:
5397 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5398 : cur_pass, context);
5399 : Assert(cmd != NULL);
5400 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5401 38 : break;
5402 56 : case AT_DropIdentity:
5403 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5404 38 : break;
5405 268 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5406 268 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5407 166 : break;
5408 408 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5409 408 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5410 408 : cmd->recurse, false, lockmode);
5411 378 : break;
5412 216 : case AT_SetExpression:
5413 216 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5414 186 : break;
5415 56 : case AT_DropExpression:
5416 56 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5417 32 : break;
5418 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5419 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5420 116 : break;
5421 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5422 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5423 26 : break;
5424 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5425 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5426 6 : break;
5427 260 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5428 260 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5429 248 : break;
5430 78 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5431 78 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5432 : lockmode);
5433 72 : break;
5434 1640 : case AT_DropColumn: /* DROP COLUMN */
5435 1640 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5436 1640 : cmd->behavior, cmd->recurse, false,
5437 1640 : cmd->missing_ok, lockmode,
5438 : NULL);
5439 1460 : break;
5440 1178 : case AT_AddIndex: /* ADD INDEX */
5441 1178 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5442 : lockmode);
5443 1008 : break;
5444 456 : case AT_ReAddIndex: /* ADD INDEX */
5445 456 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5446 : lockmode);
5447 456 : break;
5448 26 : case AT_ReAddStatistics: /* ADD STATISTICS */
5449 26 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5450 : true, lockmode);
5451 26 : break;
5452 28534 : case AT_AddConstraint: /* ADD CONSTRAINT */
5453 : /* Transform the command only during initial examination */
5454 28534 : if (cur_pass == AT_PASS_ADD_CONSTR)
5455 16006 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5456 16036 : cmd->recurse, lockmode,
5457 : cur_pass, context);
5458 : /* Depending on constraint type, might be no more work to do now */
5459 28504 : if (cmd != NULL)
5460 : address =
5461 12498 : ATExecAddConstraint(wqueue, tab, rel,
5462 12498 : (Constraint *) cmd->def,
5463 12498 : cmd->recurse, false, lockmode);
5464 27824 : break;
5465 338 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5466 : address =
5467 338 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5468 : true, true, lockmode);
5469 326 : break;
5470 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5471 : * constraint */
5472 : address =
5473 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5474 14 : ((AlterDomainStmt *) cmd->def)->def,
5475 : NULL);
5476 8 : break;
5477 78 : case AT_ReAddComment: /* Re-add existing comment */
5478 78 : address = CommentObject((CommentStmt *) cmd->def);
5479 78 : break;
5480 10640 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5481 10640 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5482 : lockmode);
5483 10628 : break;
5484 288 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5485 288 : address = ATExecAlterConstraint(wqueue, rel,
5486 288 : castNode(ATAlterConstraint, cmd->def),
5487 288 : cmd->recurse, lockmode);
5488 222 : break;
5489 476 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5490 476 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5491 : false, lockmode);
5492 470 : break;
5493 812 : case AT_DropConstraint: /* DROP CONSTRAINT */
5494 812 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5495 812 : cmd->recurse,
5496 812 : cmd->missing_ok, lockmode);
5497 602 : break;
5498 1078 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5499 : /* parse transformation was done earlier */
5500 1078 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5501 1036 : break;
5502 172 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5503 : address =
5504 172 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5505 172 : (List *) cmd->def, lockmode);
5506 166 : break;
5507 1998 : case AT_ChangeOwner: /* ALTER OWNER */
5508 1992 : ATExecChangeOwner(RelationGetRelid(rel),
5509 1998 : get_rolespec_oid(cmd->newowner, false),
5510 : false, lockmode);
5511 1980 : break;
5512 64 : case AT_ClusterOn: /* CLUSTER ON */
5513 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5514 58 : break;
5515 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5516 18 : ATExecDropCluster(rel, lockmode);
5517 12 : break;
5518 88 : case AT_SetLogged: /* SET LOGGED */
5519 : case AT_SetUnLogged: /* SET UNLOGGED */
5520 88 : break;
5521 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5522 : /* nothing to do here, oid columns don't exist anymore */
5523 6 : break;
5524 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5525 :
5526 : /*
5527 : * Only do this for partitioned tables, for which this is just a
5528 : * catalog change. Tables with storage are handled by Phase 3.
5529 : */
5530 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5531 50 : tab->chgAccessMethod)
5532 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5533 92 : break;
5534 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5535 :
5536 : /*
5537 : * Only do this for partitioned tables and indexes, for which this
5538 : * is just a catalog change. Other relation types which have
5539 : * storage are handled by Phase 3.
5540 : */
5541 158 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5542 146 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5543 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5544 :
5545 152 : break;
5546 960 : case AT_SetRelOptions: /* SET (...) */
5547 : case AT_ResetRelOptions: /* RESET (...) */
5548 : case AT_ReplaceRelOptions: /* replace entire option list */
5549 960 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5550 908 : break;
5551 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5552 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5553 : TRIGGER_FIRES_ON_ORIGIN, false,
5554 122 : cmd->recurse,
5555 : lockmode);
5556 122 : break;
5557 42 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5558 42 : ATExecEnableDisableTrigger(rel, cmd->name,
5559 : TRIGGER_FIRES_ALWAYS, false,
5560 42 : cmd->recurse,
5561 : lockmode);
5562 42 : break;
5563 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5564 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5565 : TRIGGER_FIRES_ON_REPLICA, false,
5566 16 : cmd->recurse,
5567 : lockmode);
5568 16 : break;
5569 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5570 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5571 : TRIGGER_DISABLED, false,
5572 138 : cmd->recurse,
5573 : lockmode);
5574 138 : break;
5575 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5576 0 : ATExecEnableDisableTrigger(rel, NULL,
5577 : TRIGGER_FIRES_ON_ORIGIN, false,
5578 0 : cmd->recurse,
5579 : lockmode);
5580 0 : break;
5581 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5582 12 : ATExecEnableDisableTrigger(rel, NULL,
5583 : TRIGGER_DISABLED, false,
5584 12 : cmd->recurse,
5585 : lockmode);
5586 12 : break;
5587 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5588 0 : ATExecEnableDisableTrigger(rel, NULL,
5589 : TRIGGER_FIRES_ON_ORIGIN, true,
5590 0 : cmd->recurse,
5591 : lockmode);
5592 0 : break;
5593 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5594 12 : ATExecEnableDisableTrigger(rel, NULL,
5595 : TRIGGER_DISABLED, true,
5596 12 : cmd->recurse,
5597 : lockmode);
5598 12 : break;
5599 :
5600 8 : case AT_EnableRule: /* ENABLE RULE name */
5601 8 : ATExecEnableDisableRule(rel, cmd->name,
5602 : RULE_FIRES_ON_ORIGIN, lockmode);
5603 8 : break;
5604 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5605 0 : ATExecEnableDisableRule(rel, cmd->name,
5606 : RULE_FIRES_ALWAYS, lockmode);
5607 0 : break;
5608 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5609 6 : ATExecEnableDisableRule(rel, cmd->name,
5610 : RULE_FIRES_ON_REPLICA, lockmode);
5611 6 : break;
5612 32 : case AT_DisableRule: /* DISABLE RULE name */
5613 32 : ATExecEnableDisableRule(rel, cmd->name,
5614 : RULE_DISABLED, lockmode);
5615 32 : break;
5616 :
5617 444 : case AT_AddInherit:
5618 444 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5619 324 : break;
5620 94 : case AT_DropInherit:
5621 94 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5622 88 : break;
5623 66 : case AT_AddOf:
5624 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5625 30 : break;
5626 6 : case AT_DropOf:
5627 6 : ATExecDropOf(rel, lockmode);
5628 6 : break;
5629 512 : case AT_ReplicaIdentity:
5630 512 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5631 464 : break;
5632 326 : case AT_EnableRowSecurity:
5633 326 : ATExecSetRowSecurity(rel, true);
5634 326 : break;
5635 10 : case AT_DisableRowSecurity:
5636 10 : ATExecSetRowSecurity(rel, false);
5637 10 : break;
5638 100 : case AT_ForceRowSecurity:
5639 100 : ATExecForceNoForceRowSecurity(rel, true);
5640 100 : break;
5641 32 : case AT_NoForceRowSecurity:
5642 32 : ATExecForceNoForceRowSecurity(rel, false);
5643 32 : break;
5644 58 : case AT_GenericOptions:
5645 58 : ATExecGenericOptions(rel, (List *) cmd->def);
5646 56 : break;
5647 2798 : case AT_AttachPartition:
5648 2798 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5649 : cur_pass, context);
5650 : Assert(cmd != NULL);
5651 2774 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5652 2394 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5653 : context);
5654 : else
5655 380 : address = ATExecAttachPartitionIdx(wqueue, rel,
5656 380 : ((PartitionCmd *) cmd->def)->name);
5657 2384 : break;
5658 578 : case AT_DetachPartition:
5659 578 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5660 : cur_pass, context);
5661 : Assert(cmd != NULL);
5662 : /* ATPrepCmd ensures it must be a table */
5663 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5664 578 : address = ATExecDetachPartition(wqueue, tab, rel,
5665 578 : ((PartitionCmd *) cmd->def)->name,
5666 578 : ((PartitionCmd *) cmd->def)->concurrent);
5667 448 : break;
5668 14 : case AT_DetachPartitionFinalize:
5669 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5670 14 : break;
5671 0 : default: /* oops */
5672 0 : elog(ERROR, "unrecognized alter table type: %d",
5673 : (int) cmd->subtype);
5674 : break;
5675 : }
5676 :
5677 : /*
5678 : * Report the subcommand to interested event triggers.
5679 : */
5680 56320 : if (cmd)
5681 40314 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5682 :
5683 : /*
5684 : * Bump the command counter to ensure the next subcommand in the sequence
5685 : * can see the changes so far
5686 : */
5687 56320 : CommandCounterIncrement();
5688 56320 : }
5689 :
5690 : /*
5691 : * ATParseTransformCmd: perform parse transformation for one subcommand
5692 : *
5693 : * Returns the transformed subcommand tree, if there is one, else NULL.
5694 : *
5695 : * The parser may hand back additional AlterTableCmd(s) and/or other
5696 : * utility statements, either before or after the original subcommand.
5697 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5698 : * AlteredTableInfo (they had better be for later passes than the current one).
5699 : * Utility statements that are supposed to happen before the AlterTableCmd
5700 : * are executed immediately. Those that are supposed to happen afterwards
5701 : * are added to the tab->afterStmts list to be done at the very end.
5702 : */
5703 : static AlterTableCmd *
5704 23010 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5705 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5706 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5707 : {
5708 23010 : AlterTableCmd *newcmd = NULL;
5709 23010 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5710 : List *beforeStmts;
5711 : List *afterStmts;
5712 : ListCell *lc;
5713 :
5714 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5715 23010 : atstmt->relation =
5716 23010 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5717 23010 : pstrdup(RelationGetRelationName(rel)),
5718 : -1);
5719 23010 : atstmt->relation->inh = recurse;
5720 23010 : atstmt->cmds = list_make1(cmd);
5721 23010 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5722 23010 : atstmt->missing_ok = false;
5723 :
5724 : /* Transform the AlterTableStmt */
5725 23010 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5726 : atstmt,
5727 : context->queryString,
5728 : &beforeStmts,
5729 : &afterStmts);
5730 :
5731 : /* Execute any statements that should happen before these subcommand(s) */
5732 23418 : foreach(lc, beforeStmts)
5733 : {
5734 486 : Node *stmt = (Node *) lfirst(lc);
5735 :
5736 486 : ProcessUtilityForAlterTable(stmt, context);
5737 474 : CommandCounterIncrement();
5738 : }
5739 :
5740 : /* Examine the transformed subcommands and schedule them appropriately */
5741 54210 : foreach(lc, atstmt->cmds)
5742 : {
5743 31278 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5744 : AlterTablePass pass;
5745 :
5746 : /*
5747 : * This switch need only cover the subcommand types that can be added
5748 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5749 : * executing the subcommand immediately, as a substitute for the
5750 : * original subcommand. (Note, however, that this does cause
5751 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5752 : * which is important for index and foreign key constraints.)
5753 : *
5754 : * We assume we needn't do any phase-1 checks for added subcommands.
5755 : */
5756 31278 : switch (cmd2->subtype)
5757 : {
5758 1202 : case AT_AddIndex:
5759 1202 : pass = AT_PASS_ADD_INDEX;
5760 1202 : break;
5761 10640 : case AT_AddIndexConstraint:
5762 10640 : pass = AT_PASS_ADD_INDEXCONSTR;
5763 10640 : break;
5764 12510 : case AT_AddConstraint:
5765 : /* Recursion occurs during execution phase */
5766 12510 : if (recurse)
5767 12462 : cmd2->recurse = true;
5768 12510 : switch (castNode(Constraint, cmd2->def)->contype)
5769 : {
5770 8984 : case CONSTR_NOTNULL:
5771 8984 : pass = AT_PASS_COL_ATTRS;
5772 8984 : break;
5773 0 : case CONSTR_PRIMARY:
5774 : case CONSTR_UNIQUE:
5775 : case CONSTR_EXCLUSION:
5776 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5777 0 : break;
5778 3526 : default:
5779 3526 : pass = AT_PASS_ADD_OTHERCONSTR;
5780 3526 : break;
5781 : }
5782 12510 : break;
5783 0 : case AT_AlterColumnGenericOptions:
5784 : /* This command never recurses */
5785 : /* No command-specific prep needed */
5786 0 : pass = AT_PASS_MISC;
5787 0 : break;
5788 6926 : default:
5789 6926 : pass = cur_pass;
5790 6926 : break;
5791 : }
5792 :
5793 31278 : if (pass < cur_pass)
5794 : {
5795 : /* Cannot schedule into a pass we already finished */
5796 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5797 : pass);
5798 : }
5799 31278 : else if (pass > cur_pass)
5800 : {
5801 : /* OK, queue it up for later */
5802 24352 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5803 : }
5804 : else
5805 : {
5806 : /*
5807 : * We should see at most one subcommand for the current pass,
5808 : * which is the transformed version of the original subcommand.
5809 : */
5810 6926 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5811 : {
5812 : /* Found the transformed version of our subcommand */
5813 6926 : newcmd = cmd2;
5814 : }
5815 : else
5816 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5817 : pass);
5818 : }
5819 : }
5820 :
5821 : /* Queue up any after-statements to happen at the end */
5822 22932 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5823 :
5824 22932 : return newcmd;
5825 : }
5826 :
5827 : /*
5828 : * ATRewriteTables: ALTER TABLE phase 3
5829 : */
5830 : static void
5831 29040 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5832 : AlterTableUtilityContext *context)
5833 : {
5834 : ListCell *ltab;
5835 :
5836 : /* Go through each table that needs to be checked or rewritten */
5837 61858 : foreach(ltab, *wqueue)
5838 : {
5839 33200 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5840 :
5841 : /* Relations without storage may be ignored here */
5842 33200 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5843 5794 : continue;
5844 :
5845 : /*
5846 : * If we change column data types, the operation has to be propagated
5847 : * to tables that use this table's rowtype as a column type.
5848 : * tab->newvals will also be non-NULL in the case where we're adding a
5849 : * column with a default. We choose to forbid that case as well,
5850 : * since composite types might eventually support defaults.
5851 : *
5852 : * (Eventually we'll probably need to check for composite type
5853 : * dependencies even when we're just scanning the table without a
5854 : * rewrite, but at the moment a composite type does not enforce any
5855 : * constraints, so it's not necessary/appropriate to enforce them just
5856 : * during ALTER.)
5857 : */
5858 27406 : if (tab->newvals != NIL || tab->rewrite > 0)
5859 : {
5860 : Relation rel;
5861 :
5862 1736 : rel = table_open(tab->relid, NoLock);
5863 1736 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5864 1682 : table_close(rel, NoLock);
5865 : }
5866 :
5867 : /*
5868 : * We only need to rewrite the table if at least one column needs to
5869 : * be recomputed, or we are changing its persistence or access method.
5870 : *
5871 : * There are two reasons for requiring a rewrite when changing
5872 : * persistence: on one hand, we need to ensure that the buffers
5873 : * belonging to each of the two relations are marked with or without
5874 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5875 : * and assigns a new relfilenumber, we automatically create or drop an
5876 : * init fork for the relation as appropriate.
5877 : */
5878 27352 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5879 934 : {
5880 : /* Build a temporary relation and copy data */
5881 : Relation OldHeap;
5882 : Oid OIDNewHeap;
5883 : Oid NewAccessMethod;
5884 : Oid NewTableSpace;
5885 : char persistence;
5886 :
5887 990 : OldHeap = table_open(tab->relid, NoLock);
5888 :
5889 : /*
5890 : * We don't support rewriting of system catalogs; there are too
5891 : * many corner cases and too little benefit. In particular this
5892 : * is certainly not going to work for mapped catalogs.
5893 : */
5894 990 : if (IsSystemRelation(OldHeap))
5895 0 : ereport(ERROR,
5896 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5897 : errmsg("cannot rewrite system relation \"%s\"",
5898 : RelationGetRelationName(OldHeap))));
5899 :
5900 990 : if (RelationIsUsedAsCatalogTable(OldHeap))
5901 2 : ereport(ERROR,
5902 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5903 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5904 : RelationGetRelationName(OldHeap))));
5905 :
5906 : /*
5907 : * Don't allow rewrite on temp tables of other backends ... their
5908 : * local buffer manager is not going to cope. (This is redundant
5909 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5910 : * check here too.)
5911 : */
5912 988 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5913 0 : ereport(ERROR,
5914 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5915 : errmsg("cannot rewrite temporary tables of other sessions")));
5916 :
5917 : /*
5918 : * Select destination tablespace (same as original unless user
5919 : * requested a change)
5920 : */
5921 988 : if (tab->newTableSpace)
5922 0 : NewTableSpace = tab->newTableSpace;
5923 : else
5924 988 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5925 :
5926 : /*
5927 : * Select destination access method (same as original unless user
5928 : * requested a change)
5929 : */
5930 988 : if (tab->chgAccessMethod)
5931 36 : NewAccessMethod = tab->newAccessMethod;
5932 : else
5933 952 : NewAccessMethod = OldHeap->rd_rel->relam;
5934 :
5935 : /*
5936 : * Select persistence of transient table (same as original unless
5937 : * user requested a change)
5938 : */
5939 988 : persistence = tab->chgPersistence ?
5940 936 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5941 :
5942 988 : table_close(OldHeap, NoLock);
5943 :
5944 : /*
5945 : * Fire off an Event Trigger now, before actually rewriting the
5946 : * table.
5947 : *
5948 : * We don't support Event Trigger for nested commands anywhere,
5949 : * here included, and parsetree is given NULL when coming from
5950 : * AlterTableInternal.
5951 : *
5952 : * And fire it only once.
5953 : */
5954 988 : if (parsetree)
5955 988 : EventTriggerTableRewrite((Node *) parsetree,
5956 : tab->relid,
5957 : tab->rewrite);
5958 :
5959 : /*
5960 : * Create transient table that will receive the modified data.
5961 : *
5962 : * Ensure it is marked correctly as logged or unlogged. We have
5963 : * to do this here so that buffers for the new relfilenumber will
5964 : * have the right persistence set, and at the same time ensure
5965 : * that the original filenumbers's buffers will get read in with
5966 : * the correct setting (i.e. the original one). Otherwise a
5967 : * rollback after the rewrite would possibly result with buffers
5968 : * for the original filenumbers having the wrong persistence
5969 : * setting.
5970 : *
5971 : * NB: This relies on swap_relation_files() also swapping the
5972 : * persistence. That wouldn't work for pg_class, but that can't be
5973 : * unlogged anyway.
5974 : */
5975 982 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5976 : persistence, lockmode);
5977 :
5978 : /*
5979 : * Copy the heap data into the new table with the desired
5980 : * modifications, and test the current data within the table
5981 : * against new constraints generated by ALTER TABLE commands.
5982 : */
5983 982 : ATRewriteTable(tab, OIDNewHeap);
5984 :
5985 : /*
5986 : * Swap the physical files of the old and new heaps, then rebuild
5987 : * indexes and discard the old heap. We can use RecentXmin for
5988 : * the table's new relfrozenxid because we rewrote all the tuples
5989 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5990 : * we never try to swap toast tables by content, since we have no
5991 : * interest in letting this code work on system catalogs.
5992 : */
5993 940 : finish_heap_swap(tab->relid, OIDNewHeap,
5994 : false, false, true,
5995 940 : !OidIsValid(tab->newTableSpace),
5996 : RecentXmin,
5997 : ReadNextMultiXactId(),
5998 : persistence);
5999 :
6000 934 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
6001 : }
6002 26362 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6003 : {
6004 24 : if (tab->chgPersistence)
6005 24 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
6006 : }
6007 : else
6008 : {
6009 : /*
6010 : * If required, test the current data within the table against new
6011 : * constraints generated by ALTER TABLE commands, but don't
6012 : * rebuild data.
6013 : */
6014 26338 : if (tab->constraints != NIL || tab->verify_new_notnull ||
6015 23460 : tab->partition_constraint != NULL)
6016 4794 : ATRewriteTable(tab, InvalidOid);
6017 :
6018 : /*
6019 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
6020 : * just do a block-by-block copy.
6021 : */
6022 26066 : if (tab->newTableSpace)
6023 122 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6024 : }
6025 :
6026 : /*
6027 : * Also change persistence of owned sequences, so that it matches the
6028 : * table persistence.
6029 : */
6030 27024 : if (tab->chgPersistence)
6031 : {
6032 76 : List *seqlist = getOwnedSequences(tab->relid);
6033 : ListCell *lc;
6034 :
6035 124 : foreach(lc, seqlist)
6036 : {
6037 48 : Oid seq_relid = lfirst_oid(lc);
6038 :
6039 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
6040 : }
6041 : }
6042 : }
6043 :
6044 : /*
6045 : * Foreign key constraints are checked in a final pass, since (a) it's
6046 : * generally best to examine each one separately, and (b) it's at least
6047 : * theoretically possible that we have changed both relations of the
6048 : * foreign key, and we'd better have finished both rewrites before we try
6049 : * to read the tables.
6050 : */
6051 61218 : foreach(ltab, *wqueue)
6052 : {
6053 32652 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6054 32652 : Relation rel = NULL;
6055 : ListCell *lcon;
6056 :
6057 : /* Relations without storage may be ignored here too */
6058 32652 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6059 5690 : continue;
6060 :
6061 28766 : foreach(lcon, tab->constraints)
6062 : {
6063 1896 : NewConstraint *con = lfirst(lcon);
6064 :
6065 1896 : if (con->contype == CONSTR_FOREIGN)
6066 : {
6067 1166 : Constraint *fkconstraint = (Constraint *) con->qual;
6068 : Relation refrel;
6069 :
6070 1166 : if (rel == NULL)
6071 : {
6072 : /* Long since locked, no need for another */
6073 1154 : rel = table_open(tab->relid, NoLock);
6074 : }
6075 :
6076 1166 : refrel = table_open(con->refrelid, RowShareLock);
6077 :
6078 1166 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6079 : con->refindid,
6080 : con->conid,
6081 1166 : con->conwithperiod);
6082 :
6083 : /*
6084 : * No need to mark the constraint row as validated, we did
6085 : * that when we inserted the row earlier.
6086 : */
6087 :
6088 1074 : table_close(refrel, NoLock);
6089 : }
6090 : }
6091 :
6092 26870 : if (rel)
6093 1062 : table_close(rel, NoLock);
6094 : }
6095 :
6096 : /* Finally, run any afterStmts that were queued up */
6097 61082 : foreach(ltab, *wqueue)
6098 : {
6099 32516 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6100 : ListCell *lc;
6101 :
6102 32602 : foreach(lc, tab->afterStmts)
6103 : {
6104 86 : Node *stmt = (Node *) lfirst(lc);
6105 :
6106 86 : ProcessUtilityForAlterTable(stmt, context);
6107 86 : CommandCounterIncrement();
6108 : }
6109 : }
6110 28566 : }
6111 :
6112 : /*
6113 : * ATRewriteTable: scan or rewrite one table
6114 : *
6115 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6116 : * must already hold AccessExclusiveLock on it.
6117 : */
6118 : static void
6119 5776 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6120 : {
6121 : Relation oldrel;
6122 : Relation newrel;
6123 : TupleDesc oldTupDesc;
6124 : TupleDesc newTupDesc;
6125 5776 : bool needscan = false;
6126 : List *notnull_attrs;
6127 : List *notnull_virtual_attrs;
6128 : int i;
6129 : ListCell *l;
6130 : EState *estate;
6131 : CommandId mycid;
6132 : BulkInsertState bistate;
6133 : int ti_options;
6134 5776 : ExprState *partqualstate = NULL;
6135 :
6136 : /*
6137 : * Open the relation(s). We have surely already locked the existing
6138 : * table.
6139 : */
6140 5776 : oldrel = table_open(tab->relid, NoLock);
6141 5776 : oldTupDesc = tab->oldDesc;
6142 5776 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6143 :
6144 5776 : if (OidIsValid(OIDNewHeap))
6145 : {
6146 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6147 : false));
6148 982 : newrel = table_open(OIDNewHeap, NoLock);
6149 : }
6150 : else
6151 4794 : newrel = NULL;
6152 :
6153 : /*
6154 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6155 : * is empty, so don't bother using it.
6156 : */
6157 5776 : if (newrel)
6158 : {
6159 982 : mycid = GetCurrentCommandId(true);
6160 982 : bistate = GetBulkInsertState();
6161 982 : ti_options = TABLE_INSERT_SKIP_FSM;
6162 : }
6163 : else
6164 : {
6165 : /* keep compiler quiet about using these uninitialized */
6166 4794 : mycid = 0;
6167 4794 : bistate = NULL;
6168 4794 : ti_options = 0;
6169 : }
6170 :
6171 : /*
6172 : * Generate the constraint and default execution states
6173 : */
6174 :
6175 5776 : estate = CreateExecutorState();
6176 :
6177 : /* Build the needed expression execution states */
6178 7792 : foreach(l, tab->constraints)
6179 : {
6180 2016 : NewConstraint *con = lfirst(l);
6181 :
6182 2016 : switch (con->contype)
6183 : {
6184 844 : case CONSTR_CHECK:
6185 844 : needscan = true;
6186 844 : con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6187 844 : break;
6188 1172 : case CONSTR_FOREIGN:
6189 : /* Nothing to do here */
6190 1172 : break;
6191 0 : default:
6192 0 : elog(ERROR, "unrecognized constraint type: %d",
6193 : (int) con->contype);
6194 : }
6195 : }
6196 :
6197 : /* Build expression execution states for partition check quals */
6198 5776 : if (tab->partition_constraint)
6199 : {
6200 2064 : needscan = true;
6201 2064 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6202 : }
6203 :
6204 6820 : foreach(l, tab->newvals)
6205 : {
6206 1044 : NewColumnValue *ex = lfirst(l);
6207 :
6208 : /* expr already planned */
6209 1044 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6210 : }
6211 :
6212 5776 : notnull_attrs = notnull_virtual_attrs = NIL;
6213 5776 : if (newrel || tab->verify_new_notnull)
6214 : {
6215 : /*
6216 : * If we are rebuilding the tuples OR if we added any new but not
6217 : * verified not-null constraints, check all *valid* not-null
6218 : * constraints. This is a bit of overkill but it minimizes risk of
6219 : * bugs.
6220 : *
6221 : * notnull_attrs does *not* collect attribute numbers for valid
6222 : * not-null constraints over virtual generated columns; instead, they
6223 : * are collected in notnull_virtual_attrs for verification elsewhere.
6224 : */
6225 7502 : for (i = 0; i < newTupDesc->natts; i++)
6226 : {
6227 5488 : CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6228 :
6229 5488 : if (attr->attnullability == ATTNULLABLE_VALID &&
6230 2112 : !attr->attisdropped)
6231 : {
6232 2112 : Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6233 :
6234 2112 : if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6235 2022 : notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6236 : else
6237 90 : notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6238 90 : wholeatt->attnum);
6239 : }
6240 : }
6241 2014 : if (notnull_attrs || notnull_virtual_attrs)
6242 1550 : needscan = true;
6243 : }
6244 :
6245 5776 : if (newrel || needscan)
6246 : {
6247 : ExprContext *econtext;
6248 : TupleTableSlot *oldslot;
6249 : TupleTableSlot *newslot;
6250 : TableScanDesc scan;
6251 : MemoryContext oldCxt;
6252 4806 : List *dropped_attrs = NIL;
6253 : ListCell *lc;
6254 : Snapshot snapshot;
6255 4806 : ResultRelInfo *rInfo = NULL;
6256 :
6257 : /*
6258 : * When adding or changing a virtual generated column with a not-null
6259 : * constraint, we need to evaluate whether the generation expression
6260 : * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6261 : * prepare a dummy ResultRelInfo.
6262 : */
6263 4806 : if (notnull_virtual_attrs != NIL)
6264 : {
6265 : MemoryContext oldcontext;
6266 :
6267 : Assert(newTupDesc->constr->has_generated_virtual);
6268 : Assert(newTupDesc->constr->has_not_null);
6269 60 : oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6270 60 : rInfo = makeNode(ResultRelInfo);
6271 60 : InitResultRelInfo(rInfo,
6272 : oldrel,
6273 : 0, /* dummy rangetable index */
6274 : NULL,
6275 : estate->es_instrument);
6276 60 : MemoryContextSwitchTo(oldcontext);
6277 : }
6278 :
6279 4806 : if (newrel)
6280 982 : ereport(DEBUG1,
6281 : (errmsg_internal("rewriting table \"%s\"",
6282 : RelationGetRelationName(oldrel))));
6283 : else
6284 3824 : ereport(DEBUG1,
6285 : (errmsg_internal("verifying table \"%s\"",
6286 : RelationGetRelationName(oldrel))));
6287 :
6288 4806 : if (newrel)
6289 : {
6290 : /*
6291 : * All predicate locks on the tuples or pages are about to be made
6292 : * invalid, because we move tuples around. Promote them to
6293 : * relation locks.
6294 : */
6295 982 : TransferPredicateLocksToHeapRelation(oldrel);
6296 : }
6297 :
6298 4806 : econtext = GetPerTupleExprContext(estate);
6299 :
6300 : /*
6301 : * Create necessary tuple slots. When rewriting, two slots are needed,
6302 : * otherwise one suffices. In the case where one slot suffices, we
6303 : * need to use the new tuple descriptor, otherwise some constraints
6304 : * can't be evaluated. Note that even when the tuple layout is the
6305 : * same and no rewrite is required, the tupDescs might not be
6306 : * (consider ADD COLUMN without a default).
6307 : */
6308 4806 : if (tab->rewrite)
6309 : {
6310 : Assert(newrel != NULL);
6311 982 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6312 : table_slot_callbacks(oldrel));
6313 982 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6314 : table_slot_callbacks(newrel));
6315 :
6316 : /*
6317 : * Set all columns in the new slot to NULL initially, to ensure
6318 : * columns added as part of the rewrite are initialized to NULL.
6319 : * That is necessary as tab->newvals will not contain an
6320 : * expression for columns with a NULL default, e.g. when adding a
6321 : * column without a default together with a column with a default
6322 : * requiring an actual rewrite.
6323 : */
6324 982 : ExecStoreAllNullTuple(newslot);
6325 : }
6326 : else
6327 : {
6328 3824 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6329 : table_slot_callbacks(oldrel));
6330 3824 : newslot = NULL;
6331 : }
6332 :
6333 : /*
6334 : * Any attributes that are dropped according to the new tuple
6335 : * descriptor can be set to NULL. We precompute the list of dropped
6336 : * attributes to avoid needing to do so in the per-tuple loop.
6337 : */
6338 16988 : for (i = 0; i < newTupDesc->natts; i++)
6339 : {
6340 12182 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6341 790 : dropped_attrs = lappend_int(dropped_attrs, i);
6342 : }
6343 :
6344 : /*
6345 : * Scan through the rows, generating a new row if needed and then
6346 : * checking all the constraints.
6347 : */
6348 4806 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6349 4806 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6350 :
6351 : /*
6352 : * Switch to per-tuple memory context and reset it for each tuple
6353 : * produced, so we don't leak memory.
6354 : */
6355 4806 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6356 :
6357 774526 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6358 : {
6359 : TupleTableSlot *insertslot;
6360 :
6361 765228 : if (tab->rewrite > 0)
6362 : {
6363 : /* Extract data from old tuple */
6364 99834 : slot_getallattrs(oldslot);
6365 99834 : ExecClearTuple(newslot);
6366 :
6367 : /* copy attributes */
6368 99834 : memcpy(newslot->tts_values, oldslot->tts_values,
6369 99834 : sizeof(Datum) * oldslot->tts_nvalid);
6370 99834 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6371 99834 : sizeof(bool) * oldslot->tts_nvalid);
6372 :
6373 : /* Set dropped attributes to null in new tuple */
6374 99950 : foreach(lc, dropped_attrs)
6375 116 : newslot->tts_isnull[lfirst_int(lc)] = true;
6376 :
6377 : /*
6378 : * Constraints and GENERATED expressions might reference the
6379 : * tableoid column, so fill tts_tableOid with the desired
6380 : * value. (We must do this each time, because it gets
6381 : * overwritten with newrel's OID during storing.)
6382 : */
6383 99834 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6384 :
6385 : /*
6386 : * Process supplied expressions to replace selected columns.
6387 : *
6388 : * First, evaluate expressions whose inputs come from the old
6389 : * tuple.
6390 : */
6391 99834 : econtext->ecxt_scantuple = oldslot;
6392 :
6393 205620 : foreach(l, tab->newvals)
6394 : {
6395 105798 : NewColumnValue *ex = lfirst(l);
6396 :
6397 105798 : if (ex->is_generated)
6398 312 : continue;
6399 :
6400 105486 : newslot->tts_values[ex->attnum - 1]
6401 105474 : = ExecEvalExpr(ex->exprstate,
6402 : econtext,
6403 105486 : &newslot->tts_isnull[ex->attnum - 1]);
6404 : }
6405 :
6406 99822 : ExecStoreVirtualTuple(newslot);
6407 :
6408 : /*
6409 : * Now, evaluate any expressions whose inputs come from the
6410 : * new tuple. We assume these columns won't reference each
6411 : * other, so that there's no ordering dependency.
6412 : */
6413 99822 : econtext->ecxt_scantuple = newslot;
6414 :
6415 205608 : foreach(l, tab->newvals)
6416 : {
6417 105786 : NewColumnValue *ex = lfirst(l);
6418 :
6419 105786 : if (!ex->is_generated)
6420 105474 : continue;
6421 :
6422 312 : newslot->tts_values[ex->attnum - 1]
6423 312 : = ExecEvalExpr(ex->exprstate,
6424 : econtext,
6425 312 : &newslot->tts_isnull[ex->attnum - 1]);
6426 : }
6427 :
6428 99822 : insertslot = newslot;
6429 : }
6430 : else
6431 : {
6432 : /*
6433 : * If there's no rewrite, old and new table are guaranteed to
6434 : * have the same AM, so we can just use the old slot to verify
6435 : * new constraints etc.
6436 : */
6437 665394 : insertslot = oldslot;
6438 : }
6439 :
6440 : /* Now check any constraints on the possibly-changed tuple */
6441 765216 : econtext->ecxt_scantuple = insertslot;
6442 :
6443 4106702 : foreach_int(attn, notnull_attrs)
6444 : {
6445 2576474 : if (slot_attisnull(insertslot, attn))
6446 : {
6447 102 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6448 :
6449 102 : ereport(ERROR,
6450 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6451 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6452 : NameStr(attr->attname),
6453 : RelationGetRelationName(oldrel)),
6454 : errtablecol(oldrel, attn)));
6455 : }
6456 : }
6457 :
6458 765114 : if (notnull_virtual_attrs != NIL)
6459 : {
6460 : AttrNumber attnum;
6461 :
6462 84 : attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6463 : estate,
6464 : notnull_virtual_attrs);
6465 84 : if (attnum != InvalidAttrNumber)
6466 : {
6467 30 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6468 :
6469 30 : ereport(ERROR,
6470 : errcode(ERRCODE_NOT_NULL_VIOLATION),
6471 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6472 : NameStr(attr->attname),
6473 : RelationGetRelationName(oldrel)),
6474 : errtablecol(oldrel, attnum));
6475 : }
6476 : }
6477 :
6478 773244 : foreach(l, tab->constraints)
6479 : {
6480 8256 : NewConstraint *con = lfirst(l);
6481 :
6482 8256 : switch (con->contype)
6483 : {
6484 8150 : case CONSTR_CHECK:
6485 8150 : if (!ExecCheck(con->qualstate, econtext))
6486 96 : ereport(ERROR,
6487 : (errcode(ERRCODE_CHECK_VIOLATION),
6488 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6489 : con->name,
6490 : RelationGetRelationName(oldrel)),
6491 : errtableconstraint(oldrel, con->name)));
6492 8054 : break;
6493 106 : case CONSTR_NOTNULL:
6494 : case CONSTR_FOREIGN:
6495 : /* Nothing to do here */
6496 106 : break;
6497 0 : default:
6498 0 : elog(ERROR, "unrecognized constraint type: %d",
6499 : (int) con->contype);
6500 : }
6501 : }
6502 :
6503 764988 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6504 : {
6505 74 : if (tab->validate_default)
6506 26 : ereport(ERROR,
6507 : (errcode(ERRCODE_CHECK_VIOLATION),
6508 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6509 : RelationGetRelationName(oldrel)),
6510 : errtable(oldrel)));
6511 : else
6512 48 : ereport(ERROR,
6513 : (errcode(ERRCODE_CHECK_VIOLATION),
6514 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6515 : RelationGetRelationName(oldrel)),
6516 : errtable(oldrel)));
6517 : }
6518 :
6519 : /* Write the tuple out to the new relation */
6520 764914 : if (newrel)
6521 99792 : table_tuple_insert(newrel, insertslot, mycid,
6522 : ti_options, bistate);
6523 :
6524 764914 : ResetExprContext(econtext);
6525 :
6526 764914 : CHECK_FOR_INTERRUPTS();
6527 : }
6528 :
6529 4492 : MemoryContextSwitchTo(oldCxt);
6530 4492 : table_endscan(scan);
6531 4492 : UnregisterSnapshot(snapshot);
6532 :
6533 4492 : ExecDropSingleTupleTableSlot(oldslot);
6534 4492 : if (newslot)
6535 940 : ExecDropSingleTupleTableSlot(newslot);
6536 : }
6537 :
6538 5462 : FreeExecutorState(estate);
6539 :
6540 5462 : table_close(oldrel, NoLock);
6541 5462 : if (newrel)
6542 : {
6543 940 : FreeBulkInsertState(bistate);
6544 :
6545 940 : table_finish_bulk_insert(newrel, ti_options);
6546 :
6547 940 : table_close(newrel, NoLock);
6548 : }
6549 5462 : }
6550 :
6551 : /*
6552 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6553 : */
6554 : static AlteredTableInfo *
6555 41562 : ATGetQueueEntry(List **wqueue, Relation rel)
6556 : {
6557 41562 : Oid relid = RelationGetRelid(rel);
6558 : AlteredTableInfo *tab;
6559 : ListCell *ltab;
6560 :
6561 51290 : foreach(ltab, *wqueue)
6562 : {
6563 14600 : tab = (AlteredTableInfo *) lfirst(ltab);
6564 14600 : if (tab->relid == relid)
6565 4872 : return tab;
6566 : }
6567 :
6568 : /*
6569 : * Not there, so add it. Note that we make a copy of the relation's
6570 : * existing descriptor before anything interesting can happen to it.
6571 : */
6572 36690 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6573 36690 : tab->relid = relid;
6574 36690 : tab->rel = NULL; /* set later */
6575 36690 : tab->relkind = rel->rd_rel->relkind;
6576 36690 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6577 36690 : tab->newAccessMethod = InvalidOid;
6578 36690 : tab->chgAccessMethod = false;
6579 36690 : tab->newTableSpace = InvalidOid;
6580 36690 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6581 36690 : tab->chgPersistence = false;
6582 :
6583 36690 : *wqueue = lappend(*wqueue, tab);
6584 :
6585 36690 : return tab;
6586 : }
6587 :
6588 : static const char *
6589 80 : alter_table_type_to_string(AlterTableType cmdtype)
6590 : {
6591 80 : switch (cmdtype)
6592 : {
6593 0 : case AT_AddColumn:
6594 : case AT_AddColumnToView:
6595 0 : return "ADD COLUMN";
6596 0 : case AT_ColumnDefault:
6597 : case AT_CookedColumnDefault:
6598 0 : return "ALTER COLUMN ... SET DEFAULT";
6599 6 : case AT_DropNotNull:
6600 6 : return "ALTER COLUMN ... DROP NOT NULL";
6601 6 : case AT_SetNotNull:
6602 6 : return "ALTER COLUMN ... SET NOT NULL";
6603 0 : case AT_SetExpression:
6604 0 : return "ALTER COLUMN ... SET EXPRESSION";
6605 0 : case AT_DropExpression:
6606 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6607 0 : case AT_SetStatistics:
6608 0 : return "ALTER COLUMN ... SET STATISTICS";
6609 12 : case AT_SetOptions:
6610 12 : return "ALTER COLUMN ... SET";
6611 0 : case AT_ResetOptions:
6612 0 : return "ALTER COLUMN ... RESET";
6613 0 : case AT_SetStorage:
6614 0 : return "ALTER COLUMN ... SET STORAGE";
6615 0 : case AT_SetCompression:
6616 0 : return "ALTER COLUMN ... SET COMPRESSION";
6617 6 : case AT_DropColumn:
6618 6 : return "DROP COLUMN";
6619 0 : case AT_AddIndex:
6620 : case AT_ReAddIndex:
6621 0 : return NULL; /* not real grammar */
6622 0 : case AT_AddConstraint:
6623 : case AT_ReAddConstraint:
6624 : case AT_ReAddDomainConstraint:
6625 : case AT_AddIndexConstraint:
6626 0 : return "ADD CONSTRAINT";
6627 6 : case AT_AlterConstraint:
6628 6 : return "ALTER CONSTRAINT";
6629 0 : case AT_ValidateConstraint:
6630 0 : return "VALIDATE CONSTRAINT";
6631 0 : case AT_DropConstraint:
6632 0 : return "DROP CONSTRAINT";
6633 0 : case AT_ReAddComment:
6634 0 : return NULL; /* not real grammar */
6635 0 : case AT_AlterColumnType:
6636 0 : return "ALTER COLUMN ... SET DATA TYPE";
6637 0 : case AT_AlterColumnGenericOptions:
6638 0 : return "ALTER COLUMN ... OPTIONS";
6639 0 : case AT_ChangeOwner:
6640 0 : return "OWNER TO";
6641 0 : case AT_ClusterOn:
6642 0 : return "CLUSTER ON";
6643 0 : case AT_DropCluster:
6644 0 : return "SET WITHOUT CLUSTER";
6645 0 : case AT_SetAccessMethod:
6646 0 : return "SET ACCESS METHOD";
6647 6 : case AT_SetLogged:
6648 6 : return "SET LOGGED";
6649 6 : case AT_SetUnLogged:
6650 6 : return "SET UNLOGGED";
6651 0 : case AT_DropOids:
6652 0 : return "SET WITHOUT OIDS";
6653 0 : case AT_SetTableSpace:
6654 0 : return "SET TABLESPACE";
6655 2 : case AT_SetRelOptions:
6656 2 : return "SET";
6657 0 : case AT_ResetRelOptions:
6658 0 : return "RESET";
6659 0 : case AT_ReplaceRelOptions:
6660 0 : return NULL; /* not real grammar */
6661 0 : case AT_EnableTrig:
6662 0 : return "ENABLE TRIGGER";
6663 0 : case AT_EnableAlwaysTrig:
6664 0 : return "ENABLE ALWAYS TRIGGER";
6665 0 : case AT_EnableReplicaTrig:
6666 0 : return "ENABLE REPLICA TRIGGER";
6667 0 : case AT_DisableTrig:
6668 0 : return "DISABLE TRIGGER";
6669 0 : case AT_EnableTrigAll:
6670 0 : return "ENABLE TRIGGER ALL";
6671 0 : case AT_DisableTrigAll:
6672 0 : return "DISABLE TRIGGER ALL";
6673 0 : case AT_EnableTrigUser:
6674 0 : return "ENABLE TRIGGER USER";
6675 0 : case AT_DisableTrigUser:
6676 0 : return "DISABLE TRIGGER USER";
6677 0 : case AT_EnableRule:
6678 0 : return "ENABLE RULE";
6679 0 : case AT_EnableAlwaysRule:
6680 0 : return "ENABLE ALWAYS RULE";
6681 0 : case AT_EnableReplicaRule:
6682 0 : return "ENABLE REPLICA RULE";
6683 0 : case AT_DisableRule:
6684 0 : return "DISABLE RULE";
6685 0 : case AT_AddInherit:
6686 0 : return "INHERIT";
6687 0 : case AT_DropInherit:
6688 0 : return "NO INHERIT";
6689 0 : case AT_AddOf:
6690 0 : return "OF";
6691 0 : case AT_DropOf:
6692 0 : return "NOT OF";
6693 0 : case AT_ReplicaIdentity:
6694 0 : return "REPLICA IDENTITY";
6695 0 : case AT_EnableRowSecurity:
6696 0 : return "ENABLE ROW SECURITY";
6697 0 : case AT_DisableRowSecurity:
6698 0 : return "DISABLE ROW SECURITY";
6699 0 : case AT_ForceRowSecurity:
6700 0 : return "FORCE ROW SECURITY";
6701 0 : case AT_NoForceRowSecurity:
6702 0 : return "NO FORCE ROW SECURITY";
6703 0 : case AT_GenericOptions:
6704 0 : return "OPTIONS";
6705 6 : case AT_AttachPartition:
6706 6 : return "ATTACH PARTITION";
6707 18 : case AT_DetachPartition:
6708 18 : return "DETACH PARTITION";
6709 6 : case AT_DetachPartitionFinalize:
6710 6 : return "DETACH PARTITION ... FINALIZE";
6711 0 : case AT_AddIdentity:
6712 0 : return "ALTER COLUMN ... ADD IDENTITY";
6713 0 : case AT_SetIdentity:
6714 0 : return "ALTER COLUMN ... SET";
6715 0 : case AT_DropIdentity:
6716 0 : return "ALTER COLUMN ... DROP IDENTITY";
6717 0 : case AT_ReAddStatistics:
6718 0 : return NULL; /* not real grammar */
6719 : }
6720 :
6721 0 : return NULL;
6722 : }
6723 :
6724 : /*
6725 : * ATSimplePermissions
6726 : *
6727 : * - Ensure that it is a relation (or possibly a view)
6728 : * - Ensure this user is the owner
6729 : * - Ensure that it is not a system table
6730 : */
6731 : static void
6732 37822 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6733 : {
6734 : int actual_target;
6735 :
6736 37822 : switch (rel->rd_rel->relkind)
6737 : {
6738 29934 : case RELKIND_RELATION:
6739 29934 : actual_target = ATT_TABLE;
6740 29934 : break;
6741 5616 : case RELKIND_PARTITIONED_TABLE:
6742 5616 : actual_target = ATT_PARTITIONED_TABLE;
6743 5616 : break;
6744 402 : case RELKIND_VIEW:
6745 402 : actual_target = ATT_VIEW;
6746 402 : break;
6747 46 : case RELKIND_MATVIEW:
6748 46 : actual_target = ATT_MATVIEW;
6749 46 : break;
6750 228 : case RELKIND_INDEX:
6751 228 : actual_target = ATT_INDEX;
6752 228 : break;
6753 422 : case RELKIND_PARTITIONED_INDEX:
6754 422 : actual_target = ATT_PARTITIONED_INDEX;
6755 422 : break;
6756 216 : case RELKIND_COMPOSITE_TYPE:
6757 216 : actual_target = ATT_COMPOSITE_TYPE;
6758 216 : break;
6759 932 : case RELKIND_FOREIGN_TABLE:
6760 932 : actual_target = ATT_FOREIGN_TABLE;
6761 932 : break;
6762 24 : case RELKIND_SEQUENCE:
6763 24 : actual_target = ATT_SEQUENCE;
6764 24 : break;
6765 2 : default:
6766 2 : actual_target = 0;
6767 2 : break;
6768 : }
6769 :
6770 : /* Wrong target type? */
6771 37822 : if ((actual_target & allowed_targets) == 0)
6772 : {
6773 80 : const char *action_str = alter_table_type_to_string(cmdtype);
6774 :
6775 80 : if (action_str)
6776 80 : ereport(ERROR,
6777 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6778 : /* translator: %s is a group of some SQL keywords */
6779 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6780 : action_str, RelationGetRelationName(rel)),
6781 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6782 : else
6783 : /* internal error? */
6784 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6785 : RelationGetRelationName(rel));
6786 : }
6787 :
6788 : /* Permissions checks */
6789 37742 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6790 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6791 12 : RelationGetRelationName(rel));
6792 :
6793 37730 : if (!allowSystemTableMods && IsSystemRelation(rel))
6794 0 : ereport(ERROR,
6795 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6796 : errmsg("permission denied: \"%s\" is a system catalog",
6797 : RelationGetRelationName(rel))));
6798 37730 : }
6799 :
6800 : /*
6801 : * ATSimpleRecursion
6802 : *
6803 : * Simple table recursion sufficient for most ALTER TABLE operations.
6804 : * All direct and indirect children are processed in an unspecified order.
6805 : * Note that if a child inherits from the original table via multiple
6806 : * inheritance paths, it will be visited just once.
6807 : */
6808 : static void
6809 1346 : ATSimpleRecursion(List **wqueue, Relation rel,
6810 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6811 : AlterTableUtilityContext *context)
6812 : {
6813 : /*
6814 : * Propagate to children, if desired and if there are (or might be) any
6815 : * children.
6816 : */
6817 1346 : if (recurse && rel->rd_rel->relhassubclass)
6818 : {
6819 84 : Oid relid = RelationGetRelid(rel);
6820 : ListCell *child;
6821 : List *children;
6822 :
6823 84 : children = find_all_inheritors(relid, lockmode, NULL);
6824 :
6825 : /*
6826 : * find_all_inheritors does the recursive search of the inheritance
6827 : * hierarchy, so all we have to do is process all of the relids in the
6828 : * list that it returns.
6829 : */
6830 366 : foreach(child, children)
6831 : {
6832 282 : Oid childrelid = lfirst_oid(child);
6833 : Relation childrel;
6834 :
6835 282 : if (childrelid == relid)
6836 84 : continue;
6837 : /* find_all_inheritors already got lock */
6838 198 : childrel = relation_open(childrelid, NoLock);
6839 198 : CheckAlterTableIsSafe(childrel);
6840 198 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6841 198 : relation_close(childrel, NoLock);
6842 : }
6843 : }
6844 1346 : }
6845 :
6846 : /*
6847 : * Obtain list of partitions of the given table, locking them all at the given
6848 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6849 : *
6850 : * This function is a no-op if the given relation is not a partitioned table;
6851 : * in particular, nothing is done if it's a legacy inheritance parent.
6852 : */
6853 : static void
6854 818 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6855 : {
6856 818 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6857 : {
6858 : List *inh;
6859 : ListCell *cell;
6860 :
6861 176 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6862 : /* first element is the parent rel; must ignore it */
6863 574 : for_each_from(cell, inh, 1)
6864 : {
6865 : Relation childrel;
6866 :
6867 : /* find_all_inheritors already got lock */
6868 404 : childrel = table_open(lfirst_oid(cell), NoLock);
6869 404 : CheckAlterTableIsSafe(childrel);
6870 398 : table_close(childrel, NoLock);
6871 : }
6872 170 : list_free(inh);
6873 : }
6874 812 : }
6875 :
6876 : /*
6877 : * ATTypedTableRecursion
6878 : *
6879 : * Propagate ALTER TYPE operations to the typed tables of that type.
6880 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6881 : * recursion to inheritance children of the typed tables.
6882 : */
6883 : static void
6884 192 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6885 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6886 : {
6887 : ListCell *child;
6888 : List *children;
6889 :
6890 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6891 :
6892 192 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6893 192 : RelationGetRelationName(rel),
6894 : cmd->behavior);
6895 :
6896 204 : foreach(child, children)
6897 : {
6898 30 : Oid childrelid = lfirst_oid(child);
6899 : Relation childrel;
6900 :
6901 30 : childrel = relation_open(childrelid, lockmode);
6902 30 : CheckAlterTableIsSafe(childrel);
6903 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6904 30 : relation_close(childrel, NoLock);
6905 : }
6906 174 : }
6907 :
6908 :
6909 : /*
6910 : * find_composite_type_dependencies
6911 : *
6912 : * Check to see if the type "typeOid" is being used as a column in some table
6913 : * (possibly nested several levels deep in composite types, arrays, etc!).
6914 : * Eventually, we'd like to propagate the check or rewrite operation
6915 : * into such tables, but for now, just error out if we find any.
6916 : *
6917 : * Caller should provide either the associated relation of a rowtype,
6918 : * or a type name (not both) for use in the error message, if any.
6919 : *
6920 : * Note that "typeOid" is not necessarily a composite type; it could also be
6921 : * another container type such as an array or range, or a domain over one of
6922 : * these things. The name of this function is therefore somewhat historical,
6923 : * but it's not worth changing.
6924 : *
6925 : * We assume that functions and views depending on the type are not reasons
6926 : * to reject the ALTER. (How safe is this really?)
6927 : */
6928 : void
6929 4526 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6930 : const char *origTypeName)
6931 : {
6932 : Relation depRel;
6933 : ScanKeyData key[2];
6934 : SysScanDesc depScan;
6935 : HeapTuple depTup;
6936 :
6937 : /* since this function recurses, it could be driven to stack overflow */
6938 4526 : check_stack_depth();
6939 :
6940 : /*
6941 : * We scan pg_depend to find those things that depend on the given type.
6942 : * (We assume we can ignore refobjsubid for a type.)
6943 : */
6944 4526 : depRel = table_open(DependRelationId, AccessShareLock);
6945 :
6946 4526 : ScanKeyInit(&key[0],
6947 : Anum_pg_depend_refclassid,
6948 : BTEqualStrategyNumber, F_OIDEQ,
6949 : ObjectIdGetDatum(TypeRelationId));
6950 4526 : ScanKeyInit(&key[1],
6951 : Anum_pg_depend_refobjid,
6952 : BTEqualStrategyNumber, F_OIDEQ,
6953 : ObjectIdGetDatum(typeOid));
6954 :
6955 4526 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6956 : NULL, 2, key);
6957 :
6958 6956 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6959 : {
6960 2586 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6961 : Relation rel;
6962 : TupleDesc tupleDesc;
6963 : Form_pg_attribute att;
6964 :
6965 : /* Check for directly dependent types */
6966 2586 : if (pg_depend->classid == TypeRelationId)
6967 : {
6968 : /*
6969 : * This must be an array, domain, or range containing the given
6970 : * type, so recursively check for uses of this type. Note that
6971 : * any error message will mention the original type not the
6972 : * container; this is intentional.
6973 : */
6974 2180 : find_composite_type_dependencies(pg_depend->objid,
6975 : origRelation, origTypeName);
6976 2156 : continue;
6977 : }
6978 :
6979 : /* Else, ignore dependees that aren't relations */
6980 406 : if (pg_depend->classid != RelationRelationId)
6981 122 : continue;
6982 :
6983 284 : rel = relation_open(pg_depend->objid, AccessShareLock);
6984 284 : tupleDesc = RelationGetDescr(rel);
6985 :
6986 : /*
6987 : * If objsubid identifies a specific column, refer to that in error
6988 : * messages. Otherwise, search to see if there's a user column of the
6989 : * type. (We assume system columns are never of interesting types.)
6990 : * The search is needed because an index containing an expression
6991 : * column of the target type will just be recorded as a whole-relation
6992 : * dependency. If we do not find a column of the type, the dependency
6993 : * must indicate that the type is transiently referenced in an index
6994 : * expression but not stored on disk, which we assume is OK, just as
6995 : * we do for references in views. (It could also be that the target
6996 : * type is embedded in some container type that is stored in an index
6997 : * column, but the previous recursion should catch such cases.)
6998 : */
6999 284 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
7000 126 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7001 : else
7002 : {
7003 158 : att = NULL;
7004 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
7005 : {
7006 254 : att = TupleDescAttr(tupleDesc, attno - 1);
7007 254 : if (att->atttypid == typeOid && !att->attisdropped)
7008 6 : break;
7009 248 : att = NULL;
7010 : }
7011 158 : if (att == NULL)
7012 : {
7013 : /* No such column, so assume OK */
7014 152 : relation_close(rel, AccessShareLock);
7015 152 : continue;
7016 : }
7017 : }
7018 :
7019 : /*
7020 : * We definitely should reject if the relation has storage. If it's
7021 : * partitioned, then perhaps we don't have to reject: if there are
7022 : * partitions then we'll fail when we find one, else there is no
7023 : * stored data to worry about. However, it's possible that the type
7024 : * change would affect conclusions about whether the type is sortable
7025 : * or hashable and thus (if it's a partitioning column) break the
7026 : * partitioning rule. For now, reject for partitioned rels too.
7027 : */
7028 132 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7029 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7030 : {
7031 132 : if (origTypeName)
7032 30 : ereport(ERROR,
7033 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7034 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7035 : origTypeName,
7036 : RelationGetRelationName(rel),
7037 : NameStr(att->attname))));
7038 102 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7039 18 : ereport(ERROR,
7040 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7041 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7042 : RelationGetRelationName(origRelation),
7043 : RelationGetRelationName(rel),
7044 : NameStr(att->attname))));
7045 84 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7046 6 : ereport(ERROR,
7047 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7048 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7049 : RelationGetRelationName(origRelation),
7050 : RelationGetRelationName(rel),
7051 : NameStr(att->attname))));
7052 : else
7053 78 : ereport(ERROR,
7054 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7055 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7056 : RelationGetRelationName(origRelation),
7057 : RelationGetRelationName(rel),
7058 : NameStr(att->attname))));
7059 : }
7060 0 : else if (OidIsValid(rel->rd_rel->reltype))
7061 : {
7062 : /*
7063 : * A view or composite type itself isn't a problem, but we must
7064 : * recursively check for indirect dependencies via its rowtype.
7065 : */
7066 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
7067 : origRelation, origTypeName);
7068 : }
7069 :
7070 0 : relation_close(rel, AccessShareLock);
7071 : }
7072 :
7073 4370 : systable_endscan(depScan);
7074 :
7075 4370 : relation_close(depRel, AccessShareLock);
7076 4370 : }
7077 :
7078 :
7079 : /*
7080 : * find_typed_table_dependencies
7081 : *
7082 : * Check to see if a composite type is being used as the type of a
7083 : * typed table. Abort if any are found and behavior is RESTRICT.
7084 : * Else return the list of tables.
7085 : */
7086 : static List *
7087 216 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7088 : {
7089 : Relation classRel;
7090 : ScanKeyData key[1];
7091 : TableScanDesc scan;
7092 : HeapTuple tuple;
7093 216 : List *result = NIL;
7094 :
7095 216 : classRel = table_open(RelationRelationId, AccessShareLock);
7096 :
7097 216 : ScanKeyInit(&key[0],
7098 : Anum_pg_class_reloftype,
7099 : BTEqualStrategyNumber, F_OIDEQ,
7100 : ObjectIdGetDatum(typeOid));
7101 :
7102 216 : scan = table_beginscan_catalog(classRel, 1, key);
7103 :
7104 252 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7105 : {
7106 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7107 :
7108 60 : if (behavior == DROP_RESTRICT)
7109 24 : ereport(ERROR,
7110 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7111 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7112 : typeName),
7113 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7114 : else
7115 36 : result = lappend_oid(result, classform->oid);
7116 : }
7117 :
7118 192 : table_endscan(scan);
7119 192 : table_close(classRel, AccessShareLock);
7120 :
7121 192 : return result;
7122 : }
7123 :
7124 :
7125 : /*
7126 : * check_of_type
7127 : *
7128 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7129 : * isn't suitable, throw an error. Currently, we require that the type
7130 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7131 : * would require handling a number of extra corner cases in the DDL commands.
7132 : * (Also, allowing domain-over-composite would open up a can of worms about
7133 : * whether and how the domain's constraints should apply to derived tables.)
7134 : */
7135 : void
7136 182 : check_of_type(HeapTuple typetuple)
7137 : {
7138 182 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7139 182 : bool typeOk = false;
7140 :
7141 182 : if (typ->typtype == TYPTYPE_COMPOSITE)
7142 : {
7143 : Relation typeRelation;
7144 :
7145 : Assert(OidIsValid(typ->typrelid));
7146 176 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7147 176 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7148 :
7149 : /*
7150 : * Close the parent rel, but keep our AccessShareLock on it until xact
7151 : * commit. That will prevent someone else from deleting or ALTERing
7152 : * the type before the typed table creation/conversion commits.
7153 : */
7154 176 : relation_close(typeRelation, NoLock);
7155 :
7156 176 : if (!typeOk)
7157 6 : ereport(ERROR,
7158 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7159 : errmsg("type %s is the row type of another table",
7160 : format_type_be(typ->oid)),
7161 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7162 : }
7163 : else
7164 6 : ereport(ERROR,
7165 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7166 : errmsg("type %s is not a composite type",
7167 : format_type_be(typ->oid))));
7168 170 : }
7169 :
7170 :
7171 : /*
7172 : * ALTER TABLE ADD COLUMN
7173 : *
7174 : * Adds an additional attribute to a relation making the assumption that
7175 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7176 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7177 : * AlterTableCmd's.
7178 : *
7179 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7180 : * have to decide at runtime whether to recurse or not depending on whether we
7181 : * actually add a column or merely merge with an existing column. (We can't
7182 : * check this in a static pre-pass because it won't handle multiple inheritance
7183 : * situations correctly.)
7184 : */
7185 : static void
7186 2190 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7187 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7188 : AlterTableUtilityContext *context)
7189 : {
7190 2190 : if (rel->rd_rel->reloftype && !recursing)
7191 6 : ereport(ERROR,
7192 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7193 : errmsg("cannot add column to typed table")));
7194 :
7195 2184 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7196 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7197 :
7198 2178 : if (recurse && !is_view)
7199 2078 : cmd->recurse = true;
7200 2178 : }
7201 :
7202 : /*
7203 : * Add a column to a table. The return value is the address of the
7204 : * new column in the parent relation.
7205 : *
7206 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7207 : * copy (but that happens only after we check for IF NOT EXISTS).
7208 : */
7209 : static ObjectAddress
7210 2910 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7211 : AlterTableCmd **cmd, bool recurse, bool recursing,
7212 : LOCKMODE lockmode, AlterTablePass cur_pass,
7213 : AlterTableUtilityContext *context)
7214 : {
7215 2910 : Oid myrelid = RelationGetRelid(rel);
7216 2910 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7217 2910 : bool if_not_exists = (*cmd)->missing_ok;
7218 : Relation pgclass,
7219 : attrdesc;
7220 : HeapTuple reltup;
7221 : Form_pg_class relform;
7222 : Form_pg_attribute attribute;
7223 : int newattnum;
7224 : char relkind;
7225 : Expr *defval;
7226 : List *children;
7227 : ListCell *child;
7228 : AlterTableCmd *childcmd;
7229 : ObjectAddress address;
7230 : TupleDesc tupdesc;
7231 :
7232 : /* since this function recurses, it could be driven to stack overflow */
7233 2910 : check_stack_depth();
7234 :
7235 : /* At top level, permission check was done in ATPrepCmd, else do it */
7236 2910 : if (recursing)
7237 738 : ATSimplePermissions((*cmd)->subtype, rel,
7238 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7239 :
7240 2910 : if (rel->rd_rel->relispartition && !recursing)
7241 12 : ereport(ERROR,
7242 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7243 : errmsg("cannot add column to a partition")));
7244 :
7245 2898 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7246 :
7247 : /*
7248 : * Are we adding the column to a recursion child? If so, check whether to
7249 : * merge with an existing definition for the column. If we do merge, we
7250 : * must not recurse. Children will already have the column, and recursing
7251 : * into them would mess up attinhcount.
7252 : */
7253 2898 : if (colDef->inhcount > 0)
7254 : {
7255 : HeapTuple tuple;
7256 :
7257 : /* Does child already have a column by this name? */
7258 738 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7259 738 : if (HeapTupleIsValid(tuple))
7260 : {
7261 60 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7262 : Oid ctypeId;
7263 : int32 ctypmod;
7264 : Oid ccollid;
7265 :
7266 : /* Child column must match on type, typmod, and collation */
7267 60 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7268 60 : if (ctypeId != childatt->atttypid ||
7269 60 : ctypmod != childatt->atttypmod)
7270 0 : ereport(ERROR,
7271 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7272 : errmsg("child table \"%s\" has different type for column \"%s\"",
7273 : RelationGetRelationName(rel), colDef->colname)));
7274 60 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7275 60 : if (ccollid != childatt->attcollation)
7276 0 : ereport(ERROR,
7277 : (errcode(ERRCODE_COLLATION_MISMATCH),
7278 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7279 : RelationGetRelationName(rel), colDef->colname),
7280 : errdetail("\"%s\" versus \"%s\"",
7281 : get_collation_name(ccollid),
7282 : get_collation_name(childatt->attcollation))));
7283 :
7284 : /* Bump the existing child att's inhcount */
7285 60 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7286 : &childatt->attinhcount))
7287 0 : ereport(ERROR,
7288 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7289 : errmsg("too many inheritance parents"));
7290 60 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7291 :
7292 60 : heap_freetuple(tuple);
7293 :
7294 : /* Inform the user about the merge */
7295 60 : ereport(NOTICE,
7296 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7297 : colDef->colname, RelationGetRelationName(rel))));
7298 :
7299 60 : table_close(attrdesc, RowExclusiveLock);
7300 :
7301 : /* Make the child column change visible */
7302 60 : CommandCounterIncrement();
7303 :
7304 60 : return InvalidObjectAddress;
7305 : }
7306 : }
7307 :
7308 : /* skip if the name already exists and if_not_exists is true */
7309 2838 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7310 : {
7311 54 : table_close(attrdesc, RowExclusiveLock);
7312 54 : return InvalidObjectAddress;
7313 : }
7314 :
7315 : /*
7316 : * Okay, we need to add the column, so go ahead and do parse
7317 : * transformation. This can result in queueing up, or even immediately
7318 : * executing, subsidiary operations (such as creation of unique indexes);
7319 : * so we mustn't do it until we have made the if_not_exists check.
7320 : *
7321 : * When recursing, the command was already transformed and we needn't do
7322 : * so again. Also, if context isn't given we can't transform. (That
7323 : * currently happens only for AT_AddColumnToView; we expect that view.c
7324 : * passed us a ColumnDef that doesn't need work.)
7325 : */
7326 2754 : if (context != NULL && !recursing)
7327 : {
7328 2052 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7329 : cur_pass, context);
7330 : Assert(*cmd != NULL);
7331 2046 : colDef = castNode(ColumnDef, (*cmd)->def);
7332 : }
7333 :
7334 : /*
7335 : * Regular inheritance children are independent enough not to inherit the
7336 : * identity column from parent hence cannot recursively add identity
7337 : * column if the table has inheritance children.
7338 : *
7339 : * Partitions, on the other hand, are integral part of a partitioned table
7340 : * and inherit identity column. Hence propagate identity column down the
7341 : * partition hierarchy.
7342 : */
7343 2748 : if (colDef->identity &&
7344 54 : recurse &&
7345 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7346 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7347 6 : ereport(ERROR,
7348 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7349 : errmsg("cannot recursively add identity column to table that has child tables")));
7350 :
7351 2742 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7352 :
7353 2742 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7354 2742 : if (!HeapTupleIsValid(reltup))
7355 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7356 2742 : relform = (Form_pg_class) GETSTRUCT(reltup);
7357 2742 : relkind = relform->relkind;
7358 :
7359 : /* Determine the new attribute's number */
7360 2742 : newattnum = relform->relnatts + 1;
7361 2742 : if (newattnum > MaxHeapAttributeNumber)
7362 0 : ereport(ERROR,
7363 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7364 : errmsg("tables can have at most %d columns",
7365 : MaxHeapAttributeNumber)));
7366 :
7367 : /*
7368 : * Construct new attribute's pg_attribute entry.
7369 : */
7370 2742 : tupdesc = BuildDescForRelation(list_make1(colDef));
7371 :
7372 2730 : attribute = TupleDescAttr(tupdesc, 0);
7373 :
7374 : /* Fix up attribute number */
7375 2730 : attribute->attnum = newattnum;
7376 :
7377 : /* make sure datatype is legal for a column */
7378 5460 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7379 2730 : list_make1_oid(rel->rd_rel->reltype),
7380 2730 : (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7381 :
7382 2694 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7383 :
7384 2694 : table_close(attrdesc, RowExclusiveLock);
7385 :
7386 : /*
7387 : * Update pg_class tuple as appropriate
7388 : */
7389 2694 : relform->relnatts = newattnum;
7390 :
7391 2694 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7392 :
7393 2694 : heap_freetuple(reltup);
7394 :
7395 : /* Post creation hook for new attribute */
7396 2694 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7397 :
7398 2694 : table_close(pgclass, RowExclusiveLock);
7399 :
7400 : /* Make the attribute's catalog entry visible */
7401 2694 : CommandCounterIncrement();
7402 :
7403 : /*
7404 : * Store the DEFAULT, if any, in the catalogs
7405 : */
7406 2694 : if (colDef->raw_default)
7407 : {
7408 : RawColumnDefault *rawEnt;
7409 :
7410 926 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7411 926 : rawEnt->attnum = attribute->attnum;
7412 926 : rawEnt->raw_default = copyObject(colDef->raw_default);
7413 926 : rawEnt->generated = colDef->generated;
7414 :
7415 : /*
7416 : * This function is intended for CREATE TABLE, so it processes a
7417 : * _list_ of defaults, but we just do one.
7418 : */
7419 926 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7420 : false, true, false, NULL);
7421 :
7422 : /* Make the additional catalog changes visible */
7423 902 : CommandCounterIncrement();
7424 : }
7425 :
7426 : /*
7427 : * Tell Phase 3 to fill in the default expression, if there is one.
7428 : *
7429 : * If there is no default, Phase 3 doesn't have to do anything, because
7430 : * that effectively means that the default is NULL. The heap tuple access
7431 : * routines always check for attnum > # of attributes in tuple, and return
7432 : * NULL if so, so without any modification of the tuple data we will get
7433 : * the effect of NULL values in the new column.
7434 : *
7435 : * An exception occurs when the new column is of a domain type: the domain
7436 : * might have a not-null constraint, or a check constraint that indirectly
7437 : * rejects nulls. If there are any domain constraints then we construct
7438 : * an explicit NULL default value that will be passed through
7439 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7440 : * rewriting the table which we really wouldn't have to do; but we do it
7441 : * to preserve the historical behavior that such a failure will be raised
7442 : * only if the table currently contains some rows.)
7443 : *
7444 : * Note: we use build_column_default, and not just the cooked default
7445 : * returned by AddRelationNewConstraints, so that the right thing happens
7446 : * when a datatype's default applies.
7447 : *
7448 : * Note: it might seem that this should happen at the end of Phase 2, so
7449 : * that the effects of subsequent subcommands can be taken into account.
7450 : * It's intentional that we do it now, though. The new column should be
7451 : * filled according to what is said in the ADD COLUMN subcommand, so that
7452 : * the effects are the same as if this subcommand had been run by itself
7453 : * and the later subcommands had been issued in new ALTER TABLE commands.
7454 : *
7455 : * We can skip this entirely for relations without storage, since Phase 3
7456 : * is certainly not going to touch them.
7457 : */
7458 2670 : if (RELKIND_HAS_STORAGE(relkind))
7459 : {
7460 : bool has_domain_constraints;
7461 2294 : bool has_missing = false;
7462 :
7463 : /*
7464 : * For an identity column, we can't use build_column_default(),
7465 : * because the sequence ownership isn't set yet. So do it manually.
7466 : */
7467 2294 : if (colDef->identity)
7468 : {
7469 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7470 :
7471 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7472 42 : nve->typeId = attribute->atttypid;
7473 :
7474 42 : defval = (Expr *) nve;
7475 : }
7476 : else
7477 2252 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7478 :
7479 : /* Build CoerceToDomain(NULL) expression if needed */
7480 2294 : has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7481 2294 : if (!defval && has_domain_constraints)
7482 : {
7483 : Oid baseTypeId;
7484 : int32 baseTypeMod;
7485 : Oid baseTypeColl;
7486 :
7487 6 : baseTypeMod = attribute->atttypmod;
7488 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7489 6 : baseTypeColl = get_typcollation(baseTypeId);
7490 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7491 6 : defval = (Expr *) coerce_to_target_type(NULL,
7492 : (Node *) defval,
7493 : baseTypeId,
7494 : attribute->atttypid,
7495 : attribute->atttypmod,
7496 : COERCION_ASSIGNMENT,
7497 : COERCE_IMPLICIT_CAST,
7498 : -1);
7499 6 : if (defval == NULL) /* should not happen */
7500 0 : elog(ERROR, "failed to coerce base type to domain");
7501 : }
7502 :
7503 2294 : if (defval)
7504 : {
7505 : NewColumnValue *newval;
7506 :
7507 : /* Prepare defval for execution, either here or in Phase 3 */
7508 810 : defval = expression_planner(defval);
7509 :
7510 : /* Add the new default to the newvals list */
7511 810 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7512 810 : newval->attnum = attribute->attnum;
7513 810 : newval->expr = defval;
7514 810 : newval->is_generated = (colDef->generated != '\0');
7515 :
7516 810 : tab->newvals = lappend(tab->newvals, newval);
7517 :
7518 : /*
7519 : * Attempt to skip a complete table rewrite by storing the
7520 : * specified DEFAULT value outside of the heap. This is only
7521 : * allowed for plain relations and non-generated columns, and the
7522 : * default expression can't be volatile (stable is OK). Note that
7523 : * contain_volatile_functions deems CoerceToDomain immutable, but
7524 : * here we consider that coercion to a domain with constraints is
7525 : * volatile; else it might fail even when the table is empty.
7526 : */
7527 810 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
7528 810 : !colDef->generated &&
7529 682 : !has_domain_constraints &&
7530 670 : !contain_volatile_functions((Node *) defval))
7531 502 : {
7532 : EState *estate;
7533 : ExprState *exprState;
7534 : Datum missingval;
7535 : bool missingIsNull;
7536 :
7537 : /* Evaluate the default expression */
7538 502 : estate = CreateExecutorState();
7539 502 : exprState = ExecPrepareExpr(defval, estate);
7540 502 : missingval = ExecEvalExpr(exprState,
7541 502 : GetPerTupleExprContext(estate),
7542 : &missingIsNull);
7543 : /* If it turns out NULL, nothing to do; else store it */
7544 502 : if (!missingIsNull)
7545 : {
7546 502 : StoreAttrMissingVal(rel, attribute->attnum, missingval);
7547 : /* Make the additional catalog change visible */
7548 502 : CommandCounterIncrement();
7549 502 : has_missing = true;
7550 : }
7551 502 : FreeExecutorState(estate);
7552 : }
7553 : else
7554 : {
7555 : /*
7556 : * Failed to use missing mode. We have to do a table rewrite
7557 : * to install the value --- unless it's a virtual generated
7558 : * column.
7559 : */
7560 308 : if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7561 216 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7562 : }
7563 : }
7564 :
7565 2294 : if (!has_missing)
7566 : {
7567 : /*
7568 : * If the new column is NOT NULL, and there is no missing value,
7569 : * tell Phase 3 it needs to check for NULLs.
7570 : */
7571 1792 : tab->verify_new_notnull |= colDef->is_not_null;
7572 : }
7573 : }
7574 :
7575 : /*
7576 : * Add needed dependency entries for the new column.
7577 : */
7578 2670 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7579 2670 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7580 :
7581 : /*
7582 : * Propagate to children as appropriate. Unlike most other ALTER
7583 : * routines, we have to do this one level of recursion at a time; we can't
7584 : * use find_all_inheritors to do it in one pass.
7585 : */
7586 : children =
7587 2670 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7588 :
7589 : /*
7590 : * If we are told not to recurse, there had better not be any child
7591 : * tables; else the addition would put them out of step.
7592 : */
7593 2670 : if (children && !recurse)
7594 12 : ereport(ERROR,
7595 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7596 : errmsg("column must be added to child tables too")));
7597 :
7598 : /* Children should see column as singly inherited */
7599 2658 : if (!recursing)
7600 : {
7601 1980 : childcmd = copyObject(*cmd);
7602 1980 : colDef = castNode(ColumnDef, childcmd->def);
7603 1980 : colDef->inhcount = 1;
7604 1980 : colDef->is_local = false;
7605 : }
7606 : else
7607 678 : childcmd = *cmd; /* no need to copy again */
7608 :
7609 3396 : foreach(child, children)
7610 : {
7611 738 : Oid childrelid = lfirst_oid(child);
7612 : Relation childrel;
7613 : AlteredTableInfo *childtab;
7614 :
7615 : /* find_inheritance_children already got lock */
7616 738 : childrel = table_open(childrelid, NoLock);
7617 738 : CheckAlterTableIsSafe(childrel);
7618 :
7619 : /* Find or create work queue entry for this table */
7620 738 : childtab = ATGetQueueEntry(wqueue, childrel);
7621 :
7622 : /* Recurse to child; return value is ignored */
7623 738 : ATExecAddColumn(wqueue, childtab, childrel,
7624 : &childcmd, recurse, true,
7625 : lockmode, cur_pass, context);
7626 :
7627 738 : table_close(childrel, NoLock);
7628 : }
7629 :
7630 2658 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7631 2658 : return address;
7632 : }
7633 :
7634 : /*
7635 : * If a new or renamed column will collide with the name of an existing
7636 : * column and if_not_exists is false then error out, else do nothing.
7637 : */
7638 : static bool
7639 3288 : check_for_column_name_collision(Relation rel, const char *colname,
7640 : bool if_not_exists)
7641 : {
7642 : HeapTuple attTuple;
7643 : int attnum;
7644 :
7645 : /*
7646 : * this test is deliberately not attisdropped-aware, since if one tries to
7647 : * add a column matching a dropped column name, it's gonna fail anyway.
7648 : */
7649 3288 : attTuple = SearchSysCache2(ATTNAME,
7650 : ObjectIdGetDatum(RelationGetRelid(rel)),
7651 : PointerGetDatum(colname));
7652 3288 : if (!HeapTupleIsValid(attTuple))
7653 3192 : return true;
7654 :
7655 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7656 96 : ReleaseSysCache(attTuple);
7657 :
7658 : /*
7659 : * We throw a different error message for conflicts with system column
7660 : * names, since they are normally not shown and the user might otherwise
7661 : * be confused about the reason for the conflict.
7662 : */
7663 96 : if (attnum <= 0)
7664 12 : ereport(ERROR,
7665 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7666 : errmsg("column name \"%s\" conflicts with a system column name",
7667 : colname)));
7668 : else
7669 : {
7670 84 : if (if_not_exists)
7671 : {
7672 54 : ereport(NOTICE,
7673 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7674 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7675 : colname, RelationGetRelationName(rel))));
7676 54 : return false;
7677 : }
7678 :
7679 30 : ereport(ERROR,
7680 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7681 : errmsg("column \"%s\" of relation \"%s\" already exists",
7682 : colname, RelationGetRelationName(rel))));
7683 : }
7684 :
7685 : return true;
7686 : }
7687 :
7688 : /*
7689 : * Install a column's dependency on its datatype.
7690 : */
7691 : static void
7692 3706 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7693 : {
7694 : ObjectAddress myself,
7695 : referenced;
7696 :
7697 3706 : myself.classId = RelationRelationId;
7698 3706 : myself.objectId = relid;
7699 3706 : myself.objectSubId = attnum;
7700 3706 : referenced.classId = TypeRelationId;
7701 3706 : referenced.objectId = typid;
7702 3706 : referenced.objectSubId = 0;
7703 3706 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7704 3706 : }
7705 :
7706 : /*
7707 : * Install a column's dependency on its collation.
7708 : */
7709 : static void
7710 3706 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7711 : {
7712 : ObjectAddress myself,
7713 : referenced;
7714 :
7715 : /* We know the default collation is pinned, so don't bother recording it */
7716 3706 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7717 : {
7718 18 : myself.classId = RelationRelationId;
7719 18 : myself.objectId = relid;
7720 18 : myself.objectSubId = attnum;
7721 18 : referenced.classId = CollationRelationId;
7722 18 : referenced.objectId = collid;
7723 18 : referenced.objectSubId = 0;
7724 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7725 : }
7726 3706 : }
7727 :
7728 : /*
7729 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7730 : *
7731 : * Return the address of the modified column. If the column was already
7732 : * nullable, InvalidObjectAddress is returned.
7733 : */
7734 : static ObjectAddress
7735 268 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7736 : LOCKMODE lockmode)
7737 : {
7738 : HeapTuple tuple;
7739 : HeapTuple conTup;
7740 : Form_pg_attribute attTup;
7741 : AttrNumber attnum;
7742 : Relation attr_rel;
7743 : ObjectAddress address;
7744 :
7745 : /*
7746 : * lookup the attribute
7747 : */
7748 268 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7749 :
7750 268 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7751 268 : if (!HeapTupleIsValid(tuple))
7752 18 : ereport(ERROR,
7753 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7754 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7755 : colName, RelationGetRelationName(rel))));
7756 250 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7757 250 : attnum = attTup->attnum;
7758 250 : ObjectAddressSubSet(address, RelationRelationId,
7759 : RelationGetRelid(rel), attnum);
7760 :
7761 : /* If the column is already nullable there's nothing to do. */
7762 250 : if (!attTup->attnotnull)
7763 : {
7764 0 : table_close(attr_rel, RowExclusiveLock);
7765 0 : return InvalidObjectAddress;
7766 : }
7767 :
7768 : /* Prevent them from altering a system attribute */
7769 250 : if (attnum <= 0)
7770 0 : ereport(ERROR,
7771 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7772 : errmsg("cannot alter system column \"%s\"",
7773 : colName)));
7774 :
7775 250 : if (attTup->attidentity)
7776 18 : ereport(ERROR,
7777 : (errcode(ERRCODE_SYNTAX_ERROR),
7778 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7779 : colName, RelationGetRelationName(rel))));
7780 :
7781 : /*
7782 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7783 : */
7784 232 : if (rel->rd_rel->relispartition)
7785 : {
7786 12 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7787 12 : Relation parent = table_open(parentId, AccessShareLock);
7788 12 : TupleDesc tupDesc = RelationGetDescr(parent);
7789 : AttrNumber parent_attnum;
7790 :
7791 12 : parent_attnum = get_attnum(parentId, colName);
7792 12 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7793 12 : ereport(ERROR,
7794 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7795 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7796 : colName)));
7797 0 : table_close(parent, AccessShareLock);
7798 : }
7799 :
7800 : /*
7801 : * Find the constraint that makes this column NOT NULL, and drop it.
7802 : * dropconstraint_internal() resets attnotnull.
7803 : */
7804 220 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7805 220 : if (conTup == NULL)
7806 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7807 : colName, RelationGetRelationName(rel));
7808 :
7809 : /* The normal case: we have a pg_constraint row, remove it */
7810 220 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7811 : false, lockmode);
7812 166 : heap_freetuple(conTup);
7813 :
7814 166 : InvokeObjectPostAlterHook(RelationRelationId,
7815 : RelationGetRelid(rel), attnum);
7816 :
7817 166 : table_close(attr_rel, RowExclusiveLock);
7818 :
7819 166 : return address;
7820 : }
7821 :
7822 : /*
7823 : * set_attnotnull
7824 : * Helper to update/validate the pg_attribute status of a not-null
7825 : * constraint
7826 : *
7827 : * pg_attribute.attnotnull is set true, if it isn't already.
7828 : * If queue_validation is true, also set up wqueue to validate the constraint.
7829 : * wqueue may be given as NULL when validation is not needed (e.g., on table
7830 : * creation).
7831 : */
7832 : static void
7833 25206 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7834 : bool is_valid, bool queue_validation)
7835 : {
7836 : Form_pg_attribute attr;
7837 : CompactAttribute *thisatt;
7838 :
7839 : Assert(!queue_validation || wqueue);
7840 :
7841 25206 : CheckAlterTableIsSafe(rel);
7842 :
7843 : /*
7844 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7845 : * attribute.
7846 : */
7847 25206 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7848 25206 : if (attr->attisdropped)
7849 0 : return;
7850 :
7851 25206 : if (!attr->attnotnull)
7852 : {
7853 : Relation attr_rel;
7854 : HeapTuple tuple;
7855 :
7856 1456 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7857 :
7858 1456 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7859 1456 : if (!HeapTupleIsValid(tuple))
7860 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7861 : attnum, RelationGetRelid(rel));
7862 :
7863 1456 : thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7864 1456 : thisatt->attnullability = ATTNULLABLE_VALID;
7865 :
7866 1456 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7867 :
7868 1456 : attr->attnotnull = true;
7869 1456 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7870 :
7871 : /*
7872 : * If the nullness isn't already proven by validated constraints, have
7873 : * ALTER TABLE phase 3 test for it.
7874 : */
7875 1456 : if (queue_validation && wqueue &&
7876 1238 : !NotNullImpliedByRelConstraints(rel, attr))
7877 : {
7878 : AlteredTableInfo *tab;
7879 :
7880 1188 : tab = ATGetQueueEntry(wqueue, rel);
7881 1188 : tab->verify_new_notnull = true;
7882 : }
7883 :
7884 1456 : CommandCounterIncrement();
7885 :
7886 1456 : table_close(attr_rel, RowExclusiveLock);
7887 1456 : heap_freetuple(tuple);
7888 : }
7889 : else
7890 : {
7891 23750 : CacheInvalidateRelcache(rel);
7892 : }
7893 : }
7894 :
7895 : /*
7896 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7897 : *
7898 : * Add a not-null constraint to a single table and its children. Returns
7899 : * the address of the constraint added to the parent relation, if one gets
7900 : * added, or InvalidObjectAddress otherwise.
7901 : *
7902 : * We must recurse to child tables during execution, rather than using
7903 : * ALTER TABLE's normal prep-time recursion.
7904 : */
7905 : static ObjectAddress
7906 706 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7907 : bool recurse, bool recursing, LOCKMODE lockmode)
7908 : {
7909 : HeapTuple tuple;
7910 : AttrNumber attnum;
7911 : ObjectAddress address;
7912 : Constraint *constraint;
7913 : CookedConstraint *ccon;
7914 : List *cooked;
7915 706 : bool is_no_inherit = false;
7916 :
7917 : /* Guard against stack overflow due to overly deep inheritance tree. */
7918 706 : check_stack_depth();
7919 :
7920 : /* At top level, permission check was done in ATPrepCmd, else do it */
7921 706 : if (recursing)
7922 : {
7923 298 : ATSimplePermissions(AT_AddConstraint, rel,
7924 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7925 : Assert(conName != NULL);
7926 : }
7927 :
7928 706 : attnum = get_attnum(RelationGetRelid(rel), colName);
7929 706 : if (attnum == InvalidAttrNumber)
7930 18 : ereport(ERROR,
7931 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7932 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7933 : colName, RelationGetRelationName(rel))));
7934 :
7935 : /* Prevent them from altering a system attribute */
7936 688 : if (attnum <= 0)
7937 0 : ereport(ERROR,
7938 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7939 : errmsg("cannot alter system column \"%s\"",
7940 : colName)));
7941 :
7942 : /* See if there's already a constraint */
7943 688 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7944 688 : if (HeapTupleIsValid(tuple))
7945 : {
7946 158 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7947 158 : bool changed = false;
7948 :
7949 : /*
7950 : * Don't let a NO INHERIT constraint be changed into inherit.
7951 : */
7952 158 : if (conForm->connoinherit && recurse)
7953 12 : ereport(ERROR,
7954 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7955 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7956 : NameStr(conForm->conname),
7957 : RelationGetRelationName(rel)));
7958 :
7959 : /*
7960 : * If we find an appropriate constraint, we're almost done, but just
7961 : * need to change some properties on it: if we're recursing, increment
7962 : * coninhcount; if not, set conislocal if not already set.
7963 : */
7964 146 : if (recursing)
7965 : {
7966 102 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
7967 : &conForm->coninhcount))
7968 0 : ereport(ERROR,
7969 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7970 : errmsg("too many inheritance parents"));
7971 102 : changed = true;
7972 : }
7973 44 : else if (!conForm->conislocal)
7974 : {
7975 0 : conForm->conislocal = true;
7976 0 : changed = true;
7977 : }
7978 44 : else if (!conForm->convalidated)
7979 : {
7980 : /*
7981 : * Flip attnotnull and convalidated, and also validate the
7982 : * constraint.
7983 : */
7984 24 : return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
7985 : recurse, recursing, lockmode);
7986 : }
7987 :
7988 122 : if (changed)
7989 : {
7990 : Relation constr_rel;
7991 :
7992 102 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7993 :
7994 102 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7995 102 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7996 102 : table_close(constr_rel, RowExclusiveLock);
7997 : }
7998 :
7999 122 : if (changed)
8000 102 : return address;
8001 : else
8002 20 : return InvalidObjectAddress;
8003 : }
8004 :
8005 : /*
8006 : * If we're asked not to recurse, and children exist, raise an error for
8007 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
8008 : * specified.
8009 : */
8010 554 : if (!recurse &&
8011 24 : find_inheritance_children(RelationGetRelid(rel),
8012 : NoLock) != NIL)
8013 : {
8014 18 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8015 6 : ereport(ERROR,
8016 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8017 : errmsg("constraint must be added to child tables too"),
8018 : errhint("Do not specify the ONLY keyword."));
8019 : else
8020 12 : is_no_inherit = true;
8021 : }
8022 :
8023 : /*
8024 : * No constraint exists; we must add one. First determine a name to use,
8025 : * if we haven't already.
8026 : */
8027 524 : if (!recursing)
8028 : {
8029 : Assert(conName == NULL);
8030 334 : conName = ChooseConstraintName(RelationGetRelationName(rel),
8031 : colName, "not_null",
8032 334 : RelationGetNamespace(rel),
8033 : NIL);
8034 : }
8035 :
8036 524 : constraint = makeNotNullConstraint(makeString(colName));
8037 524 : constraint->is_no_inherit = is_no_inherit;
8038 524 : constraint->conname = conName;
8039 :
8040 : /* and do it */
8041 524 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8042 524 : false, !recursing, false, NULL);
8043 524 : ccon = linitial(cooked);
8044 524 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8045 :
8046 524 : InvokeObjectPostAlterHook(RelationRelationId,
8047 : RelationGetRelid(rel), attnum);
8048 :
8049 : /* Mark pg_attribute.attnotnull for the column and queue validation */
8050 524 : set_attnotnull(wqueue, rel, attnum, true, true);
8051 :
8052 : /*
8053 : * Recurse to propagate the constraint to children that don't have one.
8054 : */
8055 524 : if (recurse)
8056 : {
8057 : List *children;
8058 :
8059 506 : children = find_inheritance_children(RelationGetRelid(rel),
8060 : lockmode);
8061 :
8062 1244 : foreach_oid(childoid, children)
8063 : {
8064 244 : Relation childrel = table_open(childoid, NoLock);
8065 :
8066 244 : CommandCounterIncrement();
8067 :
8068 244 : ATExecSetNotNull(wqueue, childrel, conName, colName,
8069 : recurse, true, lockmode);
8070 238 : table_close(childrel, NoLock);
8071 : }
8072 : }
8073 :
8074 518 : return address;
8075 : }
8076 :
8077 : /*
8078 : * NotNullImpliedByRelConstraints
8079 : * Does rel's existing constraints imply NOT NULL for the given attribute?
8080 : */
8081 : static bool
8082 1238 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8083 : {
8084 1238 : NullTest *nnulltest = makeNode(NullTest);
8085 :
8086 2476 : nnulltest->arg = (Expr *) makeVar(1,
8087 1238 : attr->attnum,
8088 : attr->atttypid,
8089 : attr->atttypmod,
8090 : attr->attcollation,
8091 : 0);
8092 1238 : nnulltest->nulltesttype = IS_NOT_NULL;
8093 :
8094 : /*
8095 : * argisrow = false is correct even for a composite column, because
8096 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8097 : * case, just IS DISTINCT FROM NULL.
8098 : */
8099 1238 : nnulltest->argisrow = false;
8100 1238 : nnulltest->location = -1;
8101 :
8102 1238 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8103 : {
8104 50 : ereport(DEBUG1,
8105 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8106 : RelationGetRelationName(rel), NameStr(attr->attname))));
8107 50 : return true;
8108 : }
8109 :
8110 1188 : return false;
8111 : }
8112 :
8113 : /*
8114 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8115 : *
8116 : * Return the address of the affected column.
8117 : */
8118 : static ObjectAddress
8119 584 : ATExecColumnDefault(Relation rel, const char *colName,
8120 : Node *newDefault, LOCKMODE lockmode)
8121 : {
8122 584 : TupleDesc tupdesc = RelationGetDescr(rel);
8123 : AttrNumber attnum;
8124 : ObjectAddress address;
8125 :
8126 : /*
8127 : * get the number of the attribute
8128 : */
8129 584 : attnum = get_attnum(RelationGetRelid(rel), colName);
8130 584 : if (attnum == InvalidAttrNumber)
8131 30 : ereport(ERROR,
8132 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8133 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8134 : colName, RelationGetRelationName(rel))));
8135 :
8136 : /* Prevent them from altering a system attribute */
8137 554 : if (attnum <= 0)
8138 0 : ereport(ERROR,
8139 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8140 : errmsg("cannot alter system column \"%s\"",
8141 : colName)));
8142 :
8143 554 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8144 18 : ereport(ERROR,
8145 : (errcode(ERRCODE_SYNTAX_ERROR),
8146 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8147 : colName, RelationGetRelationName(rel)),
8148 : /* translator: %s is an SQL ALTER command */
8149 : newDefault ? 0 : errhint("Use %s instead.",
8150 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8151 :
8152 536 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8153 12 : ereport(ERROR,
8154 : (errcode(ERRCODE_SYNTAX_ERROR),
8155 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8156 : colName, RelationGetRelationName(rel)),
8157 : newDefault ?
8158 : /* translator: %s is an SQL ALTER command */
8159 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8160 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8161 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8162 :
8163 : /*
8164 : * Remove any old default for the column. We use RESTRICT here for
8165 : * safety, but at present we do not expect anything to depend on the
8166 : * default.
8167 : *
8168 : * We treat removing the existing default as an internal operation when it
8169 : * is preparatory to adding a new default, but as a user-initiated
8170 : * operation when the user asked for a drop.
8171 : */
8172 524 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8173 : newDefault != NULL);
8174 :
8175 524 : if (newDefault)
8176 : {
8177 : /* SET DEFAULT */
8178 : RawColumnDefault *rawEnt;
8179 :
8180 350 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8181 350 : rawEnt->attnum = attnum;
8182 350 : rawEnt->raw_default = newDefault;
8183 350 : rawEnt->generated = '\0';
8184 :
8185 : /*
8186 : * This function is intended for CREATE TABLE, so it processes a
8187 : * _list_ of defaults, but we just do one.
8188 : */
8189 350 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8190 : false, true, false, NULL);
8191 : }
8192 :
8193 518 : ObjectAddressSubSet(address, RelationRelationId,
8194 : RelationGetRelid(rel), attnum);
8195 518 : return address;
8196 : }
8197 :
8198 : /*
8199 : * Add a pre-cooked default expression.
8200 : *
8201 : * Return the address of the affected column.
8202 : */
8203 : static ObjectAddress
8204 80 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8205 : Node *newDefault)
8206 : {
8207 : ObjectAddress address;
8208 :
8209 : /* We assume no checking is required */
8210 :
8211 : /*
8212 : * Remove any old default for the column. We use RESTRICT here for
8213 : * safety, but at present we do not expect anything to depend on the
8214 : * default. (In ordinary cases, there could not be a default in place
8215 : * anyway, but it's possible when combining LIKE with inheritance.)
8216 : */
8217 80 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8218 : true);
8219 :
8220 80 : (void) StoreAttrDefault(rel, attnum, newDefault, true);
8221 :
8222 80 : ObjectAddressSubSet(address, RelationRelationId,
8223 : RelationGetRelid(rel), attnum);
8224 80 : return address;
8225 : }
8226 :
8227 : /*
8228 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8229 : *
8230 : * Return the address of the affected column.
8231 : */
8232 : static ObjectAddress
8233 160 : ATExecAddIdentity(Relation rel, const char *colName,
8234 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8235 : {
8236 : Relation attrelation;
8237 : HeapTuple tuple;
8238 : Form_pg_attribute attTup;
8239 : AttrNumber attnum;
8240 : ObjectAddress address;
8241 160 : ColumnDef *cdef = castNode(ColumnDef, def);
8242 : bool ispartitioned;
8243 :
8244 160 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8245 160 : if (ispartitioned && !recurse)
8246 6 : ereport(ERROR,
8247 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8248 : errmsg("cannot add identity to a column of only the partitioned table"),
8249 : errhint("Do not specify the ONLY keyword.")));
8250 :
8251 154 : if (rel->rd_rel->relispartition && !recursing)
8252 12 : ereport(ERROR,
8253 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8254 : errmsg("cannot add identity to a column of a partition"));
8255 :
8256 142 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8257 :
8258 142 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8259 142 : if (!HeapTupleIsValid(tuple))
8260 0 : ereport(ERROR,
8261 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8262 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8263 : colName, RelationGetRelationName(rel))));
8264 142 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8265 142 : attnum = attTup->attnum;
8266 :
8267 : /* Can't alter a system attribute */
8268 142 : if (attnum <= 0)
8269 0 : ereport(ERROR,
8270 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8271 : errmsg("cannot alter system column \"%s\"",
8272 : colName)));
8273 :
8274 : /*
8275 : * Creating a column as identity implies NOT NULL, so adding the identity
8276 : * to an existing column that is not NOT NULL would create a state that
8277 : * cannot be reproduced without contortions.
8278 : */
8279 142 : if (!attTup->attnotnull)
8280 6 : ereport(ERROR,
8281 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8282 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8283 : colName, RelationGetRelationName(rel))));
8284 :
8285 136 : if (attTup->attidentity)
8286 18 : ereport(ERROR,
8287 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8288 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8289 : colName, RelationGetRelationName(rel))));
8290 :
8291 118 : if (attTup->atthasdef)
8292 6 : ereport(ERROR,
8293 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8294 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8295 : colName, RelationGetRelationName(rel))));
8296 :
8297 112 : attTup->attidentity = cdef->identity;
8298 112 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8299 :
8300 112 : InvokeObjectPostAlterHook(RelationRelationId,
8301 : RelationGetRelid(rel),
8302 : attTup->attnum);
8303 112 : ObjectAddressSubSet(address, RelationRelationId,
8304 : RelationGetRelid(rel), attnum);
8305 112 : heap_freetuple(tuple);
8306 :
8307 112 : table_close(attrelation, RowExclusiveLock);
8308 :
8309 : /*
8310 : * Recurse to propagate the identity column to partitions. Identity is
8311 : * not inherited in regular inheritance children.
8312 : */
8313 112 : if (recurse && ispartitioned)
8314 : {
8315 : List *children;
8316 : ListCell *lc;
8317 :
8318 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8319 :
8320 16 : foreach(lc, children)
8321 : {
8322 : Relation childrel;
8323 :
8324 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8325 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8326 6 : table_close(childrel, NoLock);
8327 : }
8328 : }
8329 :
8330 112 : return address;
8331 : }
8332 :
8333 : /*
8334 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8335 : *
8336 : * Return the address of the affected column.
8337 : */
8338 : static ObjectAddress
8339 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8340 : LOCKMODE lockmode, bool recurse, bool recursing)
8341 : {
8342 : ListCell *option;
8343 74 : DefElem *generatedEl = NULL;
8344 : HeapTuple tuple;
8345 : Form_pg_attribute attTup;
8346 : AttrNumber attnum;
8347 : Relation attrelation;
8348 : ObjectAddress address;
8349 : bool ispartitioned;
8350 :
8351 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8352 74 : if (ispartitioned && !recurse)
8353 6 : ereport(ERROR,
8354 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8355 : errmsg("cannot change identity column of only the partitioned table"),
8356 : errhint("Do not specify the ONLY keyword.")));
8357 :
8358 68 : if (rel->rd_rel->relispartition && !recursing)
8359 12 : ereport(ERROR,
8360 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8361 : errmsg("cannot change identity column of a partition"));
8362 :
8363 100 : foreach(option, castNode(List, def))
8364 : {
8365 44 : DefElem *defel = lfirst_node(DefElem, option);
8366 :
8367 44 : if (strcmp(defel->defname, "generated") == 0)
8368 : {
8369 44 : if (generatedEl)
8370 0 : ereport(ERROR,
8371 : (errcode(ERRCODE_SYNTAX_ERROR),
8372 : errmsg("conflicting or redundant options")));
8373 44 : generatedEl = defel;
8374 : }
8375 : else
8376 0 : elog(ERROR, "option \"%s\" not recognized",
8377 : defel->defname);
8378 : }
8379 :
8380 : /*
8381 : * Even if there is nothing to change here, we run all the checks. There
8382 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8383 : * there.
8384 : */
8385 :
8386 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8387 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8388 56 : if (!HeapTupleIsValid(tuple))
8389 0 : ereport(ERROR,
8390 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8391 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8392 : colName, RelationGetRelationName(rel))));
8393 :
8394 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8395 56 : attnum = attTup->attnum;
8396 :
8397 56 : if (attnum <= 0)
8398 0 : ereport(ERROR,
8399 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8400 : errmsg("cannot alter system column \"%s\"",
8401 : colName)));
8402 :
8403 56 : if (!attTup->attidentity)
8404 6 : ereport(ERROR,
8405 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8406 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8407 : colName, RelationGetRelationName(rel))));
8408 :
8409 50 : if (generatedEl)
8410 : {
8411 44 : attTup->attidentity = defGetInt32(generatedEl);
8412 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8413 :
8414 44 : InvokeObjectPostAlterHook(RelationRelationId,
8415 : RelationGetRelid(rel),
8416 : attTup->attnum);
8417 44 : ObjectAddressSubSet(address, RelationRelationId,
8418 : RelationGetRelid(rel), attnum);
8419 : }
8420 : else
8421 6 : address = InvalidObjectAddress;
8422 :
8423 50 : heap_freetuple(tuple);
8424 50 : table_close(attrelation, RowExclusiveLock);
8425 :
8426 : /*
8427 : * Recurse to propagate the identity change to partitions. Identity is not
8428 : * inherited in regular inheritance children.
8429 : */
8430 50 : if (generatedEl && recurse && ispartitioned)
8431 : {
8432 : List *children;
8433 : ListCell *lc;
8434 :
8435 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8436 :
8437 18 : foreach(lc, children)
8438 : {
8439 : Relation childrel;
8440 :
8441 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8442 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8443 12 : table_close(childrel, NoLock);
8444 : }
8445 : }
8446 :
8447 50 : return address;
8448 : }
8449 :
8450 : /*
8451 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8452 : *
8453 : * Return the address of the affected column.
8454 : */
8455 : static ObjectAddress
8456 68 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8457 : bool recurse, bool recursing)
8458 : {
8459 : HeapTuple tuple;
8460 : Form_pg_attribute attTup;
8461 : AttrNumber attnum;
8462 : Relation attrelation;
8463 : ObjectAddress address;
8464 : Oid seqid;
8465 : ObjectAddress seqaddress;
8466 : bool ispartitioned;
8467 :
8468 68 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8469 68 : if (ispartitioned && !recurse)
8470 6 : ereport(ERROR,
8471 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8472 : errmsg("cannot drop identity from a column of only the partitioned table"),
8473 : errhint("Do not specify the ONLY keyword.")));
8474 :
8475 62 : if (rel->rd_rel->relispartition && !recursing)
8476 6 : ereport(ERROR,
8477 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8478 : errmsg("cannot drop identity from a column of a partition"));
8479 :
8480 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8481 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8482 56 : if (!HeapTupleIsValid(tuple))
8483 0 : ereport(ERROR,
8484 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8485 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8486 : colName, RelationGetRelationName(rel))));
8487 :
8488 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8489 56 : attnum = attTup->attnum;
8490 :
8491 56 : if (attnum <= 0)
8492 0 : ereport(ERROR,
8493 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8494 : errmsg("cannot alter system column \"%s\"",
8495 : colName)));
8496 :
8497 56 : if (!attTup->attidentity)
8498 : {
8499 12 : if (!missing_ok)
8500 6 : ereport(ERROR,
8501 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8502 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8503 : colName, RelationGetRelationName(rel))));
8504 : else
8505 : {
8506 6 : ereport(NOTICE,
8507 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8508 : colName, RelationGetRelationName(rel))));
8509 6 : heap_freetuple(tuple);
8510 6 : table_close(attrelation, RowExclusiveLock);
8511 6 : return InvalidObjectAddress;
8512 : }
8513 : }
8514 :
8515 44 : attTup->attidentity = '\0';
8516 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8517 :
8518 44 : InvokeObjectPostAlterHook(RelationRelationId,
8519 : RelationGetRelid(rel),
8520 : attTup->attnum);
8521 44 : ObjectAddressSubSet(address, RelationRelationId,
8522 : RelationGetRelid(rel), attnum);
8523 44 : heap_freetuple(tuple);
8524 :
8525 44 : table_close(attrelation, RowExclusiveLock);
8526 :
8527 : /*
8528 : * Recurse to drop the identity from column in partitions. Identity is
8529 : * not inherited in regular inheritance children so ignore them.
8530 : */
8531 44 : if (recurse && ispartitioned)
8532 : {
8533 : List *children;
8534 : ListCell *lc;
8535 :
8536 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8537 :
8538 12 : foreach(lc, children)
8539 : {
8540 : Relation childrel;
8541 :
8542 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8543 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8544 6 : table_close(childrel, NoLock);
8545 : }
8546 : }
8547 :
8548 44 : if (!recursing)
8549 : {
8550 : /* drop the internal sequence */
8551 32 : seqid = getIdentitySequence(rel, attnum, false);
8552 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8553 : RelationRelationId, DEPENDENCY_INTERNAL);
8554 32 : CommandCounterIncrement();
8555 32 : seqaddress.classId = RelationRelationId;
8556 32 : seqaddress.objectId = seqid;
8557 32 : seqaddress.objectSubId = 0;
8558 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8559 : }
8560 :
8561 44 : return address;
8562 : }
8563 :
8564 : /*
8565 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8566 : *
8567 : * Return the address of the affected column.
8568 : */
8569 : static ObjectAddress
8570 216 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8571 : Node *newExpr, LOCKMODE lockmode)
8572 : {
8573 : HeapTuple tuple;
8574 : Form_pg_attribute attTup;
8575 : AttrNumber attnum;
8576 : char attgenerated;
8577 : bool rewrite;
8578 : Oid attrdefoid;
8579 : ObjectAddress address;
8580 : Expr *defval;
8581 : NewColumnValue *newval;
8582 : RawColumnDefault *rawEnt;
8583 :
8584 216 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8585 216 : if (!HeapTupleIsValid(tuple))
8586 0 : ereport(ERROR,
8587 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8588 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8589 : colName, RelationGetRelationName(rel))));
8590 :
8591 216 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8592 :
8593 216 : attnum = attTup->attnum;
8594 216 : if (attnum <= 0)
8595 0 : ereport(ERROR,
8596 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8597 : errmsg("cannot alter system column \"%s\"",
8598 : colName)));
8599 :
8600 216 : attgenerated = attTup->attgenerated;
8601 216 : if (!attgenerated)
8602 12 : ereport(ERROR,
8603 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8604 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8605 : colName, RelationGetRelationName(rel))));
8606 :
8607 : /*
8608 : * TODO: This could be done, just need to recheck any constraints
8609 : * afterwards.
8610 : */
8611 204 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8612 108 : rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8613 12 : ereport(ERROR,
8614 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8615 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
8616 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8617 : colName, RelationGetRelationName(rel))));
8618 :
8619 192 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8620 24 : tab->verify_new_notnull = true;
8621 :
8622 : /*
8623 : * We need to prevent this because a change of expression could affect a
8624 : * row filter and inject expressions that are not permitted in a row
8625 : * filter. XXX We could try to have a more precise check to catch only
8626 : * publications with row filters, or even re-verify the row filter
8627 : * expressions.
8628 : */
8629 288 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8630 96 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
8631 6 : ereport(ERROR,
8632 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8633 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8634 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8635 : colName, RelationGetRelationName(rel))));
8636 :
8637 186 : rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8638 :
8639 186 : ReleaseSysCache(tuple);
8640 :
8641 186 : if (rewrite)
8642 : {
8643 : /*
8644 : * Clear all the missing values if we're rewriting the table, since
8645 : * this renders them pointless.
8646 : */
8647 96 : RelationClearMissing(rel);
8648 :
8649 : /* make sure we don't conflict with later attribute modifications */
8650 96 : CommandCounterIncrement();
8651 :
8652 : /*
8653 : * Find everything that depends on the column (constraints, indexes,
8654 : * etc), and record enough information to let us recreate the objects
8655 : * after rewrite.
8656 : */
8657 96 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8658 : }
8659 :
8660 : /*
8661 : * Drop the dependency records of the GENERATED expression, in particular
8662 : * its INTERNAL dependency on the column, which would otherwise cause
8663 : * dependency.c to refuse to perform the deletion.
8664 : */
8665 186 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8666 186 : if (!OidIsValid(attrdefoid))
8667 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8668 : RelationGetRelid(rel), attnum);
8669 186 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8670 :
8671 : /* Make above changes visible */
8672 186 : CommandCounterIncrement();
8673 :
8674 : /*
8675 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8676 : * safety, but at present we do not expect anything to depend on the
8677 : * expression.
8678 : */
8679 186 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8680 : false, false);
8681 :
8682 : /* Prepare to store the new expression, in the catalogs */
8683 186 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8684 186 : rawEnt->attnum = attnum;
8685 186 : rawEnt->raw_default = newExpr;
8686 186 : rawEnt->generated = attgenerated;
8687 :
8688 : /* Store the generated expression */
8689 186 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8690 : false, true, false, NULL);
8691 :
8692 : /* Make above new expression visible */
8693 186 : CommandCounterIncrement();
8694 :
8695 186 : if (rewrite)
8696 : {
8697 : /* Prepare for table rewrite */
8698 96 : defval = (Expr *) build_column_default(rel, attnum);
8699 :
8700 96 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8701 96 : newval->attnum = attnum;
8702 96 : newval->expr = expression_planner(defval);
8703 96 : newval->is_generated = true;
8704 :
8705 96 : tab->newvals = lappend(tab->newvals, newval);
8706 96 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8707 : }
8708 :
8709 : /* Drop any pg_statistic entry for the column */
8710 186 : RemoveStatistics(RelationGetRelid(rel), attnum);
8711 :
8712 186 : InvokeObjectPostAlterHook(RelationRelationId,
8713 : RelationGetRelid(rel), attnum);
8714 :
8715 186 : ObjectAddressSubSet(address, RelationRelationId,
8716 : RelationGetRelid(rel), attnum);
8717 186 : return address;
8718 : }
8719 :
8720 : /*
8721 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8722 : */
8723 : static void
8724 86 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8725 : {
8726 : /*
8727 : * Reject ONLY if there are child tables. We could implement this, but it
8728 : * is a bit complicated. GENERATED clauses must be attached to the column
8729 : * definition and cannot be added later like DEFAULT, so if a child table
8730 : * has a generation expression that the parent does not have, the child
8731 : * column will necessarily be an attislocal column. So to implement ONLY
8732 : * here, we'd need extra code to update attislocal of the direct child
8733 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8734 : * resulting state can be properly dumped and restored.
8735 : */
8736 110 : if (!recurse &&
8737 24 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8738 12 : ereport(ERROR,
8739 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8740 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8741 :
8742 : /*
8743 : * Cannot drop generation expression from inherited columns.
8744 : */
8745 74 : if (!recursing)
8746 : {
8747 : HeapTuple tuple;
8748 : Form_pg_attribute attTup;
8749 :
8750 62 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8751 62 : if (!HeapTupleIsValid(tuple))
8752 0 : ereport(ERROR,
8753 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8754 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8755 : cmd->name, RelationGetRelationName(rel))));
8756 :
8757 62 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8758 :
8759 62 : if (attTup->attinhcount > 0)
8760 12 : ereport(ERROR,
8761 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8762 : errmsg("cannot drop generation expression from inherited column")));
8763 : }
8764 62 : }
8765 :
8766 : /*
8767 : * Return the address of the affected column.
8768 : */
8769 : static ObjectAddress
8770 56 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8771 : {
8772 : HeapTuple tuple;
8773 : Form_pg_attribute attTup;
8774 : AttrNumber attnum;
8775 : Relation attrelation;
8776 : Oid attrdefoid;
8777 : ObjectAddress address;
8778 :
8779 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8780 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8781 56 : if (!HeapTupleIsValid(tuple))
8782 0 : ereport(ERROR,
8783 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8784 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8785 : colName, RelationGetRelationName(rel))));
8786 :
8787 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8788 56 : attnum = attTup->attnum;
8789 :
8790 56 : if (attnum <= 0)
8791 0 : ereport(ERROR,
8792 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8793 : errmsg("cannot alter system column \"%s\"",
8794 : colName)));
8795 :
8796 : /*
8797 : * TODO: This could be done, but it would need a table rewrite to
8798 : * materialize the generated values. Note that for the time being, we
8799 : * still error with missing_ok, so that we don't silently leave the column
8800 : * as generated.
8801 : */
8802 56 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8803 12 : ereport(ERROR,
8804 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8805 : errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8806 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8807 : colName, RelationGetRelationName(rel))));
8808 :
8809 44 : if (!attTup->attgenerated)
8810 : {
8811 24 : if (!missing_ok)
8812 12 : ereport(ERROR,
8813 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8814 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8815 : colName, RelationGetRelationName(rel))));
8816 : else
8817 : {
8818 12 : ereport(NOTICE,
8819 : (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8820 : colName, RelationGetRelationName(rel))));
8821 12 : heap_freetuple(tuple);
8822 12 : table_close(attrelation, RowExclusiveLock);
8823 12 : return InvalidObjectAddress;
8824 : }
8825 : }
8826 :
8827 : /*
8828 : * Mark the column as no longer generated. (The atthasdef flag needs to
8829 : * get cleared too, but RemoveAttrDefault will handle that.)
8830 : */
8831 20 : attTup->attgenerated = '\0';
8832 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8833 :
8834 20 : InvokeObjectPostAlterHook(RelationRelationId,
8835 : RelationGetRelid(rel),
8836 : attnum);
8837 20 : heap_freetuple(tuple);
8838 :
8839 20 : table_close(attrelation, RowExclusiveLock);
8840 :
8841 : /*
8842 : * Drop the dependency records of the GENERATED expression, in particular
8843 : * its INTERNAL dependency on the column, which would otherwise cause
8844 : * dependency.c to refuse to perform the deletion.
8845 : */
8846 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8847 20 : if (!OidIsValid(attrdefoid))
8848 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8849 : RelationGetRelid(rel), attnum);
8850 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8851 :
8852 : /* Make above changes visible */
8853 20 : CommandCounterIncrement();
8854 :
8855 : /*
8856 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8857 : * safety, but at present we do not expect anything to depend on the
8858 : * default.
8859 : */
8860 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8861 : false, false);
8862 :
8863 20 : ObjectAddressSubSet(address, RelationRelationId,
8864 : RelationGetRelid(rel), attnum);
8865 20 : return address;
8866 : }
8867 :
8868 : /*
8869 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8870 : *
8871 : * Return value is the address of the modified column
8872 : */
8873 : static ObjectAddress
8874 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8875 : {
8876 164 : int newtarget = 0;
8877 : bool newtarget_default;
8878 : Relation attrelation;
8879 : HeapTuple tuple,
8880 : newtuple;
8881 : Form_pg_attribute attrtuple;
8882 : AttrNumber attnum;
8883 : ObjectAddress address;
8884 : Datum repl_val[Natts_pg_attribute];
8885 : bool repl_null[Natts_pg_attribute];
8886 : bool repl_repl[Natts_pg_attribute];
8887 :
8888 : /*
8889 : * We allow referencing columns by numbers only for indexes, since table
8890 : * column numbers could contain gaps if columns are later dropped.
8891 : */
8892 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8893 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8894 : !colName)
8895 0 : ereport(ERROR,
8896 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8897 : errmsg("cannot refer to non-index column by number")));
8898 :
8899 : /* -1 was used in previous versions for the default setting */
8900 164 : if (newValue && intVal(newValue) != -1)
8901 : {
8902 120 : newtarget = intVal(newValue);
8903 120 : newtarget_default = false;
8904 : }
8905 : else
8906 44 : newtarget_default = true;
8907 :
8908 164 : if (!newtarget_default)
8909 : {
8910 : /*
8911 : * Limit target to a sane range
8912 : */
8913 120 : if (newtarget < 0)
8914 : {
8915 0 : ereport(ERROR,
8916 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8917 : errmsg("statistics target %d is too low",
8918 : newtarget)));
8919 : }
8920 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8921 : {
8922 0 : newtarget = MAX_STATISTICS_TARGET;
8923 0 : ereport(WARNING,
8924 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8925 : errmsg("lowering statistics target to %d",
8926 : newtarget)));
8927 : }
8928 : }
8929 :
8930 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8931 :
8932 164 : if (colName)
8933 : {
8934 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8935 :
8936 100 : if (!HeapTupleIsValid(tuple))
8937 12 : ereport(ERROR,
8938 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8939 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8940 : colName, RelationGetRelationName(rel))));
8941 : }
8942 : else
8943 : {
8944 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8945 :
8946 64 : if (!HeapTupleIsValid(tuple))
8947 12 : ereport(ERROR,
8948 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8949 : errmsg("column number %d of relation \"%s\" does not exist",
8950 : colNum, RelationGetRelationName(rel))));
8951 : }
8952 :
8953 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8954 :
8955 140 : attnum = attrtuple->attnum;
8956 140 : if (attnum <= 0)
8957 0 : ereport(ERROR,
8958 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8959 : errmsg("cannot alter system column \"%s\"",
8960 : colName)));
8961 :
8962 : /*
8963 : * Prevent this as long as the ANALYZE code skips virtual generated
8964 : * columns.
8965 : */
8966 140 : if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8967 0 : ereport(ERROR,
8968 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8969 : errmsg("cannot alter statistics on virtual generated column \"%s\"",
8970 : colName)));
8971 :
8972 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8973 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8974 : {
8975 52 : if (attnum > rel->rd_index->indnkeyatts)
8976 6 : ereport(ERROR,
8977 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8978 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8979 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8980 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8981 18 : ereport(ERROR,
8982 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8983 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8984 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8985 : errhint("Alter statistics on table column instead.")));
8986 : }
8987 :
8988 : /* Build new tuple. */
8989 116 : memset(repl_null, false, sizeof(repl_null));
8990 116 : memset(repl_repl, false, sizeof(repl_repl));
8991 116 : if (!newtarget_default)
8992 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
8993 : else
8994 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8995 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8996 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8997 : repl_val, repl_null, repl_repl);
8998 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8999 :
9000 116 : InvokeObjectPostAlterHook(RelationRelationId,
9001 : RelationGetRelid(rel),
9002 : attrtuple->attnum);
9003 116 : ObjectAddressSubSet(address, RelationRelationId,
9004 : RelationGetRelid(rel), attnum);
9005 :
9006 116 : heap_freetuple(newtuple);
9007 :
9008 116 : ReleaseSysCache(tuple);
9009 :
9010 116 : table_close(attrelation, RowExclusiveLock);
9011 :
9012 116 : return address;
9013 : }
9014 :
9015 : /*
9016 : * Return value is the address of the modified column
9017 : */
9018 : static ObjectAddress
9019 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
9020 : bool isReset, LOCKMODE lockmode)
9021 : {
9022 : Relation attrelation;
9023 : HeapTuple tuple,
9024 : newtuple;
9025 : Form_pg_attribute attrtuple;
9026 : AttrNumber attnum;
9027 : Datum datum,
9028 : newOptions;
9029 : bool isnull;
9030 : ObjectAddress address;
9031 : Datum repl_val[Natts_pg_attribute];
9032 : bool repl_null[Natts_pg_attribute];
9033 : bool repl_repl[Natts_pg_attribute];
9034 :
9035 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9036 :
9037 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9038 :
9039 32 : if (!HeapTupleIsValid(tuple))
9040 0 : ereport(ERROR,
9041 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9042 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9043 : colName, RelationGetRelationName(rel))));
9044 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9045 :
9046 32 : attnum = attrtuple->attnum;
9047 32 : if (attnum <= 0)
9048 0 : ereport(ERROR,
9049 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9050 : errmsg("cannot alter system column \"%s\"",
9051 : colName)));
9052 :
9053 : /* Generate new proposed attoptions (text array) */
9054 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9055 : &isnull);
9056 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9057 : castNode(List, options), NULL, NULL,
9058 : false, isReset);
9059 : /* Validate new options */
9060 32 : (void) attribute_reloptions(newOptions, true);
9061 :
9062 : /* Build new tuple. */
9063 32 : memset(repl_null, false, sizeof(repl_null));
9064 32 : memset(repl_repl, false, sizeof(repl_repl));
9065 32 : if (newOptions != (Datum) 0)
9066 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9067 : else
9068 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
9069 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9070 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9071 : repl_val, repl_null, repl_repl);
9072 :
9073 : /* Update system catalog. */
9074 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9075 :
9076 32 : InvokeObjectPostAlterHook(RelationRelationId,
9077 : RelationGetRelid(rel),
9078 : attrtuple->attnum);
9079 32 : ObjectAddressSubSet(address, RelationRelationId,
9080 : RelationGetRelid(rel), attnum);
9081 :
9082 32 : heap_freetuple(newtuple);
9083 :
9084 32 : ReleaseSysCache(tuple);
9085 :
9086 32 : table_close(attrelation, RowExclusiveLock);
9087 :
9088 32 : return address;
9089 : }
9090 :
9091 : /*
9092 : * Helper function for ATExecSetStorage and ATExecSetCompression
9093 : *
9094 : * Set the attstorage and/or attcompression fields for index columns
9095 : * associated with the specified table column.
9096 : */
9097 : static void
9098 320 : SetIndexStorageProperties(Relation rel, Relation attrelation,
9099 : AttrNumber attnum,
9100 : bool setstorage, char newstorage,
9101 : bool setcompression, char newcompression,
9102 : LOCKMODE lockmode)
9103 : {
9104 : ListCell *lc;
9105 :
9106 412 : foreach(lc, RelationGetIndexList(rel))
9107 : {
9108 92 : Oid indexoid = lfirst_oid(lc);
9109 : Relation indrel;
9110 92 : AttrNumber indattnum = 0;
9111 : HeapTuple tuple;
9112 :
9113 92 : indrel = index_open(indexoid, lockmode);
9114 :
9115 154 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9116 : {
9117 98 : if (indrel->rd_index->indkey.values[i] == attnum)
9118 : {
9119 36 : indattnum = i + 1;
9120 36 : break;
9121 : }
9122 : }
9123 :
9124 92 : if (indattnum == 0)
9125 : {
9126 56 : index_close(indrel, lockmode);
9127 56 : continue;
9128 : }
9129 :
9130 36 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9131 :
9132 36 : if (HeapTupleIsValid(tuple))
9133 : {
9134 36 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9135 :
9136 36 : if (setstorage)
9137 24 : attrtuple->attstorage = newstorage;
9138 :
9139 36 : if (setcompression)
9140 12 : attrtuple->attcompression = newcompression;
9141 :
9142 36 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9143 :
9144 36 : InvokeObjectPostAlterHook(RelationRelationId,
9145 : RelationGetRelid(rel),
9146 : attrtuple->attnum);
9147 :
9148 36 : heap_freetuple(tuple);
9149 : }
9150 :
9151 36 : index_close(indrel, lockmode);
9152 : }
9153 320 : }
9154 :
9155 : /*
9156 : * ALTER TABLE ALTER COLUMN SET STORAGE
9157 : *
9158 : * Return value is the address of the modified column
9159 : */
9160 : static ObjectAddress
9161 260 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9162 : {
9163 : Relation attrelation;
9164 : HeapTuple tuple;
9165 : Form_pg_attribute attrtuple;
9166 : AttrNumber attnum;
9167 : ObjectAddress address;
9168 :
9169 260 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9170 :
9171 260 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9172 :
9173 260 : if (!HeapTupleIsValid(tuple))
9174 12 : ereport(ERROR,
9175 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9176 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9177 : colName, RelationGetRelationName(rel))));
9178 248 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9179 :
9180 248 : attnum = attrtuple->attnum;
9181 248 : if (attnum <= 0)
9182 0 : ereport(ERROR,
9183 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9184 : errmsg("cannot alter system column \"%s\"",
9185 : colName)));
9186 :
9187 248 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9188 :
9189 248 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9190 :
9191 248 : InvokeObjectPostAlterHook(RelationRelationId,
9192 : RelationGetRelid(rel),
9193 : attrtuple->attnum);
9194 :
9195 : /*
9196 : * Apply the change to indexes as well (only for simple index columns,
9197 : * matching behavior of index.c ConstructTupleDescriptor()).
9198 : */
9199 248 : SetIndexStorageProperties(rel, attrelation, attnum,
9200 248 : true, attrtuple->attstorage,
9201 : false, 0,
9202 : lockmode);
9203 :
9204 248 : heap_freetuple(tuple);
9205 :
9206 248 : table_close(attrelation, RowExclusiveLock);
9207 :
9208 248 : ObjectAddressSubSet(address, RelationRelationId,
9209 : RelationGetRelid(rel), attnum);
9210 248 : return address;
9211 : }
9212 :
9213 :
9214 : /*
9215 : * ALTER TABLE DROP COLUMN
9216 : *
9217 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9218 : * because we have to decide at runtime whether to recurse or not depending
9219 : * on whether attinhcount goes to zero or not. (We can't check this in a
9220 : * static pre-pass because it won't handle multiple inheritance situations
9221 : * correctly.)
9222 : */
9223 : static void
9224 1652 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9225 : AlterTableCmd *cmd, LOCKMODE lockmode,
9226 : AlterTableUtilityContext *context)
9227 : {
9228 1652 : if (rel->rd_rel->reloftype && !recursing)
9229 6 : ereport(ERROR,
9230 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9231 : errmsg("cannot drop column from typed table")));
9232 :
9233 1646 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9234 84 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9235 :
9236 1640 : if (recurse)
9237 1360 : cmd->recurse = true;
9238 1640 : }
9239 :
9240 : /*
9241 : * Drops column 'colName' from relation 'rel' and returns the address of the
9242 : * dropped column. The column is also dropped (or marked as no longer
9243 : * inherited from relation) from the relation's inheritance children, if any.
9244 : *
9245 : * In the recursive invocations for inheritance child relations, instead of
9246 : * dropping the column directly (if to be dropped at all), its object address
9247 : * is added to 'addrs', which must be non-NULL in such invocations. All
9248 : * columns are dropped at the same time after all the children have been
9249 : * checked recursively.
9250 : */
9251 : static ObjectAddress
9252 2196 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9253 : DropBehavior behavior,
9254 : bool recurse, bool recursing,
9255 : bool missing_ok, LOCKMODE lockmode,
9256 : ObjectAddresses *addrs)
9257 : {
9258 : HeapTuple tuple;
9259 : Form_pg_attribute targetatt;
9260 : AttrNumber attnum;
9261 : List *children;
9262 : ObjectAddress object;
9263 : bool is_expr;
9264 :
9265 : /* At top level, permission check was done in ATPrepCmd, else do it */
9266 2196 : if (recursing)
9267 556 : ATSimplePermissions(AT_DropColumn, rel,
9268 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9269 :
9270 : /* Initialize addrs on the first invocation */
9271 : Assert(!recursing || addrs != NULL);
9272 :
9273 : /* since this function recurses, it could be driven to stack overflow */
9274 2196 : check_stack_depth();
9275 :
9276 2196 : if (!recursing)
9277 1640 : addrs = new_object_addresses();
9278 :
9279 : /*
9280 : * get the number of the attribute
9281 : */
9282 2196 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9283 2196 : if (!HeapTupleIsValid(tuple))
9284 : {
9285 54 : if (!missing_ok)
9286 : {
9287 36 : ereport(ERROR,
9288 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9289 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9290 : colName, RelationGetRelationName(rel))));
9291 : }
9292 : else
9293 : {
9294 18 : ereport(NOTICE,
9295 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9296 : colName, RelationGetRelationName(rel))));
9297 18 : return InvalidObjectAddress;
9298 : }
9299 : }
9300 2142 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9301 :
9302 2142 : attnum = targetatt->attnum;
9303 :
9304 : /* Can't drop a system attribute */
9305 2142 : if (attnum <= 0)
9306 6 : ereport(ERROR,
9307 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9308 : errmsg("cannot drop system column \"%s\"",
9309 : colName)));
9310 :
9311 : /*
9312 : * Don't drop inherited columns, unless recursing (presumably from a drop
9313 : * of the parent column)
9314 : */
9315 2136 : if (targetatt->attinhcount > 0 && !recursing)
9316 48 : ereport(ERROR,
9317 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9318 : errmsg("cannot drop inherited column \"%s\"",
9319 : colName)));
9320 :
9321 : /*
9322 : * Don't drop columns used in the partition key, either. (If we let this
9323 : * go through, the key column's dependencies would cause a cascaded drop
9324 : * of the whole table, which is surely not what the user expected.)
9325 : */
9326 2088 : if (has_partition_attrs(rel,
9327 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9328 : &is_expr))
9329 30 : ereport(ERROR,
9330 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9331 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9332 : colName, RelationGetRelationName(rel))));
9333 :
9334 2058 : ReleaseSysCache(tuple);
9335 :
9336 : /*
9337 : * Propagate to children as appropriate. Unlike most other ALTER
9338 : * routines, we have to do this one level of recursion at a time; we can't
9339 : * use find_all_inheritors to do it in one pass.
9340 : */
9341 : children =
9342 2058 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9343 :
9344 2058 : if (children)
9345 : {
9346 : Relation attr_rel;
9347 : ListCell *child;
9348 :
9349 : /*
9350 : * In case of a partitioned table, the column must be dropped from the
9351 : * partitions as well.
9352 : */
9353 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9354 6 : ereport(ERROR,
9355 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9356 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9357 : errhint("Do not specify the ONLY keyword.")));
9358 :
9359 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9360 882 : foreach(child, children)
9361 : {
9362 592 : Oid childrelid = lfirst_oid(child);
9363 : Relation childrel;
9364 : Form_pg_attribute childatt;
9365 :
9366 : /* find_inheritance_children already got lock */
9367 592 : childrel = table_open(childrelid, NoLock);
9368 592 : CheckAlterTableIsSafe(childrel);
9369 :
9370 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9371 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9372 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9373 : colName, childrelid);
9374 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9375 :
9376 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9377 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9378 : childrelid, colName);
9379 :
9380 592 : if (recurse)
9381 : {
9382 : /*
9383 : * If the child column has other definition sources, just
9384 : * decrement its inheritance count; if not, recurse to delete
9385 : * it.
9386 : */
9387 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9388 : {
9389 : /* Time to delete this child column, too */
9390 556 : ATExecDropColumn(wqueue, childrel, colName,
9391 : behavior, true, true,
9392 : false, lockmode, addrs);
9393 : }
9394 : else
9395 : {
9396 : /* Child column must survive my deletion */
9397 12 : childatt->attinhcount--;
9398 :
9399 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9400 :
9401 : /* Make update visible */
9402 12 : CommandCounterIncrement();
9403 : }
9404 : }
9405 : else
9406 : {
9407 : /*
9408 : * If we were told to drop ONLY in this table (no recursion),
9409 : * we need to mark the inheritors' attributes as locally
9410 : * defined rather than inherited.
9411 : */
9412 24 : childatt->attinhcount--;
9413 24 : childatt->attislocal = true;
9414 :
9415 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9416 :
9417 : /* Make update visible */
9418 24 : CommandCounterIncrement();
9419 : }
9420 :
9421 586 : heap_freetuple(tuple);
9422 :
9423 586 : table_close(childrel, NoLock);
9424 : }
9425 290 : table_close(attr_rel, RowExclusiveLock);
9426 : }
9427 :
9428 : /* Add object to delete */
9429 2046 : object.classId = RelationRelationId;
9430 2046 : object.objectId = RelationGetRelid(rel);
9431 2046 : object.objectSubId = attnum;
9432 2046 : add_exact_object_address(&object, addrs);
9433 :
9434 2046 : if (!recursing)
9435 : {
9436 : /* Recursion has ended, drop everything that was collected */
9437 1496 : performMultipleDeletions(addrs, behavior, 0);
9438 1442 : free_object_addresses(addrs);
9439 : }
9440 :
9441 1992 : return object;
9442 : }
9443 :
9444 : /*
9445 : * Prepare to add a primary key on a table, by adding not-null constraints
9446 : * on all columns.
9447 : *
9448 : * The not-null constraints for a primary key must cover the whole inheritance
9449 : * hierarchy (failing to ensure that leads to funny corner cases). For the
9450 : * normal case where we're asked to recurse, this routine checks if the
9451 : * not-null constraints exist already, and if not queues a requirement for
9452 : * them to be created by phase 2.
9453 : *
9454 : * For the case where we're asked not to recurse, we verify that a not-null
9455 : * constraint exists on each column of each (direct) child table, throwing an
9456 : * error if not. Not throwing an error would also work, because a not-null
9457 : * constraint would be created anyway, but it'd cause a silent scan of the
9458 : * child table to verify absence of nulls. We prefer to let the user know so
9459 : * that they can add the constraint manually without having to hold
9460 : * AccessExclusiveLock while at it.
9461 : *
9462 : * However, it's also important that we do not acquire locks on children if
9463 : * the not-null constraints already exist on the parent, to avoid risking
9464 : * deadlocks during parallel pg_restore of PKs on partitioned tables.
9465 : */
9466 : static void
9467 16066 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9468 : bool recurse, LOCKMODE lockmode,
9469 : AlterTableUtilityContext *context)
9470 : {
9471 : Constraint *pkconstr;
9472 16066 : List *children = NIL;
9473 16066 : bool got_children = false;
9474 :
9475 16066 : pkconstr = castNode(Constraint, cmd->def);
9476 16066 : if (pkconstr->contype != CONSTR_PRIMARY)
9477 9212 : return;
9478 :
9479 : /* Verify that columns are not-null, or request that they be made so */
9480 14676 : foreach_node(String, column, pkconstr->keys)
9481 : {
9482 : AlterTableCmd *newcmd;
9483 : Constraint *nnconstr;
9484 : HeapTuple tuple;
9485 :
9486 : /*
9487 : * First check if a suitable constraint exists. If it does, we don't
9488 : * need to request another one. We do need to bail out if it's not
9489 : * valid, though.
9490 : */
9491 1028 : tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9492 1028 : if (tuple != NULL)
9493 : {
9494 518 : verifyNotNullPKCompatible(tuple, strVal(column));
9495 :
9496 : /* All good with this one; don't request another */
9497 506 : heap_freetuple(tuple);
9498 506 : continue;
9499 : }
9500 510 : else if (!recurse)
9501 : {
9502 : /*
9503 : * No constraint on this column. Asked not to recurse, we won't
9504 : * create one here, but verify that all children have one.
9505 : */
9506 36 : if (!got_children)
9507 : {
9508 36 : children = find_inheritance_children(RelationGetRelid(rel),
9509 : lockmode);
9510 : /* only search for children on the first time through */
9511 36 : got_children = true;
9512 : }
9513 :
9514 72 : foreach_oid(childrelid, children)
9515 : {
9516 : HeapTuple tup;
9517 :
9518 36 : tup = findNotNullConstraint(childrelid, strVal(column));
9519 36 : if (!tup)
9520 6 : ereport(ERROR,
9521 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9522 : strVal(column), get_rel_name(childrelid)));
9523 : /* verify it's good enough */
9524 30 : verifyNotNullPKCompatible(tup, strVal(column));
9525 : }
9526 : }
9527 :
9528 : /* This column is not already not-null, so add it to the queue */
9529 492 : nnconstr = makeNotNullConstraint(column);
9530 :
9531 492 : newcmd = makeNode(AlterTableCmd);
9532 492 : newcmd->subtype = AT_AddConstraint;
9533 : /* note we force recurse=true here; see above */
9534 492 : newcmd->recurse = true;
9535 492 : newcmd->def = (Node *) nnconstr;
9536 :
9537 492 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9538 : }
9539 : }
9540 :
9541 : /*
9542 : * Verify whether the given not-null constraint is compatible with a
9543 : * primary key. If not, an error is thrown.
9544 : */
9545 : static void
9546 548 : verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9547 : {
9548 548 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
9549 :
9550 548 : if (conForm->contype != CONSTRAINT_NOTNULL)
9551 0 : elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9552 :
9553 : /* a NO INHERIT constraint is no good */
9554 548 : if (conForm->connoinherit)
9555 12 : ereport(ERROR,
9556 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9557 : errmsg("cannot create primary key on column \"%s\"", colname),
9558 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9559 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9560 : NameStr(conForm->conname), colname,
9561 : get_rel_name(conForm->conrelid), "NO INHERIT"),
9562 : errhint("You might need to make the existing constraint inheritable using %s.",
9563 : "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9564 :
9565 : /* an unvalidated constraint is no good */
9566 536 : if (!conForm->convalidated)
9567 12 : ereport(ERROR,
9568 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9569 : errmsg("cannot create primary key on column \"%s\"", colname),
9570 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9571 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9572 : NameStr(conForm->conname), colname,
9573 : get_rel_name(conForm->conrelid), "NOT VALID"),
9574 : errhint("You might need to validate it using %s.",
9575 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
9576 524 : }
9577 :
9578 : /*
9579 : * ALTER TABLE ADD INDEX
9580 : *
9581 : * There is no such command in the grammar, but parse_utilcmd.c converts
9582 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9583 : * us schedule creation of the index at the appropriate time during ALTER.
9584 : *
9585 : * Return value is the address of the new index.
9586 : */
9587 : static ObjectAddress
9588 1634 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9589 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9590 : {
9591 : bool check_rights;
9592 : bool skip_build;
9593 : bool quiet;
9594 : ObjectAddress address;
9595 :
9596 : Assert(IsA(stmt, IndexStmt));
9597 : Assert(!stmt->concurrent);
9598 :
9599 : /* The IndexStmt has already been through transformIndexStmt */
9600 : Assert(stmt->transformed);
9601 :
9602 : /* suppress schema rights check when rebuilding existing index */
9603 1634 : check_rights = !is_rebuild;
9604 : /* skip index build if phase 3 will do it or we're reusing an old one */
9605 1634 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9606 : /* suppress notices when rebuilding existing index */
9607 1634 : quiet = is_rebuild;
9608 :
9609 1634 : address = DefineIndex(RelationGetRelid(rel),
9610 : stmt,
9611 : InvalidOid, /* no predefined OID */
9612 : InvalidOid, /* no parent index */
9613 : InvalidOid, /* no parent constraint */
9614 : -1, /* total_parts unknown */
9615 : true, /* is_alter_table */
9616 : check_rights,
9617 : false, /* check_not_in_use - we did it already */
9618 : skip_build,
9619 : quiet);
9620 :
9621 : /*
9622 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9623 : * new index instead of building from scratch. Restore associated fields.
9624 : * This may store InvalidSubTransactionId in both fields, in which case
9625 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9626 : * this after the CCI that made catalog rows visible to any rebuild. The
9627 : * DROP of the old edition of this index will have scheduled the storage
9628 : * for deletion at commit, so cancel that pending deletion.
9629 : */
9630 1464 : if (RelFileNumberIsValid(stmt->oldNumber))
9631 : {
9632 74 : Relation irel = index_open(address.objectId, NoLock);
9633 :
9634 74 : irel->rd_createSubid = stmt->oldCreateSubid;
9635 74 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9636 74 : RelationPreserveStorage(irel->rd_locator, true);
9637 74 : index_close(irel, NoLock);
9638 : }
9639 :
9640 1464 : return address;
9641 : }
9642 :
9643 : /*
9644 : * ALTER TABLE ADD STATISTICS
9645 : *
9646 : * This is no such command in the grammar, but we use this internally to add
9647 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9648 : * column type change.
9649 : */
9650 : static ObjectAddress
9651 26 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9652 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9653 : {
9654 : ObjectAddress address;
9655 :
9656 : Assert(IsA(stmt, CreateStatsStmt));
9657 :
9658 : /* The CreateStatsStmt has already been through transformStatsStmt */
9659 : Assert(stmt->transformed);
9660 :
9661 26 : address = CreateStatistics(stmt);
9662 :
9663 26 : return address;
9664 : }
9665 :
9666 : /*
9667 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9668 : *
9669 : * Returns the address of the new constraint.
9670 : */
9671 : static ObjectAddress
9672 10640 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9673 : IndexStmt *stmt, LOCKMODE lockmode)
9674 : {
9675 10640 : Oid index_oid = stmt->indexOid;
9676 : Relation indexRel;
9677 : char *indexName;
9678 : IndexInfo *indexInfo;
9679 : char *constraintName;
9680 : char constraintType;
9681 : ObjectAddress address;
9682 : bits16 flags;
9683 :
9684 : Assert(IsA(stmt, IndexStmt));
9685 : Assert(OidIsValid(index_oid));
9686 : Assert(stmt->isconstraint);
9687 :
9688 : /*
9689 : * Doing this on partitioned tables is not a simple feature to implement,
9690 : * so let's punt for now.
9691 : */
9692 10640 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9693 6 : ereport(ERROR,
9694 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9695 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9696 :
9697 10634 : indexRel = index_open(index_oid, AccessShareLock);
9698 :
9699 10634 : indexName = pstrdup(RelationGetRelationName(indexRel));
9700 :
9701 10634 : indexInfo = BuildIndexInfo(indexRel);
9702 :
9703 : /* this should have been checked at parse time */
9704 10634 : if (!indexInfo->ii_Unique)
9705 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9706 :
9707 : /*
9708 : * Determine name to assign to constraint. We require a constraint to
9709 : * have the same name as the underlying index; therefore, use the index's
9710 : * existing name as the default constraint name, and if the user
9711 : * explicitly gives some other name for the constraint, rename the index
9712 : * to match.
9713 : */
9714 10634 : constraintName = stmt->idxname;
9715 10634 : if (constraintName == NULL)
9716 10608 : constraintName = indexName;
9717 26 : else if (strcmp(constraintName, indexName) != 0)
9718 : {
9719 20 : ereport(NOTICE,
9720 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9721 : indexName, constraintName)));
9722 20 : RenameRelationInternal(index_oid, constraintName, false, true);
9723 : }
9724 :
9725 : /* Extra checks needed if making primary key */
9726 10634 : if (stmt->primary)
9727 6006 : index_check_primary_key(rel, indexInfo, true, stmt);
9728 :
9729 : /* Note we currently don't support EXCLUSION constraints here */
9730 10628 : if (stmt->primary)
9731 6000 : constraintType = CONSTRAINT_PRIMARY;
9732 : else
9733 4628 : constraintType = CONSTRAINT_UNIQUE;
9734 :
9735 : /* Create the catalog entries for the constraint */
9736 10628 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9737 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9738 21256 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9739 10628 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9740 10628 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9741 :
9742 10628 : address = index_constraint_create(rel,
9743 : index_oid,
9744 : InvalidOid,
9745 : indexInfo,
9746 : constraintName,
9747 : constraintType,
9748 : flags,
9749 : allowSystemTableMods,
9750 : false); /* is_internal */
9751 :
9752 10628 : index_close(indexRel, NoLock);
9753 :
9754 10628 : return address;
9755 : }
9756 :
9757 : /*
9758 : * ALTER TABLE ADD CONSTRAINT
9759 : *
9760 : * Return value is the address of the new constraint; if no constraint was
9761 : * added, InvalidObjectAddress is returned.
9762 : */
9763 : static ObjectAddress
9764 12836 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9765 : Constraint *newConstraint, bool recurse, bool is_readd,
9766 : LOCKMODE lockmode)
9767 : {
9768 12836 : ObjectAddress address = InvalidObjectAddress;
9769 :
9770 : Assert(IsA(newConstraint, Constraint));
9771 :
9772 : /*
9773 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9774 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9775 : * parse_utilcmd.c).
9776 : */
9777 12836 : switch (newConstraint->contype)
9778 : {
9779 10176 : case CONSTR_CHECK:
9780 : case CONSTR_NOTNULL:
9781 : address =
9782 10176 : ATAddCheckNNConstraint(wqueue, tab, rel,
9783 : newConstraint, recurse, false, is_readd,
9784 : lockmode);
9785 10032 : break;
9786 :
9787 2660 : case CONSTR_FOREIGN:
9788 :
9789 : /*
9790 : * Assign or validate constraint name
9791 : */
9792 2660 : if (newConstraint->conname)
9793 : {
9794 1200 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9795 : RelationGetRelid(rel),
9796 1200 : newConstraint->conname))
9797 0 : ereport(ERROR,
9798 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9799 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9800 : newConstraint->conname,
9801 : RelationGetRelationName(rel))));
9802 : }
9803 : else
9804 1460 : newConstraint->conname =
9805 1460 : ChooseConstraintName(RelationGetRelationName(rel),
9806 1460 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9807 : "fkey",
9808 1460 : RelationGetNamespace(rel),
9809 : NIL);
9810 :
9811 2660 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9812 : newConstraint,
9813 : recurse, false,
9814 : lockmode);
9815 2112 : break;
9816 :
9817 0 : default:
9818 0 : elog(ERROR, "unrecognized constraint type: %d",
9819 : (int) newConstraint->contype);
9820 : }
9821 :
9822 12144 : return address;
9823 : }
9824 :
9825 : /*
9826 : * Generate the column-name portion of the constraint name for a new foreign
9827 : * key given the list of column names that reference the referenced
9828 : * table. This will be passed to ChooseConstraintName along with the parent
9829 : * table name and the "fkey" suffix.
9830 : *
9831 : * We know that less than NAMEDATALEN characters will actually be used, so we
9832 : * can truncate the result once we've generated that many.
9833 : *
9834 : * XXX see also ChooseExtendedStatisticNameAddition and
9835 : * ChooseIndexNameAddition.
9836 : */
9837 : static char *
9838 1460 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9839 : {
9840 : char buf[NAMEDATALEN * 2];
9841 1460 : int buflen = 0;
9842 : ListCell *lc;
9843 :
9844 1460 : buf[0] = '\0';
9845 3348 : foreach(lc, colnames)
9846 : {
9847 1888 : const char *name = strVal(lfirst(lc));
9848 :
9849 1888 : if (buflen > 0)
9850 428 : buf[buflen++] = '_'; /* insert _ between names */
9851 :
9852 : /*
9853 : * At this point we have buflen <= NAMEDATALEN. name should be less
9854 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9855 : */
9856 1888 : strlcpy(buf + buflen, name, NAMEDATALEN);
9857 1888 : buflen += strlen(buf + buflen);
9858 1888 : if (buflen >= NAMEDATALEN)
9859 0 : break;
9860 : }
9861 1460 : return pstrdup(buf);
9862 : }
9863 :
9864 : /*
9865 : * Add a check or not-null constraint to a single table and its children.
9866 : * Returns the address of the constraint added to the parent relation,
9867 : * if one gets added, or InvalidObjectAddress otherwise.
9868 : *
9869 : * Subroutine for ATExecAddConstraint.
9870 : *
9871 : * We must recurse to child tables during execution, rather than using
9872 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9873 : * constraints *must* be given the same name, else they won't be seen as
9874 : * related later. If the user didn't explicitly specify a name, then
9875 : * AddRelationNewConstraints would normally assign different names to the
9876 : * child constraints. To fix that, we must capture the name assigned at
9877 : * the parent table and pass that down.
9878 : */
9879 : static ObjectAddress
9880 11100 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9881 : Constraint *constr, bool recurse, bool recursing,
9882 : bool is_readd, LOCKMODE lockmode)
9883 : {
9884 : List *newcons;
9885 : ListCell *lcon;
9886 : List *children;
9887 : ListCell *child;
9888 11100 : ObjectAddress address = InvalidObjectAddress;
9889 :
9890 : /* Guard against stack overflow due to overly deep inheritance tree. */
9891 11100 : check_stack_depth();
9892 :
9893 : /* At top level, permission check was done in ATPrepCmd, else do it */
9894 11100 : if (recursing)
9895 790 : ATSimplePermissions(AT_AddConstraint, rel,
9896 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9897 :
9898 : /*
9899 : * Call AddRelationNewConstraints to do the work, making sure it works on
9900 : * a copy of the Constraint so transformExpr can't modify the original. It
9901 : * returns a list of cooked constraints.
9902 : *
9903 : * If the constraint ends up getting merged with a pre-existing one, it's
9904 : * omitted from the returned list, which is what we want: we do not need
9905 : * to do any validation work. That can only happen at child tables,
9906 : * though, since we disallow merging at the top level.
9907 : */
9908 11100 : newcons = AddRelationNewConstraints(rel, NIL,
9909 11100 : list_make1(copyObject(constr)),
9910 11100 : recursing || is_readd, /* allow_merge */
9911 11100 : !recursing, /* is_local */
9912 : is_readd, /* is_internal */
9913 11100 : NULL); /* queryString not available
9914 : * here */
9915 :
9916 : /* we don't expect more than one constraint here */
9917 : Assert(list_length(newcons) <= 1);
9918 :
9919 : /* Add each to-be-validated constraint to Phase 3's queue */
9920 21724 : foreach(lcon, newcons)
9921 : {
9922 10762 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9923 :
9924 10762 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9925 : {
9926 : NewConstraint *newcon;
9927 :
9928 910 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9929 910 : newcon->name = ccon->name;
9930 910 : newcon->contype = ccon->contype;
9931 910 : newcon->qual = ccon->expr;
9932 :
9933 910 : tab->constraints = lappend(tab->constraints, newcon);
9934 : }
9935 :
9936 : /* Save the actually assigned name if it was defaulted */
9937 10762 : if (constr->conname == NULL)
9938 9032 : constr->conname = ccon->name;
9939 :
9940 : /*
9941 : * If adding a valid not-null constraint, set the pg_attribute flag
9942 : * and tell phase 3 to verify existing rows, if needed. For an
9943 : * invalid constraint, just set attnotnull, without queueing
9944 : * verification.
9945 : */
9946 10762 : if (constr->contype == CONSTR_NOTNULL)
9947 9314 : set_attnotnull(wqueue, rel, ccon->attnum,
9948 9314 : !constr->skip_validation,
9949 9314 : !constr->skip_validation);
9950 :
9951 10762 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9952 : }
9953 :
9954 : /* At this point we must have a locked-down name to use */
9955 : Assert(newcons == NIL || constr->conname != NULL);
9956 :
9957 : /* Advance command counter in case same table is visited multiple times */
9958 10962 : CommandCounterIncrement();
9959 :
9960 : /*
9961 : * If the constraint got merged with an existing constraint, we're done.
9962 : * We mustn't recurse to child tables in this case, because they've
9963 : * already got the constraint, and visiting them again would lead to an
9964 : * incorrect value for coninhcount.
9965 : */
9966 10962 : if (newcons == NIL)
9967 200 : return address;
9968 :
9969 : /*
9970 : * If adding a NO INHERIT constraint, no need to find our children.
9971 : */
9972 10762 : if (constr->is_no_inherit)
9973 84 : return address;
9974 :
9975 : /*
9976 : * Propagate to children as appropriate. Unlike most other ALTER
9977 : * routines, we have to do this one level of recursion at a time; we can't
9978 : * use find_all_inheritors to do it in one pass.
9979 : */
9980 : children =
9981 10678 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9982 :
9983 : /*
9984 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9985 : * constraint creation only if there are no children currently. Error out
9986 : * otherwise.
9987 : */
9988 10678 : if (!recurse && children != NIL)
9989 6 : ereport(ERROR,
9990 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9991 : errmsg("constraint must be added to child tables too")));
9992 :
9993 : /*
9994 : * Recurse to create the constraint on each child.
9995 : */
9996 11432 : foreach(child, children)
9997 : {
9998 790 : Oid childrelid = lfirst_oid(child);
9999 : Relation childrel;
10000 : AlteredTableInfo *childtab;
10001 :
10002 : /* find_inheritance_children already got lock */
10003 790 : childrel = table_open(childrelid, NoLock);
10004 790 : CheckAlterTableIsSafe(childrel);
10005 :
10006 : /* Find or create work queue entry for this table */
10007 790 : childtab = ATGetQueueEntry(wqueue, childrel);
10008 :
10009 : /* Recurse to this child */
10010 790 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
10011 : constr, recurse, true, is_readd, lockmode);
10012 :
10013 760 : table_close(childrel, NoLock);
10014 : }
10015 :
10016 10642 : return address;
10017 : }
10018 :
10019 : /*
10020 : * Add a foreign-key constraint to a single table; return the new constraint's
10021 : * address.
10022 : *
10023 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
10024 : * lock on the rel, and have done appropriate validity checks for it.
10025 : * We do permissions checks here, however.
10026 : *
10027 : * When the referenced or referencing tables (or both) are partitioned,
10028 : * multiple pg_constraint rows are required -- one for each partitioned table
10029 : * and each partition on each side (fortunately, not one for every combination
10030 : * thereof). We also need action triggers on each leaf partition on the
10031 : * referenced side, and check triggers on each leaf partition on the
10032 : * referencing side.
10033 : */
10034 : static ObjectAddress
10035 2660 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
10036 : Constraint *fkconstraint,
10037 : bool recurse, bool recursing, LOCKMODE lockmode)
10038 : {
10039 : Relation pkrel;
10040 2660 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
10041 2660 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
10042 2660 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
10043 2660 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
10044 2660 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10045 2660 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10046 2660 : Oid opclasses[INDEX_MAX_KEYS] = {0};
10047 2660 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10048 2660 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10049 2660 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10050 2660 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10051 : bool with_period;
10052 : bool pk_has_without_overlaps;
10053 : int i;
10054 : int numfks,
10055 : numpks,
10056 : numfkdelsetcols;
10057 : Oid indexOid;
10058 : bool old_check_ok;
10059 : ObjectAddress address;
10060 2660 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10061 :
10062 : /*
10063 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10064 : * delete rows out from under us.
10065 : */
10066 2660 : if (OidIsValid(fkconstraint->old_pktable_oid))
10067 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10068 : else
10069 2588 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10070 :
10071 : /*
10072 : * Validity checks (permission checks wait till we have the column
10073 : * numbers)
10074 : */
10075 2654 : if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10076 6 : ereport(ERROR,
10077 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
10078 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10079 : RelationGetRelationName(rel),
10080 : RelationGetRelationName(pkrel)));
10081 :
10082 2648 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10083 344 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10084 0 : ereport(ERROR,
10085 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10086 : errmsg("referenced relation \"%s\" is not a table",
10087 : RelationGetRelationName(pkrel))));
10088 :
10089 2648 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
10090 2 : ereport(ERROR,
10091 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10092 : errmsg("permission denied: \"%s\" is a system catalog",
10093 : RelationGetRelationName(pkrel))));
10094 :
10095 : /*
10096 : * References from permanent or unlogged tables to temp tables, and from
10097 : * permanent tables to unlogged tables, are disallowed because the
10098 : * referenced data can vanish out from under us. References from temp
10099 : * tables to any other table type are also disallowed, because other
10100 : * backends might need to run the RI triggers on the perm table, but they
10101 : * can't reliably see tuples in the local buffers of other backends.
10102 : */
10103 2646 : switch (rel->rd_rel->relpersistence)
10104 : {
10105 2356 : case RELPERSISTENCE_PERMANENT:
10106 2356 : if (!RelationIsPermanent(pkrel))
10107 0 : ereport(ERROR,
10108 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10109 : errmsg("constraints on permanent tables may reference only permanent tables")));
10110 2356 : break;
10111 12 : case RELPERSISTENCE_UNLOGGED:
10112 12 : if (!RelationIsPermanent(pkrel)
10113 12 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10114 0 : ereport(ERROR,
10115 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10116 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10117 12 : break;
10118 278 : case RELPERSISTENCE_TEMP:
10119 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10120 0 : ereport(ERROR,
10121 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10122 : errmsg("constraints on temporary tables may reference only temporary tables")));
10123 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10124 0 : ereport(ERROR,
10125 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10126 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
10127 278 : break;
10128 : }
10129 :
10130 : /*
10131 : * Look up the referencing attributes to make sure they exist, and record
10132 : * their attnums and type and collation OIDs.
10133 : */
10134 2646 : numfks = transformColumnNameList(RelationGetRelid(rel),
10135 : fkconstraint->fk_attrs,
10136 : fkattnum, fktypoid, fkcolloid);
10137 2616 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10138 2616 : if (with_period && !fkconstraint->fk_with_period)
10139 24 : ereport(ERROR,
10140 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10141 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10142 :
10143 2592 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10144 : fkconstraint->fk_del_set_cols,
10145 : fkdelsetcols, NULL, NULL);
10146 2586 : numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10147 : numfkdelsetcols,
10148 : fkdelsetcols,
10149 : fkconstraint->fk_del_set_cols);
10150 :
10151 : /*
10152 : * If the attribute list for the referenced table was omitted, lookup the
10153 : * definition of the primary key and use it. Otherwise, validate the
10154 : * supplied attribute list. In either case, discover the index OID and
10155 : * index opclasses, and the attnums and type and collation OIDs of the
10156 : * attributes.
10157 : */
10158 2580 : if (fkconstraint->pk_attrs == NIL)
10159 : {
10160 1244 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10161 : &fkconstraint->pk_attrs,
10162 : pkattnum, pktypoid, pkcolloid,
10163 : opclasses, &pk_has_without_overlaps);
10164 :
10165 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10166 1244 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10167 24 : ereport(ERROR,
10168 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10169 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10170 : }
10171 : else
10172 : {
10173 1336 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
10174 : fkconstraint->pk_attrs,
10175 : pkattnum, pktypoid, pkcolloid);
10176 :
10177 : /* Since we got pk_attrs, one should be a period. */
10178 1306 : if (with_period && !fkconstraint->pk_with_period)
10179 24 : ereport(ERROR,
10180 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10181 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10182 :
10183 : /* Look for an index matching the column list */
10184 1282 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10185 : with_period, opclasses, &pk_has_without_overlaps);
10186 : }
10187 :
10188 : /*
10189 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10190 : * must use PERIOD.
10191 : */
10192 2466 : if (pk_has_without_overlaps && !with_period)
10193 12 : ereport(ERROR,
10194 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10195 : errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10196 :
10197 : /*
10198 : * Now we can check permissions.
10199 : */
10200 2454 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10201 :
10202 : /*
10203 : * Check some things for generated columns.
10204 : */
10205 5772 : for (i = 0; i < numfks; i++)
10206 : {
10207 3348 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10208 :
10209 3348 : if (attgenerated)
10210 : {
10211 : /*
10212 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10213 : */
10214 48 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10215 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10216 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10217 12 : ereport(ERROR,
10218 : (errcode(ERRCODE_SYNTAX_ERROR),
10219 : errmsg("invalid %s action for foreign key constraint containing generated column",
10220 : "ON UPDATE")));
10221 36 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10222 24 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10223 12 : ereport(ERROR,
10224 : (errcode(ERRCODE_SYNTAX_ERROR),
10225 : errmsg("invalid %s action for foreign key constraint containing generated column",
10226 : "ON DELETE")));
10227 : }
10228 :
10229 : /*
10230 : * FKs on virtual columns are not supported. This would require
10231 : * various additional support in ri_triggers.c, including special
10232 : * handling in ri_NullCheck(), ri_KeysEqual(),
10233 : * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10234 : * as NULL there). Also not really practical as long as you can't
10235 : * index virtual columns.
10236 : */
10237 3324 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10238 6 : ereport(ERROR,
10239 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10240 : errmsg("foreign key constraints on virtual generated columns are not supported")));
10241 : }
10242 :
10243 : /*
10244 : * Some actions are currently unsupported for foreign keys using PERIOD.
10245 : */
10246 2424 : if (fkconstraint->fk_with_period)
10247 : {
10248 278 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10249 266 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10250 248 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10251 230 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10252 66 : ereport(ERROR,
10253 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10254 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10255 : "ON UPDATE"));
10256 :
10257 212 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10258 206 : fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10259 206 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10260 206 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10261 6 : ereport(ERROR,
10262 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10263 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10264 : "ON DELETE"));
10265 : }
10266 :
10267 : /*
10268 : * Look up the equality operators to use in the constraint.
10269 : *
10270 : * Note that we have to be careful about the difference between the actual
10271 : * PK column type and the opclass' declared input type, which might be
10272 : * only binary-compatible with it. The declared opcintype is the right
10273 : * thing to probe pg_amop with.
10274 : */
10275 2352 : if (numfks != numpks)
10276 0 : ereport(ERROR,
10277 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10278 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10279 :
10280 : /*
10281 : * On the strength of a previous constraint, we might avoid scanning
10282 : * tables to validate this one. See below.
10283 : */
10284 2352 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10285 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10286 :
10287 5142 : for (i = 0; i < numpks; i++)
10288 : {
10289 3030 : Oid pktype = pktypoid[i];
10290 3030 : Oid fktype = fktypoid[i];
10291 : Oid fktyped;
10292 3030 : Oid pkcoll = pkcolloid[i];
10293 3030 : Oid fkcoll = fkcolloid[i];
10294 : HeapTuple cla_ht;
10295 : Form_pg_opclass cla_tup;
10296 : Oid amid;
10297 : Oid opfamily;
10298 : Oid opcintype;
10299 : bool for_overlaps;
10300 : CompareType cmptype;
10301 : Oid pfeqop;
10302 : Oid ppeqop;
10303 : Oid ffeqop;
10304 : int16 eqstrategy;
10305 : Oid pfeqop_right;
10306 :
10307 : /* We need several fields out of the pg_opclass entry */
10308 3030 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10309 3030 : if (!HeapTupleIsValid(cla_ht))
10310 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10311 3030 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10312 3030 : amid = cla_tup->opcmethod;
10313 3030 : opfamily = cla_tup->opcfamily;
10314 3030 : opcintype = cla_tup->opcintype;
10315 3030 : ReleaseSysCache(cla_ht);
10316 :
10317 : /*
10318 : * Get strategy number from index AM.
10319 : *
10320 : * For a normal foreign-key constraint, this should not fail, since we
10321 : * already checked that the index is unique and should therefore have
10322 : * appropriate equal operators. For a period foreign key, this could
10323 : * fail if we selected a non-matching exclusion constraint earlier.
10324 : * (XXX Maybe we should do these lookups earlier so we don't end up
10325 : * doing that.)
10326 : */
10327 3030 : for_overlaps = with_period && i == numpks - 1;
10328 3030 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10329 3030 : eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10330 3030 : if (eqstrategy == InvalidStrategy)
10331 0 : ereport(ERROR,
10332 : errcode(ERRCODE_UNDEFINED_OBJECT),
10333 : for_overlaps
10334 : ? errmsg("could not identify an overlaps operator for foreign key")
10335 : : errmsg("could not identify an equality operator for foreign key"),
10336 : errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10337 : cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10338 :
10339 : /*
10340 : * There had better be a primary equality operator for the index.
10341 : * We'll use it for PK = PK comparisons.
10342 : */
10343 3030 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10344 : eqstrategy);
10345 :
10346 3030 : if (!OidIsValid(ppeqop))
10347 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10348 : eqstrategy, opcintype, opcintype, opfamily);
10349 :
10350 : /*
10351 : * Are there equality operators that take exactly the FK type? Assume
10352 : * we should look through any domain here.
10353 : */
10354 3030 : fktyped = getBaseType(fktype);
10355 :
10356 3030 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10357 : eqstrategy);
10358 3030 : if (OidIsValid(pfeqop))
10359 : {
10360 2334 : pfeqop_right = fktyped;
10361 2334 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10362 : eqstrategy);
10363 : }
10364 : else
10365 : {
10366 : /* keep compiler quiet */
10367 696 : pfeqop_right = InvalidOid;
10368 696 : ffeqop = InvalidOid;
10369 : }
10370 :
10371 3030 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10372 : {
10373 : /*
10374 : * Otherwise, look for an implicit cast from the FK type to the
10375 : * opcintype, and if found, use the primary equality operator.
10376 : * This is a bit tricky because opcintype might be a polymorphic
10377 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10378 : * whether the two actual column types can be concurrently cast to
10379 : * that type. (Otherwise, we'd fail to reject combinations such
10380 : * as int[] and point[].)
10381 : */
10382 : Oid input_typeids[2];
10383 : Oid target_typeids[2];
10384 :
10385 696 : input_typeids[0] = pktype;
10386 696 : input_typeids[1] = fktype;
10387 696 : target_typeids[0] = opcintype;
10388 696 : target_typeids[1] = opcintype;
10389 696 : if (can_coerce_type(2, input_typeids, target_typeids,
10390 : COERCION_IMPLICIT))
10391 : {
10392 468 : pfeqop = ffeqop = ppeqop;
10393 468 : pfeqop_right = opcintype;
10394 : }
10395 : }
10396 :
10397 3030 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10398 228 : ereport(ERROR,
10399 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10400 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10401 : fkconstraint->conname),
10402 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10403 : "are of incompatible types: %s and %s.",
10404 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10405 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10406 : format_type_be(fktype),
10407 : format_type_be(pktype))));
10408 :
10409 : /*
10410 : * This shouldn't be possible, but better check to make sure we have a
10411 : * consistent state for the check below.
10412 : */
10413 2802 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10414 0 : elog(ERROR, "key columns are not both collatable");
10415 :
10416 2802 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10417 : {
10418 : bool pkcolldet;
10419 : bool fkcolldet;
10420 :
10421 104 : pkcolldet = get_collation_isdeterministic(pkcoll);
10422 104 : fkcolldet = get_collation_isdeterministic(fkcoll);
10423 :
10424 : /*
10425 : * SQL requires that both collations are the same. This is
10426 : * because we need a consistent notion of equality on both
10427 : * columns. We relax this by allowing different collations if
10428 : * they are both deterministic. (This is also for backward
10429 : * compatibility, because PostgreSQL has always allowed this.)
10430 : */
10431 104 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10432 12 : ereport(ERROR,
10433 : (errcode(ERRCODE_COLLATION_MISMATCH),
10434 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10435 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10436 : "have incompatible collations: \"%s\" and \"%s\". "
10437 : "If either collation is nondeterministic, then both collations have to be the same.",
10438 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10439 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10440 : get_collation_name(fkcoll),
10441 : get_collation_name(pkcoll))));
10442 : }
10443 :
10444 2790 : if (old_check_ok)
10445 : {
10446 : /*
10447 : * When a pfeqop changes, revalidate the constraint. We could
10448 : * permit intra-opfamily changes, but that adds subtle complexity
10449 : * without any concrete benefit for core types. We need not
10450 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10451 : */
10452 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10453 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10454 : old_pfeqop_item);
10455 : }
10456 2790 : if (old_check_ok)
10457 : {
10458 : Oid old_fktype;
10459 : Oid new_fktype;
10460 : CoercionPathType old_pathtype;
10461 : CoercionPathType new_pathtype;
10462 : Oid old_castfunc;
10463 : Oid new_castfunc;
10464 : Oid old_fkcoll;
10465 : Oid new_fkcoll;
10466 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10467 6 : fkattnum[i] - 1);
10468 :
10469 : /*
10470 : * Identify coercion pathways from each of the old and new FK-side
10471 : * column types to the right (foreign) operand type of the pfeqop.
10472 : * We may assume that pg_constraint.conkey is not changing.
10473 : */
10474 6 : old_fktype = attr->atttypid;
10475 6 : new_fktype = fktype;
10476 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10477 : &old_castfunc);
10478 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10479 : &new_castfunc);
10480 :
10481 6 : old_fkcoll = attr->attcollation;
10482 6 : new_fkcoll = fkcoll;
10483 :
10484 : /*
10485 : * Upon a change to the cast from the FK column to its pfeqop
10486 : * operand, revalidate the constraint. For this evaluation, a
10487 : * binary coercion cast is equivalent to no cast at all. While
10488 : * type implementors should design implicit casts with an eye
10489 : * toward consistency of operations like equality, we cannot
10490 : * assume here that they have done so.
10491 : *
10492 : * A function with a polymorphic argument could change behavior
10493 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10494 : * when the cast destination is polymorphic, we only avoid
10495 : * revalidation if the input type has not changed at all. Given
10496 : * just the core data types and operator classes, this requirement
10497 : * prevents no would-be optimizations.
10498 : *
10499 : * If the cast converts from a base type to a domain thereon, then
10500 : * that domain type must be the opcintype of the unique index.
10501 : * Necessarily, the primary key column must then be of the domain
10502 : * type. Since the constraint was previously valid, all values on
10503 : * the foreign side necessarily exist on the primary side and in
10504 : * turn conform to the domain. Consequently, we need not treat
10505 : * domains specially here.
10506 : *
10507 : * If the collation changes, revalidation is required, unless both
10508 : * collations are deterministic, because those share the same
10509 : * notion of equality (because texteq reduces to bitwise
10510 : * equality).
10511 : *
10512 : * We need not directly consider the PK type. It's necessarily
10513 : * binary coercible to the opcintype of the unique index column,
10514 : * and ri_triggers.c will only deal with PK datums in terms of
10515 : * that opcintype. Changing the opcintype also changes pfeqop.
10516 : */
10517 6 : old_check_ok = (new_pathtype == old_pathtype &&
10518 6 : new_castfunc == old_castfunc &&
10519 6 : (!IsPolymorphicType(pfeqop_right) ||
10520 12 : new_fktype == old_fktype) &&
10521 0 : (new_fkcoll == old_fkcoll ||
10522 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10523 : }
10524 :
10525 2790 : pfeqoperators[i] = pfeqop;
10526 2790 : ppeqoperators[i] = ppeqop;
10527 2790 : ffeqoperators[i] = ffeqop;
10528 : }
10529 :
10530 : /*
10531 : * For FKs with PERIOD we need additional operators to check whether the
10532 : * referencing row's range is contained by the aggregated ranges of the
10533 : * referenced row(s). For rangetypes and multirangetypes this is
10534 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10535 : * support for now. FKs will look these up at "runtime", but we should
10536 : * make sure the lookup works here, even if we don't use the values.
10537 : */
10538 2112 : if (with_period)
10539 : {
10540 : Oid periodoperoid;
10541 : Oid aggedperiodoperoid;
10542 : Oid intersectoperoid;
10543 :
10544 188 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10545 : &intersectoperoid);
10546 : }
10547 :
10548 : /* First, create the constraint catalog entry itself. */
10549 2112 : address = addFkConstraint(addFkBothSides,
10550 : fkconstraint->conname, fkconstraint, rel, pkrel,
10551 : indexOid,
10552 : InvalidOid, /* no parent constraint */
10553 : numfks,
10554 : pkattnum,
10555 : fkattnum,
10556 : pfeqoperators,
10557 : ppeqoperators,
10558 : ffeqoperators,
10559 : numfkdelsetcols,
10560 : fkdelsetcols,
10561 : false,
10562 : with_period);
10563 :
10564 : /* Next process the action triggers at the referenced side and recurse */
10565 2112 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10566 : indexOid,
10567 : address.objectId,
10568 : numfks,
10569 : pkattnum,
10570 : fkattnum,
10571 : pfeqoperators,
10572 : ppeqoperators,
10573 : ffeqoperators,
10574 : numfkdelsetcols,
10575 : fkdelsetcols,
10576 : old_check_ok,
10577 : InvalidOid, InvalidOid,
10578 : with_period);
10579 :
10580 : /* Lastly create the check triggers at the referencing side and recurse */
10581 2112 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10582 : indexOid,
10583 : address.objectId,
10584 : numfks,
10585 : pkattnum,
10586 : fkattnum,
10587 : pfeqoperators,
10588 : ppeqoperators,
10589 : ffeqoperators,
10590 : numfkdelsetcols,
10591 : fkdelsetcols,
10592 : old_check_ok,
10593 : lockmode,
10594 : InvalidOid, InvalidOid,
10595 : with_period);
10596 :
10597 : /*
10598 : * Done. Close pk table, but keep lock until we've committed.
10599 : */
10600 2112 : table_close(pkrel, NoLock);
10601 :
10602 2112 : return address;
10603 : }
10604 :
10605 : /*
10606 : * validateFkOnDeleteSetColumns
10607 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10608 : * column lists are valid.
10609 : *
10610 : * If there are duplicates in the fksetcolsattnums[] array, this silently
10611 : * removes the dups. The new count of numfksetcols is returned.
10612 : */
10613 : static int
10614 2586 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10615 : int numfksetcols, int16 *fksetcolsattnums,
10616 : List *fksetcols)
10617 : {
10618 2586 : int numcolsout = 0;
10619 :
10620 2616 : for (int i = 0; i < numfksetcols; i++)
10621 : {
10622 36 : int16 setcol_attnum = fksetcolsattnums[i];
10623 36 : bool seen = false;
10624 :
10625 : /* Make sure it's in fkattnums[] */
10626 66 : for (int j = 0; j < numfks; j++)
10627 : {
10628 60 : if (fkattnums[j] == setcol_attnum)
10629 : {
10630 30 : seen = true;
10631 30 : break;
10632 : }
10633 : }
10634 :
10635 36 : if (!seen)
10636 : {
10637 6 : char *col = strVal(list_nth(fksetcols, i));
10638 :
10639 6 : ereport(ERROR,
10640 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10641 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10642 : }
10643 :
10644 : /* Now check for dups */
10645 30 : seen = false;
10646 30 : for (int j = 0; j < numcolsout; j++)
10647 : {
10648 6 : if (fksetcolsattnums[j] == setcol_attnum)
10649 : {
10650 6 : seen = true;
10651 6 : break;
10652 : }
10653 : }
10654 30 : if (!seen)
10655 24 : fksetcolsattnums[numcolsout++] = setcol_attnum;
10656 : }
10657 2580 : return numcolsout;
10658 : }
10659 :
10660 : /*
10661 : * addFkConstraint
10662 : * Install pg_constraint entries to implement a foreign key constraint.
10663 : * Caller must separately invoke addFkRecurseReferenced and
10664 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10665 : * and (for partitioned tables) recurse to partitions.
10666 : *
10667 : * fkside: the side of the FK (or both) to create. Caller should
10668 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10669 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10670 : * addFkBothSides.
10671 : * constraintname: the base name for the constraint being added,
10672 : * copied to fkconstraint->conname if the latter is not set
10673 : * fkconstraint: the constraint being added
10674 : * rel: the root referencing relation
10675 : * pkrel: the referenced relation; might be a partition, if recursing
10676 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10677 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10678 : * top-level constraint
10679 : * numfks: the number of columns in the foreign key
10680 : * pkattnum: the attnum array of referenced attributes
10681 : * fkattnum: the attnum array of referencing attributes
10682 : * pf/pp/ffeqoperators: OID array of operators between columns
10683 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10684 : * (...) clause
10685 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10686 : * NULL/DEFAULT clause
10687 : * with_period: true if this is a temporal FK
10688 : */
10689 : static ObjectAddress
10690 4064 : addFkConstraint(addFkConstraintSides fkside,
10691 : char *constraintname, Constraint *fkconstraint,
10692 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10693 : int numfks, int16 *pkattnum,
10694 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10695 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10696 : bool is_internal, bool with_period)
10697 : {
10698 : ObjectAddress address;
10699 : Oid constrOid;
10700 : char *conname;
10701 : bool conislocal;
10702 : int16 coninhcount;
10703 : bool connoinherit;
10704 :
10705 : /*
10706 : * Verify relkind for each referenced partition. At the top level, this
10707 : * is redundant with a previous check, but we need it when recursing.
10708 : */
10709 4064 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10710 862 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10711 0 : ereport(ERROR,
10712 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10713 : errmsg("referenced relation \"%s\" is not a table",
10714 : RelationGetRelationName(pkrel))));
10715 :
10716 : /*
10717 : * Caller supplies us with a constraint name; however, it may be used in
10718 : * this partition, so come up with a different one in that case. Unless
10719 : * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10720 : * supplied name with an underscore and digit(s) appended.
10721 : */
10722 4064 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10723 : RelationGetRelid(rel),
10724 : constraintname))
10725 1164 : conname = ChooseConstraintName(constraintname,
10726 : NULL,
10727 : "",
10728 1164 : RelationGetNamespace(rel), NIL);
10729 : else
10730 2900 : conname = constraintname;
10731 :
10732 4064 : if (fkconstraint->conname == NULL)
10733 424 : fkconstraint->conname = pstrdup(conname);
10734 :
10735 4064 : if (OidIsValid(parentConstr))
10736 : {
10737 1952 : conislocal = false;
10738 1952 : coninhcount = 1;
10739 1952 : connoinherit = false;
10740 : }
10741 : else
10742 : {
10743 2112 : conislocal = true;
10744 2112 : coninhcount = 0;
10745 :
10746 : /*
10747 : * always inherit for partitioned tables, never for legacy inheritance
10748 : */
10749 2112 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10750 : }
10751 :
10752 : /*
10753 : * Record the FK constraint in pg_constraint.
10754 : */
10755 4064 : constrOid = CreateConstraintEntry(conname,
10756 4064 : RelationGetNamespace(rel),
10757 : CONSTRAINT_FOREIGN,
10758 4064 : fkconstraint->deferrable,
10759 4064 : fkconstraint->initdeferred,
10760 4064 : fkconstraint->is_enforced,
10761 4064 : fkconstraint->initially_valid,
10762 : parentConstr,
10763 : RelationGetRelid(rel),
10764 : fkattnum,
10765 : numfks,
10766 : numfks,
10767 : InvalidOid, /* not a domain constraint */
10768 : indexOid,
10769 : RelationGetRelid(pkrel),
10770 : pkattnum,
10771 : pfeqoperators,
10772 : ppeqoperators,
10773 : ffeqoperators,
10774 : numfks,
10775 4064 : fkconstraint->fk_upd_action,
10776 4064 : fkconstraint->fk_del_action,
10777 : fkdelsetcols,
10778 : numfkdelsetcols,
10779 4064 : fkconstraint->fk_matchtype,
10780 : NULL, /* no exclusion constraint */
10781 : NULL, /* no check constraint */
10782 : NULL,
10783 : conislocal, /* islocal */
10784 : coninhcount, /* inhcount */
10785 : connoinherit, /* conNoInherit */
10786 : with_period, /* conPeriod */
10787 : is_internal); /* is_internal */
10788 :
10789 4064 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10790 :
10791 : /*
10792 : * In partitioning cases, create the dependency entries for this
10793 : * constraint. (For non-partitioned cases, relevant entries were created
10794 : * by CreateConstraintEntry.)
10795 : *
10796 : * On the referenced side, we need the constraint to have an internal
10797 : * dependency on its parent constraint; this means that this constraint
10798 : * cannot be dropped on its own -- only through the parent constraint. It
10799 : * also means the containing partition cannot be dropped on its own, but
10800 : * it can be detached, at which point this dependency is removed (after
10801 : * verifying that no rows are referenced via this FK.)
10802 : *
10803 : * When processing the referencing side, we link the constraint via the
10804 : * special partitioning dependencies: the parent constraint is the primary
10805 : * dependent, and the partition on which the foreign key exists is the
10806 : * secondary dependency. That way, this constraint is dropped if either
10807 : * of these objects is.
10808 : *
10809 : * Note that this is only necessary for the subsidiary pg_constraint rows
10810 : * in partitions; the topmost row doesn't need any of this.
10811 : */
10812 4064 : if (OidIsValid(parentConstr))
10813 : {
10814 : ObjectAddress referenced;
10815 :
10816 1952 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10817 :
10818 : Assert(fkside != addFkBothSides);
10819 1952 : if (fkside == addFkReferencedSide)
10820 1158 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10821 : else
10822 : {
10823 794 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10824 794 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10825 794 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10826 : }
10827 : }
10828 :
10829 : /* make new constraint visible, in case we add more */
10830 4064 : CommandCounterIncrement();
10831 :
10832 4064 : return address;
10833 : }
10834 :
10835 : /*
10836 : * addFkRecurseReferenced
10837 : * Recursive helper for the referenced side of foreign key creation,
10838 : * which creates the action triggers and recurses
10839 : *
10840 : * If the referenced relation is a plain relation, create the necessary action
10841 : * triggers that implement the constraint. If the referenced relation is a
10842 : * partitioned table, then we create a pg_constraint row referencing the parent
10843 : * of the referencing side for it and recurse on this routine for each
10844 : * partition.
10845 : *
10846 : * fkconstraint: the constraint being added
10847 : * rel: the root referencing relation
10848 : * pkrel: the referenced relation; might be a partition, if recursing
10849 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10850 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10851 : * top-level constraint
10852 : * numfks: the number of columns in the foreign key
10853 : * pkattnum: the attnum array of referenced attributes
10854 : * fkattnum: the attnum array of referencing attributes
10855 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10856 : * NULL/DEFAULT (...) clause
10857 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10858 : * NULL/DEFAULT clause
10859 : * pf/pp/ffeqoperators: OID array of operators between columns
10860 : * old_check_ok: true if this constraint replaces an existing one that
10861 : * was already validated (thus this one doesn't need validation)
10862 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10863 : * partition, the OIDs of the parent action triggers for DELETE and
10864 : * UPDATE respectively.
10865 : * with_period: true if this is a temporal FK
10866 : */
10867 : static void
10868 3372 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10869 : Relation pkrel, Oid indexOid, Oid parentConstr,
10870 : int numfks,
10871 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10872 : Oid *ppeqoperators, Oid *ffeqoperators,
10873 : int numfkdelsetcols, int16 *fkdelsetcols,
10874 : bool old_check_ok,
10875 : Oid parentDelTrigger, Oid parentUpdTrigger,
10876 : bool with_period)
10877 : {
10878 3372 : Oid deleteTriggerOid = InvalidOid,
10879 3372 : updateTriggerOid = InvalidOid;
10880 :
10881 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10882 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10883 :
10884 : /*
10885 : * Create action triggers to enforce the constraint, or skip them if the
10886 : * constraint is NOT ENFORCED.
10887 : */
10888 3372 : if (fkconstraint->is_enforced)
10889 3324 : createForeignKeyActionTriggers(RelationGetRelid(rel),
10890 : RelationGetRelid(pkrel),
10891 : fkconstraint,
10892 : parentConstr, indexOid,
10893 : parentDelTrigger, parentUpdTrigger,
10894 : &deleteTriggerOid, &updateTriggerOid);
10895 :
10896 : /*
10897 : * If the referenced table is partitioned, recurse on ourselves to handle
10898 : * each partition. We need one pg_constraint row created for each
10899 : * partition in addition to the pg_constraint row for the parent table.
10900 : */
10901 3372 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10902 : {
10903 552 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10904 :
10905 1488 : for (int i = 0; i < pd->nparts; i++)
10906 : {
10907 : Relation partRel;
10908 : AttrMap *map;
10909 : AttrNumber *mapped_pkattnum;
10910 : Oid partIndexId;
10911 : ObjectAddress address;
10912 :
10913 : /* XXX would it be better to acquire these locks beforehand? */
10914 936 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10915 :
10916 : /*
10917 : * Map the attribute numbers in the referenced side of the FK
10918 : * definition to match the partition's column layout.
10919 : */
10920 936 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10921 : RelationGetDescr(pkrel),
10922 : false);
10923 936 : if (map)
10924 : {
10925 130 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10926 272 : for (int j = 0; j < numfks; j++)
10927 142 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10928 : }
10929 : else
10930 806 : mapped_pkattnum = pkattnum;
10931 :
10932 : /* Determine the index to use at this level */
10933 936 : partIndexId = index_get_partition(partRel, indexOid);
10934 936 : if (!OidIsValid(partIndexId))
10935 0 : elog(ERROR, "index for %u not found in partition %s",
10936 : indexOid, RelationGetRelationName(partRel));
10937 :
10938 : /* Create entry at this level ... */
10939 936 : address = addFkConstraint(addFkReferencedSide,
10940 : fkconstraint->conname, fkconstraint, rel,
10941 : partRel, partIndexId, parentConstr,
10942 : numfks, mapped_pkattnum,
10943 : fkattnum, pfeqoperators, ppeqoperators,
10944 : ffeqoperators, numfkdelsetcols,
10945 : fkdelsetcols, true, with_period);
10946 : /* ... and recurse to our children */
10947 936 : addFkRecurseReferenced(fkconstraint, rel, partRel,
10948 : partIndexId, address.objectId, numfks,
10949 : mapped_pkattnum, fkattnum,
10950 : pfeqoperators, ppeqoperators, ffeqoperators,
10951 : numfkdelsetcols, fkdelsetcols,
10952 : old_check_ok,
10953 : deleteTriggerOid, updateTriggerOid,
10954 : with_period);
10955 :
10956 : /* Done -- clean up (but keep the lock) */
10957 936 : table_close(partRel, NoLock);
10958 936 : if (map)
10959 : {
10960 130 : pfree(mapped_pkattnum);
10961 130 : free_attrmap(map);
10962 : }
10963 : }
10964 : }
10965 3372 : }
10966 :
10967 : /*
10968 : * addFkRecurseReferencing
10969 : * Recursive helper for the referencing side of foreign key creation,
10970 : * which creates the check triggers and recurses
10971 : *
10972 : * If the referencing relation is a plain relation, create the necessary check
10973 : * triggers that implement the constraint, and set up for Phase 3 constraint
10974 : * verification. If the referencing relation is a partitioned table, then
10975 : * we create a pg_constraint row for it and recurse on this routine for each
10976 : * partition.
10977 : *
10978 : * We assume that the referenced relation is locked against concurrent
10979 : * deletions. If it's a partitioned relation, every partition must be so
10980 : * locked.
10981 : *
10982 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
10983 : * of an ALTER TABLE sequence.
10984 : * fkconstraint: the constraint being added
10985 : * rel: the referencing relation; might be a partition, if recursing
10986 : * pkrel: the root referenced relation
10987 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10988 : * parentConstr: the OID of the parent constraint (there is always one)
10989 : * numfks: the number of columns in the foreign key
10990 : * pkattnum: the attnum array of referenced attributes
10991 : * fkattnum: the attnum array of referencing attributes
10992 : * pf/pp/ffeqoperators: OID array of operators between columns
10993 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10994 : * (...) clause
10995 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10996 : * NULL/DEFAULT clause
10997 : * old_check_ok: true if this constraint replaces an existing one that
10998 : * was already validated (thus this one doesn't need validation)
10999 : * lockmode: the lockmode to acquire on partitions when recursing
11000 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
11001 : * a partition, the OIDs of the parent check triggers for INSERT and
11002 : * UPDATE respectively.
11003 : * with_period: true if this is a temporal FK
11004 : */
11005 : static void
11006 2906 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
11007 : Relation pkrel, Oid indexOid, Oid parentConstr,
11008 : int numfks, int16 *pkattnum, int16 *fkattnum,
11009 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11010 : int numfkdelsetcols, int16 *fkdelsetcols,
11011 : bool old_check_ok, LOCKMODE lockmode,
11012 : Oid parentInsTrigger, Oid parentUpdTrigger,
11013 : bool with_period)
11014 : {
11015 2906 : Oid insertTriggerOid = InvalidOid,
11016 2906 : updateTriggerOid = InvalidOid;
11017 :
11018 : Assert(OidIsValid(parentConstr));
11019 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
11020 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
11021 :
11022 2906 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11023 0 : ereport(ERROR,
11024 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11025 : errmsg("foreign key constraints are not supported on foreign tables")));
11026 :
11027 : /*
11028 : * Add check triggers if the constraint is ENFORCED, and if needed,
11029 : * schedule them to be checked in Phase 3.
11030 : *
11031 : * If the relation is partitioned, drill down to do it to its partitions.
11032 : */
11033 2906 : if (fkconstraint->is_enforced)
11034 2864 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
11035 : RelationGetRelid(pkrel),
11036 : fkconstraint,
11037 : parentConstr,
11038 : indexOid,
11039 : parentInsTrigger, parentUpdTrigger,
11040 : &insertTriggerOid, &updateTriggerOid);
11041 :
11042 2906 : if (rel->rd_rel->relkind == RELKIND_RELATION)
11043 : {
11044 : /*
11045 : * Tell Phase 3 to check that the constraint is satisfied by existing
11046 : * rows. We can skip this during table creation, when constraint is
11047 : * specified as NOT ENFORCED, or when requested explicitly by
11048 : * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11049 : * recreating a constraint following a SET DATA TYPE operation that
11050 : * did not impugn its validity.
11051 : */
11052 2428 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11053 772 : fkconstraint->is_enforced)
11054 : {
11055 : NewConstraint *newcon;
11056 : AlteredTableInfo *tab;
11057 :
11058 772 : tab = ATGetQueueEntry(wqueue, rel);
11059 :
11060 772 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11061 772 : newcon->name = get_constraint_name(parentConstr);
11062 772 : newcon->contype = CONSTR_FOREIGN;
11063 772 : newcon->refrelid = RelationGetRelid(pkrel);
11064 772 : newcon->refindid = indexOid;
11065 772 : newcon->conid = parentConstr;
11066 772 : newcon->conwithperiod = fkconstraint->fk_with_period;
11067 772 : newcon->qual = (Node *) fkconstraint;
11068 :
11069 772 : tab->constraints = lappend(tab->constraints, newcon);
11070 : }
11071 : }
11072 478 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11073 : {
11074 478 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
11075 : Relation trigrel;
11076 :
11077 : /*
11078 : * Triggers of the foreign keys will be manipulated a bunch of times
11079 : * in the loop below. To avoid repeatedly opening/closing the trigger
11080 : * catalog relation, we open it here and pass it to the subroutines
11081 : * called below.
11082 : */
11083 478 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11084 :
11085 : /*
11086 : * Recurse to take appropriate action on each partition; either we
11087 : * find an existing constraint to reparent to ours, or we create a new
11088 : * one.
11089 : */
11090 860 : for (int i = 0; i < pd->nparts; i++)
11091 : {
11092 388 : Relation partition = table_open(pd->oids[i], lockmode);
11093 : List *partFKs;
11094 : AttrMap *attmap;
11095 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11096 : bool attached;
11097 : ObjectAddress address;
11098 :
11099 388 : CheckAlterTableIsSafe(partition);
11100 :
11101 382 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
11102 : RelationGetDescr(rel),
11103 : false);
11104 986 : for (int j = 0; j < numfks; j++)
11105 604 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11106 :
11107 : /* Check whether an existing constraint can be repurposed */
11108 382 : partFKs = copyObject(RelationGetFKeyList(partition));
11109 382 : attached = false;
11110 782 : foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11111 : {
11112 30 : if (tryAttachPartitionForeignKey(wqueue,
11113 : fk,
11114 : partition,
11115 : parentConstr,
11116 : numfks,
11117 : mapped_fkattnum,
11118 : pkattnum,
11119 : pfeqoperators,
11120 : insertTriggerOid,
11121 : updateTriggerOid,
11122 : trigrel))
11123 : {
11124 12 : attached = true;
11125 12 : break;
11126 : }
11127 : }
11128 382 : if (attached)
11129 : {
11130 12 : table_close(partition, NoLock);
11131 12 : continue;
11132 : }
11133 :
11134 : /*
11135 : * No luck finding a good constraint to reuse; create our own.
11136 : */
11137 370 : address = addFkConstraint(addFkReferencingSide,
11138 : fkconstraint->conname, fkconstraint,
11139 : partition, pkrel, indexOid, parentConstr,
11140 : numfks, pkattnum,
11141 : mapped_fkattnum, pfeqoperators,
11142 : ppeqoperators, ffeqoperators,
11143 : numfkdelsetcols, fkdelsetcols, true,
11144 : with_period);
11145 :
11146 : /* call ourselves to finalize the creation and we're done */
11147 370 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11148 : indexOid,
11149 : address.objectId,
11150 : numfks,
11151 : pkattnum,
11152 : mapped_fkattnum,
11153 : pfeqoperators,
11154 : ppeqoperators,
11155 : ffeqoperators,
11156 : numfkdelsetcols,
11157 : fkdelsetcols,
11158 : old_check_ok,
11159 : lockmode,
11160 : insertTriggerOid,
11161 : updateTriggerOid,
11162 : with_period);
11163 :
11164 370 : table_close(partition, NoLock);
11165 : }
11166 :
11167 472 : table_close(trigrel, RowExclusiveLock);
11168 : }
11169 2900 : }
11170 :
11171 : /*
11172 : * CloneForeignKeyConstraints
11173 : * Clone foreign keys from a partitioned table to a newly acquired
11174 : * partition.
11175 : *
11176 : * partitionRel is a partition of parentRel, so we can be certain that it has
11177 : * the same columns with the same datatypes. The columns may be in different
11178 : * order, though.
11179 : *
11180 : * wqueue must be passed to set up phase 3 constraint checking, unless the
11181 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
11182 : * PARTITION OF).
11183 : */
11184 : static void
11185 9668 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
11186 : Relation partitionRel)
11187 : {
11188 : /* This only works for declarative partitioning */
11189 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11190 :
11191 : /*
11192 : * First, clone constraints where the parent is on the referencing side.
11193 : */
11194 9668 : CloneFkReferencing(wqueue, parentRel, partitionRel);
11195 :
11196 : /*
11197 : * Clone constraints for which the parent is on the referenced side.
11198 : */
11199 9650 : CloneFkReferenced(parentRel, partitionRel);
11200 9650 : }
11201 :
11202 : /*
11203 : * CloneFkReferenced
11204 : * Subroutine for CloneForeignKeyConstraints
11205 : *
11206 : * Find all the FKs that have the parent relation on the referenced side;
11207 : * clone those constraints to the given partition. This is to be called
11208 : * when the partition is being created or attached.
11209 : *
11210 : * This recurses to partitions, if the relation being attached is partitioned.
11211 : * Recursion is done by calling addFkRecurseReferenced.
11212 : */
11213 : static void
11214 9650 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
11215 : {
11216 : Relation pg_constraint;
11217 : AttrMap *attmap;
11218 : ListCell *cell;
11219 : SysScanDesc scan;
11220 : ScanKeyData key[2];
11221 : HeapTuple tuple;
11222 9650 : List *clone = NIL;
11223 : Relation trigrel;
11224 :
11225 : /*
11226 : * Search for any constraints where this partition's parent is in the
11227 : * referenced side. However, we must not clone any constraint whose
11228 : * parent constraint is also going to be cloned, to avoid duplicates. So
11229 : * do it in two steps: first construct the list of constraints to clone,
11230 : * then go over that list cloning those whose parents are not in the list.
11231 : * (We must not rely on the parent being seen first, since the catalog
11232 : * scan could return children first.)
11233 : */
11234 9650 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11235 9650 : ScanKeyInit(&key[0],
11236 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11237 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11238 9650 : ScanKeyInit(&key[1],
11239 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11240 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11241 : /* This is a seqscan, as we don't have a usable index ... */
11242 9650 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11243 : NULL, 2, key);
11244 10094 : while ((tuple = systable_getnext(scan)) != NULL)
11245 : {
11246 444 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11247 :
11248 444 : clone = lappend_oid(clone, constrForm->oid);
11249 : }
11250 9650 : systable_endscan(scan);
11251 9650 : table_close(pg_constraint, RowShareLock);
11252 :
11253 : /*
11254 : * Triggers of the foreign keys will be manipulated a bunch of times in
11255 : * the loop below. To avoid repeatedly opening/closing the trigger
11256 : * catalog relation, we open it here and pass it to the subroutines called
11257 : * below.
11258 : */
11259 9650 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11260 :
11261 9650 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11262 : RelationGetDescr(parentRel),
11263 : false);
11264 10094 : foreach(cell, clone)
11265 : {
11266 444 : Oid constrOid = lfirst_oid(cell);
11267 : Form_pg_constraint constrForm;
11268 : Relation fkRel;
11269 : Oid indexOid;
11270 : Oid partIndexId;
11271 : int numfks;
11272 : AttrNumber conkey[INDEX_MAX_KEYS];
11273 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11274 : AttrNumber confkey[INDEX_MAX_KEYS];
11275 : Oid conpfeqop[INDEX_MAX_KEYS];
11276 : Oid conppeqop[INDEX_MAX_KEYS];
11277 : Oid conffeqop[INDEX_MAX_KEYS];
11278 : int numfkdelsetcols;
11279 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11280 : Constraint *fkconstraint;
11281 : ObjectAddress address;
11282 444 : Oid deleteTriggerOid = InvalidOid,
11283 444 : updateTriggerOid = InvalidOid;
11284 :
11285 444 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11286 444 : if (!HeapTupleIsValid(tuple))
11287 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11288 444 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11289 :
11290 : /*
11291 : * As explained above: don't try to clone a constraint for which we're
11292 : * going to clone the parent.
11293 : */
11294 444 : if (list_member_oid(clone, constrForm->conparentid))
11295 : {
11296 222 : ReleaseSysCache(tuple);
11297 222 : continue;
11298 : }
11299 :
11300 : /* We need the same lock level that CreateTrigger will acquire */
11301 222 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11302 :
11303 222 : indexOid = constrForm->conindid;
11304 222 : DeconstructFkConstraintRow(tuple,
11305 : &numfks,
11306 : conkey,
11307 : confkey,
11308 : conpfeqop,
11309 : conppeqop,
11310 : conffeqop,
11311 : &numfkdelsetcols,
11312 : confdelsetcols);
11313 :
11314 486 : for (int i = 0; i < numfks; i++)
11315 264 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11316 :
11317 222 : fkconstraint = makeNode(Constraint);
11318 222 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11319 222 : fkconstraint->conname = NameStr(constrForm->conname);
11320 222 : fkconstraint->deferrable = constrForm->condeferrable;
11321 222 : fkconstraint->initdeferred = constrForm->condeferred;
11322 222 : fkconstraint->location = -1;
11323 222 : fkconstraint->pktable = NULL;
11324 : /* ->fk_attrs determined below */
11325 222 : fkconstraint->pk_attrs = NIL;
11326 222 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11327 222 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11328 222 : fkconstraint->fk_del_action = constrForm->confdeltype;
11329 222 : fkconstraint->fk_del_set_cols = NIL;
11330 222 : fkconstraint->old_conpfeqop = NIL;
11331 222 : fkconstraint->old_pktable_oid = InvalidOid;
11332 222 : fkconstraint->is_enforced = constrForm->conenforced;
11333 222 : fkconstraint->skip_validation = false;
11334 222 : fkconstraint->initially_valid = constrForm->convalidated;
11335 :
11336 : /* set up colnames that are used to generate the constraint name */
11337 486 : for (int i = 0; i < numfks; i++)
11338 : {
11339 : Form_pg_attribute att;
11340 :
11341 264 : att = TupleDescAttr(RelationGetDescr(fkRel),
11342 264 : conkey[i] - 1);
11343 264 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11344 264 : makeString(NameStr(att->attname)));
11345 : }
11346 :
11347 : /*
11348 : * Add the new foreign key constraint pointing to the new partition.
11349 : * Because this new partition appears in the referenced side of the
11350 : * constraint, we don't need to set up for Phase 3 check.
11351 : */
11352 222 : partIndexId = index_get_partition(partitionRel, indexOid);
11353 222 : if (!OidIsValid(partIndexId))
11354 0 : elog(ERROR, "index for %u not found in partition %s",
11355 : indexOid, RelationGetRelationName(partitionRel));
11356 :
11357 : /*
11358 : * Get the "action" triggers belonging to the constraint to pass as
11359 : * parent OIDs for similar triggers that will be created on the
11360 : * partition in addFkRecurseReferenced().
11361 : */
11362 222 : if (constrForm->conenforced)
11363 222 : GetForeignKeyActionTriggers(trigrel, constrOid,
11364 : constrForm->confrelid, constrForm->conrelid,
11365 : &deleteTriggerOid, &updateTriggerOid);
11366 :
11367 : /* Add this constraint ... */
11368 222 : address = addFkConstraint(addFkReferencedSide,
11369 : fkconstraint->conname, fkconstraint, fkRel,
11370 : partitionRel, partIndexId, constrOid,
11371 : numfks, mapped_confkey,
11372 : conkey, conpfeqop, conppeqop, conffeqop,
11373 : numfkdelsetcols, confdelsetcols, false,
11374 222 : constrForm->conperiod);
11375 : /* ... and recurse */
11376 222 : addFkRecurseReferenced(fkconstraint,
11377 : fkRel,
11378 : partitionRel,
11379 : partIndexId,
11380 : address.objectId,
11381 : numfks,
11382 : mapped_confkey,
11383 : conkey,
11384 : conpfeqop,
11385 : conppeqop,
11386 : conffeqop,
11387 : numfkdelsetcols,
11388 : confdelsetcols,
11389 : true,
11390 : deleteTriggerOid,
11391 : updateTriggerOid,
11392 222 : constrForm->conperiod);
11393 :
11394 222 : table_close(fkRel, NoLock);
11395 222 : ReleaseSysCache(tuple);
11396 : }
11397 :
11398 9650 : table_close(trigrel, RowExclusiveLock);
11399 9650 : }
11400 :
11401 : /*
11402 : * CloneFkReferencing
11403 : * Subroutine for CloneForeignKeyConstraints
11404 : *
11405 : * For each FK constraint of the parent relation in the given list, find an
11406 : * equivalent constraint in its partition relation that can be reparented;
11407 : * if one cannot be found, create a new constraint in the partition as its
11408 : * child.
11409 : *
11410 : * If wqueue is given, it is used to set up phase-3 verification for each
11411 : * cloned constraint; omit it if such verification is not needed
11412 : * (example: the partition is being created anew).
11413 : */
11414 : static void
11415 9668 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11416 : {
11417 : AttrMap *attmap;
11418 : List *partFKs;
11419 9668 : List *clone = NIL;
11420 : ListCell *cell;
11421 : Relation trigrel;
11422 :
11423 : /* obtain a list of constraints that we need to clone */
11424 10972 : foreach(cell, RelationGetFKeyList(parentRel))
11425 : {
11426 1310 : ForeignKeyCacheInfo *fk = lfirst(cell);
11427 :
11428 : /*
11429 : * Refuse to attach a table as partition that this partitioned table
11430 : * already has a foreign key to. This isn't useful schema, which is
11431 : * proven by the fact that there have been no user complaints that
11432 : * it's already impossible to achieve this in the opposite direction,
11433 : * i.e., creating a foreign key that references a partition. This
11434 : * restriction allows us to dodge some complexities around
11435 : * pg_constraint and pg_trigger row creations that would be needed
11436 : * during ATTACH/DETACH for this kind of relationship.
11437 : */
11438 1310 : if (fk->confrelid == RelationGetRelid(partRel))
11439 6 : ereport(ERROR,
11440 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11441 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11442 : RelationGetRelationName(partRel),
11443 : get_constraint_name(fk->conoid))));
11444 :
11445 1304 : clone = lappend_oid(clone, fk->conoid);
11446 : }
11447 :
11448 : /*
11449 : * Silently do nothing if there's nothing to do. In particular, this
11450 : * avoids throwing a spurious error for foreign tables.
11451 : */
11452 9662 : if (clone == NIL)
11453 9118 : return;
11454 :
11455 544 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11456 0 : ereport(ERROR,
11457 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11458 : errmsg("foreign key constraints are not supported on foreign tables")));
11459 :
11460 : /*
11461 : * Triggers of the foreign keys will be manipulated a bunch of times in
11462 : * the loop below. To avoid repeatedly opening/closing the trigger
11463 : * catalog relation, we open it here and pass it to the subroutines called
11464 : * below.
11465 : */
11466 544 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11467 :
11468 : /*
11469 : * The constraint key may differ, if the columns in the partition are
11470 : * different. This map is used to convert them.
11471 : */
11472 544 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11473 : RelationGetDescr(parentRel),
11474 : false);
11475 :
11476 544 : partFKs = copyObject(RelationGetFKeyList(partRel));
11477 :
11478 1836 : foreach(cell, clone)
11479 : {
11480 1304 : Oid parentConstrOid = lfirst_oid(cell);
11481 : Form_pg_constraint constrForm;
11482 : Relation pkrel;
11483 : HeapTuple tuple;
11484 : int numfks;
11485 : AttrNumber conkey[INDEX_MAX_KEYS];
11486 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11487 : AttrNumber confkey[INDEX_MAX_KEYS];
11488 : Oid conpfeqop[INDEX_MAX_KEYS];
11489 : Oid conppeqop[INDEX_MAX_KEYS];
11490 : Oid conffeqop[INDEX_MAX_KEYS];
11491 : int numfkdelsetcols;
11492 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11493 : Constraint *fkconstraint;
11494 : bool attached;
11495 : Oid indexOid;
11496 : ObjectAddress address;
11497 : ListCell *lc;
11498 1304 : Oid insertTriggerOid = InvalidOid,
11499 1304 : updateTriggerOid = InvalidOid;
11500 : bool with_period;
11501 :
11502 1304 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11503 1304 : if (!HeapTupleIsValid(tuple))
11504 0 : elog(ERROR, "cache lookup failed for constraint %u",
11505 : parentConstrOid);
11506 1304 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11507 :
11508 : /* Don't clone constraints whose parents are being cloned */
11509 1304 : if (list_member_oid(clone, constrForm->conparentid))
11510 : {
11511 724 : ReleaseSysCache(tuple);
11512 874 : continue;
11513 : }
11514 :
11515 : /*
11516 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11517 : * relation, that means to lock all partitions.
11518 : */
11519 580 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11520 580 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11521 250 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11522 : ShareRowExclusiveLock, NULL);
11523 :
11524 580 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11525 : conpfeqop, conppeqop, conffeqop,
11526 : &numfkdelsetcols, confdelsetcols);
11527 1394 : for (int i = 0; i < numfks; i++)
11528 814 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11529 :
11530 : /*
11531 : * Get the "check" triggers belonging to the constraint, if it is
11532 : * ENFORCED, to pass as parent OIDs for similar triggers that will be
11533 : * created on the partition in addFkRecurseReferencing(). They are
11534 : * also passed to tryAttachPartitionForeignKey() below to simply
11535 : * assign as parents to the partition's existing "check" triggers,
11536 : * that is, if the corresponding constraints is deemed attachable to
11537 : * the parent constraint.
11538 : */
11539 580 : if (constrForm->conenforced)
11540 568 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11541 : constrForm->confrelid, constrForm->conrelid,
11542 : &insertTriggerOid, &updateTriggerOid);
11543 :
11544 : /*
11545 : * Before creating a new constraint, see whether any existing FKs are
11546 : * fit for the purpose. If one is, attach the parent constraint to
11547 : * it, and don't clone anything. This way we avoid the expensive
11548 : * verification step and don't end up with a duplicate FK, and we
11549 : * don't need to recurse to partitions for this constraint.
11550 : */
11551 580 : attached = false;
11552 670 : foreach(lc, partFKs)
11553 : {
11554 246 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11555 :
11556 246 : if (tryAttachPartitionForeignKey(wqueue,
11557 : fk,
11558 : partRel,
11559 : parentConstrOid,
11560 : numfks,
11561 : mapped_conkey,
11562 : confkey,
11563 : conpfeqop,
11564 : insertTriggerOid,
11565 : updateTriggerOid,
11566 : trigrel))
11567 : {
11568 150 : attached = true;
11569 150 : table_close(pkrel, NoLock);
11570 150 : break;
11571 : }
11572 : }
11573 574 : if (attached)
11574 : {
11575 150 : ReleaseSysCache(tuple);
11576 150 : continue;
11577 : }
11578 :
11579 : /* No dice. Set up to create our own constraint */
11580 424 : fkconstraint = makeNode(Constraint);
11581 424 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11582 : /* ->conname determined below */
11583 424 : fkconstraint->deferrable = constrForm->condeferrable;
11584 424 : fkconstraint->initdeferred = constrForm->condeferred;
11585 424 : fkconstraint->location = -1;
11586 424 : fkconstraint->pktable = NULL;
11587 : /* ->fk_attrs determined below */
11588 424 : fkconstraint->pk_attrs = NIL;
11589 424 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11590 424 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11591 424 : fkconstraint->fk_del_action = constrForm->confdeltype;
11592 424 : fkconstraint->fk_del_set_cols = NIL;
11593 424 : fkconstraint->old_conpfeqop = NIL;
11594 424 : fkconstraint->old_pktable_oid = InvalidOid;
11595 424 : fkconstraint->is_enforced = constrForm->conenforced;
11596 424 : fkconstraint->skip_validation = false;
11597 424 : fkconstraint->initially_valid = constrForm->convalidated;
11598 968 : for (int i = 0; i < numfks; i++)
11599 : {
11600 : Form_pg_attribute att;
11601 :
11602 544 : att = TupleDescAttr(RelationGetDescr(partRel),
11603 544 : mapped_conkey[i] - 1);
11604 544 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11605 544 : makeString(NameStr(att->attname)));
11606 : }
11607 :
11608 424 : indexOid = constrForm->conindid;
11609 424 : with_period = constrForm->conperiod;
11610 :
11611 : /* Create the pg_constraint entry at this level */
11612 424 : address = addFkConstraint(addFkReferencingSide,
11613 424 : NameStr(constrForm->conname), fkconstraint,
11614 : partRel, pkrel, indexOid, parentConstrOid,
11615 : numfks, confkey,
11616 : mapped_conkey, conpfeqop,
11617 : conppeqop, conffeqop,
11618 : numfkdelsetcols, confdelsetcols,
11619 : false, with_period);
11620 :
11621 : /* Done with the cloned constraint's tuple */
11622 424 : ReleaseSysCache(tuple);
11623 :
11624 : /* Create the check triggers, and recurse to partitions, if any */
11625 424 : addFkRecurseReferencing(wqueue,
11626 : fkconstraint,
11627 : partRel,
11628 : pkrel,
11629 : indexOid,
11630 : address.objectId,
11631 : numfks,
11632 : confkey,
11633 : mapped_conkey,
11634 : conpfeqop,
11635 : conppeqop,
11636 : conffeqop,
11637 : numfkdelsetcols,
11638 : confdelsetcols,
11639 : false, /* no old check exists */
11640 : AccessExclusiveLock,
11641 : insertTriggerOid,
11642 : updateTriggerOid,
11643 : with_period);
11644 418 : table_close(pkrel, NoLock);
11645 : }
11646 :
11647 532 : table_close(trigrel, RowExclusiveLock);
11648 : }
11649 :
11650 : /*
11651 : * When the parent of a partition receives [the referencing side of] a foreign
11652 : * key, we must propagate that foreign key to the partition. However, the
11653 : * partition might already have an equivalent foreign key; this routine
11654 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11655 : * by the other parameters. If they are equivalent, create the link between
11656 : * the two constraints and return true.
11657 : *
11658 : * If the given FK does not match the one defined by rest of the params,
11659 : * return false.
11660 : */
11661 : static bool
11662 276 : tryAttachPartitionForeignKey(List **wqueue,
11663 : ForeignKeyCacheInfo *fk,
11664 : Relation partition,
11665 : Oid parentConstrOid,
11666 : int numfks,
11667 : AttrNumber *mapped_conkey,
11668 : AttrNumber *confkey,
11669 : Oid *conpfeqop,
11670 : Oid parentInsTrigger,
11671 : Oid parentUpdTrigger,
11672 : Relation trigrel)
11673 : {
11674 : HeapTuple parentConstrTup;
11675 : Form_pg_constraint parentConstr;
11676 : HeapTuple partcontup;
11677 : Form_pg_constraint partConstr;
11678 :
11679 276 : parentConstrTup = SearchSysCache1(CONSTROID,
11680 : ObjectIdGetDatum(parentConstrOid));
11681 276 : if (!HeapTupleIsValid(parentConstrTup))
11682 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11683 276 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11684 :
11685 : /*
11686 : * Do some quick & easy initial checks. If any of these fail, we cannot
11687 : * use this constraint.
11688 : */
11689 276 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11690 : {
11691 0 : ReleaseSysCache(parentConstrTup);
11692 0 : return false;
11693 : }
11694 768 : for (int i = 0; i < numfks; i++)
11695 : {
11696 492 : if (fk->conkey[i] != mapped_conkey[i] ||
11697 492 : fk->confkey[i] != confkey[i] ||
11698 492 : fk->conpfeqop[i] != conpfeqop[i])
11699 : {
11700 0 : ReleaseSysCache(parentConstrTup);
11701 0 : return false;
11702 : }
11703 : }
11704 :
11705 : /* Looks good so far; perform more extensive checks. */
11706 276 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11707 276 : if (!HeapTupleIsValid(partcontup))
11708 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11709 276 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11710 :
11711 : /*
11712 : * An error should be raised if the constraint enforceability is
11713 : * different. Returning false without raising an error, as we do for other
11714 : * attributes, could lead to a duplicate constraint with the same
11715 : * enforceability as the parent. While this may be acceptable, it may not
11716 : * be ideal. Therefore, it's better to raise an error and allow the user
11717 : * to correct the enforceability before proceeding.
11718 : */
11719 276 : if (partConstr->conenforced != parentConstr->conenforced)
11720 6 : ereport(ERROR,
11721 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11722 : errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11723 : NameStr(parentConstr->conname),
11724 : NameStr(partConstr->conname),
11725 : RelationGetRelationName(partition))));
11726 :
11727 270 : if (OidIsValid(partConstr->conparentid) ||
11728 240 : partConstr->condeferrable != parentConstr->condeferrable ||
11729 212 : partConstr->condeferred != parentConstr->condeferred ||
11730 212 : partConstr->confupdtype != parentConstr->confupdtype ||
11731 176 : partConstr->confdeltype != parentConstr->confdeltype ||
11732 176 : partConstr->confmatchtype != parentConstr->confmatchtype)
11733 : {
11734 108 : ReleaseSysCache(parentConstrTup);
11735 108 : ReleaseSysCache(partcontup);
11736 108 : return false;
11737 : }
11738 :
11739 162 : ReleaseSysCache(parentConstrTup);
11740 162 : ReleaseSysCache(partcontup);
11741 :
11742 : /* Looks good! Attach this constraint. */
11743 162 : AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11744 : parentConstrOid, parentInsTrigger,
11745 : parentUpdTrigger, trigrel);
11746 :
11747 162 : return true;
11748 : }
11749 :
11750 : /*
11751 : * AttachPartitionForeignKey
11752 : *
11753 : * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11754 : * attaching the constraint, removing redundant triggers and entries from
11755 : * pg_constraint, and setting the constraint's parent.
11756 : */
11757 : static void
11758 162 : AttachPartitionForeignKey(List **wqueue,
11759 : Relation partition,
11760 : Oid partConstrOid,
11761 : Oid parentConstrOid,
11762 : Oid parentInsTrigger,
11763 : Oid parentUpdTrigger,
11764 : Relation trigrel)
11765 : {
11766 : HeapTuple parentConstrTup;
11767 : Form_pg_constraint parentConstr;
11768 : HeapTuple partcontup;
11769 : Form_pg_constraint partConstr;
11770 : bool queueValidation;
11771 : Oid partConstrFrelid;
11772 : Oid partConstrRelid;
11773 : bool parentConstrIsEnforced;
11774 :
11775 : /* Fetch the parent constraint tuple */
11776 162 : parentConstrTup = SearchSysCache1(CONSTROID,
11777 : ObjectIdGetDatum(parentConstrOid));
11778 162 : if (!HeapTupleIsValid(parentConstrTup))
11779 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11780 162 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11781 162 : parentConstrIsEnforced = parentConstr->conenforced;
11782 :
11783 : /* Fetch the child constraint tuple */
11784 162 : partcontup = SearchSysCache1(CONSTROID,
11785 : ObjectIdGetDatum(partConstrOid));
11786 162 : if (!HeapTupleIsValid(partcontup))
11787 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11788 162 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11789 162 : partConstrFrelid = partConstr->confrelid;
11790 162 : partConstrRelid = partConstr->conrelid;
11791 :
11792 : /*
11793 : * If the referenced table is partitioned, then the partition we're
11794 : * attaching now has extra pg_constraint rows and action triggers that are
11795 : * no longer needed. Remove those.
11796 : */
11797 162 : if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11798 : {
11799 36 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11800 :
11801 36 : RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11802 : partConstrRelid);
11803 :
11804 36 : table_close(pg_constraint, RowShareLock);
11805 : }
11806 :
11807 : /*
11808 : * Will we need to validate this constraint? A valid parent constraint
11809 : * implies that all child constraints have been validated, so if this one
11810 : * isn't, we must trigger phase 3 validation.
11811 : */
11812 162 : queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11813 :
11814 162 : ReleaseSysCache(partcontup);
11815 162 : ReleaseSysCache(parentConstrTup);
11816 :
11817 : /*
11818 : * The action triggers in the new partition become redundant -- the parent
11819 : * table already has equivalent ones, and those will be able to reach the
11820 : * partition. Remove the ones in the partition. We identify them because
11821 : * they have our constraint OID, as well as being on the referenced rel.
11822 : */
11823 162 : DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11824 : partConstrRelid);
11825 :
11826 162 : ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11827 : RelationGetRelid(partition));
11828 :
11829 : /*
11830 : * Like the constraint, attach partition's "check" triggers to the
11831 : * corresponding parent triggers if the constraint is ENFORCED. NOT
11832 : * ENFORCED constraints do not have these triggers.
11833 : */
11834 162 : if (parentConstrIsEnforced)
11835 : {
11836 : Oid insertTriggerOid,
11837 : updateTriggerOid;
11838 :
11839 150 : GetForeignKeyCheckTriggers(trigrel,
11840 : partConstrOid, partConstrFrelid, partConstrRelid,
11841 : &insertTriggerOid, &updateTriggerOid);
11842 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11843 150 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11844 : RelationGetRelid(partition));
11845 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11846 150 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11847 : RelationGetRelid(partition));
11848 : }
11849 :
11850 : /*
11851 : * We updated this pg_constraint row above to set its parent; validating
11852 : * it will cause its convalidated flag to change, so we need CCI here. In
11853 : * addition, we need it unconditionally for the rare case where the parent
11854 : * table has *two* identical constraints; when reaching this function for
11855 : * the second one, we must have made our changes visible, otherwise we
11856 : * would try to attach both to this one.
11857 : */
11858 162 : CommandCounterIncrement();
11859 :
11860 : /* If validation is needed, put it in the queue now. */
11861 162 : if (queueValidation)
11862 : {
11863 : Relation conrel;
11864 : Oid confrelid;
11865 :
11866 18 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11867 :
11868 18 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11869 18 : if (!HeapTupleIsValid(partcontup))
11870 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11871 :
11872 18 : confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11873 :
11874 : /* Use the same lock as for AT_ValidateConstraint */
11875 18 : QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11876 : partcontup, ShareUpdateExclusiveLock);
11877 18 : ReleaseSysCache(partcontup);
11878 18 : table_close(conrel, RowExclusiveLock);
11879 : }
11880 162 : }
11881 :
11882 : /*
11883 : * RemoveInheritedConstraint
11884 : *
11885 : * Removes the constraint and its associated trigger from the specified
11886 : * relation, which inherited the given constraint.
11887 : */
11888 : static void
11889 36 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
11890 : Oid conrelid)
11891 : {
11892 : ObjectAddresses *objs;
11893 : HeapTuple consttup;
11894 : ScanKeyData key;
11895 : SysScanDesc scan;
11896 : HeapTuple trigtup;
11897 :
11898 36 : ScanKeyInit(&key,
11899 : Anum_pg_constraint_conrelid,
11900 : BTEqualStrategyNumber, F_OIDEQ,
11901 : ObjectIdGetDatum(conrelid));
11902 :
11903 36 : scan = systable_beginscan(conrel,
11904 : ConstraintRelidTypidNameIndexId,
11905 : true, NULL, 1, &key);
11906 36 : objs = new_object_addresses();
11907 324 : while ((consttup = systable_getnext(scan)) != NULL)
11908 : {
11909 288 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11910 :
11911 288 : if (conform->conparentid != conoid)
11912 210 : continue;
11913 : else
11914 : {
11915 : ObjectAddress addr;
11916 : SysScanDesc scan2;
11917 : ScanKeyData key2;
11918 : int n PG_USED_FOR_ASSERTS_ONLY;
11919 :
11920 78 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11921 78 : add_exact_object_address(&addr, objs);
11922 :
11923 : /*
11924 : * First we must delete the dependency record that binds the
11925 : * constraint records together.
11926 : */
11927 78 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11928 : conform->oid,
11929 : DEPENDENCY_INTERNAL,
11930 : ConstraintRelationId,
11931 : conoid);
11932 : Assert(n == 1); /* actually only one is expected */
11933 :
11934 : /*
11935 : * Now search for the triggers for this constraint and set them up
11936 : * for deletion too
11937 : */
11938 78 : ScanKeyInit(&key2,
11939 : Anum_pg_trigger_tgconstraint,
11940 : BTEqualStrategyNumber, F_OIDEQ,
11941 : ObjectIdGetDatum(conform->oid));
11942 78 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11943 : true, NULL, 1, &key2);
11944 234 : while ((trigtup = systable_getnext(scan2)) != NULL)
11945 : {
11946 156 : ObjectAddressSet(addr, TriggerRelationId,
11947 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11948 156 : add_exact_object_address(&addr, objs);
11949 : }
11950 78 : systable_endscan(scan2);
11951 : }
11952 : }
11953 : /* make the dependency deletions visible */
11954 36 : CommandCounterIncrement();
11955 36 : performMultipleDeletions(objs, DROP_RESTRICT,
11956 : PERFORM_DELETION_INTERNAL);
11957 36 : systable_endscan(scan);
11958 36 : }
11959 :
11960 : /*
11961 : * DropForeignKeyConstraintTriggers
11962 : *
11963 : * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11964 : * action triggers for the foreign key constraint.
11965 : *
11966 : * If valid confrelid and conrelid values are not provided, the respective
11967 : * trigger check will be skipped, and the trigger will be considered for
11968 : * removal.
11969 : */
11970 : static void
11971 234 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
11972 : Oid conrelid)
11973 : {
11974 : ScanKeyData key;
11975 : SysScanDesc scan;
11976 : HeapTuple trigtup;
11977 :
11978 234 : ScanKeyInit(&key,
11979 : Anum_pg_trigger_tgconstraint,
11980 : BTEqualStrategyNumber, F_OIDEQ,
11981 : ObjectIdGetDatum(conoid));
11982 234 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11983 : NULL, 1, &key);
11984 1014 : while ((trigtup = systable_getnext(scan)) != NULL)
11985 : {
11986 780 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11987 : ObjectAddress trigger;
11988 :
11989 : /* Invalid if trigger is not for a referential integrity constraint */
11990 780 : if (!OidIsValid(trgform->tgconstrrelid))
11991 300 : continue;
11992 780 : if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
11993 300 : continue;
11994 480 : if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
11995 0 : continue;
11996 :
11997 : /* We should be dropping trigger related to foreign key constraint */
11998 : Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
11999 : trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12000 : trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12001 : trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12002 : trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12003 : trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12004 : trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12005 : trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12006 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12007 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12008 : trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12009 : trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12010 :
12011 : /*
12012 : * The constraint is originally set up to contain this trigger as an
12013 : * implementation object, so there's a dependency record that links
12014 : * the two; however, since the trigger is no longer needed, we remove
12015 : * the dependency link in order to be able to drop the trigger while
12016 : * keeping the constraint intact.
12017 : */
12018 480 : deleteDependencyRecordsFor(TriggerRelationId,
12019 : trgform->oid,
12020 : false);
12021 : /* make dependency deletion visible to performDeletion */
12022 480 : CommandCounterIncrement();
12023 480 : ObjectAddressSet(trigger, TriggerRelationId,
12024 : trgform->oid);
12025 480 : performDeletion(&trigger, DROP_RESTRICT, 0);
12026 : /* make trigger drop visible, in case the loop iterates */
12027 480 : CommandCounterIncrement();
12028 : }
12029 :
12030 234 : systable_endscan(scan);
12031 234 : }
12032 :
12033 : /*
12034 : * GetForeignKeyActionTriggers
12035 : * Returns delete and update "action" triggers of the given relation
12036 : * belonging to the given constraint
12037 : */
12038 : static void
12039 222 : GetForeignKeyActionTriggers(Relation trigrel,
12040 : Oid conoid, Oid confrelid, Oid conrelid,
12041 : Oid *deleteTriggerOid,
12042 : Oid *updateTriggerOid)
12043 : {
12044 : ScanKeyData key;
12045 : SysScanDesc scan;
12046 : HeapTuple trigtup;
12047 :
12048 222 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12049 222 : ScanKeyInit(&key,
12050 : Anum_pg_trigger_tgconstraint,
12051 : BTEqualStrategyNumber, F_OIDEQ,
12052 : ObjectIdGetDatum(conoid));
12053 :
12054 222 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12055 : NULL, 1, &key);
12056 454 : while ((trigtup = systable_getnext(scan)) != NULL)
12057 : {
12058 454 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12059 :
12060 454 : if (trgform->tgconstrrelid != conrelid)
12061 8 : continue;
12062 446 : if (trgform->tgrelid != confrelid)
12063 0 : continue;
12064 : /* Only ever look at "action" triggers on the PK side. */
12065 446 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12066 2 : continue;
12067 444 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
12068 : {
12069 : Assert(*deleteTriggerOid == InvalidOid);
12070 222 : *deleteTriggerOid = trgform->oid;
12071 : }
12072 222 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12073 : {
12074 : Assert(*updateTriggerOid == InvalidOid);
12075 222 : *updateTriggerOid = trgform->oid;
12076 : }
12077 : #ifndef USE_ASSERT_CHECKING
12078 : /* In an assert-enabled build, continue looking to find duplicates */
12079 444 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12080 222 : break;
12081 : #endif
12082 : }
12083 :
12084 222 : if (!OidIsValid(*deleteTriggerOid))
12085 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12086 : conoid);
12087 222 : if (!OidIsValid(*updateTriggerOid))
12088 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12089 : conoid);
12090 :
12091 222 : systable_endscan(scan);
12092 222 : }
12093 :
12094 : /*
12095 : * GetForeignKeyCheckTriggers
12096 : * Returns insert and update "check" triggers of the given relation
12097 : * belonging to the given constraint
12098 : */
12099 : static void
12100 820 : GetForeignKeyCheckTriggers(Relation trigrel,
12101 : Oid conoid, Oid confrelid, Oid conrelid,
12102 : Oid *insertTriggerOid,
12103 : Oid *updateTriggerOid)
12104 : {
12105 : ScanKeyData key;
12106 : SysScanDesc scan;
12107 : HeapTuple trigtup;
12108 :
12109 820 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
12110 820 : ScanKeyInit(&key,
12111 : Anum_pg_trigger_tgconstraint,
12112 : BTEqualStrategyNumber, F_OIDEQ,
12113 : ObjectIdGetDatum(conoid));
12114 :
12115 820 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12116 : NULL, 1, &key);
12117 2630 : while ((trigtup = systable_getnext(scan)) != NULL)
12118 : {
12119 2630 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12120 :
12121 2630 : if (trgform->tgconstrrelid != confrelid)
12122 882 : continue;
12123 1748 : if (trgform->tgrelid != conrelid)
12124 0 : continue;
12125 : /* Only ever look at "check" triggers on the FK side. */
12126 1748 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12127 108 : continue;
12128 1640 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
12129 : {
12130 : Assert(*insertTriggerOid == InvalidOid);
12131 820 : *insertTriggerOid = trgform->oid;
12132 : }
12133 820 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12134 : {
12135 : Assert(*updateTriggerOid == InvalidOid);
12136 820 : *updateTriggerOid = trgform->oid;
12137 : }
12138 : #ifndef USE_ASSERT_CHECKING
12139 : /* In an assert-enabled build, continue looking to find duplicates. */
12140 1640 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12141 820 : break;
12142 : #endif
12143 : }
12144 :
12145 820 : if (!OidIsValid(*insertTriggerOid))
12146 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12147 : conoid);
12148 820 : if (!OidIsValid(*updateTriggerOid))
12149 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12150 : conoid);
12151 :
12152 820 : systable_endscan(scan);
12153 820 : }
12154 :
12155 : /*
12156 : * ALTER TABLE ALTER CONSTRAINT
12157 : *
12158 : * Update the attributes of a constraint.
12159 : *
12160 : * Currently only works for Foreign Key and not null constraints.
12161 : *
12162 : * If the constraint is modified, returns its address; otherwise, return
12163 : * InvalidObjectAddress.
12164 : */
12165 : static ObjectAddress
12166 288 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
12167 : bool recurse, LOCKMODE lockmode)
12168 : {
12169 : Relation conrel;
12170 : Relation tgrel;
12171 : SysScanDesc scan;
12172 : ScanKeyData skey[3];
12173 : HeapTuple contuple;
12174 : Form_pg_constraint currcon;
12175 : ObjectAddress address;
12176 :
12177 : /*
12178 : * Disallow altering ONLY a partitioned table, as it would make no sense.
12179 : * This is okay for legacy inheritance.
12180 : */
12181 288 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12182 0 : ereport(ERROR,
12183 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12184 : errmsg("constraint must be altered in child tables too"),
12185 : errhint("Do not specify the ONLY keyword."));
12186 :
12187 :
12188 288 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12189 288 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12190 :
12191 : /*
12192 : * Find and check the target constraint
12193 : */
12194 288 : ScanKeyInit(&skey[0],
12195 : Anum_pg_constraint_conrelid,
12196 : BTEqualStrategyNumber, F_OIDEQ,
12197 : ObjectIdGetDatum(RelationGetRelid(rel)));
12198 288 : ScanKeyInit(&skey[1],
12199 : Anum_pg_constraint_contypid,
12200 : BTEqualStrategyNumber, F_OIDEQ,
12201 : ObjectIdGetDatum(InvalidOid));
12202 288 : ScanKeyInit(&skey[2],
12203 : Anum_pg_constraint_conname,
12204 : BTEqualStrategyNumber, F_NAMEEQ,
12205 288 : CStringGetDatum(cmdcon->conname));
12206 288 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12207 : true, NULL, 3, skey);
12208 :
12209 : /* There can be at most one matching row */
12210 288 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12211 6 : ereport(ERROR,
12212 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12213 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12214 : cmdcon->conname, RelationGetRelationName(rel))));
12215 :
12216 282 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12217 282 : if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12218 0 : ereport(ERROR,
12219 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12220 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12221 : cmdcon->conname, RelationGetRelationName(rel))));
12222 282 : if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12223 12 : ereport(ERROR,
12224 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12225 : errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12226 : cmdcon->conname, RelationGetRelationName(rel))));
12227 270 : if (cmdcon->alterInheritability &&
12228 90 : currcon->contype != CONSTRAINT_NOTNULL)
12229 24 : ereport(ERROR,
12230 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12231 : errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12232 : cmdcon->conname, RelationGetRelationName(rel)));
12233 :
12234 : /* Refuse to modify inheritability of inherited constraints */
12235 246 : if (cmdcon->alterInheritability &&
12236 66 : cmdcon->noinherit && currcon->coninhcount > 0)
12237 6 : ereport(ERROR,
12238 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12239 : errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12240 : NameStr(currcon->conname),
12241 : RelationGetRelationName(rel)));
12242 :
12243 : /*
12244 : * If it's not the topmost constraint, raise an error.
12245 : *
12246 : * Altering a non-topmost constraint leaves some triggers untouched, since
12247 : * they are not directly connected to this constraint; also, pg_dump would
12248 : * ignore the deferrability status of the individual constraint, since it
12249 : * only dumps topmost constraints. Avoid these problems by refusing this
12250 : * operation and telling the user to alter the parent constraint instead.
12251 : */
12252 240 : if (OidIsValid(currcon->conparentid))
12253 : {
12254 : HeapTuple tp;
12255 12 : Oid parent = currcon->conparentid;
12256 12 : char *ancestorname = NULL;
12257 12 : char *ancestortable = NULL;
12258 :
12259 : /* Loop to find the topmost constraint */
12260 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12261 : {
12262 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
12263 :
12264 : /* If no parent, this is the constraint we want */
12265 24 : if (!OidIsValid(contup->conparentid))
12266 : {
12267 12 : ancestorname = pstrdup(NameStr(contup->conname));
12268 12 : ancestortable = get_rel_name(contup->conrelid);
12269 12 : ReleaseSysCache(tp);
12270 12 : break;
12271 : }
12272 :
12273 12 : parent = contup->conparentid;
12274 12 : ReleaseSysCache(tp);
12275 : }
12276 :
12277 12 : ereport(ERROR,
12278 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12279 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12280 : cmdcon->conname, RelationGetRelationName(rel)),
12281 : ancestorname && ancestortable ?
12282 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12283 : cmdcon->conname, ancestorname, ancestortable) : 0,
12284 : errhint("You may alter the constraint it derives from instead.")));
12285 : }
12286 :
12287 228 : address = InvalidObjectAddress;
12288 :
12289 : /*
12290 : * Do the actual catalog work, and recurse if necessary.
12291 : */
12292 228 : if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12293 : contuple, recurse, lockmode))
12294 216 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12295 :
12296 222 : systable_endscan(scan);
12297 :
12298 222 : table_close(tgrel, RowExclusiveLock);
12299 222 : table_close(conrel, RowExclusiveLock);
12300 :
12301 222 : return address;
12302 : }
12303 :
12304 : /*
12305 : * A subroutine of ATExecAlterConstraint that calls the respective routines for
12306 : * altering constraint's enforceability, deferrability or inheritability.
12307 : */
12308 : static bool
12309 228 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
12310 : Relation conrel, Relation tgrel, Relation rel,
12311 : HeapTuple contuple, bool recurse,
12312 : LOCKMODE lockmode)
12313 : {
12314 : Form_pg_constraint currcon;
12315 228 : bool changed = false;
12316 228 : List *otherrelids = NIL;
12317 :
12318 228 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12319 :
12320 : /*
12321 : * Do the catalog work for the enforceability or deferrability change,
12322 : * recurse if necessary.
12323 : *
12324 : * Note that even if deferrability is requested to be altered along with
12325 : * enforceability, we don't need to explicitly update multiple entries in
12326 : * pg_trigger related to deferrability.
12327 : *
12328 : * Modifying enforceability involves either creating or dropping the
12329 : * trigger, during which the deferrability setting will be adjusted
12330 : * automatically.
12331 : */
12332 300 : if (cmdcon->alterEnforceability &&
12333 72 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12334 : currcon->conrelid, currcon->confrelid,
12335 : contuple, lockmode, InvalidOid,
12336 : InvalidOid, InvalidOid, InvalidOid))
12337 66 : changed = true;
12338 :
12339 258 : else if (cmdcon->alterDeferrability &&
12340 96 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12341 : contuple, recurse, &otherrelids,
12342 : lockmode))
12343 : {
12344 : /*
12345 : * AlterConstrUpdateConstraintEntry already invalidated relcache for
12346 : * the relations having the constraint itself; here we also invalidate
12347 : * for relations that have any triggers that are part of the
12348 : * constraint.
12349 : */
12350 306 : foreach_oid(relid, otherrelids)
12351 114 : CacheInvalidateRelcacheByRelid(relid);
12352 :
12353 96 : changed = true;
12354 : }
12355 :
12356 : /*
12357 : * Do the catalog work for the inheritability change.
12358 : */
12359 282 : if (cmdcon->alterInheritability &&
12360 60 : ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12361 : lockmode))
12362 54 : changed = true;
12363 :
12364 222 : return changed;
12365 : }
12366 :
12367 : /*
12368 : * Returns true if the constraint's enforceability is altered.
12369 : *
12370 : * Depending on whether the constraint is being set to ENFORCED or NOT
12371 : * ENFORCED, it creates or drops the trigger accordingly.
12372 : *
12373 : * Note that we must recurse even when trying to change a constraint to not
12374 : * enforced if it is already not enforced, in case descendant constraints
12375 : * might be enforced and need to be changed to not enforced. Conversely, we
12376 : * should do nothing if a constraint is being set to enforced and is already
12377 : * enforced, as descendant constraints cannot be different in that case.
12378 : */
12379 : static bool
12380 168 : ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
12381 : Relation conrel, Relation tgrel,
12382 : Oid fkrelid, Oid pkrelid,
12383 : HeapTuple contuple, LOCKMODE lockmode,
12384 : Oid ReferencedParentDelTrigger,
12385 : Oid ReferencedParentUpdTrigger,
12386 : Oid ReferencingParentInsTrigger,
12387 : Oid ReferencingParentUpdTrigger)
12388 : {
12389 : Form_pg_constraint currcon;
12390 : Oid conoid;
12391 : Relation rel;
12392 168 : bool changed = false;
12393 :
12394 : /* Since this function recurses, it could be driven to stack overflow */
12395 168 : check_stack_depth();
12396 :
12397 : Assert(cmdcon->alterEnforceability);
12398 :
12399 168 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12400 168 : conoid = currcon->oid;
12401 :
12402 : /* Should be foreign key constraint */
12403 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12404 :
12405 168 : rel = table_open(currcon->conrelid, lockmode);
12406 :
12407 168 : if (currcon->conenforced != cmdcon->is_enforced)
12408 : {
12409 162 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12410 162 : changed = true;
12411 : }
12412 :
12413 : /* Drop triggers */
12414 168 : if (!cmdcon->is_enforced)
12415 : {
12416 : /*
12417 : * When setting a constraint to NOT ENFORCED, the constraint triggers
12418 : * need to be dropped. Therefore, we must process the child relations
12419 : * first, followed by the parent, to account for dependencies.
12420 : */
12421 126 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12422 54 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12423 18 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12424 : fkrelid, pkrelid, contuple,
12425 : lockmode, InvalidOid, InvalidOid,
12426 : InvalidOid, InvalidOid);
12427 :
12428 : /* Drop all the triggers */
12429 72 : DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
12430 : }
12431 96 : else if (changed) /* Create triggers */
12432 : {
12433 96 : Oid ReferencedDelTriggerOid = InvalidOid,
12434 96 : ReferencedUpdTriggerOid = InvalidOid,
12435 96 : ReferencingInsTriggerOid = InvalidOid,
12436 96 : ReferencingUpdTriggerOid = InvalidOid;
12437 :
12438 : /* Prepare the minimal information required for trigger creation. */
12439 96 : Constraint *fkconstraint = makeNode(Constraint);
12440 :
12441 96 : fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12442 96 : fkconstraint->fk_matchtype = currcon->confmatchtype;
12443 96 : fkconstraint->fk_upd_action = currcon->confupdtype;
12444 96 : fkconstraint->fk_del_action = currcon->confdeltype;
12445 :
12446 : /* Create referenced triggers */
12447 96 : if (currcon->conrelid == fkrelid)
12448 54 : createForeignKeyActionTriggers(currcon->conrelid,
12449 : currcon->confrelid,
12450 : fkconstraint,
12451 : conoid,
12452 : currcon->conindid,
12453 : ReferencedParentDelTrigger,
12454 : ReferencedParentUpdTrigger,
12455 : &ReferencedDelTriggerOid,
12456 : &ReferencedUpdTriggerOid);
12457 :
12458 : /* Create referencing triggers */
12459 96 : if (currcon->confrelid == pkrelid)
12460 84 : createForeignKeyCheckTriggers(currcon->conrelid,
12461 : pkrelid,
12462 : fkconstraint,
12463 : conoid,
12464 : currcon->conindid,
12465 : ReferencingParentInsTrigger,
12466 : ReferencingParentUpdTrigger,
12467 : &ReferencingInsTriggerOid,
12468 : &ReferencingUpdTriggerOid);
12469 :
12470 : /*
12471 : * Tell Phase 3 to check that the constraint is satisfied by existing
12472 : * rows. Only applies to leaf partitions, and (for constraints that
12473 : * reference a partitioned table) only if this is not one of the
12474 : * pg_constraint rows that exist solely to support action triggers.
12475 : */
12476 96 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
12477 78 : currcon->confrelid == pkrelid)
12478 : {
12479 : AlteredTableInfo *tab;
12480 : NewConstraint *newcon;
12481 :
12482 66 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12483 66 : newcon->name = fkconstraint->conname;
12484 66 : newcon->contype = CONSTR_FOREIGN;
12485 66 : newcon->refrelid = currcon->confrelid;
12486 66 : newcon->refindid = currcon->conindid;
12487 66 : newcon->conid = currcon->oid;
12488 66 : newcon->qual = (Node *) fkconstraint;
12489 :
12490 : /* Find or create work queue entry for this table */
12491 66 : tab = ATGetQueueEntry(wqueue, rel);
12492 66 : tab->constraints = lappend(tab->constraints, newcon);
12493 : }
12494 :
12495 : /*
12496 : * If the table at either end of the constraint is partitioned, we
12497 : * need to recurse and create triggers for each constraint that is a
12498 : * child of this one.
12499 : */
12500 174 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12501 78 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12502 24 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12503 : fkrelid, pkrelid, contuple,
12504 : lockmode, ReferencedDelTriggerOid,
12505 : ReferencedUpdTriggerOid,
12506 : ReferencingInsTriggerOid,
12507 : ReferencingUpdTriggerOid);
12508 : }
12509 :
12510 168 : table_close(rel, NoLock);
12511 :
12512 168 : return changed;
12513 : }
12514 :
12515 : /*
12516 : * Returns true if the constraint's deferrability is altered.
12517 : *
12518 : * *otherrelids is appended OIDs of relations containing affected triggers.
12519 : *
12520 : * Note that we must recurse even when the values are correct, in case
12521 : * indirect descendants have had their constraints altered locally.
12522 : * (This could be avoided if we forbade altering constraints in partitions
12523 : * but existing releases don't do that.)
12524 : */
12525 : static bool
12526 162 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
12527 : Relation conrel, Relation tgrel, Relation rel,
12528 : HeapTuple contuple, bool recurse,
12529 : List **otherrelids, LOCKMODE lockmode)
12530 : {
12531 : Form_pg_constraint currcon;
12532 : Oid refrelid;
12533 162 : bool changed = false;
12534 :
12535 : /* since this function recurses, it could be driven to stack overflow */
12536 162 : check_stack_depth();
12537 :
12538 : Assert(cmdcon->alterDeferrability);
12539 :
12540 162 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12541 162 : refrelid = currcon->confrelid;
12542 :
12543 : /* Should be foreign key constraint */
12544 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12545 :
12546 : /*
12547 : * If called to modify a constraint that's already in the desired state,
12548 : * silently do nothing.
12549 : */
12550 162 : if (currcon->condeferrable != cmdcon->deferrable ||
12551 6 : currcon->condeferred != cmdcon->initdeferred)
12552 : {
12553 162 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12554 162 : changed = true;
12555 :
12556 : /*
12557 : * Now we need to update the multiple entries in pg_trigger that
12558 : * implement the constraint.
12559 : */
12560 162 : AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12561 162 : cmdcon->deferrable,
12562 162 : cmdcon->initdeferred, otherrelids);
12563 : }
12564 :
12565 : /*
12566 : * If the table at either end of the constraint is partitioned, we need to
12567 : * handle every constraint that is a child of this one.
12568 : */
12569 162 : if (recurse && changed &&
12570 300 : (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12571 138 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12572 42 : AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12573 : contuple, recurse, otherrelids,
12574 : lockmode);
12575 :
12576 162 : return changed;
12577 : }
12578 :
12579 : /*
12580 : * Returns true if the constraint's inheritability is altered.
12581 : */
12582 : static bool
12583 60 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
12584 : Relation conrel, Relation rel,
12585 : HeapTuple contuple, LOCKMODE lockmode)
12586 : {
12587 : Form_pg_constraint currcon;
12588 : AttrNumber colNum;
12589 : char *colName;
12590 : List *children;
12591 :
12592 : Assert(cmdcon->alterInheritability);
12593 :
12594 60 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12595 :
12596 : /* The current implementation only works for NOT NULL constraints */
12597 : Assert(currcon->contype == CONSTRAINT_NOTNULL);
12598 :
12599 : /*
12600 : * If called to modify a constraint that's already in the desired state,
12601 : * silently do nothing.
12602 : */
12603 60 : if (cmdcon->noinherit == currcon->connoinherit)
12604 0 : return false;
12605 :
12606 60 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12607 60 : CommandCounterIncrement();
12608 :
12609 : /* Fetch the column number and name */
12610 60 : colNum = extractNotNullColumn(contuple);
12611 60 : colName = get_attname(currcon->conrelid, colNum, false);
12612 :
12613 : /*
12614 : * Propagate the change to children. For this subcommand type we don't
12615 : * recursively affect children, just the immediate level.
12616 : */
12617 60 : children = find_inheritance_children(RelationGetRelid(rel),
12618 : lockmode);
12619 192 : foreach_oid(childoid, children)
12620 : {
12621 : ObjectAddress addr;
12622 :
12623 84 : if (cmdcon->noinherit)
12624 : {
12625 : HeapTuple childtup;
12626 : Form_pg_constraint childcon;
12627 :
12628 30 : childtup = findNotNullConstraint(childoid, colName);
12629 30 : if (!childtup)
12630 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12631 : colName, childoid);
12632 30 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12633 : Assert(childcon->coninhcount > 0);
12634 30 : childcon->coninhcount--;
12635 30 : childcon->conislocal = true;
12636 30 : CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12637 30 : heap_freetuple(childtup);
12638 : }
12639 : else
12640 : {
12641 54 : Relation childrel = table_open(childoid, NoLock);
12642 :
12643 54 : addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12644 : colName, true, true, lockmode);
12645 48 : if (OidIsValid(addr.objectId))
12646 48 : CommandCounterIncrement();
12647 48 : table_close(childrel, NoLock);
12648 : }
12649 : }
12650 :
12651 54 : return true;
12652 : }
12653 :
12654 : /*
12655 : * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12656 : * trigger's deferrability.
12657 : *
12658 : * The arguments to this function have the same meaning as the arguments to
12659 : * ATExecAlterConstrDeferrability.
12660 : */
12661 : static void
12662 162 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12663 : bool deferrable, bool initdeferred,
12664 : List **otherrelids)
12665 : {
12666 : HeapTuple tgtuple;
12667 : ScanKeyData tgkey;
12668 : SysScanDesc tgscan;
12669 :
12670 162 : ScanKeyInit(&tgkey,
12671 : Anum_pg_trigger_tgconstraint,
12672 : BTEqualStrategyNumber, F_OIDEQ,
12673 : ObjectIdGetDatum(conoid));
12674 162 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12675 : NULL, 1, &tgkey);
12676 630 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12677 : {
12678 468 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12679 : Form_pg_trigger copy_tg;
12680 : HeapTuple tgCopyTuple;
12681 :
12682 : /*
12683 : * Remember OIDs of other relation(s) involved in FK constraint.
12684 : * (Note: it's likely that we could skip forcing a relcache inval for
12685 : * other rels that don't have a trigger whose properties change, but
12686 : * let's be conservative.)
12687 : */
12688 468 : if (tgform->tgrelid != RelationGetRelid(rel))
12689 228 : *otherrelids = list_append_unique_oid(*otherrelids,
12690 : tgform->tgrelid);
12691 :
12692 : /*
12693 : * Update enable status and deferrability of RI_FKey_noaction_del,
12694 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12695 : * triggers, but not others; see createForeignKeyActionTriggers and
12696 : * CreateFKCheckTrigger.
12697 : */
12698 468 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12699 372 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12700 258 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12701 138 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12702 18 : continue;
12703 :
12704 450 : tgCopyTuple = heap_copytuple(tgtuple);
12705 450 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12706 :
12707 450 : copy_tg->tgdeferrable = deferrable;
12708 450 : copy_tg->tginitdeferred = initdeferred;
12709 450 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12710 :
12711 450 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12712 :
12713 450 : heap_freetuple(tgCopyTuple);
12714 : }
12715 :
12716 162 : systable_endscan(tgscan);
12717 162 : }
12718 :
12719 : /*
12720 : * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12721 : * the specified constraint.
12722 : *
12723 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12724 : * list of child relations and recursing; instead it uses the conparentid
12725 : * relationships. This may need to be reconsidered.
12726 : *
12727 : * The arguments to this function have the same meaning as the arguments to
12728 : * ATExecAlterConstrEnforceability.
12729 : */
12730 : static void
12731 42 : AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12732 : Relation conrel, Relation tgrel,
12733 : Oid fkrelid, Oid pkrelid,
12734 : HeapTuple contuple, LOCKMODE lockmode,
12735 : Oid ReferencedParentDelTrigger,
12736 : Oid ReferencedParentUpdTrigger,
12737 : Oid ReferencingParentInsTrigger,
12738 : Oid ReferencingParentUpdTrigger)
12739 : {
12740 : Form_pg_constraint currcon;
12741 : Oid conoid;
12742 : ScanKeyData pkey;
12743 : SysScanDesc pscan;
12744 : HeapTuple childtup;
12745 :
12746 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12747 42 : conoid = currcon->oid;
12748 :
12749 42 : ScanKeyInit(&pkey,
12750 : Anum_pg_constraint_conparentid,
12751 : BTEqualStrategyNumber, F_OIDEQ,
12752 : ObjectIdGetDatum(conoid));
12753 :
12754 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12755 : true, NULL, 1, &pkey);
12756 :
12757 138 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12758 96 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12759 : pkrelid, childtup, lockmode,
12760 : ReferencedParentDelTrigger,
12761 : ReferencedParentUpdTrigger,
12762 : ReferencingParentInsTrigger,
12763 : ReferencingParentUpdTrigger);
12764 :
12765 42 : systable_endscan(pscan);
12766 42 : }
12767 :
12768 : /*
12769 : * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12770 : * the specified constraint.
12771 : *
12772 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12773 : * list of child relations and recursing; instead it uses the conparentid
12774 : * relationships. This may need to be reconsidered.
12775 : *
12776 : * The arguments to this function have the same meaning as the arguments to
12777 : * ATExecAlterConstrDeferrability.
12778 : */
12779 : static void
12780 42 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12781 : Relation conrel, Relation tgrel, Relation rel,
12782 : HeapTuple contuple, bool recurse,
12783 : List **otherrelids, LOCKMODE lockmode)
12784 : {
12785 : Form_pg_constraint currcon;
12786 : Oid conoid;
12787 : ScanKeyData pkey;
12788 : SysScanDesc pscan;
12789 : HeapTuple childtup;
12790 :
12791 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12792 42 : conoid = currcon->oid;
12793 :
12794 42 : ScanKeyInit(&pkey,
12795 : Anum_pg_constraint_conparentid,
12796 : BTEqualStrategyNumber, F_OIDEQ,
12797 : ObjectIdGetDatum(conoid));
12798 :
12799 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12800 : true, NULL, 1, &pkey);
12801 :
12802 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12803 : {
12804 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12805 : Relation childrel;
12806 :
12807 66 : childrel = table_open(childcon->conrelid, lockmode);
12808 :
12809 66 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12810 : childtup, recurse, otherrelids, lockmode);
12811 66 : table_close(childrel, NoLock);
12812 : }
12813 :
12814 42 : systable_endscan(pscan);
12815 42 : }
12816 :
12817 : /*
12818 : * Update the constraint entry for the given ATAlterConstraint command, and
12819 : * invoke the appropriate hooks.
12820 : */
12821 : static void
12822 384 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
12823 : HeapTuple contuple)
12824 : {
12825 : HeapTuple copyTuple;
12826 : Form_pg_constraint copy_con;
12827 :
12828 : Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12829 : cmdcon->alterInheritability);
12830 :
12831 384 : copyTuple = heap_copytuple(contuple);
12832 384 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12833 :
12834 384 : if (cmdcon->alterEnforceability)
12835 : {
12836 162 : copy_con->conenforced = cmdcon->is_enforced;
12837 :
12838 : /*
12839 : * NB: The convalidated status is irrelevant when the constraint is
12840 : * set to NOT ENFORCED, but for consistency, it should still be set
12841 : * appropriately. Similarly, if the constraint is later changed to
12842 : * ENFORCED, validation will be performed during phase 3, so it makes
12843 : * sense to mark it as valid in that case.
12844 : */
12845 162 : copy_con->convalidated = cmdcon->is_enforced;
12846 : }
12847 384 : if (cmdcon->alterDeferrability)
12848 : {
12849 168 : copy_con->condeferrable = cmdcon->deferrable;
12850 168 : copy_con->condeferred = cmdcon->initdeferred;
12851 : }
12852 384 : if (cmdcon->alterInheritability)
12853 60 : copy_con->connoinherit = cmdcon->noinherit;
12854 :
12855 384 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12856 384 : InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12857 :
12858 : /* Make new constraint flags visible to others */
12859 384 : CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12860 :
12861 384 : heap_freetuple(copyTuple);
12862 384 : }
12863 :
12864 : /*
12865 : * ALTER TABLE VALIDATE CONSTRAINT
12866 : *
12867 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12868 : * there's no good way to skip recursing when handling foreign keys: there is
12869 : * no need to lock children in that case, yet we wouldn't be able to avoid
12870 : * doing so at that level.
12871 : *
12872 : * Return value is the address of the validated constraint. If the constraint
12873 : * was already validated, InvalidObjectAddress is returned.
12874 : */
12875 : static ObjectAddress
12876 584 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12877 : bool recurse, bool recursing, LOCKMODE lockmode)
12878 : {
12879 : Relation conrel;
12880 : SysScanDesc scan;
12881 : ScanKeyData skey[3];
12882 : HeapTuple tuple;
12883 : Form_pg_constraint con;
12884 : ObjectAddress address;
12885 :
12886 584 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12887 :
12888 : /*
12889 : * Find and check the target constraint
12890 : */
12891 584 : ScanKeyInit(&skey[0],
12892 : Anum_pg_constraint_conrelid,
12893 : BTEqualStrategyNumber, F_OIDEQ,
12894 : ObjectIdGetDatum(RelationGetRelid(rel)));
12895 584 : ScanKeyInit(&skey[1],
12896 : Anum_pg_constraint_contypid,
12897 : BTEqualStrategyNumber, F_OIDEQ,
12898 : ObjectIdGetDatum(InvalidOid));
12899 584 : ScanKeyInit(&skey[2],
12900 : Anum_pg_constraint_conname,
12901 : BTEqualStrategyNumber, F_NAMEEQ,
12902 : CStringGetDatum(constrName));
12903 584 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12904 : true, NULL, 3, skey);
12905 :
12906 : /* There can be at most one matching row */
12907 584 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12908 0 : ereport(ERROR,
12909 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12910 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12911 : constrName, RelationGetRelationName(rel))));
12912 :
12913 584 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12914 584 : if (con->contype != CONSTRAINT_FOREIGN &&
12915 256 : con->contype != CONSTRAINT_CHECK &&
12916 112 : con->contype != CONSTRAINT_NOTNULL)
12917 0 : ereport(ERROR,
12918 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12919 : errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12920 : constrName, RelationGetRelationName(rel)),
12921 : errdetail("This operation is not supported for this type of constraint."));
12922 :
12923 584 : if (!con->conenforced)
12924 6 : ereport(ERROR,
12925 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12926 : errmsg("cannot validate NOT ENFORCED constraint")));
12927 :
12928 578 : if (!con->convalidated)
12929 : {
12930 560 : if (con->contype == CONSTRAINT_FOREIGN)
12931 : {
12932 322 : QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12933 : tuple, lockmode);
12934 : }
12935 238 : else if (con->contype == CONSTRAINT_CHECK)
12936 : {
12937 126 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12938 : tuple, recurse, recursing, lockmode);
12939 : }
12940 112 : else if (con->contype == CONSTRAINT_NOTNULL)
12941 : {
12942 112 : QueueNNConstraintValidation(wqueue, conrel, rel,
12943 : tuple, recurse, recursing, lockmode);
12944 : }
12945 :
12946 560 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12947 : }
12948 : else
12949 18 : address = InvalidObjectAddress; /* already validated */
12950 :
12951 578 : systable_endscan(scan);
12952 :
12953 578 : table_close(conrel, RowExclusiveLock);
12954 :
12955 578 : return address;
12956 : }
12957 :
12958 : /*
12959 : * QueueFKConstraintValidation
12960 : *
12961 : * Add an entry to the wqueue to validate the given foreign key constraint in
12962 : * Phase 3 and update the convalidated field in the pg_constraint catalog
12963 : * for the specified relation and all its children.
12964 : */
12965 : static void
12966 394 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
12967 : Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
12968 : {
12969 : Form_pg_constraint con;
12970 : AlteredTableInfo *tab;
12971 : HeapTuple copyTuple;
12972 : Form_pg_constraint copy_con;
12973 :
12974 394 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12975 : Assert(con->contype == CONSTRAINT_FOREIGN);
12976 : Assert(!con->convalidated);
12977 :
12978 : /*
12979 : * Add the validation to phase 3's queue; not needed for partitioned
12980 : * tables themselves, only for their partitions.
12981 : *
12982 : * When the referenced table (pkrelid) is partitioned, the referencing
12983 : * table (fkrel) has one pg_constraint row pointing to each partition
12984 : * thereof. These rows are there only to support action triggers and no
12985 : * table scan is needed, therefore skip this for them as well.
12986 : */
12987 394 : if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
12988 346 : con->confrelid == pkrelid)
12989 : {
12990 : NewConstraint *newcon;
12991 : Constraint *fkconstraint;
12992 :
12993 : /* Queue validation for phase 3 */
12994 334 : fkconstraint = makeNode(Constraint);
12995 : /* for now this is all we need */
12996 334 : fkconstraint->conname = pstrdup(NameStr(con->conname));
12997 :
12998 334 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12999 334 : newcon->name = fkconstraint->conname;
13000 334 : newcon->contype = CONSTR_FOREIGN;
13001 334 : newcon->refrelid = con->confrelid;
13002 334 : newcon->refindid = con->conindid;
13003 334 : newcon->conid = con->oid;
13004 334 : newcon->qual = (Node *) fkconstraint;
13005 :
13006 : /* Find or create work queue entry for this table */
13007 334 : tab = ATGetQueueEntry(wqueue, fkrel);
13008 334 : tab->constraints = lappend(tab->constraints, newcon);
13009 : }
13010 :
13011 : /*
13012 : * If the table at either end of the constraint is partitioned, we need to
13013 : * recurse and handle every unvalidate constraint that is a child of this
13014 : * constraint.
13015 : */
13016 740 : if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13017 346 : get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13018 : {
13019 : ScanKeyData pkey;
13020 : SysScanDesc pscan;
13021 : HeapTuple childtup;
13022 :
13023 72 : ScanKeyInit(&pkey,
13024 : Anum_pg_constraint_conparentid,
13025 : BTEqualStrategyNumber, F_OIDEQ,
13026 : ObjectIdGetDatum(con->oid));
13027 :
13028 72 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13029 : true, NULL, 1, &pkey);
13030 :
13031 144 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13032 : {
13033 : Form_pg_constraint childcon;
13034 : Relation childrel;
13035 :
13036 72 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13037 :
13038 : /*
13039 : * If the child constraint has already been validated, no further
13040 : * action is required for it or its descendants, as they are all
13041 : * valid.
13042 : */
13043 72 : if (childcon->convalidated)
13044 18 : continue;
13045 :
13046 54 : childrel = table_open(childcon->conrelid, lockmode);
13047 :
13048 : /*
13049 : * NB: Note that pkrelid should be passed as-is during recursion,
13050 : * as it is required to identify the root referenced table.
13051 : */
13052 54 : QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13053 : childtup, lockmode);
13054 54 : table_close(childrel, NoLock);
13055 : }
13056 :
13057 72 : systable_endscan(pscan);
13058 : }
13059 :
13060 : /*
13061 : * Now mark the pg_constraint row as validated (even if we didn't check,
13062 : * notably the ones for partitions on the referenced side).
13063 : *
13064 : * We rely on transaction abort to roll back this change if phase 3
13065 : * ultimately finds violating rows. This is a bit ugly.
13066 : */
13067 394 : copyTuple = heap_copytuple(contuple);
13068 394 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13069 394 : copy_con->convalidated = true;
13070 394 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13071 :
13072 394 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13073 :
13074 394 : heap_freetuple(copyTuple);
13075 394 : }
13076 :
13077 : /*
13078 : * QueueCheckConstraintValidation
13079 : *
13080 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
13081 : * and update the convalidated field in the pg_constraint catalog for the
13082 : * specified relation and all its inheriting children.
13083 : */
13084 : static void
13085 126 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13086 : char *constrName, HeapTuple contuple,
13087 : bool recurse, bool recursing, LOCKMODE lockmode)
13088 : {
13089 : Form_pg_constraint con;
13090 : AlteredTableInfo *tab;
13091 : HeapTuple copyTuple;
13092 : Form_pg_constraint copy_con;
13093 :
13094 126 : List *children = NIL;
13095 : ListCell *child;
13096 : NewConstraint *newcon;
13097 : Datum val;
13098 : char *conbin;
13099 :
13100 126 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13101 : Assert(con->contype == CONSTRAINT_CHECK);
13102 :
13103 : /*
13104 : * If we're recursing, the parent has already done this, so skip it. Also,
13105 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13106 : * for it in the children.
13107 : */
13108 126 : if (!recursing && !con->connoinherit)
13109 72 : children = find_all_inheritors(RelationGetRelid(rel),
13110 : lockmode, NULL);
13111 :
13112 : /*
13113 : * For CHECK constraints, we must ensure that we only mark the constraint
13114 : * as validated on the parent if it's already validated on the children.
13115 : *
13116 : * We recurse before validating on the parent, to reduce risk of
13117 : * deadlocks.
13118 : */
13119 246 : foreach(child, children)
13120 : {
13121 120 : Oid childoid = lfirst_oid(child);
13122 : Relation childrel;
13123 :
13124 120 : if (childoid == RelationGetRelid(rel))
13125 72 : continue;
13126 :
13127 : /*
13128 : * If we are told not to recurse, there had better not be any child
13129 : * tables, because we can't mark the constraint on the parent valid
13130 : * unless it is valid for all child tables.
13131 : */
13132 48 : if (!recurse)
13133 0 : ereport(ERROR,
13134 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13135 : errmsg("constraint must be validated on child tables too")));
13136 :
13137 : /* find_all_inheritors already got lock */
13138 48 : childrel = table_open(childoid, NoLock);
13139 :
13140 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
13141 : true, lockmode);
13142 48 : table_close(childrel, NoLock);
13143 : }
13144 :
13145 : /* Queue validation for phase 3 */
13146 126 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13147 126 : newcon->name = constrName;
13148 126 : newcon->contype = CONSTR_CHECK;
13149 126 : newcon->refrelid = InvalidOid;
13150 126 : newcon->refindid = InvalidOid;
13151 126 : newcon->conid = con->oid;
13152 :
13153 126 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13154 : Anum_pg_constraint_conbin);
13155 126 : conbin = TextDatumGetCString(val);
13156 126 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13157 :
13158 : /* Find or create work queue entry for this table */
13159 126 : tab = ATGetQueueEntry(wqueue, rel);
13160 126 : tab->constraints = lappend(tab->constraints, newcon);
13161 :
13162 : /*
13163 : * Invalidate relcache so that others see the new validated constraint.
13164 : */
13165 126 : CacheInvalidateRelcache(rel);
13166 :
13167 : /*
13168 : * Now update the catalog, while we have the door open.
13169 : */
13170 126 : copyTuple = heap_copytuple(contuple);
13171 126 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13172 126 : copy_con->convalidated = true;
13173 126 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13174 :
13175 126 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13176 :
13177 126 : heap_freetuple(copyTuple);
13178 126 : }
13179 :
13180 : /*
13181 : * QueueNNConstraintValidation
13182 : *
13183 : * Add an entry to the wqueue to validate the given not-null constraint in
13184 : * Phase 3 and update the convalidated field in the pg_constraint catalog for
13185 : * the specified relation and all its inheriting children.
13186 : */
13187 : static void
13188 112 : QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13189 : HeapTuple contuple, bool recurse, bool recursing,
13190 : LOCKMODE lockmode)
13191 : {
13192 : Form_pg_constraint con;
13193 : AlteredTableInfo *tab;
13194 : HeapTuple copyTuple;
13195 : Form_pg_constraint copy_con;
13196 112 : List *children = NIL;
13197 : AttrNumber attnum;
13198 : char *colname;
13199 :
13200 112 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13201 : Assert(con->contype == CONSTRAINT_NOTNULL);
13202 :
13203 112 : attnum = extractNotNullColumn(contuple);
13204 :
13205 : /*
13206 : * If we're recursing, we've already done this for parent, so skip it.
13207 : * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13208 : * look for it in the children.
13209 : *
13210 : * We recurse before validating on the parent, to reduce risk of
13211 : * deadlocks.
13212 : */
13213 112 : if (!recursing && !con->connoinherit)
13214 76 : children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13215 :
13216 112 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13217 378 : foreach_oid(childoid, children)
13218 : {
13219 : Relation childrel;
13220 : HeapTuple contup;
13221 : Form_pg_constraint childcon;
13222 : char *conname;
13223 :
13224 154 : if (childoid == RelationGetRelid(rel))
13225 76 : continue;
13226 :
13227 : /*
13228 : * If we are told not to recurse, there had better not be any child
13229 : * tables, because we can't mark the constraint on the parent valid
13230 : * unless it is valid for all child tables.
13231 : */
13232 78 : if (!recurse)
13233 0 : ereport(ERROR,
13234 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13235 : errmsg("constraint must be validated on child tables too"));
13236 :
13237 : /*
13238 : * The column on child might have a different attnum, so search by
13239 : * column name.
13240 : */
13241 78 : contup = findNotNullConstraint(childoid, colname);
13242 78 : if (!contup)
13243 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13244 : colname, get_rel_name(childoid));
13245 78 : childcon = (Form_pg_constraint) GETSTRUCT(contup);
13246 78 : if (childcon->convalidated)
13247 42 : continue;
13248 :
13249 : /* find_all_inheritors already got lock */
13250 36 : childrel = table_open(childoid, NoLock);
13251 36 : conname = pstrdup(NameStr(childcon->conname));
13252 :
13253 : /* XXX improve ATExecValidateConstraint API to avoid double search */
13254 36 : ATExecValidateConstraint(wqueue, childrel, conname,
13255 : false, true, lockmode);
13256 36 : table_close(childrel, NoLock);
13257 : }
13258 :
13259 : /* Set attnotnull appropriately without queueing another validation */
13260 112 : set_attnotnull(NULL, rel, attnum, true, false);
13261 :
13262 112 : tab = ATGetQueueEntry(wqueue, rel);
13263 112 : tab->verify_new_notnull = true;
13264 :
13265 : /*
13266 : * Invalidate relcache so that others see the new validated constraint.
13267 : */
13268 112 : CacheInvalidateRelcache(rel);
13269 :
13270 : /*
13271 : * Now update the catalogs, while we have the door open.
13272 : */
13273 112 : copyTuple = heap_copytuple(contuple);
13274 112 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13275 112 : copy_con->convalidated = true;
13276 112 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13277 :
13278 112 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13279 :
13280 112 : heap_freetuple(copyTuple);
13281 112 : }
13282 :
13283 : /*
13284 : * transformColumnNameList - transform list of column names
13285 : *
13286 : * Lookup each name and return its attnum and, optionally, type and collation
13287 : * OIDs
13288 : *
13289 : * Note: the name of this function suggests that it's general-purpose,
13290 : * but actually it's only used to look up names appearing in foreign-key
13291 : * clauses. The error messages would need work to use it in other cases,
13292 : * and perhaps the validity checks as well.
13293 : */
13294 : static int
13295 6574 : transformColumnNameList(Oid relId, List *colList,
13296 : int16 *attnums, Oid *atttypids, Oid *attcollids)
13297 : {
13298 : ListCell *l;
13299 : int attnum;
13300 :
13301 6574 : attnum = 0;
13302 12010 : foreach(l, colList)
13303 : {
13304 5502 : char *attname = strVal(lfirst(l));
13305 : HeapTuple atttuple;
13306 : Form_pg_attribute attform;
13307 :
13308 5502 : atttuple = SearchSysCacheAttName(relId, attname);
13309 5502 : if (!HeapTupleIsValid(atttuple))
13310 54 : ereport(ERROR,
13311 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13312 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13313 : attname)));
13314 5448 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13315 5448 : if (attform->attnum < 0)
13316 12 : ereport(ERROR,
13317 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13318 : errmsg("system columns cannot be used in foreign keys")));
13319 5436 : if (attnum >= INDEX_MAX_KEYS)
13320 0 : ereport(ERROR,
13321 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
13322 : errmsg("cannot have more than %d keys in a foreign key",
13323 : INDEX_MAX_KEYS)));
13324 5436 : attnums[attnum] = attform->attnum;
13325 5436 : if (atttypids != NULL)
13326 5400 : atttypids[attnum] = attform->atttypid;
13327 5436 : if (attcollids != NULL)
13328 5400 : attcollids[attnum] = attform->attcollation;
13329 5436 : ReleaseSysCache(atttuple);
13330 5436 : attnum++;
13331 : }
13332 :
13333 6508 : return attnum;
13334 : }
13335 :
13336 : /*
13337 : * transformFkeyGetPrimaryKey -
13338 : *
13339 : * Look up the names, attnums, types, and collations of the primary key attributes
13340 : * for the pkrel. Also return the index OID and index opclasses of the
13341 : * index supporting the primary key. Also return whether the index has
13342 : * WITHOUT OVERLAPS.
13343 : *
13344 : * All parameters except pkrel are output parameters. Also, the function
13345 : * return value is the number of attributes in the primary key.
13346 : *
13347 : * Used when the column list in the REFERENCES specification is omitted.
13348 : */
13349 : static int
13350 1244 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
13351 : List **attnamelist,
13352 : int16 *attnums, Oid *atttypids, Oid *attcollids,
13353 : Oid *opclasses, bool *pk_has_without_overlaps)
13354 : {
13355 : List *indexoidlist;
13356 : ListCell *indexoidscan;
13357 1244 : HeapTuple indexTuple = NULL;
13358 1244 : Form_pg_index indexStruct = NULL;
13359 : Datum indclassDatum;
13360 : oidvector *indclass;
13361 : int i;
13362 :
13363 : /*
13364 : * Get the list of index OIDs for the table from the relcache, and look up
13365 : * each one in the pg_index syscache until we find one marked primary key
13366 : * (hopefully there isn't more than one such). Insist it's valid, too.
13367 : */
13368 1244 : *indexOid = InvalidOid;
13369 :
13370 1244 : indexoidlist = RelationGetIndexList(pkrel);
13371 :
13372 1250 : foreach(indexoidscan, indexoidlist)
13373 : {
13374 1250 : Oid indexoid = lfirst_oid(indexoidscan);
13375 :
13376 1250 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13377 1250 : if (!HeapTupleIsValid(indexTuple))
13378 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13379 1250 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13380 1250 : if (indexStruct->indisprimary && indexStruct->indisvalid)
13381 : {
13382 : /*
13383 : * Refuse to use a deferrable primary key. This is per SQL spec,
13384 : * and there would be a lot of interesting semantic problems if we
13385 : * tried to allow it.
13386 : */
13387 1244 : if (!indexStruct->indimmediate)
13388 0 : ereport(ERROR,
13389 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13390 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13391 : RelationGetRelationName(pkrel))));
13392 :
13393 1244 : *indexOid = indexoid;
13394 1244 : break;
13395 : }
13396 6 : ReleaseSysCache(indexTuple);
13397 : }
13398 :
13399 1244 : list_free(indexoidlist);
13400 :
13401 : /*
13402 : * Check that we found it
13403 : */
13404 1244 : if (!OidIsValid(*indexOid))
13405 0 : ereport(ERROR,
13406 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13407 : errmsg("there is no primary key for referenced table \"%s\"",
13408 : RelationGetRelationName(pkrel))));
13409 :
13410 : /* Must get indclass the hard way */
13411 1244 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13412 : Anum_pg_index_indclass);
13413 1244 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13414 :
13415 : /*
13416 : * Now build the list of PK attributes from the indkey definition (we
13417 : * assume a primary key cannot have expressional elements)
13418 : */
13419 1244 : *attnamelist = NIL;
13420 2966 : for (i = 0; i < indexStruct->indnkeyatts; i++)
13421 : {
13422 1722 : int pkattno = indexStruct->indkey.values[i];
13423 :
13424 1722 : attnums[i] = pkattno;
13425 1722 : atttypids[i] = attnumTypeId(pkrel, pkattno);
13426 1722 : attcollids[i] = attnumCollationId(pkrel, pkattno);
13427 1722 : opclasses[i] = indclass->values[i];
13428 1722 : *attnamelist = lappend(*attnamelist,
13429 1722 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13430 : }
13431 :
13432 1244 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13433 :
13434 1244 : ReleaseSysCache(indexTuple);
13435 :
13436 1244 : return i;
13437 : }
13438 :
13439 : /*
13440 : * transformFkeyCheckAttrs -
13441 : *
13442 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13443 : * reference as part of a foreign key constraint.
13444 : *
13445 : * Returns the OID of the unique index supporting the constraint and
13446 : * populates the caller-provided 'opclasses' array with the opclasses
13447 : * associated with the index columns. Also sets whether the index
13448 : * uses WITHOUT OVERLAPS.
13449 : *
13450 : * Raises an ERROR on validation failure.
13451 : */
13452 : static Oid
13453 1282 : transformFkeyCheckAttrs(Relation pkrel,
13454 : int numattrs, int16 *attnums,
13455 : bool with_period, Oid *opclasses,
13456 : bool *pk_has_without_overlaps)
13457 : {
13458 1282 : Oid indexoid = InvalidOid;
13459 1282 : bool found = false;
13460 1282 : bool found_deferrable = false;
13461 : List *indexoidlist;
13462 : ListCell *indexoidscan;
13463 : int i,
13464 : j;
13465 :
13466 : /*
13467 : * Reject duplicate appearances of columns in the referenced-columns list.
13468 : * Such a case is forbidden by the SQL standard, and even if we thought it
13469 : * useful to allow it, there would be ambiguity about how to match the
13470 : * list to unique indexes (in particular, it'd be unclear which index
13471 : * opclass goes with which FK column).
13472 : */
13473 2992 : for (i = 0; i < numattrs; i++)
13474 : {
13475 2256 : for (j = i + 1; j < numattrs; j++)
13476 : {
13477 546 : if (attnums[i] == attnums[j])
13478 24 : ereport(ERROR,
13479 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13480 : errmsg("foreign key referenced-columns list must not contain duplicates")));
13481 : }
13482 : }
13483 :
13484 : /*
13485 : * Get the list of index OIDs for the table from the relcache, and look up
13486 : * each one in the pg_index syscache, and match unique indexes to the list
13487 : * of attnums we are given.
13488 : */
13489 1258 : indexoidlist = RelationGetIndexList(pkrel);
13490 :
13491 1438 : foreach(indexoidscan, indexoidlist)
13492 : {
13493 : HeapTuple indexTuple;
13494 : Form_pg_index indexStruct;
13495 :
13496 1426 : indexoid = lfirst_oid(indexoidscan);
13497 1426 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13498 1426 : if (!HeapTupleIsValid(indexTuple))
13499 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13500 1426 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13501 :
13502 : /*
13503 : * Must have the right number of columns; must be unique (or if
13504 : * temporal then exclusion instead) and not a partial index; forget it
13505 : * if there are any expressions, too. Invalid indexes are out as well.
13506 : */
13507 2744 : if (indexStruct->indnkeyatts == numattrs &&
13508 1318 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13509 2608 : indexStruct->indisvalid &&
13510 2608 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13511 1304 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13512 : {
13513 : Datum indclassDatum;
13514 : oidvector *indclass;
13515 :
13516 : /* Must get indclass the hard way */
13517 1304 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13518 : Anum_pg_index_indclass);
13519 1304 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13520 :
13521 : /*
13522 : * The given attnum list may match the index columns in any order.
13523 : * Check for a match, and extract the appropriate opclasses while
13524 : * we're at it.
13525 : *
13526 : * We know that attnums[] is duplicate-free per the test at the
13527 : * start of this function, and we checked above that the number of
13528 : * index columns agrees, so if we find a match for each attnums[]
13529 : * entry then we must have a one-to-one match in some order.
13530 : */
13531 3002 : for (i = 0; i < numattrs; i++)
13532 : {
13533 1756 : found = false;
13534 2336 : for (j = 0; j < numattrs; j++)
13535 : {
13536 2278 : if (attnums[i] == indexStruct->indkey.values[j])
13537 : {
13538 1698 : opclasses[i] = indclass->values[j];
13539 1698 : found = true;
13540 1698 : break;
13541 : }
13542 : }
13543 1756 : if (!found)
13544 58 : break;
13545 : }
13546 : /* The last attribute in the index must be the PERIOD FK part */
13547 1304 : if (found && with_period)
13548 : {
13549 122 : int16 periodattnum = attnums[numattrs - 1];
13550 :
13551 122 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13552 : }
13553 :
13554 : /*
13555 : * Refuse to use a deferrable unique/primary key. This is per SQL
13556 : * spec, and there would be a lot of interesting semantic problems
13557 : * if we tried to allow it.
13558 : */
13559 1304 : if (found && !indexStruct->indimmediate)
13560 : {
13561 : /*
13562 : * Remember that we found an otherwise matching index, so that
13563 : * we can generate a more appropriate error message.
13564 : */
13565 0 : found_deferrable = true;
13566 0 : found = false;
13567 : }
13568 :
13569 : /* We need to know whether the index has WITHOUT OVERLAPS */
13570 1304 : if (found)
13571 1246 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13572 : }
13573 1426 : ReleaseSysCache(indexTuple);
13574 1426 : if (found)
13575 1246 : break;
13576 : }
13577 :
13578 1258 : if (!found)
13579 : {
13580 12 : if (found_deferrable)
13581 0 : ereport(ERROR,
13582 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13583 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13584 : RelationGetRelationName(pkrel))));
13585 : else
13586 12 : ereport(ERROR,
13587 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13588 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13589 : RelationGetRelationName(pkrel))));
13590 : }
13591 :
13592 1246 : list_free(indexoidlist);
13593 :
13594 1246 : return indexoid;
13595 : }
13596 :
13597 : /*
13598 : * findFkeyCast -
13599 : *
13600 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13601 : * Caller has equal regard for binary coercibility and for an exact match.
13602 : */
13603 : static CoercionPathType
13604 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13605 : {
13606 : CoercionPathType ret;
13607 :
13608 12 : if (targetTypeId == sourceTypeId)
13609 : {
13610 12 : ret = COERCION_PATH_RELABELTYPE;
13611 12 : *funcid = InvalidOid;
13612 : }
13613 : else
13614 : {
13615 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13616 : COERCION_IMPLICIT, funcid);
13617 0 : if (ret == COERCION_PATH_NONE)
13618 : /* A previously-relied-upon cast is now gone. */
13619 0 : elog(ERROR, "could not find cast from %u to %u",
13620 : sourceTypeId, targetTypeId);
13621 : }
13622 :
13623 12 : return ret;
13624 : }
13625 :
13626 : /*
13627 : * Permissions checks on the referenced table for ADD FOREIGN KEY
13628 : *
13629 : * Note: we have already checked that the user owns the referencing table,
13630 : * else we'd have failed much earlier; no additional checks are needed for it.
13631 : */
13632 : static void
13633 2454 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13634 : {
13635 2454 : Oid roleid = GetUserId();
13636 : AclResult aclresult;
13637 : int i;
13638 :
13639 : /* Okay if we have relation-level REFERENCES permission */
13640 2454 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13641 : ACL_REFERENCES);
13642 2454 : if (aclresult == ACLCHECK_OK)
13643 2454 : return;
13644 : /* Else we must have REFERENCES on each column */
13645 0 : for (i = 0; i < natts; i++)
13646 : {
13647 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13648 : roleid, ACL_REFERENCES);
13649 0 : if (aclresult != ACLCHECK_OK)
13650 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13651 0 : RelationGetRelationName(rel));
13652 : }
13653 : }
13654 :
13655 : /*
13656 : * Scan the existing rows in a table to verify they meet a proposed FK
13657 : * constraint.
13658 : *
13659 : * Caller must have opened and locked both relations appropriately.
13660 : */
13661 : static void
13662 1166 : validateForeignKeyConstraint(char *conname,
13663 : Relation rel,
13664 : Relation pkrel,
13665 : Oid pkindOid,
13666 : Oid constraintOid,
13667 : bool hasperiod)
13668 : {
13669 : TupleTableSlot *slot;
13670 : TableScanDesc scan;
13671 1166 : Trigger trig = {0};
13672 : Snapshot snapshot;
13673 : MemoryContext oldcxt;
13674 : MemoryContext perTupCxt;
13675 :
13676 1166 : ereport(DEBUG1,
13677 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13678 :
13679 : /*
13680 : * Build a trigger call structure; we'll need it either way.
13681 : */
13682 1166 : trig.tgoid = InvalidOid;
13683 1166 : trig.tgname = conname;
13684 1166 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13685 1166 : trig.tgisinternal = true;
13686 1166 : trig.tgconstrrelid = RelationGetRelid(pkrel);
13687 1166 : trig.tgconstrindid = pkindOid;
13688 1166 : trig.tgconstraint = constraintOid;
13689 1166 : trig.tgdeferrable = false;
13690 1166 : trig.tginitdeferred = false;
13691 : /* we needn't fill in remaining fields */
13692 :
13693 : /*
13694 : * See if we can do it with a single LEFT JOIN query. A false result
13695 : * indicates we must proceed with the fire-the-trigger method. We can't do
13696 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13697 : * left joins.
13698 : */
13699 1166 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13700 984 : return;
13701 :
13702 : /*
13703 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13704 : * if that tuple had just been inserted. If any of those fail, it should
13705 : * ereport(ERROR) and that's that.
13706 : */
13707 108 : snapshot = RegisterSnapshot(GetLatestSnapshot());
13708 108 : slot = table_slot_create(rel, NULL);
13709 108 : scan = table_beginscan(rel, snapshot, 0, NULL);
13710 :
13711 108 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
13712 : "validateForeignKeyConstraint",
13713 : ALLOCSET_SMALL_SIZES);
13714 108 : oldcxt = MemoryContextSwitchTo(perTupCxt);
13715 :
13716 192 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13717 : {
13718 102 : LOCAL_FCINFO(fcinfo, 0);
13719 102 : TriggerData trigdata = {0};
13720 :
13721 102 : CHECK_FOR_INTERRUPTS();
13722 :
13723 : /*
13724 : * Make a call to the trigger function
13725 : *
13726 : * No parameters are passed, but we do set a context
13727 : */
13728 510 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13729 :
13730 : /*
13731 : * We assume RI_FKey_check_ins won't look at flinfo...
13732 : */
13733 102 : trigdata.type = T_TriggerData;
13734 102 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
13735 102 : trigdata.tg_relation = rel;
13736 102 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13737 102 : trigdata.tg_trigslot = slot;
13738 102 : trigdata.tg_trigger = &trig;
13739 :
13740 102 : fcinfo->context = (Node *) &trigdata;
13741 :
13742 102 : RI_FKey_check_ins(fcinfo);
13743 :
13744 84 : MemoryContextReset(perTupCxt);
13745 : }
13746 :
13747 90 : MemoryContextSwitchTo(oldcxt);
13748 90 : MemoryContextDelete(perTupCxt);
13749 90 : table_endscan(scan);
13750 90 : UnregisterSnapshot(snapshot);
13751 90 : ExecDropSingleTupleTableSlot(slot);
13752 : }
13753 :
13754 : /*
13755 : * CreateFKCheckTrigger
13756 : * Creates the insert (on_insert=true) or update "check" trigger that
13757 : * implements a given foreign key
13758 : *
13759 : * Returns the OID of the so created trigger.
13760 : */
13761 : static Oid
13762 5896 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13763 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13764 : bool on_insert)
13765 : {
13766 : ObjectAddress trigAddress;
13767 : CreateTrigStmt *fk_trigger;
13768 :
13769 : /*
13770 : * Note: for a self-referential FK (referencing and referenced tables are
13771 : * the same), it is important that the ON UPDATE action fires before the
13772 : * CHECK action, since both triggers will fire on the same row during an
13773 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13774 : * state of the row. Triggers fire in name order, so we ensure this by
13775 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13776 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13777 : */
13778 5896 : fk_trigger = makeNode(CreateTrigStmt);
13779 5896 : fk_trigger->replace = false;
13780 5896 : fk_trigger->isconstraint = true;
13781 5896 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
13782 5896 : fk_trigger->relation = NULL;
13783 :
13784 : /* Either ON INSERT or ON UPDATE */
13785 5896 : if (on_insert)
13786 : {
13787 2948 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13788 2948 : fk_trigger->events = TRIGGER_TYPE_INSERT;
13789 : }
13790 : else
13791 : {
13792 2948 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13793 2948 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13794 : }
13795 :
13796 5896 : fk_trigger->args = NIL;
13797 5896 : fk_trigger->row = true;
13798 5896 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13799 5896 : fk_trigger->columns = NIL;
13800 5896 : fk_trigger->whenClause = NULL;
13801 5896 : fk_trigger->transitionRels = NIL;
13802 5896 : fk_trigger->deferrable = fkconstraint->deferrable;
13803 5896 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13804 5896 : fk_trigger->constrrel = NULL;
13805 :
13806 5896 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13807 : constraintOid, indexOid, InvalidOid,
13808 : parentTrigOid, NULL, true, false);
13809 :
13810 : /* Make changes-so-far visible */
13811 5896 : CommandCounterIncrement();
13812 :
13813 5896 : return trigAddress.objectId;
13814 : }
13815 :
13816 : /*
13817 : * createForeignKeyActionTriggers
13818 : * Create the referenced-side "action" triggers that implement a foreign
13819 : * key.
13820 : *
13821 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
13822 : * *updateTrigOid.
13823 : */
13824 : static void
13825 3378 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13826 : Oid constraintOid, Oid indexOid,
13827 : Oid parentDelTrigger, Oid parentUpdTrigger,
13828 : Oid *deleteTrigOid, Oid *updateTrigOid)
13829 : {
13830 : CreateTrigStmt *fk_trigger;
13831 : ObjectAddress trigAddress;
13832 :
13833 : /*
13834 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13835 : * DELETE action on the referenced table.
13836 : */
13837 3378 : fk_trigger = makeNode(CreateTrigStmt);
13838 3378 : fk_trigger->replace = false;
13839 3378 : fk_trigger->isconstraint = true;
13840 3378 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13841 3378 : fk_trigger->relation = NULL;
13842 3378 : fk_trigger->args = NIL;
13843 3378 : fk_trigger->row = true;
13844 3378 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13845 3378 : fk_trigger->events = TRIGGER_TYPE_DELETE;
13846 3378 : fk_trigger->columns = NIL;
13847 3378 : fk_trigger->whenClause = NULL;
13848 3378 : fk_trigger->transitionRels = NIL;
13849 3378 : fk_trigger->constrrel = NULL;
13850 :
13851 3378 : switch (fkconstraint->fk_del_action)
13852 : {
13853 2750 : case FKCONSTR_ACTION_NOACTION:
13854 2750 : fk_trigger->deferrable = fkconstraint->deferrable;
13855 2750 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13856 2750 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13857 2750 : break;
13858 30 : case FKCONSTR_ACTION_RESTRICT:
13859 30 : fk_trigger->deferrable = false;
13860 30 : fk_trigger->initdeferred = false;
13861 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13862 30 : break;
13863 440 : case FKCONSTR_ACTION_CASCADE:
13864 440 : fk_trigger->deferrable = false;
13865 440 : fk_trigger->initdeferred = false;
13866 440 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13867 440 : break;
13868 98 : case FKCONSTR_ACTION_SETNULL:
13869 98 : fk_trigger->deferrable = false;
13870 98 : fk_trigger->initdeferred = false;
13871 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13872 98 : break;
13873 60 : case FKCONSTR_ACTION_SETDEFAULT:
13874 60 : fk_trigger->deferrable = false;
13875 60 : fk_trigger->initdeferred = false;
13876 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13877 60 : break;
13878 0 : default:
13879 0 : elog(ERROR, "unrecognized FK action type: %d",
13880 : (int) fkconstraint->fk_del_action);
13881 : break;
13882 : }
13883 :
13884 3378 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13885 : constraintOid, indexOid, InvalidOid,
13886 : parentDelTrigger, NULL, true, false);
13887 3378 : if (deleteTrigOid)
13888 3378 : *deleteTrigOid = trigAddress.objectId;
13889 :
13890 : /* Make changes-so-far visible */
13891 3378 : CommandCounterIncrement();
13892 :
13893 : /*
13894 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13895 : * UPDATE action on the referenced table.
13896 : */
13897 3378 : fk_trigger = makeNode(CreateTrigStmt);
13898 3378 : fk_trigger->replace = false;
13899 3378 : fk_trigger->isconstraint = true;
13900 3378 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13901 3378 : fk_trigger->relation = NULL;
13902 3378 : fk_trigger->args = NIL;
13903 3378 : fk_trigger->row = true;
13904 3378 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13905 3378 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13906 3378 : fk_trigger->columns = NIL;
13907 3378 : fk_trigger->whenClause = NULL;
13908 3378 : fk_trigger->transitionRels = NIL;
13909 3378 : fk_trigger->constrrel = NULL;
13910 :
13911 3378 : switch (fkconstraint->fk_upd_action)
13912 : {
13913 2944 : case FKCONSTR_ACTION_NOACTION:
13914 2944 : fk_trigger->deferrable = fkconstraint->deferrable;
13915 2944 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13916 2944 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13917 2944 : break;
13918 36 : case FKCONSTR_ACTION_RESTRICT:
13919 36 : fk_trigger->deferrable = false;
13920 36 : fk_trigger->initdeferred = false;
13921 36 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13922 36 : break;
13923 294 : case FKCONSTR_ACTION_CASCADE:
13924 294 : fk_trigger->deferrable = false;
13925 294 : fk_trigger->initdeferred = false;
13926 294 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13927 294 : break;
13928 62 : case FKCONSTR_ACTION_SETNULL:
13929 62 : fk_trigger->deferrable = false;
13930 62 : fk_trigger->initdeferred = false;
13931 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13932 62 : break;
13933 42 : case FKCONSTR_ACTION_SETDEFAULT:
13934 42 : fk_trigger->deferrable = false;
13935 42 : fk_trigger->initdeferred = false;
13936 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13937 42 : break;
13938 0 : default:
13939 0 : elog(ERROR, "unrecognized FK action type: %d",
13940 : (int) fkconstraint->fk_upd_action);
13941 : break;
13942 : }
13943 :
13944 3378 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13945 : constraintOid, indexOid, InvalidOid,
13946 : parentUpdTrigger, NULL, true, false);
13947 3378 : if (updateTrigOid)
13948 3378 : *updateTrigOid = trigAddress.objectId;
13949 3378 : }
13950 :
13951 : /*
13952 : * createForeignKeyCheckTriggers
13953 : * Create the referencing-side "check" triggers that implement a foreign
13954 : * key.
13955 : *
13956 : * Returns the OIDs of the so created triggers in *insertTrigOid and
13957 : * *updateTrigOid.
13958 : */
13959 : static void
13960 2948 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
13961 : Constraint *fkconstraint, Oid constraintOid,
13962 : Oid indexOid,
13963 : Oid parentInsTrigger, Oid parentUpdTrigger,
13964 : Oid *insertTrigOid, Oid *updateTrigOid)
13965 : {
13966 2948 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13967 : constraintOid, indexOid,
13968 : parentInsTrigger, true);
13969 2948 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13970 : constraintOid, indexOid,
13971 : parentUpdTrigger, false);
13972 2948 : }
13973 :
13974 : /*
13975 : * ALTER TABLE DROP CONSTRAINT
13976 : *
13977 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
13978 : */
13979 : static void
13980 812 : ATExecDropConstraint(Relation rel, const char *constrName,
13981 : DropBehavior behavior, bool recurse,
13982 : bool missing_ok, LOCKMODE lockmode)
13983 : {
13984 : Relation conrel;
13985 : SysScanDesc scan;
13986 : ScanKeyData skey[3];
13987 : HeapTuple tuple;
13988 812 : bool found = false;
13989 :
13990 812 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13991 :
13992 : /*
13993 : * Find and drop the target constraint
13994 : */
13995 812 : ScanKeyInit(&skey[0],
13996 : Anum_pg_constraint_conrelid,
13997 : BTEqualStrategyNumber, F_OIDEQ,
13998 : ObjectIdGetDatum(RelationGetRelid(rel)));
13999 812 : ScanKeyInit(&skey[1],
14000 : Anum_pg_constraint_contypid,
14001 : BTEqualStrategyNumber, F_OIDEQ,
14002 : ObjectIdGetDatum(InvalidOid));
14003 812 : ScanKeyInit(&skey[2],
14004 : Anum_pg_constraint_conname,
14005 : BTEqualStrategyNumber, F_NAMEEQ,
14006 : CStringGetDatum(constrName));
14007 812 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14008 : true, NULL, 3, skey);
14009 :
14010 : /* There can be at most one matching row */
14011 812 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14012 : {
14013 776 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
14014 : missing_ok, lockmode);
14015 590 : found = true;
14016 : }
14017 :
14018 626 : systable_endscan(scan);
14019 :
14020 626 : if (!found)
14021 : {
14022 36 : if (!missing_ok)
14023 24 : ereport(ERROR,
14024 : errcode(ERRCODE_UNDEFINED_OBJECT),
14025 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14026 : constrName, RelationGetRelationName(rel)));
14027 : else
14028 12 : ereport(NOTICE,
14029 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14030 : constrName, RelationGetRelationName(rel)));
14031 : }
14032 :
14033 602 : table_close(conrel, RowExclusiveLock);
14034 602 : }
14035 :
14036 : /*
14037 : * Remove a constraint, using its pg_constraint tuple
14038 : *
14039 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14040 : * DROP NOT NULL.
14041 : *
14042 : * Returns the address of the constraint being removed.
14043 : */
14044 : static ObjectAddress
14045 1206 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
14046 : bool recurse, bool recursing, bool missing_ok,
14047 : LOCKMODE lockmode)
14048 : {
14049 : Relation conrel;
14050 : Form_pg_constraint con;
14051 : ObjectAddress conobj;
14052 : List *children;
14053 1206 : bool is_no_inherit_constraint = false;
14054 : char *constrName;
14055 1206 : char *colname = NULL;
14056 :
14057 : /* Guard against stack overflow due to overly deep inheritance tree. */
14058 1206 : check_stack_depth();
14059 :
14060 : /* At top level, permission check was done in ATPrepCmd, else do it */
14061 1206 : if (recursing)
14062 210 : ATSimplePermissions(AT_DropConstraint, rel,
14063 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
14064 :
14065 1200 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14066 :
14067 1200 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14068 1200 : constrName = NameStr(con->conname);
14069 :
14070 : /* Don't allow drop of inherited constraints */
14071 1200 : if (con->coninhcount > 0 && !recursing)
14072 156 : ereport(ERROR,
14073 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14074 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14075 : constrName, RelationGetRelationName(rel))));
14076 :
14077 : /*
14078 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14079 : *
14080 : * While doing that, we're in a good position to disallow dropping a not-
14081 : * null constraint underneath a primary key, a replica identity index, or
14082 : * a generated identity column.
14083 : */
14084 1044 : if (con->contype == CONSTRAINT_NOTNULL)
14085 : {
14086 314 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14087 314 : AttrNumber attnum = extractNotNullColumn(constraintTup);
14088 : Bitmapset *pkattrs;
14089 : Bitmapset *irattrs;
14090 : HeapTuple atttup;
14091 : Form_pg_attribute attForm;
14092 :
14093 : /* save column name for recursion step */
14094 314 : colname = get_attname(RelationGetRelid(rel), attnum, false);
14095 :
14096 : /*
14097 : * Disallow if it's in the primary key. For partitioned tables we
14098 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14099 : * return NULL if the primary key is invalid; but we still need to
14100 : * protect not-null constraints under such a constraint, so check the
14101 : * slow way.
14102 : */
14103 314 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
14104 :
14105 314 : if (pkattrs == NULL &&
14106 278 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14107 : {
14108 18 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14109 :
14110 18 : if (OidIsValid(pkindex))
14111 : {
14112 0 : Relation pk = relation_open(pkindex, AccessShareLock);
14113 :
14114 0 : pkattrs = NULL;
14115 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14116 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14117 :
14118 0 : relation_close(pk, AccessShareLock);
14119 : }
14120 : }
14121 :
14122 350 : if (pkattrs &&
14123 36 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
14124 24 : ereport(ERROR,
14125 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14126 : errmsg("column \"%s\" is in a primary key",
14127 : get_attname(RelationGetRelid(rel), attnum, false)));
14128 :
14129 : /* Disallow if it's in the replica identity */
14130 290 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
14131 290 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
14132 12 : ereport(ERROR,
14133 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14134 : errmsg("column \"%s\" is in index used as replica identity",
14135 : get_attname(RelationGetRelid(rel), attnum, false)));
14136 :
14137 : /* Disallow if it's a GENERATED AS IDENTITY column */
14138 278 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
14139 278 : if (!HeapTupleIsValid(atttup))
14140 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14141 : attnum, RelationGetRelid(rel));
14142 278 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14143 278 : if (attForm->attidentity != '\0')
14144 0 : ereport(ERROR,
14145 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14146 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
14147 : get_attname(RelationGetRelid(rel), attnum,
14148 : false),
14149 : RelationGetRelationName(rel)));
14150 :
14151 : /* All good -- reset attnotnull if needed */
14152 278 : if (attForm->attnotnull)
14153 : {
14154 278 : attForm->attnotnull = false;
14155 278 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14156 : }
14157 :
14158 278 : table_close(attrel, RowExclusiveLock);
14159 : }
14160 :
14161 1008 : is_no_inherit_constraint = con->connoinherit;
14162 :
14163 : /*
14164 : * If it's a foreign-key constraint, we'd better lock the referenced table
14165 : * and check that that's not in use, just as we've already done for the
14166 : * constrained table (else we might, eg, be dropping a trigger that has
14167 : * unfired events). But we can/must skip that in the self-referential
14168 : * case.
14169 : */
14170 1008 : if (con->contype == CONSTRAINT_FOREIGN &&
14171 168 : con->confrelid != RelationGetRelid(rel))
14172 : {
14173 : Relation frel;
14174 :
14175 : /* Must match lock taken by RemoveTriggerById: */
14176 168 : frel = table_open(con->confrelid, AccessExclusiveLock);
14177 168 : CheckAlterTableIsSafe(frel);
14178 162 : table_close(frel, NoLock);
14179 : }
14180 :
14181 : /*
14182 : * Perform the actual constraint deletion
14183 : */
14184 1002 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14185 1002 : performDeletion(&conobj, behavior, 0);
14186 :
14187 : /*
14188 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14189 : * are dropped via the dependency mechanism, so we're done here.
14190 : */
14191 966 : if (con->contype != CONSTRAINT_CHECK &&
14192 630 : con->contype != CONSTRAINT_NOTNULL &&
14193 352 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14194 : {
14195 78 : table_close(conrel, RowExclusiveLock);
14196 78 : return conobj;
14197 : }
14198 :
14199 : /*
14200 : * Propagate to children as appropriate. Unlike most other ALTER
14201 : * routines, we have to do this one level of recursion at a time; we can't
14202 : * use find_all_inheritors to do it in one pass.
14203 : */
14204 888 : if (!is_no_inherit_constraint)
14205 602 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14206 : else
14207 286 : children = NIL;
14208 :
14209 2148 : foreach_oid(childrelid, children)
14210 : {
14211 : Relation childrel;
14212 : HeapTuple tuple;
14213 : Form_pg_constraint childcon;
14214 :
14215 : /* find_inheritance_children already got lock */
14216 384 : childrel = table_open(childrelid, NoLock);
14217 384 : CheckAlterTableIsSafe(childrel);
14218 :
14219 : /*
14220 : * We search for not-null constraints by column name, and others by
14221 : * constraint name.
14222 : */
14223 384 : if (con->contype == CONSTRAINT_NOTNULL)
14224 : {
14225 148 : tuple = findNotNullConstraint(childrelid, colname);
14226 148 : if (!HeapTupleIsValid(tuple))
14227 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14228 : colname, RelationGetRelid(childrel));
14229 : }
14230 : else
14231 : {
14232 : SysScanDesc scan;
14233 : ScanKeyData skey[3];
14234 :
14235 236 : ScanKeyInit(&skey[0],
14236 : Anum_pg_constraint_conrelid,
14237 : BTEqualStrategyNumber, F_OIDEQ,
14238 : ObjectIdGetDatum(childrelid));
14239 236 : ScanKeyInit(&skey[1],
14240 : Anum_pg_constraint_contypid,
14241 : BTEqualStrategyNumber, F_OIDEQ,
14242 : ObjectIdGetDatum(InvalidOid));
14243 236 : ScanKeyInit(&skey[2],
14244 : Anum_pg_constraint_conname,
14245 : BTEqualStrategyNumber, F_NAMEEQ,
14246 : CStringGetDatum(constrName));
14247 236 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14248 : true, NULL, 3, skey);
14249 : /* There can only be one, so no need to loop */
14250 236 : tuple = systable_getnext(scan);
14251 236 : if (!HeapTupleIsValid(tuple))
14252 0 : ereport(ERROR,
14253 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14254 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14255 : constrName,
14256 : RelationGetRelationName(childrel))));
14257 236 : tuple = heap_copytuple(tuple);
14258 236 : systable_endscan(scan);
14259 : }
14260 :
14261 384 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14262 :
14263 : /* Right now only CHECK and not-null constraints can be inherited */
14264 384 : if (childcon->contype != CONSTRAINT_CHECK &&
14265 148 : childcon->contype != CONSTRAINT_NOTNULL)
14266 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14267 :
14268 384 : if (childcon->coninhcount <= 0) /* shouldn't happen */
14269 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14270 : childrelid, NameStr(childcon->conname));
14271 :
14272 384 : if (recurse)
14273 : {
14274 : /*
14275 : * If the child constraint has other definition sources, just
14276 : * decrement its inheritance count; if not, recurse to delete it.
14277 : */
14278 282 : if (childcon->coninhcount == 1 && !childcon->conislocal)
14279 : {
14280 : /* Time to delete this child constraint, too */
14281 210 : dropconstraint_internal(childrel, tuple, behavior,
14282 : recurse, true, missing_ok,
14283 : lockmode);
14284 : }
14285 : else
14286 : {
14287 : /* Child constraint must survive my deletion */
14288 72 : childcon->coninhcount--;
14289 72 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14290 :
14291 : /* Make update visible */
14292 72 : CommandCounterIncrement();
14293 : }
14294 : }
14295 : else
14296 : {
14297 : /*
14298 : * If we were told to drop ONLY in this table (no recursion) and
14299 : * there are no further parents for this constraint, we need to
14300 : * mark the inheritors' constraints as locally defined rather than
14301 : * inherited.
14302 : */
14303 102 : childcon->coninhcount--;
14304 102 : if (childcon->coninhcount == 0)
14305 102 : childcon->conislocal = true;
14306 :
14307 102 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14308 :
14309 : /* Make update visible */
14310 102 : CommandCounterIncrement();
14311 : }
14312 :
14313 378 : heap_freetuple(tuple);
14314 :
14315 378 : table_close(childrel, NoLock);
14316 : }
14317 :
14318 882 : table_close(conrel, RowExclusiveLock);
14319 :
14320 882 : return conobj;
14321 : }
14322 :
14323 : /*
14324 : * ALTER COLUMN TYPE
14325 : *
14326 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14327 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14328 : * transformed (and must be, because we rely on some transformed fields).
14329 : *
14330 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14331 : * table will be done "in parallel" during phase 3, so all the USING
14332 : * expressions should be parsed assuming the original column types. Also,
14333 : * this allows a USING expression to refer to a field that will be dropped.
14334 : *
14335 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14336 : * the first two execution steps in phase 2; they must not see the effects
14337 : * of any other subcommand types, since the USING expressions are parsed
14338 : * against the unmodified table's state.
14339 : */
14340 : static void
14341 1312 : ATPrepAlterColumnType(List **wqueue,
14342 : AlteredTableInfo *tab, Relation rel,
14343 : bool recurse, bool recursing,
14344 : AlterTableCmd *cmd, LOCKMODE lockmode,
14345 : AlterTableUtilityContext *context)
14346 : {
14347 1312 : char *colName = cmd->name;
14348 1312 : ColumnDef *def = (ColumnDef *) cmd->def;
14349 1312 : TypeName *typeName = def->typeName;
14350 1312 : Node *transform = def->cooked_default;
14351 : HeapTuple tuple;
14352 : Form_pg_attribute attTup;
14353 : AttrNumber attnum;
14354 : Oid targettype;
14355 : int32 targettypmod;
14356 : Oid targetcollid;
14357 : NewColumnValue *newval;
14358 1312 : ParseState *pstate = make_parsestate(NULL);
14359 : AclResult aclresult;
14360 : bool is_expr;
14361 :
14362 1312 : pstate->p_sourcetext = context->queryString;
14363 :
14364 1312 : if (rel->rd_rel->reloftype && !recursing)
14365 6 : ereport(ERROR,
14366 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14367 : errmsg("cannot alter column type of typed table"),
14368 : parser_errposition(pstate, def->location)));
14369 :
14370 : /* lookup the attribute so we can check inheritance status */
14371 1306 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14372 1306 : if (!HeapTupleIsValid(tuple))
14373 0 : ereport(ERROR,
14374 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14375 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14376 : colName, RelationGetRelationName(rel)),
14377 : parser_errposition(pstate, def->location)));
14378 1306 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14379 1306 : attnum = attTup->attnum;
14380 :
14381 : /* Can't alter a system attribute */
14382 1306 : if (attnum <= 0)
14383 6 : ereport(ERROR,
14384 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14385 : errmsg("cannot alter system column \"%s\"", colName),
14386 : parser_errposition(pstate, def->location)));
14387 :
14388 : /*
14389 : * Cannot specify USING when altering type of a generated column, because
14390 : * that would violate the generation expression.
14391 : */
14392 1300 : if (attTup->attgenerated && def->cooked_default)
14393 12 : ereport(ERROR,
14394 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14395 : errmsg("cannot specify USING when altering type of generated column"),
14396 : errdetail("Column \"%s\" is a generated column.", colName),
14397 : parser_errposition(pstate, def->location)));
14398 :
14399 : /*
14400 : * Don't alter inherited columns. At outer level, there had better not be
14401 : * any inherited definition; when recursing, we assume this was checked at
14402 : * the parent level (see below).
14403 : */
14404 1288 : if (attTup->attinhcount > 0 && !recursing)
14405 6 : ereport(ERROR,
14406 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14407 : errmsg("cannot alter inherited column \"%s\"", colName),
14408 : parser_errposition(pstate, def->location)));
14409 :
14410 : /* Don't alter columns used in the partition key */
14411 1282 : if (has_partition_attrs(rel,
14412 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
14413 : &is_expr))
14414 18 : ereport(ERROR,
14415 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14416 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14417 : colName, RelationGetRelationName(rel)),
14418 : parser_errposition(pstate, def->location)));
14419 :
14420 : /* Look up the target type */
14421 1264 : typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14422 :
14423 1258 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14424 1258 : if (aclresult != ACLCHECK_OK)
14425 12 : aclcheck_error_type(aclresult, targettype);
14426 :
14427 : /* And the collation */
14428 1246 : targetcollid = GetColumnDefCollation(pstate, def, targettype);
14429 :
14430 : /* make sure datatype is legal for a column */
14431 2480 : CheckAttributeType(colName, targettype, targetcollid,
14432 1240 : list_make1_oid(rel->rd_rel->reltype),
14433 1240 : (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14434 :
14435 1228 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14436 : {
14437 : /* do nothing */
14438 : }
14439 1192 : else if (tab->relkind == RELKIND_RELATION ||
14440 202 : tab->relkind == RELKIND_PARTITIONED_TABLE)
14441 : {
14442 : /*
14443 : * Set up an expression to transform the old data value to the new
14444 : * type. If a USING option was given, use the expression as
14445 : * transformed by transformAlterTableStmt, else just take the old
14446 : * value and try to coerce it. We do this first so that type
14447 : * incompatibility can be detected before we waste effort, and because
14448 : * we need the expression to be parsed against the original table row
14449 : * type.
14450 : */
14451 1056 : if (!transform)
14452 : {
14453 828 : transform = (Node *) makeVar(1, attnum,
14454 : attTup->atttypid, attTup->atttypmod,
14455 : attTup->attcollation,
14456 : 0);
14457 : }
14458 :
14459 1056 : transform = coerce_to_target_type(pstate,
14460 : transform, exprType(transform),
14461 : targettype, targettypmod,
14462 : COERCION_ASSIGNMENT,
14463 : COERCE_IMPLICIT_CAST,
14464 : -1);
14465 1056 : if (transform == NULL)
14466 : {
14467 : /* error text depends on whether USING was specified or not */
14468 24 : if (def->cooked_default != NULL)
14469 6 : ereport(ERROR,
14470 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14471 : errmsg("result of USING clause for column \"%s\""
14472 : " cannot be cast automatically to type %s",
14473 : colName, format_type_be(targettype)),
14474 : errhint("You might need to add an explicit cast.")));
14475 : else
14476 18 : ereport(ERROR,
14477 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14478 : errmsg("column \"%s\" cannot be cast automatically to type %s",
14479 : colName, format_type_be(targettype)),
14480 : !attTup->attgenerated ?
14481 : /* translator: USING is SQL, don't translate it */
14482 : errhint("You might need to specify \"USING %s::%s\".",
14483 : quote_identifier(colName),
14484 : format_type_with_typemod(targettype,
14485 : targettypmod)) : 0));
14486 : }
14487 :
14488 : /* Fix collations after all else */
14489 1032 : assign_expr_collations(pstate, transform);
14490 :
14491 : /* Expand virtual generated columns in the expr. */
14492 1032 : transform = expand_generated_columns_in_expr(transform, rel, 1);
14493 :
14494 : /* Plan the expr now so we can accurately assess the need to rewrite. */
14495 1032 : transform = (Node *) expression_planner((Expr *) transform);
14496 :
14497 : /*
14498 : * Add a work queue item to make ATRewriteTable update the column
14499 : * contents.
14500 : */
14501 1032 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
14502 1032 : newval->attnum = attnum;
14503 1032 : newval->expr = (Expr *) transform;
14504 1032 : newval->is_generated = false;
14505 :
14506 1032 : tab->newvals = lappend(tab->newvals, newval);
14507 1032 : if (ATColumnChangeRequiresRewrite(transform, attnum))
14508 836 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
14509 : }
14510 136 : else if (transform)
14511 12 : ereport(ERROR,
14512 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14513 : errmsg("\"%s\" is not a table",
14514 : RelationGetRelationName(rel))));
14515 :
14516 1192 : if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14517 : {
14518 : /*
14519 : * For relations or columns without storage, do this check now.
14520 : * Regular tables will check it later when the table is being
14521 : * rewritten.
14522 : */
14523 226 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14524 : }
14525 :
14526 1144 : ReleaseSysCache(tuple);
14527 :
14528 : /*
14529 : * Recurse manually by queueing a new command for each child, if
14530 : * necessary. We cannot apply ATSimpleRecursion here because we need to
14531 : * remap attribute numbers in the USING expression, if any.
14532 : *
14533 : * If we are told not to recurse, there had better not be any child
14534 : * tables; else the alter would put them out of step.
14535 : */
14536 1144 : if (recurse)
14537 : {
14538 886 : Oid relid = RelationGetRelid(rel);
14539 : List *child_oids,
14540 : *child_numparents;
14541 : ListCell *lo,
14542 : *li;
14543 :
14544 886 : child_oids = find_all_inheritors(relid, lockmode,
14545 : &child_numparents);
14546 :
14547 : /*
14548 : * find_all_inheritors does the recursive search of the inheritance
14549 : * hierarchy, so all we have to do is process all of the relids in the
14550 : * list that it returns.
14551 : */
14552 1980 : forboth(lo, child_oids, li, child_numparents)
14553 : {
14554 1118 : Oid childrelid = lfirst_oid(lo);
14555 1118 : int numparents = lfirst_int(li);
14556 : Relation childrel;
14557 : HeapTuple childtuple;
14558 : Form_pg_attribute childattTup;
14559 :
14560 1118 : if (childrelid == relid)
14561 886 : continue;
14562 :
14563 : /* find_all_inheritors already got lock */
14564 232 : childrel = relation_open(childrelid, NoLock);
14565 232 : CheckAlterTableIsSafe(childrel);
14566 :
14567 : /*
14568 : * Verify that the child doesn't have any inherited definitions of
14569 : * this column that came from outside this inheritance hierarchy.
14570 : * (renameatt makes a similar test, though in a different way
14571 : * because of its different recursion mechanism.)
14572 : */
14573 232 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14574 : colName);
14575 232 : if (!HeapTupleIsValid(childtuple))
14576 0 : ereport(ERROR,
14577 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14578 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14579 : colName, RelationGetRelationName(childrel))));
14580 232 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14581 :
14582 232 : if (childattTup->attinhcount > numparents)
14583 6 : ereport(ERROR,
14584 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14585 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14586 : colName, RelationGetRelationName(childrel))));
14587 :
14588 226 : ReleaseSysCache(childtuple);
14589 :
14590 : /*
14591 : * Remap the attribute numbers. If no USING expression was
14592 : * specified, there is no need for this step.
14593 : */
14594 226 : if (def->cooked_default)
14595 : {
14596 : AttrMap *attmap;
14597 : bool found_whole_row;
14598 :
14599 : /* create a copy to scribble on */
14600 78 : cmd = copyObject(cmd);
14601 :
14602 78 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14603 : RelationGetDescr(rel),
14604 : false);
14605 156 : ((ColumnDef *) cmd->def)->cooked_default =
14606 78 : map_variable_attnos(def->cooked_default,
14607 : 1, 0,
14608 : attmap,
14609 : InvalidOid, &found_whole_row);
14610 78 : if (found_whole_row)
14611 6 : ereport(ERROR,
14612 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14613 : errmsg("cannot convert whole-row table reference"),
14614 : errdetail("USING expression contains a whole-row table reference.")));
14615 72 : pfree(attmap);
14616 : }
14617 220 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14618 208 : relation_close(childrel, NoLock);
14619 : }
14620 : }
14621 308 : else if (!recursing &&
14622 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
14623 0 : ereport(ERROR,
14624 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14625 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
14626 : colName)));
14627 :
14628 1120 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14629 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14630 1114 : }
14631 :
14632 : /*
14633 : * When the data type of a column is changed, a rewrite might not be required
14634 : * if the new type is sufficiently identical to the old one, and the USING
14635 : * clause isn't trying to insert some other value. It's safe to skip the
14636 : * rewrite in these cases:
14637 : *
14638 : * - the old type is binary coercible to the new type
14639 : * - the new type is an unconstrained domain over the old type
14640 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14641 : *
14642 : * In the case of a constrained domain, we could get by with scanning the
14643 : * table and checking the constraint rather than actually rewriting it, but we
14644 : * don't currently try to do that.
14645 : */
14646 : static bool
14647 1032 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
14648 : {
14649 : Assert(expr != NULL);
14650 :
14651 : for (;;)
14652 : {
14653 : /* only one varno, so no need to check that */
14654 1150 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14655 196 : return false;
14656 954 : else if (IsA(expr, RelabelType))
14657 106 : expr = (Node *) ((RelabelType *) expr)->arg;
14658 848 : else if (IsA(expr, CoerceToDomain))
14659 : {
14660 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
14661 :
14662 0 : if (DomainHasConstraints(d->resulttype))
14663 0 : return true;
14664 0 : expr = (Node *) d->arg;
14665 : }
14666 848 : else if (IsA(expr, FuncExpr))
14667 : {
14668 642 : FuncExpr *f = (FuncExpr *) expr;
14669 :
14670 642 : switch (f->funcid)
14671 : {
14672 18 : case F_TIMESTAMPTZ_TIMESTAMP:
14673 : case F_TIMESTAMP_TIMESTAMPTZ:
14674 18 : if (TimestampTimestampTzRequiresRewrite())
14675 6 : return true;
14676 : else
14677 12 : expr = linitial(f->args);
14678 12 : break;
14679 624 : default:
14680 624 : return true;
14681 : }
14682 : }
14683 : else
14684 206 : return true;
14685 : }
14686 : }
14687 :
14688 : /*
14689 : * ALTER COLUMN .. SET DATA TYPE
14690 : *
14691 : * Return the address of the modified column.
14692 : */
14693 : static ObjectAddress
14694 1078 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
14695 : AlterTableCmd *cmd, LOCKMODE lockmode)
14696 : {
14697 1078 : char *colName = cmd->name;
14698 1078 : ColumnDef *def = (ColumnDef *) cmd->def;
14699 1078 : TypeName *typeName = def->typeName;
14700 : HeapTuple heapTup;
14701 : Form_pg_attribute attTup,
14702 : attOldTup;
14703 : AttrNumber attnum;
14704 : HeapTuple typeTuple;
14705 : Form_pg_type tform;
14706 : Oid targettype;
14707 : int32 targettypmod;
14708 : Oid targetcollid;
14709 : Node *defaultexpr;
14710 : Relation attrelation;
14711 : Relation depRel;
14712 : ScanKeyData key[3];
14713 : SysScanDesc scan;
14714 : HeapTuple depTup;
14715 : ObjectAddress address;
14716 :
14717 : /*
14718 : * Clear all the missing values if we're rewriting the table, since this
14719 : * renders them pointless.
14720 : */
14721 1078 : if (tab->rewrite)
14722 : {
14723 : Relation newrel;
14724 :
14725 776 : newrel = table_open(RelationGetRelid(rel), NoLock);
14726 776 : RelationClearMissing(newrel);
14727 776 : relation_close(newrel, NoLock);
14728 : /* make sure we don't conflict with later attribute modifications */
14729 776 : CommandCounterIncrement();
14730 : }
14731 :
14732 1078 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14733 :
14734 : /* Look up the target column */
14735 1078 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14736 1078 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14737 0 : ereport(ERROR,
14738 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14739 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14740 : colName, RelationGetRelationName(rel))));
14741 1078 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14742 1078 : attnum = attTup->attnum;
14743 1078 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14744 :
14745 : /* Check for multiple ALTER TYPE on same column --- can't cope */
14746 1078 : if (attTup->atttypid != attOldTup->atttypid ||
14747 1078 : attTup->atttypmod != attOldTup->atttypmod)
14748 0 : ereport(ERROR,
14749 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14750 : errmsg("cannot alter type of column \"%s\" twice",
14751 : colName)));
14752 :
14753 : /* Look up the target type (should not fail, since prep found it) */
14754 1078 : typeTuple = typenameType(NULL, typeName, &targettypmod);
14755 1078 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
14756 1078 : targettype = tform->oid;
14757 : /* And the collation */
14758 1078 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
14759 :
14760 : /*
14761 : * If there is a default expression for the column, get it and ensure we
14762 : * can coerce it to the new datatype. (We must do this before changing
14763 : * the column type, because build_column_default itself will try to
14764 : * coerce, and will not issue the error message we want if it fails.)
14765 : *
14766 : * We remove any implicit coercion steps at the top level of the old
14767 : * default expression; this has been agreed to satisfy the principle of
14768 : * least surprise. (The conversion to the new column type should act like
14769 : * it started from what the user sees as the stored expression, and the
14770 : * implicit coercions aren't going to be shown.)
14771 : */
14772 1078 : if (attTup->atthasdef)
14773 : {
14774 92 : defaultexpr = build_column_default(rel, attnum);
14775 : Assert(defaultexpr);
14776 92 : defaultexpr = strip_implicit_coercions(defaultexpr);
14777 92 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14778 : defaultexpr, exprType(defaultexpr),
14779 : targettype, targettypmod,
14780 : COERCION_ASSIGNMENT,
14781 : COERCE_IMPLICIT_CAST,
14782 : -1);
14783 92 : if (defaultexpr == NULL)
14784 : {
14785 6 : if (attTup->attgenerated)
14786 0 : ereport(ERROR,
14787 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14788 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14789 : colName, format_type_be(targettype))));
14790 : else
14791 6 : ereport(ERROR,
14792 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14793 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14794 : colName, format_type_be(targettype))));
14795 : }
14796 : }
14797 : else
14798 986 : defaultexpr = NULL;
14799 :
14800 : /*
14801 : * Find everything that depends on the column (constraints, indexes, etc),
14802 : * and record enough information to let us recreate the objects.
14803 : *
14804 : * The actual recreation does not happen here, but only after we have
14805 : * performed all the individual ALTER TYPE operations. We have to save
14806 : * the info before executing ALTER TYPE, though, else the deparser will
14807 : * get confused.
14808 : */
14809 1072 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
14810 :
14811 : /*
14812 : * Now scan for dependencies of this column on other things. The only
14813 : * things we should find are the dependency on the column datatype and
14814 : * possibly a collation dependency. Those can be removed.
14815 : */
14816 1036 : depRel = table_open(DependRelationId, RowExclusiveLock);
14817 :
14818 1036 : ScanKeyInit(&key[0],
14819 : Anum_pg_depend_classid,
14820 : BTEqualStrategyNumber, F_OIDEQ,
14821 : ObjectIdGetDatum(RelationRelationId));
14822 1036 : ScanKeyInit(&key[1],
14823 : Anum_pg_depend_objid,
14824 : BTEqualStrategyNumber, F_OIDEQ,
14825 : ObjectIdGetDatum(RelationGetRelid(rel)));
14826 1036 : ScanKeyInit(&key[2],
14827 : Anum_pg_depend_objsubid,
14828 : BTEqualStrategyNumber, F_INT4EQ,
14829 : Int32GetDatum((int32) attnum));
14830 :
14831 1036 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
14832 : NULL, 3, key);
14833 :
14834 1040 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14835 : {
14836 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14837 : ObjectAddress foundObject;
14838 :
14839 4 : foundObject.classId = foundDep->refclassid;
14840 4 : foundObject.objectId = foundDep->refobjid;
14841 4 : foundObject.objectSubId = foundDep->refobjsubid;
14842 :
14843 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
14844 0 : elog(ERROR, "found unexpected dependency type '%c'",
14845 : foundDep->deptype);
14846 4 : if (!(foundDep->refclassid == TypeRelationId &&
14847 4 : foundDep->refobjid == attTup->atttypid) &&
14848 0 : !(foundDep->refclassid == CollationRelationId &&
14849 0 : foundDep->refobjid == attTup->attcollation))
14850 0 : elog(ERROR, "found unexpected dependency for column: %s",
14851 : getObjectDescription(&foundObject, false));
14852 :
14853 4 : CatalogTupleDelete(depRel, &depTup->t_self);
14854 : }
14855 :
14856 1036 : systable_endscan(scan);
14857 :
14858 1036 : table_close(depRel, RowExclusiveLock);
14859 :
14860 : /*
14861 : * Here we go --- change the recorded column type and collation. (Note
14862 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14863 : * fix up the missing value if any.
14864 : */
14865 1036 : if (attTup->atthasmissing)
14866 : {
14867 : Datum missingval;
14868 : bool missingNull;
14869 :
14870 : /* if rewrite is true the missing value should already be cleared */
14871 : Assert(tab->rewrite == 0);
14872 :
14873 : /* Get the missing value datum */
14874 6 : missingval = heap_getattr(heapTup,
14875 : Anum_pg_attribute_attmissingval,
14876 : attrelation->rd_att,
14877 : &missingNull);
14878 :
14879 : /* if it's a null array there is nothing to do */
14880 :
14881 6 : if (!missingNull)
14882 : {
14883 : /*
14884 : * Get the datum out of the array and repack it in a new array
14885 : * built with the new type data. We assume that since the table
14886 : * doesn't need rewriting, the actual Datum doesn't need to be
14887 : * changed, only the array metadata.
14888 : */
14889 :
14890 6 : int one = 1;
14891 : bool isNull;
14892 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
14893 6 : bool nullsAtt[Natts_pg_attribute] = {0};
14894 6 : bool replacesAtt[Natts_pg_attribute] = {0};
14895 : HeapTuple newTup;
14896 :
14897 12 : missingval = array_get_element(missingval,
14898 : 1,
14899 : &one,
14900 : 0,
14901 6 : attTup->attlen,
14902 6 : attTup->attbyval,
14903 6 : attTup->attalign,
14904 : &isNull);
14905 6 : missingval = PointerGetDatum(construct_array(&missingval,
14906 : 1,
14907 : targettype,
14908 6 : tform->typlen,
14909 6 : tform->typbyval,
14910 6 : tform->typalign));
14911 :
14912 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14913 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14914 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14915 :
14916 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14917 : valuesAtt, nullsAtt, replacesAtt);
14918 6 : heap_freetuple(heapTup);
14919 6 : heapTup = newTup;
14920 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14921 : }
14922 : }
14923 :
14924 1036 : attTup->atttypid = targettype;
14925 1036 : attTup->atttypmod = targettypmod;
14926 1036 : attTup->attcollation = targetcollid;
14927 1036 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14928 0 : ereport(ERROR,
14929 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14930 : errmsg("too many array dimensions"));
14931 1036 : attTup->attndims = list_length(typeName->arrayBounds);
14932 1036 : attTup->attlen = tform->typlen;
14933 1036 : attTup->attbyval = tform->typbyval;
14934 1036 : attTup->attalign = tform->typalign;
14935 1036 : attTup->attstorage = tform->typstorage;
14936 1036 : attTup->attcompression = InvalidCompressionMethod;
14937 :
14938 1036 : ReleaseSysCache(typeTuple);
14939 :
14940 1036 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14941 :
14942 1036 : table_close(attrelation, RowExclusiveLock);
14943 :
14944 : /* Install dependencies on new datatype and collation */
14945 1036 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
14946 1036 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
14947 :
14948 : /*
14949 : * Drop any pg_statistic entry for the column, since it's now wrong type
14950 : */
14951 1036 : RemoveStatistics(RelationGetRelid(rel), attnum);
14952 :
14953 1036 : InvokeObjectPostAlterHook(RelationRelationId,
14954 : RelationGetRelid(rel), attnum);
14955 :
14956 : /*
14957 : * Update the default, if present, by brute force --- remove and re-add
14958 : * the default. Probably unsafe to take shortcuts, since the new version
14959 : * may well have additional dependencies. (It's okay to do this now,
14960 : * rather than after other ALTER TYPE commands, since the default won't
14961 : * depend on other column types.)
14962 : */
14963 1036 : if (defaultexpr)
14964 : {
14965 : /*
14966 : * If it's a GENERATED default, drop its dependency records, in
14967 : * particular its INTERNAL dependency on the column, which would
14968 : * otherwise cause dependency.c to refuse to perform the deletion.
14969 : */
14970 86 : if (attTup->attgenerated)
14971 : {
14972 36 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14973 :
14974 36 : if (!OidIsValid(attrdefoid))
14975 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14976 : RelationGetRelid(rel), attnum);
14977 36 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14978 : }
14979 :
14980 : /*
14981 : * Make updates-so-far visible, particularly the new pg_attribute row
14982 : * which will be updated again.
14983 : */
14984 86 : CommandCounterIncrement();
14985 :
14986 : /*
14987 : * We use RESTRICT here for safety, but at present we do not expect
14988 : * anything to depend on the default.
14989 : */
14990 86 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
14991 : true);
14992 :
14993 86 : (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
14994 : }
14995 :
14996 1036 : ObjectAddressSubSet(address, RelationRelationId,
14997 : RelationGetRelid(rel), attnum);
14998 :
14999 : /* Cleanup */
15000 1036 : heap_freetuple(heapTup);
15001 :
15002 1036 : return address;
15003 : }
15004 :
15005 : /*
15006 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
15007 : * that depends on the column (constraints, indexes, etc), and record enough
15008 : * information to let us recreate the objects.
15009 : */
15010 : static void
15011 1168 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
15012 : Relation rel, AttrNumber attnum, const char *colName)
15013 : {
15014 : Relation depRel;
15015 : ScanKeyData key[3];
15016 : SysScanDesc scan;
15017 : HeapTuple depTup;
15018 :
15019 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15020 :
15021 1168 : depRel = table_open(DependRelationId, RowExclusiveLock);
15022 :
15023 1168 : ScanKeyInit(&key[0],
15024 : Anum_pg_depend_refclassid,
15025 : BTEqualStrategyNumber, F_OIDEQ,
15026 : ObjectIdGetDatum(RelationRelationId));
15027 1168 : ScanKeyInit(&key[1],
15028 : Anum_pg_depend_refobjid,
15029 : BTEqualStrategyNumber, F_OIDEQ,
15030 : ObjectIdGetDatum(RelationGetRelid(rel)));
15031 1168 : ScanKeyInit(&key[2],
15032 : Anum_pg_depend_refobjsubid,
15033 : BTEqualStrategyNumber, F_INT4EQ,
15034 : Int32GetDatum((int32) attnum));
15035 :
15036 1168 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15037 : NULL, 3, key);
15038 :
15039 2348 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15040 : {
15041 1216 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15042 : ObjectAddress foundObject;
15043 :
15044 1216 : foundObject.classId = foundDep->classid;
15045 1216 : foundObject.objectId = foundDep->objid;
15046 1216 : foundObject.objectSubId = foundDep->objsubid;
15047 :
15048 1216 : switch (foundObject.classId)
15049 : {
15050 286 : case RelationRelationId:
15051 : {
15052 286 : char relKind = get_rel_relkind(foundObject.objectId);
15053 :
15054 286 : if (relKind == RELKIND_INDEX ||
15055 : relKind == RELKIND_PARTITIONED_INDEX)
15056 : {
15057 : Assert(foundObject.objectSubId == 0);
15058 248 : RememberIndexForRebuilding(foundObject.objectId, tab);
15059 : }
15060 38 : else if (relKind == RELKIND_SEQUENCE)
15061 : {
15062 : /*
15063 : * This must be a SERIAL column's sequence. We need
15064 : * not do anything to it.
15065 : */
15066 : Assert(foundObject.objectSubId == 0);
15067 : }
15068 : else
15069 : {
15070 : /* Not expecting any other direct dependencies... */
15071 0 : elog(ERROR, "unexpected object depending on column: %s",
15072 : getObjectDescription(&foundObject, false));
15073 : }
15074 286 : break;
15075 : }
15076 :
15077 686 : case ConstraintRelationId:
15078 : Assert(foundObject.objectSubId == 0);
15079 686 : RememberConstraintForRebuilding(foundObject.objectId, tab);
15080 686 : break;
15081 :
15082 0 : case ProcedureRelationId:
15083 :
15084 : /*
15085 : * A new-style SQL function can depend on a column, if that
15086 : * column is referenced in the parsed function body. Ideally
15087 : * we'd automatically update the function by deparsing and
15088 : * reparsing it, but that's risky and might well fail anyhow.
15089 : * FIXME someday.
15090 : *
15091 : * This is only a problem for AT_AlterColumnType, not
15092 : * AT_SetExpression.
15093 : */
15094 0 : if (subtype == AT_AlterColumnType)
15095 0 : ereport(ERROR,
15096 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15097 : errmsg("cannot alter type of a column used by a function or procedure"),
15098 : errdetail("%s depends on column \"%s\"",
15099 : getObjectDescription(&foundObject, false),
15100 : colName)));
15101 0 : break;
15102 :
15103 12 : case RewriteRelationId:
15104 :
15105 : /*
15106 : * View/rule bodies have pretty much the same issues as
15107 : * function bodies. FIXME someday.
15108 : */
15109 12 : if (subtype == AT_AlterColumnType)
15110 12 : ereport(ERROR,
15111 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15112 : errmsg("cannot alter type of a column used by a view or rule"),
15113 : errdetail("%s depends on column \"%s\"",
15114 : getObjectDescription(&foundObject, false),
15115 : colName)));
15116 0 : break;
15117 :
15118 0 : case TriggerRelationId:
15119 :
15120 : /*
15121 : * A trigger can depend on a column because the column is
15122 : * specified as an update target, or because the column is
15123 : * used in the trigger's WHEN condition. The first case would
15124 : * not require any extra work, but the second case would
15125 : * require updating the WHEN expression, which has the same
15126 : * issues as above. Since we can't easily tell which case
15127 : * applies, we punt for both. FIXME someday.
15128 : */
15129 0 : if (subtype == AT_AlterColumnType)
15130 0 : ereport(ERROR,
15131 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15132 : errmsg("cannot alter type of a column used in a trigger definition"),
15133 : errdetail("%s depends on column \"%s\"",
15134 : getObjectDescription(&foundObject, false),
15135 : colName)));
15136 0 : break;
15137 :
15138 0 : case PolicyRelationId:
15139 :
15140 : /*
15141 : * A policy can depend on a column because the column is
15142 : * specified in the policy's USING or WITH CHECK qual
15143 : * expressions. It might be possible to rewrite and recheck
15144 : * the policy expression, but punt for now. It's certainly
15145 : * easy enough to remove and recreate the policy; still, FIXME
15146 : * someday.
15147 : */
15148 0 : if (subtype == AT_AlterColumnType)
15149 0 : ereport(ERROR,
15150 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15151 : errmsg("cannot alter type of a column used in a policy definition"),
15152 : errdetail("%s depends on column \"%s\"",
15153 : getObjectDescription(&foundObject, false),
15154 : colName)));
15155 0 : break;
15156 :
15157 206 : case AttrDefaultRelationId:
15158 : {
15159 206 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
15160 :
15161 206 : if (col.objectId == RelationGetRelid(rel) &&
15162 206 : col.objectSubId == attnum)
15163 : {
15164 : /*
15165 : * Ignore the column's own default expression. The
15166 : * caller deals with it.
15167 : */
15168 : }
15169 : else
15170 : {
15171 : /*
15172 : * This must be a reference from the expression of a
15173 : * generated column elsewhere in the same table.
15174 : * Changing the type/generated expression of a column
15175 : * that is used by a generated column is not allowed
15176 : * by SQL standard, so just punt for now. It might be
15177 : * doable with some thinking and effort.
15178 : */
15179 24 : if (subtype == AT_AlterColumnType)
15180 24 : ereport(ERROR,
15181 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15182 : errmsg("cannot alter type of a column used by a generated column"),
15183 : errdetail("Column \"%s\" is used by generated column \"%s\".",
15184 : colName,
15185 : get_attname(col.objectId,
15186 : col.objectSubId,
15187 : false))));
15188 : }
15189 182 : break;
15190 : }
15191 :
15192 26 : case StatisticExtRelationId:
15193 :
15194 : /*
15195 : * Give the extended-stats machinery a chance to fix anything
15196 : * that this column type change would break.
15197 : */
15198 26 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
15199 26 : break;
15200 :
15201 0 : case PublicationRelRelationId:
15202 :
15203 : /*
15204 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15205 : * clause. Same issues as above. FIXME someday.
15206 : */
15207 0 : if (subtype == AT_AlterColumnType)
15208 0 : ereport(ERROR,
15209 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15210 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
15211 : errdetail("%s depends on column \"%s\"",
15212 : getObjectDescription(&foundObject, false),
15213 : colName)));
15214 0 : break;
15215 :
15216 0 : default:
15217 :
15218 : /*
15219 : * We don't expect any other sorts of objects to depend on a
15220 : * column.
15221 : */
15222 0 : elog(ERROR, "unexpected object depending on column: %s",
15223 : getObjectDescription(&foundObject, false));
15224 : break;
15225 : }
15226 : }
15227 :
15228 1132 : systable_endscan(scan);
15229 1132 : table_close(depRel, NoLock);
15230 1132 : }
15231 :
15232 : /*
15233 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
15234 : * needs to be reset.
15235 : */
15236 : static void
15237 456 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
15238 : {
15239 456 : if (!get_index_isreplident(indoid))
15240 438 : return;
15241 :
15242 18 : if (tab->replicaIdentityIndex)
15243 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15244 :
15245 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
15246 : }
15247 :
15248 : /*
15249 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
15250 : */
15251 : static void
15252 456 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
15253 : {
15254 456 : if (!get_index_isclustered(indoid))
15255 438 : return;
15256 :
15257 18 : if (tab->clusterOnIndex)
15258 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15259 :
15260 18 : tab->clusterOnIndex = get_rel_name(indoid);
15261 : }
15262 :
15263 : /*
15264 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15265 : * to be rebuilt (which we might already know).
15266 : */
15267 : static void
15268 698 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
15269 : {
15270 : /*
15271 : * This de-duplication check is critical for two independent reasons: we
15272 : * mustn't try to recreate the same constraint twice, and if a constraint
15273 : * depends on more than one column whose type is to be altered, we must
15274 : * capture its definition string before applying any of the column type
15275 : * changes. ruleutils.c will get confused if we ask again later.
15276 : */
15277 698 : if (!list_member_oid(tab->changedConstraintOids, conoid))
15278 : {
15279 : /* OK, capture the constraint's existing definition string */
15280 608 : char *defstring = pg_get_constraintdef_command(conoid);
15281 : Oid indoid;
15282 :
15283 : /*
15284 : * It is critical to create not-null constraints ahead of primary key
15285 : * indexes; otherwise, the not-null constraint would be created by the
15286 : * primary key, and the constraint name would be wrong.
15287 : */
15288 608 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15289 : {
15290 198 : tab->changedConstraintOids = lcons_oid(conoid,
15291 : tab->changedConstraintOids);
15292 198 : tab->changedConstraintDefs = lcons(defstring,
15293 : tab->changedConstraintDefs);
15294 : }
15295 : else
15296 : {
15297 :
15298 410 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
15299 : conoid);
15300 410 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
15301 : defstring);
15302 : }
15303 :
15304 : /*
15305 : * For the index of a constraint, if any, remember if it is used for
15306 : * the table's replica identity or if it is a clustered index, so that
15307 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15308 : * those properties.
15309 : */
15310 608 : indoid = get_constraint_index(conoid);
15311 608 : if (OidIsValid(indoid))
15312 : {
15313 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
15314 228 : RememberClusterOnForRebuilding(indoid, tab);
15315 : }
15316 : }
15317 698 : }
15318 :
15319 : /*
15320 : * Subroutine for ATExecAlterColumnType: remember that an index needs
15321 : * to be rebuilt (which we might already know).
15322 : */
15323 : static void
15324 248 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
15325 : {
15326 : /*
15327 : * This de-duplication check is critical for two independent reasons: we
15328 : * mustn't try to recreate the same index twice, and if an index depends
15329 : * on more than one column whose type is to be altered, we must capture
15330 : * its definition string before applying any of the column type changes.
15331 : * ruleutils.c will get confused if we ask again later.
15332 : */
15333 248 : if (!list_member_oid(tab->changedIndexOids, indoid))
15334 : {
15335 : /*
15336 : * Before adding it as an index-to-rebuild, we'd better see if it
15337 : * belongs to a constraint, and if so rebuild the constraint instead.
15338 : * Typically this check fails, because constraint indexes normally
15339 : * have only dependencies on their constraint. But it's possible for
15340 : * such an index to also have direct dependencies on table columns,
15341 : * for example with a partial exclusion constraint.
15342 : */
15343 240 : Oid conoid = get_index_constraint(indoid);
15344 :
15345 240 : if (OidIsValid(conoid))
15346 : {
15347 12 : RememberConstraintForRebuilding(conoid, tab);
15348 : }
15349 : else
15350 : {
15351 : /* OK, capture the index's existing definition string */
15352 228 : char *defstring = pg_get_indexdef_string(indoid);
15353 :
15354 228 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
15355 : indoid);
15356 228 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
15357 : defstring);
15358 :
15359 : /*
15360 : * Remember if this index is used for the table's replica identity
15361 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15362 : * can queue up commands necessary to restore those properties.
15363 : */
15364 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
15365 228 : RememberClusterOnForRebuilding(indoid, tab);
15366 : }
15367 : }
15368 248 : }
15369 :
15370 : /*
15371 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
15372 : * needs to be rebuilt (which we might already know).
15373 : */
15374 : static void
15375 26 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
15376 : {
15377 : /*
15378 : * This de-duplication check is critical for two independent reasons: we
15379 : * mustn't try to recreate the same statistics object twice, and if the
15380 : * statistics object depends on more than one column whose type is to be
15381 : * altered, we must capture its definition string before applying any of
15382 : * the type changes. ruleutils.c will get confused if we ask again later.
15383 : */
15384 26 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15385 : {
15386 : /* OK, capture the statistics object's existing definition string */
15387 26 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
15388 :
15389 26 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
15390 : stxoid);
15391 26 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
15392 : defstring);
15393 : }
15394 26 : }
15395 :
15396 : /*
15397 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15398 : * operations for a particular relation. We have to drop and recreate all the
15399 : * indexes and constraints that depend on the altered columns. We do the
15400 : * actual dropping here, but re-creation is managed by adding work queue
15401 : * entries to do those steps later.
15402 : */
15403 : static void
15404 1180 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
15405 : {
15406 : ObjectAddress obj;
15407 : ObjectAddresses *objects;
15408 : ListCell *def_item;
15409 : ListCell *oid_item;
15410 :
15411 : /*
15412 : * Collect all the constraints and indexes to drop so we can process them
15413 : * in a single call. That way we don't have to worry about dependencies
15414 : * among them.
15415 : */
15416 1180 : objects = new_object_addresses();
15417 :
15418 : /*
15419 : * Re-parse the index and constraint definitions, and attach them to the
15420 : * appropriate work queue entries. We do this before dropping because in
15421 : * the case of a constraint on another table, we might not yet have
15422 : * exclusive lock on the table the constraint is attached to, and we need
15423 : * to get that before reparsing/dropping. (That's possible at least for
15424 : * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15425 : * requires a dependency on the target table's composite type in the other
15426 : * table's constraint expressions.)
15427 : *
15428 : * We can't rely on the output of deparsing to tell us which relation to
15429 : * operate on, because concurrent activity might have made the name
15430 : * resolve differently. Instead, we've got to use the OID of the
15431 : * constraint or index we're processing to figure out which relation to
15432 : * operate on.
15433 : */
15434 1788 : forboth(oid_item, tab->changedConstraintOids,
15435 : def_item, tab->changedConstraintDefs)
15436 : {
15437 608 : Oid oldId = lfirst_oid(oid_item);
15438 : HeapTuple tup;
15439 : Form_pg_constraint con;
15440 : Oid relid;
15441 : Oid confrelid;
15442 : bool conislocal;
15443 :
15444 608 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15445 608 : if (!HeapTupleIsValid(tup)) /* should not happen */
15446 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15447 608 : con = (Form_pg_constraint) GETSTRUCT(tup);
15448 608 : if (OidIsValid(con->conrelid))
15449 594 : relid = con->conrelid;
15450 : else
15451 : {
15452 : /* must be a domain constraint */
15453 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
15454 14 : if (!OidIsValid(relid))
15455 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15456 : }
15457 608 : confrelid = con->confrelid;
15458 608 : conislocal = con->conislocal;
15459 608 : ReleaseSysCache(tup);
15460 :
15461 608 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
15462 608 : add_exact_object_address(&obj, objects);
15463 :
15464 : /*
15465 : * If the constraint is inherited (only), we don't want to inject a
15466 : * new definition here; it'll get recreated when
15467 : * ATAddCheckNNConstraint recurses from adding the parent table's
15468 : * constraint. But we had to carry the info this far so that we can
15469 : * drop the constraint below.
15470 : */
15471 608 : if (!conislocal)
15472 28 : continue;
15473 :
15474 : /*
15475 : * When rebuilding another table's constraint that references the
15476 : * table we're modifying, we might not yet have any lock on the other
15477 : * table, so get one now. We'll need AccessExclusiveLock for the DROP
15478 : * CONSTRAINT step, so there's no value in asking for anything weaker.
15479 : */
15480 580 : if (relid != tab->relid)
15481 48 : LockRelationOid(relid, AccessExclusiveLock);
15482 :
15483 580 : ATPostAlterTypeParse(oldId, relid, confrelid,
15484 580 : (char *) lfirst(def_item),
15485 580 : wqueue, lockmode, tab->rewrite);
15486 : }
15487 1408 : forboth(oid_item, tab->changedIndexOids,
15488 : def_item, tab->changedIndexDefs)
15489 : {
15490 228 : Oid oldId = lfirst_oid(oid_item);
15491 : Oid relid;
15492 :
15493 228 : relid = IndexGetRelation(oldId, false);
15494 :
15495 : /*
15496 : * As above, make sure we have lock on the index's table if it's not
15497 : * the same table.
15498 : */
15499 228 : if (relid != tab->relid)
15500 12 : LockRelationOid(relid, AccessExclusiveLock);
15501 :
15502 228 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15503 228 : (char *) lfirst(def_item),
15504 228 : wqueue, lockmode, tab->rewrite);
15505 :
15506 228 : ObjectAddressSet(obj, RelationRelationId, oldId);
15507 228 : add_exact_object_address(&obj, objects);
15508 : }
15509 :
15510 : /* add dependencies for new statistics */
15511 1206 : forboth(oid_item, tab->changedStatisticsOids,
15512 : def_item, tab->changedStatisticsDefs)
15513 : {
15514 26 : Oid oldId = lfirst_oid(oid_item);
15515 : Oid relid;
15516 :
15517 26 : relid = StatisticsGetRelation(oldId, false);
15518 :
15519 : /*
15520 : * As above, make sure we have lock on the statistics object's table
15521 : * if it's not the same table. However, we take
15522 : * ShareUpdateExclusiveLock here, aligning with the lock level used in
15523 : * CreateStatistics and RemoveStatisticsById.
15524 : *
15525 : * CAUTION: this should be done after all cases that grab
15526 : * AccessExclusiveLock, else we risk causing deadlock due to needing
15527 : * to promote our table lock.
15528 : */
15529 26 : if (relid != tab->relid)
15530 12 : LockRelationOid(relid, ShareUpdateExclusiveLock);
15531 :
15532 26 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15533 26 : (char *) lfirst(def_item),
15534 26 : wqueue, lockmode, tab->rewrite);
15535 :
15536 26 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15537 26 : add_exact_object_address(&obj, objects);
15538 : }
15539 :
15540 : /*
15541 : * Queue up command to restore replica identity index marking
15542 : */
15543 1180 : if (tab->replicaIdentityIndex)
15544 : {
15545 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15546 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
15547 :
15548 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15549 18 : subcmd->name = tab->replicaIdentityIndex;
15550 18 : cmd->subtype = AT_ReplicaIdentity;
15551 18 : cmd->def = (Node *) subcmd;
15552 :
15553 : /* do it after indexes and constraints */
15554 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15555 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15556 : }
15557 :
15558 : /*
15559 : * Queue up command to restore marking of index used for cluster.
15560 : */
15561 1180 : if (tab->clusterOnIndex)
15562 : {
15563 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15564 :
15565 18 : cmd->subtype = AT_ClusterOn;
15566 18 : cmd->name = tab->clusterOnIndex;
15567 :
15568 : /* do it after indexes and constraints */
15569 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15570 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15571 : }
15572 :
15573 : /*
15574 : * It should be okay to use DROP_RESTRICT here, since nothing else should
15575 : * be depending on these objects.
15576 : */
15577 1180 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
15578 :
15579 1180 : free_object_addresses(objects);
15580 :
15581 : /*
15582 : * The objects will get recreated during subsequent passes over the work
15583 : * queue.
15584 : */
15585 1180 : }
15586 :
15587 : /*
15588 : * Parse the previously-saved definition string for a constraint, index or
15589 : * statistics object against the newly-established column data type(s), and
15590 : * queue up the resulting command parsetrees for execution.
15591 : *
15592 : * This might fail if, for example, you have a WHERE clause that uses an
15593 : * operator that's not available for the new column type.
15594 : */
15595 : static void
15596 834 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15597 : List **wqueue, LOCKMODE lockmode, bool rewrite)
15598 : {
15599 : List *raw_parsetree_list;
15600 : List *querytree_list;
15601 : ListCell *list_item;
15602 : Relation rel;
15603 :
15604 : /*
15605 : * We expect that we will get only ALTER TABLE and CREATE INDEX
15606 : * statements. Hence, there is no need to pass them through
15607 : * parse_analyze_*() or the rewriter, but instead we need to pass them
15608 : * through parse_utilcmd.c to make them ready for execution.
15609 : */
15610 834 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15611 834 : querytree_list = NIL;
15612 1668 : foreach(list_item, raw_parsetree_list)
15613 : {
15614 834 : RawStmt *rs = lfirst_node(RawStmt, list_item);
15615 834 : Node *stmt = rs->stmt;
15616 :
15617 834 : if (IsA(stmt, IndexStmt))
15618 228 : querytree_list = lappend(querytree_list,
15619 228 : transformIndexStmt(oldRelId,
15620 : (IndexStmt *) stmt,
15621 : cmd));
15622 606 : else if (IsA(stmt, AlterTableStmt))
15623 : {
15624 : List *beforeStmts;
15625 : List *afterStmts;
15626 :
15627 566 : stmt = (Node *) transformAlterTableStmt(oldRelId,
15628 : (AlterTableStmt *) stmt,
15629 : cmd,
15630 : &beforeStmts,
15631 : &afterStmts);
15632 566 : querytree_list = list_concat(querytree_list, beforeStmts);
15633 566 : querytree_list = lappend(querytree_list, stmt);
15634 566 : querytree_list = list_concat(querytree_list, afterStmts);
15635 : }
15636 40 : else if (IsA(stmt, CreateStatsStmt))
15637 26 : querytree_list = lappend(querytree_list,
15638 26 : transformStatsStmt(oldRelId,
15639 : (CreateStatsStmt *) stmt,
15640 : cmd));
15641 : else
15642 14 : querytree_list = lappend(querytree_list, stmt);
15643 : }
15644 :
15645 : /* Caller should already have acquired whatever lock we need. */
15646 834 : rel = relation_open(oldRelId, NoLock);
15647 :
15648 : /*
15649 : * Attach each generated command to the proper place in the work queue.
15650 : * Note this could result in creation of entirely new work-queue entries.
15651 : *
15652 : * Also note that we have to tweak the command subtypes, because it turns
15653 : * out that re-creation of indexes and constraints has to act a bit
15654 : * differently from initial creation.
15655 : */
15656 1668 : foreach(list_item, querytree_list)
15657 : {
15658 834 : Node *stm = (Node *) lfirst(list_item);
15659 : AlteredTableInfo *tab;
15660 :
15661 834 : tab = ATGetQueueEntry(wqueue, rel);
15662 :
15663 834 : if (IsA(stm, IndexStmt))
15664 : {
15665 228 : IndexStmt *stmt = (IndexStmt *) stm;
15666 : AlterTableCmd *newcmd;
15667 :
15668 228 : if (!rewrite)
15669 56 : TryReuseIndex(oldId, stmt);
15670 228 : stmt->reset_default_tblspc = true;
15671 : /* keep the index's comment */
15672 228 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15673 :
15674 228 : newcmd = makeNode(AlterTableCmd);
15675 228 : newcmd->subtype = AT_ReAddIndex;
15676 228 : newcmd->def = (Node *) stmt;
15677 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
15678 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15679 : }
15680 606 : else if (IsA(stm, AlterTableStmt))
15681 : {
15682 566 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
15683 : ListCell *lcmd;
15684 :
15685 1132 : foreach(lcmd, stmt->cmds)
15686 : {
15687 566 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
15688 :
15689 566 : if (cmd->subtype == AT_AddIndex)
15690 : {
15691 : IndexStmt *indstmt;
15692 : Oid indoid;
15693 :
15694 228 : indstmt = castNode(IndexStmt, cmd->def);
15695 228 : indoid = get_constraint_index(oldId);
15696 :
15697 228 : if (!rewrite)
15698 48 : TryReuseIndex(indoid, indstmt);
15699 : /* keep any comment on the index */
15700 228 : indstmt->idxcomment = GetComment(indoid,
15701 : RelationRelationId, 0);
15702 228 : indstmt->reset_default_tblspc = true;
15703 :
15704 228 : cmd->subtype = AT_ReAddIndex;
15705 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
15706 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15707 :
15708 : /* recreate any comment on the constraint */
15709 228 : RebuildConstraintComment(tab,
15710 : AT_PASS_OLD_INDEX,
15711 : oldId,
15712 : rel,
15713 : NIL,
15714 228 : indstmt->idxname);
15715 : }
15716 338 : else if (cmd->subtype == AT_AddConstraint)
15717 : {
15718 338 : Constraint *con = castNode(Constraint, cmd->def);
15719 :
15720 338 : con->old_pktable_oid = refRelId;
15721 : /* rewriting neither side of a FK */
15722 338 : if (con->contype == CONSTR_FOREIGN &&
15723 72 : !rewrite && tab->rewrite == 0)
15724 6 : TryReuseForeignKey(oldId, con);
15725 338 : con->reset_default_tblspc = true;
15726 338 : cmd->subtype = AT_ReAddConstraint;
15727 338 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15728 338 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15729 :
15730 : /*
15731 : * Recreate any comment on the constraint. If we have
15732 : * recreated a primary key, then transformTableConstraint
15733 : * has added an unnamed not-null constraint here; skip
15734 : * this in that case.
15735 : */
15736 338 : if (con->conname)
15737 338 : RebuildConstraintComment(tab,
15738 : AT_PASS_OLD_CONSTR,
15739 : oldId,
15740 : rel,
15741 : NIL,
15742 338 : con->conname);
15743 : else
15744 : Assert(con->contype == CONSTR_NOTNULL);
15745 : }
15746 : else
15747 0 : elog(ERROR, "unexpected statement subtype: %d",
15748 : (int) cmd->subtype);
15749 : }
15750 : }
15751 40 : else if (IsA(stm, AlterDomainStmt))
15752 : {
15753 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
15754 :
15755 14 : if (stmt->subtype == AD_AddConstraint)
15756 : {
15757 14 : Constraint *con = castNode(Constraint, stmt->def);
15758 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15759 :
15760 14 : cmd->subtype = AT_ReAddDomainConstraint;
15761 14 : cmd->def = (Node *) stmt;
15762 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15763 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15764 :
15765 : /* recreate any comment on the constraint */
15766 14 : RebuildConstraintComment(tab,
15767 : AT_PASS_OLD_CONSTR,
15768 : oldId,
15769 : NULL,
15770 : stmt->typeName,
15771 14 : con->conname);
15772 : }
15773 : else
15774 0 : elog(ERROR, "unexpected statement subtype: %d",
15775 : (int) stmt->subtype);
15776 : }
15777 26 : else if (IsA(stm, CreateStatsStmt))
15778 : {
15779 26 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
15780 : AlterTableCmd *newcmd;
15781 :
15782 : /* keep the statistics object's comment */
15783 26 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15784 :
15785 26 : newcmd = makeNode(AlterTableCmd);
15786 26 : newcmd->subtype = AT_ReAddStatistics;
15787 26 : newcmd->def = (Node *) stmt;
15788 26 : tab->subcmds[AT_PASS_MISC] =
15789 26 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15790 : }
15791 : else
15792 0 : elog(ERROR, "unexpected statement type: %d",
15793 : (int) nodeTag(stm));
15794 : }
15795 :
15796 834 : relation_close(rel, NoLock);
15797 834 : }
15798 :
15799 : /*
15800 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15801 : * for a table or domain constraint that is being rebuilt.
15802 : *
15803 : * objid is the OID of the constraint.
15804 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15805 : * as a string list) for a domain constraint.
15806 : * (We could dig that info, as well as the conname, out of the pg_constraint
15807 : * entry; but callers already have them so might as well pass them.)
15808 : */
15809 : static void
15810 580 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
15811 : Relation rel, List *domname,
15812 : const char *conname)
15813 : {
15814 : CommentStmt *cmd;
15815 : char *comment_str;
15816 : AlterTableCmd *newcmd;
15817 :
15818 : /* Look for comment for object wanted, and leave if none */
15819 580 : comment_str = GetComment(objid, ConstraintRelationId, 0);
15820 580 : if (comment_str == NULL)
15821 490 : return;
15822 :
15823 : /* Build CommentStmt node, copying all input data for safety */
15824 90 : cmd = makeNode(CommentStmt);
15825 90 : if (rel)
15826 : {
15827 78 : cmd->objtype = OBJECT_TABCONSTRAINT;
15828 78 : cmd->object = (Node *)
15829 78 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
15830 : makeString(pstrdup(RelationGetRelationName(rel))),
15831 : makeString(pstrdup(conname)));
15832 : }
15833 : else
15834 : {
15835 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
15836 12 : cmd->object = (Node *)
15837 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
15838 : makeString(pstrdup(conname)));
15839 : }
15840 90 : cmd->comment = comment_str;
15841 :
15842 : /* Append it to list of commands */
15843 90 : newcmd = makeNode(AlterTableCmd);
15844 90 : newcmd->subtype = AT_ReAddComment;
15845 90 : newcmd->def = (Node *) cmd;
15846 90 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15847 : }
15848 :
15849 : /*
15850 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15851 : * for the real analysis, then mutates the IndexStmt based on that verdict.
15852 : */
15853 : static void
15854 104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
15855 : {
15856 104 : if (CheckIndexCompatible(oldId,
15857 104 : stmt->accessMethod,
15858 104 : stmt->indexParams,
15859 104 : stmt->excludeOpNames,
15860 104 : stmt->iswithoutoverlaps))
15861 : {
15862 104 : Relation irel = index_open(oldId, NoLock);
15863 :
15864 : /* If it's a partitioned index, there is no storage to share. */
15865 104 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15866 : {
15867 74 : stmt->oldNumber = irel->rd_locator.relNumber;
15868 74 : stmt->oldCreateSubid = irel->rd_createSubid;
15869 74 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15870 : }
15871 104 : index_close(irel, NoLock);
15872 : }
15873 104 : }
15874 :
15875 : /*
15876 : * Subroutine for ATPostAlterTypeParse().
15877 : *
15878 : * Stash the old P-F equality operator into the Constraint node, for possible
15879 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15880 : * this constraint can be skipped.
15881 : */
15882 : static void
15883 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
15884 : {
15885 : HeapTuple tup;
15886 : Datum adatum;
15887 : ArrayType *arr;
15888 : Oid *rawarr;
15889 : int numkeys;
15890 : int i;
15891 :
15892 : Assert(con->contype == CONSTR_FOREIGN);
15893 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15894 :
15895 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15896 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
15897 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15898 :
15899 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15900 : Anum_pg_constraint_conpfeqop);
15901 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15902 6 : numkeys = ARR_DIMS(arr)[0];
15903 : /* test follows the one in ri_FetchConstraintInfo() */
15904 6 : if (ARR_NDIM(arr) != 1 ||
15905 6 : ARR_HASNULL(arr) ||
15906 6 : ARR_ELEMTYPE(arr) != OIDOID)
15907 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
15908 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
15909 :
15910 : /* stash a List of the operator Oids in our Constraint node */
15911 12 : for (i = 0; i < numkeys; i++)
15912 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15913 :
15914 6 : ReleaseSysCache(tup);
15915 6 : }
15916 :
15917 : /*
15918 : * ALTER COLUMN .. OPTIONS ( ... )
15919 : *
15920 : * Returns the address of the modified column
15921 : */
15922 : static ObjectAddress
15923 172 : ATExecAlterColumnGenericOptions(Relation rel,
15924 : const char *colName,
15925 : List *options,
15926 : LOCKMODE lockmode)
15927 : {
15928 : Relation ftrel;
15929 : Relation attrel;
15930 : ForeignServer *server;
15931 : ForeignDataWrapper *fdw;
15932 : HeapTuple tuple;
15933 : HeapTuple newtuple;
15934 : bool isnull;
15935 : Datum repl_val[Natts_pg_attribute];
15936 : bool repl_null[Natts_pg_attribute];
15937 : bool repl_repl[Natts_pg_attribute];
15938 : Datum datum;
15939 : Form_pg_foreign_table fttableform;
15940 : Form_pg_attribute atttableform;
15941 : AttrNumber attnum;
15942 : ObjectAddress address;
15943 :
15944 172 : if (options == NIL)
15945 0 : return InvalidObjectAddress;
15946 :
15947 : /* First, determine FDW validator associated to the foreign table. */
15948 172 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15949 172 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15950 172 : if (!HeapTupleIsValid(tuple))
15951 0 : ereport(ERROR,
15952 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15953 : errmsg("foreign table \"%s\" does not exist",
15954 : RelationGetRelationName(rel))));
15955 172 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15956 172 : server = GetForeignServer(fttableform->ftserver);
15957 172 : fdw = GetForeignDataWrapper(server->fdwid);
15958 :
15959 172 : table_close(ftrel, AccessShareLock);
15960 172 : ReleaseSysCache(tuple);
15961 :
15962 172 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
15963 172 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15964 172 : if (!HeapTupleIsValid(tuple))
15965 0 : ereport(ERROR,
15966 : (errcode(ERRCODE_UNDEFINED_COLUMN),
15967 : errmsg("column \"%s\" of relation \"%s\" does not exist",
15968 : colName, RelationGetRelationName(rel))));
15969 :
15970 : /* Prevent them from altering a system attribute */
15971 172 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15972 172 : attnum = atttableform->attnum;
15973 172 : if (attnum <= 0)
15974 6 : ereport(ERROR,
15975 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15976 : errmsg("cannot alter system column \"%s\"", colName)));
15977 :
15978 :
15979 : /* Initialize buffers for new tuple values */
15980 166 : memset(repl_val, 0, sizeof(repl_val));
15981 166 : memset(repl_null, false, sizeof(repl_null));
15982 166 : memset(repl_repl, false, sizeof(repl_repl));
15983 :
15984 : /* Extract the current options */
15985 166 : datum = SysCacheGetAttr(ATTNAME,
15986 : tuple,
15987 : Anum_pg_attribute_attfdwoptions,
15988 : &isnull);
15989 166 : if (isnull)
15990 156 : datum = PointerGetDatum(NULL);
15991 :
15992 : /* Transform the options */
15993 166 : datum = transformGenericOptions(AttributeRelationId,
15994 : datum,
15995 : options,
15996 : fdw->fdwvalidator);
15997 :
15998 166 : if (PointerIsValid(DatumGetPointer(datum)))
15999 166 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16000 : else
16001 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16002 :
16003 166 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16004 :
16005 : /* Everything looks good - update the tuple */
16006 :
16007 166 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16008 : repl_val, repl_null, repl_repl);
16009 :
16010 166 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16011 :
16012 166 : InvokeObjectPostAlterHook(RelationRelationId,
16013 : RelationGetRelid(rel),
16014 : atttableform->attnum);
16015 166 : ObjectAddressSubSet(address, RelationRelationId,
16016 : RelationGetRelid(rel), attnum);
16017 :
16018 166 : ReleaseSysCache(tuple);
16019 :
16020 166 : table_close(attrel, RowExclusiveLock);
16021 :
16022 166 : heap_freetuple(newtuple);
16023 :
16024 166 : return address;
16025 : }
16026 :
16027 : /*
16028 : * ALTER TABLE OWNER
16029 : *
16030 : * recursing is true if we are recursing from a table to its indexes,
16031 : * sequences, or toast table. We don't allow the ownership of those things to
16032 : * be changed separately from the parent table. Also, we can skip permission
16033 : * checks (this is necessary not just an optimization, else we'd fail to
16034 : * handle toast tables properly).
16035 : *
16036 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
16037 : * free-standing composite type.
16038 : */
16039 : void
16040 2224 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
16041 : {
16042 : Relation target_rel;
16043 : Relation class_rel;
16044 : HeapTuple tuple;
16045 : Form_pg_class tuple_class;
16046 :
16047 : /*
16048 : * Get exclusive lock till end of transaction on the target table. Use
16049 : * relation_open so that we can work on indexes and sequences.
16050 : */
16051 2224 : target_rel = relation_open(relationOid, lockmode);
16052 :
16053 : /* Get its pg_class tuple, too */
16054 2224 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
16055 :
16056 2224 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16057 2224 : if (!HeapTupleIsValid(tuple))
16058 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
16059 2224 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16060 :
16061 : /* Can we change the ownership of this tuple? */
16062 2224 : switch (tuple_class->relkind)
16063 : {
16064 1940 : case RELKIND_RELATION:
16065 : case RELKIND_VIEW:
16066 : case RELKIND_MATVIEW:
16067 : case RELKIND_FOREIGN_TABLE:
16068 : case RELKIND_PARTITIONED_TABLE:
16069 : /* ok to change owner */
16070 1940 : break;
16071 96 : case RELKIND_INDEX:
16072 96 : if (!recursing)
16073 : {
16074 : /*
16075 : * Because ALTER INDEX OWNER used to be allowed, and in fact
16076 : * is generated by old versions of pg_dump, we give a warning
16077 : * and do nothing rather than erroring out. Also, to avoid
16078 : * unnecessary chatter while restoring those old dumps, say
16079 : * nothing at all if the command would be a no-op anyway.
16080 : */
16081 0 : if (tuple_class->relowner != newOwnerId)
16082 0 : ereport(WARNING,
16083 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16084 : errmsg("cannot change owner of index \"%s\"",
16085 : NameStr(tuple_class->relname)),
16086 : errhint("Change the ownership of the index's table instead.")));
16087 : /* quick hack to exit via the no-op path */
16088 0 : newOwnerId = tuple_class->relowner;
16089 : }
16090 96 : break;
16091 20 : case RELKIND_PARTITIONED_INDEX:
16092 20 : if (recursing)
16093 20 : break;
16094 0 : ereport(ERROR,
16095 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16096 : errmsg("cannot change owner of index \"%s\"",
16097 : NameStr(tuple_class->relname)),
16098 : errhint("Change the ownership of the index's table instead.")));
16099 : break;
16100 118 : case RELKIND_SEQUENCE:
16101 118 : if (!recursing &&
16102 70 : tuple_class->relowner != newOwnerId)
16103 : {
16104 : /* if it's an owned sequence, disallow changing it by itself */
16105 : Oid tableId;
16106 : int32 colId;
16107 :
16108 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16109 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16110 0 : ereport(ERROR,
16111 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16112 : errmsg("cannot change owner of sequence \"%s\"",
16113 : NameStr(tuple_class->relname)),
16114 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
16115 : NameStr(tuple_class->relname),
16116 : get_rel_name(tableId))));
16117 : }
16118 118 : break;
16119 8 : case RELKIND_COMPOSITE_TYPE:
16120 8 : if (recursing)
16121 8 : break;
16122 0 : ereport(ERROR,
16123 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16124 : errmsg("\"%s\" is a composite type",
16125 : NameStr(tuple_class->relname)),
16126 : /* translator: %s is an SQL ALTER command */
16127 : errhint("Use %s instead.",
16128 : "ALTER TYPE")));
16129 : break;
16130 42 : case RELKIND_TOASTVALUE:
16131 42 : if (recursing)
16132 42 : break;
16133 : /* FALL THRU */
16134 : default:
16135 0 : ereport(ERROR,
16136 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16137 : errmsg("cannot change owner of relation \"%s\"",
16138 : NameStr(tuple_class->relname)),
16139 : errdetail_relkind_not_supported(tuple_class->relkind)));
16140 : }
16141 :
16142 : /*
16143 : * If the new owner is the same as the existing owner, consider the
16144 : * command to have succeeded. This is for dump restoration purposes.
16145 : */
16146 2224 : if (tuple_class->relowner != newOwnerId)
16147 : {
16148 : Datum repl_val[Natts_pg_class];
16149 : bool repl_null[Natts_pg_class];
16150 : bool repl_repl[Natts_pg_class];
16151 : Acl *newAcl;
16152 : Datum aclDatum;
16153 : bool isNull;
16154 : HeapTuple newtuple;
16155 :
16156 : /* skip permission checks when recursing to index or toast table */
16157 498 : if (!recursing)
16158 : {
16159 : /* Superusers can always do it */
16160 280 : if (!superuser())
16161 : {
16162 42 : Oid namespaceOid = tuple_class->relnamespace;
16163 : AclResult aclresult;
16164 :
16165 : /* Otherwise, must be owner of the existing object */
16166 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16167 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
16168 0 : RelationGetRelationName(target_rel));
16169 :
16170 : /* Must be able to become new owner */
16171 42 : check_can_set_role(GetUserId(), newOwnerId);
16172 :
16173 : /* New owner must have CREATE privilege on namespace */
16174 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16175 : ACL_CREATE);
16176 30 : if (aclresult != ACLCHECK_OK)
16177 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
16178 0 : get_namespace_name(namespaceOid));
16179 : }
16180 : }
16181 :
16182 486 : memset(repl_null, false, sizeof(repl_null));
16183 486 : memset(repl_repl, false, sizeof(repl_repl));
16184 :
16185 486 : repl_repl[Anum_pg_class_relowner - 1] = true;
16186 486 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16187 :
16188 : /*
16189 : * Determine the modified ACL for the new owner. This is only
16190 : * necessary when the ACL is non-null.
16191 : */
16192 486 : aclDatum = SysCacheGetAttr(RELOID, tuple,
16193 : Anum_pg_class_relacl,
16194 : &isNull);
16195 486 : if (!isNull)
16196 : {
16197 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16198 : tuple_class->relowner, newOwnerId);
16199 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
16200 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16201 : }
16202 :
16203 486 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16204 :
16205 486 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16206 :
16207 486 : heap_freetuple(newtuple);
16208 :
16209 : /*
16210 : * We must similarly update any per-column ACLs to reflect the new
16211 : * owner; for neatness reasons that's split out as a subroutine.
16212 : */
16213 486 : change_owner_fix_column_acls(relationOid,
16214 : tuple_class->relowner,
16215 : newOwnerId);
16216 :
16217 : /*
16218 : * Update owner dependency reference, if any. A composite type has
16219 : * none, because it's tracked for the pg_type entry instead of here;
16220 : * indexes and TOAST tables don't have their own entries either.
16221 : */
16222 486 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16223 478 : tuple_class->relkind != RELKIND_INDEX &&
16224 382 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16225 362 : tuple_class->relkind != RELKIND_TOASTVALUE)
16226 320 : changeDependencyOnOwner(RelationRelationId, relationOid,
16227 : newOwnerId);
16228 :
16229 : /*
16230 : * Also change the ownership of the table's row type, if it has one
16231 : */
16232 486 : if (OidIsValid(tuple_class->reltype))
16233 294 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16234 :
16235 : /*
16236 : * If we are operating on a table or materialized view, also change
16237 : * the ownership of any indexes and sequences that belong to the
16238 : * relation, as well as its toast table (if it has one).
16239 : */
16240 486 : if (tuple_class->relkind == RELKIND_RELATION ||
16241 262 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16242 224 : tuple_class->relkind == RELKIND_MATVIEW ||
16243 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
16244 : {
16245 : List *index_oid_list;
16246 : ListCell *i;
16247 :
16248 : /* Find all the indexes belonging to this relation */
16249 304 : index_oid_list = RelationGetIndexList(target_rel);
16250 :
16251 : /* For each index, recursively change its ownership */
16252 420 : foreach(i, index_oid_list)
16253 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16254 :
16255 304 : list_free(index_oid_list);
16256 : }
16257 :
16258 : /* If it has a toast table, recurse to change its ownership */
16259 486 : if (tuple_class->reltoastrelid != InvalidOid)
16260 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16261 : true, lockmode);
16262 :
16263 : /* If it has dependent sequences, recurse to change them too */
16264 486 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16265 : }
16266 :
16267 2212 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16268 :
16269 2212 : ReleaseSysCache(tuple);
16270 2212 : table_close(class_rel, RowExclusiveLock);
16271 2212 : relation_close(target_rel, NoLock);
16272 2212 : }
16273 :
16274 : /*
16275 : * change_owner_fix_column_acls
16276 : *
16277 : * Helper function for ATExecChangeOwner. Scan the columns of the table
16278 : * and fix any non-null column ACLs to reflect the new owner.
16279 : */
16280 : static void
16281 486 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16282 : {
16283 : Relation attRelation;
16284 : SysScanDesc scan;
16285 : ScanKeyData key[1];
16286 : HeapTuple attributeTuple;
16287 :
16288 486 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16289 486 : ScanKeyInit(&key[0],
16290 : Anum_pg_attribute_attrelid,
16291 : BTEqualStrategyNumber, F_OIDEQ,
16292 : ObjectIdGetDatum(relationOid));
16293 486 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16294 : true, NULL, 1, key);
16295 3372 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16296 : {
16297 2886 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16298 : Datum repl_val[Natts_pg_attribute];
16299 : bool repl_null[Natts_pg_attribute];
16300 : bool repl_repl[Natts_pg_attribute];
16301 : Acl *newAcl;
16302 : Datum aclDatum;
16303 : bool isNull;
16304 : HeapTuple newtuple;
16305 :
16306 : /* Ignore dropped columns */
16307 2886 : if (att->attisdropped)
16308 2884 : continue;
16309 :
16310 2886 : aclDatum = heap_getattr(attributeTuple,
16311 : Anum_pg_attribute_attacl,
16312 : RelationGetDescr(attRelation),
16313 : &isNull);
16314 : /* Null ACLs do not require changes */
16315 2886 : if (isNull)
16316 2884 : continue;
16317 :
16318 2 : memset(repl_null, false, sizeof(repl_null));
16319 2 : memset(repl_repl, false, sizeof(repl_repl));
16320 :
16321 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16322 : oldOwnerId, newOwnerId);
16323 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
16324 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16325 :
16326 2 : newtuple = heap_modify_tuple(attributeTuple,
16327 : RelationGetDescr(attRelation),
16328 : repl_val, repl_null, repl_repl);
16329 :
16330 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16331 :
16332 2 : heap_freetuple(newtuple);
16333 : }
16334 486 : systable_endscan(scan);
16335 486 : table_close(attRelation, RowExclusiveLock);
16336 486 : }
16337 :
16338 : /*
16339 : * change_owner_recurse_to_sequences
16340 : *
16341 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
16342 : * for sequences that are dependent on serial columns, and changes their
16343 : * ownership.
16344 : */
16345 : static void
16346 486 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16347 : {
16348 : Relation depRel;
16349 : SysScanDesc scan;
16350 : ScanKeyData key[2];
16351 : HeapTuple tup;
16352 :
16353 : /*
16354 : * SERIAL sequences are those having an auto dependency on one of the
16355 : * table's columns (we don't care *which* column, exactly).
16356 : */
16357 486 : depRel = table_open(DependRelationId, AccessShareLock);
16358 :
16359 486 : ScanKeyInit(&key[0],
16360 : Anum_pg_depend_refclassid,
16361 : BTEqualStrategyNumber, F_OIDEQ,
16362 : ObjectIdGetDatum(RelationRelationId));
16363 486 : ScanKeyInit(&key[1],
16364 : Anum_pg_depend_refobjid,
16365 : BTEqualStrategyNumber, F_OIDEQ,
16366 : ObjectIdGetDatum(relationOid));
16367 : /* we leave refobjsubid unspecified */
16368 :
16369 486 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16370 : NULL, 2, key);
16371 :
16372 1374 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
16373 : {
16374 888 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16375 : Relation seqRel;
16376 :
16377 : /* skip dependencies other than auto dependencies on columns */
16378 888 : if (depForm->refobjsubid == 0 ||
16379 352 : depForm->classid != RelationRelationId ||
16380 142 : depForm->objsubid != 0 ||
16381 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16382 746 : continue;
16383 :
16384 : /* Use relation_open just in case it's an index */
16385 142 : seqRel = relation_open(depForm->objid, lockmode);
16386 :
16387 : /* skip non-sequence relations */
16388 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16389 : {
16390 : /* No need to keep the lock */
16391 116 : relation_close(seqRel, lockmode);
16392 116 : continue;
16393 : }
16394 :
16395 : /* We don't need to close the sequence while we alter it. */
16396 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16397 :
16398 : /* Now we can close it. Keep the lock till end of transaction. */
16399 26 : relation_close(seqRel, NoLock);
16400 : }
16401 :
16402 486 : systable_endscan(scan);
16403 :
16404 486 : relation_close(depRel, AccessShareLock);
16405 486 : }
16406 :
16407 : /*
16408 : * ALTER TABLE CLUSTER ON
16409 : *
16410 : * The only thing we have to do is to change the indisclustered bits.
16411 : *
16412 : * Return the address of the new clustering index.
16413 : */
16414 : static ObjectAddress
16415 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16416 : {
16417 : Oid indexOid;
16418 : ObjectAddress address;
16419 :
16420 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16421 :
16422 64 : if (!OidIsValid(indexOid))
16423 0 : ereport(ERROR,
16424 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16425 : errmsg("index \"%s\" for table \"%s\" does not exist",
16426 : indexName, RelationGetRelationName(rel))));
16427 :
16428 : /* Check index is valid to cluster on */
16429 64 : check_index_is_clusterable(rel, indexOid, lockmode);
16430 :
16431 : /* And do the work */
16432 64 : mark_index_clustered(rel, indexOid, false);
16433 :
16434 58 : ObjectAddressSet(address,
16435 : RelationRelationId, indexOid);
16436 :
16437 58 : return address;
16438 : }
16439 :
16440 : /*
16441 : * ALTER TABLE SET WITHOUT CLUSTER
16442 : *
16443 : * We have to find any indexes on the table that have indisclustered bit
16444 : * set and turn it off.
16445 : */
16446 : static void
16447 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
16448 : {
16449 18 : mark_index_clustered(rel, InvalidOid, false);
16450 12 : }
16451 :
16452 : /*
16453 : * Preparation phase for SET ACCESS METHOD
16454 : *
16455 : * Check that the access method exists and determine whether a change is
16456 : * actually needed.
16457 : */
16458 : static void
16459 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
16460 : {
16461 : Oid amoid;
16462 :
16463 : /*
16464 : * Look up the access method name and check that it differs from the
16465 : * table's current AM. If DEFAULT was specified for a partitioned table
16466 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16467 : */
16468 110 : if (amname != NULL)
16469 74 : amoid = get_table_am_oid(amname, false);
16470 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16471 18 : amoid = InvalidOid;
16472 : else
16473 18 : amoid = get_table_am_oid(default_table_access_method, false);
16474 :
16475 : /* if it's a match, phase 3 doesn't need to do anything */
16476 110 : if (rel->rd_rel->relam == amoid)
16477 12 : return;
16478 :
16479 : /* Save info for Phase 3 to do the real work */
16480 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
16481 98 : tab->newAccessMethod = amoid;
16482 98 : tab->chgAccessMethod = true;
16483 : }
16484 :
16485 : /*
16486 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16487 : * storage that have an interest in preserving AM.
16488 : *
16489 : * Since these have no storage, setting the access method is a catalog only
16490 : * operation.
16491 : */
16492 : static void
16493 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
16494 : {
16495 : Relation pg_class;
16496 : Oid oldAccessMethodId;
16497 : HeapTuple tuple;
16498 : Form_pg_class rd_rel;
16499 44 : Oid reloid = RelationGetRelid(rel);
16500 :
16501 : /*
16502 : * Shouldn't be called on relations having storage; these are processed in
16503 : * phase 3.
16504 : */
16505 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16506 :
16507 : /* Get a modifiable copy of the relation's pg_class row. */
16508 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
16509 :
16510 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16511 44 : if (!HeapTupleIsValid(tuple))
16512 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
16513 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16514 :
16515 : /* Update the pg_class row. */
16516 44 : oldAccessMethodId = rd_rel->relam;
16517 44 : rd_rel->relam = newAccessMethodId;
16518 :
16519 : /* Leave if no update required */
16520 44 : if (rd_rel->relam == oldAccessMethodId)
16521 : {
16522 0 : heap_freetuple(tuple);
16523 0 : table_close(pg_class, RowExclusiveLock);
16524 0 : return;
16525 : }
16526 :
16527 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16528 :
16529 : /*
16530 : * Update the dependency on the new access method. No dependency is added
16531 : * if the new access method is InvalidOid (default case). Be very careful
16532 : * that this has to compare the previous value stored in pg_class with the
16533 : * new one.
16534 : */
16535 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16536 20 : {
16537 : ObjectAddress relobj,
16538 : referenced;
16539 :
16540 : /*
16541 : * New access method is defined and there was no dependency
16542 : * previously, so record a new one.
16543 : */
16544 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
16545 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16546 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16547 : }
16548 24 : else if (OidIsValid(oldAccessMethodId) &&
16549 24 : !OidIsValid(rd_rel->relam))
16550 : {
16551 : /*
16552 : * There was an access method defined, and no new one, so just remove
16553 : * the existing dependency.
16554 : */
16555 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
16556 : AccessMethodRelationId,
16557 : DEPENDENCY_NORMAL);
16558 : }
16559 : else
16560 : {
16561 : Assert(OidIsValid(oldAccessMethodId) &&
16562 : OidIsValid(rd_rel->relam));
16563 :
16564 : /* Both are valid, so update the dependency */
16565 12 : changeDependencyFor(RelationRelationId, reloid,
16566 : AccessMethodRelationId,
16567 : oldAccessMethodId, rd_rel->relam);
16568 : }
16569 :
16570 : /* make the relam and dependency changes visible */
16571 44 : CommandCounterIncrement();
16572 :
16573 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16574 :
16575 44 : heap_freetuple(tuple);
16576 44 : table_close(pg_class, RowExclusiveLock);
16577 : }
16578 :
16579 : /*
16580 : * ALTER TABLE SET TABLESPACE
16581 : */
16582 : static void
16583 158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16584 : {
16585 : Oid tablespaceId;
16586 :
16587 : /* Check that the tablespace exists */
16588 158 : tablespaceId = get_tablespace_oid(tablespacename, false);
16589 :
16590 : /* Check permissions except when moving to database's default */
16591 158 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16592 : {
16593 : AclResult aclresult;
16594 :
16595 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16596 66 : if (aclresult != ACLCHECK_OK)
16597 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16598 : }
16599 :
16600 : /* Save info for Phase 3 to do the real work */
16601 158 : if (OidIsValid(tab->newTableSpace))
16602 0 : ereport(ERROR,
16603 : (errcode(ERRCODE_SYNTAX_ERROR),
16604 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
16605 :
16606 158 : tab->newTableSpace = tablespaceId;
16607 158 : }
16608 :
16609 : /*
16610 : * Set, reset, or replace reloptions.
16611 : */
16612 : static void
16613 960 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
16614 : LOCKMODE lockmode)
16615 : {
16616 : Oid relid;
16617 : Relation pgclass;
16618 : HeapTuple tuple;
16619 : HeapTuple newtuple;
16620 : Datum datum;
16621 : Datum newOptions;
16622 : Datum repl_val[Natts_pg_class];
16623 : bool repl_null[Natts_pg_class];
16624 : bool repl_repl[Natts_pg_class];
16625 960 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16626 :
16627 960 : if (defList == NIL && operation != AT_ReplaceRelOptions)
16628 0 : return; /* nothing to do */
16629 :
16630 960 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
16631 :
16632 : /* Fetch heap tuple */
16633 960 : relid = RelationGetRelid(rel);
16634 960 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16635 960 : if (!HeapTupleIsValid(tuple))
16636 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16637 :
16638 960 : if (operation == AT_ReplaceRelOptions)
16639 : {
16640 : /*
16641 : * If we're supposed to replace the reloptions list, we just pretend
16642 : * there were none before.
16643 : */
16644 194 : datum = (Datum) 0;
16645 : }
16646 : else
16647 : {
16648 : bool isnull;
16649 :
16650 : /* Get the old reloptions */
16651 766 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16652 : &isnull);
16653 766 : if (isnull)
16654 478 : datum = (Datum) 0;
16655 : }
16656 :
16657 : /* Generate new proposed reloptions (text array) */
16658 960 : newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16659 : operation == AT_ResetRelOptions);
16660 :
16661 : /* Validate */
16662 954 : switch (rel->rd_rel->relkind)
16663 : {
16664 536 : case RELKIND_RELATION:
16665 : case RELKIND_MATVIEW:
16666 536 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16667 536 : break;
16668 6 : case RELKIND_PARTITIONED_TABLE:
16669 6 : (void) partitioned_table_reloptions(newOptions, true);
16670 0 : break;
16671 296 : case RELKIND_VIEW:
16672 296 : (void) view_reloptions(newOptions, true);
16673 278 : break;
16674 116 : case RELKIND_INDEX:
16675 : case RELKIND_PARTITIONED_INDEX:
16676 116 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16677 94 : break;
16678 0 : case RELKIND_TOASTVALUE:
16679 : /* fall through to error -- shouldn't ever get here */
16680 : default:
16681 0 : ereport(ERROR,
16682 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16683 : errmsg("cannot set options for relation \"%s\"",
16684 : RelationGetRelationName(rel)),
16685 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16686 : break;
16687 : }
16688 :
16689 : /* Special-case validation of view options */
16690 908 : if (rel->rd_rel->relkind == RELKIND_VIEW)
16691 : {
16692 278 : Query *view_query = get_view_query(rel);
16693 278 : List *view_options = untransformRelOptions(newOptions);
16694 : ListCell *cell;
16695 278 : bool check_option = false;
16696 :
16697 380 : foreach(cell, view_options)
16698 : {
16699 102 : DefElem *defel = (DefElem *) lfirst(cell);
16700 :
16701 102 : if (strcmp(defel->defname, "check_option") == 0)
16702 24 : check_option = true;
16703 : }
16704 :
16705 : /*
16706 : * If the check option is specified, look to see if the view is
16707 : * actually auto-updatable or not.
16708 : */
16709 278 : if (check_option)
16710 : {
16711 : const char *view_updatable_error =
16712 24 : view_query_is_auto_updatable(view_query, true);
16713 :
16714 24 : if (view_updatable_error)
16715 0 : ereport(ERROR,
16716 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16717 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16718 : errhint("%s", _(view_updatable_error))));
16719 : }
16720 : }
16721 :
16722 : /*
16723 : * All we need do here is update the pg_class row; the new options will be
16724 : * propagated into relcaches during post-commit cache inval.
16725 : */
16726 908 : memset(repl_val, 0, sizeof(repl_val));
16727 908 : memset(repl_null, false, sizeof(repl_null));
16728 908 : memset(repl_repl, false, sizeof(repl_repl));
16729 :
16730 908 : if (newOptions != (Datum) 0)
16731 614 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16732 : else
16733 294 : repl_null[Anum_pg_class_reloptions - 1] = true;
16734 :
16735 908 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16736 :
16737 908 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16738 : repl_val, repl_null, repl_repl);
16739 :
16740 908 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16741 908 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16742 :
16743 908 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16744 :
16745 908 : heap_freetuple(newtuple);
16746 :
16747 908 : ReleaseSysCache(tuple);
16748 :
16749 : /* repeat the whole exercise for the toast table, if there's one */
16750 908 : if (OidIsValid(rel->rd_rel->reltoastrelid))
16751 : {
16752 : Relation toastrel;
16753 268 : Oid toastid = rel->rd_rel->reltoastrelid;
16754 :
16755 268 : toastrel = table_open(toastid, lockmode);
16756 :
16757 : /* Fetch heap tuple */
16758 268 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16759 268 : if (!HeapTupleIsValid(tuple))
16760 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
16761 :
16762 268 : if (operation == AT_ReplaceRelOptions)
16763 : {
16764 : /*
16765 : * If we're supposed to replace the reloptions list, we just
16766 : * pretend there were none before.
16767 : */
16768 0 : datum = (Datum) 0;
16769 : }
16770 : else
16771 : {
16772 : bool isnull;
16773 :
16774 : /* Get the old reloptions */
16775 268 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16776 : &isnull);
16777 268 : if (isnull)
16778 232 : datum = (Datum) 0;
16779 : }
16780 :
16781 268 : newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16782 : false, operation == AT_ResetRelOptions);
16783 :
16784 268 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16785 :
16786 268 : memset(repl_val, 0, sizeof(repl_val));
16787 268 : memset(repl_null, false, sizeof(repl_null));
16788 268 : memset(repl_repl, false, sizeof(repl_repl));
16789 :
16790 268 : if (newOptions != (Datum) 0)
16791 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16792 : else
16793 226 : repl_null[Anum_pg_class_reloptions - 1] = true;
16794 :
16795 268 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16796 :
16797 268 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16798 : repl_val, repl_null, repl_repl);
16799 :
16800 268 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16801 :
16802 268 : InvokeObjectPostAlterHookArg(RelationRelationId,
16803 : RelationGetRelid(toastrel), 0,
16804 : InvalidOid, true);
16805 :
16806 268 : heap_freetuple(newtuple);
16807 :
16808 268 : ReleaseSysCache(tuple);
16809 :
16810 268 : table_close(toastrel, NoLock);
16811 : }
16812 :
16813 908 : table_close(pgclass, RowExclusiveLock);
16814 : }
16815 :
16816 : /*
16817 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16818 : * rewriting to be done, so we just want to copy the data as fast as possible.
16819 : */
16820 : static void
16821 162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16822 : {
16823 : Relation rel;
16824 : Oid reltoastrelid;
16825 : RelFileNumber newrelfilenumber;
16826 : RelFileLocator newrlocator;
16827 162 : List *reltoastidxids = NIL;
16828 : ListCell *lc;
16829 :
16830 : /*
16831 : * Need lock here in case we are recursing to toast table or index
16832 : */
16833 162 : rel = relation_open(tableOid, lockmode);
16834 :
16835 : /* Check first if relation can be moved to new tablespace */
16836 162 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16837 : {
16838 2 : InvokeObjectPostAlterHook(RelationRelationId,
16839 : RelationGetRelid(rel), 0);
16840 2 : relation_close(rel, NoLock);
16841 2 : return;
16842 : }
16843 :
16844 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
16845 : /* Fetch the list of indexes on toast relation if necessary */
16846 160 : if (OidIsValid(reltoastrelid))
16847 : {
16848 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
16849 :
16850 20 : reltoastidxids = RelationGetIndexList(toastRel);
16851 20 : relation_close(toastRel, lockmode);
16852 : }
16853 :
16854 : /*
16855 : * Relfilenumbers are not unique in databases across tablespaces, so we
16856 : * need to allocate a new one in the new tablespace.
16857 : */
16858 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16859 160 : rel->rd_rel->relpersistence);
16860 :
16861 : /* Open old and new relation */
16862 160 : newrlocator = rel->rd_locator;
16863 160 : newrlocator.relNumber = newrelfilenumber;
16864 160 : newrlocator.spcOid = newTableSpace;
16865 :
16866 : /* hand off to AM to actually create new rel storage and copy the data */
16867 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
16868 : {
16869 62 : index_copy_data(rel, newrlocator);
16870 : }
16871 : else
16872 : {
16873 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16874 98 : table_relation_copy_data(rel, &newrlocator);
16875 : }
16876 :
16877 : /*
16878 : * Update the pg_class row.
16879 : *
16880 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16881 : * executed on pg_class or its indexes (the above copy wouldn't contain
16882 : * the updated pg_class entry), but that's forbidden with
16883 : * CheckRelationTableSpaceMove().
16884 : */
16885 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16886 :
16887 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16888 :
16889 160 : RelationAssumeNewRelfilelocator(rel);
16890 :
16891 160 : relation_close(rel, NoLock);
16892 :
16893 : /* Make sure the reltablespace change is visible */
16894 160 : CommandCounterIncrement();
16895 :
16896 : /* Move associated toast relation and/or indexes, too */
16897 160 : if (OidIsValid(reltoastrelid))
16898 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16899 180 : foreach(lc, reltoastidxids)
16900 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16901 :
16902 : /* Clean up */
16903 160 : list_free(reltoastidxids);
16904 : }
16905 :
16906 : /*
16907 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16908 : * storage that have an interest in preserving tablespace.
16909 : *
16910 : * Since these have no storage the tablespace can be updated with a simple
16911 : * metadata only operation to update the tablespace.
16912 : */
16913 : static void
16914 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
16915 : {
16916 : /*
16917 : * Shouldn't be called on relations having storage; these are processed in
16918 : * phase 3.
16919 : */
16920 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16921 :
16922 : /* check if relation can be moved to its new tablespace */
16923 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16924 : {
16925 0 : InvokeObjectPostAlterHook(RelationRelationId,
16926 : RelationGetRelid(rel),
16927 : 0);
16928 0 : return;
16929 : }
16930 :
16931 : /* Update can be done, so change reltablespace */
16932 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16933 :
16934 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16935 :
16936 : /* Make sure the reltablespace change is visible */
16937 30 : CommandCounterIncrement();
16938 : }
16939 :
16940 : /*
16941 : * Alter Table ALL ... SET TABLESPACE
16942 : *
16943 : * Allows a user to move all objects of some type in a given tablespace in the
16944 : * current database to another tablespace. Objects can be chosen based on the
16945 : * owner of the object also, to allow users to move only their objects.
16946 : * The user must have CREATE rights on the new tablespace, as usual. The main
16947 : * permissions handling is done by the lower-level table move function.
16948 : *
16949 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
16950 : * lock can't be acquired then we ereport(ERROR).
16951 : */
16952 : Oid
16953 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
16954 : {
16955 30 : List *relations = NIL;
16956 : ListCell *l;
16957 : ScanKeyData key[1];
16958 : Relation rel;
16959 : TableScanDesc scan;
16960 : HeapTuple tuple;
16961 : Oid orig_tablespaceoid;
16962 : Oid new_tablespaceoid;
16963 30 : List *role_oids = roleSpecsToIds(stmt->roles);
16964 :
16965 : /* Ensure we were not asked to move something we can't */
16966 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16967 12 : stmt->objtype != OBJECT_MATVIEW)
16968 0 : ereport(ERROR,
16969 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16970 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16971 :
16972 : /* Get the orig and new tablespace OIDs */
16973 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16974 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16975 :
16976 : /* Can't move shared relations in to or out of pg_global */
16977 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16978 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16979 : new_tablespaceoid == GLOBALTABLESPACE_OID)
16980 0 : ereport(ERROR,
16981 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16982 : errmsg("cannot move relations in to or out of pg_global tablespace")));
16983 :
16984 : /*
16985 : * Must have CREATE rights on the new tablespace, unless it is the
16986 : * database default tablespace (which all users implicitly have CREATE
16987 : * rights on).
16988 : */
16989 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16990 : {
16991 : AclResult aclresult;
16992 :
16993 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16994 : ACL_CREATE);
16995 0 : if (aclresult != ACLCHECK_OK)
16996 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
16997 0 : get_tablespace_name(new_tablespaceoid));
16998 : }
16999 :
17000 : /*
17001 : * Now that the checks are done, check if we should set either to
17002 : * InvalidOid because it is our database's default tablespace.
17003 : */
17004 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
17005 0 : orig_tablespaceoid = InvalidOid;
17006 :
17007 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
17008 30 : new_tablespaceoid = InvalidOid;
17009 :
17010 : /* no-op */
17011 30 : if (orig_tablespaceoid == new_tablespaceoid)
17012 0 : return new_tablespaceoid;
17013 :
17014 : /*
17015 : * Walk the list of objects in the tablespace and move them. This will
17016 : * only find objects in our database, of course.
17017 : */
17018 30 : ScanKeyInit(&key[0],
17019 : Anum_pg_class_reltablespace,
17020 : BTEqualStrategyNumber, F_OIDEQ,
17021 : ObjectIdGetDatum(orig_tablespaceoid));
17022 :
17023 30 : rel = table_open(RelationRelationId, AccessShareLock);
17024 30 : scan = table_beginscan_catalog(rel, 1, key);
17025 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17026 : {
17027 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17028 102 : Oid relOid = relForm->oid;
17029 :
17030 : /*
17031 : * Do not move objects in pg_catalog as part of this, if an admin
17032 : * really wishes to do so, they can issue the individual ALTER
17033 : * commands directly.
17034 : *
17035 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
17036 : * (TOAST will be moved with the main table).
17037 : */
17038 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
17039 204 : relForm->relisshared ||
17040 204 : isAnyTempNamespace(relForm->relnamespace) ||
17041 102 : IsToastNamespace(relForm->relnamespace))
17042 0 : continue;
17043 :
17044 : /* Only move the object type requested */
17045 102 : if ((stmt->objtype == OBJECT_TABLE &&
17046 60 : relForm->relkind != RELKIND_RELATION &&
17047 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17048 66 : (stmt->objtype == OBJECT_INDEX &&
17049 36 : relForm->relkind != RELKIND_INDEX &&
17050 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17051 60 : (stmt->objtype == OBJECT_MATVIEW &&
17052 6 : relForm->relkind != RELKIND_MATVIEW))
17053 42 : continue;
17054 :
17055 : /* Check if we are only moving objects owned by certain roles */
17056 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17057 0 : continue;
17058 :
17059 : /*
17060 : * Handle permissions-checking here since we are locking the tables
17061 : * and also to avoid doing a bunch of work only to fail part-way. Note
17062 : * that permissions will also be checked by AlterTableInternal().
17063 : *
17064 : * Caller must be considered an owner on the table to move it.
17065 : */
17066 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17067 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
17068 0 : NameStr(relForm->relname));
17069 :
17070 60 : if (stmt->nowait &&
17071 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
17072 0 : ereport(ERROR,
17073 : (errcode(ERRCODE_OBJECT_IN_USE),
17074 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
17075 : get_namespace_name(relForm->relnamespace),
17076 : NameStr(relForm->relname))));
17077 : else
17078 60 : LockRelationOid(relOid, AccessExclusiveLock);
17079 :
17080 : /* Add to our list of objects to move */
17081 60 : relations = lappend_oid(relations, relOid);
17082 : }
17083 :
17084 30 : table_endscan(scan);
17085 30 : table_close(rel, AccessShareLock);
17086 :
17087 30 : if (relations == NIL)
17088 12 : ereport(NOTICE,
17089 : (errcode(ERRCODE_NO_DATA_FOUND),
17090 : errmsg("no matching relations in tablespace \"%s\" found",
17091 : orig_tablespaceoid == InvalidOid ? "(database default)" :
17092 : get_tablespace_name(orig_tablespaceoid))));
17093 :
17094 : /* Everything is locked, loop through and move all of the relations. */
17095 90 : foreach(l, relations)
17096 : {
17097 60 : List *cmds = NIL;
17098 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
17099 :
17100 60 : cmd->subtype = AT_SetTableSpace;
17101 60 : cmd->name = stmt->new_tablespacename;
17102 :
17103 60 : cmds = lappend(cmds, cmd);
17104 :
17105 60 : EventTriggerAlterTableStart((Node *) stmt);
17106 : /* OID is set by AlterTableInternal */
17107 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
17108 60 : EventTriggerAlterTableEnd();
17109 : }
17110 :
17111 30 : return new_tablespaceoid;
17112 : }
17113 :
17114 : static void
17115 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
17116 : {
17117 : SMgrRelation dstrel;
17118 :
17119 : /*
17120 : * Since we copy the file directly without looking at the shared buffers,
17121 : * we'd better first flush out any pages of the source relation that are
17122 : * in shared buffers. We assume no new changes will be made while we are
17123 : * holding exclusive lock on the rel.
17124 : */
17125 62 : FlushRelationBuffers(rel);
17126 :
17127 : /*
17128 : * Create and copy all forks of the relation, and schedule unlinking of
17129 : * old physical files.
17130 : *
17131 : * NOTE: any conflict in relfilenumber value will be caught in
17132 : * RelationCreateStorage().
17133 : */
17134 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17135 :
17136 : /* copy main fork */
17137 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
17138 62 : rel->rd_rel->relpersistence);
17139 :
17140 : /* copy those extra forks that exist */
17141 62 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17142 248 : forkNum <= MAX_FORKNUM; forkNum++)
17143 : {
17144 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
17145 : {
17146 0 : smgrcreate(dstrel, forkNum, false);
17147 :
17148 : /*
17149 : * WAL log creation if the relation is persistent, or this is the
17150 : * init fork of an unlogged relation.
17151 : */
17152 0 : if (RelationIsPermanent(rel) ||
17153 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17154 : forkNum == INIT_FORKNUM))
17155 0 : log_smgrcreate(&newrlocator, forkNum);
17156 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17157 0 : rel->rd_rel->relpersistence);
17158 : }
17159 : }
17160 :
17161 : /* drop old relation, and close new one */
17162 62 : RelationDropStorage(rel);
17163 62 : smgrclose(dstrel);
17164 62 : }
17165 :
17166 : /*
17167 : * ALTER TABLE ENABLE/DISABLE TRIGGER
17168 : *
17169 : * We just pass this off to trigger.c.
17170 : */
17171 : static void
17172 342 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17173 : char fires_when, bool skip_system, bool recurse,
17174 : LOCKMODE lockmode)
17175 : {
17176 342 : EnableDisableTrigger(rel, trigname, InvalidOid,
17177 : fires_when, skip_system, recurse,
17178 : lockmode);
17179 :
17180 342 : InvokeObjectPostAlterHook(RelationRelationId,
17181 : RelationGetRelid(rel), 0);
17182 342 : }
17183 :
17184 : /*
17185 : * ALTER TABLE ENABLE/DISABLE RULE
17186 : *
17187 : * We just pass this off to rewriteDefine.c.
17188 : */
17189 : static void
17190 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
17191 : char fires_when, LOCKMODE lockmode)
17192 : {
17193 46 : EnableDisableRule(rel, rulename, fires_when);
17194 :
17195 46 : InvokeObjectPostAlterHook(RelationRelationId,
17196 : RelationGetRelid(rel), 0);
17197 46 : }
17198 :
17199 : /*
17200 : * ALTER TABLE INHERIT
17201 : *
17202 : * Add a parent to the child's parents. This verifies that all the columns and
17203 : * check constraints of the parent appear in the child and that they have the
17204 : * same data types and expressions.
17205 : */
17206 : static void
17207 462 : ATPrepAddInherit(Relation child_rel)
17208 : {
17209 462 : if (child_rel->rd_rel->reloftype)
17210 6 : ereport(ERROR,
17211 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17212 : errmsg("cannot change inheritance of typed table")));
17213 :
17214 456 : if (child_rel->rd_rel->relispartition)
17215 6 : ereport(ERROR,
17216 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17217 : errmsg("cannot change inheritance of a partition")));
17218 :
17219 450 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17220 6 : ereport(ERROR,
17221 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17222 : errmsg("cannot change inheritance of partitioned table")));
17223 444 : }
17224 :
17225 : /*
17226 : * Return the address of the new parent relation.
17227 : */
17228 : static ObjectAddress
17229 444 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17230 : {
17231 : Relation parent_rel;
17232 : List *children;
17233 : ObjectAddress address;
17234 : const char *trigger_name;
17235 :
17236 : /*
17237 : * A self-exclusive lock is needed here. See the similar case in
17238 : * MergeAttributes() for a full explanation.
17239 : */
17240 444 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17241 :
17242 : /*
17243 : * Must be owner of both parent and child -- child was checked by
17244 : * ATSimplePermissions call in ATPrepCmd
17245 : */
17246 444 : ATSimplePermissions(AT_AddInherit, parent_rel,
17247 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
17248 :
17249 : /* Permanent rels cannot inherit from temporary ones */
17250 444 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17251 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17252 0 : ereport(ERROR,
17253 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17254 : errmsg("cannot inherit from temporary relation \"%s\"",
17255 : RelationGetRelationName(parent_rel))));
17256 :
17257 : /* If parent rel is temp, it must belong to this session */
17258 444 : if (RELATION_IS_OTHER_TEMP(parent_rel))
17259 0 : ereport(ERROR,
17260 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17261 : errmsg("cannot inherit from temporary relation of another session")));
17262 :
17263 : /* Ditto for the child */
17264 444 : if (RELATION_IS_OTHER_TEMP(child_rel))
17265 0 : ereport(ERROR,
17266 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17267 : errmsg("cannot inherit to temporary relation of another session")));
17268 :
17269 : /* Prevent partitioned tables from becoming inheritance parents */
17270 444 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17271 6 : ereport(ERROR,
17272 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17273 : errmsg("cannot inherit from partitioned table \"%s\"",
17274 : parent->relname)));
17275 :
17276 : /* Likewise for partitions */
17277 438 : if (parent_rel->rd_rel->relispartition)
17278 6 : ereport(ERROR,
17279 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17280 : errmsg("cannot inherit from a partition")));
17281 :
17282 : /*
17283 : * Prevent circularity by seeing if proposed parent inherits from child.
17284 : * (In particular, this disallows making a rel inherit from itself.)
17285 : *
17286 : * This is not completely bulletproof because of race conditions: in
17287 : * multi-level inheritance trees, someone else could concurrently be
17288 : * making another inheritance link that closes the loop but does not join
17289 : * either of the rels we have locked. Preventing that seems to require
17290 : * exclusive locks on the entire inheritance tree, which is a cure worse
17291 : * than the disease. find_all_inheritors() will cope with circularity
17292 : * anyway, so don't sweat it too much.
17293 : *
17294 : * We use weakest lock we can on child's children, namely AccessShareLock.
17295 : */
17296 432 : children = find_all_inheritors(RelationGetRelid(child_rel),
17297 : AccessShareLock, NULL);
17298 :
17299 432 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
17300 12 : ereport(ERROR,
17301 : (errcode(ERRCODE_DUPLICATE_TABLE),
17302 : errmsg("circular inheritance not allowed"),
17303 : errdetail("\"%s\" is already a child of \"%s\".",
17304 : parent->relname,
17305 : RelationGetRelationName(child_rel))));
17306 :
17307 : /*
17308 : * If child_rel has row-level triggers with transition tables, we
17309 : * currently don't allow it to become an inheritance child. See also
17310 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
17311 : */
17312 420 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17313 420 : if (trigger_name != NULL)
17314 6 : ereport(ERROR,
17315 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17316 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17317 : trigger_name, RelationGetRelationName(child_rel)),
17318 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17319 :
17320 : /* OK to create inheritance */
17321 414 : CreateInheritance(child_rel, parent_rel, false);
17322 :
17323 324 : ObjectAddressSet(address, RelationRelationId,
17324 : RelationGetRelid(parent_rel));
17325 :
17326 : /* keep our lock on the parent relation until commit */
17327 324 : table_close(parent_rel, NoLock);
17328 :
17329 324 : return address;
17330 : }
17331 :
17332 : /*
17333 : * CreateInheritance
17334 : * Catalog manipulation portion of creating inheritance between a child
17335 : * table and a parent table.
17336 : *
17337 : * Common to ATExecAddInherit() and ATExecAttachPartition().
17338 : */
17339 : static void
17340 2652 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17341 : {
17342 : Relation catalogRelation;
17343 : SysScanDesc scan;
17344 : ScanKeyData key;
17345 : HeapTuple inheritsTuple;
17346 : int32 inhseqno;
17347 :
17348 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17349 2652 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17350 :
17351 : /*
17352 : * Check for duplicates in the list of parents, and determine the highest
17353 : * inhseqno already present; we'll use the next one for the new parent.
17354 : * Also, if proposed child is a partition, it cannot already be
17355 : * inheriting.
17356 : *
17357 : * Note: we do not reject the case where the child already inherits from
17358 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17359 : */
17360 2652 : ScanKeyInit(&key,
17361 : Anum_pg_inherits_inhrelid,
17362 : BTEqualStrategyNumber, F_OIDEQ,
17363 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17364 2652 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17365 : true, NULL, 1, &key);
17366 :
17367 : /* inhseqno sequences start at 1 */
17368 2652 : inhseqno = 0;
17369 2722 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17370 : {
17371 76 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17372 :
17373 76 : if (inh->inhparent == RelationGetRelid(parent_rel))
17374 6 : ereport(ERROR,
17375 : (errcode(ERRCODE_DUPLICATE_TABLE),
17376 : errmsg("relation \"%s\" would be inherited from more than once",
17377 : RelationGetRelationName(parent_rel))));
17378 :
17379 70 : if (inh->inhseqno > inhseqno)
17380 70 : inhseqno = inh->inhseqno;
17381 : }
17382 2646 : systable_endscan(scan);
17383 :
17384 : /* Match up the columns and bump attinhcount as needed */
17385 2646 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17386 :
17387 : /* Match up the constraints and bump coninhcount as needed */
17388 2514 : MergeConstraintsIntoExisting(child_rel, parent_rel);
17389 :
17390 : /*
17391 : * OK, it looks valid. Make the catalog entries that show inheritance.
17392 : */
17393 2454 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
17394 : RelationGetRelid(parent_rel),
17395 : inhseqno + 1,
17396 : catalogRelation,
17397 2454 : parent_rel->rd_rel->relkind ==
17398 : RELKIND_PARTITIONED_TABLE);
17399 :
17400 : /* Now we're done with pg_inherits */
17401 2454 : table_close(catalogRelation, RowExclusiveLock);
17402 2454 : }
17403 :
17404 : /*
17405 : * Obtain the source-text form of the constraint expression for a check
17406 : * constraint, given its pg_constraint tuple
17407 : */
17408 : static char *
17409 184 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
17410 : {
17411 : Form_pg_constraint con;
17412 : bool isnull;
17413 : Datum attr;
17414 : Datum expr;
17415 :
17416 184 : con = (Form_pg_constraint) GETSTRUCT(contup);
17417 184 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17418 184 : if (isnull)
17419 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
17420 :
17421 184 : expr = DirectFunctionCall2(pg_get_expr, attr,
17422 : ObjectIdGetDatum(con->conrelid));
17423 184 : return TextDatumGetCString(expr);
17424 : }
17425 :
17426 : /*
17427 : * Determine whether two check constraints are functionally equivalent
17428 : *
17429 : * The test we apply is to see whether they reverse-compile to the same
17430 : * source string. This insulates us from issues like whether attributes
17431 : * have the same physical column numbers in parent and child relations.
17432 : *
17433 : * Note that we ignore enforceability as there are cases where constraints
17434 : * with differing enforceability are allowed.
17435 : */
17436 : static bool
17437 92 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
17438 : {
17439 92 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
17440 92 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
17441 :
17442 92 : if (acon->condeferrable != bcon->condeferrable ||
17443 92 : acon->condeferred != bcon->condeferred ||
17444 92 : strcmp(decompile_conbin(a, tupleDesc),
17445 92 : decompile_conbin(b, tupleDesc)) != 0)
17446 6 : return false;
17447 : else
17448 86 : return true;
17449 : }
17450 :
17451 : /*
17452 : * Check columns in child table match up with columns in parent, and increment
17453 : * their attinhcount.
17454 : *
17455 : * Called by CreateInheritance
17456 : *
17457 : * Currently all parent columns must be found in child. Missing columns are an
17458 : * error. One day we might consider creating new columns like CREATE TABLE
17459 : * does. However, that is widely unpopular --- in the common use case of
17460 : * partitioned tables it's a foot-gun.
17461 : *
17462 : * The data type must match exactly. If the parent column is NOT NULL then
17463 : * the child must be as well. Defaults are not compared, however.
17464 : */
17465 : static void
17466 2646 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17467 : {
17468 : Relation attrrel;
17469 : TupleDesc parent_desc;
17470 :
17471 2646 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17472 2646 : parent_desc = RelationGetDescr(parent_rel);
17473 :
17474 8446 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17475 : {
17476 5932 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17477 5932 : char *parent_attname = NameStr(parent_att->attname);
17478 : HeapTuple tuple;
17479 :
17480 : /* Ignore dropped columns in the parent. */
17481 5932 : if (parent_att->attisdropped)
17482 296 : continue;
17483 :
17484 : /* Find same column in child (matching on column name). */
17485 5636 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17486 5636 : if (HeapTupleIsValid(tuple))
17487 : {
17488 5624 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17489 :
17490 5624 : if (parent_att->atttypid != child_att->atttypid ||
17491 5618 : parent_att->atttypmod != child_att->atttypmod)
17492 12 : ereport(ERROR,
17493 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17494 : errmsg("child table \"%s\" has different type for column \"%s\"",
17495 : RelationGetRelationName(child_rel), parent_attname)));
17496 :
17497 5612 : if (parent_att->attcollation != child_att->attcollation)
17498 6 : ereport(ERROR,
17499 : (errcode(ERRCODE_COLLATION_MISMATCH),
17500 : errmsg("child table \"%s\" has different collation for column \"%s\"",
17501 : RelationGetRelationName(child_rel), parent_attname)));
17502 :
17503 : /*
17504 : * If the parent has a not-null constraint that's not NO INHERIT,
17505 : * make sure the child has one too.
17506 : *
17507 : * Other constraints are checked elsewhere.
17508 : */
17509 5606 : if (parent_att->attnotnull && !child_att->attnotnull)
17510 : {
17511 : HeapTuple contup;
17512 :
17513 48 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17514 48 : parent_att->attnum);
17515 48 : if (HeapTupleIsValid(contup) &&
17516 48 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17517 30 : ereport(ERROR,
17518 : errcode(ERRCODE_DATATYPE_MISMATCH),
17519 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17520 : parent_attname, RelationGetRelationName(child_rel)));
17521 : }
17522 :
17523 : /*
17524 : * Child column must be generated if and only if parent column is.
17525 : */
17526 5576 : if (parent_att->attgenerated && !child_att->attgenerated)
17527 36 : ereport(ERROR,
17528 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17529 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17530 5540 : if (child_att->attgenerated && !parent_att->attgenerated)
17531 24 : ereport(ERROR,
17532 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17533 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17534 :
17535 5516 : if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17536 12 : ereport(ERROR,
17537 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17538 : errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17539 : errdetail("Parent column is %s, child column is %s.",
17540 : parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17541 : child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17542 :
17543 : /*
17544 : * Regular inheritance children are independent enough not to
17545 : * inherit identity columns. But partitions are integral part of
17546 : * a partitioned table and inherit identity column.
17547 : */
17548 5504 : if (ispartition)
17549 4778 : child_att->attidentity = parent_att->attidentity;
17550 :
17551 : /*
17552 : * OK, bump the child column's inheritance count. (If we fail
17553 : * later on, this change will just roll back.)
17554 : */
17555 5504 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
17556 : &child_att->attinhcount))
17557 0 : ereport(ERROR,
17558 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17559 : errmsg("too many inheritance parents"));
17560 :
17561 : /*
17562 : * In case of partitions, we must enforce that value of attislocal
17563 : * is same in all partitions. (Note: there are only inherited
17564 : * attributes in partitions)
17565 : */
17566 5504 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17567 : {
17568 : Assert(child_att->attinhcount == 1);
17569 4778 : child_att->attislocal = false;
17570 : }
17571 :
17572 5504 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17573 5504 : heap_freetuple(tuple);
17574 : }
17575 : else
17576 : {
17577 12 : ereport(ERROR,
17578 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17579 : errmsg("child table is missing column \"%s\"", parent_attname)));
17580 : }
17581 : }
17582 :
17583 2514 : table_close(attrrel, RowExclusiveLock);
17584 2514 : }
17585 :
17586 : /*
17587 : * Check constraints in child table match up with constraints in parent,
17588 : * and increment their coninhcount.
17589 : *
17590 : * Constraints that are marked ONLY in the parent are ignored.
17591 : *
17592 : * Called by CreateInheritance
17593 : *
17594 : * Currently all constraints in parent must be present in the child. One day we
17595 : * may consider adding new constraints like CREATE TABLE does.
17596 : *
17597 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
17598 : * constraints. As long as tables have more like 10 constraints it shouldn't be
17599 : * a problem though. Even 100 constraints ought not be the end of the world.
17600 : *
17601 : * XXX See MergeWithExistingConstraint too if you change this code.
17602 : */
17603 : static void
17604 2514 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
17605 : {
17606 : Relation constraintrel;
17607 : SysScanDesc parent_scan;
17608 : ScanKeyData parent_key;
17609 : HeapTuple parent_tuple;
17610 2514 : Oid parent_relid = RelationGetRelid(parent_rel);
17611 : AttrMap *attmap;
17612 :
17613 2514 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17614 :
17615 : /* Outer loop scans through the parent's constraint definitions */
17616 2514 : ScanKeyInit(&parent_key,
17617 : Anum_pg_constraint_conrelid,
17618 : BTEqualStrategyNumber, F_OIDEQ,
17619 : ObjectIdGetDatum(parent_relid));
17620 2514 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17621 : true, NULL, 1, &parent_key);
17622 :
17623 2514 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17624 : RelationGetDescr(child_rel),
17625 : true);
17626 :
17627 4464 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17628 : {
17629 2010 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17630 : SysScanDesc child_scan;
17631 : ScanKeyData child_key;
17632 : HeapTuple child_tuple;
17633 : AttrNumber parent_attno;
17634 2010 : bool found = false;
17635 :
17636 2010 : if (parent_con->contype != CONSTRAINT_CHECK &&
17637 1874 : parent_con->contype != CONSTRAINT_NOTNULL)
17638 994 : continue;
17639 :
17640 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17641 1060 : if (parent_con->connoinherit)
17642 44 : continue;
17643 :
17644 1016 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17645 900 : parent_attno = extractNotNullColumn(parent_tuple);
17646 : else
17647 116 : parent_attno = InvalidAttrNumber;
17648 :
17649 : /* Search for a child constraint matching this one */
17650 1016 : ScanKeyInit(&child_key,
17651 : Anum_pg_constraint_conrelid,
17652 : BTEqualStrategyNumber, F_OIDEQ,
17653 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17654 1016 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17655 : true, NULL, 1, &child_key);
17656 :
17657 1594 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17658 : {
17659 1570 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17660 : HeapTuple child_copy;
17661 :
17662 1570 : if (child_con->contype != parent_con->contype)
17663 280 : continue;
17664 :
17665 : /*
17666 : * CHECK constraint are matched by constraint name, NOT NULL ones
17667 : * by attribute number.
17668 : */
17669 1290 : if (child_con->contype == CONSTRAINT_CHECK)
17670 : {
17671 152 : if (strcmp(NameStr(parent_con->conname),
17672 122 : NameStr(child_con->conname)) != 0)
17673 30 : continue;
17674 : }
17675 1168 : else if (child_con->contype == CONSTRAINT_NOTNULL)
17676 : {
17677 : Form_pg_attribute parent_attr;
17678 : Form_pg_attribute child_attr;
17679 : AttrNumber child_attno;
17680 :
17681 1168 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17682 1168 : child_attno = extractNotNullColumn(child_tuple);
17683 1168 : if (parent_attno != attmap->attnums[child_attno - 1])
17684 268 : continue;
17685 :
17686 900 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17687 : /* there shouldn't be constraints on dropped columns */
17688 900 : if (parent_attr->attisdropped || child_attr->attisdropped)
17689 0 : elog(ERROR, "found not-null constraint on dropped columns");
17690 : }
17691 :
17692 992 : if (child_con->contype == CONSTRAINT_CHECK &&
17693 92 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17694 6 : ereport(ERROR,
17695 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17696 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17697 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17698 :
17699 : /*
17700 : * If the child constraint is "no inherit" then cannot merge
17701 : */
17702 986 : if (child_con->connoinherit)
17703 12 : ereport(ERROR,
17704 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17705 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17706 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17707 :
17708 : /*
17709 : * If the child constraint is "not valid" then cannot merge with a
17710 : * valid parent constraint
17711 : */
17712 974 : if (parent_con->convalidated && child_con->conenforced &&
17713 920 : !child_con->convalidated)
17714 12 : ereport(ERROR,
17715 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17716 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17717 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17718 :
17719 : /*
17720 : * A NOT ENFORCED child constraint cannot be merged with an
17721 : * ENFORCED parent constraint. However, the reverse is allowed,
17722 : * where the child constraint is ENFORCED.
17723 : */
17724 962 : if (parent_con->conenforced && !child_con->conenforced)
17725 6 : ereport(ERROR,
17726 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17727 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17728 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17729 :
17730 : /*
17731 : * OK, bump the child constraint's inheritance count. (If we fail
17732 : * later on, this change will just roll back.)
17733 : */
17734 956 : child_copy = heap_copytuple(child_tuple);
17735 956 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17736 :
17737 956 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
17738 : &child_con->coninhcount))
17739 0 : ereport(ERROR,
17740 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17741 : errmsg("too many inheritance parents"));
17742 :
17743 : /*
17744 : * In case of partitions, an inherited constraint must be
17745 : * inherited only once since it cannot have multiple parents and
17746 : * it is never considered local.
17747 : */
17748 956 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17749 : {
17750 : Assert(child_con->coninhcount == 1);
17751 808 : child_con->conislocal = false;
17752 : }
17753 :
17754 956 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17755 956 : heap_freetuple(child_copy);
17756 :
17757 956 : found = true;
17758 956 : break;
17759 : }
17760 :
17761 980 : systable_endscan(child_scan);
17762 :
17763 980 : if (!found)
17764 : {
17765 24 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17766 0 : ereport(ERROR,
17767 : errcode(ERRCODE_DATATYPE_MISMATCH),
17768 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17769 : get_attname(parent_relid,
17770 : extractNotNullColumn(parent_tuple),
17771 : false),
17772 : RelationGetRelationName(child_rel)));
17773 :
17774 24 : ereport(ERROR,
17775 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17776 : errmsg("child table is missing constraint \"%s\"",
17777 : NameStr(parent_con->conname))));
17778 : }
17779 : }
17780 :
17781 2454 : systable_endscan(parent_scan);
17782 2454 : table_close(constraintrel, RowExclusiveLock);
17783 2454 : }
17784 :
17785 : /*
17786 : * ALTER TABLE NO INHERIT
17787 : *
17788 : * Return value is the address of the relation that is no longer parent.
17789 : */
17790 : static ObjectAddress
17791 94 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
17792 : {
17793 : ObjectAddress address;
17794 : Relation parent_rel;
17795 :
17796 94 : if (rel->rd_rel->relispartition)
17797 0 : ereport(ERROR,
17798 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17799 : errmsg("cannot change inheritance of a partition")));
17800 :
17801 : /*
17802 : * AccessShareLock on the parent is probably enough, seeing that DROP
17803 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
17804 : * be inspecting the parent's schema.
17805 : */
17806 94 : parent_rel = table_openrv(parent, AccessShareLock);
17807 :
17808 : /*
17809 : * We don't bother to check ownership of the parent table --- ownership of
17810 : * the child is presumed enough rights.
17811 : */
17812 :
17813 : /* Off to RemoveInheritance() where most of the work happens */
17814 94 : RemoveInheritance(rel, parent_rel, false);
17815 :
17816 88 : ObjectAddressSet(address, RelationRelationId,
17817 : RelationGetRelid(parent_rel));
17818 :
17819 : /* keep our lock on the parent relation until commit */
17820 88 : table_close(parent_rel, NoLock);
17821 :
17822 88 : return address;
17823 : }
17824 :
17825 : /*
17826 : * MarkInheritDetached
17827 : *
17828 : * Set inhdetachpending for a partition, for ATExecDetachPartition
17829 : * in concurrent mode. While at it, verify that no other partition is
17830 : * already pending detach.
17831 : */
17832 : static void
17833 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
17834 : {
17835 : Relation catalogRelation;
17836 : SysScanDesc scan;
17837 : ScanKeyData key;
17838 : HeapTuple inheritsTuple;
17839 146 : bool found = false;
17840 :
17841 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17842 :
17843 : /*
17844 : * Find pg_inherits entries by inhparent. (We need to scan them all in
17845 : * order to verify that no other partition is pending detach.)
17846 : */
17847 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17848 146 : ScanKeyInit(&key,
17849 : Anum_pg_inherits_inhparent,
17850 : BTEqualStrategyNumber, F_OIDEQ,
17851 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17852 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17853 : true, NULL, 1, &key);
17854 :
17855 576 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17856 : {
17857 : Form_pg_inherits inhForm;
17858 :
17859 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17860 286 : if (inhForm->inhdetachpending)
17861 2 : ereport(ERROR,
17862 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17863 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17864 : get_rel_name(inhForm->inhrelid),
17865 : get_namespace_name(parent_rel->rd_rel->relnamespace),
17866 : RelationGetRelationName(parent_rel)),
17867 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17868 :
17869 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
17870 : {
17871 : HeapTuple newtup;
17872 :
17873 144 : newtup = heap_copytuple(inheritsTuple);
17874 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17875 :
17876 144 : CatalogTupleUpdate(catalogRelation,
17877 : &inheritsTuple->t_self,
17878 : newtup);
17879 144 : found = true;
17880 144 : heap_freetuple(newtup);
17881 : /* keep looking, to ensure we catch others pending detach */
17882 : }
17883 : }
17884 :
17885 : /* Done */
17886 144 : systable_endscan(scan);
17887 144 : table_close(catalogRelation, RowExclusiveLock);
17888 :
17889 144 : if (!found)
17890 0 : ereport(ERROR,
17891 : (errcode(ERRCODE_UNDEFINED_TABLE),
17892 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17893 : RelationGetRelationName(child_rel),
17894 : RelationGetRelationName(parent_rel))));
17895 144 : }
17896 :
17897 : /*
17898 : * RemoveInheritance
17899 : *
17900 : * Drop a parent from the child's parents. This just adjusts the attinhcount
17901 : * and attislocal of the columns and removes the pg_inherit and pg_depend
17902 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17903 : *
17904 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17905 : * up attislocal stays true, which means if a child is ever removed from a
17906 : * parent then its columns will never be automatically dropped which may
17907 : * surprise. But at least we'll never surprise by dropping columns someone
17908 : * isn't expecting to be dropped which would actually mean data loss.
17909 : *
17910 : * coninhcount and conislocal for inherited constraints are adjusted in
17911 : * exactly the same way.
17912 : *
17913 : * Common to ATExecDropInherit() and ATExecDetachPartition().
17914 : */
17915 : static void
17916 606 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17917 : {
17918 : Relation catalogRelation;
17919 : SysScanDesc scan;
17920 : ScanKeyData key[3];
17921 : HeapTuple attributeTuple,
17922 : constraintTuple;
17923 : AttrMap *attmap;
17924 : List *connames;
17925 : List *nncolumns;
17926 : bool found;
17927 : bool is_partitioning;
17928 :
17929 606 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17930 :
17931 606 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17932 : RelationGetRelid(parent_rel),
17933 : expect_detached,
17934 606 : RelationGetRelationName(child_rel));
17935 606 : if (!found)
17936 : {
17937 24 : if (is_partitioning)
17938 18 : ereport(ERROR,
17939 : (errcode(ERRCODE_UNDEFINED_TABLE),
17940 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17941 : RelationGetRelationName(child_rel),
17942 : RelationGetRelationName(parent_rel))));
17943 : else
17944 6 : ereport(ERROR,
17945 : (errcode(ERRCODE_UNDEFINED_TABLE),
17946 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17947 : RelationGetRelationName(parent_rel),
17948 : RelationGetRelationName(child_rel))));
17949 : }
17950 :
17951 : /*
17952 : * Search through child columns looking for ones matching parent rel
17953 : */
17954 582 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17955 582 : ScanKeyInit(&key[0],
17956 : Anum_pg_attribute_attrelid,
17957 : BTEqualStrategyNumber, F_OIDEQ,
17958 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17959 582 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17960 : true, NULL, 1, key);
17961 5198 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17962 : {
17963 4616 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17964 :
17965 : /* Ignore if dropped or not inherited */
17966 4616 : if (att->attisdropped)
17967 6 : continue;
17968 4610 : if (att->attinhcount <= 0)
17969 3522 : continue;
17970 :
17971 1088 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
17972 1088 : NameStr(att->attname)))
17973 : {
17974 : /* Decrement inhcount and possibly set islocal to true */
17975 1034 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
17976 1034 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17977 :
17978 1034 : copy_att->attinhcount--;
17979 1034 : if (copy_att->attinhcount == 0)
17980 1004 : copy_att->attislocal = true;
17981 :
17982 1034 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17983 1034 : heap_freetuple(copyTuple);
17984 : }
17985 : }
17986 582 : systable_endscan(scan);
17987 582 : table_close(catalogRelation, RowExclusiveLock);
17988 :
17989 : /*
17990 : * Likewise, find inherited check and not-null constraints and disinherit
17991 : * them. To do this, we first need a list of the names of the parent's
17992 : * check constraints. (We cheat a bit by only checking for name matches,
17993 : * assuming that the expressions will match.)
17994 : *
17995 : * For NOT NULL columns, we store column numbers to match, mapping them in
17996 : * to the child rel's attribute numbers.
17997 : */
17998 582 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17999 : RelationGetDescr(parent_rel),
18000 : false);
18001 :
18002 582 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
18003 582 : ScanKeyInit(&key[0],
18004 : Anum_pg_constraint_conrelid,
18005 : BTEqualStrategyNumber, F_OIDEQ,
18006 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18007 582 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18008 : true, NULL, 1, key);
18009 :
18010 582 : connames = NIL;
18011 582 : nncolumns = NIL;
18012 :
18013 1236 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18014 : {
18015 654 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18016 :
18017 654 : if (con->connoinherit)
18018 110 : continue;
18019 :
18020 544 : if (con->contype == CONSTRAINT_CHECK)
18021 12 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
18022 544 : if (con->contype == CONSTRAINT_NOTNULL)
18023 : {
18024 208 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
18025 :
18026 208 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18027 : }
18028 : }
18029 :
18030 582 : systable_endscan(scan);
18031 :
18032 : /* Now scan the child's constraints to find matches */
18033 582 : ScanKeyInit(&key[0],
18034 : Anum_pg_constraint_conrelid,
18035 : BTEqualStrategyNumber, F_OIDEQ,
18036 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
18037 582 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18038 : true, NULL, 1, key);
18039 :
18040 1336 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18041 : {
18042 754 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18043 754 : bool match = false;
18044 :
18045 : /*
18046 : * Match CHECK constraints by name, not-null constraints by column
18047 : * number, and ignore all others.
18048 : */
18049 754 : if (con->contype == CONSTRAINT_CHECK)
18050 : {
18051 350 : foreach_ptr(char, chkname, connames)
18052 : {
18053 18 : if (con->contype == CONSTRAINT_CHECK &&
18054 18 : strcmp(NameStr(con->conname), chkname) == 0)
18055 : {
18056 12 : match = true;
18057 12 : connames = foreach_delete_current(connames, chkname);
18058 12 : break;
18059 : }
18060 : }
18061 : }
18062 582 : else if (con->contype == CONSTRAINT_NOTNULL)
18063 : {
18064 268 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18065 :
18066 542 : foreach_int(prevattno, nncolumns)
18067 : {
18068 214 : if (prevattno == child_attno)
18069 : {
18070 208 : match = true;
18071 208 : nncolumns = foreach_delete_current(nncolumns, prevattno);
18072 208 : break;
18073 : }
18074 : }
18075 : }
18076 : else
18077 314 : continue;
18078 :
18079 440 : if (match)
18080 : {
18081 : /* Decrement inhcount and possibly set islocal to true */
18082 220 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
18083 220 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18084 :
18085 220 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
18086 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18087 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
18088 :
18089 220 : copy_con->coninhcount--;
18090 220 : if (copy_con->coninhcount == 0)
18091 202 : copy_con->conislocal = true;
18092 :
18093 220 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18094 220 : heap_freetuple(copyTuple);
18095 : }
18096 : }
18097 :
18098 : /* We should have matched all constraints */
18099 582 : if (connames != NIL || nncolumns != NIL)
18100 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18101 : list_length(connames) + list_length(nncolumns),
18102 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18103 :
18104 582 : systable_endscan(scan);
18105 582 : table_close(catalogRelation, RowExclusiveLock);
18106 :
18107 582 : drop_parent_dependency(RelationGetRelid(child_rel),
18108 : RelationRelationId,
18109 : RelationGetRelid(parent_rel),
18110 : child_dependency_type(is_partitioning));
18111 :
18112 : /*
18113 : * Post alter hook of this inherits. Since object_access_hook doesn't take
18114 : * multiple object identifiers, we relay oid of parent relation using
18115 : * auxiliary_id argument.
18116 : */
18117 582 : InvokeObjectPostAlterHookArg(InheritsRelationId,
18118 : RelationGetRelid(child_rel), 0,
18119 : RelationGetRelid(parent_rel), false);
18120 582 : }
18121 :
18122 : /*
18123 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18124 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18125 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18126 : * be TypeRelationId). There's no convenient way to do this, so go trawling
18127 : * through pg_depend.
18128 : */
18129 : static void
18130 594 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18131 : DependencyType deptype)
18132 : {
18133 : Relation catalogRelation;
18134 : SysScanDesc scan;
18135 : ScanKeyData key[3];
18136 : HeapTuple depTuple;
18137 :
18138 594 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18139 :
18140 594 : ScanKeyInit(&key[0],
18141 : Anum_pg_depend_classid,
18142 : BTEqualStrategyNumber, F_OIDEQ,
18143 : ObjectIdGetDatum(RelationRelationId));
18144 594 : ScanKeyInit(&key[1],
18145 : Anum_pg_depend_objid,
18146 : BTEqualStrategyNumber, F_OIDEQ,
18147 : ObjectIdGetDatum(relid));
18148 594 : ScanKeyInit(&key[2],
18149 : Anum_pg_depend_objsubid,
18150 : BTEqualStrategyNumber, F_INT4EQ,
18151 : Int32GetDatum(0));
18152 :
18153 594 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18154 : NULL, 3, key);
18155 :
18156 1846 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18157 : {
18158 1252 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18159 :
18160 1252 : if (dep->refclassid == refclassid &&
18161 636 : dep->refobjid == refobjid &&
18162 594 : dep->refobjsubid == 0 &&
18163 594 : dep->deptype == deptype)
18164 594 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18165 : }
18166 :
18167 594 : systable_endscan(scan);
18168 594 : table_close(catalogRelation, RowExclusiveLock);
18169 594 : }
18170 :
18171 : /*
18172 : * ALTER TABLE OF
18173 : *
18174 : * Attach a table to a composite type, as though it had been created with CREATE
18175 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18176 : * subject table must not have inheritance parents. These restrictions ensure
18177 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18178 : *
18179 : * The address of the type is returned.
18180 : */
18181 : static ObjectAddress
18182 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18183 : {
18184 66 : Oid relid = RelationGetRelid(rel);
18185 : Type typetuple;
18186 : Form_pg_type typeform;
18187 : Oid typeid;
18188 : Relation inheritsRelation,
18189 : relationRelation;
18190 : SysScanDesc scan;
18191 : ScanKeyData key;
18192 : AttrNumber table_attno,
18193 : type_attno;
18194 : TupleDesc typeTupleDesc,
18195 : tableTupleDesc;
18196 : ObjectAddress tableobj,
18197 : typeobj;
18198 : HeapTuple classtuple;
18199 :
18200 : /* Validate the type. */
18201 66 : typetuple = typenameType(NULL, ofTypename, NULL);
18202 66 : check_of_type(typetuple);
18203 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
18204 66 : typeid = typeform->oid;
18205 :
18206 : /* Fail if the table has any inheritance parents. */
18207 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18208 66 : ScanKeyInit(&key,
18209 : Anum_pg_inherits_inhrelid,
18210 : BTEqualStrategyNumber, F_OIDEQ,
18211 : ObjectIdGetDatum(relid));
18212 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18213 : true, NULL, 1, &key);
18214 66 : if (HeapTupleIsValid(systable_getnext(scan)))
18215 6 : ereport(ERROR,
18216 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18217 : errmsg("typed tables cannot inherit")));
18218 60 : systable_endscan(scan);
18219 60 : table_close(inheritsRelation, AccessShareLock);
18220 :
18221 : /*
18222 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
18223 : * require that the order also match. However, attnotnull need not match.
18224 : */
18225 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18226 60 : tableTupleDesc = RelationGetDescr(rel);
18227 60 : table_attno = 1;
18228 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18229 : {
18230 : Form_pg_attribute type_attr,
18231 : table_attr;
18232 : const char *type_attname,
18233 : *table_attname;
18234 :
18235 : /* Get the next non-dropped type attribute. */
18236 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18237 154 : if (type_attr->attisdropped)
18238 44 : continue;
18239 110 : type_attname = NameStr(type_attr->attname);
18240 :
18241 : /* Get the next non-dropped table attribute. */
18242 : do
18243 : {
18244 122 : if (table_attno > tableTupleDesc->natts)
18245 6 : ereport(ERROR,
18246 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18247 : errmsg("table is missing column \"%s\"",
18248 : type_attname)));
18249 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18250 116 : table_attno++;
18251 116 : } while (table_attr->attisdropped);
18252 104 : table_attname = NameStr(table_attr->attname);
18253 :
18254 : /* Compare name. */
18255 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18256 6 : ereport(ERROR,
18257 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18258 : errmsg("table has column \"%s\" where type requires \"%s\"",
18259 : table_attname, type_attname)));
18260 :
18261 : /* Compare type. */
18262 98 : if (table_attr->atttypid != type_attr->atttypid ||
18263 92 : table_attr->atttypmod != type_attr->atttypmod ||
18264 86 : table_attr->attcollation != type_attr->attcollation)
18265 12 : ereport(ERROR,
18266 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18267 : errmsg("table \"%s\" has different type for column \"%s\"",
18268 : RelationGetRelationName(rel), type_attname)));
18269 : }
18270 36 : ReleaseTupleDesc(typeTupleDesc);
18271 :
18272 : /* Any remaining columns at the end of the table had better be dropped. */
18273 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
18274 : {
18275 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18276 : table_attno - 1);
18277 :
18278 6 : if (!table_attr->attisdropped)
18279 6 : ereport(ERROR,
18280 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18281 : errmsg("table has extra column \"%s\"",
18282 : NameStr(table_attr->attname))));
18283 : }
18284 :
18285 : /* If the table was already typed, drop the existing dependency. */
18286 30 : if (rel->rd_rel->reloftype)
18287 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18288 : DEPENDENCY_NORMAL);
18289 :
18290 : /* Record a dependency on the new type. */
18291 30 : tableobj.classId = RelationRelationId;
18292 30 : tableobj.objectId = relid;
18293 30 : tableobj.objectSubId = 0;
18294 30 : typeobj.classId = TypeRelationId;
18295 30 : typeobj.objectId = typeid;
18296 30 : typeobj.objectSubId = 0;
18297 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18298 :
18299 : /* Update pg_class.reloftype */
18300 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18301 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18302 30 : if (!HeapTupleIsValid(classtuple))
18303 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18304 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18305 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18306 :
18307 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18308 :
18309 30 : heap_freetuple(classtuple);
18310 30 : table_close(relationRelation, RowExclusiveLock);
18311 :
18312 30 : ReleaseSysCache(typetuple);
18313 :
18314 30 : return typeobj;
18315 : }
18316 :
18317 : /*
18318 : * ALTER TABLE NOT OF
18319 : *
18320 : * Detach a typed table from its originating type. Just clear reloftype and
18321 : * remove the dependency.
18322 : */
18323 : static void
18324 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
18325 : {
18326 6 : Oid relid = RelationGetRelid(rel);
18327 : Relation relationRelation;
18328 : HeapTuple tuple;
18329 :
18330 6 : if (!OidIsValid(rel->rd_rel->reloftype))
18331 0 : ereport(ERROR,
18332 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18333 : errmsg("\"%s\" is not a typed table",
18334 : RelationGetRelationName(rel))));
18335 :
18336 : /*
18337 : * We don't bother to check ownership of the type --- ownership of the
18338 : * table is presumed enough rights. No lock required on the type, either.
18339 : */
18340 :
18341 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18342 : DEPENDENCY_NORMAL);
18343 :
18344 : /* Clear pg_class.reloftype */
18345 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18346 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18347 6 : if (!HeapTupleIsValid(tuple))
18348 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18349 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18350 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18351 :
18352 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18353 :
18354 6 : heap_freetuple(tuple);
18355 6 : table_close(relationRelation, RowExclusiveLock);
18356 6 : }
18357 :
18358 : /*
18359 : * relation_mark_replica_identity: Update a table's replica identity
18360 : *
18361 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18362 : * index. Otherwise, it must be InvalidOid.
18363 : *
18364 : * Caller had better hold an exclusive lock on the relation, as the results
18365 : * of running two of these concurrently wouldn't be pretty.
18366 : */
18367 : static void
18368 464 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18369 : bool is_internal)
18370 : {
18371 : Relation pg_index;
18372 : Relation pg_class;
18373 : HeapTuple pg_class_tuple;
18374 : HeapTuple pg_index_tuple;
18375 : Form_pg_class pg_class_form;
18376 : Form_pg_index pg_index_form;
18377 : ListCell *index;
18378 :
18379 : /*
18380 : * Check whether relreplident has changed, and update it if so.
18381 : */
18382 464 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18383 464 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
18384 : ObjectIdGetDatum(RelationGetRelid(rel)));
18385 464 : if (!HeapTupleIsValid(pg_class_tuple))
18386 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
18387 : RelationGetRelationName(rel));
18388 464 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18389 464 : if (pg_class_form->relreplident != ri_type)
18390 : {
18391 414 : pg_class_form->relreplident = ri_type;
18392 414 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18393 : }
18394 464 : table_close(pg_class, RowExclusiveLock);
18395 464 : heap_freetuple(pg_class_tuple);
18396 :
18397 : /*
18398 : * Update the per-index indisreplident flags correctly.
18399 : */
18400 464 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
18401 1188 : foreach(index, RelationGetIndexList(rel))
18402 : {
18403 724 : Oid thisIndexOid = lfirst_oid(index);
18404 724 : bool dirty = false;
18405 :
18406 724 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18407 : ObjectIdGetDatum(thisIndexOid));
18408 724 : if (!HeapTupleIsValid(pg_index_tuple))
18409 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18410 724 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18411 :
18412 724 : if (thisIndexOid == indexOid)
18413 : {
18414 : /* Set the bit if not already set. */
18415 240 : if (!pg_index_form->indisreplident)
18416 : {
18417 222 : dirty = true;
18418 222 : pg_index_form->indisreplident = true;
18419 : }
18420 : }
18421 : else
18422 : {
18423 : /* Unset the bit if set. */
18424 484 : if (pg_index_form->indisreplident)
18425 : {
18426 52 : dirty = true;
18427 52 : pg_index_form->indisreplident = false;
18428 : }
18429 : }
18430 :
18431 724 : if (dirty)
18432 : {
18433 274 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18434 274 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18435 : InvalidOid, is_internal);
18436 :
18437 : /*
18438 : * Invalidate the relcache for the table, so that after we commit
18439 : * all sessions will refresh the table's replica identity index
18440 : * before attempting any UPDATE or DELETE on the table. (If we
18441 : * changed the table's pg_class row above, then a relcache inval
18442 : * is already queued due to that; but we might not have.)
18443 : */
18444 274 : CacheInvalidateRelcache(rel);
18445 : }
18446 724 : heap_freetuple(pg_index_tuple);
18447 : }
18448 :
18449 464 : table_close(pg_index, RowExclusiveLock);
18450 464 : }
18451 :
18452 : /*
18453 : * ALTER TABLE <name> REPLICA IDENTITY ...
18454 : */
18455 : static void
18456 512 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
18457 : {
18458 : Oid indexOid;
18459 : Relation indexRel;
18460 : int key;
18461 :
18462 512 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18463 : {
18464 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18465 6 : return;
18466 : }
18467 506 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18468 : {
18469 170 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18470 170 : return;
18471 : }
18472 336 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18473 : {
18474 48 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18475 48 : return;
18476 : }
18477 288 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18478 : {
18479 : /* fallthrough */ ;
18480 : }
18481 : else
18482 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18483 :
18484 : /* Check that the index exists */
18485 288 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18486 288 : if (!OidIsValid(indexOid))
18487 0 : ereport(ERROR,
18488 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18489 : errmsg("index \"%s\" for table \"%s\" does not exist",
18490 : stmt->name, RelationGetRelationName(rel))));
18491 :
18492 288 : indexRel = index_open(indexOid, ShareLock);
18493 :
18494 : /* Check that the index is on the relation we're altering. */
18495 288 : if (indexRel->rd_index == NULL ||
18496 288 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
18497 6 : ereport(ERROR,
18498 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18499 : errmsg("\"%s\" is not an index for table \"%s\"",
18500 : RelationGetRelationName(indexRel),
18501 : RelationGetRelationName(rel))));
18502 :
18503 : /*
18504 : * The AM must support uniqueness, and the index must in fact be unique.
18505 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18506 : * exclusion), we can use that too.
18507 : */
18508 282 : if ((!indexRel->rd_indam->amcanunique ||
18509 262 : !indexRel->rd_index->indisunique) &&
18510 26 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18511 12 : ereport(ERROR,
18512 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18513 : errmsg("cannot use non-unique index \"%s\" as replica identity",
18514 : RelationGetRelationName(indexRel))));
18515 : /* Deferred indexes are not guaranteed to be always unique. */
18516 270 : if (!indexRel->rd_index->indimmediate)
18517 12 : ereport(ERROR,
18518 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18519 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
18520 : RelationGetRelationName(indexRel))));
18521 : /* Expression indexes aren't supported. */
18522 258 : if (RelationGetIndexExpressions(indexRel) != NIL)
18523 6 : ereport(ERROR,
18524 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18525 : errmsg("cannot use expression index \"%s\" as replica identity",
18526 : RelationGetRelationName(indexRel))));
18527 : /* Predicate indexes aren't supported. */
18528 252 : if (RelationGetIndexPredicate(indexRel) != NIL)
18529 6 : ereport(ERROR,
18530 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18531 : errmsg("cannot use partial index \"%s\" as replica identity",
18532 : RelationGetRelationName(indexRel))));
18533 :
18534 : /* Check index for nullable columns. */
18535 552 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18536 : {
18537 312 : int16 attno = indexRel->rd_index->indkey.values[key];
18538 : Form_pg_attribute attr;
18539 :
18540 : /*
18541 : * Reject any other system columns. (Going forward, we'll disallow
18542 : * indexes containing such columns in the first place, but they might
18543 : * exist in older branches.)
18544 : */
18545 312 : if (attno <= 0)
18546 0 : ereport(ERROR,
18547 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18548 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18549 : RelationGetRelationName(indexRel), attno)));
18550 :
18551 312 : attr = TupleDescAttr(rel->rd_att, attno - 1);
18552 312 : if (!attr->attnotnull)
18553 6 : ereport(ERROR,
18554 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18555 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18556 : RelationGetRelationName(indexRel),
18557 : NameStr(attr->attname))));
18558 : }
18559 :
18560 : /* This index is suitable for use as a replica identity. Mark it. */
18561 240 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18562 :
18563 240 : index_close(indexRel, NoLock);
18564 : }
18565 :
18566 : /*
18567 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18568 : */
18569 : static void
18570 336 : ATExecSetRowSecurity(Relation rel, bool rls)
18571 : {
18572 : Relation pg_class;
18573 : Oid relid;
18574 : HeapTuple tuple;
18575 :
18576 336 : relid = RelationGetRelid(rel);
18577 :
18578 : /* Pull the record for this relation and update it */
18579 336 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18580 :
18581 336 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18582 :
18583 336 : if (!HeapTupleIsValid(tuple))
18584 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18585 :
18586 336 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18587 336 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18588 :
18589 336 : InvokeObjectPostAlterHook(RelationRelationId,
18590 : RelationGetRelid(rel), 0);
18591 :
18592 336 : table_close(pg_class, RowExclusiveLock);
18593 336 : heap_freetuple(tuple);
18594 336 : }
18595 :
18596 : /*
18597 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18598 : */
18599 : static void
18600 132 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
18601 : {
18602 : Relation pg_class;
18603 : Oid relid;
18604 : HeapTuple tuple;
18605 :
18606 132 : relid = RelationGetRelid(rel);
18607 :
18608 132 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18609 :
18610 132 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18611 :
18612 132 : if (!HeapTupleIsValid(tuple))
18613 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18614 :
18615 132 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18616 132 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18617 :
18618 132 : InvokeObjectPostAlterHook(RelationRelationId,
18619 : RelationGetRelid(rel), 0);
18620 :
18621 132 : table_close(pg_class, RowExclusiveLock);
18622 132 : heap_freetuple(tuple);
18623 132 : }
18624 :
18625 : /*
18626 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
18627 : */
18628 : static void
18629 58 : ATExecGenericOptions(Relation rel, List *options)
18630 : {
18631 : Relation ftrel;
18632 : ForeignServer *server;
18633 : ForeignDataWrapper *fdw;
18634 : HeapTuple tuple;
18635 : bool isnull;
18636 : Datum repl_val[Natts_pg_foreign_table];
18637 : bool repl_null[Natts_pg_foreign_table];
18638 : bool repl_repl[Natts_pg_foreign_table];
18639 : Datum datum;
18640 : Form_pg_foreign_table tableform;
18641 :
18642 58 : if (options == NIL)
18643 0 : return;
18644 :
18645 58 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18646 :
18647 58 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18648 : ObjectIdGetDatum(rel->rd_id));
18649 58 : if (!HeapTupleIsValid(tuple))
18650 0 : ereport(ERROR,
18651 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18652 : errmsg("foreign table \"%s\" does not exist",
18653 : RelationGetRelationName(rel))));
18654 58 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18655 58 : server = GetForeignServer(tableform->ftserver);
18656 58 : fdw = GetForeignDataWrapper(server->fdwid);
18657 :
18658 58 : memset(repl_val, 0, sizeof(repl_val));
18659 58 : memset(repl_null, false, sizeof(repl_null));
18660 58 : memset(repl_repl, false, sizeof(repl_repl));
18661 :
18662 : /* Extract the current options */
18663 58 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
18664 : tuple,
18665 : Anum_pg_foreign_table_ftoptions,
18666 : &isnull);
18667 58 : if (isnull)
18668 4 : datum = PointerGetDatum(NULL);
18669 :
18670 : /* Transform the options */
18671 58 : datum = transformGenericOptions(ForeignTableRelationId,
18672 : datum,
18673 : options,
18674 : fdw->fdwvalidator);
18675 :
18676 56 : if (PointerIsValid(DatumGetPointer(datum)))
18677 56 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18678 : else
18679 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18680 :
18681 56 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18682 :
18683 : /* Everything looks good - update the tuple */
18684 :
18685 56 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18686 : repl_val, repl_null, repl_repl);
18687 :
18688 56 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18689 :
18690 : /*
18691 : * Invalidate relcache so that all sessions will refresh any cached plans
18692 : * that might depend on the old options.
18693 : */
18694 56 : CacheInvalidateRelcache(rel);
18695 :
18696 56 : InvokeObjectPostAlterHook(ForeignTableRelationId,
18697 : RelationGetRelid(rel), 0);
18698 :
18699 56 : table_close(ftrel, RowExclusiveLock);
18700 :
18701 56 : heap_freetuple(tuple);
18702 : }
18703 :
18704 : /*
18705 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
18706 : *
18707 : * Return value is the address of the modified column
18708 : */
18709 : static ObjectAddress
18710 78 : ATExecSetCompression(Relation rel,
18711 : const char *column,
18712 : Node *newValue,
18713 : LOCKMODE lockmode)
18714 : {
18715 : Relation attrel;
18716 : HeapTuple tuple;
18717 : Form_pg_attribute atttableform;
18718 : AttrNumber attnum;
18719 : char *compression;
18720 : char cmethod;
18721 : ObjectAddress address;
18722 :
18723 78 : compression = strVal(newValue);
18724 :
18725 78 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
18726 :
18727 : /* copy the cache entry so we can scribble on it below */
18728 78 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18729 78 : if (!HeapTupleIsValid(tuple))
18730 0 : ereport(ERROR,
18731 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18732 : errmsg("column \"%s\" of relation \"%s\" does not exist",
18733 : column, RelationGetRelationName(rel))));
18734 :
18735 : /* prevent them from altering a system attribute */
18736 78 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18737 78 : attnum = atttableform->attnum;
18738 78 : if (attnum <= 0)
18739 0 : ereport(ERROR,
18740 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18741 : errmsg("cannot alter system column \"%s\"", column)));
18742 :
18743 : /*
18744 : * Check that column type is compressible, then get the attribute
18745 : * compression method code
18746 : */
18747 78 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18748 :
18749 : /* update pg_attribute entry */
18750 72 : atttableform->attcompression = cmethod;
18751 72 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18752 :
18753 72 : InvokeObjectPostAlterHook(RelationRelationId,
18754 : RelationGetRelid(rel),
18755 : attnum);
18756 :
18757 : /*
18758 : * Apply the change to indexes as well (only for simple index columns,
18759 : * matching behavior of index.c ConstructTupleDescriptor()).
18760 : */
18761 72 : SetIndexStorageProperties(rel, attrel, attnum,
18762 : false, 0,
18763 : true, cmethod,
18764 : lockmode);
18765 :
18766 72 : heap_freetuple(tuple);
18767 :
18768 72 : table_close(attrel, RowExclusiveLock);
18769 :
18770 : /* make changes visible */
18771 72 : CommandCounterIncrement();
18772 :
18773 72 : ObjectAddressSubSet(address, RelationRelationId,
18774 : RelationGetRelid(rel), attnum);
18775 72 : return address;
18776 : }
18777 :
18778 :
18779 : /*
18780 : * Preparation phase for SET LOGGED/UNLOGGED
18781 : *
18782 : * This verifies that we're not trying to change a temp table. Also,
18783 : * existing foreign key constraints are checked to avoid ending up with
18784 : * permanent tables referencing unlogged tables.
18785 : */
18786 : static void
18787 100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
18788 : {
18789 : Relation pg_constraint;
18790 : HeapTuple tuple;
18791 : SysScanDesc scan;
18792 : ScanKeyData skey[1];
18793 :
18794 : /*
18795 : * Disallow changing status for a temp table. Also verify whether we can
18796 : * get away with doing nothing; in such cases we don't need to run the
18797 : * checks below, either.
18798 : */
18799 100 : switch (rel->rd_rel->relpersistence)
18800 : {
18801 0 : case RELPERSISTENCE_TEMP:
18802 0 : ereport(ERROR,
18803 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18804 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
18805 : RelationGetRelationName(rel)),
18806 : errtable(rel)));
18807 : break;
18808 56 : case RELPERSISTENCE_PERMANENT:
18809 56 : if (toLogged)
18810 : /* nothing to do */
18811 12 : return;
18812 50 : break;
18813 44 : case RELPERSISTENCE_UNLOGGED:
18814 44 : if (!toLogged)
18815 : /* nothing to do */
18816 6 : return;
18817 38 : break;
18818 : }
18819 :
18820 : /*
18821 : * Check that the table is not part of any publication when changing to
18822 : * UNLOGGED, as UNLOGGED tables can't be published.
18823 : */
18824 138 : if (!toLogged &&
18825 50 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
18826 0 : ereport(ERROR,
18827 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18828 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18829 : RelationGetRelationName(rel)),
18830 : errdetail("Unlogged relations cannot be replicated.")));
18831 :
18832 : /*
18833 : * Check existing foreign key constraints to preserve the invariant that
18834 : * permanent tables cannot reference unlogged ones. Self-referencing
18835 : * foreign keys can safely be ignored.
18836 : */
18837 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18838 :
18839 : /*
18840 : * Scan conrelid if changing to permanent, else confrelid. This also
18841 : * determines whether a useful index exists.
18842 : */
18843 88 : ScanKeyInit(&skey[0],
18844 : toLogged ? Anum_pg_constraint_conrelid :
18845 : Anum_pg_constraint_confrelid,
18846 : BTEqualStrategyNumber, F_OIDEQ,
18847 : ObjectIdGetDatum(RelationGetRelid(rel)));
18848 88 : scan = systable_beginscan(pg_constraint,
18849 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18850 : true, NULL, 1, skey);
18851 :
18852 142 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18853 : {
18854 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
18855 :
18856 66 : if (con->contype == CONSTRAINT_FOREIGN)
18857 : {
18858 : Oid foreignrelid;
18859 : Relation foreignrel;
18860 :
18861 : /* the opposite end of what we used as scankey */
18862 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
18863 :
18864 : /* ignore if self-referencing */
18865 30 : if (RelationGetRelid(rel) == foreignrelid)
18866 12 : continue;
18867 :
18868 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
18869 :
18870 18 : if (toLogged)
18871 : {
18872 6 : if (!RelationIsPermanent(foreignrel))
18873 6 : ereport(ERROR,
18874 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18875 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18876 : RelationGetRelationName(rel),
18877 : RelationGetRelationName(foreignrel)),
18878 : errtableconstraint(rel, NameStr(con->conname))));
18879 : }
18880 : else
18881 : {
18882 12 : if (RelationIsPermanent(foreignrel))
18883 6 : ereport(ERROR,
18884 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18885 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18886 : RelationGetRelationName(rel),
18887 : RelationGetRelationName(foreignrel)),
18888 : errtableconstraint(rel, NameStr(con->conname))));
18889 : }
18890 :
18891 6 : relation_close(foreignrel, AccessShareLock);
18892 : }
18893 : }
18894 :
18895 76 : systable_endscan(scan);
18896 :
18897 76 : table_close(pg_constraint, AccessShareLock);
18898 :
18899 : /* force rewrite if necessary; see comment in ATRewriteTables */
18900 76 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
18901 76 : if (toLogged)
18902 32 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18903 : else
18904 44 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18905 76 : tab->chgPersistence = true;
18906 : }
18907 :
18908 : /*
18909 : * Execute ALTER TABLE SET SCHEMA
18910 : */
18911 : ObjectAddress
18912 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
18913 : {
18914 : Relation rel;
18915 : Oid relid;
18916 : Oid oldNspOid;
18917 : Oid nspOid;
18918 : RangeVar *newrv;
18919 : ObjectAddresses *objsMoved;
18920 : ObjectAddress myself;
18921 :
18922 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
18923 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
18924 : RangeVarCallbackForAlterRelation,
18925 : stmt);
18926 :
18927 102 : if (!OidIsValid(relid))
18928 : {
18929 12 : ereport(NOTICE,
18930 : (errmsg("relation \"%s\" does not exist, skipping",
18931 : stmt->relation->relname)));
18932 12 : return InvalidObjectAddress;
18933 : }
18934 :
18935 90 : rel = relation_open(relid, NoLock);
18936 :
18937 90 : oldNspOid = RelationGetNamespace(rel);
18938 :
18939 : /* If it's an owned sequence, disallow moving it by itself. */
18940 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18941 : {
18942 : Oid tableId;
18943 : int32 colId;
18944 :
18945 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18946 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18947 6 : ereport(ERROR,
18948 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18949 : errmsg("cannot move an owned sequence into another schema"),
18950 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
18951 : RelationGetRelationName(rel),
18952 : get_rel_name(tableId))));
18953 : }
18954 :
18955 : /* Get and lock schema OID and check its permissions. */
18956 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18957 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18958 :
18959 : /* common checks on switching namespaces */
18960 84 : CheckSetNamespace(oldNspOid, nspOid);
18961 :
18962 84 : objsMoved = new_object_addresses();
18963 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18964 84 : free_object_addresses(objsMoved);
18965 :
18966 84 : ObjectAddressSet(myself, RelationRelationId, relid);
18967 :
18968 84 : if (oldschema)
18969 84 : *oldschema = oldNspOid;
18970 :
18971 : /* close rel, but keep lock until commit */
18972 84 : relation_close(rel, NoLock);
18973 :
18974 84 : return myself;
18975 : }
18976 :
18977 : /*
18978 : * The guts of relocating a table or materialized view to another namespace:
18979 : * besides moving the relation itself, its dependent objects are relocated to
18980 : * the new schema.
18981 : */
18982 : void
18983 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
18984 : ObjectAddresses *objsMoved)
18985 : {
18986 : Relation classRel;
18987 :
18988 : Assert(objsMoved != NULL);
18989 :
18990 : /* OK, modify the pg_class row and pg_depend entry */
18991 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
18992 :
18993 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18994 : nspOid, true, objsMoved);
18995 :
18996 : /* Fix the table's row type too, if it has one */
18997 86 : if (OidIsValid(rel->rd_rel->reltype))
18998 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18999 : false, /* isImplicitArray */
19000 : false, /* ignoreDependent */
19001 : false, /* errorOnTableType */
19002 : objsMoved);
19003 :
19004 : /* Fix other dependent stuff */
19005 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19006 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19007 : objsMoved, AccessExclusiveLock);
19008 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19009 : false, objsMoved);
19010 :
19011 86 : table_close(classRel, RowExclusiveLock);
19012 86 : }
19013 :
19014 : /*
19015 : * The guts of relocating a relation to another namespace: fix the pg_class
19016 : * entry, and the pg_depend entry if any. Caller must already have
19017 : * opened and write-locked pg_class.
19018 : */
19019 : void
19020 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
19021 : Oid oldNspOid, Oid newNspOid,
19022 : bool hasDependEntry,
19023 : ObjectAddresses *objsMoved)
19024 : {
19025 : HeapTuple classTup;
19026 : Form_pg_class classForm;
19027 : ObjectAddress thisobj;
19028 188 : bool already_done = false;
19029 :
19030 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19031 188 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19032 188 : if (!HeapTupleIsValid(classTup))
19033 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
19034 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
19035 :
19036 : Assert(classForm->relnamespace == oldNspOid);
19037 :
19038 188 : thisobj.classId = RelationRelationId;
19039 188 : thisobj.objectId = relOid;
19040 188 : thisobj.objectSubId = 0;
19041 :
19042 : /*
19043 : * If the object has already been moved, don't move it again. If it's
19044 : * already in the right place, don't move it, but still fire the object
19045 : * access hook.
19046 : */
19047 188 : already_done = object_address_present(&thisobj, objsMoved);
19048 188 : if (!already_done && oldNspOid != newNspOid)
19049 146 : {
19050 146 : ItemPointerData otid = classTup->t_self;
19051 :
19052 : /* check for duplicate name (more friendly than unique-index failure) */
19053 146 : if (get_relname_relid(NameStr(classForm->relname),
19054 : newNspOid) != InvalidOid)
19055 0 : ereport(ERROR,
19056 : (errcode(ERRCODE_DUPLICATE_TABLE),
19057 : errmsg("relation \"%s\" already exists in schema \"%s\"",
19058 : NameStr(classForm->relname),
19059 : get_namespace_name(newNspOid))));
19060 :
19061 : /* classTup is a copy, so OK to scribble on */
19062 146 : classForm->relnamespace = newNspOid;
19063 :
19064 146 : CatalogTupleUpdate(classRel, &otid, classTup);
19065 146 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19066 :
19067 :
19068 : /* Update dependency on schema if caller said so */
19069 250 : if (hasDependEntry &&
19070 104 : changeDependencyFor(RelationRelationId,
19071 : relOid,
19072 : NamespaceRelationId,
19073 : oldNspOid,
19074 : newNspOid) != 1)
19075 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
19076 : NameStr(classForm->relname));
19077 : }
19078 : else
19079 42 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19080 188 : if (!already_done)
19081 : {
19082 188 : add_exact_object_address(&thisobj, objsMoved);
19083 :
19084 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19085 : }
19086 :
19087 188 : heap_freetuple(classTup);
19088 188 : }
19089 :
19090 : /*
19091 : * Move all indexes for the specified relation to another namespace.
19092 : *
19093 : * Note: we assume adequate permission checking was done by the caller,
19094 : * and that the caller has a suitable lock on the owning relation.
19095 : */
19096 : static void
19097 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
19098 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19099 : {
19100 : List *indexList;
19101 : ListCell *l;
19102 :
19103 86 : indexList = RelationGetIndexList(rel);
19104 :
19105 132 : foreach(l, indexList)
19106 : {
19107 46 : Oid indexOid = lfirst_oid(l);
19108 : ObjectAddress thisobj;
19109 :
19110 46 : thisobj.classId = RelationRelationId;
19111 46 : thisobj.objectId = indexOid;
19112 46 : thisobj.objectSubId = 0;
19113 :
19114 : /*
19115 : * Note: currently, the index will not have its own dependency on the
19116 : * namespace, so we don't need to do changeDependencyFor(). There's no
19117 : * row type in pg_type, either.
19118 : *
19119 : * XXX this objsMoved test may be pointless -- surely we have a single
19120 : * dependency link from a relation to each index?
19121 : */
19122 46 : if (!object_address_present(&thisobj, objsMoved))
19123 : {
19124 46 : AlterRelationNamespaceInternal(classRel, indexOid,
19125 : oldNspOid, newNspOid,
19126 : false, objsMoved);
19127 46 : add_exact_object_address(&thisobj, objsMoved);
19128 : }
19129 : }
19130 :
19131 86 : list_free(indexList);
19132 86 : }
19133 :
19134 : /*
19135 : * Move all identity and SERIAL-column sequences of the specified relation to another
19136 : * namespace.
19137 : *
19138 : * Note: we assume adequate permission checking was done by the caller,
19139 : * and that the caller has a suitable lock on the owning relation.
19140 : */
19141 : static void
19142 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
19143 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19144 : LOCKMODE lockmode)
19145 : {
19146 : Relation depRel;
19147 : SysScanDesc scan;
19148 : ScanKeyData key[2];
19149 : HeapTuple tup;
19150 :
19151 : /*
19152 : * SERIAL sequences are those having an auto dependency on one of the
19153 : * table's columns (we don't care *which* column, exactly).
19154 : */
19155 86 : depRel = table_open(DependRelationId, AccessShareLock);
19156 :
19157 86 : ScanKeyInit(&key[0],
19158 : Anum_pg_depend_refclassid,
19159 : BTEqualStrategyNumber, F_OIDEQ,
19160 : ObjectIdGetDatum(RelationRelationId));
19161 86 : ScanKeyInit(&key[1],
19162 : Anum_pg_depend_refobjid,
19163 : BTEqualStrategyNumber, F_OIDEQ,
19164 : ObjectIdGetDatum(RelationGetRelid(rel)));
19165 : /* we leave refobjsubid unspecified */
19166 :
19167 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19168 : NULL, 2, key);
19169 :
19170 616 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
19171 : {
19172 530 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19173 : Relation seqRel;
19174 :
19175 : /* skip dependencies other than auto dependencies on columns */
19176 530 : if (depForm->refobjsubid == 0 ||
19177 382 : depForm->classid != RelationRelationId ||
19178 42 : depForm->objsubid != 0 ||
19179 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19180 488 : continue;
19181 :
19182 : /* Use relation_open just in case it's an index */
19183 42 : seqRel = relation_open(depForm->objid, lockmode);
19184 :
19185 : /* skip non-sequence relations */
19186 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19187 : {
19188 : /* No need to keep the lock */
19189 0 : relation_close(seqRel, lockmode);
19190 0 : continue;
19191 : }
19192 :
19193 : /* Fix the pg_class and pg_depend entries */
19194 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
19195 : oldNspOid, newNspOid,
19196 : true, objsMoved);
19197 :
19198 : /*
19199 : * Sequences used to have entries in pg_type, but no longer do. If we
19200 : * ever re-instate that, we'll need to move the pg_type entry to the
19201 : * new namespace, too (using AlterTypeNamespaceInternal).
19202 : */
19203 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19204 :
19205 : /* Now we can close it. Keep the lock till end of transaction. */
19206 42 : relation_close(seqRel, NoLock);
19207 : }
19208 :
19209 86 : systable_endscan(scan);
19210 :
19211 86 : relation_close(depRel, AccessShareLock);
19212 86 : }
19213 :
19214 :
19215 : /*
19216 : * This code supports
19217 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19218 : *
19219 : * Because we only support this for TEMP tables, it's sufficient to remember
19220 : * the state in a backend-local data structure.
19221 : */
19222 :
19223 : /*
19224 : * Register a newly-created relation's ON COMMIT action.
19225 : */
19226 : void
19227 176 : register_on_commit_action(Oid relid, OnCommitAction action)
19228 : {
19229 : OnCommitItem *oc;
19230 : MemoryContext oldcxt;
19231 :
19232 : /*
19233 : * We needn't bother registering the relation unless there is an ON COMMIT
19234 : * action we need to take.
19235 : */
19236 176 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19237 24 : return;
19238 :
19239 152 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
19240 :
19241 152 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19242 152 : oc->relid = relid;
19243 152 : oc->oncommit = action;
19244 152 : oc->creating_subid = GetCurrentSubTransactionId();
19245 152 : oc->deleting_subid = InvalidSubTransactionId;
19246 :
19247 : /*
19248 : * We use lcons() here so that ON COMMIT actions are processed in reverse
19249 : * order of registration. That might not be essential but it seems
19250 : * reasonable.
19251 : */
19252 152 : on_commits = lcons(oc, on_commits);
19253 :
19254 152 : MemoryContextSwitchTo(oldcxt);
19255 : }
19256 :
19257 : /*
19258 : * Unregister any ON COMMIT action when a relation is deleted.
19259 : *
19260 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19261 : */
19262 : void
19263 48434 : remove_on_commit_action(Oid relid)
19264 : {
19265 : ListCell *l;
19266 :
19267 48580 : foreach(l, on_commits)
19268 : {
19269 286 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19270 :
19271 286 : if (oc->relid == relid)
19272 : {
19273 140 : oc->deleting_subid = GetCurrentSubTransactionId();
19274 140 : break;
19275 : }
19276 : }
19277 48434 : }
19278 :
19279 : /*
19280 : * Perform ON COMMIT actions.
19281 : *
19282 : * This is invoked just before actually committing, since it's possible
19283 : * to encounter errors.
19284 : */
19285 : void
19286 1062436 : PreCommit_on_commit_actions(void)
19287 : {
19288 : ListCell *l;
19289 1062436 : List *oids_to_truncate = NIL;
19290 1062436 : List *oids_to_drop = NIL;
19291 :
19292 1063258 : foreach(l, on_commits)
19293 : {
19294 822 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19295 :
19296 : /* Ignore entry if already dropped in this xact */
19297 822 : if (oc->deleting_subid != InvalidSubTransactionId)
19298 74 : continue;
19299 :
19300 748 : switch (oc->oncommit)
19301 : {
19302 0 : case ONCOMMIT_NOOP:
19303 : case ONCOMMIT_PRESERVE_ROWS:
19304 : /* Do nothing (there shouldn't be such entries, actually) */
19305 0 : break;
19306 694 : case ONCOMMIT_DELETE_ROWS:
19307 :
19308 : /*
19309 : * If this transaction hasn't accessed any temporary
19310 : * relations, we can skip truncating ON COMMIT DELETE ROWS
19311 : * tables, as they must still be empty.
19312 : */
19313 694 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
19314 448 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19315 694 : break;
19316 54 : case ONCOMMIT_DROP:
19317 54 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19318 54 : break;
19319 : }
19320 : }
19321 :
19322 : /*
19323 : * Truncate relations before dropping so that all dependencies between
19324 : * relations are removed after they are worked on. Doing it like this
19325 : * might be a waste as it is possible that a relation being truncated will
19326 : * be dropped anyway due to its parent being dropped, but this makes the
19327 : * code more robust because of not having to re-check that the relation
19328 : * exists at truncation time.
19329 : */
19330 1062436 : if (oids_to_truncate != NIL)
19331 382 : heap_truncate(oids_to_truncate);
19332 :
19333 1062430 : if (oids_to_drop != NIL)
19334 : {
19335 48 : ObjectAddresses *targetObjects = new_object_addresses();
19336 :
19337 102 : foreach(l, oids_to_drop)
19338 : {
19339 : ObjectAddress object;
19340 :
19341 54 : object.classId = RelationRelationId;
19342 54 : object.objectId = lfirst_oid(l);
19343 54 : object.objectSubId = 0;
19344 :
19345 : Assert(!object_address_present(&object, targetObjects));
19346 :
19347 54 : add_exact_object_address(&object, targetObjects);
19348 : }
19349 :
19350 : /*
19351 : * Object deletion might involve toast table access (to clean up
19352 : * toasted catalog entries), so ensure we have a valid snapshot.
19353 : */
19354 48 : PushActiveSnapshot(GetTransactionSnapshot());
19355 :
19356 : /*
19357 : * Since this is an automatic drop, rather than one directly initiated
19358 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19359 : */
19360 48 : performMultipleDeletions(targetObjects, DROP_CASCADE,
19361 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
19362 :
19363 48 : PopActiveSnapshot();
19364 :
19365 : #ifdef USE_ASSERT_CHECKING
19366 :
19367 : /*
19368 : * Note that table deletion will call remove_on_commit_action, so the
19369 : * entry should get marked as deleted.
19370 : */
19371 : foreach(l, on_commits)
19372 : {
19373 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19374 :
19375 : if (oc->oncommit != ONCOMMIT_DROP)
19376 : continue;
19377 :
19378 : Assert(oc->deleting_subid != InvalidSubTransactionId);
19379 : }
19380 : #endif
19381 : }
19382 1062430 : }
19383 :
19384 : /*
19385 : * Post-commit or post-abort cleanup for ON COMMIT management.
19386 : *
19387 : * All we do here is remove no-longer-needed OnCommitItem entries.
19388 : *
19389 : * During commit, remove entries that were deleted during this transaction;
19390 : * during abort, remove those created during this transaction.
19391 : */
19392 : void
19393 1111958 : AtEOXact_on_commit_actions(bool isCommit)
19394 : {
19395 : ListCell *cur_item;
19396 :
19397 1112810 : foreach(cur_item, on_commits)
19398 : {
19399 852 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19400 :
19401 954 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19402 102 : oc->creating_subid != InvalidSubTransactionId)
19403 : {
19404 : /* cur_item must be removed */
19405 152 : on_commits = foreach_delete_current(on_commits, cur_item);
19406 152 : pfree(oc);
19407 : }
19408 : else
19409 : {
19410 : /* cur_item must be preserved */
19411 700 : oc->creating_subid = InvalidSubTransactionId;
19412 700 : oc->deleting_subid = InvalidSubTransactionId;
19413 : }
19414 : }
19415 1111958 : }
19416 :
19417 : /*
19418 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19419 : *
19420 : * During subabort, we can immediately remove entries created during this
19421 : * subtransaction. During subcommit, just relabel entries marked during
19422 : * this subtransaction as being the parent's responsibility.
19423 : */
19424 : void
19425 20036 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
19426 : SubTransactionId parentSubid)
19427 : {
19428 : ListCell *cur_item;
19429 :
19430 20036 : foreach(cur_item, on_commits)
19431 : {
19432 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19433 :
19434 0 : if (!isCommit && oc->creating_subid == mySubid)
19435 : {
19436 : /* cur_item must be removed */
19437 0 : on_commits = foreach_delete_current(on_commits, cur_item);
19438 0 : pfree(oc);
19439 : }
19440 : else
19441 : {
19442 : /* cur_item must be preserved */
19443 0 : if (oc->creating_subid == mySubid)
19444 0 : oc->creating_subid = parentSubid;
19445 0 : if (oc->deleting_subid == mySubid)
19446 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19447 : }
19448 : }
19449 20036 : }
19450 :
19451 : /*
19452 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19453 : * the relation to be locked only if (1) it's a plain or partitioned table,
19454 : * materialized view, or TOAST table and (2) the current user is the owner (or
19455 : * the superuser) or has been granted MAINTAIN. This meets the
19456 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19457 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19458 : */
19459 : void
19460 1012 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
19461 : Oid relId, Oid oldRelId, void *arg)
19462 : {
19463 : char relkind;
19464 : AclResult aclresult;
19465 :
19466 : /* Nothing to do if the relation was not found. */
19467 1012 : if (!OidIsValid(relId))
19468 6 : return;
19469 :
19470 : /*
19471 : * If the relation does exist, check whether it's an index. But note that
19472 : * the relation might have been dropped between the time we did the name
19473 : * lookup and now. In that case, there's nothing to do.
19474 : */
19475 1006 : relkind = get_rel_relkind(relId);
19476 1006 : if (!relkind)
19477 0 : return;
19478 1006 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19479 138 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19480 28 : ereport(ERROR,
19481 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19482 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19483 :
19484 : /* Check permissions */
19485 978 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19486 978 : if (aclresult != ACLCHECK_OK)
19487 30 : aclcheck_error(aclresult,
19488 30 : get_relkind_objtype(get_rel_relkind(relId)),
19489 30 : relation->relname);
19490 : }
19491 :
19492 : /*
19493 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19494 : */
19495 : static void
19496 2122 : RangeVarCallbackForTruncate(const RangeVar *relation,
19497 : Oid relId, Oid oldRelId, void *arg)
19498 : {
19499 : HeapTuple tuple;
19500 :
19501 : /* Nothing to do if the relation was not found. */
19502 2122 : if (!OidIsValid(relId))
19503 0 : return;
19504 :
19505 2122 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19506 2122 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19507 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19508 :
19509 2122 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
19510 2116 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
19511 :
19512 2084 : ReleaseSysCache(tuple);
19513 : }
19514 :
19515 : /*
19516 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19517 : * the owner of the relation, or superuser.
19518 : */
19519 : void
19520 15830 : RangeVarCallbackOwnsRelation(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 15830 : if (!OidIsValid(relId))
19527 6 : return;
19528 :
19529 15824 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19530 15824 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19531 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19532 :
19533 15824 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19534 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
19535 6 : relation->relname);
19536 :
19537 31516 : if (!allowSystemTableMods &&
19538 15698 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19539 2 : ereport(ERROR,
19540 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19541 : errmsg("permission denied: \"%s\" is a system catalog",
19542 : relation->relname)));
19543 :
19544 15816 : ReleaseSysCache(tuple);
19545 : }
19546 :
19547 : /*
19548 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
19549 : * processing.
19550 : */
19551 : static void
19552 33598 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
19553 : void *arg)
19554 : {
19555 33598 : Node *stmt = (Node *) arg;
19556 : ObjectType reltype;
19557 : HeapTuple tuple;
19558 : Form_pg_class classform;
19559 : AclResult aclresult;
19560 : char relkind;
19561 :
19562 33598 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19563 33598 : if (!HeapTupleIsValid(tuple))
19564 216 : return; /* concurrently dropped */
19565 33382 : classform = (Form_pg_class) GETSTRUCT(tuple);
19566 33382 : relkind = classform->relkind;
19567 :
19568 : /* Must own relation. */
19569 33382 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19570 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
19571 :
19572 : /* No system table modifications unless explicitly allowed. */
19573 33322 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
19574 30 : ereport(ERROR,
19575 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19576 : errmsg("permission denied: \"%s\" is a system catalog",
19577 : rv->relname)));
19578 :
19579 : /*
19580 : * Extract the specified relation type from the statement parse tree.
19581 : *
19582 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
19583 : * have CREATE rights on the containing namespace.
19584 : */
19585 33292 : if (IsA(stmt, RenameStmt))
19586 : {
19587 488 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19588 : GetUserId(), ACL_CREATE);
19589 488 : if (aclresult != ACLCHECK_OK)
19590 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
19591 0 : get_namespace_name(classform->relnamespace));
19592 488 : reltype = ((RenameStmt *) stmt)->renameType;
19593 : }
19594 32804 : else if (IsA(stmt, AlterObjectSchemaStmt))
19595 90 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19596 :
19597 32714 : else if (IsA(stmt, AlterTableStmt))
19598 32714 : reltype = ((AlterTableStmt *) stmt)->objtype;
19599 : else
19600 : {
19601 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19602 : reltype = OBJECT_TABLE; /* placate compiler */
19603 : }
19604 :
19605 : /*
19606 : * For compatibility with prior releases, we allow ALTER TABLE to be used
19607 : * with most other types of relations (but not composite types). We allow
19608 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
19609 : * otherwise. Otherwise, the user must select the correct form of the
19610 : * command for the relation at issue.
19611 : */
19612 33292 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19613 0 : ereport(ERROR,
19614 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19615 : errmsg("\"%s\" is not a sequence", rv->relname)));
19616 :
19617 33292 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19618 0 : ereport(ERROR,
19619 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19620 : errmsg("\"%s\" is not a view", rv->relname)));
19621 :
19622 33292 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19623 0 : ereport(ERROR,
19624 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19625 : errmsg("\"%s\" is not a materialized view", rv->relname)));
19626 :
19627 33292 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19628 0 : ereport(ERROR,
19629 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19630 : errmsg("\"%s\" is not a foreign table", rv->relname)));
19631 :
19632 33292 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19633 0 : ereport(ERROR,
19634 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19635 : errmsg("\"%s\" is not a composite type", rv->relname)));
19636 :
19637 33292 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19638 : relkind != RELKIND_PARTITIONED_INDEX
19639 32 : && !IsA(stmt, RenameStmt))
19640 6 : ereport(ERROR,
19641 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19642 : errmsg("\"%s\" is not an index", rv->relname)));
19643 :
19644 : /*
19645 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19646 : * TYPE for that.
19647 : */
19648 33286 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19649 0 : ereport(ERROR,
19650 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19651 : errmsg("\"%s\" is a composite type", rv->relname),
19652 : /* translator: %s is an SQL ALTER command */
19653 : errhint("Use %s instead.",
19654 : "ALTER TYPE")));
19655 :
19656 : /*
19657 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19658 : * to a different schema, such as indexes and TOAST tables.
19659 : */
19660 33286 : if (IsA(stmt, AlterObjectSchemaStmt))
19661 : {
19662 90 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19663 0 : ereport(ERROR,
19664 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19665 : errmsg("cannot change schema of index \"%s\"",
19666 : rv->relname),
19667 : errhint("Change the schema of the table instead.")));
19668 90 : else if (relkind == RELKIND_COMPOSITE_TYPE)
19669 0 : ereport(ERROR,
19670 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19671 : errmsg("cannot change schema of composite type \"%s\"",
19672 : rv->relname),
19673 : /* translator: %s is an SQL ALTER command */
19674 : errhint("Use %s instead.",
19675 : "ALTER TYPE")));
19676 90 : else if (relkind == RELKIND_TOASTVALUE)
19677 0 : ereport(ERROR,
19678 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19679 : errmsg("cannot change schema of TOAST table \"%s\"",
19680 : rv->relname),
19681 : errhint("Change the schema of the table instead.")));
19682 : }
19683 :
19684 33286 : ReleaseSysCache(tuple);
19685 : }
19686 :
19687 : /*
19688 : * Transform any expressions present in the partition key
19689 : *
19690 : * Returns a transformed PartitionSpec.
19691 : */
19692 : static PartitionSpec *
19693 4980 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
19694 : {
19695 : PartitionSpec *newspec;
19696 : ParseState *pstate;
19697 : ParseNamespaceItem *nsitem;
19698 : ListCell *l;
19699 :
19700 4980 : newspec = makeNode(PartitionSpec);
19701 :
19702 4980 : newspec->strategy = partspec->strategy;
19703 4980 : newspec->partParams = NIL;
19704 4980 : newspec->location = partspec->location;
19705 :
19706 : /* Check valid number of columns for strategy */
19707 7478 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19708 2498 : list_length(partspec->partParams) != 1)
19709 6 : ereport(ERROR,
19710 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19711 : errmsg("cannot use \"list\" partition strategy with more than one column")));
19712 :
19713 : /*
19714 : * Create a dummy ParseState and insert the target relation as its sole
19715 : * rangetable entry. We need a ParseState for transformExpr.
19716 : */
19717 4974 : pstate = make_parsestate(NULL);
19718 4974 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19719 : NULL, false, true);
19720 4974 : addNSItemToQuery(pstate, nsitem, true, true, true);
19721 :
19722 : /* take care of any partition expressions */
19723 10374 : foreach(l, partspec->partParams)
19724 : {
19725 5424 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
19726 :
19727 5424 : if (pelem->expr)
19728 : {
19729 : /* Copy, to avoid scribbling on the input */
19730 304 : pelem = copyObject(pelem);
19731 :
19732 : /* Now do parse transformation of the expression */
19733 304 : pelem->expr = transformExpr(pstate, pelem->expr,
19734 : EXPR_KIND_PARTITION_EXPRESSION);
19735 :
19736 : /* we have to fix its collations too */
19737 280 : assign_expr_collations(pstate, pelem->expr);
19738 : }
19739 :
19740 5400 : newspec->partParams = lappend(newspec->partParams, pelem);
19741 : }
19742 :
19743 4950 : return newspec;
19744 : }
19745 :
19746 : /*
19747 : * Compute per-partition-column information from a list of PartitionElems.
19748 : * Expressions in the PartitionElems must be parse-analyzed already.
19749 : */
19750 : static void
19751 4950 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19752 : List **partexprs, Oid *partopclass, Oid *partcollation,
19753 : PartitionStrategy strategy)
19754 : {
19755 : int attn;
19756 : ListCell *lc;
19757 : Oid am_oid;
19758 :
19759 4950 : attn = 0;
19760 10254 : foreach(lc, partParams)
19761 : {
19762 5400 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
19763 : Oid atttype;
19764 : Oid attcollation;
19765 :
19766 5400 : if (pelem->name != NULL)
19767 : {
19768 : /* Simple attribute reference */
19769 : HeapTuple atttuple;
19770 : Form_pg_attribute attform;
19771 :
19772 5120 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
19773 5120 : pelem->name);
19774 5120 : if (!HeapTupleIsValid(atttuple))
19775 12 : ereport(ERROR,
19776 : (errcode(ERRCODE_UNDEFINED_COLUMN),
19777 : errmsg("column \"%s\" named in partition key does not exist",
19778 : pelem->name),
19779 : parser_errposition(pstate, pelem->location)));
19780 5108 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19781 :
19782 5108 : if (attform->attnum <= 0)
19783 6 : ereport(ERROR,
19784 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19785 : errmsg("cannot use system column \"%s\" in partition key",
19786 : pelem->name),
19787 : parser_errposition(pstate, pelem->location)));
19788 :
19789 : /*
19790 : * Stored generated columns cannot work: They are computed after
19791 : * BEFORE triggers, but partition routing is done before all
19792 : * triggers. Maybe virtual generated columns could be made to
19793 : * work, but then they would need to be handled as an expression
19794 : * below.
19795 : */
19796 5102 : if (attform->attgenerated)
19797 12 : ereport(ERROR,
19798 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19799 : errmsg("cannot use generated column in partition key"),
19800 : errdetail("Column \"%s\" is a generated column.",
19801 : pelem->name),
19802 : parser_errposition(pstate, pelem->location)));
19803 :
19804 5090 : partattrs[attn] = attform->attnum;
19805 5090 : atttype = attform->atttypid;
19806 5090 : attcollation = attform->attcollation;
19807 5090 : ReleaseSysCache(atttuple);
19808 : }
19809 : else
19810 : {
19811 : /* Expression */
19812 280 : Node *expr = pelem->expr;
19813 : char partattname[16];
19814 :
19815 : Assert(expr != NULL);
19816 280 : atttype = exprType(expr);
19817 280 : attcollation = exprCollation(expr);
19818 :
19819 : /*
19820 : * The expression must be of a storable type (e.g., not RECORD).
19821 : * The test is the same as for whether a table column is of a safe
19822 : * type (which is why we needn't check for the non-expression
19823 : * case).
19824 : */
19825 280 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19826 280 : CheckAttributeType(partattname,
19827 : atttype, attcollation,
19828 : NIL, CHKATYPE_IS_PARTKEY);
19829 :
19830 : /*
19831 : * Strip any top-level COLLATE clause. This ensures that we treat
19832 : * "x COLLATE y" and "(x COLLATE y)" alike.
19833 : */
19834 268 : while (IsA(expr, CollateExpr))
19835 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
19836 :
19837 268 : if (IsA(expr, Var) &&
19838 12 : ((Var *) expr)->varattno > 0)
19839 : {
19840 : /*
19841 : * User wrote "(column)" or "(column COLLATE something)".
19842 : * Treat it like simple attribute anyway.
19843 : */
19844 6 : partattrs[attn] = ((Var *) expr)->varattno;
19845 : }
19846 : else
19847 : {
19848 262 : Bitmapset *expr_attrs = NULL;
19849 : int i;
19850 :
19851 262 : partattrs[attn] = 0; /* marks the column as expression */
19852 262 : *partexprs = lappend(*partexprs, expr);
19853 :
19854 : /*
19855 : * transformPartitionSpec() should have already rejected
19856 : * subqueries, aggregates, window functions, and SRFs, based
19857 : * on the EXPR_KIND_ for partition expressions.
19858 : */
19859 :
19860 : /*
19861 : * Cannot allow system column references, since that would
19862 : * make partition routing impossible: their values won't be
19863 : * known yet when we need to do that.
19864 : */
19865 262 : pull_varattnos(expr, 1, &expr_attrs);
19866 2096 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
19867 : {
19868 1834 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
19869 : expr_attrs))
19870 0 : ereport(ERROR,
19871 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19872 : errmsg("partition key expressions cannot contain system column references")));
19873 : }
19874 :
19875 : /*
19876 : * Stored generated columns cannot work: They are computed
19877 : * after BEFORE triggers, but partition routing is done before
19878 : * all triggers. Virtual generated columns could probably
19879 : * work, but it would require more work elsewhere (for example
19880 : * SET EXPRESSION would need to check whether the column is
19881 : * used in partition keys). Seems safer to prohibit for now.
19882 : */
19883 262 : i = -1;
19884 570 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
19885 : {
19886 320 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
19887 :
19888 320 : if (attno > 0 &&
19889 314 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19890 12 : ereport(ERROR,
19891 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19892 : errmsg("cannot use generated column in partition key"),
19893 : errdetail("Column \"%s\" is a generated column.",
19894 : get_attname(RelationGetRelid(rel), attno, false)),
19895 : parser_errposition(pstate, pelem->location)));
19896 : }
19897 :
19898 : /*
19899 : * Preprocess the expression before checking for mutability.
19900 : * This is essential for the reasons described in
19901 : * contain_mutable_functions_after_planning. However, we call
19902 : * expression_planner for ourselves rather than using that
19903 : * function, because if constant-folding reduces the
19904 : * expression to a constant, we'd like to know that so we can
19905 : * complain below.
19906 : *
19907 : * Like contain_mutable_functions_after_planning, assume that
19908 : * expression_planner won't scribble on its input, so this
19909 : * won't affect the partexprs entry we saved above.
19910 : */
19911 250 : expr = (Node *) expression_planner((Expr *) expr);
19912 :
19913 : /*
19914 : * Partition expressions cannot contain mutable functions,
19915 : * because a given row must always map to the same partition
19916 : * as long as there is no change in the partition boundary
19917 : * structure.
19918 : */
19919 250 : if (contain_mutable_functions(expr))
19920 6 : ereport(ERROR,
19921 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19922 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
19923 :
19924 : /*
19925 : * While it is not exactly *wrong* for a partition expression
19926 : * to be a constant, it seems better to reject such keys.
19927 : */
19928 244 : if (IsA(expr, Const))
19929 12 : ereport(ERROR,
19930 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19931 : errmsg("cannot use constant expression as partition key")));
19932 : }
19933 : }
19934 :
19935 : /*
19936 : * Apply collation override if any
19937 : */
19938 5328 : if (pelem->collation)
19939 54 : attcollation = get_collation_oid(pelem->collation, false);
19940 :
19941 : /*
19942 : * Check we have a collation iff it's a collatable type. The only
19943 : * expected failures here are (1) COLLATE applied to a noncollatable
19944 : * type, or (2) partition expression had an unresolved collation. But
19945 : * we might as well code this to be a complete consistency check.
19946 : */
19947 5328 : if (type_is_collatable(atttype))
19948 : {
19949 606 : if (!OidIsValid(attcollation))
19950 0 : ereport(ERROR,
19951 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
19952 : errmsg("could not determine which collation to use for partition expression"),
19953 : errhint("Use the COLLATE clause to set the collation explicitly.")));
19954 : }
19955 : else
19956 : {
19957 4722 : if (OidIsValid(attcollation))
19958 0 : ereport(ERROR,
19959 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19960 : errmsg("collations are not supported by type %s",
19961 : format_type_be(atttype))));
19962 : }
19963 :
19964 5328 : partcollation[attn] = attcollation;
19965 :
19966 : /*
19967 : * Identify the appropriate operator class. For list and range
19968 : * partitioning, we use a btree operator class; hash partitioning uses
19969 : * a hash operator class.
19970 : */
19971 5328 : if (strategy == PARTITION_STRATEGY_HASH)
19972 314 : am_oid = HASH_AM_OID;
19973 : else
19974 5014 : am_oid = BTREE_AM_OID;
19975 :
19976 5328 : if (!pelem->opclass)
19977 : {
19978 5190 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19979 :
19980 5190 : if (!OidIsValid(partopclass[attn]))
19981 : {
19982 12 : if (strategy == PARTITION_STRATEGY_HASH)
19983 0 : ereport(ERROR,
19984 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19985 : errmsg("data type %s has no default operator class for access method \"%s\"",
19986 : format_type_be(atttype), "hash"),
19987 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19988 : else
19989 12 : ereport(ERROR,
19990 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19991 : errmsg("data type %s has no default operator class for access method \"%s\"",
19992 : format_type_be(atttype), "btree"),
19993 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19994 : }
19995 : }
19996 : else
19997 138 : partopclass[attn] = ResolveOpClass(pelem->opclass,
19998 : atttype,
19999 : am_oid == HASH_AM_OID ? "hash" : "btree",
20000 : am_oid);
20001 :
20002 5304 : attn++;
20003 : }
20004 4854 : }
20005 :
20006 : /*
20007 : * PartConstraintImpliedByRelConstraint
20008 : * Do scanrel's existing constraints imply the partition constraint?
20009 : *
20010 : * "Existing constraints" include its check constraints and column-level
20011 : * not-null constraints. partConstraint describes the partition constraint,
20012 : * in implicit-AND form.
20013 : */
20014 : bool
20015 3194 : PartConstraintImpliedByRelConstraint(Relation scanrel,
20016 : List *partConstraint)
20017 : {
20018 3194 : List *existConstraint = NIL;
20019 3194 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20020 : int i;
20021 :
20022 3194 : if (constr && constr->has_not_null)
20023 : {
20024 836 : int natts = scanrel->rd_att->natts;
20025 :
20026 2706 : for (i = 1; i <= natts; i++)
20027 : {
20028 1870 : CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20029 :
20030 : /* invalid not-null constraint must be ignored here */
20031 1870 : if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20032 : {
20033 1132 : Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20034 1132 : NullTest *ntest = makeNode(NullTest);
20035 :
20036 1132 : ntest->arg = (Expr *) makeVar(1,
20037 : i,
20038 : wholeatt->atttypid,
20039 : wholeatt->atttypmod,
20040 : wholeatt->attcollation,
20041 : 0);
20042 1132 : ntest->nulltesttype = IS_NOT_NULL;
20043 :
20044 : /*
20045 : * argisrow=false is correct even for a composite column,
20046 : * because attnotnull does not represent a SQL-spec IS NOT
20047 : * NULL test in such a case, just IS DISTINCT FROM NULL.
20048 : */
20049 1132 : ntest->argisrow = false;
20050 1132 : ntest->location = -1;
20051 1132 : existConstraint = lappend(existConstraint, ntest);
20052 : }
20053 : }
20054 : }
20055 :
20056 3194 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20057 : }
20058 :
20059 : /*
20060 : * ConstraintImpliedByRelConstraint
20061 : * Do scanrel's existing constraints imply the given constraint?
20062 : *
20063 : * testConstraint is the constraint to validate. provenConstraint is a
20064 : * caller-provided list of conditions which this function may assume
20065 : * to be true. Both provenConstraint and testConstraint must be in
20066 : * implicit-AND form, must only contain immutable clauses, and must
20067 : * contain only Vars with varno = 1.
20068 : */
20069 : bool
20070 4432 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20071 : {
20072 4432 : List *existConstraint = list_copy(provenConstraint);
20073 4432 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20074 : int num_check,
20075 : i;
20076 :
20077 4432 : num_check = (constr != NULL) ? constr->num_check : 0;
20078 4948 : for (i = 0; i < num_check; i++)
20079 : {
20080 : Node *cexpr;
20081 :
20082 : /*
20083 : * If this constraint hasn't been fully validated yet, we must ignore
20084 : * it here.
20085 : */
20086 516 : if (!constr->check[i].ccvalid)
20087 6 : continue;
20088 :
20089 : /*
20090 : * NOT ENFORCED constraints are always marked as invalid, which should
20091 : * have been ignored.
20092 : */
20093 : Assert(constr->check[i].ccenforced);
20094 :
20095 510 : cexpr = stringToNode(constr->check[i].ccbin);
20096 :
20097 : /*
20098 : * Run each expression through const-simplification and
20099 : * canonicalization. It is necessary, because we will be comparing it
20100 : * to similarly-processed partition constraint expressions, and may
20101 : * fail to detect valid matches without this.
20102 : */
20103 510 : cexpr = eval_const_expressions(NULL, cexpr);
20104 510 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20105 :
20106 510 : existConstraint = list_concat(existConstraint,
20107 510 : make_ands_implicit((Expr *) cexpr));
20108 : }
20109 :
20110 : /*
20111 : * Try to make the proof. Since we are comparing CHECK constraints, we
20112 : * need to use weak implication, i.e., we assume existConstraint is
20113 : * not-false and try to prove the same for testConstraint.
20114 : *
20115 : * Note that predicate_implied_by assumes its first argument is known
20116 : * immutable. That should always be true for both NOT NULL and partition
20117 : * constraints, so we don't test it here.
20118 : */
20119 4432 : return predicate_implied_by(testConstraint, existConstraint, true);
20120 : }
20121 :
20122 : /*
20123 : * QueuePartitionConstraintValidation
20124 : *
20125 : * Add an entry to wqueue to have the given partition constraint validated by
20126 : * Phase 3, for the given relation, and all its children.
20127 : *
20128 : * We first verify whether the given constraint is implied by pre-existing
20129 : * relation constraints; if it is, there's no need to scan the table to
20130 : * validate, so don't queue in that case.
20131 : */
20132 : static void
20133 2564 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
20134 : List *partConstraint,
20135 : bool validate_default)
20136 : {
20137 : /*
20138 : * Based on the table's existing constraints, determine whether or not we
20139 : * may skip scanning the table.
20140 : */
20141 2564 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20142 : {
20143 90 : if (!validate_default)
20144 68 : ereport(DEBUG1,
20145 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20146 : RelationGetRelationName(scanrel))));
20147 : else
20148 22 : ereport(DEBUG1,
20149 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20150 : RelationGetRelationName(scanrel))));
20151 90 : return;
20152 : }
20153 :
20154 : /*
20155 : * Constraints proved insufficient. For plain relations, queue a
20156 : * validation item now; for partitioned tables, recurse to process each
20157 : * partition.
20158 : */
20159 2474 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20160 : {
20161 : AlteredTableInfo *tab;
20162 :
20163 : /* Grab a work queue entry. */
20164 2070 : tab = ATGetQueueEntry(wqueue, scanrel);
20165 : Assert(tab->partition_constraint == NULL);
20166 2070 : tab->partition_constraint = (Expr *) linitial(partConstraint);
20167 2070 : tab->validate_default = validate_default;
20168 : }
20169 404 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20170 : {
20171 354 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20172 : int i;
20173 :
20174 744 : for (i = 0; i < partdesc->nparts; i++)
20175 : {
20176 : Relation part_rel;
20177 : List *thisPartConstraint;
20178 :
20179 : /*
20180 : * This is the minimum lock we need to prevent deadlocks.
20181 : */
20182 390 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20183 :
20184 : /*
20185 : * Adjust the constraint for scanrel so that it matches this
20186 : * partition's attribute numbers.
20187 : */
20188 : thisPartConstraint =
20189 390 : map_partition_varattnos(partConstraint, 1,
20190 : part_rel, scanrel);
20191 :
20192 390 : QueuePartitionConstraintValidation(wqueue, part_rel,
20193 : thisPartConstraint,
20194 : validate_default);
20195 390 : table_close(part_rel, NoLock); /* keep lock till commit */
20196 : }
20197 : }
20198 : }
20199 :
20200 : /*
20201 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20202 : *
20203 : * Return the address of the newly attached partition.
20204 : */
20205 : static ObjectAddress
20206 2394 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
20207 : AlterTableUtilityContext *context)
20208 : {
20209 : Relation attachrel,
20210 : catalog;
20211 : List *attachrel_children;
20212 : List *partConstraint;
20213 : SysScanDesc scan;
20214 : ScanKeyData skey;
20215 : AttrNumber attno;
20216 : int natts;
20217 : TupleDesc tupleDesc;
20218 : ObjectAddress address;
20219 : const char *trigger_name;
20220 : Oid defaultPartOid;
20221 : List *partBoundConstraint;
20222 2394 : ParseState *pstate = make_parsestate(NULL);
20223 :
20224 2394 : pstate->p_sourcetext = context->queryString;
20225 :
20226 : /*
20227 : * We must lock the default partition if one exists, because attaching a
20228 : * new partition will change its partition constraint.
20229 : */
20230 : defaultPartOid =
20231 2394 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20232 2394 : if (OidIsValid(defaultPartOid))
20233 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20234 :
20235 2394 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20236 :
20237 : /*
20238 : * XXX I think it'd be a good idea to grab locks on all tables referenced
20239 : * by FKs at this point also.
20240 : */
20241 :
20242 : /*
20243 : * Must be owner of both parent and source table -- parent was checked by
20244 : * ATSimplePermissions call in ATPrepCmd
20245 : */
20246 2388 : ATSimplePermissions(AT_AttachPartition, attachrel,
20247 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
20248 :
20249 : /* A partition can only have one parent */
20250 2382 : if (attachrel->rd_rel->relispartition)
20251 6 : ereport(ERROR,
20252 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20253 : errmsg("\"%s\" is already a partition",
20254 : RelationGetRelationName(attachrel))));
20255 :
20256 2376 : if (OidIsValid(attachrel->rd_rel->reloftype))
20257 6 : ereport(ERROR,
20258 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20259 : errmsg("cannot attach a typed table as partition")));
20260 :
20261 : /*
20262 : * Table being attached should not already be part of inheritance; either
20263 : * as a child table...
20264 : */
20265 2370 : catalog = table_open(InheritsRelationId, AccessShareLock);
20266 2370 : ScanKeyInit(&skey,
20267 : Anum_pg_inherits_inhrelid,
20268 : BTEqualStrategyNumber, F_OIDEQ,
20269 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20270 2370 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20271 : NULL, 1, &skey);
20272 2370 : if (HeapTupleIsValid(systable_getnext(scan)))
20273 6 : ereport(ERROR,
20274 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20275 : errmsg("cannot attach inheritance child as partition")));
20276 2364 : systable_endscan(scan);
20277 :
20278 : /* ...or as a parent table (except the case when it is partitioned) */
20279 2364 : ScanKeyInit(&skey,
20280 : Anum_pg_inherits_inhparent,
20281 : BTEqualStrategyNumber, F_OIDEQ,
20282 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20283 2364 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20284 : 1, &skey);
20285 2364 : if (HeapTupleIsValid(systable_getnext(scan)) &&
20286 262 : attachrel->rd_rel->relkind == RELKIND_RELATION)
20287 6 : ereport(ERROR,
20288 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20289 : errmsg("cannot attach inheritance parent as partition")));
20290 2358 : systable_endscan(scan);
20291 2358 : table_close(catalog, AccessShareLock);
20292 :
20293 : /*
20294 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
20295 : * particular, this disallows making a rel a partition of itself.)
20296 : *
20297 : * We do that by checking if rel is a member of the list of attachrel's
20298 : * partitions provided the latter is partitioned at all. We want to avoid
20299 : * having to construct this list again, so we request the strongest lock
20300 : * on all partitions. We need the strongest lock, because we may decide
20301 : * to scan them if we find out that the table being attached (or its leaf
20302 : * partitions) may contain rows that violate the partition constraint. If
20303 : * the table has a constraint that would prevent such rows, which by
20304 : * definition is present in all the partitions, we need not scan the
20305 : * table, nor its partitions. But we cannot risk a deadlock by taking a
20306 : * weaker lock now and the stronger one only when needed.
20307 : */
20308 2358 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20309 : AccessExclusiveLock, NULL);
20310 2358 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20311 12 : ereport(ERROR,
20312 : (errcode(ERRCODE_DUPLICATE_TABLE),
20313 : errmsg("circular inheritance not allowed"),
20314 : errdetail("\"%s\" is already a child of \"%s\".",
20315 : RelationGetRelationName(rel),
20316 : RelationGetRelationName(attachrel))));
20317 :
20318 : /* If the parent is permanent, so must be all of its partitions. */
20319 2346 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20320 2304 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20321 6 : ereport(ERROR,
20322 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20323 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20324 : RelationGetRelationName(rel))));
20325 :
20326 : /* Temp parent cannot have a partition that is itself not a temp */
20327 2340 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20328 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20329 18 : ereport(ERROR,
20330 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20331 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20332 : RelationGetRelationName(rel))));
20333 :
20334 : /* If the parent is temp, it must belong to this session */
20335 2322 : if (RELATION_IS_OTHER_TEMP(rel))
20336 0 : ereport(ERROR,
20337 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20338 : errmsg("cannot attach as partition of temporary relation of another session")));
20339 :
20340 : /* Ditto for the partition */
20341 2322 : if (RELATION_IS_OTHER_TEMP(attachrel))
20342 0 : ereport(ERROR,
20343 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20344 : errmsg("cannot attach temporary relation of another session as partition")));
20345 :
20346 : /*
20347 : * Check if attachrel has any identity columns or any columns that aren't
20348 : * in the parent.
20349 : */
20350 2322 : tupleDesc = RelationGetDescr(attachrel);
20351 2322 : natts = tupleDesc->natts;
20352 7926 : for (attno = 1; attno <= natts; attno++)
20353 : {
20354 5646 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20355 5646 : char *attributeName = NameStr(attribute->attname);
20356 :
20357 : /* Ignore dropped */
20358 5646 : if (attribute->attisdropped)
20359 580 : continue;
20360 :
20361 5066 : if (attribute->attidentity)
20362 24 : ereport(ERROR,
20363 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20364 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20365 : RelationGetRelationName(attachrel), attributeName),
20366 : errdetail("The new partition may not contain an identity column."));
20367 :
20368 : /* Try to find the column in parent (matching on column name) */
20369 5042 : if (!SearchSysCacheExists2(ATTNAME,
20370 : ObjectIdGetDatum(RelationGetRelid(rel)),
20371 : CStringGetDatum(attributeName)))
20372 18 : ereport(ERROR,
20373 : (errcode(ERRCODE_DATATYPE_MISMATCH),
20374 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20375 : RelationGetRelationName(attachrel), attributeName,
20376 : RelationGetRelationName(rel)),
20377 : errdetail("The new partition may contain only the columns present in parent.")));
20378 : }
20379 :
20380 : /*
20381 : * If child_rel has row-level triggers with transition tables, we
20382 : * currently don't allow it to become a partition. See also prohibitions
20383 : * in ATExecAddInherit() and CreateTrigger().
20384 : */
20385 2280 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20386 2280 : if (trigger_name != NULL)
20387 6 : ereport(ERROR,
20388 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20389 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20390 : trigger_name, RelationGetRelationName(attachrel)),
20391 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
20392 :
20393 : /*
20394 : * Check that the new partition's bound is valid and does not overlap any
20395 : * of existing partitions of the parent - note that it does not return on
20396 : * error.
20397 : */
20398 2274 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
20399 : cmd->bound, pstate);
20400 :
20401 : /* OK to create inheritance. Rest of the checks performed there */
20402 2238 : CreateInheritance(attachrel, rel, true);
20403 :
20404 : /* Update the pg_class entry. */
20405 2130 : StorePartitionBound(attachrel, rel, cmd->bound);
20406 :
20407 : /* Ensure there exists a correct set of indexes in the partition. */
20408 2130 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20409 :
20410 : /* and triggers */
20411 2100 : CloneRowTriggersToPartition(rel, attachrel);
20412 :
20413 : /*
20414 : * Clone foreign key constraints. Callee is responsible for setting up
20415 : * for phase 3 constraint verification.
20416 : */
20417 2094 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
20418 :
20419 : /*
20420 : * Generate partition constraint from the partition bound specification.
20421 : * If the parent itself is a partition, make sure to include its
20422 : * constraint as well.
20423 : */
20424 2076 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20425 :
20426 : /*
20427 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20428 : * since it's needed later to construct the constraint expression for
20429 : * validating against the default partition, if any.
20430 : */
20431 2076 : partConstraint = list_concat_copy(partBoundConstraint,
20432 2076 : RelationGetPartitionQual(rel));
20433 :
20434 : /* Skip validation if there are no constraints to validate. */
20435 2076 : if (partConstraint)
20436 : {
20437 : /*
20438 : * Run the partition quals through const-simplification similar to
20439 : * check constraints. We skip canonicalize_qual, though, because
20440 : * partition quals should be in canonical form already.
20441 : */
20442 : partConstraint =
20443 2028 : (List *) eval_const_expressions(NULL,
20444 : (Node *) partConstraint);
20445 :
20446 : /* XXX this sure looks wrong */
20447 2028 : partConstraint = list_make1(make_ands_explicit(partConstraint));
20448 :
20449 : /*
20450 : * Adjust the generated constraint to match this partition's attribute
20451 : * numbers.
20452 : */
20453 2028 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20454 : rel);
20455 :
20456 : /* Validate partition constraints against the table being attached. */
20457 2028 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20458 : false);
20459 : }
20460 :
20461 : /*
20462 : * If we're attaching a partition other than the default partition and a
20463 : * default one exists, then that partition's partition constraint changes,
20464 : * so add an entry to the work queue to validate it, too. (We must not do
20465 : * this when the partition being attached is the default one; we already
20466 : * did it above!)
20467 : */
20468 2076 : if (OidIsValid(defaultPartOid))
20469 : {
20470 : Relation defaultrel;
20471 : List *defPartConstraint;
20472 :
20473 : Assert(!cmd->bound->is_default);
20474 :
20475 : /* we already hold a lock on the default partition */
20476 146 : defaultrel = table_open(defaultPartOid, NoLock);
20477 : defPartConstraint =
20478 146 : get_proposed_default_constraint(partBoundConstraint);
20479 :
20480 : /*
20481 : * Map the Vars in the constraint expression from rel's attnos to
20482 : * defaultrel's.
20483 : */
20484 : defPartConstraint =
20485 146 : map_partition_varattnos(defPartConstraint,
20486 : 1, defaultrel, rel);
20487 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
20488 : defPartConstraint, true);
20489 :
20490 : /* keep our lock until commit. */
20491 146 : table_close(defaultrel, NoLock);
20492 : }
20493 :
20494 2076 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20495 :
20496 : /*
20497 : * If the partition we just attached is partitioned itself, invalidate
20498 : * relcache for all descendent partitions too to ensure that their
20499 : * rd_partcheck expression trees are rebuilt; partitions already locked at
20500 : * the beginning of this function.
20501 : */
20502 2076 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20503 : {
20504 : ListCell *l;
20505 :
20506 1008 : foreach(l, attachrel_children)
20507 : {
20508 678 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
20509 : }
20510 : }
20511 :
20512 : /* keep our lock until commit */
20513 2076 : table_close(attachrel, NoLock);
20514 :
20515 2076 : return address;
20516 : }
20517 :
20518 : /*
20519 : * AttachPartitionEnsureIndexes
20520 : * subroutine for ATExecAttachPartition to create/match indexes
20521 : *
20522 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20523 : * PARTITION: every partition must have an index attached to each index on the
20524 : * partitioned table.
20525 : */
20526 : static void
20527 2130 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
20528 : {
20529 : List *idxes;
20530 : List *attachRelIdxs;
20531 : Relation *attachrelIdxRels;
20532 : IndexInfo **attachInfos;
20533 : ListCell *cell;
20534 : MemoryContext cxt;
20535 : MemoryContext oldcxt;
20536 :
20537 2130 : cxt = AllocSetContextCreate(CurrentMemoryContext,
20538 : "AttachPartitionEnsureIndexes",
20539 : ALLOCSET_DEFAULT_SIZES);
20540 2130 : oldcxt = MemoryContextSwitchTo(cxt);
20541 :
20542 2130 : idxes = RelationGetIndexList(rel);
20543 2130 : attachRelIdxs = RelationGetIndexList(attachrel);
20544 2130 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20545 2130 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20546 :
20547 : /* Build arrays of all existing indexes and their IndexInfos */
20548 4642 : foreach_oid(cldIdxId, attachRelIdxs)
20549 : {
20550 382 : int i = foreach_current_index(cldIdxId);
20551 :
20552 382 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20553 382 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20554 : }
20555 :
20556 : /*
20557 : * If we're attaching a foreign table, we must fail if any of the indexes
20558 : * is a constraint index; otherwise, there's nothing to do here. Do this
20559 : * before starting work, to avoid wasting the effort of building a few
20560 : * non-unique indexes before coming across a unique one.
20561 : */
20562 2130 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20563 : {
20564 88 : foreach(cell, idxes)
20565 : {
20566 36 : Oid idx = lfirst_oid(cell);
20567 36 : Relation idxRel = index_open(idx, AccessShareLock);
20568 :
20569 36 : if (idxRel->rd_index->indisunique ||
20570 24 : idxRel->rd_index->indisprimary)
20571 12 : ereport(ERROR,
20572 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20573 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20574 : RelationGetRelationName(attachrel),
20575 : RelationGetRelationName(rel)),
20576 : errdetail("Partitioned table \"%s\" contains unique indexes.",
20577 : RelationGetRelationName(rel))));
20578 24 : index_close(idxRel, AccessShareLock);
20579 : }
20580 :
20581 52 : goto out;
20582 : }
20583 :
20584 : /*
20585 : * For each index on the partitioned table, find a matching one in the
20586 : * partition-to-be; if one is not found, create one.
20587 : */
20588 2498 : foreach(cell, idxes)
20589 : {
20590 450 : Oid idx = lfirst_oid(cell);
20591 450 : Relation idxRel = index_open(idx, AccessShareLock);
20592 : IndexInfo *info;
20593 : AttrMap *attmap;
20594 450 : bool found = false;
20595 : Oid constraintOid;
20596 :
20597 : /*
20598 : * Ignore indexes in the partitioned table other than partitioned
20599 : * indexes.
20600 : */
20601 450 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20602 : {
20603 0 : index_close(idxRel, AccessShareLock);
20604 0 : continue;
20605 : }
20606 :
20607 : /* construct an indexinfo to compare existing indexes against */
20608 450 : info = BuildIndexInfo(idxRel);
20609 450 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20610 : RelationGetDescr(rel),
20611 : false);
20612 450 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
20613 :
20614 : /*
20615 : * Scan the list of existing indexes in the partition-to-be, and mark
20616 : * the first matching, valid, unattached one we find, if any, as
20617 : * partition of the parent index. If we find one, we're done.
20618 : */
20619 510 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20620 : {
20621 274 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20622 274 : Oid cldConstrOid = InvalidOid;
20623 :
20624 : /* does this index have a parent? if so, can't use it */
20625 274 : if (attachrelIdxRels[i]->rd_rel->relispartition)
20626 12 : continue;
20627 :
20628 : /* If this index is invalid, can't use it */
20629 262 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
20630 6 : continue;
20631 :
20632 256 : if (CompareIndexInfo(attachInfos[i], info,
20633 256 : attachrelIdxRels[i]->rd_indcollation,
20634 256 : idxRel->rd_indcollation,
20635 256 : attachrelIdxRels[i]->rd_opfamily,
20636 256 : idxRel->rd_opfamily,
20637 : attmap))
20638 : {
20639 : /*
20640 : * If this index is being created in the parent because of a
20641 : * constraint, then the child needs to have a constraint also,
20642 : * so look for one. If there is no such constraint, this
20643 : * index is no good, so keep looking.
20644 : */
20645 220 : if (OidIsValid(constraintOid))
20646 : {
20647 : cldConstrOid =
20648 122 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
20649 : cldIdxId);
20650 : /* no dice */
20651 122 : if (!OidIsValid(cldConstrOid))
20652 6 : continue;
20653 :
20654 : /* Ensure they're both the same type of constraint */
20655 232 : if (get_constraint_type(constraintOid) !=
20656 116 : get_constraint_type(cldConstrOid))
20657 0 : continue;
20658 : }
20659 :
20660 : /* bingo. */
20661 214 : IndexSetParentIndex(attachrelIdxRels[i], idx);
20662 214 : if (OidIsValid(constraintOid))
20663 116 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20664 : RelationGetRelid(attachrel));
20665 214 : found = true;
20666 :
20667 214 : CommandCounterIncrement();
20668 214 : break;
20669 : }
20670 : }
20671 :
20672 : /*
20673 : * If no suitable index was found in the partition-to-be, create one
20674 : * now. Note that if this is a PK, not-null constraints must already
20675 : * exist.
20676 : */
20677 450 : if (!found)
20678 : {
20679 : IndexStmt *stmt;
20680 : Oid conOid;
20681 :
20682 236 : stmt = generateClonedIndexStmt(NULL,
20683 : idxRel, attmap,
20684 : &conOid);
20685 236 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
20686 : RelationGetRelid(idxRel),
20687 : conOid,
20688 : -1,
20689 : true, false, false, false, false);
20690 : }
20691 :
20692 432 : index_close(idxRel, AccessShareLock);
20693 : }
20694 :
20695 2100 : out:
20696 : /* Clean up. */
20697 2470 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20698 370 : index_close(attachrelIdxRels[i], AccessShareLock);
20699 2100 : MemoryContextSwitchTo(oldcxt);
20700 2100 : MemoryContextDelete(cxt);
20701 2100 : }
20702 :
20703 : /*
20704 : * CloneRowTriggersToPartition
20705 : * subroutine for ATExecAttachPartition/DefineRelation to create row
20706 : * triggers on partitions
20707 : */
20708 : static void
20709 2532 : CloneRowTriggersToPartition(Relation parent, Relation partition)
20710 : {
20711 : Relation pg_trigger;
20712 : ScanKeyData key;
20713 : SysScanDesc scan;
20714 : HeapTuple tuple;
20715 : MemoryContext perTupCxt;
20716 :
20717 2532 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20718 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20719 2532 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20720 2532 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20721 : true, NULL, 1, &key);
20722 :
20723 2532 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
20724 : "clone trig", ALLOCSET_SMALL_SIZES);
20725 :
20726 4370 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20727 : {
20728 1844 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20729 : CreateTrigStmt *trigStmt;
20730 1844 : Node *qual = NULL;
20731 : Datum value;
20732 : bool isnull;
20733 1844 : List *cols = NIL;
20734 1844 : List *trigargs = NIL;
20735 : MemoryContext oldcxt;
20736 :
20737 : /*
20738 : * Ignore statement-level triggers; those are not cloned.
20739 : */
20740 1844 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20741 1688 : continue;
20742 :
20743 : /*
20744 : * Don't clone internal triggers, because the constraint cloning code
20745 : * will.
20746 : */
20747 1826 : if (trigForm->tgisinternal)
20748 1670 : continue;
20749 :
20750 : /*
20751 : * Complain if we find an unexpected trigger type.
20752 : */
20753 156 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20754 138 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
20755 0 : elog(ERROR, "unexpected trigger \"%s\" found",
20756 : NameStr(trigForm->tgname));
20757 :
20758 : /* Use short-lived context for CREATE TRIGGER */
20759 156 : oldcxt = MemoryContextSwitchTo(perTupCxt);
20760 :
20761 : /*
20762 : * If there is a WHEN clause, generate a 'cooked' version of it that's
20763 : * appropriate for the partition.
20764 : */
20765 156 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20766 : RelationGetDescr(pg_trigger), &isnull);
20767 156 : if (!isnull)
20768 : {
20769 6 : qual = stringToNode(TextDatumGetCString(value));
20770 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20771 : partition, parent);
20772 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20773 : partition, parent);
20774 : }
20775 :
20776 : /*
20777 : * If there is a column list, transform it to a list of column names.
20778 : * Note we don't need to map this list in any way ...
20779 : */
20780 156 : if (trigForm->tgattr.dim1 > 0)
20781 : {
20782 : int i;
20783 :
20784 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
20785 : {
20786 : Form_pg_attribute col;
20787 :
20788 6 : col = TupleDescAttr(parent->rd_att,
20789 6 : trigForm->tgattr.values[i] - 1);
20790 6 : cols = lappend(cols,
20791 6 : makeString(pstrdup(NameStr(col->attname))));
20792 : }
20793 : }
20794 :
20795 : /* Reconstruct trigger arguments list. */
20796 156 : if (trigForm->tgnargs > 0)
20797 : {
20798 : char *p;
20799 :
20800 12 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20801 : RelationGetDescr(pg_trigger), &isnull);
20802 12 : if (isnull)
20803 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20804 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
20805 :
20806 12 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20807 :
20808 36 : for (int i = 0; i < trigForm->tgnargs; i++)
20809 : {
20810 24 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
20811 24 : p += strlen(p) + 1;
20812 : }
20813 : }
20814 :
20815 156 : trigStmt = makeNode(CreateTrigStmt);
20816 156 : trigStmt->replace = false;
20817 156 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20818 156 : trigStmt->trigname = NameStr(trigForm->tgname);
20819 156 : trigStmt->relation = NULL;
20820 156 : trigStmt->funcname = NULL; /* passed separately */
20821 156 : trigStmt->args = trigargs;
20822 156 : trigStmt->row = true;
20823 156 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20824 156 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20825 156 : trigStmt->columns = cols;
20826 156 : trigStmt->whenClause = NULL; /* passed separately */
20827 156 : trigStmt->transitionRels = NIL; /* not supported at present */
20828 156 : trigStmt->deferrable = trigForm->tgdeferrable;
20829 156 : trigStmt->initdeferred = trigForm->tginitdeferred;
20830 156 : trigStmt->constrrel = NULL; /* passed separately */
20831 :
20832 156 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20833 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20834 : trigForm->tgfoid, trigForm->oid, qual,
20835 156 : false, true, trigForm->tgenabled);
20836 :
20837 150 : MemoryContextSwitchTo(oldcxt);
20838 150 : MemoryContextReset(perTupCxt);
20839 : }
20840 :
20841 2526 : MemoryContextDelete(perTupCxt);
20842 :
20843 2526 : systable_endscan(scan);
20844 2526 : table_close(pg_trigger, RowExclusiveLock);
20845 2526 : }
20846 :
20847 : /*
20848 : * ALTER TABLE DETACH PARTITION
20849 : *
20850 : * Return the address of the relation that is no longer a partition of rel.
20851 : *
20852 : * If concurrent mode is requested, we run in two transactions. A side-
20853 : * effect is that this command cannot run in a multi-part ALTER TABLE.
20854 : * Currently, that's enforced by the grammar.
20855 : *
20856 : * The strategy for concurrency is to first modify the partition's
20857 : * pg_inherit catalog row to make it visible to everyone that the
20858 : * partition is detached, lock the partition against writes, and commit
20859 : * the transaction; anyone who requests the partition descriptor from
20860 : * that point onwards has to ignore such a partition. In a second
20861 : * transaction, we wait until all transactions that could have seen the
20862 : * partition as attached are gone, then we remove the rest of partition
20863 : * metadata (pg_inherits and pg_class.relpartbounds).
20864 : */
20865 : static ObjectAddress
20866 578 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
20867 : RangeVar *name, bool concurrent)
20868 : {
20869 : Relation partRel;
20870 : ObjectAddress address;
20871 : Oid defaultPartOid;
20872 :
20873 : /*
20874 : * We must lock the default partition, because detaching this partition
20875 : * will change its partition constraint.
20876 : */
20877 : defaultPartOid =
20878 578 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20879 578 : if (OidIsValid(defaultPartOid))
20880 : {
20881 : /*
20882 : * Concurrent detaching when a default partition exists is not
20883 : * supported. The main problem is that the default partition
20884 : * constraint would change. And there's a definitional problem: what
20885 : * should happen to the tuples that are being inserted that belong to
20886 : * the partition being detached? Putting them on the partition being
20887 : * detached would be wrong, since they'd become "lost" after the
20888 : * detaching completes but we cannot put them in the default partition
20889 : * either until we alter its partition constraint.
20890 : *
20891 : * I think we could solve this problem if we effected the constraint
20892 : * change before committing the first transaction. But the lock would
20893 : * have to remain AEL and it would cause concurrent query planning to
20894 : * be blocked, so changing it that way would be even worse.
20895 : */
20896 106 : if (concurrent)
20897 12 : ereport(ERROR,
20898 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20899 : errmsg("cannot detach partitions concurrently when a default partition exists")));
20900 94 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20901 : }
20902 :
20903 : /*
20904 : * In concurrent mode, the partition is locked with share-update-exclusive
20905 : * in the first transaction. This allows concurrent transactions to be
20906 : * doing DML to the partition.
20907 : */
20908 566 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20909 : AccessExclusiveLock);
20910 :
20911 : /*
20912 : * Check inheritance conditions and either delete the pg_inherits row (in
20913 : * non-concurrent mode) or just set the inhdetachpending flag.
20914 : */
20915 554 : if (!concurrent)
20916 408 : RemoveInheritance(partRel, rel, false);
20917 : else
20918 146 : MarkInheritDetached(partRel, rel);
20919 :
20920 : /*
20921 : * Ensure that foreign keys still hold after this detach. This keeps
20922 : * locks on the referencing tables, which prevents concurrent transactions
20923 : * from adding rows that we wouldn't see. For this to work in concurrent
20924 : * mode, it is critical that the partition appears as no longer attached
20925 : * for the RI queries as soon as the first transaction commits.
20926 : */
20927 534 : ATDetachCheckNoForeignKeyRefs(partRel);
20928 :
20929 : /*
20930 : * Concurrent mode has to work harder; first we add a new constraint to
20931 : * the partition that matches the partition constraint. Then we close our
20932 : * existing transaction, and in a new one wait for all processes to catch
20933 : * up on the catalog updates we've done so far; at that point we can
20934 : * complete the operation.
20935 : */
20936 500 : if (concurrent)
20937 : {
20938 : Oid partrelid,
20939 : parentrelid;
20940 : LOCKTAG tag;
20941 : char *parentrelname;
20942 : char *partrelname;
20943 :
20944 : /*
20945 : * Add a new constraint to the partition being detached, which
20946 : * supplants the partition constraint (unless there is one already).
20947 : */
20948 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
20949 :
20950 : /*
20951 : * We're almost done now; the only traces that remain are the
20952 : * pg_inherits tuple and the partition's relpartbounds. Before we can
20953 : * remove those, we need to wait until all transactions that know that
20954 : * this is a partition are gone.
20955 : */
20956 :
20957 : /*
20958 : * Remember relation OIDs to re-acquire them later; and relation names
20959 : * too, for error messages if something is dropped in between.
20960 : */
20961 140 : partrelid = RelationGetRelid(partRel);
20962 140 : parentrelid = RelationGetRelid(rel);
20963 140 : parentrelname = MemoryContextStrdup(PortalContext,
20964 140 : RelationGetRelationName(rel));
20965 140 : partrelname = MemoryContextStrdup(PortalContext,
20966 140 : RelationGetRelationName(partRel));
20967 :
20968 : /* Invalidate relcache entries for the parent -- must be before close */
20969 140 : CacheInvalidateRelcache(rel);
20970 :
20971 140 : table_close(partRel, NoLock);
20972 140 : table_close(rel, NoLock);
20973 140 : tab->rel = NULL;
20974 :
20975 : /* Make updated catalog entry visible */
20976 140 : PopActiveSnapshot();
20977 140 : CommitTransactionCommand();
20978 :
20979 140 : StartTransactionCommand();
20980 :
20981 : /*
20982 : * Now wait. This ensures that all queries that were planned
20983 : * including the partition are finished before we remove the rest of
20984 : * catalog entries. We don't need or indeed want to acquire this
20985 : * lock, though -- that would block later queries.
20986 : *
20987 : * We don't need to concern ourselves with waiting for a lock on the
20988 : * partition itself, since we will acquire AccessExclusiveLock below.
20989 : */
20990 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20991 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
20992 :
20993 : /*
20994 : * Now acquire locks in both relations again. Note they may have been
20995 : * removed in the meantime, so care is required.
20996 : */
20997 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20998 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
20999 :
21000 : /* If the relations aren't there, something bad happened; bail out */
21001 90 : if (rel == NULL)
21002 : {
21003 0 : if (partRel != NULL) /* shouldn't happen */
21004 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21005 : partrelname);
21006 0 : ereport(ERROR,
21007 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21008 : errmsg("partitioned table \"%s\" was removed concurrently",
21009 : parentrelname)));
21010 : }
21011 90 : if (partRel == NULL)
21012 0 : ereport(ERROR,
21013 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21014 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
21015 :
21016 90 : tab->rel = rel;
21017 : }
21018 :
21019 : /*
21020 : * Detaching the partition might involve TOAST table access, so ensure we
21021 : * have a valid snapshot.
21022 : */
21023 450 : PushActiveSnapshot(GetTransactionSnapshot());
21024 :
21025 : /* Do the final part of detaching */
21026 450 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21027 :
21028 448 : PopActiveSnapshot();
21029 :
21030 448 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21031 :
21032 : /* keep our lock until commit */
21033 448 : table_close(partRel, NoLock);
21034 :
21035 448 : return address;
21036 : }
21037 :
21038 : /*
21039 : * Second part of ALTER TABLE .. DETACH.
21040 : *
21041 : * This is separate so that it can be run independently when the second
21042 : * transaction of the concurrent algorithm fails (crash or abort).
21043 : */
21044 : static void
21045 464 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
21046 : Oid defaultPartOid)
21047 : {
21048 : Relation classRel;
21049 : List *fks;
21050 : ListCell *cell;
21051 : List *indexes;
21052 : Datum new_val[Natts_pg_class];
21053 : bool new_null[Natts_pg_class],
21054 : new_repl[Natts_pg_class];
21055 : HeapTuple tuple,
21056 : newtuple;
21057 464 : Relation trigrel = NULL;
21058 464 : List *fkoids = NIL;
21059 :
21060 464 : if (concurrent)
21061 : {
21062 : /*
21063 : * We can remove the pg_inherits row now. (In the non-concurrent case,
21064 : * this was already done).
21065 : */
21066 104 : RemoveInheritance(partRel, rel, true);
21067 : }
21068 :
21069 : /* Drop any triggers that were cloned on creation/attach. */
21070 464 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
21071 :
21072 : /*
21073 : * Detach any foreign keys that are inherited. This includes creating
21074 : * additional action triggers.
21075 : */
21076 464 : fks = copyObject(RelationGetFKeyList(partRel));
21077 464 : if (fks != NIL)
21078 84 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21079 :
21080 : /*
21081 : * It's possible that the partition being detached has a foreign key that
21082 : * references a partitioned table. When that happens, there are multiple
21083 : * pg_constraint rows for the partition: one points to the partitioned
21084 : * table itself, while the others point to each of its partitions. Only
21085 : * the topmost one is to be considered here; the child constraints must be
21086 : * left alone, because conceptually those aren't coming from our parent
21087 : * partitioned table, but from this partition itself.
21088 : *
21089 : * We implement this by collecting all the constraint OIDs in a first scan
21090 : * of the FK array, and skipping in the loop below those constraints whose
21091 : * parents are listed here.
21092 : */
21093 1096 : foreach_node(ForeignKeyCacheInfo, fk, fks)
21094 168 : fkoids = lappend_oid(fkoids, fk->conoid);
21095 :
21096 632 : foreach(cell, fks)
21097 : {
21098 168 : ForeignKeyCacheInfo *fk = lfirst(cell);
21099 : HeapTuple contup;
21100 : Form_pg_constraint conform;
21101 :
21102 168 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21103 168 : if (!HeapTupleIsValid(contup))
21104 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21105 168 : conform = (Form_pg_constraint) GETSTRUCT(contup);
21106 :
21107 : /*
21108 : * Consider only inherited foreign keys, and only if their parents
21109 : * aren't in the list.
21110 : */
21111 168 : if (conform->contype != CONSTRAINT_FOREIGN ||
21112 312 : !OidIsValid(conform->conparentid) ||
21113 144 : list_member_oid(fkoids, conform->conparentid))
21114 : {
21115 66 : ReleaseSysCache(contup);
21116 66 : continue;
21117 : }
21118 :
21119 : /*
21120 : * The constraint on this table must be marked no longer a child of
21121 : * the parent's constraint, as do its check triggers.
21122 : */
21123 102 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
21124 :
21125 : /*
21126 : * Also, look up the partition's "check" triggers corresponding to the
21127 : * ENFORCED constraint being detached and detach them from the parent
21128 : * triggers. NOT ENFORCED constraints do not have these triggers;
21129 : * therefore, this step is not needed.
21130 : */
21131 102 : if (fk->conenforced)
21132 : {
21133 : Oid insertTriggerOid,
21134 : updateTriggerOid;
21135 :
21136 102 : GetForeignKeyCheckTriggers(trigrel,
21137 : fk->conoid, fk->confrelid, fk->conrelid,
21138 : &insertTriggerOid, &updateTriggerOid);
21139 : Assert(OidIsValid(insertTriggerOid));
21140 102 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21141 : RelationGetRelid(partRel));
21142 : Assert(OidIsValid(updateTriggerOid));
21143 102 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21144 : RelationGetRelid(partRel));
21145 : }
21146 :
21147 : /*
21148 : * Lastly, create the action triggers on the referenced table, using
21149 : * addFkRecurseReferenced, which requires some elaborate setup (so put
21150 : * it in a separate block). While at it, if the table is partitioned,
21151 : * that function will recurse to create the pg_constraint rows and
21152 : * action triggers for each partition.
21153 : *
21154 : * Note there's no need to do addFkConstraint() here, because the
21155 : * pg_constraint row already exists.
21156 : */
21157 : {
21158 : Constraint *fkconstraint;
21159 : int numfks;
21160 : AttrNumber conkey[INDEX_MAX_KEYS];
21161 : AttrNumber confkey[INDEX_MAX_KEYS];
21162 : Oid conpfeqop[INDEX_MAX_KEYS];
21163 : Oid conppeqop[INDEX_MAX_KEYS];
21164 : Oid conffeqop[INDEX_MAX_KEYS];
21165 : int numfkdelsetcols;
21166 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21167 : Relation refdRel;
21168 :
21169 102 : DeconstructFkConstraintRow(contup,
21170 : &numfks,
21171 : conkey,
21172 : confkey,
21173 : conpfeqop,
21174 : conppeqop,
21175 : conffeqop,
21176 : &numfkdelsetcols,
21177 : confdelsetcols);
21178 :
21179 : /* Create a synthetic node we'll use throughout */
21180 102 : fkconstraint = makeNode(Constraint);
21181 102 : fkconstraint->contype = CONSTRAINT_FOREIGN;
21182 102 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
21183 102 : fkconstraint->deferrable = conform->condeferrable;
21184 102 : fkconstraint->initdeferred = conform->condeferred;
21185 102 : fkconstraint->is_enforced = conform->conenforced;
21186 102 : fkconstraint->skip_validation = true;
21187 102 : fkconstraint->initially_valid = conform->convalidated;
21188 : /* a few irrelevant fields omitted here */
21189 102 : fkconstraint->pktable = NULL;
21190 102 : fkconstraint->fk_attrs = NIL;
21191 102 : fkconstraint->pk_attrs = NIL;
21192 102 : fkconstraint->fk_matchtype = conform->confmatchtype;
21193 102 : fkconstraint->fk_upd_action = conform->confupdtype;
21194 102 : fkconstraint->fk_del_action = conform->confdeltype;
21195 102 : fkconstraint->fk_del_set_cols = NIL;
21196 102 : fkconstraint->old_conpfeqop = NIL;
21197 102 : fkconstraint->old_pktable_oid = InvalidOid;
21198 102 : fkconstraint->location = -1;
21199 :
21200 : /* set up colnames, used to generate the constraint name */
21201 252 : for (int i = 0; i < numfks; i++)
21202 : {
21203 : Form_pg_attribute att;
21204 :
21205 150 : att = TupleDescAttr(RelationGetDescr(partRel),
21206 150 : conkey[i] - 1);
21207 :
21208 150 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21209 150 : makeString(NameStr(att->attname)));
21210 : }
21211 :
21212 102 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
21213 :
21214 102 : addFkRecurseReferenced(fkconstraint, partRel,
21215 : refdRel,
21216 : conform->conindid,
21217 : fk->conoid,
21218 : numfks,
21219 : confkey,
21220 : conkey,
21221 : conpfeqop,
21222 : conppeqop,
21223 : conffeqop,
21224 : numfkdelsetcols,
21225 : confdelsetcols,
21226 : true,
21227 : InvalidOid, InvalidOid,
21228 102 : conform->conperiod);
21229 102 : table_close(refdRel, NoLock); /* keep lock till end of xact */
21230 : }
21231 :
21232 102 : ReleaseSysCache(contup);
21233 : }
21234 464 : list_free_deep(fks);
21235 464 : if (trigrel)
21236 84 : table_close(trigrel, RowExclusiveLock);
21237 :
21238 : /*
21239 : * Any sub-constraints that are in the referenced-side of a larger
21240 : * constraint have to be removed. This partition is no longer part of the
21241 : * key space of the constraint.
21242 : */
21243 524 : foreach(cell, GetParentedForeignKeyRefs(partRel))
21244 : {
21245 62 : Oid constrOid = lfirst_oid(cell);
21246 : ObjectAddress constraint;
21247 :
21248 62 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21249 62 : deleteDependencyRecordsForClass(ConstraintRelationId,
21250 : constrOid,
21251 : ConstraintRelationId,
21252 : DEPENDENCY_INTERNAL);
21253 62 : CommandCounterIncrement();
21254 :
21255 62 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21256 62 : performDeletion(&constraint, DROP_RESTRICT, 0);
21257 : }
21258 :
21259 : /* Now we can detach indexes */
21260 462 : indexes = RelationGetIndexList(partRel);
21261 656 : foreach(cell, indexes)
21262 : {
21263 194 : Oid idxid = lfirst_oid(cell);
21264 : Oid parentidx;
21265 : Relation idx;
21266 : Oid constrOid;
21267 : Oid parentConstrOid;
21268 :
21269 194 : if (!has_superclass(idxid))
21270 12 : continue;
21271 :
21272 182 : parentidx = get_partition_parent(idxid, false);
21273 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21274 :
21275 182 : idx = index_open(idxid, AccessExclusiveLock);
21276 182 : IndexSetParentIndex(idx, InvalidOid);
21277 :
21278 : /*
21279 : * If there's a constraint associated with the index, detach it too.
21280 : * Careful: it is possible for a constraint index in a partition to be
21281 : * the child of a non-constraint index, so verify whether the parent
21282 : * index does actually have a constraint.
21283 : */
21284 182 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
21285 : idxid);
21286 182 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
21287 : parentidx);
21288 182 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21289 84 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21290 :
21291 182 : index_close(idx, NoLock);
21292 : }
21293 :
21294 : /* Update pg_class tuple */
21295 462 : classRel = table_open(RelationRelationId, RowExclusiveLock);
21296 462 : tuple = SearchSysCacheCopy1(RELOID,
21297 : ObjectIdGetDatum(RelationGetRelid(partRel)));
21298 462 : if (!HeapTupleIsValid(tuple))
21299 0 : elog(ERROR, "cache lookup failed for relation %u",
21300 : RelationGetRelid(partRel));
21301 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21302 :
21303 : /* Clear relpartbound and reset relispartition */
21304 462 : memset(new_val, 0, sizeof(new_val));
21305 462 : memset(new_null, false, sizeof(new_null));
21306 462 : memset(new_repl, false, sizeof(new_repl));
21307 462 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21308 462 : new_null[Anum_pg_class_relpartbound - 1] = true;
21309 462 : new_repl[Anum_pg_class_relpartbound - 1] = true;
21310 462 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21311 : new_val, new_null, new_repl);
21312 :
21313 462 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21314 462 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21315 462 : heap_freetuple(newtuple);
21316 462 : table_close(classRel, RowExclusiveLock);
21317 :
21318 : /*
21319 : * Drop identity property from all identity columns of partition.
21320 : */
21321 1318 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21322 : {
21323 856 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21324 :
21325 856 : if (!attr->attisdropped && attr->attidentity)
21326 6 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21327 : AccessExclusiveLock, true, true);
21328 : }
21329 :
21330 462 : if (OidIsValid(defaultPartOid))
21331 : {
21332 : /*
21333 : * If the relation being detached is the default partition itself,
21334 : * remove it from the parent's pg_partitioned_table entry.
21335 : *
21336 : * If not, we must invalidate default partition's relcache entry, as
21337 : * in StorePartitionBound: its partition constraint depends on every
21338 : * other partition's partition constraint.
21339 : */
21340 46 : if (RelationGetRelid(partRel) == defaultPartOid)
21341 2 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
21342 : else
21343 44 : CacheInvalidateRelcacheByRelid(defaultPartOid);
21344 : }
21345 :
21346 : /*
21347 : * Invalidate the parent's relcache so that the partition is no longer
21348 : * included in its partition descriptor.
21349 : */
21350 462 : CacheInvalidateRelcache(rel);
21351 :
21352 : /*
21353 : * If the partition we just detached is partitioned itself, invalidate
21354 : * relcache for all descendent partitions too to ensure that their
21355 : * rd_partcheck expression trees are rebuilt; must lock partitions before
21356 : * doing so, using the same lockmode as what partRel has been locked with
21357 : * by the caller.
21358 : */
21359 462 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21360 : {
21361 : List *children;
21362 :
21363 62 : children = find_all_inheritors(RelationGetRelid(partRel),
21364 : AccessExclusiveLock, NULL);
21365 204 : foreach(cell, children)
21366 : {
21367 142 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
21368 : }
21369 : }
21370 462 : }
21371 :
21372 : /*
21373 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21374 : *
21375 : * To use when a DETACH PARTITION command previously did not run to
21376 : * completion; this completes the detaching process.
21377 : */
21378 : static ObjectAddress
21379 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
21380 : {
21381 : Relation partRel;
21382 : ObjectAddress address;
21383 14 : Snapshot snap = GetActiveSnapshot();
21384 :
21385 14 : partRel = table_openrv(name, AccessExclusiveLock);
21386 :
21387 : /*
21388 : * Wait until existing snapshots are gone. This is important if the
21389 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21390 : * user could immediately run DETACH FINALIZE without actually waiting for
21391 : * existing transactions. We must not complete the detach action until
21392 : * all such queries are complete (otherwise we would present them with an
21393 : * inconsistent view of catalogs).
21394 : */
21395 14 : WaitForOlderSnapshots(snap->xmin, false);
21396 :
21397 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21398 :
21399 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21400 :
21401 14 : table_close(partRel, NoLock);
21402 :
21403 14 : return address;
21404 : }
21405 :
21406 : /*
21407 : * DetachAddConstraintIfNeeded
21408 : * Subroutine for ATExecDetachPartition. Create a constraint that
21409 : * takes the place of the partition constraint, but avoid creating
21410 : * a dupe if a constraint already exists which implies the needed
21411 : * constraint.
21412 : */
21413 : static void
21414 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
21415 : {
21416 : List *constraintExpr;
21417 :
21418 140 : constraintExpr = RelationGetPartitionQual(partRel);
21419 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
21420 :
21421 : /*
21422 : * Avoid adding a new constraint if the needed constraint is implied by an
21423 : * existing constraint
21424 : */
21425 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
21426 : {
21427 : AlteredTableInfo *tab;
21428 : Constraint *n;
21429 :
21430 134 : tab = ATGetQueueEntry(wqueue, partRel);
21431 :
21432 : /* Add constraint on partition, equivalent to the partition constraint */
21433 134 : n = makeNode(Constraint);
21434 134 : n->contype = CONSTR_CHECK;
21435 134 : n->conname = NULL;
21436 134 : n->location = -1;
21437 134 : n->is_no_inherit = false;
21438 134 : n->raw_expr = NULL;
21439 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
21440 134 : n->is_enforced = true;
21441 134 : n->initially_valid = true;
21442 134 : n->skip_validation = true;
21443 : /* It's a re-add, since it nominally already exists */
21444 134 : ATAddCheckNNConstraint(wqueue, tab, partRel, n,
21445 : true, false, true, ShareUpdateExclusiveLock);
21446 : }
21447 140 : }
21448 :
21449 : /*
21450 : * DropClonedTriggersFromPartition
21451 : * subroutine for ATExecDetachPartition to remove any triggers that were
21452 : * cloned to the partition when it was created-as-partition or attached.
21453 : * This undoes what CloneRowTriggersToPartition did.
21454 : */
21455 : static void
21456 464 : DropClonedTriggersFromPartition(Oid partitionId)
21457 : {
21458 : ScanKeyData skey;
21459 : SysScanDesc scan;
21460 : HeapTuple trigtup;
21461 : Relation tgrel;
21462 : ObjectAddresses *objects;
21463 :
21464 464 : objects = new_object_addresses();
21465 :
21466 : /*
21467 : * Scan pg_trigger to search for all triggers on this rel.
21468 : */
21469 464 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21470 : F_OIDEQ, ObjectIdGetDatum(partitionId));
21471 464 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21472 464 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21473 : true, NULL, 1, &skey);
21474 876 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21475 : {
21476 412 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21477 : ObjectAddress trig;
21478 :
21479 : /* Ignore triggers that weren't cloned */
21480 412 : if (!OidIsValid(pg_trigger->tgparentid))
21481 394 : continue;
21482 :
21483 : /*
21484 : * Ignore internal triggers that are implementation objects of foreign
21485 : * keys, because these will be detached when the foreign keys
21486 : * themselves are.
21487 : */
21488 346 : if (OidIsValid(pg_trigger->tgconstrrelid))
21489 328 : continue;
21490 :
21491 : /*
21492 : * This is ugly, but necessary: remove the dependency markings on the
21493 : * trigger so that it can be removed.
21494 : */
21495 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21496 : TriggerRelationId,
21497 : DEPENDENCY_PARTITION_PRI);
21498 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21499 : RelationRelationId,
21500 : DEPENDENCY_PARTITION_SEC);
21501 :
21502 : /* remember this trigger to remove it below */
21503 18 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21504 18 : add_exact_object_address(&trig, objects);
21505 : }
21506 :
21507 : /* make the dependency removal visible to the deletion below */
21508 464 : CommandCounterIncrement();
21509 464 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
21510 :
21511 : /* done */
21512 464 : free_object_addresses(objects);
21513 464 : systable_endscan(scan);
21514 464 : table_close(tgrel, RowExclusiveLock);
21515 464 : }
21516 :
21517 : /*
21518 : * Before acquiring lock on an index, acquire the same lock on the owning
21519 : * table.
21520 : */
21521 : struct AttachIndexCallbackState
21522 : {
21523 : Oid partitionOid;
21524 : Oid parentTblOid;
21525 : bool lockedParentTbl;
21526 : };
21527 :
21528 : static void
21529 404 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21530 : void *arg)
21531 : {
21532 : struct AttachIndexCallbackState *state;
21533 : Form_pg_class classform;
21534 : HeapTuple tuple;
21535 :
21536 404 : state = (struct AttachIndexCallbackState *) arg;
21537 :
21538 404 : if (!state->lockedParentTbl)
21539 : {
21540 380 : LockRelationOid(state->parentTblOid, AccessShareLock);
21541 380 : state->lockedParentTbl = true;
21542 : }
21543 :
21544 : /*
21545 : * If we previously locked some other heap, and the name we're looking up
21546 : * no longer refers to an index on that relation, release the now-useless
21547 : * lock. XXX maybe we should do *after* we verify whether the index does
21548 : * not actually belong to the same relation ...
21549 : */
21550 404 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21551 : {
21552 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
21553 0 : state->partitionOid = InvalidOid;
21554 : }
21555 :
21556 : /* Didn't find a relation, so no need for locking or permission checks. */
21557 404 : if (!OidIsValid(relOid))
21558 8 : return;
21559 :
21560 396 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21561 396 : if (!HeapTupleIsValid(tuple))
21562 0 : return; /* concurrently dropped, so nothing to do */
21563 396 : classform = (Form_pg_class) GETSTRUCT(tuple);
21564 396 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21565 306 : classform->relkind != RELKIND_INDEX)
21566 6 : ereport(ERROR,
21567 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21568 : errmsg("\"%s\" is not an index", rv->relname)));
21569 390 : ReleaseSysCache(tuple);
21570 :
21571 : /*
21572 : * Since we need only examine the heap's tupledesc, an access share lock
21573 : * on it (preventing any DDL) is sufficient.
21574 : */
21575 390 : state->partitionOid = IndexGetRelation(relOid, false);
21576 390 : LockRelationOid(state->partitionOid, AccessShareLock);
21577 : }
21578 :
21579 : /*
21580 : * ALTER INDEX i1 ATTACH PARTITION i2
21581 : */
21582 : static ObjectAddress
21583 380 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
21584 : {
21585 : Relation partIdx;
21586 : Relation partTbl;
21587 : Relation parentTbl;
21588 : ObjectAddress address;
21589 : Oid partIdxId;
21590 : Oid currParent;
21591 : struct AttachIndexCallbackState state;
21592 :
21593 : /*
21594 : * We need to obtain lock on the index 'name' to modify it, but we also
21595 : * need to read its owning table's tuple descriptor -- so we need to lock
21596 : * both. To avoid deadlocks, obtain lock on the table before doing so on
21597 : * the index. Furthermore, we need to examine the parent table of the
21598 : * partition, so lock that one too.
21599 : */
21600 380 : state.partitionOid = InvalidOid;
21601 380 : state.parentTblOid = parentIdx->rd_index->indrelid;
21602 380 : state.lockedParentTbl = false;
21603 : partIdxId =
21604 380 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
21605 : RangeVarCallbackForAttachIndex,
21606 : &state);
21607 : /* Not there? */
21608 368 : if (!OidIsValid(partIdxId))
21609 0 : ereport(ERROR,
21610 : (errcode(ERRCODE_UNDEFINED_OBJECT),
21611 : errmsg("index \"%s\" does not exist", name->relname)));
21612 :
21613 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21614 368 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
21615 :
21616 : /* we already hold locks on both tables, so this is safe: */
21617 368 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21618 368 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21619 :
21620 368 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21621 :
21622 : /* Silently do nothing if already in the right state */
21623 736 : currParent = partIdx->rd_rel->relispartition ?
21624 368 : get_partition_parent(partIdxId, false) : InvalidOid;
21625 368 : if (currParent != RelationGetRelid(parentIdx))
21626 : {
21627 : IndexInfo *childInfo;
21628 : IndexInfo *parentInfo;
21629 : AttrMap *attmap;
21630 : bool found;
21631 : int i;
21632 : PartitionDesc partDesc;
21633 : Oid constraintOid,
21634 344 : cldConstrId = InvalidOid;
21635 :
21636 : /*
21637 : * If this partition already has an index attached, refuse the
21638 : * operation.
21639 : */
21640 344 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21641 :
21642 338 : if (OidIsValid(currParent))
21643 0 : ereport(ERROR,
21644 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21645 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21646 : RelationGetRelationName(partIdx),
21647 : RelationGetRelationName(parentIdx)),
21648 : errdetail("Index \"%s\" is already attached to another index.",
21649 : RelationGetRelationName(partIdx))));
21650 :
21651 : /* Make sure it indexes a partition of the other index's table */
21652 338 : partDesc = RelationGetPartitionDesc(parentTbl, true);
21653 338 : found = false;
21654 528 : for (i = 0; i < partDesc->nparts; i++)
21655 : {
21656 522 : if (partDesc->oids[i] == state.partitionOid)
21657 : {
21658 332 : found = true;
21659 332 : break;
21660 : }
21661 : }
21662 338 : if (!found)
21663 6 : ereport(ERROR,
21664 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21665 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21666 : RelationGetRelationName(partIdx),
21667 : RelationGetRelationName(parentIdx)),
21668 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21669 : RelationGetRelationName(partIdx),
21670 : RelationGetRelationName(parentTbl))));
21671 :
21672 : /* Ensure the indexes are compatible */
21673 332 : childInfo = BuildIndexInfo(partIdx);
21674 332 : parentInfo = BuildIndexInfo(parentIdx);
21675 332 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21676 : RelationGetDescr(parentTbl),
21677 : false);
21678 332 : if (!CompareIndexInfo(childInfo, parentInfo,
21679 332 : partIdx->rd_indcollation,
21680 332 : parentIdx->rd_indcollation,
21681 332 : partIdx->rd_opfamily,
21682 332 : parentIdx->rd_opfamily,
21683 : attmap))
21684 42 : ereport(ERROR,
21685 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21686 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21687 : RelationGetRelationName(partIdx),
21688 : RelationGetRelationName(parentIdx)),
21689 : errdetail("The index definitions do not match.")));
21690 :
21691 : /*
21692 : * If there is a constraint in the parent, make sure there is one in
21693 : * the child too.
21694 : */
21695 290 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21696 : RelationGetRelid(parentIdx));
21697 :
21698 290 : if (OidIsValid(constraintOid))
21699 : {
21700 110 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
21701 : partIdxId);
21702 110 : if (!OidIsValid(cldConstrId))
21703 6 : ereport(ERROR,
21704 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21705 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21706 : RelationGetRelationName(partIdx),
21707 : RelationGetRelationName(parentIdx)),
21708 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21709 : RelationGetRelationName(parentIdx),
21710 : RelationGetRelationName(parentTbl),
21711 : RelationGetRelationName(partIdx))));
21712 : }
21713 :
21714 : /*
21715 : * If it's a primary key, make sure the columns in the partition are
21716 : * NOT NULL.
21717 : */
21718 284 : if (parentIdx->rd_index->indisprimary)
21719 92 : verifyPartitionIndexNotNull(childInfo, partTbl);
21720 :
21721 : /* All good -- do it */
21722 284 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21723 284 : if (OidIsValid(constraintOid))
21724 104 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
21725 : RelationGetRelid(partTbl));
21726 :
21727 284 : free_attrmap(attmap);
21728 :
21729 284 : validatePartitionedIndex(parentIdx, parentTbl);
21730 : }
21731 :
21732 308 : relation_close(parentTbl, AccessShareLock);
21733 : /* keep these locks till commit */
21734 308 : relation_close(partTbl, NoLock);
21735 308 : relation_close(partIdx, NoLock);
21736 :
21737 308 : return address;
21738 : }
21739 :
21740 : /*
21741 : * Verify whether the given partition already contains an index attached
21742 : * to the given partitioned index. If so, raise an error.
21743 : */
21744 : static void
21745 344 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21746 : {
21747 : Oid existingIdx;
21748 :
21749 344 : existingIdx = index_get_partition(partitionTbl,
21750 : RelationGetRelid(parentIdx));
21751 344 : if (OidIsValid(existingIdx))
21752 6 : ereport(ERROR,
21753 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21754 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21755 : RelationGetRelationName(partIdx),
21756 : RelationGetRelationName(parentIdx)),
21757 : errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21758 : get_rel_name(existingIdx),
21759 : RelationGetRelationName(partitionTbl))));
21760 338 : }
21761 :
21762 : /*
21763 : * Verify whether the set of attached partition indexes to a parent index on
21764 : * a partitioned table is complete. If it is, mark the parent index valid.
21765 : *
21766 : * This should be called each time a partition index is attached.
21767 : */
21768 : static void
21769 326 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
21770 : {
21771 : Relation inheritsRel;
21772 : SysScanDesc scan;
21773 : ScanKeyData key;
21774 326 : int tuples = 0;
21775 : HeapTuple inhTup;
21776 326 : bool updated = false;
21777 :
21778 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21779 :
21780 : /*
21781 : * Scan pg_inherits for this parent index. Count each valid index we find
21782 : * (verifying the pg_index entry for each), and if we reach the total
21783 : * amount we expect, we can mark this parent index as valid.
21784 : */
21785 326 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21786 326 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21787 : BTEqualStrategyNumber, F_OIDEQ,
21788 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21789 326 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21790 : NULL, 1, &key);
21791 844 : while ((inhTup = systable_getnext(scan)) != NULL)
21792 : {
21793 518 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21794 : HeapTuple indTup;
21795 : Form_pg_index indexForm;
21796 :
21797 518 : indTup = SearchSysCache1(INDEXRELID,
21798 : ObjectIdGetDatum(inhForm->inhrelid));
21799 518 : if (!HeapTupleIsValid(indTup))
21800 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21801 518 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21802 518 : if (indexForm->indisvalid)
21803 460 : tuples += 1;
21804 518 : ReleaseSysCache(indTup);
21805 : }
21806 :
21807 : /* Done with pg_inherits */
21808 326 : systable_endscan(scan);
21809 326 : table_close(inheritsRel, AccessShareLock);
21810 :
21811 : /*
21812 : * If we found as many inherited indexes as the partitioned table has
21813 : * partitions, we're good; update pg_index to set indisvalid.
21814 : */
21815 326 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21816 : {
21817 : Relation idxRel;
21818 : HeapTuple indTup;
21819 : Form_pg_index indexForm;
21820 :
21821 164 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
21822 164 : indTup = SearchSysCacheCopy1(INDEXRELID,
21823 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21824 164 : if (!HeapTupleIsValid(indTup))
21825 0 : elog(ERROR, "cache lookup failed for index %u",
21826 : RelationGetRelid(partedIdx));
21827 164 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21828 :
21829 164 : indexForm->indisvalid = true;
21830 164 : updated = true;
21831 :
21832 164 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21833 :
21834 164 : table_close(idxRel, RowExclusiveLock);
21835 164 : heap_freetuple(indTup);
21836 : }
21837 :
21838 : /*
21839 : * If this index is in turn a partition of a larger index, validating it
21840 : * might cause the parent to become valid also. Try that.
21841 : */
21842 326 : if (updated && partedIdx->rd_rel->relispartition)
21843 : {
21844 : Oid parentIdxId,
21845 : parentTblId;
21846 : Relation parentIdx,
21847 : parentTbl;
21848 :
21849 : /* make sure we see the validation we just did */
21850 42 : CommandCounterIncrement();
21851 :
21852 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21853 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21854 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21855 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21856 : Assert(!parentIdx->rd_index->indisvalid);
21857 :
21858 42 : validatePartitionedIndex(parentIdx, parentTbl);
21859 :
21860 42 : relation_close(parentIdx, AccessExclusiveLock);
21861 42 : relation_close(parentTbl, AccessExclusiveLock);
21862 : }
21863 326 : }
21864 :
21865 : /*
21866 : * When attaching an index as a partition of a partitioned index which is a
21867 : * primary key, verify that all the columns in the partition are marked NOT
21868 : * NULL.
21869 : */
21870 : static void
21871 92 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
21872 : {
21873 186 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21874 : {
21875 94 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
21876 94 : iinfo->ii_IndexAttrNumbers[i] - 1);
21877 :
21878 94 : if (!att->attnotnull)
21879 0 : ereport(ERROR,
21880 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21881 : errmsg("invalid primary key definition"),
21882 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21883 : NameStr(att->attname),
21884 : RelationGetRelationName(partition)));
21885 : }
21886 92 : }
21887 :
21888 : /*
21889 : * Return an OID list of constraints that reference the given relation
21890 : * that are marked as having a parent constraints.
21891 : */
21892 : static List *
21893 998 : GetParentedForeignKeyRefs(Relation partition)
21894 : {
21895 : Relation pg_constraint;
21896 : HeapTuple tuple;
21897 : SysScanDesc scan;
21898 : ScanKeyData key[2];
21899 998 : List *constraints = NIL;
21900 :
21901 : /*
21902 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
21903 : * scan.
21904 : */
21905 1426 : if (RelationGetIndexList(partition) == NIL ||
21906 428 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
21907 : INDEX_ATTR_BITMAP_KEY)))
21908 726 : return NIL;
21909 :
21910 : /* Search for constraints referencing this table */
21911 272 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21912 272 : ScanKeyInit(&key[0],
21913 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21914 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21915 272 : ScanKeyInit(&key[1],
21916 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
21917 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21918 :
21919 : /* XXX This is a seqscan, as we don't have a usable index */
21920 272 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21921 444 : while ((tuple = systable_getnext(scan)) != NULL)
21922 : {
21923 172 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21924 :
21925 : /*
21926 : * We only need to process constraints that are part of larger ones.
21927 : */
21928 172 : if (!OidIsValid(constrForm->conparentid))
21929 0 : continue;
21930 :
21931 172 : constraints = lappend_oid(constraints, constrForm->oid);
21932 : }
21933 :
21934 272 : systable_endscan(scan);
21935 272 : table_close(pg_constraint, AccessShareLock);
21936 :
21937 272 : return constraints;
21938 : }
21939 :
21940 : /*
21941 : * During DETACH PARTITION, verify that any foreign keys pointing to the
21942 : * partitioned table would not become invalid. An error is raised if any
21943 : * referenced values exist.
21944 : */
21945 : static void
21946 534 : ATDetachCheckNoForeignKeyRefs(Relation partition)
21947 : {
21948 : List *constraints;
21949 : ListCell *cell;
21950 :
21951 534 : constraints = GetParentedForeignKeyRefs(partition);
21952 :
21953 610 : foreach(cell, constraints)
21954 : {
21955 110 : Oid constrOid = lfirst_oid(cell);
21956 : HeapTuple tuple;
21957 : Form_pg_constraint constrForm;
21958 : Relation rel;
21959 110 : Trigger trig = {0};
21960 :
21961 110 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21962 110 : if (!HeapTupleIsValid(tuple))
21963 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21964 110 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21965 :
21966 : Assert(OidIsValid(constrForm->conparentid));
21967 : Assert(constrForm->confrelid == RelationGetRelid(partition));
21968 :
21969 : /* prevent data changes into the referencing table until commit */
21970 110 : rel = table_open(constrForm->conrelid, ShareLock);
21971 :
21972 110 : trig.tgoid = InvalidOid;
21973 110 : trig.tgname = NameStr(constrForm->conname);
21974 110 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
21975 110 : trig.tgisinternal = true;
21976 110 : trig.tgconstrrelid = RelationGetRelid(partition);
21977 110 : trig.tgconstrindid = constrForm->conindid;
21978 110 : trig.tgconstraint = constrForm->oid;
21979 110 : trig.tgdeferrable = false;
21980 110 : trig.tginitdeferred = false;
21981 : /* we needn't fill in remaining fields */
21982 :
21983 110 : RI_PartitionRemove_Check(&trig, rel, partition);
21984 :
21985 76 : ReleaseSysCache(tuple);
21986 :
21987 76 : table_close(rel, NoLock);
21988 : }
21989 500 : }
21990 :
21991 : /*
21992 : * resolve column compression specification to compression method.
21993 : */
21994 : static char
21995 256526 : GetAttributeCompression(Oid atttypid, const char *compression)
21996 : {
21997 : char cmethod;
21998 :
21999 256526 : if (compression == NULL || strcmp(compression, "default") == 0)
22000 256342 : return InvalidCompressionMethod;
22001 :
22002 : /*
22003 : * To specify a nondefault method, the column data type must be toastable.
22004 : * Note this says nothing about whether the column's attstorage setting
22005 : * permits compression; we intentionally allow attstorage and
22006 : * attcompression to be independent. But with a non-toastable type,
22007 : * attstorage could not be set to a value that would permit compression.
22008 : *
22009 : * We don't actually need to enforce this, since nothing bad would happen
22010 : * if attcompression were non-default; it would never be consulted. But
22011 : * it seems more user-friendly to complain about a certainly-useless
22012 : * attempt to set the property.
22013 : */
22014 184 : if (!TypeIsToastable(atttypid))
22015 6 : ereport(ERROR,
22016 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22017 : errmsg("column data type %s does not support compression",
22018 : format_type_be(atttypid))));
22019 :
22020 178 : cmethod = CompressionNameToMethod(compression);
22021 178 : if (!CompressionMethodIsValid(cmethod))
22022 12 : ereport(ERROR,
22023 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22024 : errmsg("invalid compression method \"%s\"", compression)));
22025 :
22026 166 : return cmethod;
22027 : }
22028 :
22029 : /*
22030 : * resolve column storage specification
22031 : */
22032 : static char
22033 274 : GetAttributeStorage(Oid atttypid, const char *storagemode)
22034 : {
22035 274 : char cstorage = 0;
22036 :
22037 274 : if (pg_strcasecmp(storagemode, "plain") == 0)
22038 56 : cstorage = TYPSTORAGE_PLAIN;
22039 218 : else if (pg_strcasecmp(storagemode, "external") == 0)
22040 176 : cstorage = TYPSTORAGE_EXTERNAL;
22041 42 : else if (pg_strcasecmp(storagemode, "extended") == 0)
22042 16 : cstorage = TYPSTORAGE_EXTENDED;
22043 26 : else if (pg_strcasecmp(storagemode, "main") == 0)
22044 20 : cstorage = TYPSTORAGE_MAIN;
22045 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
22046 6 : cstorage = get_typstorage(atttypid);
22047 : else
22048 0 : ereport(ERROR,
22049 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22050 : errmsg("invalid storage type \"%s\"",
22051 : storagemode)));
22052 :
22053 : /*
22054 : * safety check: do not allow toasted storage modes unless column datatype
22055 : * is TOAST-aware.
22056 : */
22057 274 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22058 6 : ereport(ERROR,
22059 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22060 : errmsg("column data type %s can only have storage PLAIN",
22061 : format_type_be(atttypid))));
22062 :
22063 268 : return cstorage;
22064 : }
|