Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * tablecmds.c
4 : * Commands for creating and altering table structures and settings
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/commands/tablecmds.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/attmap.h"
18 : #include "access/genam.h"
19 : #include "access/gist.h"
20 : #include "access/heapam.h"
21 : #include "access/heapam_xlog.h"
22 : #include "access/multixact.h"
23 : #include "access/reloptions.h"
24 : #include "access/relscan.h"
25 : #include "access/sysattr.h"
26 : #include "access/tableam.h"
27 : #include "access/toast_compression.h"
28 : #include "access/xact.h"
29 : #include "access/xlog.h"
30 : #include "access/xloginsert.h"
31 : #include "catalog/catalog.h"
32 : #include "catalog/heap.h"
33 : #include "catalog/index.h"
34 : #include "catalog/namespace.h"
35 : #include "catalog/objectaccess.h"
36 : #include "catalog/partition.h"
37 : #include "catalog/pg_am.h"
38 : #include "catalog/pg_attrdef.h"
39 : #include "catalog/pg_collation.h"
40 : #include "catalog/pg_constraint.h"
41 : #include "catalog/pg_depend.h"
42 : #include "catalog/pg_foreign_table.h"
43 : #include "catalog/pg_inherits.h"
44 : #include "catalog/pg_largeobject.h"
45 : #include "catalog/pg_namespace.h"
46 : #include "catalog/pg_opclass.h"
47 : #include "catalog/pg_policy.h"
48 : #include "catalog/pg_proc.h"
49 : #include "catalog/pg_publication_rel.h"
50 : #include "catalog/pg_rewrite.h"
51 : #include "catalog/pg_statistic_ext.h"
52 : #include "catalog/pg_tablespace.h"
53 : #include "catalog/pg_trigger.h"
54 : #include "catalog/pg_type.h"
55 : #include "catalog/storage.h"
56 : #include "catalog/storage_xlog.h"
57 : #include "catalog/toasting.h"
58 : #include "commands/cluster.h"
59 : #include "commands/comment.h"
60 : #include "commands/defrem.h"
61 : #include "commands/event_trigger.h"
62 : #include "commands/sequence.h"
63 : #include "commands/tablecmds.h"
64 : #include "commands/tablespace.h"
65 : #include "commands/trigger.h"
66 : #include "commands/typecmds.h"
67 : #include "commands/user.h"
68 : #include "commands/vacuum.h"
69 : #include "common/int.h"
70 : #include "executor/executor.h"
71 : #include "foreign/fdwapi.h"
72 : #include "foreign/foreign.h"
73 : #include "miscadmin.h"
74 : #include "nodes/makefuncs.h"
75 : #include "nodes/nodeFuncs.h"
76 : #include "nodes/parsenodes.h"
77 : #include "optimizer/optimizer.h"
78 : #include "parser/parse_coerce.h"
79 : #include "parser/parse_collate.h"
80 : #include "parser/parse_expr.h"
81 : #include "parser/parse_relation.h"
82 : #include "parser/parse_type.h"
83 : #include "parser/parse_utilcmd.h"
84 : #include "parser/parser.h"
85 : #include "partitioning/partbounds.h"
86 : #include "partitioning/partdesc.h"
87 : #include "pgstat.h"
88 : #include "rewrite/rewriteDefine.h"
89 : #include "rewrite/rewriteHandler.h"
90 : #include "rewrite/rewriteManip.h"
91 : #include "storage/bufmgr.h"
92 : #include "storage/lmgr.h"
93 : #include "storage/lock.h"
94 : #include "storage/predicate.h"
95 : #include "storage/smgr.h"
96 : #include "tcop/utility.h"
97 : #include "utils/acl.h"
98 : #include "utils/builtins.h"
99 : #include "utils/fmgroids.h"
100 : #include "utils/inval.h"
101 : #include "utils/lsyscache.h"
102 : #include "utils/memutils.h"
103 : #include "utils/partcache.h"
104 : #include "utils/relcache.h"
105 : #include "utils/ruleutils.h"
106 : #include "utils/snapmgr.h"
107 : #include "utils/syscache.h"
108 : #include "utils/timestamp.h"
109 : #include "utils/typcache.h"
110 : #include "utils/usercontext.h"
111 :
112 : /*
113 : * ON COMMIT action list
114 : */
115 : typedef struct OnCommitItem
116 : {
117 : Oid relid; /* relid of relation */
118 : OnCommitAction oncommit; /* what to do at end of xact */
119 :
120 : /*
121 : * If this entry was created during the current transaction,
122 : * creating_subid is the ID of the creating subxact; if created in a prior
123 : * transaction, creating_subid is zero. If deleted during the current
124 : * transaction, deleting_subid is the ID of the deleting subxact; if no
125 : * deletion request is pending, deleting_subid is zero.
126 : */
127 : SubTransactionId creating_subid;
128 : SubTransactionId deleting_subid;
129 : } OnCommitItem;
130 :
131 : static List *on_commits = NIL;
132 :
133 :
134 : /*
135 : * State information for ALTER TABLE
136 : *
137 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
138 : * structs, one for each table modified by the operation (the named table
139 : * plus any child tables that are affected). We save lists of subcommands
140 : * to apply to this table (possibly modified by parse transformation steps);
141 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
142 : * necessary information is stored in the constraints and newvals lists.
143 : *
144 : * Phase 2 is divided into multiple passes; subcommands are executed in
145 : * a pass determined by subcommand type.
146 : */
147 :
148 : typedef enum AlterTablePass
149 : {
150 : AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
151 : AT_PASS_DROP, /* DROP (all flavors) */
152 : AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
153 : AT_PASS_ADD_COL, /* ADD COLUMN */
154 : AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
155 : AT_PASS_OLD_INDEX, /* re-add existing indexes */
156 : AT_PASS_OLD_CONSTR, /* re-add existing constraints */
157 : /* We could support a RENAME COLUMN pass here, but not currently used */
158 : AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
159 : AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
160 : AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
161 : AT_PASS_ADD_INDEX, /* ADD indexes */
162 : AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
163 : AT_PASS_MISC, /* other stuff */
164 : } AlterTablePass;
165 :
166 : #define AT_NUM_PASSES (AT_PASS_MISC + 1)
167 :
168 : typedef struct AlteredTableInfo
169 : {
170 : /* Information saved before any work commences: */
171 : Oid relid; /* Relation to work on */
172 : char relkind; /* Its relkind */
173 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
174 :
175 : /*
176 : * Transiently set during Phase 2, normally set to NULL.
177 : *
178 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
179 : * returns control. This can be exploited by ATExecCmd subroutines to
180 : * close/reopen across transaction boundaries.
181 : */
182 : Relation rel;
183 :
184 : /* Information saved by Phase 1 for Phase 2: */
185 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
186 : /* Information saved by Phases 1/2 for Phase 3: */
187 : List *constraints; /* List of NewConstraint */
188 : List *newvals; /* List of NewColumnValue */
189 : List *afterStmts; /* List of utility command parsetrees */
190 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
191 : int rewrite; /* Reason for forced rewrite, if any */
192 : bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
193 : Oid newAccessMethod; /* new access method; 0 means no change,
194 : * if above is true */
195 : Oid newTableSpace; /* new tablespace; 0 means no change */
196 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
197 : char newrelpersistence; /* if above is true */
198 : Expr *partition_constraint; /* for attach partition validation */
199 : /* true, if validating default due to some other attach/detach */
200 : bool validate_default;
201 : /* Objects to rebuild after completing ALTER TYPE operations */
202 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
203 : List *changedConstraintDefs; /* string definitions of same */
204 : List *changedIndexOids; /* OIDs of indexes to rebuild */
205 : List *changedIndexDefs; /* string definitions of same */
206 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
207 : char *clusterOnIndex; /* index to use for CLUSTER */
208 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
209 : List *changedStatisticsDefs; /* string definitions of same */
210 : } AlteredTableInfo;
211 :
212 : /* Struct describing one new constraint to check in Phase 3 scan */
213 : /* Note: new not-null constraints are handled elsewhere */
214 : typedef struct NewConstraint
215 : {
216 : char *name; /* Constraint name, or NULL if none */
217 : ConstrType contype; /* CHECK or FOREIGN */
218 : Oid refrelid; /* PK rel, if FOREIGN */
219 : Oid refindid; /* OID of PK's index, if FOREIGN */
220 : bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
221 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
222 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
223 : ExprState *qualstate; /* Execution state for CHECK expr */
224 : } NewConstraint;
225 :
226 : /*
227 : * Struct describing one new column value that needs to be computed during
228 : * Phase 3 copy (this could be either a new column with a non-null default, or
229 : * a column that we're changing the type of). Columns without such an entry
230 : * are just copied from the old table during ATRewriteTable. Note that the
231 : * expr is an expression over *old* table values, except when is_generated
232 : * is true; then it is an expression over columns of the *new* tuple.
233 : */
234 : typedef struct NewColumnValue
235 : {
236 : AttrNumber attnum; /* which column */
237 : Expr *expr; /* expression to compute */
238 : ExprState *exprstate; /* execution state */
239 : bool is_generated; /* is it a GENERATED expression? */
240 : } NewColumnValue;
241 :
242 : /*
243 : * Error-reporting support for RemoveRelations
244 : */
245 : struct dropmsgstrings
246 : {
247 : char kind;
248 : int nonexistent_code;
249 : const char *nonexistent_msg;
250 : const char *skipping_msg;
251 : const char *nota_msg;
252 : const char *drophint_msg;
253 : };
254 :
255 : static const struct dropmsgstrings dropmsgstringarray[] = {
256 : {RELKIND_RELATION,
257 : ERRCODE_UNDEFINED_TABLE,
258 : gettext_noop("table \"%s\" does not exist"),
259 : gettext_noop("table \"%s\" does not exist, skipping"),
260 : gettext_noop("\"%s\" is not a table"),
261 : gettext_noop("Use DROP TABLE to remove a table.")},
262 : {RELKIND_SEQUENCE,
263 : ERRCODE_UNDEFINED_TABLE,
264 : gettext_noop("sequence \"%s\" does not exist"),
265 : gettext_noop("sequence \"%s\" does not exist, skipping"),
266 : gettext_noop("\"%s\" is not a sequence"),
267 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
268 : {RELKIND_VIEW,
269 : ERRCODE_UNDEFINED_TABLE,
270 : gettext_noop("view \"%s\" does not exist"),
271 : gettext_noop("view \"%s\" does not exist, skipping"),
272 : gettext_noop("\"%s\" is not a view"),
273 : gettext_noop("Use DROP VIEW to remove a view.")},
274 : {RELKIND_MATVIEW,
275 : ERRCODE_UNDEFINED_TABLE,
276 : gettext_noop("materialized view \"%s\" does not exist"),
277 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
278 : gettext_noop("\"%s\" is not a materialized view"),
279 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
280 : {RELKIND_INDEX,
281 : ERRCODE_UNDEFINED_OBJECT,
282 : gettext_noop("index \"%s\" does not exist"),
283 : gettext_noop("index \"%s\" does not exist, skipping"),
284 : gettext_noop("\"%s\" is not an index"),
285 : gettext_noop("Use DROP INDEX to remove an index.")},
286 : {RELKIND_COMPOSITE_TYPE,
287 : ERRCODE_UNDEFINED_OBJECT,
288 : gettext_noop("type \"%s\" does not exist"),
289 : gettext_noop("type \"%s\" does not exist, skipping"),
290 : gettext_noop("\"%s\" is not a type"),
291 : gettext_noop("Use DROP TYPE to remove a type.")},
292 : {RELKIND_FOREIGN_TABLE,
293 : ERRCODE_UNDEFINED_OBJECT,
294 : gettext_noop("foreign table \"%s\" does not exist"),
295 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
296 : gettext_noop("\"%s\" is not a foreign table"),
297 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
298 : {RELKIND_PARTITIONED_TABLE,
299 : ERRCODE_UNDEFINED_TABLE,
300 : gettext_noop("table \"%s\" does not exist"),
301 : gettext_noop("table \"%s\" does not exist, skipping"),
302 : gettext_noop("\"%s\" is not a table"),
303 : gettext_noop("Use DROP TABLE to remove a table.")},
304 : {RELKIND_PARTITIONED_INDEX,
305 : ERRCODE_UNDEFINED_OBJECT,
306 : gettext_noop("index \"%s\" does not exist"),
307 : gettext_noop("index \"%s\" does not exist, skipping"),
308 : gettext_noop("\"%s\" is not an index"),
309 : gettext_noop("Use DROP INDEX to remove an index.")},
310 : {'\0', 0, NULL, NULL, NULL, NULL}
311 : };
312 :
313 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
314 : struct DropRelationCallbackState
315 : {
316 : /* These fields are set by RemoveRelations: */
317 : char expected_relkind;
318 : LOCKMODE heap_lockmode;
319 : /* These fields are state to track which subsidiary locks are held: */
320 : Oid heapOid;
321 : Oid partParentOid;
322 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
323 : char actual_relkind;
324 : char actual_relpersistence;
325 : };
326 :
327 : /* Alter table target-type flags for ATSimplePermissions */
328 : #define ATT_TABLE 0x0001
329 : #define ATT_VIEW 0x0002
330 : #define ATT_MATVIEW 0x0004
331 : #define ATT_INDEX 0x0008
332 : #define ATT_COMPOSITE_TYPE 0x0010
333 : #define ATT_FOREIGN_TABLE 0x0020
334 : #define ATT_PARTITIONED_INDEX 0x0040
335 : #define ATT_SEQUENCE 0x0080
336 : #define ATT_PARTITIONED_TABLE 0x0100
337 :
338 : /*
339 : * ForeignTruncateInfo
340 : *
341 : * Information related to truncation of foreign tables. This is used for
342 : * the elements in a hash table. It uses the server OID as lookup key,
343 : * and includes a per-server list of all foreign tables involved in the
344 : * truncation.
345 : */
346 : typedef struct ForeignTruncateInfo
347 : {
348 : Oid serverid;
349 : List *rels;
350 : } ForeignTruncateInfo;
351 :
352 : /* Partial or complete FK creation in addFkConstraint() */
353 : typedef enum addFkConstraintSides
354 : {
355 : addFkReferencedSide,
356 : addFkReferencingSide,
357 : addFkBothSides,
358 : } addFkConstraintSides;
359 :
360 : /*
361 : * Partition tables are expected to be dropped when the parent partitioned
362 : * table gets dropped. Hence for partitioning we use AUTO dependency.
363 : * Otherwise, for regular inheritance use NORMAL dependency.
364 : */
365 : #define child_dependency_type(child_is_partition) \
366 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
367 :
368 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
369 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
370 : static void truncate_check_activity(Relation rel);
371 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
372 : Oid relId, Oid oldRelId, void *arg);
373 : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
374 : bool is_partition, List **supconstr,
375 : List **supnotnulls);
376 : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
377 : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
378 : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
379 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
380 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
381 : static void StoreCatalogInheritance(Oid relationId, List *supers,
382 : bool child_is_partition);
383 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
384 : int32 seqNumber, Relation inhRelation,
385 : bool child_is_partition);
386 : static int findAttrByName(const char *attributeName, const List *columns);
387 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
388 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
389 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
390 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
391 : LOCKMODE lockmode);
392 : static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
393 : ATAlterConstraint *cmdcon,
394 : bool recurse, LOCKMODE lockmode);
395 : static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
396 : Relation tgrel, Relation rel, HeapTuple contuple,
397 : bool recurse, LOCKMODE lockmode);
398 : static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
399 : Relation conrel, Relation tgrel,
400 : Oid fkrelid, Oid pkrelid,
401 : HeapTuple contuple, LOCKMODE lockmode,
402 : Oid ReferencedParentDelTrigger,
403 : Oid ReferencedParentUpdTrigger,
404 : Oid ReferencingParentInsTrigger,
405 : Oid ReferencingParentUpdTrigger);
406 : static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
407 : Relation conrel, Relation tgrel, Relation rel,
408 : HeapTuple contuple, bool recurse,
409 : List **otherrelids, LOCKMODE lockmode);
410 : static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
411 : Relation conrel, Relation rel,
412 : HeapTuple contuple, LOCKMODE lockmode);
413 : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
414 : bool deferrable, bool initdeferred,
415 : List **otherrelids);
416 : static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
417 : Relation conrel, Relation tgrel,
418 : Oid fkrelid, Oid pkrelid,
419 : HeapTuple contuple, LOCKMODE lockmode,
420 : Oid ReferencedParentDelTrigger,
421 : Oid ReferencedParentUpdTrigger,
422 : Oid ReferencingParentInsTrigger,
423 : Oid ReferencingParentUpdTrigger);
424 : static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
425 : Relation conrel, Relation tgrel, Relation rel,
426 : HeapTuple contuple, bool recurse,
427 : List **otherrelids, LOCKMODE lockmode);
428 : static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
429 : HeapTuple contuple);
430 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
431 : Relation rel, char *constrName,
432 : bool recurse, bool recursing, LOCKMODE lockmode);
433 : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
434 : Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
435 : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
436 : char *constrName, HeapTuple contuple,
437 : bool recurse, bool recursing, LOCKMODE lockmode);
438 : static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
439 : HeapTuple contuple, bool recurse, bool recursing,
440 : LOCKMODE lockmode);
441 : static int transformColumnNameList(Oid relId, List *colList,
442 : int16 *attnums, Oid *atttypids, Oid *attcollids);
443 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
444 : List **attnamelist,
445 : int16 *attnums, Oid *atttypids, Oid *attcollids,
446 : Oid *opclasses, bool *pk_has_without_overlaps);
447 : static Oid transformFkeyCheckAttrs(Relation pkrel,
448 : int numattrs, int16 *attnums,
449 : bool with_period, Oid *opclasses,
450 : bool *pk_has_without_overlaps);
451 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
452 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
453 : Oid *funcid);
454 : static void validateForeignKeyConstraint(char *conname,
455 : Relation rel, Relation pkrel,
456 : Oid pkindOid, Oid constraintOid, bool hasperiod);
457 : static void CheckAlterTableIsSafe(Relation rel);
458 : static void ATController(AlterTableStmt *parsetree,
459 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
460 : AlterTableUtilityContext *context);
461 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
462 : bool recurse, bool recursing, LOCKMODE lockmode,
463 : AlterTableUtilityContext *context);
464 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
465 : AlterTableUtilityContext *context);
466 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
467 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
468 : AlterTableUtilityContext *context);
469 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
470 : Relation rel, AlterTableCmd *cmd,
471 : bool recurse, LOCKMODE lockmode,
472 : AlterTablePass cur_pass,
473 : AlterTableUtilityContext *context);
474 : static void ATRewriteTables(AlterTableStmt *parsetree,
475 : List **wqueue, LOCKMODE lockmode,
476 : AlterTableUtilityContext *context);
477 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
478 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
479 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
480 : static void ATSimpleRecursion(List **wqueue, Relation rel,
481 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
482 : AlterTableUtilityContext *context);
483 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
484 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
485 : LOCKMODE lockmode,
486 : AlterTableUtilityContext *context);
487 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
488 : DropBehavior behavior);
489 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
490 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
491 : AlterTableUtilityContext *context);
492 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
493 : Relation rel, AlterTableCmd **cmd,
494 : bool recurse, bool recursing,
495 : LOCKMODE lockmode, AlterTablePass cur_pass,
496 : AlterTableUtilityContext *context);
497 : static bool check_for_column_name_collision(Relation rel, const char *colname,
498 : bool if_not_exists);
499 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
500 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
501 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
502 : LOCKMODE lockmode);
503 : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
504 : bool is_valid, bool queue_validation);
505 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
506 : char *conName, char *colName,
507 : bool recurse, bool recursing,
508 : LOCKMODE lockmode);
509 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
510 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
511 : List *testConstraint, List *provenConstraint);
512 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
513 : Node *newDefault, LOCKMODE lockmode);
514 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
515 : Node *newDefault);
516 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
517 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
518 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
519 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
520 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
521 : bool recurse, bool recursing);
522 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
523 : Node *newExpr, LOCKMODE lockmode);
524 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
525 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
526 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
527 : Node *newValue, LOCKMODE lockmode);
528 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
529 : Node *options, bool isReset, LOCKMODE lockmode);
530 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
531 : Node *newValue, LOCKMODE lockmode);
532 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
533 : AlterTableCmd *cmd, LOCKMODE lockmode,
534 : AlterTableUtilityContext *context);
535 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
536 : DropBehavior behavior,
537 : bool recurse, bool recursing,
538 : bool missing_ok, LOCKMODE lockmode,
539 : ObjectAddresses *addrs);
540 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
541 : bool recurse, LOCKMODE lockmode,
542 : AlterTableUtilityContext *context);
543 : static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
544 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
545 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
546 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
547 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
548 : static ObjectAddress ATExecAddConstraint(List **wqueue,
549 : AlteredTableInfo *tab, Relation rel,
550 : Constraint *newConstraint, bool recurse, bool is_readd,
551 : LOCKMODE lockmode);
552 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
553 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
554 : IndexStmt *stmt, LOCKMODE lockmode);
555 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
556 : AlteredTableInfo *tab, Relation rel,
557 : Constraint *constr,
558 : bool recurse, bool recursing, bool is_readd,
559 : LOCKMODE lockmode);
560 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
561 : Relation rel, Constraint *fkconstraint,
562 : bool recurse, bool recursing,
563 : LOCKMODE lockmode);
564 : static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
565 : int numfksetcols, int16 *fksetcolsattnums,
566 : List *fksetcols);
567 : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
568 : char *constraintname,
569 : Constraint *fkconstraint, Relation rel,
570 : Relation pkrel, Oid indexOid,
571 : Oid parentConstr,
572 : int numfks, int16 *pkattnum, int16 *fkattnum,
573 : Oid *pfeqoperators, Oid *ppeqoperators,
574 : Oid *ffeqoperators, int numfkdelsetcols,
575 : int16 *fkdelsetcols, bool is_internal,
576 : bool with_period);
577 : static void addFkRecurseReferenced(Constraint *fkconstraint,
578 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
579 : int numfks, int16 *pkattnum, int16 *fkattnum,
580 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
581 : int numfkdelsetcols, int16 *fkdelsetcols,
582 : bool old_check_ok,
583 : Oid parentDelTrigger, Oid parentUpdTrigger,
584 : bool with_period);
585 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
586 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
587 : int numfks, int16 *pkattnum, int16 *fkattnum,
588 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
589 : int numfkdelsetcols, int16 *fkdelsetcols,
590 : bool old_check_ok, LOCKMODE lockmode,
591 : Oid parentInsTrigger, Oid parentUpdTrigger,
592 : bool with_period);
593 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
594 : Relation partitionRel);
595 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
596 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
597 : Relation partRel);
598 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
599 : Constraint *fkconstraint, Oid constraintOid,
600 : Oid indexOid,
601 : Oid parentInsTrigger, Oid parentUpdTrigger,
602 : Oid *insertTrigOid, Oid *updateTrigOid);
603 : static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
604 : Constraint *fkconstraint, Oid constraintOid,
605 : Oid indexOid,
606 : Oid parentDelTrigger, Oid parentUpdTrigger,
607 : Oid *deleteTrigOid, Oid *updateTrigOid);
608 : static bool tryAttachPartitionForeignKey(List **wqueue,
609 : ForeignKeyCacheInfo *fk,
610 : Relation partition,
611 : Oid parentConstrOid, int numfks,
612 : AttrNumber *mapped_conkey, AttrNumber *confkey,
613 : Oid *conpfeqop,
614 : Oid parentInsTrigger,
615 : Oid parentUpdTrigger,
616 : Relation trigrel);
617 : static void AttachPartitionForeignKey(List **wqueue, Relation partition,
618 : Oid partConstrOid, Oid parentConstrOid,
619 : Oid parentInsTrigger, Oid parentUpdTrigger,
620 : Relation trigrel);
621 : static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
622 : Oid conoid, Oid conrelid);
623 : static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
624 : Oid confrelid, Oid conrelid);
625 : static void GetForeignKeyActionTriggers(Relation trigrel,
626 : Oid conoid, Oid confrelid, Oid conrelid,
627 : Oid *deleteTriggerOid,
628 : Oid *updateTriggerOid);
629 : static void GetForeignKeyCheckTriggers(Relation trigrel,
630 : Oid conoid, Oid confrelid, Oid conrelid,
631 : Oid *insertTriggerOid,
632 : Oid *updateTriggerOid);
633 : static void ATExecDropConstraint(Relation rel, const char *constrName,
634 : DropBehavior behavior, bool recurse,
635 : bool missing_ok, LOCKMODE lockmode);
636 : static ObjectAddress dropconstraint_internal(Relation rel,
637 : HeapTuple constraintTup, DropBehavior behavior,
638 : bool recurse, bool recursing,
639 : bool missing_ok, LOCKMODE lockmode);
640 : static void ATPrepAlterColumnType(List **wqueue,
641 : AlteredTableInfo *tab, Relation rel,
642 : bool recurse, bool recursing,
643 : AlterTableCmd *cmd, LOCKMODE lockmode,
644 : AlterTableUtilityContext *context);
645 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
646 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
647 : AlterTableCmd *cmd, LOCKMODE lockmode);
648 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
649 : Relation rel, AttrNumber attnum, const char *colName);
650 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
651 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
652 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
653 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
654 : LOCKMODE lockmode);
655 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
656 : char *cmd, List **wqueue, LOCKMODE lockmode,
657 : bool rewrite);
658 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
659 : Oid objid, Relation rel, List *domname,
660 : const char *conname);
661 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
662 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
663 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
664 : List *options, LOCKMODE lockmode);
665 : static void change_owner_fix_column_acls(Oid relationOid,
666 : Oid oldOwnerId, Oid newOwnerId);
667 : static void change_owner_recurse_to_sequences(Oid relationOid,
668 : Oid newOwnerId, LOCKMODE lockmode);
669 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
670 : LOCKMODE lockmode);
671 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
672 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
673 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
674 : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
675 : bool toLogged);
676 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
677 : const char *tablespacename, LOCKMODE lockmode);
678 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
679 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
680 : static void ATExecSetRelOptions(Relation rel, List *defList,
681 : AlterTableType operation,
682 : LOCKMODE lockmode);
683 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
684 : char fires_when, bool skip_system, bool recurse,
685 : LOCKMODE lockmode);
686 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
687 : char fires_when, LOCKMODE lockmode);
688 : static void ATPrepAddInherit(Relation child_rel);
689 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
690 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
691 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
692 : DependencyType deptype);
693 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
694 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
695 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
696 : static void ATExecGenericOptions(Relation rel, List *options);
697 : static void ATExecSetRowSecurity(Relation rel, bool rls);
698 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
699 : static ObjectAddress ATExecSetCompression(Relation rel,
700 : const char *column, Node *newValue, LOCKMODE lockmode);
701 :
702 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
703 : static const char *storage_name(char c);
704 :
705 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
706 : Oid oldRelOid, void *arg);
707 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
708 : Oid oldrelid, void *arg);
709 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
710 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
711 : List **partexprs, Oid *partopclass, Oid *partcollation,
712 : PartitionStrategy strategy);
713 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
714 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
715 : bool expect_detached);
716 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
717 : PartitionCmd *cmd,
718 : AlterTableUtilityContext *context);
719 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
720 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
721 : List *partConstraint,
722 : bool validate_default);
723 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
724 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
725 : static void DropClonedTriggersFromPartition(Oid partitionId);
726 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
727 : Relation rel, RangeVar *name,
728 : bool concurrent);
729 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
730 : bool concurrent, Oid defaultPartOid);
731 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
732 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
733 : RangeVar *name);
734 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
735 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
736 : Relation partitionTbl);
737 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
738 : static List *GetParentedForeignKeyRefs(Relation partition);
739 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
740 : static char GetAttributeCompression(Oid atttypid, const char *compression);
741 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
742 :
743 :
744 : /* ----------------------------------------------------------------
745 : * DefineRelation
746 : * Creates a new relation.
747 : *
748 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
749 : * The other arguments are used to extend the behavior for other cases:
750 : * relkind: relkind to assign to the new relation
751 : * ownerId: if not InvalidOid, use this as the new relation's owner.
752 : * typaddress: if not null, it's set to the pg_type entry's address.
753 : * queryString: for error reporting
754 : *
755 : * Note that permissions checks are done against current user regardless of
756 : * ownerId. A nonzero ownerId is used when someone is creating a relation
757 : * "on behalf of" someone else, so we still want to see that the current user
758 : * has permissions to do it.
759 : *
760 : * If successful, returns the address of the new relation.
761 : * ----------------------------------------------------------------
762 : */
763 : ObjectAddress
764 63746 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
765 : ObjectAddress *typaddress, const char *queryString)
766 : {
767 : char relname[NAMEDATALEN];
768 : Oid namespaceId;
769 : Oid relationId;
770 : Oid tablespaceId;
771 : Relation rel;
772 : TupleDesc descriptor;
773 : List *inheritOids;
774 : List *old_constraints;
775 : List *old_notnulls;
776 : List *rawDefaults;
777 : List *cookedDefaults;
778 : List *nncols;
779 : Datum reloptions;
780 : ListCell *listptr;
781 : AttrNumber attnum;
782 : bool partitioned;
783 63746 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
784 : Oid ofTypeId;
785 : ObjectAddress address;
786 : LOCKMODE parentLockmode;
787 63746 : Oid accessMethodId = InvalidOid;
788 :
789 : /*
790 : * Truncate relname to appropriate length (probably a waste of time, as
791 : * parser should have done this already).
792 : */
793 63746 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
794 :
795 : /*
796 : * Check consistency of arguments
797 : */
798 63746 : if (stmt->oncommit != ONCOMMIT_NOOP
799 188 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
800 12 : ereport(ERROR,
801 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
802 : errmsg("ON COMMIT can only be used on temporary tables")));
803 :
804 63734 : if (stmt->partspec != NULL)
805 : {
806 5128 : if (relkind != RELKIND_RELATION)
807 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
808 :
809 5128 : relkind = RELKIND_PARTITIONED_TABLE;
810 5128 : partitioned = true;
811 : }
812 : else
813 58606 : partitioned = false;
814 :
815 63734 : if (relkind == RELKIND_PARTITIONED_TABLE &&
816 5128 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
817 6 : ereport(ERROR,
818 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
819 : errmsg("partitioned tables cannot be unlogged")));
820 :
821 : /*
822 : * Look up the namespace in which we are supposed to create the relation,
823 : * check we have permission to create there, lock it against concurrent
824 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
825 : * namespace is selected.
826 : */
827 : namespaceId =
828 63728 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
829 :
830 : /*
831 : * Security check: disallow creating temp tables from security-restricted
832 : * code. This is needed because calling code might not expect untrusted
833 : * tables to appear in pg_temp at the front of its search path.
834 : */
835 63728 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
836 3132 : && InSecurityRestrictedOperation())
837 0 : ereport(ERROR,
838 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
839 : errmsg("cannot create temporary table within security-restricted operation")));
840 :
841 : /*
842 : * Determine the lockmode to use when scanning parents. A self-exclusive
843 : * lock is needed here.
844 : *
845 : * For regular inheritance, if two backends attempt to add children to the
846 : * same parent simultaneously, and that parent has no pre-existing
847 : * children, then both will attempt to update the parent's relhassubclass
848 : * field, leading to a "tuple concurrently updated" error. Also, this
849 : * interlocks against a concurrent ANALYZE on the parent table, which
850 : * might otherwise be attempting to clear the parent's relhassubclass
851 : * field, if its previous children were recently dropped.
852 : *
853 : * If the child table is a partition, then we instead grab an exclusive
854 : * lock on the parent because its partition descriptor will be changed by
855 : * addition of the new partition.
856 : */
857 63728 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
858 : ShareUpdateExclusiveLock);
859 :
860 : /* Determine the list of OIDs of the parents. */
861 63728 : inheritOids = NIL;
862 74264 : foreach(listptr, stmt->inhRelations)
863 : {
864 10536 : RangeVar *rv = (RangeVar *) lfirst(listptr);
865 : Oid parentOid;
866 :
867 10536 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
868 :
869 : /*
870 : * Reject duplications in the list of parents.
871 : */
872 10536 : if (list_member_oid(inheritOids, parentOid))
873 0 : ereport(ERROR,
874 : (errcode(ERRCODE_DUPLICATE_TABLE),
875 : errmsg("relation \"%s\" would be inherited from more than once",
876 : get_rel_name(parentOid))));
877 :
878 10536 : inheritOids = lappend_oid(inheritOids, parentOid);
879 : }
880 :
881 : /*
882 : * Select tablespace to use: an explicitly indicated one, or (in the case
883 : * of a partitioned table) the parent's, if it has one.
884 : */
885 63728 : if (stmt->tablespacename)
886 : {
887 118 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
888 :
889 112 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
890 6 : ereport(ERROR,
891 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
892 : errmsg("cannot specify default tablespace for partitioned relations")));
893 : }
894 63610 : else if (stmt->partbound)
895 : {
896 : Assert(list_length(inheritOids) == 1);
897 7940 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
898 : }
899 : else
900 55670 : tablespaceId = InvalidOid;
901 :
902 : /* still nothing? use the default */
903 63716 : if (!OidIsValid(tablespaceId))
904 63588 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
905 : partitioned);
906 :
907 : /* Check permissions except when using database's default */
908 63710 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
909 : {
910 : AclResult aclresult;
911 :
912 146 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
913 : ACL_CREATE);
914 146 : if (aclresult != ACLCHECK_OK)
915 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
916 6 : get_tablespace_name(tablespaceId));
917 : }
918 :
919 : /* In all cases disallow placing user relations in pg_global */
920 63704 : if (tablespaceId == GLOBALTABLESPACE_OID)
921 18 : ereport(ERROR,
922 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
923 : errmsg("only shared relations can be placed in pg_global tablespace")));
924 :
925 : /* Identify user ID that will own the table */
926 63686 : if (!OidIsValid(ownerId))
927 63402 : ownerId = GetUserId();
928 :
929 : /*
930 : * Parse and validate reloptions, if any.
931 : */
932 63686 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
933 : true, false);
934 :
935 63668 : switch (relkind)
936 : {
937 16666 : case RELKIND_VIEW:
938 16666 : (void) view_reloptions(reloptions, true);
939 16648 : break;
940 5104 : case RELKIND_PARTITIONED_TABLE:
941 5104 : (void) partitioned_table_reloptions(reloptions, true);
942 5098 : break;
943 41898 : default:
944 41898 : (void) heap_reloptions(relkind, reloptions, true);
945 : }
946 :
947 63548 : if (stmt->ofTypename)
948 : {
949 : AclResult aclresult;
950 :
951 98 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
952 :
953 98 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
954 98 : if (aclresult != ACLCHECK_OK)
955 6 : aclcheck_error_type(aclresult, ofTypeId);
956 : }
957 : else
958 63450 : ofTypeId = InvalidOid;
959 :
960 : /*
961 : * Look up inheritance ancestors and generate relation schema, including
962 : * inherited attributes. (Note that stmt->tableElts is destructively
963 : * modified by MergeAttributes.)
964 : */
965 63302 : stmt->tableElts =
966 63542 : MergeAttributes(stmt->tableElts, inheritOids,
967 63542 : stmt->relation->relpersistence,
968 63542 : stmt->partbound != NULL,
969 : &old_constraints, &old_notnulls);
970 :
971 : /*
972 : * Create a tuple descriptor from the relation schema. Note that this
973 : * deals with column names, types, and in-descriptor NOT NULL flags, but
974 : * not default values, NOT NULL or CHECK constraints; we handle those
975 : * below.
976 : */
977 63302 : descriptor = BuildDescForRelation(stmt->tableElts);
978 :
979 : /*
980 : * Find columns with default values and prepare for insertion of the
981 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
982 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
983 : * while raw defaults go into a list of RawColumnDefault structs that will
984 : * be processed by AddRelationNewConstraints. (We can't deal with raw
985 : * expressions until we can do transformExpr.)
986 : */
987 63254 : rawDefaults = NIL;
988 63254 : cookedDefaults = NIL;
989 63254 : attnum = 0;
990 :
991 320798 : foreach(listptr, stmt->tableElts)
992 : {
993 257544 : ColumnDef *colDef = lfirst(listptr);
994 :
995 257544 : attnum++;
996 257544 : if (colDef->raw_default != NULL)
997 : {
998 : RawColumnDefault *rawEnt;
999 :
1000 : Assert(colDef->cooked_default == NULL);
1001 :
1002 3364 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
1003 3364 : rawEnt->attnum = attnum;
1004 3364 : rawEnt->raw_default = colDef->raw_default;
1005 3364 : rawEnt->generated = colDef->generated;
1006 3364 : rawDefaults = lappend(rawDefaults, rawEnt);
1007 : }
1008 254180 : else if (colDef->cooked_default != NULL)
1009 : {
1010 : CookedConstraint *cooked;
1011 :
1012 404 : cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
1013 404 : cooked->contype = CONSTR_DEFAULT;
1014 404 : cooked->conoid = InvalidOid; /* until created */
1015 404 : cooked->name = NULL;
1016 404 : cooked->attnum = attnum;
1017 404 : cooked->expr = colDef->cooked_default;
1018 404 : cooked->is_enforced = true;
1019 404 : cooked->skip_validation = false;
1020 404 : cooked->is_local = true; /* not used for defaults */
1021 404 : cooked->inhcount = 0; /* ditto */
1022 404 : cooked->is_no_inherit = false;
1023 404 : cookedDefaults = lappend(cookedDefaults, cooked);
1024 : }
1025 : }
1026 :
1027 : /*
1028 : * For relations with table AM and partitioned tables, select access
1029 : * method to use: an explicitly indicated one, or (in the case of a
1030 : * partitioned table) the parent's, if it has one.
1031 : */
1032 63254 : if (stmt->accessMethod != NULL)
1033 : {
1034 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1035 122 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1036 : }
1037 63132 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1038 : {
1039 39600 : if (stmt->partbound)
1040 : {
1041 : Assert(list_length(inheritOids) == 1);
1042 7758 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1043 : }
1044 :
1045 39600 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1046 34488 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1047 : }
1048 :
1049 : /*
1050 : * Create the relation. Inherited defaults and CHECK constraints are
1051 : * passed in for immediate handling --- since they don't need parsing,
1052 : * they can be stored immediately.
1053 : */
1054 63236 : relationId = heap_create_with_catalog(relname,
1055 : namespaceId,
1056 : tablespaceId,
1057 : InvalidOid,
1058 : InvalidOid,
1059 : ofTypeId,
1060 : ownerId,
1061 : accessMethodId,
1062 : descriptor,
1063 : list_concat(cookedDefaults,
1064 : old_constraints),
1065 : relkind,
1066 63236 : stmt->relation->relpersistence,
1067 : false,
1068 : false,
1069 : stmt->oncommit,
1070 : reloptions,
1071 : true,
1072 : allowSystemTableMods,
1073 : false,
1074 : InvalidOid,
1075 : typaddress);
1076 :
1077 : /*
1078 : * We must bump the command counter to make the newly-created relation
1079 : * tuple visible for opening.
1080 : */
1081 63188 : CommandCounterIncrement();
1082 :
1083 : /*
1084 : * Open the new relation and acquire exclusive lock on it. This isn't
1085 : * really necessary for locking out other backends (since they can't see
1086 : * the new rel anyway until we commit), but it keeps the lock manager from
1087 : * complaining about deadlock risks.
1088 : */
1089 63188 : rel = relation_open(relationId, AccessExclusiveLock);
1090 :
1091 : /*
1092 : * Now add any newly specified column default and generation expressions
1093 : * to the new relation. These are passed to us in the form of raw
1094 : * parsetrees; we need to transform them to executable expression trees
1095 : * before they can be added. The most convenient way to do that is to
1096 : * apply the parser's transformExpr routine, but transformExpr doesn't
1097 : * work unless we have a pre-existing relation. So, the transformation has
1098 : * to be postponed to this final step of CREATE TABLE.
1099 : *
1100 : * This needs to be before processing the partitioning clauses because
1101 : * those could refer to generated columns.
1102 : */
1103 63188 : if (rawDefaults)
1104 2840 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1105 : true, true, false, queryString);
1106 :
1107 : /*
1108 : * Make column generation expressions visible for use by partitioning.
1109 : */
1110 62996 : CommandCounterIncrement();
1111 :
1112 : /* Process and store partition bound, if any. */
1113 62996 : if (stmt->partbound)
1114 : {
1115 : PartitionBoundSpec *bound;
1116 : ParseState *pstate;
1117 7862 : Oid parentId = linitial_oid(inheritOids),
1118 : defaultPartOid;
1119 : Relation parent,
1120 7862 : defaultRel = NULL;
1121 : ParseNamespaceItem *nsitem;
1122 :
1123 : /* Already have strong enough lock on the parent */
1124 7862 : parent = table_open(parentId, NoLock);
1125 :
1126 : /*
1127 : * We are going to try to validate the partition bound specification
1128 : * against the partition key of parentRel, so it better have one.
1129 : */
1130 7862 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1131 18 : ereport(ERROR,
1132 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1133 : errmsg("\"%s\" is not partitioned",
1134 : RelationGetRelationName(parent))));
1135 :
1136 : /*
1137 : * The partition constraint of the default partition depends on the
1138 : * partition bounds of every other partition. It is possible that
1139 : * another backend might be about to execute a query on the default
1140 : * partition table, and that the query relies on previously cached
1141 : * default partition constraints. We must therefore take a table lock
1142 : * strong enough to prevent all queries on the default partition from
1143 : * proceeding until we commit and send out a shared-cache-inval notice
1144 : * that will make them update their index lists.
1145 : *
1146 : * Order of locking: The relation being added won't be visible to
1147 : * other backends until it is committed, hence here in
1148 : * DefineRelation() the order of locking the default partition and the
1149 : * relation being added does not matter. But at all other places we
1150 : * need to lock the default relation before we lock the relation being
1151 : * added or removed i.e. we should take the lock in same order at all
1152 : * the places such that lock parent, lock default partition and then
1153 : * lock the partition so as to avoid a deadlock.
1154 : */
1155 : defaultPartOid =
1156 7844 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1157 : true));
1158 7844 : if (OidIsValid(defaultPartOid))
1159 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1160 :
1161 : /* Transform the bound values */
1162 7844 : pstate = make_parsestate(NULL);
1163 7844 : pstate->p_sourcetext = queryString;
1164 :
1165 : /*
1166 : * Add an nsitem containing this relation, so that transformExpr
1167 : * called on partition bound expressions is able to report errors
1168 : * using a proper context.
1169 : */
1170 7844 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1171 : NULL, false, false);
1172 7844 : addNSItemToQuery(pstate, nsitem, false, true, true);
1173 :
1174 7844 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1175 :
1176 : /*
1177 : * Check first that the new partition's bound is valid and does not
1178 : * overlap with any of existing partitions of the parent.
1179 : */
1180 7640 : check_new_partition_bound(relname, parent, bound, pstate);
1181 :
1182 : /*
1183 : * If the default partition exists, its partition constraints will
1184 : * change after the addition of this new partition such that it won't
1185 : * allow any row that qualifies for this new partition. So, check that
1186 : * the existing data in the default partition satisfies the constraint
1187 : * as it will exist after adding this partition.
1188 : */
1189 7526 : if (OidIsValid(defaultPartOid))
1190 : {
1191 348 : check_default_partition_contents(parent, defaultRel, bound);
1192 : /* Keep the lock until commit. */
1193 330 : table_close(defaultRel, NoLock);
1194 : }
1195 :
1196 : /* Update the pg_class entry. */
1197 7508 : StorePartitionBound(rel, parent, bound);
1198 :
1199 7508 : table_close(parent, NoLock);
1200 : }
1201 :
1202 : /* Store inheritance information for new rel. */
1203 62642 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1204 :
1205 : /*
1206 : * Process the partitioning specification (if any) and store the partition
1207 : * key information into the catalog.
1208 : */
1209 62642 : if (partitioned)
1210 : {
1211 : ParseState *pstate;
1212 : int partnatts;
1213 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1214 : Oid partopclass[PARTITION_MAX_KEYS];
1215 : Oid partcollation[PARTITION_MAX_KEYS];
1216 5098 : List *partexprs = NIL;
1217 :
1218 5098 : pstate = make_parsestate(NULL);
1219 5098 : pstate->p_sourcetext = queryString;
1220 :
1221 5098 : partnatts = list_length(stmt->partspec->partParams);
1222 :
1223 : /* Protect fixed-size arrays here and in executor */
1224 5098 : if (partnatts > PARTITION_MAX_KEYS)
1225 0 : ereport(ERROR,
1226 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1227 : errmsg("cannot partition using more than %d columns",
1228 : PARTITION_MAX_KEYS)));
1229 :
1230 : /*
1231 : * We need to transform the raw parsetrees corresponding to partition
1232 : * expressions into executable expression trees. Like column defaults
1233 : * and CHECK constraints, we could not have done the transformation
1234 : * earlier.
1235 : */
1236 5098 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1237 :
1238 5068 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1239 : partattrs, &partexprs, partopclass,
1240 5068 : partcollation, stmt->partspec->strategy);
1241 :
1242 4972 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1243 : partexprs,
1244 : partopclass, partcollation);
1245 :
1246 : /* make it all visible */
1247 4972 : CommandCounterIncrement();
1248 : }
1249 :
1250 : /*
1251 : * If we're creating a partition, create now all the indexes, triggers,
1252 : * FKs defined in the parent.
1253 : *
1254 : * We can't do it earlier, because DefineIndex wants to know the partition
1255 : * key which we just stored.
1256 : */
1257 62516 : if (stmt->partbound)
1258 : {
1259 7502 : Oid parentId = linitial_oid(inheritOids);
1260 : Relation parent;
1261 : List *idxlist;
1262 : ListCell *cell;
1263 :
1264 : /* Already have strong enough lock on the parent */
1265 7502 : parent = table_open(parentId, NoLock);
1266 7502 : idxlist = RelationGetIndexList(parent);
1267 :
1268 : /*
1269 : * For each index in the parent table, create one in the partition
1270 : */
1271 8910 : foreach(cell, idxlist)
1272 : {
1273 1426 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1274 : AttrMap *attmap;
1275 : IndexStmt *idxstmt;
1276 : Oid constraintOid;
1277 :
1278 1426 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1279 : {
1280 36 : if (idxRel->rd_index->indisunique)
1281 12 : ereport(ERROR,
1282 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1283 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1284 : RelationGetRelationName(parent)),
1285 : errdetail("Table \"%s\" contains indexes that are unique.",
1286 : RelationGetRelationName(parent))));
1287 : else
1288 : {
1289 24 : index_close(idxRel, AccessShareLock);
1290 24 : continue;
1291 : }
1292 : }
1293 :
1294 1390 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1295 : RelationGetDescr(parent),
1296 : false);
1297 : idxstmt =
1298 1390 : generateClonedIndexStmt(NULL, idxRel,
1299 : attmap, &constraintOid);
1300 1390 : DefineIndex(RelationGetRelid(rel),
1301 : idxstmt,
1302 : InvalidOid,
1303 : RelationGetRelid(idxRel),
1304 : constraintOid,
1305 : -1,
1306 : false, false, false, false, false);
1307 :
1308 1384 : index_close(idxRel, AccessShareLock);
1309 : }
1310 :
1311 7484 : list_free(idxlist);
1312 :
1313 : /*
1314 : * If there are any row-level triggers, clone them to the new
1315 : * partition.
1316 : */
1317 7484 : if (parent->trigdesc != NULL)
1318 432 : CloneRowTriggersToPartition(parent, rel);
1319 :
1320 : /*
1321 : * And foreign keys too. Note that because we're freshly creating the
1322 : * table, there is no need to verify these new constraints.
1323 : */
1324 7484 : CloneForeignKeyConstraints(NULL, parent, rel);
1325 :
1326 7484 : table_close(parent, NoLock);
1327 : }
1328 :
1329 : /*
1330 : * Now add any newly specified CHECK constraints to the new relation. Same
1331 : * as for defaults above, but these need to come after partitioning is set
1332 : * up.
1333 : */
1334 62498 : if (stmt->constraints)
1335 784 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1336 : true, true, false, queryString);
1337 :
1338 : /*
1339 : * Finally, merge the not-null constraints that are declared directly with
1340 : * those that come from parent relations (making sure to count inheritance
1341 : * appropriately for each), create them, and set the attnotnull flag on
1342 : * columns that don't yet have it.
1343 : */
1344 62468 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1345 : old_notnulls);
1346 140312 : foreach_int(attrnum, nncols)
1347 15532 : set_attnotnull(NULL, rel, attrnum, true, false);
1348 :
1349 62390 : ObjectAddressSet(address, RelationRelationId, relationId);
1350 :
1351 : /*
1352 : * Clean up. We keep lock on new relation (although it shouldn't be
1353 : * visible to anyone else anyway, until commit).
1354 : */
1355 62390 : relation_close(rel, NoLock);
1356 :
1357 62390 : return address;
1358 : }
1359 :
1360 : /*
1361 : * BuildDescForRelation
1362 : *
1363 : * Given a list of ColumnDef nodes, build a TupleDesc.
1364 : *
1365 : * Note: This is only for the limited purpose of table and view creation. Not
1366 : * everything is filled in. A real tuple descriptor should be obtained from
1367 : * the relcache.
1368 : */
1369 : TupleDesc
1370 66270 : BuildDescForRelation(const List *columns)
1371 : {
1372 : int natts;
1373 : AttrNumber attnum;
1374 : ListCell *l;
1375 : TupleDesc desc;
1376 : char *attname;
1377 : Oid atttypid;
1378 : int32 atttypmod;
1379 : Oid attcollation;
1380 : int attdim;
1381 :
1382 : /*
1383 : * allocate a new tuple descriptor
1384 : */
1385 66270 : natts = list_length(columns);
1386 66270 : desc = CreateTemplateTupleDesc(natts);
1387 :
1388 66270 : attnum = 0;
1389 :
1390 327046 : foreach(l, columns)
1391 : {
1392 260836 : ColumnDef *entry = lfirst(l);
1393 : AclResult aclresult;
1394 : Form_pg_attribute att;
1395 :
1396 : /*
1397 : * for each entry in the list, get the name and type information from
1398 : * the list and have TupleDescInitEntry fill in the attribute
1399 : * information we need.
1400 : */
1401 260836 : attnum++;
1402 :
1403 260836 : attname = entry->colname;
1404 260836 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1405 :
1406 260836 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1407 260836 : if (aclresult != ACLCHECK_OK)
1408 42 : aclcheck_error_type(aclresult, atttypid);
1409 :
1410 260794 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1411 260794 : attdim = list_length(entry->typeName->arrayBounds);
1412 260794 : if (attdim > PG_INT16_MAX)
1413 0 : ereport(ERROR,
1414 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1415 : errmsg("too many array dimensions"));
1416 :
1417 260794 : if (entry->typeName->setof)
1418 0 : ereport(ERROR,
1419 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1420 : errmsg("column \"%s\" cannot be declared SETOF",
1421 : attname)));
1422 :
1423 260794 : TupleDescInitEntry(desc, attnum, attname,
1424 : atttypid, atttypmod, attdim);
1425 260794 : att = TupleDescAttr(desc, attnum - 1);
1426 :
1427 : /* Override TupleDescInitEntry's settings as requested */
1428 260794 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1429 :
1430 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1431 260794 : att->attnotnull = entry->is_not_null;
1432 260794 : att->attislocal = entry->is_local;
1433 260794 : att->attinhcount = entry->inhcount;
1434 260794 : att->attidentity = entry->identity;
1435 260794 : att->attgenerated = entry->generated;
1436 260794 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1437 260782 : if (entry->storage)
1438 20162 : att->attstorage = entry->storage;
1439 240620 : else if (entry->storage_name)
1440 20 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1441 :
1442 260776 : populate_compact_attribute(desc, attnum - 1);
1443 : }
1444 :
1445 66210 : return desc;
1446 : }
1447 :
1448 : /*
1449 : * Emit the right error or warning message for a "DROP" command issued on a
1450 : * non-existent relation
1451 : */
1452 : static void
1453 1084 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1454 : {
1455 : const struct dropmsgstrings *rentry;
1456 :
1457 1204 : if (rel->schemaname != NULL &&
1458 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1459 : {
1460 42 : if (!missing_ok)
1461 : {
1462 0 : ereport(ERROR,
1463 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1464 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1465 : }
1466 : else
1467 : {
1468 42 : ereport(NOTICE,
1469 : (errmsg("schema \"%s\" does not exist, skipping",
1470 : rel->schemaname)));
1471 : }
1472 42 : return;
1473 : }
1474 :
1475 1362 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1476 : {
1477 1362 : if (rentry->kind == rightkind)
1478 : {
1479 1042 : if (!missing_ok)
1480 : {
1481 138 : ereport(ERROR,
1482 : (errcode(rentry->nonexistent_code),
1483 : errmsg(rentry->nonexistent_msg, rel->relname)));
1484 : }
1485 : else
1486 : {
1487 904 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1488 904 : break;
1489 : }
1490 : }
1491 : }
1492 :
1493 : Assert(rentry->kind != '\0'); /* Should be impossible */
1494 : }
1495 :
1496 : /*
1497 : * Emit the right error message for a "DROP" command issued on a
1498 : * relation of the wrong type
1499 : */
1500 : static void
1501 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1502 : {
1503 : const struct dropmsgstrings *rentry;
1504 : const struct dropmsgstrings *wentry;
1505 :
1506 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1507 0 : if (rentry->kind == rightkind)
1508 0 : break;
1509 : Assert(rentry->kind != '\0');
1510 :
1511 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1512 0 : if (wentry->kind == wrongkind)
1513 0 : break;
1514 : /* wrongkind could be something we don't have in our table... */
1515 :
1516 0 : ereport(ERROR,
1517 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1518 : errmsg(rentry->nota_msg, relname),
1519 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1520 : }
1521 :
1522 : /*
1523 : * RemoveRelations
1524 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1525 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1526 : */
1527 : void
1528 17088 : RemoveRelations(DropStmt *drop)
1529 : {
1530 : ObjectAddresses *objects;
1531 : char relkind;
1532 : ListCell *cell;
1533 17088 : int flags = 0;
1534 17088 : LOCKMODE lockmode = AccessExclusiveLock;
1535 :
1536 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1537 17088 : if (drop->concurrent)
1538 : {
1539 : /*
1540 : * Note that for temporary relations this lock may get upgraded later
1541 : * on, but as no other session can access a temporary relation, this
1542 : * is actually fine.
1543 : */
1544 164 : lockmode = ShareUpdateExclusiveLock;
1545 : Assert(drop->removeType == OBJECT_INDEX);
1546 164 : if (list_length(drop->objects) != 1)
1547 6 : ereport(ERROR,
1548 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1549 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1550 158 : if (drop->behavior == DROP_CASCADE)
1551 0 : ereport(ERROR,
1552 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1553 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1554 : }
1555 :
1556 : /*
1557 : * First we identify all the relations, then we delete them in a single
1558 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1559 : * RESTRICT errors if one of the relations depends on another.
1560 : */
1561 :
1562 : /* Determine required relkind */
1563 17082 : switch (drop->removeType)
1564 : {
1565 14842 : case OBJECT_TABLE:
1566 14842 : relkind = RELKIND_RELATION;
1567 14842 : break;
1568 :
1569 846 : case OBJECT_INDEX:
1570 846 : relkind = RELKIND_INDEX;
1571 846 : break;
1572 :
1573 170 : case OBJECT_SEQUENCE:
1574 170 : relkind = RELKIND_SEQUENCE;
1575 170 : break;
1576 :
1577 936 : case OBJECT_VIEW:
1578 936 : relkind = RELKIND_VIEW;
1579 936 : break;
1580 :
1581 126 : case OBJECT_MATVIEW:
1582 126 : relkind = RELKIND_MATVIEW;
1583 126 : break;
1584 :
1585 162 : case OBJECT_FOREIGN_TABLE:
1586 162 : relkind = RELKIND_FOREIGN_TABLE;
1587 162 : break;
1588 :
1589 0 : default:
1590 0 : elog(ERROR, "unrecognized drop object type: %d",
1591 : (int) drop->removeType);
1592 : relkind = 0; /* keep compiler quiet */
1593 : break;
1594 : }
1595 :
1596 : /* Lock and validate each relation; build a list of object addresses */
1597 17082 : objects = new_object_addresses();
1598 :
1599 38090 : foreach(cell, drop->objects)
1600 : {
1601 21172 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1602 : Oid relOid;
1603 : ObjectAddress obj;
1604 : struct DropRelationCallbackState state;
1605 :
1606 : /*
1607 : * These next few steps are a great deal like relation_openrv, but we
1608 : * don't bother building a relcache entry since we don't need it.
1609 : *
1610 : * Check for shared-cache-inval messages before trying to access the
1611 : * relation. This is needed to cover the case where the name
1612 : * identifies a rel that has been dropped and recreated since the
1613 : * start of our transaction: if we don't flush the old syscache entry,
1614 : * then we'll latch onto that entry and suffer an error later.
1615 : */
1616 21172 : AcceptInvalidationMessages();
1617 :
1618 : /* Look up the appropriate relation using namespace search. */
1619 21172 : state.expected_relkind = relkind;
1620 42344 : state.heap_lockmode = drop->concurrent ?
1621 21172 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1622 : /* We must initialize these fields to show that no locks are held: */
1623 21172 : state.heapOid = InvalidOid;
1624 21172 : state.partParentOid = InvalidOid;
1625 :
1626 21172 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1627 : RangeVarCallbackForDropRelation,
1628 : &state);
1629 :
1630 : /* Not there? */
1631 21152 : if (!OidIsValid(relOid))
1632 : {
1633 1084 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1634 946 : continue;
1635 : }
1636 :
1637 : /*
1638 : * Decide if concurrent mode needs to be used here or not. The
1639 : * callback retrieved the rel's persistence for us.
1640 : */
1641 20068 : if (drop->concurrent &&
1642 152 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1643 : {
1644 : Assert(list_length(drop->objects) == 1 &&
1645 : drop->removeType == OBJECT_INDEX);
1646 134 : flags |= PERFORM_DELETION_CONCURRENTLY;
1647 : }
1648 :
1649 : /*
1650 : * Concurrent index drop cannot be used with partitioned indexes,
1651 : * either.
1652 : */
1653 20068 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1654 134 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1655 6 : ereport(ERROR,
1656 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1657 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1658 : rel->relname)));
1659 :
1660 : /*
1661 : * If we're told to drop a partitioned index, we must acquire lock on
1662 : * all the children of its parent partitioned table before proceeding.
1663 : * Otherwise we'd try to lock the child index partitions before their
1664 : * tables, leading to potential deadlock against other sessions that
1665 : * will lock those objects in the other order.
1666 : */
1667 20062 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1668 76 : (void) find_all_inheritors(state.heapOid,
1669 : state.heap_lockmode,
1670 : NULL);
1671 :
1672 : /* OK, we're ready to delete this one */
1673 20062 : obj.classId = RelationRelationId;
1674 20062 : obj.objectId = relOid;
1675 20062 : obj.objectSubId = 0;
1676 :
1677 20062 : add_exact_object_address(&obj, objects);
1678 : }
1679 :
1680 16918 : performMultipleDeletions(objects, drop->behavior, flags);
1681 :
1682 16776 : free_object_addresses(objects);
1683 16776 : }
1684 :
1685 : /*
1686 : * Before acquiring a table lock, check whether we have sufficient rights.
1687 : * In the case of DROP INDEX, also try to lock the table before the index.
1688 : * Also, if the table to be dropped is a partition, we try to lock the parent
1689 : * first.
1690 : */
1691 : static void
1692 21594 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1693 : void *arg)
1694 : {
1695 : HeapTuple tuple;
1696 : struct DropRelationCallbackState *state;
1697 : char expected_relkind;
1698 : bool is_partition;
1699 : Form_pg_class classform;
1700 : LOCKMODE heap_lockmode;
1701 21594 : bool invalid_system_index = false;
1702 :
1703 21594 : state = (struct DropRelationCallbackState *) arg;
1704 21594 : heap_lockmode = state->heap_lockmode;
1705 :
1706 : /*
1707 : * If we previously locked some other index's heap, and the name we're
1708 : * looking up no longer refers to that relation, release the now-useless
1709 : * lock.
1710 : */
1711 21594 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1712 : {
1713 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1714 0 : state->heapOid = InvalidOid;
1715 : }
1716 :
1717 : /*
1718 : * Similarly, if we previously locked some other partition's heap, and the
1719 : * name we're looking up no longer refers to that relation, release the
1720 : * now-useless lock.
1721 : */
1722 21594 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1723 : {
1724 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1725 0 : state->partParentOid = InvalidOid;
1726 : }
1727 :
1728 : /* Didn't find a relation, so no need for locking or permission checks. */
1729 21594 : if (!OidIsValid(relOid))
1730 1090 : return;
1731 :
1732 20504 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1733 20504 : if (!HeapTupleIsValid(tuple))
1734 0 : return; /* concurrently dropped, so nothing to do */
1735 20504 : classform = (Form_pg_class) GETSTRUCT(tuple);
1736 20504 : is_partition = classform->relispartition;
1737 :
1738 : /* Pass back some data to save lookups in RemoveRelations */
1739 20504 : state->actual_relkind = classform->relkind;
1740 20504 : state->actual_relpersistence = classform->relpersistence;
1741 :
1742 : /*
1743 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1744 : * but RemoveRelations() can only pass one relkind for a given relation.
1745 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1746 : * That means we must be careful before giving the wrong type error when
1747 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1748 : * exists with indexes.
1749 : */
1750 20504 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1751 2942 : expected_relkind = RELKIND_RELATION;
1752 17562 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1753 90 : expected_relkind = RELKIND_INDEX;
1754 : else
1755 17472 : expected_relkind = classform->relkind;
1756 :
1757 20504 : if (state->expected_relkind != expected_relkind)
1758 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1759 0 : state->expected_relkind);
1760 :
1761 : /* Allow DROP to either table owner or schema owner */
1762 20504 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1763 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1764 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1765 18 : get_relkind_objtype(classform->relkind),
1766 18 : rel->relname);
1767 :
1768 : /*
1769 : * Check the case of a system index that might have been invalidated by a
1770 : * failed concurrent process and allow its drop. For the time being, this
1771 : * only concerns indexes of toast relations that became invalid during a
1772 : * REINDEX CONCURRENTLY process.
1773 : */
1774 20486 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1775 : {
1776 : HeapTuple locTuple;
1777 : Form_pg_index indexform;
1778 : bool indisvalid;
1779 :
1780 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1781 0 : if (!HeapTupleIsValid(locTuple))
1782 : {
1783 0 : ReleaseSysCache(tuple);
1784 0 : return;
1785 : }
1786 :
1787 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1788 0 : indisvalid = indexform->indisvalid;
1789 0 : ReleaseSysCache(locTuple);
1790 :
1791 : /* Mark object as being an invalid index of system catalogs */
1792 0 : if (!indisvalid)
1793 0 : invalid_system_index = true;
1794 : }
1795 :
1796 : /* In the case of an invalid index, it is fine to bypass this check */
1797 20486 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1798 2 : ereport(ERROR,
1799 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1800 : errmsg("permission denied: \"%s\" is a system catalog",
1801 : rel->relname)));
1802 :
1803 20484 : ReleaseSysCache(tuple);
1804 :
1805 : /*
1806 : * In DROP INDEX, attempt to acquire lock on the parent table before
1807 : * locking the index. index_drop() will need this anyway, and since
1808 : * regular queries lock tables before their indexes, we risk deadlock if
1809 : * we do it the other way around. No error if we don't find a pg_index
1810 : * entry, though --- the relation may have been dropped. Note that this
1811 : * code will execute for either plain or partitioned indexes.
1812 : */
1813 20484 : if (expected_relkind == RELKIND_INDEX &&
1814 : relOid != oldRelOid)
1815 : {
1816 834 : state->heapOid = IndexGetRelation(relOid, true);
1817 834 : if (OidIsValid(state->heapOid))
1818 834 : LockRelationOid(state->heapOid, heap_lockmode);
1819 : }
1820 :
1821 : /*
1822 : * Similarly, if the relation is a partition, we must acquire lock on its
1823 : * parent before locking the partition. That's because queries lock the
1824 : * parent before its partitions, so we risk deadlock if we do it the other
1825 : * way around.
1826 : */
1827 20484 : if (is_partition && relOid != oldRelOid)
1828 : {
1829 612 : state->partParentOid = get_partition_parent(relOid, true);
1830 612 : if (OidIsValid(state->partParentOid))
1831 612 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1832 : }
1833 : }
1834 :
1835 : /*
1836 : * ExecuteTruncate
1837 : * Executes a TRUNCATE command.
1838 : *
1839 : * This is a multi-relation truncate. We first open and grab exclusive
1840 : * lock on all relations involved, checking permissions and otherwise
1841 : * verifying that the relation is OK for truncation. Note that if relations
1842 : * are foreign tables, at this stage, we have not yet checked that their
1843 : * foreign data in external data sources are OK for truncation. These are
1844 : * checked when foreign data are actually truncated later. In CASCADE mode,
1845 : * relations having FK references to the targeted relations are automatically
1846 : * added to the group; in RESTRICT mode, we check that all FK references are
1847 : * internal to the group that's being truncated. Finally all the relations
1848 : * are truncated and reindexed.
1849 : */
1850 : void
1851 2932 : ExecuteTruncate(TruncateStmt *stmt)
1852 : {
1853 2932 : List *rels = NIL;
1854 2932 : List *relids = NIL;
1855 2932 : List *relids_logged = NIL;
1856 : ListCell *cell;
1857 :
1858 : /*
1859 : * Open, exclusive-lock, and check all the explicitly-specified relations
1860 : */
1861 6076 : foreach(cell, stmt->relations)
1862 : {
1863 3200 : RangeVar *rv = lfirst(cell);
1864 : Relation rel;
1865 3200 : bool recurse = rv->inh;
1866 : Oid myrelid;
1867 3200 : LOCKMODE lockmode = AccessExclusiveLock;
1868 :
1869 3200 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1870 : 0, RangeVarCallbackForTruncate,
1871 : NULL);
1872 :
1873 : /* don't throw error for "TRUNCATE foo, foo" */
1874 3162 : if (list_member_oid(relids, myrelid))
1875 2 : continue;
1876 :
1877 : /* open the relation, we already hold a lock on it */
1878 3160 : rel = table_open(myrelid, NoLock);
1879 :
1880 : /*
1881 : * RangeVarGetRelidExtended() has done most checks with its callback,
1882 : * but other checks with the now-opened Relation remain.
1883 : */
1884 3160 : truncate_check_activity(rel);
1885 :
1886 3154 : rels = lappend(rels, rel);
1887 3154 : relids = lappend_oid(relids, myrelid);
1888 :
1889 : /* Log this relation only if needed for logical decoding */
1890 3154 : if (RelationIsLogicallyLogged(rel))
1891 68 : relids_logged = lappend_oid(relids_logged, myrelid);
1892 :
1893 3154 : if (recurse)
1894 : {
1895 : ListCell *child;
1896 : List *children;
1897 :
1898 1836 : children = find_all_inheritors(myrelid, lockmode, NULL);
1899 :
1900 5476 : foreach(child, children)
1901 : {
1902 3640 : Oid childrelid = lfirst_oid(child);
1903 :
1904 3640 : if (list_member_oid(relids, childrelid))
1905 1836 : continue;
1906 :
1907 : /* find_all_inheritors already got lock */
1908 1804 : rel = table_open(childrelid, NoLock);
1909 :
1910 : /*
1911 : * It is possible that the parent table has children that are
1912 : * temp tables of other backends. We cannot safely access
1913 : * such tables (because of buffering issues), and the best
1914 : * thing to do is to silently ignore them. Note that this
1915 : * check is the same as one of the checks done in
1916 : * truncate_check_activity() called below, still it is kept
1917 : * here for simplicity.
1918 : */
1919 1804 : if (RELATION_IS_OTHER_TEMP(rel))
1920 : {
1921 8 : table_close(rel, lockmode);
1922 8 : continue;
1923 : }
1924 :
1925 : /*
1926 : * Inherited TRUNCATE commands perform access permission
1927 : * checks on the parent table only. So we skip checking the
1928 : * children's permissions and don't call
1929 : * truncate_check_perms() here.
1930 : */
1931 1796 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1932 1796 : truncate_check_activity(rel);
1933 :
1934 1796 : rels = lappend(rels, rel);
1935 1796 : relids = lappend_oid(relids, childrelid);
1936 :
1937 : /* Log this relation only if needed for logical decoding */
1938 1796 : if (RelationIsLogicallyLogged(rel))
1939 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1940 : }
1941 : }
1942 1318 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1943 12 : ereport(ERROR,
1944 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1945 : errmsg("cannot truncate only a partitioned table"),
1946 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1947 : }
1948 :
1949 2876 : ExecuteTruncateGuts(rels, relids, relids_logged,
1950 2876 : stmt->behavior, stmt->restart_seqs, false);
1951 :
1952 : /* And close the rels */
1953 7578 : foreach(cell, rels)
1954 : {
1955 4784 : Relation rel = (Relation) lfirst(cell);
1956 :
1957 4784 : table_close(rel, NoLock);
1958 : }
1959 2794 : }
1960 :
1961 : /*
1962 : * ExecuteTruncateGuts
1963 : *
1964 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1965 : * command (see above) as well as replication subscribers that execute a
1966 : * replicated TRUNCATE action.
1967 : *
1968 : * explicit_rels is the list of Relations to truncate that the command
1969 : * specified. relids is the list of Oids corresponding to explicit_rels.
1970 : * relids_logged is the list of Oids (a subset of relids) that require
1971 : * WAL-logging. This is all a bit redundant, but the existing callers have
1972 : * this information handy in this form.
1973 : */
1974 : void
1975 2900 : ExecuteTruncateGuts(List *explicit_rels,
1976 : List *relids,
1977 : List *relids_logged,
1978 : DropBehavior behavior, bool restart_seqs,
1979 : bool run_as_table_owner)
1980 : {
1981 : List *rels;
1982 2900 : List *seq_relids = NIL;
1983 2900 : HTAB *ft_htab = NULL;
1984 : EState *estate;
1985 : ResultRelInfo *resultRelInfos;
1986 : ResultRelInfo *resultRelInfo;
1987 : SubTransactionId mySubid;
1988 : ListCell *cell;
1989 : Oid *logrelids;
1990 :
1991 : /*
1992 : * Check the explicitly-specified relations.
1993 : *
1994 : * In CASCADE mode, suck in all referencing relations as well. This
1995 : * requires multiple iterations to find indirectly-dependent relations. At
1996 : * each phase, we need to exclusive-lock new rels before looking for their
1997 : * dependencies, else we might miss something. Also, we check each rel as
1998 : * soon as we open it, to avoid a faux pas such as holding lock for a long
1999 : * time on a rel we have no permissions for.
2000 : */
2001 2900 : rels = list_copy(explicit_rels);
2002 2900 : if (behavior == DROP_CASCADE)
2003 : {
2004 : for (;;)
2005 40 : {
2006 : List *newrelids;
2007 :
2008 80 : newrelids = heap_truncate_find_FKs(relids);
2009 80 : if (newrelids == NIL)
2010 40 : break; /* nothing else to add */
2011 :
2012 134 : foreach(cell, newrelids)
2013 : {
2014 94 : Oid relid = lfirst_oid(cell);
2015 : Relation rel;
2016 :
2017 94 : rel = table_open(relid, AccessExclusiveLock);
2018 94 : ereport(NOTICE,
2019 : (errmsg("truncate cascades to table \"%s\"",
2020 : RelationGetRelationName(rel))));
2021 94 : truncate_check_rel(relid, rel->rd_rel);
2022 94 : truncate_check_perms(relid, rel->rd_rel);
2023 94 : truncate_check_activity(rel);
2024 94 : rels = lappend(rels, rel);
2025 94 : relids = lappend_oid(relids, relid);
2026 :
2027 : /* Log this relation only if needed for logical decoding */
2028 94 : if (RelationIsLogicallyLogged(rel))
2029 0 : relids_logged = lappend_oid(relids_logged, relid);
2030 : }
2031 : }
2032 : }
2033 :
2034 : /*
2035 : * Check foreign key references. In CASCADE mode, this should be
2036 : * unnecessary since we just pulled in all the references; but as a
2037 : * cross-check, do it anyway if in an Assert-enabled build.
2038 : */
2039 : #ifdef USE_ASSERT_CHECKING
2040 : heap_truncate_check_FKs(rels, false);
2041 : #else
2042 2900 : if (behavior == DROP_RESTRICT)
2043 2860 : heap_truncate_check_FKs(rels, false);
2044 : #endif
2045 :
2046 : /*
2047 : * If we are asked to restart sequences, find all the sequences, lock them
2048 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2049 : * We want to do this early since it's pointless to do all the truncation
2050 : * work only to fail on sequence permissions.
2051 : */
2052 2826 : if (restart_seqs)
2053 : {
2054 48 : foreach(cell, rels)
2055 : {
2056 24 : Relation rel = (Relation) lfirst(cell);
2057 24 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2058 : ListCell *seqcell;
2059 :
2060 58 : foreach(seqcell, seqlist)
2061 : {
2062 34 : Oid seq_relid = lfirst_oid(seqcell);
2063 : Relation seq_rel;
2064 :
2065 34 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2066 :
2067 : /* This check must match AlterSequence! */
2068 34 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2069 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2070 0 : RelationGetRelationName(seq_rel));
2071 :
2072 34 : seq_relids = lappend_oid(seq_relids, seq_relid);
2073 :
2074 34 : relation_close(seq_rel, NoLock);
2075 : }
2076 : }
2077 : }
2078 :
2079 : /* Prepare to catch AFTER triggers. */
2080 2826 : AfterTriggerBeginQuery();
2081 :
2082 : /*
2083 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2084 : * each relation. We don't need to call ExecOpenIndices, though.
2085 : *
2086 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2087 : * though we don't have a range table and don't populate the
2088 : * es_result_relations array. That's a bit bogus, but it's enough to make
2089 : * ExecGetTriggerResultRel() find them.
2090 : */
2091 2826 : estate = CreateExecutorState();
2092 : resultRelInfos = (ResultRelInfo *)
2093 2826 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2094 2826 : resultRelInfo = resultRelInfos;
2095 7766 : foreach(cell, rels)
2096 : {
2097 4940 : Relation rel = (Relation) lfirst(cell);
2098 :
2099 4940 : InitResultRelInfo(resultRelInfo,
2100 : rel,
2101 : 0, /* dummy rangetable index */
2102 : NULL,
2103 : 0);
2104 4940 : estate->es_opened_result_relations =
2105 4940 : lappend(estate->es_opened_result_relations, resultRelInfo);
2106 4940 : resultRelInfo++;
2107 : }
2108 :
2109 : /*
2110 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2111 : * truncating (this is because one of them might throw an error). Also, if
2112 : * we were to allow them to prevent statement execution, that would need
2113 : * to be handled here.
2114 : */
2115 2826 : resultRelInfo = resultRelInfos;
2116 7766 : foreach(cell, rels)
2117 : {
2118 : UserContext ucxt;
2119 :
2120 4940 : if (run_as_table_owner)
2121 54 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2122 : &ucxt);
2123 4940 : ExecBSTruncateTriggers(estate, resultRelInfo);
2124 4940 : if (run_as_table_owner)
2125 54 : RestoreUserContext(&ucxt);
2126 4940 : resultRelInfo++;
2127 : }
2128 :
2129 : /*
2130 : * OK, truncate each table.
2131 : */
2132 2826 : mySubid = GetCurrentSubTransactionId();
2133 :
2134 7766 : foreach(cell, rels)
2135 : {
2136 4940 : Relation rel = (Relation) lfirst(cell);
2137 :
2138 : /* Skip partitioned tables as there is nothing to do */
2139 4940 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2140 704 : continue;
2141 :
2142 : /*
2143 : * Build the lists of foreign tables belonging to each foreign server
2144 : * and pass each list to the foreign data wrapper's callback function,
2145 : * so that each server can truncate its all foreign tables in bulk.
2146 : * Each list is saved as a single entry in a hash table that uses the
2147 : * server OID as lookup key.
2148 : */
2149 4236 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2150 34 : {
2151 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2152 : bool found;
2153 : ForeignTruncateInfo *ft_info;
2154 :
2155 : /* First time through, initialize hashtable for foreign tables */
2156 34 : if (!ft_htab)
2157 : {
2158 : HASHCTL hctl;
2159 :
2160 30 : memset(&hctl, 0, sizeof(HASHCTL));
2161 30 : hctl.keysize = sizeof(Oid);
2162 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2163 30 : hctl.hcxt = CurrentMemoryContext;
2164 :
2165 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2166 : 32, /* start small and extend */
2167 : &hctl,
2168 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2169 : }
2170 :
2171 : /* Find or create cached entry for the foreign table */
2172 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2173 34 : if (!found)
2174 30 : ft_info->rels = NIL;
2175 :
2176 : /*
2177 : * Save the foreign table in the entry of the server that the
2178 : * foreign table belongs to.
2179 : */
2180 34 : ft_info->rels = lappend(ft_info->rels, rel);
2181 34 : continue;
2182 : }
2183 :
2184 : /*
2185 : * Normally, we need a transaction-safe truncation here. However, if
2186 : * the table was either created in the current (sub)transaction or has
2187 : * a new relfilenumber in the current (sub)transaction, then we can
2188 : * just truncate it in-place, because a rollback would cause the whole
2189 : * table or the current physical file to be thrown away anyway.
2190 : */
2191 4202 : if (rel->rd_createSubid == mySubid ||
2192 4176 : rel->rd_newRelfilelocatorSubid == mySubid)
2193 : {
2194 : /* Immediate, non-rollbackable truncation is OK */
2195 90 : heap_truncate_one_rel(rel);
2196 : }
2197 : else
2198 : {
2199 : Oid heap_relid;
2200 : Oid toast_relid;
2201 4112 : ReindexParams reindex_params = {0};
2202 :
2203 : /*
2204 : * This effectively deletes all rows in the table, and may be done
2205 : * in a serializable transaction. In that case we must record a
2206 : * rw-conflict in to this transaction from each transaction
2207 : * holding a predicate lock on the table.
2208 : */
2209 4112 : CheckTableForSerializableConflictIn(rel);
2210 :
2211 : /*
2212 : * Need the full transaction-safe pushups.
2213 : *
2214 : * Create a new empty storage file for the relation, and assign it
2215 : * as the relfilenumber value. The old storage file is scheduled
2216 : * for deletion at commit.
2217 : */
2218 4112 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2219 :
2220 4112 : heap_relid = RelationGetRelid(rel);
2221 :
2222 : /*
2223 : * The same for the toast table, if any.
2224 : */
2225 4112 : toast_relid = rel->rd_rel->reltoastrelid;
2226 4112 : if (OidIsValid(toast_relid))
2227 : {
2228 2282 : Relation toastrel = relation_open(toast_relid,
2229 : AccessExclusiveLock);
2230 :
2231 2282 : RelationSetNewRelfilenumber(toastrel,
2232 2282 : toastrel->rd_rel->relpersistence);
2233 2282 : table_close(toastrel, NoLock);
2234 : }
2235 :
2236 : /*
2237 : * Reconstruct the indexes to match, and we're done.
2238 : */
2239 4112 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2240 : &reindex_params);
2241 : }
2242 :
2243 4202 : pgstat_count_truncate(rel);
2244 : }
2245 :
2246 : /* Now go through the hash table, and truncate foreign tables */
2247 2826 : if (ft_htab)
2248 : {
2249 : ForeignTruncateInfo *ft_info;
2250 : HASH_SEQ_STATUS seq;
2251 :
2252 30 : hash_seq_init(&seq, ft_htab);
2253 :
2254 30 : PG_TRY();
2255 : {
2256 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2257 : {
2258 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2259 :
2260 : /* truncate_check_rel() has checked that already */
2261 : Assert(routine->ExecForeignTruncate != NULL);
2262 :
2263 30 : routine->ExecForeignTruncate(ft_info->rels,
2264 : behavior,
2265 : restart_seqs);
2266 : }
2267 : }
2268 8 : PG_FINALLY();
2269 : {
2270 30 : hash_destroy(ft_htab);
2271 : }
2272 30 : PG_END_TRY();
2273 : }
2274 :
2275 : /*
2276 : * Restart owned sequences if we were asked to.
2277 : */
2278 2852 : foreach(cell, seq_relids)
2279 : {
2280 34 : Oid seq_relid = lfirst_oid(cell);
2281 :
2282 34 : ResetSequence(seq_relid);
2283 : }
2284 :
2285 : /*
2286 : * Write a WAL record to allow this set of actions to be logically
2287 : * decoded.
2288 : *
2289 : * Assemble an array of relids so we can write a single WAL record for the
2290 : * whole action.
2291 : */
2292 2818 : if (relids_logged != NIL)
2293 : {
2294 : xl_heap_truncate xlrec;
2295 54 : int i = 0;
2296 :
2297 : /* should only get here if wal_level >= logical */
2298 : Assert(XLogLogicalInfoActive());
2299 :
2300 54 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2301 144 : foreach(cell, relids_logged)
2302 90 : logrelids[i++] = lfirst_oid(cell);
2303 :
2304 54 : xlrec.dbId = MyDatabaseId;
2305 54 : xlrec.nrelids = list_length(relids_logged);
2306 54 : xlrec.flags = 0;
2307 54 : if (behavior == DROP_CASCADE)
2308 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2309 54 : if (restart_seqs)
2310 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2311 :
2312 54 : XLogBeginInsert();
2313 54 : XLogRegisterData(&xlrec, SizeOfHeapTruncate);
2314 54 : XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2315 :
2316 54 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2317 :
2318 54 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2319 : }
2320 :
2321 : /*
2322 : * Process all AFTER STATEMENT TRUNCATE triggers.
2323 : */
2324 2818 : resultRelInfo = resultRelInfos;
2325 7750 : foreach(cell, rels)
2326 : {
2327 : UserContext ucxt;
2328 :
2329 4932 : if (run_as_table_owner)
2330 54 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2331 : &ucxt);
2332 4932 : ExecASTruncateTriggers(estate, resultRelInfo);
2333 4932 : if (run_as_table_owner)
2334 54 : RestoreUserContext(&ucxt);
2335 4932 : resultRelInfo++;
2336 : }
2337 :
2338 : /* Handle queued AFTER triggers */
2339 2818 : AfterTriggerEndQuery(estate);
2340 :
2341 : /* We can clean up the EState now */
2342 2818 : FreeExecutorState(estate);
2343 :
2344 : /*
2345 : * Close any rels opened by CASCADE (can't do this while EState still
2346 : * holds refs)
2347 : */
2348 2818 : rels = list_difference_ptr(rels, explicit_rels);
2349 2912 : foreach(cell, rels)
2350 : {
2351 94 : Relation rel = (Relation) lfirst(cell);
2352 :
2353 94 : table_close(rel, NoLock);
2354 : }
2355 2818 : }
2356 :
2357 : /*
2358 : * Check that a given relation is safe to truncate. Subroutine for
2359 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2360 : */
2361 : static void
2362 5478 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2363 : {
2364 5478 : char *relname = NameStr(reltuple->relname);
2365 :
2366 : /*
2367 : * Only allow truncate on regular tables, foreign tables using foreign
2368 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2369 : * latter are only being included here for the following checks; no
2370 : * physical truncation will occur in their case.).
2371 : */
2372 5478 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2373 : {
2374 38 : Oid serverid = GetForeignServerIdByRelId(relid);
2375 38 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2376 :
2377 36 : if (!fdwroutine->ExecForeignTruncate)
2378 2 : ereport(ERROR,
2379 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2380 : errmsg("cannot truncate foreign table \"%s\"",
2381 : relname)));
2382 : }
2383 5440 : else if (reltuple->relkind != RELKIND_RELATION &&
2384 726 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2385 0 : ereport(ERROR,
2386 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2387 : errmsg("\"%s\" is not a table", relname)));
2388 :
2389 : /*
2390 : * Most system catalogs can't be truncated at all, or at least not unless
2391 : * allow_system_table_mods=on. As an exception, however, we allow
2392 : * pg_largeobject to be truncated as part of pg_upgrade, because we need
2393 : * to change its relfilenode to match the old cluster, and allowing a
2394 : * TRUNCATE command to be executed is the easiest way of doing that.
2395 : */
2396 5474 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2397 50 : && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2398 2 : ereport(ERROR,
2399 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2400 : errmsg("permission denied: \"%s\" is a system catalog",
2401 : relname)));
2402 :
2403 5472 : InvokeObjectTruncateHook(relid);
2404 5472 : }
2405 :
2406 : /*
2407 : * Check that current user has the permission to truncate given relation.
2408 : */
2409 : static void
2410 3676 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2411 : {
2412 3676 : char *relname = NameStr(reltuple->relname);
2413 : AclResult aclresult;
2414 :
2415 : /* Permissions checks */
2416 3676 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2417 3676 : if (aclresult != ACLCHECK_OK)
2418 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2419 : relname);
2420 3644 : }
2421 :
2422 : /*
2423 : * Set of extra sanity checks to check if a given relation is safe to
2424 : * truncate. This is split with truncate_check_rel() as
2425 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2426 : */
2427 : static void
2428 5050 : truncate_check_activity(Relation rel)
2429 : {
2430 : /*
2431 : * Don't allow truncate on temp tables of other backends ... their local
2432 : * buffer manager is not going to cope.
2433 : */
2434 5050 : if (RELATION_IS_OTHER_TEMP(rel))
2435 0 : ereport(ERROR,
2436 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2437 : errmsg("cannot truncate temporary tables of other sessions")));
2438 :
2439 : /*
2440 : * Also check for active uses of the relation in the current transaction,
2441 : * including open scans and pending AFTER trigger events.
2442 : */
2443 5050 : CheckTableNotInUse(rel, "TRUNCATE");
2444 5044 : }
2445 :
2446 : /*
2447 : * storage_name
2448 : * returns the name corresponding to a typstorage/attstorage enum value
2449 : */
2450 : static const char *
2451 24 : storage_name(char c)
2452 : {
2453 24 : switch (c)
2454 : {
2455 0 : case TYPSTORAGE_PLAIN:
2456 0 : return "PLAIN";
2457 0 : case TYPSTORAGE_EXTERNAL:
2458 0 : return "EXTERNAL";
2459 12 : case TYPSTORAGE_EXTENDED:
2460 12 : return "EXTENDED";
2461 12 : case TYPSTORAGE_MAIN:
2462 12 : return "MAIN";
2463 0 : default:
2464 0 : return "???";
2465 : }
2466 : }
2467 :
2468 : /*----------
2469 : * MergeAttributes
2470 : * Returns new schema given initial schema and superclasses.
2471 : *
2472 : * Input arguments:
2473 : * 'columns' is the column/attribute definition for the table. (It's a list
2474 : * of ColumnDef's.) It is destructively changed.
2475 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2476 : * 'relpersistence' is the persistence type of the table.
2477 : * 'is_partition' tells if the table is a partition.
2478 : *
2479 : * Output arguments:
2480 : * 'supconstr' receives a list of CookedConstraint representing
2481 : * CHECK constraints belonging to parent relations, updated as
2482 : * necessary to be valid for the child.
2483 : * 'supnotnulls' receives a list of CookedConstraint representing
2484 : * not-null constraints based on those from parent relations.
2485 : *
2486 : * Return value:
2487 : * Completed schema list.
2488 : *
2489 : * Notes:
2490 : * The order in which the attributes are inherited is very important.
2491 : * Intuitively, the inherited attributes should come first. If a table
2492 : * inherits from multiple parents, the order of those attributes are
2493 : * according to the order of the parents specified in CREATE TABLE.
2494 : *
2495 : * Here's an example:
2496 : *
2497 : * create table person (name text, age int4, location point);
2498 : * create table emp (salary int4, manager text) inherits(person);
2499 : * create table student (gpa float8) inherits (person);
2500 : * create table stud_emp (percent int4) inherits (emp, student);
2501 : *
2502 : * The order of the attributes of stud_emp is:
2503 : *
2504 : * person {1:name, 2:age, 3:location}
2505 : * / \
2506 : * {6:gpa} student emp {4:salary, 5:manager}
2507 : * \ /
2508 : * stud_emp {7:percent}
2509 : *
2510 : * If the same attribute name appears multiple times, then it appears
2511 : * in the result table in the proper location for its first appearance.
2512 : *
2513 : * Constraints (including not-null constraints) for the child table
2514 : * are the union of all relevant constraints, from both the child schema
2515 : * and parent tables. In addition, in legacy inheritance, each column that
2516 : * appears in a primary key in any of the parents also gets a NOT NULL
2517 : * constraint (partitioning doesn't need this, because the PK itself gets
2518 : * inherited.)
2519 : *
2520 : * The default value for a child column is defined as:
2521 : * (1) If the child schema specifies a default, that value is used.
2522 : * (2) If neither the child nor any parent specifies a default, then
2523 : * the column will not have a default.
2524 : * (3) If conflicting defaults are inherited from different parents
2525 : * (and not overridden by the child), an error is raised.
2526 : * (4) Otherwise the inherited default is used.
2527 : *
2528 : * Note that the default-value infrastructure is used for generated
2529 : * columns' expressions too, so most of the preceding paragraph applies
2530 : * to generation expressions too. We insist that a child column be
2531 : * generated if and only if its parent(s) are, but it need not have
2532 : * the same generation expression.
2533 : *----------
2534 : */
2535 : static List *
2536 63542 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2537 : bool is_partition, List **supconstr, List **supnotnulls)
2538 : {
2539 63542 : List *inh_columns = NIL;
2540 63542 : List *constraints = NIL;
2541 63542 : List *nnconstraints = NIL;
2542 63542 : bool have_bogus_defaults = false;
2543 : int child_attno;
2544 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2545 63542 : List *saved_columns = NIL;
2546 : ListCell *lc;
2547 :
2548 : /*
2549 : * Check for and reject tables with too many columns. We perform this
2550 : * check relatively early for two reasons: (a) we don't run the risk of
2551 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2552 : * okay if we're processing <= 1600 columns, but could take minutes to
2553 : * execute if the user attempts to create a table with hundreds of
2554 : * thousands of columns.
2555 : *
2556 : * Note that we also need to check that we do not exceed this figure after
2557 : * including columns from inherited relations.
2558 : */
2559 63542 : if (list_length(columns) > MaxHeapAttributeNumber)
2560 0 : ereport(ERROR,
2561 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2562 : errmsg("tables can have at most %d columns",
2563 : MaxHeapAttributeNumber)));
2564 :
2565 : /*
2566 : * Check for duplicate names in the explicit list of attributes.
2567 : *
2568 : * Although we might consider merging such entries in the same way that we
2569 : * handle name conflicts for inherited attributes, it seems to make more
2570 : * sense to assume such conflicts are errors.
2571 : *
2572 : * We don't use foreach() here because we have two nested loops over the
2573 : * columns list, with possible element deletions in the inner one. If we
2574 : * used foreach_delete_current() it could only fix up the state of one of
2575 : * the loops, so it seems cleaner to use looping over list indexes for
2576 : * both loops. Note that any deletion will happen beyond where the outer
2577 : * loop is, so its index never needs adjustment.
2578 : */
2579 301876 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2580 : {
2581 238358 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2582 :
2583 238358 : if (!is_partition && coldef->typeName == NULL)
2584 : {
2585 : /*
2586 : * Typed table column option that does not belong to a column from
2587 : * the type. This works because the columns from the type come
2588 : * first in the list. (We omit this check for partition column
2589 : * lists; those are processed separately below.)
2590 : */
2591 6 : ereport(ERROR,
2592 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2593 : errmsg("column \"%s\" does not exist",
2594 : coldef->colname)));
2595 : }
2596 :
2597 : /* restpos scans all entries beyond coldef; incr is in loop body */
2598 7774506 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2599 : {
2600 7536172 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2601 :
2602 7536172 : if (strcmp(coldef->colname, restdef->colname) == 0)
2603 : {
2604 58 : if (coldef->is_from_type)
2605 : {
2606 : /*
2607 : * merge the column options into the column from the type
2608 : */
2609 40 : coldef->is_not_null = restdef->is_not_null;
2610 40 : coldef->raw_default = restdef->raw_default;
2611 40 : coldef->cooked_default = restdef->cooked_default;
2612 40 : coldef->constraints = restdef->constraints;
2613 40 : coldef->is_from_type = false;
2614 40 : columns = list_delete_nth_cell(columns, restpos);
2615 : }
2616 : else
2617 18 : ereport(ERROR,
2618 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2619 : errmsg("column \"%s\" specified more than once",
2620 : coldef->colname)));
2621 : }
2622 : else
2623 7536114 : restpos++;
2624 : }
2625 : }
2626 :
2627 : /*
2628 : * In case of a partition, there are no new column definitions, only dummy
2629 : * ColumnDefs created for column constraints. Set them aside for now and
2630 : * process them at the end.
2631 : */
2632 63518 : if (is_partition)
2633 : {
2634 7928 : saved_columns = columns;
2635 7928 : columns = NIL;
2636 : }
2637 :
2638 : /*
2639 : * Scan the parents left-to-right, and merge their attributes to form a
2640 : * list of inherited columns (inh_columns).
2641 : */
2642 63518 : child_attno = 0;
2643 73946 : foreach(lc, supers)
2644 : {
2645 10512 : Oid parent = lfirst_oid(lc);
2646 : Relation relation;
2647 : TupleDesc tupleDesc;
2648 : TupleConstr *constr;
2649 : AttrMap *newattmap;
2650 : List *inherited_defaults;
2651 : List *cols_with_defaults;
2652 : List *nnconstrs;
2653 : ListCell *lc1;
2654 : ListCell *lc2;
2655 10512 : Bitmapset *nncols = NULL;
2656 :
2657 : /* caller already got lock */
2658 10512 : relation = table_open(parent, NoLock);
2659 :
2660 : /*
2661 : * Check for active uses of the parent partitioned table in the
2662 : * current transaction, such as being used in some manner by an
2663 : * enclosing command.
2664 : */
2665 10512 : if (is_partition)
2666 7928 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2667 :
2668 : /*
2669 : * We do not allow partitioned tables and partitions to participate in
2670 : * regular inheritance.
2671 : */
2672 10506 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2673 6 : ereport(ERROR,
2674 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2675 : errmsg("cannot inherit from partitioned table \"%s\"",
2676 : RelationGetRelationName(relation))));
2677 10500 : if (relation->rd_rel->relispartition && !is_partition)
2678 6 : ereport(ERROR,
2679 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2680 : errmsg("cannot inherit from partition \"%s\"",
2681 : RelationGetRelationName(relation))));
2682 :
2683 10494 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2684 7924 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2685 7904 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2686 0 : ereport(ERROR,
2687 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2688 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2689 : RelationGetRelationName(relation))));
2690 :
2691 : /*
2692 : * If the parent is permanent, so must be all of its partitions. Note
2693 : * that inheritance allows that case.
2694 : */
2695 10494 : if (is_partition &&
2696 7922 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2697 : relpersistence == RELPERSISTENCE_TEMP)
2698 6 : ereport(ERROR,
2699 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2700 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2701 : RelationGetRelationName(relation))));
2702 :
2703 : /* Permanent rels cannot inherit from temporary ones */
2704 10488 : if (relpersistence != RELPERSISTENCE_TEMP &&
2705 10134 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2706 24 : ereport(ERROR,
2707 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2708 : errmsg(!is_partition
2709 : ? "cannot inherit from temporary relation \"%s\""
2710 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2711 : RelationGetRelationName(relation))));
2712 :
2713 : /* If existing rel is temp, it must belong to this session */
2714 10464 : if (RELATION_IS_OTHER_TEMP(relation))
2715 0 : ereport(ERROR,
2716 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2717 : errmsg(!is_partition
2718 : ? "cannot inherit from temporary relation of another session"
2719 : : "cannot create as partition of temporary relation of another session")));
2720 :
2721 : /*
2722 : * We should have an UNDER permission flag for this, but for now,
2723 : * demand that creator of a child table own the parent.
2724 : */
2725 10464 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2726 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2727 0 : RelationGetRelationName(relation));
2728 :
2729 10464 : tupleDesc = RelationGetDescr(relation);
2730 10464 : constr = tupleDesc->constr;
2731 :
2732 : /*
2733 : * newattmap->attnums[] will contain the child-table attribute numbers
2734 : * for the attributes of this parent table. (They are not the same
2735 : * for parents after the first one, nor if we have dropped columns.)
2736 : */
2737 10464 : newattmap = make_attrmap(tupleDesc->natts);
2738 :
2739 : /* We can't process inherited defaults until newattmap is complete. */
2740 10464 : inherited_defaults = cols_with_defaults = NIL;
2741 :
2742 : /*
2743 : * Request attnotnull on columns that have a not-null constraint
2744 : * that's not marked NO INHERIT (even if not valid).
2745 : */
2746 10464 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2747 : true, false);
2748 23352 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2749 2424 : nncols = bms_add_member(nncols, cc->attnum);
2750 :
2751 31430 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2752 20966 : parent_attno++)
2753 : {
2754 21002 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2755 : parent_attno - 1);
2756 21002 : char *attributeName = NameStr(attribute->attname);
2757 : int exist_attno;
2758 : ColumnDef *newdef;
2759 : ColumnDef *mergeddef;
2760 :
2761 : /*
2762 : * Ignore dropped columns in the parent.
2763 : */
2764 21002 : if (attribute->attisdropped)
2765 192 : continue; /* leave newattmap->attnums entry as zero */
2766 :
2767 : /*
2768 : * Create new column definition
2769 : */
2770 20810 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2771 : attribute->atttypmod, attribute->attcollation);
2772 20810 : newdef->storage = attribute->attstorage;
2773 20810 : newdef->generated = attribute->attgenerated;
2774 20810 : if (CompressionMethodIsValid(attribute->attcompression))
2775 32 : newdef->compression =
2776 32 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2777 :
2778 : /*
2779 : * Regular inheritance children are independent enough not to
2780 : * inherit identity columns. But partitions are integral part of
2781 : * a partitioned table and inherit identity column.
2782 : */
2783 20810 : if (is_partition)
2784 16066 : newdef->identity = attribute->attidentity;
2785 :
2786 : /*
2787 : * Does it match some previously considered column from another
2788 : * parent?
2789 : */
2790 20810 : exist_attno = findAttrByName(attributeName, inh_columns);
2791 20810 : if (exist_attno > 0)
2792 : {
2793 : /*
2794 : * Yes, try to merge the two column definitions.
2795 : */
2796 390 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2797 :
2798 354 : newattmap->attnums[parent_attno - 1] = exist_attno;
2799 :
2800 : /*
2801 : * Partitions have only one parent, so conflict should never
2802 : * occur.
2803 : */
2804 : Assert(!is_partition);
2805 : }
2806 : else
2807 : {
2808 : /*
2809 : * No, create a new inherited column
2810 : */
2811 20420 : newdef->inhcount = 1;
2812 20420 : newdef->is_local = false;
2813 20420 : inh_columns = lappend(inh_columns, newdef);
2814 :
2815 20420 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2816 20420 : mergeddef = newdef;
2817 : }
2818 :
2819 : /*
2820 : * mark attnotnull if parent has it
2821 : */
2822 20774 : if (bms_is_member(parent_attno, nncols))
2823 2424 : mergeddef->is_not_null = true;
2824 :
2825 : /*
2826 : * Locate default/generation expression if any
2827 : */
2828 20774 : if (attribute->atthasdef)
2829 : {
2830 : Node *this_default;
2831 :
2832 718 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2833 718 : if (this_default == NULL)
2834 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2835 : parent_attno, RelationGetRelationName(relation));
2836 :
2837 : /*
2838 : * If it's a GENERATED default, it might contain Vars that
2839 : * need to be mapped to the inherited column(s)' new numbers.
2840 : * We can't do that till newattmap is ready, so just remember
2841 : * all the inherited default expressions for the moment.
2842 : */
2843 718 : inherited_defaults = lappend(inherited_defaults, this_default);
2844 718 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2845 : }
2846 : }
2847 :
2848 : /*
2849 : * Now process any inherited default expressions, adjusting attnos
2850 : * using the completed newattmap map.
2851 : */
2852 11146 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2853 : {
2854 718 : Node *this_default = (Node *) lfirst(lc1);
2855 718 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2856 : bool found_whole_row;
2857 :
2858 : /* Adjust Vars to match new table's column numbering */
2859 718 : this_default = map_variable_attnos(this_default,
2860 : 1, 0,
2861 : newattmap,
2862 : InvalidOid, &found_whole_row);
2863 :
2864 : /*
2865 : * For the moment we have to reject whole-row variables. We could
2866 : * convert them, if we knew the new table's rowtype OID, but that
2867 : * hasn't been assigned yet. (A variable could only appear in a
2868 : * generation expression, so the error message is correct.)
2869 : */
2870 718 : if (found_whole_row)
2871 0 : ereport(ERROR,
2872 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2873 : errmsg("cannot convert whole-row table reference"),
2874 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2875 : def->colname,
2876 : RelationGetRelationName(relation))));
2877 :
2878 : /*
2879 : * If we already had a default from some prior parent, check to
2880 : * see if they are the same. If so, no problem; if not, mark the
2881 : * column as having a bogus default. Below, we will complain if
2882 : * the bogus default isn't overridden by the child columns.
2883 : */
2884 : Assert(def->raw_default == NULL);
2885 718 : if (def->cooked_default == NULL)
2886 672 : def->cooked_default = this_default;
2887 46 : else if (!equal(def->cooked_default, this_default))
2888 : {
2889 40 : def->cooked_default = &bogus_marker;
2890 40 : have_bogus_defaults = true;
2891 : }
2892 : }
2893 :
2894 : /*
2895 : * Now copy the CHECK constraints of this parent, adjusting attnos
2896 : * using the completed newattmap map. Identically named constraints
2897 : * are merged if possible, else we throw error.
2898 : */
2899 10428 : if (constr && constr->num_check > 0)
2900 : {
2901 342 : ConstrCheck *check = constr->check;
2902 :
2903 1060 : for (int i = 0; i < constr->num_check; i++)
2904 : {
2905 718 : char *name = check[i].ccname;
2906 : Node *expr;
2907 : bool found_whole_row;
2908 :
2909 : /* ignore if the constraint is non-inheritable */
2910 718 : if (check[i].ccnoinherit)
2911 56 : continue;
2912 :
2913 : /* Adjust Vars to match new table's column numbering */
2914 662 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2915 : 1, 0,
2916 : newattmap,
2917 : InvalidOid, &found_whole_row);
2918 :
2919 : /*
2920 : * For the moment we have to reject whole-row variables. We
2921 : * could convert them, if we knew the new table's rowtype OID,
2922 : * but that hasn't been assigned yet.
2923 : */
2924 662 : if (found_whole_row)
2925 0 : ereport(ERROR,
2926 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2927 : errmsg("cannot convert whole-row table reference"),
2928 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2929 : name,
2930 : RelationGetRelationName(relation))));
2931 :
2932 662 : constraints = MergeCheckConstraint(constraints, name, expr,
2933 662 : check[i].ccenforced);
2934 : }
2935 : }
2936 :
2937 : /*
2938 : * Also copy the not-null constraints from this parent. The
2939 : * attnotnull markings were already installed above.
2940 : */
2941 23280 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2942 : {
2943 : Assert(nn->contype == CONSTR_NOTNULL);
2944 :
2945 2424 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2946 :
2947 2424 : nnconstraints = lappend(nnconstraints, nn);
2948 : }
2949 :
2950 10428 : free_attrmap(newattmap);
2951 :
2952 : /*
2953 : * Close the parent rel, but keep our lock on it until xact commit.
2954 : * That will prevent someone else from deleting or ALTERing the parent
2955 : * before the child is committed.
2956 : */
2957 10428 : table_close(relation, NoLock);
2958 : }
2959 :
2960 : /*
2961 : * If we had no inherited attributes, the result columns are just the
2962 : * explicitly declared columns. Otherwise, we need to merge the declared
2963 : * columns into the inherited column list. Although, we never have any
2964 : * explicitly declared columns if the table is a partition.
2965 : */
2966 63434 : if (inh_columns != NIL)
2967 : {
2968 9980 : int newcol_attno = 0;
2969 :
2970 11056 : foreach(lc, columns)
2971 : {
2972 1154 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2973 1154 : char *attributeName = newdef->colname;
2974 : int exist_attno;
2975 :
2976 : /*
2977 : * Partitions have only one parent and have no column definitions
2978 : * of their own, so conflict should never occur.
2979 : */
2980 : Assert(!is_partition);
2981 :
2982 1154 : newcol_attno++;
2983 :
2984 : /*
2985 : * Does it match some inherited column?
2986 : */
2987 1154 : exist_attno = findAttrByName(attributeName, inh_columns);
2988 1154 : if (exist_attno > 0)
2989 : {
2990 : /*
2991 : * Yes, try to merge the two column definitions.
2992 : */
2993 428 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2994 : }
2995 : else
2996 : {
2997 : /*
2998 : * No, attach new column unchanged to result columns.
2999 : */
3000 726 : inh_columns = lappend(inh_columns, newdef);
3001 : }
3002 : }
3003 :
3004 9902 : columns = inh_columns;
3005 :
3006 : /*
3007 : * Check that we haven't exceeded the legal # of columns after merging
3008 : * in inherited columns.
3009 : */
3010 9902 : if (list_length(columns) > MaxHeapAttributeNumber)
3011 0 : ereport(ERROR,
3012 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
3013 : errmsg("tables can have at most %d columns",
3014 : MaxHeapAttributeNumber)));
3015 : }
3016 :
3017 : /*
3018 : * Now that we have the column definition list for a partition, we can
3019 : * check whether the columns referenced in the column constraint specs
3020 : * actually exist. Also, merge column defaults.
3021 : */
3022 63356 : if (is_partition)
3023 : {
3024 8104 : foreach(lc, saved_columns)
3025 : {
3026 242 : ColumnDef *restdef = lfirst(lc);
3027 242 : bool found = false;
3028 : ListCell *l;
3029 :
3030 900 : foreach(l, columns)
3031 : {
3032 694 : ColumnDef *coldef = lfirst(l);
3033 :
3034 694 : if (strcmp(coldef->colname, restdef->colname) == 0)
3035 : {
3036 242 : found = true;
3037 :
3038 : /*
3039 : * Check for conflicts related to generated columns.
3040 : *
3041 : * Same rules as above: generated-ness has to match the
3042 : * parent, but the contents of the generation expression
3043 : * can be different.
3044 : */
3045 242 : if (coldef->generated)
3046 : {
3047 134 : if (restdef->raw_default && !restdef->generated)
3048 12 : ereport(ERROR,
3049 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3050 : errmsg("column \"%s\" inherits from generated column but specifies default",
3051 : restdef->colname)));
3052 122 : if (restdef->identity)
3053 0 : ereport(ERROR,
3054 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3055 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3056 : restdef->colname)));
3057 : }
3058 : else
3059 : {
3060 108 : if (restdef->generated)
3061 12 : ereport(ERROR,
3062 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3063 : errmsg("child column \"%s\" specifies generation expression",
3064 : restdef->colname),
3065 : errhint("A child table column cannot be generated unless its parent column is.")));
3066 : }
3067 :
3068 218 : if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3069 12 : ereport(ERROR,
3070 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3071 : errmsg("column \"%s\" inherits from generated column of different kind",
3072 : restdef->colname),
3073 : errdetail("Parent column is %s, child column is %s.",
3074 : coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3075 : restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3076 :
3077 : /*
3078 : * Override the parent's default value for this column
3079 : * (coldef->cooked_default) with the partition's local
3080 : * definition (restdef->raw_default), if there's one. It
3081 : * should be physically impossible to get a cooked default
3082 : * in the local definition or a raw default in the
3083 : * inherited definition, but make sure they're nulls, for
3084 : * future-proofing.
3085 : */
3086 : Assert(restdef->cooked_default == NULL);
3087 : Assert(coldef->raw_default == NULL);
3088 206 : if (restdef->raw_default)
3089 : {
3090 134 : coldef->raw_default = restdef->raw_default;
3091 134 : coldef->cooked_default = NULL;
3092 : }
3093 : }
3094 : }
3095 :
3096 : /* complain for constraints on columns not in parent */
3097 206 : if (!found)
3098 0 : ereport(ERROR,
3099 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3100 : errmsg("column \"%s\" does not exist",
3101 : restdef->colname)));
3102 : }
3103 : }
3104 :
3105 : /*
3106 : * If we found any conflicting parent default values, check to make sure
3107 : * they were overridden by the child.
3108 : */
3109 63320 : if (have_bogus_defaults)
3110 : {
3111 106 : foreach(lc, columns)
3112 : {
3113 84 : ColumnDef *def = lfirst(lc);
3114 :
3115 84 : if (def->cooked_default == &bogus_marker)
3116 : {
3117 18 : if (def->generated)
3118 12 : ereport(ERROR,
3119 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3120 : errmsg("column \"%s\" inherits conflicting generation expressions",
3121 : def->colname),
3122 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3123 : else
3124 6 : ereport(ERROR,
3125 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3126 : errmsg("column \"%s\" inherits conflicting default values",
3127 : def->colname),
3128 : errhint("To resolve the conflict, specify a default explicitly.")));
3129 : }
3130 : }
3131 : }
3132 :
3133 63302 : *supconstr = constraints;
3134 63302 : *supnotnulls = nnconstraints;
3135 :
3136 63302 : return columns;
3137 : }
3138 :
3139 :
3140 : /*
3141 : * MergeCheckConstraint
3142 : * Try to merge an inherited CHECK constraint with previous ones
3143 : *
3144 : * If we inherit identically-named constraints from multiple parents, we must
3145 : * merge them, or throw an error if they don't have identical definitions.
3146 : *
3147 : * constraints is a list of CookedConstraint structs for previous constraints.
3148 : *
3149 : * If the new constraint matches an existing one, then the existing
3150 : * constraint's inheritance count is updated. If there is a conflict (same
3151 : * name but different expression), throw an error. If the constraint neither
3152 : * matches nor conflicts with an existing one, a new constraint is appended to
3153 : * the list.
3154 : */
3155 : static List *
3156 662 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3157 : {
3158 : ListCell *lc;
3159 : CookedConstraint *newcon;
3160 :
3161 2116 : foreach(lc, constraints)
3162 : {
3163 1604 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3164 :
3165 : Assert(ccon->contype == CONSTR_CHECK);
3166 :
3167 : /* Non-matching names never conflict */
3168 1604 : if (strcmp(ccon->name, name) != 0)
3169 1454 : continue;
3170 :
3171 150 : if (equal(expr, ccon->expr))
3172 : {
3173 : /* OK to merge constraint with existing */
3174 150 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3175 : &ccon->inhcount))
3176 0 : ereport(ERROR,
3177 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3178 : errmsg("too many inheritance parents"));
3179 :
3180 : /*
3181 : * When enforceability differs, the merged constraint should be
3182 : * marked as ENFORCED because one of the parents is ENFORCED.
3183 : */
3184 150 : if (!ccon->is_enforced && is_enforced)
3185 : {
3186 48 : ccon->is_enforced = true;
3187 48 : ccon->skip_validation = false;
3188 : }
3189 :
3190 150 : return constraints;
3191 : }
3192 :
3193 0 : ereport(ERROR,
3194 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3195 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3196 : name)));
3197 : }
3198 :
3199 : /*
3200 : * Constraint couldn't be merged with an existing one and also didn't
3201 : * conflict with an existing one, so add it as a new one to the list.
3202 : */
3203 512 : newcon = palloc0_object(CookedConstraint);
3204 512 : newcon->contype = CONSTR_CHECK;
3205 512 : newcon->name = pstrdup(name);
3206 512 : newcon->expr = expr;
3207 512 : newcon->inhcount = 1;
3208 512 : newcon->is_enforced = is_enforced;
3209 512 : newcon->skip_validation = !is_enforced;
3210 512 : return lappend(constraints, newcon);
3211 : }
3212 :
3213 : /*
3214 : * MergeChildAttribute
3215 : * Merge given child attribute definition into given inherited attribute.
3216 : *
3217 : * Input arguments:
3218 : * 'inh_columns' is the list of inherited ColumnDefs.
3219 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3220 : * 'newcol_attno' is the attribute number in child table's schema definition
3221 : * 'newdef' is the column/attribute definition from the child table.
3222 : *
3223 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3224 : * ColumnDef remains unchanged.
3225 : *
3226 : * Notes:
3227 : * - The attribute is merged according to the rules laid out in the prologue
3228 : * of MergeAttributes().
3229 : * - If matching inherited attribute exists but the child attribute can not be
3230 : * merged into it, the function throws respective errors.
3231 : * - A partition can not have its own column definitions. Hence this function
3232 : * is applicable only to a regular inheritance child.
3233 : */
3234 : static void
3235 428 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3236 : {
3237 428 : char *attributeName = newdef->colname;
3238 : ColumnDef *inhdef;
3239 : Oid inhtypeid,
3240 : newtypeid;
3241 : int32 inhtypmod,
3242 : newtypmod;
3243 : Oid inhcollid,
3244 : newcollid;
3245 :
3246 428 : if (exist_attno == newcol_attno)
3247 366 : ereport(NOTICE,
3248 : (errmsg("merging column \"%s\" with inherited definition",
3249 : attributeName)));
3250 : else
3251 62 : ereport(NOTICE,
3252 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3253 : errdetail("User-specified column moved to the position of the inherited column.")));
3254 :
3255 428 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3256 :
3257 : /*
3258 : * Must have the same type and typmod
3259 : */
3260 428 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3261 428 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3262 428 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3263 12 : ereport(ERROR,
3264 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3265 : errmsg("column \"%s\" has a type conflict",
3266 : attributeName),
3267 : errdetail("%s versus %s",
3268 : format_type_with_typemod(inhtypeid, inhtypmod),
3269 : format_type_with_typemod(newtypeid, newtypmod))));
3270 :
3271 : /*
3272 : * Must have the same collation
3273 : */
3274 416 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3275 416 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3276 416 : if (inhcollid != newcollid)
3277 6 : ereport(ERROR,
3278 : (errcode(ERRCODE_COLLATION_MISMATCH),
3279 : errmsg("column \"%s\" has a collation conflict",
3280 : attributeName),
3281 : errdetail("\"%s\" versus \"%s\"",
3282 : get_collation_name(inhcollid),
3283 : get_collation_name(newcollid))));
3284 :
3285 : /*
3286 : * Identity is never inherited by a regular inheritance child. Pick
3287 : * child's identity definition if there's one.
3288 : */
3289 410 : inhdef->identity = newdef->identity;
3290 :
3291 : /*
3292 : * Copy storage parameter
3293 : */
3294 410 : if (inhdef->storage == 0)
3295 0 : inhdef->storage = newdef->storage;
3296 410 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3297 6 : ereport(ERROR,
3298 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3299 : errmsg("column \"%s\" has a storage parameter conflict",
3300 : attributeName),
3301 : errdetail("%s versus %s",
3302 : storage_name(inhdef->storage),
3303 : storage_name(newdef->storage))));
3304 :
3305 : /*
3306 : * Copy compression parameter
3307 : */
3308 404 : if (inhdef->compression == NULL)
3309 398 : inhdef->compression = newdef->compression;
3310 6 : else if (newdef->compression != NULL)
3311 : {
3312 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3313 6 : ereport(ERROR,
3314 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3315 : errmsg("column \"%s\" has a compression method conflict",
3316 : attributeName),
3317 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3318 : }
3319 :
3320 : /*
3321 : * Merge of not-null constraints = OR 'em together
3322 : */
3323 398 : inhdef->is_not_null |= newdef->is_not_null;
3324 :
3325 : /*
3326 : * Check for conflicts related to generated columns.
3327 : *
3328 : * If the parent column is generated, the child column will be made a
3329 : * generated column if it isn't already. If it is a generated column,
3330 : * we'll take its generation expression in preference to the parent's. We
3331 : * must check that the child column doesn't specify a default value or
3332 : * identity, which matches the rules for a single column in
3333 : * parse_utilcmd.c.
3334 : *
3335 : * Conversely, if the parent column is not generated, the child column
3336 : * can't be either. (We used to allow that, but it results in being able
3337 : * to override the generation expression via UPDATEs through the parent.)
3338 : */
3339 398 : if (inhdef->generated)
3340 : {
3341 78 : if (newdef->raw_default && !newdef->generated)
3342 12 : ereport(ERROR,
3343 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3344 : errmsg("column \"%s\" inherits from generated column but specifies default",
3345 : inhdef->colname)));
3346 66 : if (newdef->identity)
3347 12 : ereport(ERROR,
3348 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3349 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3350 : inhdef->colname)));
3351 : }
3352 : else
3353 : {
3354 320 : if (newdef->generated)
3355 12 : ereport(ERROR,
3356 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3357 : errmsg("child column \"%s\" specifies generation expression",
3358 : inhdef->colname),
3359 : errhint("A child table column cannot be generated unless its parent column is.")));
3360 : }
3361 :
3362 362 : if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3363 12 : ereport(ERROR,
3364 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3365 : errmsg("column \"%s\" inherits from generated column of different kind",
3366 : inhdef->colname),
3367 : errdetail("Parent column is %s, child column is %s.",
3368 : inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3369 : newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3370 :
3371 : /*
3372 : * If new def has a default, override previous default
3373 : */
3374 350 : if (newdef->raw_default != NULL)
3375 : {
3376 38 : inhdef->raw_default = newdef->raw_default;
3377 38 : inhdef->cooked_default = newdef->cooked_default;
3378 : }
3379 :
3380 : /* Mark the column as locally defined */
3381 350 : inhdef->is_local = true;
3382 350 : }
3383 :
3384 : /*
3385 : * MergeInheritedAttribute
3386 : * Merge given parent attribute definition into specified attribute
3387 : * inherited from the previous parents.
3388 : *
3389 : * Input arguments:
3390 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3391 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3392 : * 'newdef' is the new parent column/attribute definition to be merged.
3393 : *
3394 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3395 : *
3396 : * Notes:
3397 : * - The attribute is merged according to the rules laid out in the prologue
3398 : * of MergeAttributes().
3399 : * - If matching inherited attribute exists but the new attribute can not be
3400 : * merged into it, the function throws respective errors.
3401 : * - A partition inherits from only a single parent. Hence this function is
3402 : * applicable only to a regular inheritance.
3403 : */
3404 : static ColumnDef *
3405 390 : MergeInheritedAttribute(List *inh_columns,
3406 : int exist_attno,
3407 : const ColumnDef *newdef)
3408 : {
3409 390 : char *attributeName = newdef->colname;
3410 : ColumnDef *prevdef;
3411 : Oid prevtypeid,
3412 : newtypeid;
3413 : int32 prevtypmod,
3414 : newtypmod;
3415 : Oid prevcollid,
3416 : newcollid;
3417 :
3418 390 : ereport(NOTICE,
3419 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3420 : attributeName)));
3421 390 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3422 :
3423 : /*
3424 : * Must have the same type and typmod
3425 : */
3426 390 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3427 390 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3428 390 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3429 0 : ereport(ERROR,
3430 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3431 : errmsg("inherited column \"%s\" has a type conflict",
3432 : attributeName),
3433 : errdetail("%s versus %s",
3434 : format_type_with_typemod(prevtypeid, prevtypmod),
3435 : format_type_with_typemod(newtypeid, newtypmod))));
3436 :
3437 : /*
3438 : * Must have the same collation
3439 : */
3440 390 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3441 390 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3442 390 : if (prevcollid != newcollid)
3443 0 : ereport(ERROR,
3444 : (errcode(ERRCODE_COLLATION_MISMATCH),
3445 : errmsg("inherited column \"%s\" has a collation conflict",
3446 : attributeName),
3447 : errdetail("\"%s\" versus \"%s\"",
3448 : get_collation_name(prevcollid),
3449 : get_collation_name(newcollid))));
3450 :
3451 : /*
3452 : * Copy/check storage parameter
3453 : */
3454 390 : if (prevdef->storage == 0)
3455 0 : prevdef->storage = newdef->storage;
3456 390 : else if (prevdef->storage != newdef->storage)
3457 6 : ereport(ERROR,
3458 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3459 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3460 : attributeName),
3461 : errdetail("%s versus %s",
3462 : storage_name(prevdef->storage),
3463 : storage_name(newdef->storage))));
3464 :
3465 : /*
3466 : * Copy/check compression parameter
3467 : */
3468 384 : if (prevdef->compression == NULL)
3469 370 : prevdef->compression = newdef->compression;
3470 14 : else if (newdef->compression != NULL)
3471 : {
3472 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3473 6 : ereport(ERROR,
3474 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3475 : errmsg("column \"%s\" has a compression method conflict",
3476 : attributeName),
3477 : errdetail("%s versus %s",
3478 : prevdef->compression, newdef->compression)));
3479 : }
3480 :
3481 : /*
3482 : * Check for GENERATED conflicts
3483 : */
3484 378 : if (prevdef->generated != newdef->generated)
3485 24 : ereport(ERROR,
3486 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3487 : errmsg("inherited column \"%s\" has a generation conflict",
3488 : attributeName)));
3489 :
3490 : /*
3491 : * Default and other constraints are handled by the caller.
3492 : */
3493 :
3494 354 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3495 : &prevdef->inhcount))
3496 0 : ereport(ERROR,
3497 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3498 : errmsg("too many inheritance parents"));
3499 :
3500 354 : return prevdef;
3501 : }
3502 :
3503 : /*
3504 : * StoreCatalogInheritance
3505 : * Updates the system catalogs with proper inheritance information.
3506 : *
3507 : * supers is a list of the OIDs of the new relation's direct ancestors.
3508 : */
3509 : static void
3510 62642 : StoreCatalogInheritance(Oid relationId, List *supers,
3511 : bool child_is_partition)
3512 : {
3513 : Relation relation;
3514 : int32 seqNumber;
3515 : ListCell *entry;
3516 :
3517 : /*
3518 : * sanity checks
3519 : */
3520 : Assert(OidIsValid(relationId));
3521 :
3522 62642 : if (supers == NIL)
3523 53100 : return;
3524 :
3525 : /*
3526 : * Store INHERITS information in pg_inherits using direct ancestors only.
3527 : * Also enter dependencies on the direct ancestors, and make sure they are
3528 : * marked with relhassubclass = true.
3529 : *
3530 : * (Once upon a time, both direct and indirect ancestors were found here
3531 : * and then entered into pg_ipl. Since that catalog doesn't exist
3532 : * anymore, there's no need to look for indirect ancestors.)
3533 : */
3534 9542 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3535 :
3536 9542 : seqNumber = 1;
3537 19430 : foreach(entry, supers)
3538 : {
3539 9888 : Oid parentOid = lfirst_oid(entry);
3540 :
3541 9888 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3542 : child_is_partition);
3543 9888 : seqNumber++;
3544 : }
3545 :
3546 9542 : table_close(relation, RowExclusiveLock);
3547 : }
3548 :
3549 : /*
3550 : * Make catalog entries showing relationId as being an inheritance child
3551 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3552 : */
3553 : static void
3554 12726 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3555 : int32 seqNumber, Relation inhRelation,
3556 : bool child_is_partition)
3557 : {
3558 : ObjectAddress childobject,
3559 : parentobject;
3560 :
3561 : /* store the pg_inherits row */
3562 12726 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3563 :
3564 : /*
3565 : * Store a dependency too
3566 : */
3567 12726 : parentobject.classId = RelationRelationId;
3568 12726 : parentobject.objectId = parentOid;
3569 12726 : parentobject.objectSubId = 0;
3570 12726 : childobject.classId = RelationRelationId;
3571 12726 : childobject.objectId = relationId;
3572 12726 : childobject.objectSubId = 0;
3573 :
3574 12726 : recordDependencyOn(&childobject, &parentobject,
3575 : child_dependency_type(child_is_partition));
3576 :
3577 : /*
3578 : * Post creation hook of this inheritance. Since object_access_hook
3579 : * doesn't take multiple object identifiers, we relay oid of parent
3580 : * relation using auxiliary_id argument.
3581 : */
3582 12726 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3583 : relationId, 0,
3584 : parentOid, false);
3585 :
3586 : /*
3587 : * Mark the parent as having subclasses.
3588 : */
3589 12726 : SetRelationHasSubclass(parentOid, true);
3590 12726 : }
3591 :
3592 : /*
3593 : * Look for an existing column entry with the given name.
3594 : *
3595 : * Returns the index (starting with 1) if attribute already exists in columns,
3596 : * 0 if it doesn't.
3597 : */
3598 : static int
3599 21964 : findAttrByName(const char *attributeName, const List *columns)
3600 : {
3601 : ListCell *lc;
3602 21964 : int i = 1;
3603 :
3604 39368 : foreach(lc, columns)
3605 : {
3606 18222 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3607 818 : return i;
3608 :
3609 17404 : i++;
3610 : }
3611 21146 : return 0;
3612 : }
3613 :
3614 :
3615 : /*
3616 : * SetRelationHasSubclass
3617 : * Set the value of the relation's relhassubclass field in pg_class.
3618 : *
3619 : * It's always safe to set this field to true, because all SQL commands are
3620 : * ready to see true and then find no children. On the other hand, commands
3621 : * generally assume zero children if this is false.
3622 : *
3623 : * Caller must hold any self-exclusive lock until end of transaction. If the
3624 : * new value is false, caller must have acquired that lock before reading the
3625 : * evidence that justified the false value. That way, it properly waits if
3626 : * another backend is simultaneously concluding no need to change the tuple
3627 : * (new and old values are true).
3628 : *
3629 : * NOTE: an important side-effect of this operation is that an SI invalidation
3630 : * message is sent out to all backends --- including me --- causing plans
3631 : * referencing the relation to be rebuilt with the new list of children.
3632 : * This must happen even if we find that no change is needed in the pg_class
3633 : * row.
3634 : */
3635 : void
3636 15854 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3637 : {
3638 : Relation relationRelation;
3639 : HeapTuple tuple;
3640 : Form_pg_class classtuple;
3641 :
3642 : Assert(CheckRelationOidLockedByMe(relationId,
3643 : ShareUpdateExclusiveLock, false) ||
3644 : CheckRelationOidLockedByMe(relationId,
3645 : ShareRowExclusiveLock, true));
3646 :
3647 : /*
3648 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3649 : */
3650 15854 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3651 15854 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3652 15854 : if (!HeapTupleIsValid(tuple))
3653 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3654 15854 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3655 :
3656 15854 : if (classtuple->relhassubclass != relhassubclass)
3657 : {
3658 7990 : classtuple->relhassubclass = relhassubclass;
3659 7990 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3660 : }
3661 : else
3662 : {
3663 : /* no need to change tuple, but force relcache rebuild anyway */
3664 7864 : CacheInvalidateRelcacheByTuple(tuple);
3665 : }
3666 :
3667 15854 : heap_freetuple(tuple);
3668 15854 : table_close(relationRelation, RowExclusiveLock);
3669 15854 : }
3670 :
3671 : /*
3672 : * CheckRelationTableSpaceMove
3673 : * Check if relation can be moved to new tablespace.
3674 : *
3675 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3676 : *
3677 : * Returns true if the relation can be moved to the new tablespace; raises
3678 : * an error if it is not possible to do the move; returns false if the move
3679 : * would have no effect.
3680 : */
3681 : bool
3682 226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3683 : {
3684 : Oid oldTableSpaceId;
3685 :
3686 : /*
3687 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3688 : * stored as 0.
3689 : */
3690 226 : oldTableSpaceId = rel->rd_rel->reltablespace;
3691 226 : if (newTableSpaceId == oldTableSpaceId ||
3692 218 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3693 10 : return false;
3694 :
3695 : /*
3696 : * We cannot support moving mapped relations into different tablespaces.
3697 : * (In particular this eliminates all shared catalogs.)
3698 : */
3699 216 : if (RelationIsMapped(rel))
3700 0 : ereport(ERROR,
3701 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3702 : errmsg("cannot move system relation \"%s\"",
3703 : RelationGetRelationName(rel))));
3704 :
3705 : /* Cannot move a non-shared relation into pg_global */
3706 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3707 12 : ereport(ERROR,
3708 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3709 : errmsg("only shared relations can be placed in pg_global tablespace")));
3710 :
3711 : /*
3712 : * Do not allow moving temp tables of other backends ... their local
3713 : * buffer manager is not going to cope.
3714 : */
3715 204 : if (RELATION_IS_OTHER_TEMP(rel))
3716 0 : ereport(ERROR,
3717 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3718 : errmsg("cannot move temporary tables of other sessions")));
3719 :
3720 204 : return true;
3721 : }
3722 :
3723 : /*
3724 : * SetRelationTableSpace
3725 : * Set new reltablespace and relfilenumber in pg_class entry.
3726 : *
3727 : * newTableSpaceId is the new tablespace for the relation, and
3728 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3729 : * InvalidRelFileNumber, this field is not updated.
3730 : *
3731 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3732 : *
3733 : * The caller of this routine had better check if a relation can be
3734 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3735 : * first, and is responsible for making the change visible with
3736 : * CommandCounterIncrement().
3737 : */
3738 : void
3739 204 : SetRelationTableSpace(Relation rel,
3740 : Oid newTableSpaceId,
3741 : RelFileNumber newRelFilenumber)
3742 : {
3743 : Relation pg_class;
3744 : HeapTuple tuple;
3745 : ItemPointerData otid;
3746 : Form_pg_class rd_rel;
3747 204 : Oid reloid = RelationGetRelid(rel);
3748 :
3749 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3750 :
3751 : /* Get a modifiable copy of the relation's pg_class row. */
3752 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3753 :
3754 204 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3755 204 : if (!HeapTupleIsValid(tuple))
3756 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3757 204 : otid = tuple->t_self;
3758 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3759 :
3760 : /* Update the pg_class row. */
3761 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3762 204 : InvalidOid : newTableSpaceId;
3763 204 : if (RelFileNumberIsValid(newRelFilenumber))
3764 160 : rd_rel->relfilenode = newRelFilenumber;
3765 204 : CatalogTupleUpdate(pg_class, &otid, tuple);
3766 204 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3767 :
3768 : /*
3769 : * Record dependency on tablespace. This is only required for relations
3770 : * that have no physical storage.
3771 : */
3772 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3773 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3774 : rd_rel->reltablespace);
3775 :
3776 204 : heap_freetuple(tuple);
3777 204 : table_close(pg_class, RowExclusiveLock);
3778 204 : }
3779 :
3780 : /*
3781 : * renameatt_check - basic sanity checks before attribute rename
3782 : */
3783 : static void
3784 1022 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3785 : {
3786 1022 : char relkind = classform->relkind;
3787 :
3788 1022 : if (classform->reloftype && !recursing)
3789 6 : ereport(ERROR,
3790 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3791 : errmsg("cannot rename column of typed table")));
3792 :
3793 : /*
3794 : * Renaming the columns of sequences or toast tables doesn't actually
3795 : * break anything from the system's point of view, since internal
3796 : * references are by attnum. But it doesn't seem right to allow users to
3797 : * change names that are hardcoded into the system, hence the following
3798 : * restriction.
3799 : */
3800 1016 : if (relkind != RELKIND_RELATION &&
3801 84 : relkind != RELKIND_VIEW &&
3802 84 : relkind != RELKIND_MATVIEW &&
3803 36 : relkind != RELKIND_COMPOSITE_TYPE &&
3804 36 : relkind != RELKIND_INDEX &&
3805 36 : relkind != RELKIND_PARTITIONED_INDEX &&
3806 0 : relkind != RELKIND_FOREIGN_TABLE &&
3807 : relkind != RELKIND_PARTITIONED_TABLE)
3808 0 : ereport(ERROR,
3809 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3810 : errmsg("cannot rename columns of relation \"%s\"",
3811 : NameStr(classform->relname)),
3812 : errdetail_relkind_not_supported(relkind)));
3813 :
3814 : /*
3815 : * permissions checking. only the owner of a class can change its schema.
3816 : */
3817 1016 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3818 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3819 0 : NameStr(classform->relname));
3820 1016 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3821 2 : ereport(ERROR,
3822 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3823 : errmsg("permission denied: \"%s\" is a system catalog",
3824 : NameStr(classform->relname))));
3825 1014 : }
3826 :
3827 : /*
3828 : * renameatt_internal - workhorse for renameatt
3829 : *
3830 : * Return value is the attribute number in the 'myrelid' relation.
3831 : */
3832 : static AttrNumber
3833 552 : renameatt_internal(Oid myrelid,
3834 : const char *oldattname,
3835 : const char *newattname,
3836 : bool recurse,
3837 : bool recursing,
3838 : int expected_parents,
3839 : DropBehavior behavior)
3840 : {
3841 : Relation targetrelation;
3842 : Relation attrelation;
3843 : HeapTuple atttup;
3844 : Form_pg_attribute attform;
3845 : AttrNumber attnum;
3846 :
3847 : /*
3848 : * Grab an exclusive lock on the target table, which we will NOT release
3849 : * until end of transaction.
3850 : */
3851 552 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3852 552 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3853 :
3854 : /*
3855 : * if the 'recurse' flag is set then we are supposed to rename this
3856 : * attribute in all classes that inherit from 'relname' (as well as in
3857 : * 'relname').
3858 : *
3859 : * any permissions or problems with duplicate attributes will cause the
3860 : * whole transaction to abort, which is what we want -- all or nothing.
3861 : */
3862 552 : if (recurse)
3863 : {
3864 : List *child_oids,
3865 : *child_numparents;
3866 : ListCell *lo,
3867 : *li;
3868 :
3869 : /*
3870 : * we need the number of parents for each child so that the recursive
3871 : * calls to renameatt() can determine whether there are any parents
3872 : * outside the inheritance hierarchy being processed.
3873 : */
3874 248 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3875 : &child_numparents);
3876 :
3877 : /*
3878 : * find_all_inheritors does the recursive search of the inheritance
3879 : * hierarchy, so all we have to do is process all of the relids in the
3880 : * list that it returns.
3881 : */
3882 734 : forboth(lo, child_oids, li, child_numparents)
3883 : {
3884 516 : Oid childrelid = lfirst_oid(lo);
3885 516 : int numparents = lfirst_int(li);
3886 :
3887 516 : if (childrelid == myrelid)
3888 248 : continue;
3889 : /* note we need not recurse again */
3890 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3891 : }
3892 : }
3893 : else
3894 : {
3895 : /*
3896 : * If we are told not to recurse, there had better not be any child
3897 : * tables; else the rename would put them out of step.
3898 : *
3899 : * expected_parents will only be 0 if we are not already recursing.
3900 : */
3901 340 : if (expected_parents == 0 &&
3902 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3903 12 : ereport(ERROR,
3904 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3905 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3906 : oldattname)));
3907 : }
3908 :
3909 : /* rename attributes in typed tables of composite type */
3910 510 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3911 : {
3912 : List *child_oids;
3913 : ListCell *lo;
3914 :
3915 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3916 24 : RelationGetRelationName(targetrelation),
3917 : behavior);
3918 :
3919 24 : foreach(lo, child_oids)
3920 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3921 : }
3922 :
3923 504 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3924 :
3925 504 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3926 504 : if (!HeapTupleIsValid(atttup))
3927 24 : ereport(ERROR,
3928 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3929 : errmsg("column \"%s\" does not exist",
3930 : oldattname)));
3931 480 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3932 :
3933 480 : attnum = attform->attnum;
3934 480 : if (attnum <= 0)
3935 0 : ereport(ERROR,
3936 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3937 : errmsg("cannot rename system column \"%s\"",
3938 : oldattname)));
3939 :
3940 : /*
3941 : * if the attribute is inherited, forbid the renaming. if this is a
3942 : * top-level call to renameatt(), then expected_parents will be 0, so the
3943 : * effect of this code will be to prohibit the renaming if the attribute
3944 : * is inherited at all. if this is a recursive call to renameatt(),
3945 : * expected_parents will be the number of parents the current relation has
3946 : * within the inheritance hierarchy being processed, so we'll prohibit the
3947 : * renaming only if there are additional parents from elsewhere.
3948 : */
3949 480 : if (attform->attinhcount > expected_parents)
3950 30 : ereport(ERROR,
3951 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3952 : errmsg("cannot rename inherited column \"%s\"",
3953 : oldattname)));
3954 :
3955 : /* new name should not already exist */
3956 450 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3957 :
3958 : /* apply the update */
3959 438 : namestrcpy(&(attform->attname), newattname);
3960 :
3961 438 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3962 :
3963 438 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3964 :
3965 438 : heap_freetuple(atttup);
3966 :
3967 438 : table_close(attrelation, RowExclusiveLock);
3968 :
3969 438 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3970 :
3971 438 : return attnum;
3972 : }
3973 :
3974 : /*
3975 : * Perform permissions and integrity checks before acquiring a relation lock.
3976 : */
3977 : static void
3978 424 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3979 : void *arg)
3980 : {
3981 : HeapTuple tuple;
3982 : Form_pg_class form;
3983 :
3984 424 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3985 424 : if (!HeapTupleIsValid(tuple))
3986 38 : return; /* concurrently dropped */
3987 386 : form = (Form_pg_class) GETSTRUCT(tuple);
3988 386 : renameatt_check(relid, form, false);
3989 378 : ReleaseSysCache(tuple);
3990 : }
3991 :
3992 : /*
3993 : * renameatt - changes the name of an attribute in a relation
3994 : *
3995 : * The returned ObjectAddress is that of the renamed column.
3996 : */
3997 : ObjectAddress
3998 316 : renameatt(RenameStmt *stmt)
3999 : {
4000 : Oid relid;
4001 : AttrNumber attnum;
4002 : ObjectAddress address;
4003 :
4004 : /* lock level taken here should match renameatt_internal */
4005 316 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4006 316 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4007 : RangeVarCallbackForRenameAttribute,
4008 : NULL);
4009 :
4010 302 : if (!OidIsValid(relid))
4011 : {
4012 24 : ereport(NOTICE,
4013 : (errmsg("relation \"%s\" does not exist, skipping",
4014 : stmt->relation->relname)));
4015 24 : return InvalidObjectAddress;
4016 : }
4017 :
4018 : attnum =
4019 278 : renameatt_internal(relid,
4020 278 : stmt->subname, /* old att name */
4021 278 : stmt->newname, /* new att name */
4022 278 : stmt->relation->inh, /* recursive? */
4023 : false, /* recursing? */
4024 : 0, /* expected inhcount */
4025 : stmt->behavior);
4026 :
4027 194 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4028 :
4029 194 : return address;
4030 : }
4031 :
4032 : /*
4033 : * same logic as renameatt_internal
4034 : */
4035 : static ObjectAddress
4036 90 : rename_constraint_internal(Oid myrelid,
4037 : Oid mytypid,
4038 : const char *oldconname,
4039 : const char *newconname,
4040 : bool recurse,
4041 : bool recursing,
4042 : int expected_parents)
4043 : {
4044 90 : Relation targetrelation = NULL;
4045 : Oid constraintOid;
4046 : HeapTuple tuple;
4047 : Form_pg_constraint con;
4048 : ObjectAddress address;
4049 :
4050 : Assert(!myrelid || !mytypid);
4051 :
4052 90 : if (mytypid)
4053 : {
4054 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4055 : }
4056 : else
4057 : {
4058 84 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4059 :
4060 : /*
4061 : * don't tell it whether we're recursing; we allow changing typed
4062 : * tables here
4063 : */
4064 84 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4065 :
4066 84 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4067 : }
4068 :
4069 90 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4070 90 : if (!HeapTupleIsValid(tuple))
4071 0 : elog(ERROR, "cache lookup failed for constraint %u",
4072 : constraintOid);
4073 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4074 :
4075 90 : if (myrelid &&
4076 84 : (con->contype == CONSTRAINT_CHECK ||
4077 24 : con->contype == CONSTRAINT_NOTNULL) &&
4078 66 : !con->connoinherit)
4079 : {
4080 54 : if (recurse)
4081 : {
4082 : List *child_oids,
4083 : *child_numparents;
4084 : ListCell *lo,
4085 : *li;
4086 :
4087 36 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4088 : &child_numparents);
4089 :
4090 84 : forboth(lo, child_oids, li, child_numparents)
4091 : {
4092 48 : Oid childrelid = lfirst_oid(lo);
4093 48 : int numparents = lfirst_int(li);
4094 :
4095 48 : if (childrelid == myrelid)
4096 36 : continue;
4097 :
4098 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4099 : }
4100 : }
4101 : else
4102 : {
4103 24 : if (expected_parents == 0 &&
4104 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4105 6 : ereport(ERROR,
4106 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4107 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4108 : oldconname)));
4109 : }
4110 :
4111 48 : if (con->coninhcount > expected_parents)
4112 6 : ereport(ERROR,
4113 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4114 : errmsg("cannot rename inherited constraint \"%s\"",
4115 : oldconname)));
4116 : }
4117 :
4118 78 : if (con->conindid
4119 18 : && (con->contype == CONSTRAINT_PRIMARY
4120 6 : || con->contype == CONSTRAINT_UNIQUE
4121 0 : || con->contype == CONSTRAINT_EXCLUSION))
4122 : /* rename the index; this renames the constraint as well */
4123 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4124 : else
4125 60 : RenameConstraintById(constraintOid, newconname);
4126 :
4127 78 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4128 :
4129 78 : ReleaseSysCache(tuple);
4130 :
4131 78 : if (targetrelation)
4132 : {
4133 : /*
4134 : * Invalidate relcache so as others can see the new constraint name.
4135 : */
4136 72 : CacheInvalidateRelcache(targetrelation);
4137 :
4138 72 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4139 : }
4140 :
4141 78 : return address;
4142 : }
4143 :
4144 : ObjectAddress
4145 84 : RenameConstraint(RenameStmt *stmt)
4146 : {
4147 84 : Oid relid = InvalidOid;
4148 84 : Oid typid = InvalidOid;
4149 :
4150 84 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4151 : {
4152 : Relation rel;
4153 : HeapTuple tup;
4154 :
4155 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4156 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4157 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4158 6 : if (!HeapTupleIsValid(tup))
4159 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4160 6 : checkDomainOwner(tup);
4161 6 : ReleaseSysCache(tup);
4162 6 : table_close(rel, NoLock);
4163 : }
4164 : else
4165 : {
4166 : /* lock level taken here should match rename_constraint_internal */
4167 78 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4168 78 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4169 : RangeVarCallbackForRenameAttribute,
4170 : NULL);
4171 78 : if (!OidIsValid(relid))
4172 : {
4173 6 : ereport(NOTICE,
4174 : (errmsg("relation \"%s\" does not exist, skipping",
4175 : stmt->relation->relname)));
4176 6 : return InvalidObjectAddress;
4177 : }
4178 : }
4179 :
4180 : return
4181 78 : rename_constraint_internal(relid, typid,
4182 78 : stmt->subname,
4183 78 : stmt->newname,
4184 150 : (stmt->relation &&
4185 72 : stmt->relation->inh), /* recursive? */
4186 : false, /* recursing? */
4187 : 0 /* expected inhcount */ );
4188 : }
4189 :
4190 : /*
4191 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4192 : * RENAME
4193 : */
4194 : ObjectAddress
4195 510 : RenameRelation(RenameStmt *stmt)
4196 : {
4197 510 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4198 : Oid relid;
4199 : ObjectAddress address;
4200 :
4201 : /*
4202 : * Grab an exclusive lock on the target table, index, sequence, view,
4203 : * materialized view, or foreign table, which we will NOT release until
4204 : * end of transaction.
4205 : *
4206 : * Lock level used here should match RenameRelationInternal, to avoid lock
4207 : * escalation. However, because ALTER INDEX can be used with any relation
4208 : * type, we mustn't believe without verification.
4209 : */
4210 : for (;;)
4211 12 : {
4212 : LOCKMODE lockmode;
4213 : char relkind;
4214 : bool obj_is_index;
4215 :
4216 522 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4217 :
4218 522 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4219 522 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4220 : RangeVarCallbackForAlterRelation,
4221 : stmt);
4222 :
4223 472 : if (!OidIsValid(relid))
4224 : {
4225 18 : ereport(NOTICE,
4226 : (errmsg("relation \"%s\" does not exist, skipping",
4227 : stmt->relation->relname)));
4228 18 : return InvalidObjectAddress;
4229 : }
4230 :
4231 : /*
4232 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4233 : * to rename a table), but we might've used the wrong lock level. If
4234 : * that happens, retry with the correct lock level. We don't bother
4235 : * if we already acquired AccessExclusiveLock with an index, however.
4236 : */
4237 454 : relkind = get_rel_relkind(relid);
4238 454 : obj_is_index = (relkind == RELKIND_INDEX ||
4239 : relkind == RELKIND_PARTITIONED_INDEX);
4240 454 : if (obj_is_index || is_index_stmt == obj_is_index)
4241 : break;
4242 :
4243 12 : UnlockRelationOid(relid, lockmode);
4244 12 : is_index_stmt = obj_is_index;
4245 : }
4246 :
4247 : /* Do the work */
4248 442 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4249 :
4250 430 : ObjectAddressSet(address, RelationRelationId, relid);
4251 :
4252 430 : return address;
4253 : }
4254 :
4255 : /*
4256 : * RenameRelationInternal - change the name of a relation
4257 : */
4258 : void
4259 1702 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4260 : {
4261 : Relation targetrelation;
4262 : Relation relrelation; /* for RELATION relation */
4263 : ItemPointerData otid;
4264 : HeapTuple reltup;
4265 : Form_pg_class relform;
4266 : Oid namespaceId;
4267 :
4268 : /*
4269 : * Grab a lock on the target relation, which we will NOT release until end
4270 : * of transaction. We need at least a self-exclusive lock so that
4271 : * concurrent DDL doesn't overwrite the rename if they start updating
4272 : * while still seeing the old version. The lock also guards against
4273 : * triggering relcache reloads in concurrent sessions, which might not
4274 : * handle this information changing under them. For indexes, we can use a
4275 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4276 : * specially.
4277 : */
4278 1702 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4279 1702 : namespaceId = RelationGetNamespace(targetrelation);
4280 :
4281 : /*
4282 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4283 : */
4284 1702 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4285 :
4286 1702 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4287 1702 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4288 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4289 1702 : otid = reltup->t_self;
4290 1702 : relform = (Form_pg_class) GETSTRUCT(reltup);
4291 :
4292 1702 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4293 12 : ereport(ERROR,
4294 : (errcode(ERRCODE_DUPLICATE_TABLE),
4295 : errmsg("relation \"%s\" already exists",
4296 : newrelname)));
4297 :
4298 : /*
4299 : * RenameRelation is careful not to believe the caller's idea of the
4300 : * relation kind being handled. We don't have to worry about this, but
4301 : * let's not be totally oblivious to it. We can process an index as
4302 : * not-an-index, but not the other way around.
4303 : */
4304 : Assert(!is_index ||
4305 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4306 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4307 :
4308 : /*
4309 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4310 : * because it's a copy...)
4311 : */
4312 1690 : namestrcpy(&(relform->relname), newrelname);
4313 :
4314 1690 : CatalogTupleUpdate(relrelation, &otid, reltup);
4315 1690 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4316 :
4317 1690 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4318 : InvalidOid, is_internal);
4319 :
4320 1690 : heap_freetuple(reltup);
4321 1690 : table_close(relrelation, RowExclusiveLock);
4322 :
4323 : /*
4324 : * Also rename the associated type, if any.
4325 : */
4326 1690 : if (OidIsValid(targetrelation->rd_rel->reltype))
4327 124 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4328 : newrelname, namespaceId);
4329 :
4330 : /*
4331 : * Also rename the associated constraint, if any.
4332 : */
4333 1690 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4334 886 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4335 : {
4336 822 : Oid constraintId = get_index_constraint(myrelid);
4337 :
4338 822 : if (OidIsValid(constraintId))
4339 36 : RenameConstraintById(constraintId, newrelname);
4340 : }
4341 :
4342 : /*
4343 : * Close rel, but keep lock!
4344 : */
4345 1690 : relation_close(targetrelation, NoLock);
4346 1690 : }
4347 :
4348 : /*
4349 : * ResetRelRewrite - reset relrewrite
4350 : */
4351 : void
4352 610 : ResetRelRewrite(Oid myrelid)
4353 : {
4354 : Relation relrelation; /* for RELATION relation */
4355 : HeapTuple reltup;
4356 : Form_pg_class relform;
4357 :
4358 : /*
4359 : * Find relation's pg_class tuple.
4360 : */
4361 610 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4362 :
4363 610 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4364 610 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4365 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4366 610 : relform = (Form_pg_class) GETSTRUCT(reltup);
4367 :
4368 : /*
4369 : * Update pg_class tuple.
4370 : */
4371 610 : relform->relrewrite = InvalidOid;
4372 :
4373 610 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4374 :
4375 610 : heap_freetuple(reltup);
4376 610 : table_close(relrelation, RowExclusiveLock);
4377 610 : }
4378 :
4379 : /*
4380 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4381 : * any open reference to the target table besides the one just acquired by
4382 : * the calling command; this implies there's an open cursor or active plan.
4383 : * We need this check because our lock doesn't protect us against stomping
4384 : * on our own foot, only other people's feet!
4385 : *
4386 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4387 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4388 : * possibly be relaxed to only error out for certain types of alterations.
4389 : * But the use-case for allowing any of these things is not obvious, so we
4390 : * won't work hard at it for now.
4391 : *
4392 : * We also reject these commands if there are any pending AFTER trigger events
4393 : * for the rel. This is certainly necessary for the rewriting variants of
4394 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4395 : * events would try to fetch the wrong tuples. It might be overly cautious
4396 : * in other cases, but again it seems better to err on the side of paranoia.
4397 : *
4398 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4399 : * we are worried about active indexscans on the index. The trigger-event
4400 : * check can be skipped, since we are doing no damage to the parent table.
4401 : *
4402 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4403 : */
4404 : void
4405 172552 : CheckTableNotInUse(Relation rel, const char *stmt)
4406 : {
4407 : int expected_refcnt;
4408 :
4409 172552 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4410 172552 : if (rel->rd_refcnt != expected_refcnt)
4411 42 : ereport(ERROR,
4412 : (errcode(ERRCODE_OBJECT_IN_USE),
4413 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4414 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4415 : stmt, RelationGetRelationName(rel))));
4416 :
4417 172510 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4418 282062 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4419 139948 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4420 18 : ereport(ERROR,
4421 : (errcode(ERRCODE_OBJECT_IN_USE),
4422 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4423 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4424 : stmt, RelationGetRelationName(rel))));
4425 172492 : }
4426 :
4427 : /*
4428 : * CheckAlterTableIsSafe
4429 : * Verify that it's safe to allow ALTER TABLE on this relation.
4430 : *
4431 : * This consists of CheckTableNotInUse() plus a check that the relation
4432 : * isn't another session's temp table. We must split out the temp-table
4433 : * check because there are callers of CheckTableNotInUse() that don't want
4434 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4435 : * an orphaned temp schema.) Compare truncate_check_activity().
4436 : */
4437 : static void
4438 63808 : CheckAlterTableIsSafe(Relation rel)
4439 : {
4440 : /*
4441 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4442 : * manager is not going to cope if we need to change the table's contents.
4443 : * Even if we don't, there may be optimizations that assume temp tables
4444 : * aren't subject to such interference.
4445 : */
4446 63808 : if (RELATION_IS_OTHER_TEMP(rel))
4447 0 : ereport(ERROR,
4448 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4449 : errmsg("cannot alter temporary tables of other sessions")));
4450 :
4451 : /*
4452 : * Also check for active uses of the relation in the current transaction,
4453 : * including open scans and pending AFTER trigger events.
4454 : */
4455 63808 : CheckTableNotInUse(rel, "ALTER TABLE");
4456 63772 : }
4457 :
4458 : /*
4459 : * AlterTableLookupRelation
4460 : * Look up, and lock, the OID for the relation named by an alter table
4461 : * statement.
4462 : */
4463 : Oid
4464 34538 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4465 : {
4466 68988 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4467 34538 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4468 : RangeVarCallbackForAlterRelation,
4469 : stmt);
4470 : }
4471 :
4472 : /*
4473 : * AlterTable
4474 : * Execute ALTER TABLE, which can be a list of subcommands
4475 : *
4476 : * ALTER TABLE is performed in three phases:
4477 : * 1. Examine subcommands and perform pre-transformation checking.
4478 : * 2. Validate and transform subcommands, and update system catalogs.
4479 : * 3. Scan table(s) to check new constraints, and optionally recopy
4480 : * the data into new table(s).
4481 : * Phase 3 is not performed unless one or more of the subcommands requires
4482 : * it. The intention of this design is to allow multiple independent
4483 : * updates of the table schema to be performed with only one pass over the
4484 : * data.
4485 : *
4486 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4487 : * each table to be affected (there may be multiple affected tables if the
4488 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4489 : * validation of the subcommands. Because earlier subcommands may change
4490 : * the catalog state seen by later commands, there are limits to what can
4491 : * be done in this phase. Generally, this phase acquires table locks,
4492 : * checks permissions and relkind, and recurses to find child tables.
4493 : *
4494 : * ATRewriteCatalogs performs phase 2 for each affected table.
4495 : * Certain subcommands need to be performed before others to avoid
4496 : * unnecessary conflicts; for example, DROP COLUMN should come before
4497 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4498 : * lists, one for each logical "pass" of phase 2.
4499 : *
4500 : * ATRewriteTables performs phase 3 for those tables that need it.
4501 : *
4502 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4503 : * since phase 1 already does it. However, for certain subcommand types
4504 : * it is only possible to determine how to recurse at phase 2 time; for
4505 : * those cases, phase 1 sets the cmd->recurse flag.
4506 : *
4507 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4508 : * the whole operation; we don't have to do anything special to clean up.
4509 : *
4510 : * The caller must lock the relation, with an appropriate lock level
4511 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4512 : * or higher. We pass the lock level down
4513 : * so that we can apply it recursively to inherited tables. Note that the
4514 : * lock level we want as we recurse might well be higher than required for
4515 : * that specific subcommand. So we pass down the overall lock requirement,
4516 : * rather than reassess it at lower levels.
4517 : *
4518 : * The caller also provides a "context" which is to be passed back to
4519 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4520 : * Some of the fields therein, such as the relid, are used here as well.
4521 : */
4522 : void
4523 34312 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4524 : AlterTableUtilityContext *context)
4525 : {
4526 : Relation rel;
4527 :
4528 : /* Caller is required to provide an adequate lock. */
4529 34312 : rel = relation_open(context->relid, NoLock);
4530 :
4531 34312 : CheckAlterTableIsSafe(rel);
4532 :
4533 34294 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4534 30622 : }
4535 :
4536 : /*
4537 : * AlterTableInternal
4538 : *
4539 : * ALTER TABLE with target specified by OID
4540 : *
4541 : * We do not reject if the relation is already open, because it's quite
4542 : * likely that one or more layers of caller have it open. That means it
4543 : * is unsafe to use this entry point for alterations that could break
4544 : * existing query plans. On the assumption it's not used for such, we
4545 : * don't have to reject pending AFTER triggers, either.
4546 : *
4547 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4548 : * used for any subcommand types that require parse transformation or
4549 : * could generate subcommands that have to be passed to ProcessUtility.
4550 : */
4551 : void
4552 282 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4553 : {
4554 : Relation rel;
4555 282 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4556 :
4557 282 : rel = relation_open(relid, lockmode);
4558 :
4559 282 : EventTriggerAlterTableRelid(relid);
4560 :
4561 282 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4562 282 : }
4563 :
4564 : /*
4565 : * AlterTableGetLockLevel
4566 : *
4567 : * Sets the overall lock level required for the supplied list of subcommands.
4568 : * Policy for doing this set according to needs of AlterTable(), see
4569 : * comments there for overall explanation.
4570 : *
4571 : * Function is called before and after parsing, so it must give same
4572 : * answer each time it is called. Some subcommands are transformed
4573 : * into other subcommand types, so the transform must never be made to a
4574 : * lower lock level than previously assigned. All transforms are noted below.
4575 : *
4576 : * Since this is called before we lock the table we cannot use table metadata
4577 : * to influence the type of lock we acquire.
4578 : *
4579 : * There should be no lockmodes hardcoded into the subcommand functions. All
4580 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4581 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4582 : * and does not travel through this section of code and cannot be combined with
4583 : * any of the subcommands given here.
4584 : *
4585 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4586 : * so any changes that might affect SELECTs running on standbys need to use
4587 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4588 : * have a solution for that also.
4589 : *
4590 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4591 : * that takes a lock less than AccessExclusiveLock can change object definitions
4592 : * while pg_dump is running. Be careful to check that the appropriate data is
4593 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4594 : * otherwise we might end up with an inconsistent dump that can't restore.
4595 : */
4596 : LOCKMODE
4597 34820 : AlterTableGetLockLevel(List *cmds)
4598 : {
4599 : /*
4600 : * This only works if we read catalog tables using MVCC snapshots.
4601 : */
4602 : ListCell *lcmd;
4603 34820 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4604 :
4605 70812 : foreach(lcmd, cmds)
4606 : {
4607 35992 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4608 35992 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4609 :
4610 35992 : switch (cmd->subtype)
4611 : {
4612 : /*
4613 : * These subcommands rewrite the heap, so require full locks.
4614 : */
4615 3584 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4616 : * to SELECT */
4617 : case AT_SetAccessMethod: /* must rewrite heap */
4618 : case AT_SetTableSpace: /* must rewrite heap */
4619 : case AT_AlterColumnType: /* must rewrite heap */
4620 3584 : cmd_lockmode = AccessExclusiveLock;
4621 3584 : break;
4622 :
4623 : /*
4624 : * These subcommands may require addition of toast tables. If
4625 : * we add a toast table to a table currently being scanned, we
4626 : * might miss data added to the new toast table by concurrent
4627 : * insert transactions.
4628 : */
4629 224 : case AT_SetStorage: /* may add toast tables, see
4630 : * ATRewriteCatalogs() */
4631 224 : cmd_lockmode = AccessExclusiveLock;
4632 224 : break;
4633 :
4634 : /*
4635 : * Removing constraints can affect SELECTs that have been
4636 : * optimized assuming the constraint holds true. See also
4637 : * CloneFkReferenced.
4638 : */
4639 1110 : case AT_DropConstraint: /* as DROP INDEX */
4640 : case AT_DropNotNull: /* may change some SQL plans */
4641 1110 : cmd_lockmode = AccessExclusiveLock;
4642 1110 : break;
4643 :
4644 : /*
4645 : * Subcommands that may be visible to concurrent SELECTs
4646 : */
4647 1758 : case AT_DropColumn: /* change visible to SELECT */
4648 : case AT_AddColumnToView: /* CREATE VIEW */
4649 : case AT_DropOids: /* used to equiv to DropColumn */
4650 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4651 : case AT_EnableReplicaRule: /* may change SELECT rules */
4652 : case AT_EnableRule: /* may change SELECT rules */
4653 : case AT_DisableRule: /* may change SELECT rules */
4654 1758 : cmd_lockmode = AccessExclusiveLock;
4655 1758 : break;
4656 :
4657 : /*
4658 : * Changing owner may remove implicit SELECT privileges
4659 : */
4660 3610 : case AT_ChangeOwner: /* change visible to SELECT */
4661 3610 : cmd_lockmode = AccessExclusiveLock;
4662 3610 : break;
4663 :
4664 : /*
4665 : * Changing foreign table options may affect optimization.
4666 : */
4667 254 : case AT_GenericOptions:
4668 : case AT_AlterColumnGenericOptions:
4669 254 : cmd_lockmode = AccessExclusiveLock;
4670 254 : break;
4671 :
4672 : /*
4673 : * These subcommands affect write operations only.
4674 : */
4675 346 : case AT_EnableTrig:
4676 : case AT_EnableAlwaysTrig:
4677 : case AT_EnableReplicaTrig:
4678 : case AT_EnableTrigAll:
4679 : case AT_EnableTrigUser:
4680 : case AT_DisableTrig:
4681 : case AT_DisableTrigAll:
4682 : case AT_DisableTrigUser:
4683 346 : cmd_lockmode = ShareRowExclusiveLock;
4684 346 : break;
4685 :
4686 : /*
4687 : * These subcommands affect write operations only. XXX
4688 : * Theoretically, these could be ShareRowExclusiveLock.
4689 : */
4690 2952 : case AT_ColumnDefault:
4691 : case AT_CookedColumnDefault:
4692 : case AT_AlterConstraint:
4693 : case AT_AddIndex: /* from ADD CONSTRAINT */
4694 : case AT_AddIndexConstraint:
4695 : case AT_ReplicaIdentity:
4696 : case AT_SetNotNull:
4697 : case AT_EnableRowSecurity:
4698 : case AT_DisableRowSecurity:
4699 : case AT_ForceRowSecurity:
4700 : case AT_NoForceRowSecurity:
4701 : case AT_AddIdentity:
4702 : case AT_DropIdentity:
4703 : case AT_SetIdentity:
4704 : case AT_SetExpression:
4705 : case AT_DropExpression:
4706 : case AT_SetCompression:
4707 2952 : cmd_lockmode = AccessExclusiveLock;
4708 2952 : break;
4709 :
4710 15846 : case AT_AddConstraint:
4711 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4712 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4713 15846 : if (IsA(cmd->def, Constraint))
4714 : {
4715 15846 : Constraint *con = (Constraint *) cmd->def;
4716 :
4717 15846 : switch (con->contype)
4718 : {
4719 12166 : case CONSTR_EXCLUSION:
4720 : case CONSTR_PRIMARY:
4721 : case CONSTR_UNIQUE:
4722 :
4723 : /*
4724 : * Cases essentially the same as CREATE INDEX. We
4725 : * could reduce the lock strength to ShareLock if
4726 : * we can work out how to allow concurrent catalog
4727 : * updates. XXX Might be set down to
4728 : * ShareRowExclusiveLock but requires further
4729 : * analysis.
4730 : */
4731 12166 : cmd_lockmode = AccessExclusiveLock;
4732 12166 : break;
4733 2572 : case CONSTR_FOREIGN:
4734 :
4735 : /*
4736 : * We add triggers to both tables when we add a
4737 : * Foreign Key, so the lock level must be at least
4738 : * as strong as CREATE TRIGGER.
4739 : */
4740 2572 : cmd_lockmode = ShareRowExclusiveLock;
4741 2572 : break;
4742 :
4743 1108 : default:
4744 1108 : cmd_lockmode = AccessExclusiveLock;
4745 : }
4746 : }
4747 15846 : break;
4748 :
4749 : /*
4750 : * These subcommands affect inheritance behaviour. Queries
4751 : * started before us will continue to see the old inheritance
4752 : * behaviour, while queries started after we commit will see
4753 : * new behaviour. No need to prevent reads or writes to the
4754 : * subtable while we hook it up though. Changing the TupDesc
4755 : * may be a problem, so keep highest lock.
4756 : */
4757 530 : case AT_AddInherit:
4758 : case AT_DropInherit:
4759 530 : cmd_lockmode = AccessExclusiveLock;
4760 530 : break;
4761 :
4762 : /*
4763 : * These subcommands affect implicit row type conversion. They
4764 : * have affects similar to CREATE/DROP CAST on queries. don't
4765 : * provide for invalidating parse trees as a result of such
4766 : * changes, so we keep these at AccessExclusiveLock.
4767 : */
4768 72 : case AT_AddOf:
4769 : case AT_DropOf:
4770 72 : cmd_lockmode = AccessExclusiveLock;
4771 72 : break;
4772 :
4773 : /*
4774 : * Only used by CREATE OR REPLACE VIEW which must conflict
4775 : * with an SELECTs currently using the view.
4776 : */
4777 198 : case AT_ReplaceRelOptions:
4778 198 : cmd_lockmode = AccessExclusiveLock;
4779 198 : break;
4780 :
4781 : /*
4782 : * These subcommands affect general strategies for performance
4783 : * and maintenance, though don't change the semantic results
4784 : * from normal data reads and writes. Delaying an ALTER TABLE
4785 : * behind currently active writes only delays the point where
4786 : * the new strategy begins to take effect, so there is no
4787 : * benefit in waiting. In this case the minimum restriction
4788 : * applies: we don't currently allow concurrent catalog
4789 : * updates.
4790 : */
4791 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4792 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4793 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4794 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4795 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4796 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4797 234 : break;
4798 :
4799 112 : case AT_SetLogged:
4800 : case AT_SetUnLogged:
4801 112 : cmd_lockmode = AccessExclusiveLock;
4802 112 : break;
4803 :
4804 476 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4805 476 : cmd_lockmode = ShareUpdateExclusiveLock;
4806 476 : break;
4807 :
4808 : /*
4809 : * Rel options are more complex than first appears. Options
4810 : * are set here for tables, views and indexes; for historical
4811 : * reasons these can all be used with ALTER TABLE, so we can't
4812 : * decide between them using the basic grammar.
4813 : */
4814 764 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4815 : * getTables() */
4816 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4817 : * getTables() */
4818 764 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4819 764 : break;
4820 :
4821 3314 : case AT_AttachPartition:
4822 3314 : cmd_lockmode = ShareUpdateExclusiveLock;
4823 3314 : break;
4824 :
4825 588 : case AT_DetachPartition:
4826 588 : if (((PartitionCmd *) cmd->def)->concurrent)
4827 164 : cmd_lockmode = ShareUpdateExclusiveLock;
4828 : else
4829 424 : cmd_lockmode = AccessExclusiveLock;
4830 588 : break;
4831 :
4832 20 : case AT_DetachPartitionFinalize:
4833 20 : cmd_lockmode = ShareUpdateExclusiveLock;
4834 20 : break;
4835 :
4836 0 : default: /* oops */
4837 0 : elog(ERROR, "unrecognized alter table type: %d",
4838 : (int) cmd->subtype);
4839 : break;
4840 : }
4841 :
4842 : /*
4843 : * Take the greatest lockmode from any subcommand
4844 : */
4845 35992 : if (cmd_lockmode > lockmode)
4846 30036 : lockmode = cmd_lockmode;
4847 : }
4848 :
4849 34820 : return lockmode;
4850 : }
4851 :
4852 : /*
4853 : * ATController provides top level control over the phases.
4854 : *
4855 : * parsetree is passed in to allow it to be passed to event triggers
4856 : * when requested.
4857 : */
4858 : static void
4859 34576 : ATController(AlterTableStmt *parsetree,
4860 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4861 : AlterTableUtilityContext *context)
4862 : {
4863 34576 : List *wqueue = NIL;
4864 : ListCell *lcmd;
4865 :
4866 : /* Phase 1: preliminary examination of commands, create work queue */
4867 69912 : foreach(lcmd, cmds)
4868 : {
4869 35742 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4870 :
4871 35742 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4872 : }
4873 :
4874 : /* Close the relation, but keep lock until commit */
4875 34170 : relation_close(rel, NoLock);
4876 :
4877 : /* Phase 2: update system catalogs */
4878 34170 : ATRewriteCatalogs(&wqueue, lockmode, context);
4879 :
4880 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4881 31348 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4882 30904 : }
4883 :
4884 : /*
4885 : * ATPrepCmd
4886 : *
4887 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4888 : * recursion and permission checks.
4889 : *
4890 : * Caller must have acquired appropriate lock type on relation already.
4891 : * This lock should be held until commit.
4892 : */
4893 : static void
4894 36682 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4895 : bool recurse, bool recursing, LOCKMODE lockmode,
4896 : AlterTableUtilityContext *context)
4897 : {
4898 : AlteredTableInfo *tab;
4899 36682 : AlterTablePass pass = AT_PASS_UNSET;
4900 :
4901 : /* Find or create work queue entry for this table */
4902 36682 : tab = ATGetQueueEntry(wqueue, rel);
4903 :
4904 : /*
4905 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4906 : * partitions that are pending detach.
4907 : */
4908 36682 : if (rel->rd_rel->relispartition &&
4909 3068 : cmd->subtype != AT_DetachPartitionFinalize &&
4910 1534 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4911 2 : ereport(ERROR,
4912 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4913 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4914 : RelationGetRelationName(rel)),
4915 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4916 :
4917 : /*
4918 : * Copy the original subcommand for each table, so we can scribble on it.
4919 : * This avoids conflicts when different child tables need to make
4920 : * different parse transformations (for example, the same column may have
4921 : * different column numbers in different children).
4922 : */
4923 36680 : cmd = copyObject(cmd);
4924 :
4925 : /*
4926 : * Do permissions and relkind checking, recursion to child tables if
4927 : * needed, and any additional phase-1 processing needed. (But beware of
4928 : * adding any processing that looks at table details that another
4929 : * subcommand could change. In some cases we reject multiple subcommands
4930 : * that could try to change the same state in contrary ways.)
4931 : */
4932 36680 : switch (cmd->subtype)
4933 : {
4934 2164 : case AT_AddColumn: /* ADD COLUMN */
4935 2164 : ATSimplePermissions(cmd->subtype, rel,
4936 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4937 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4938 2164 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4939 : lockmode, context);
4940 : /* Recursion occurs during execution phase */
4941 2152 : pass = AT_PASS_ADD_COL;
4942 2152 : break;
4943 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4944 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4945 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4946 : lockmode, context);
4947 : /* Recursion occurs during execution phase */
4948 24 : pass = AT_PASS_ADD_COL;
4949 24 : break;
4950 648 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4951 :
4952 : /*
4953 : * We allow defaults on views so that INSERT into a view can have
4954 : * default-ish behavior. This works because the rewriter
4955 : * substitutes default values into INSERTs before it expands
4956 : * rules.
4957 : */
4958 648 : ATSimplePermissions(cmd->subtype, rel,
4959 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4960 : ATT_FOREIGN_TABLE);
4961 648 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4962 : /* No command-specific prep needed */
4963 648 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4964 648 : break;
4965 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4966 : /* This is currently used only in CREATE TABLE */
4967 : /* (so the permission check really isn't necessary) */
4968 80 : ATSimplePermissions(cmd->subtype, rel,
4969 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4970 : /* This command never recurses */
4971 80 : pass = AT_PASS_ADD_OTHERCONSTR;
4972 80 : break;
4973 210 : case AT_AddIdentity:
4974 210 : ATSimplePermissions(cmd->subtype, rel,
4975 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4976 : ATT_FOREIGN_TABLE);
4977 : /* Set up recursion for phase 2; no other prep needed */
4978 210 : if (recurse)
4979 204 : cmd->recurse = true;
4980 210 : pass = AT_PASS_ADD_OTHERCONSTR;
4981 210 : break;
4982 62 : case AT_SetIdentity:
4983 62 : ATSimplePermissions(cmd->subtype, rel,
4984 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4985 : ATT_FOREIGN_TABLE);
4986 : /* Set up recursion for phase 2; no other prep needed */
4987 62 : if (recurse)
4988 56 : cmd->recurse = true;
4989 : /* This should run after AddIdentity, so do it in MISC pass */
4990 62 : pass = AT_PASS_MISC;
4991 62 : break;
4992 56 : case AT_DropIdentity:
4993 56 : ATSimplePermissions(cmd->subtype, rel,
4994 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4995 : ATT_FOREIGN_TABLE);
4996 : /* Set up recursion for phase 2; no other prep needed */
4997 56 : if (recurse)
4998 50 : cmd->recurse = true;
4999 56 : pass = AT_PASS_DROP;
5000 56 : break;
5001 274 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5002 274 : ATSimplePermissions(cmd->subtype, rel,
5003 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5004 : /* Set up recursion for phase 2; no other prep needed */
5005 268 : if (recurse)
5006 250 : cmd->recurse = true;
5007 268 : pass = AT_PASS_DROP;
5008 268 : break;
5009 414 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5010 414 : ATSimplePermissions(cmd->subtype, rel,
5011 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5012 : /* Set up recursion for phase 2; no other prep needed */
5013 408 : if (recurse)
5014 384 : cmd->recurse = true;
5015 408 : pass = AT_PASS_COL_ATTRS;
5016 408 : break;
5017 180 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5018 180 : ATSimplePermissions(cmd->subtype, rel,
5019 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5020 180 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5021 180 : pass = AT_PASS_SET_EXPRESSION;
5022 180 : break;
5023 86 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5024 86 : ATSimplePermissions(cmd->subtype, rel,
5025 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5026 86 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5027 86 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5028 62 : pass = AT_PASS_DROP;
5029 62 : break;
5030 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5031 164 : ATSimplePermissions(cmd->subtype, rel,
5032 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
5033 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
5034 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5035 : /* No command-specific prep needed */
5036 164 : pass = AT_PASS_MISC;
5037 164 : break;
5038 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5039 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5040 44 : ATSimplePermissions(cmd->subtype, rel,
5041 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5042 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5043 : /* This command never recurses */
5044 32 : pass = AT_PASS_MISC;
5045 32 : break;
5046 246 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5047 246 : ATSimplePermissions(cmd->subtype, rel,
5048 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5049 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5050 246 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5051 : /* No command-specific prep needed */
5052 246 : pass = AT_PASS_MISC;
5053 246 : break;
5054 90 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5055 90 : ATSimplePermissions(cmd->subtype, rel,
5056 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5057 : /* This command never recurses */
5058 : /* No command-specific prep needed */
5059 90 : pass = AT_PASS_MISC;
5060 90 : break;
5061 1658 : case AT_DropColumn: /* DROP COLUMN */
5062 1658 : ATSimplePermissions(cmd->subtype, rel,
5063 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5064 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5065 1652 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5066 : lockmode, context);
5067 : /* Recursion occurs during execution phase */
5068 1640 : pass = AT_PASS_DROP;
5069 1640 : break;
5070 0 : case AT_AddIndex: /* ADD INDEX */
5071 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5072 : /* This command never recurses */
5073 : /* No command-specific prep needed */
5074 0 : pass = AT_PASS_ADD_INDEX;
5075 0 : break;
5076 16310 : case AT_AddConstraint: /* ADD CONSTRAINT */
5077 16310 : ATSimplePermissions(cmd->subtype, rel,
5078 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5079 16310 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5080 16280 : if (recurse)
5081 : {
5082 : /* recurses at exec time; lock descendants and set flag */
5083 15620 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5084 15620 : cmd->recurse = true;
5085 : }
5086 16280 : pass = AT_PASS_ADD_CONSTR;
5087 16280 : break;
5088 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5089 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5090 : /* This command never recurses */
5091 : /* No command-specific prep needed */
5092 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5093 0 : break;
5094 798 : case AT_DropConstraint: /* DROP CONSTRAINT */
5095 798 : ATSimplePermissions(cmd->subtype, rel,
5096 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5097 798 : ATCheckPartitionsNotInUse(rel, lockmode);
5098 : /* Other recursion occurs during execution phase */
5099 : /* No command-specific prep needed except saving recurse flag */
5100 792 : if (recurse)
5101 756 : cmd->recurse = true;
5102 792 : pass = AT_PASS_DROP;
5103 792 : break;
5104 1306 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5105 1306 : ATSimplePermissions(cmd->subtype, rel,
5106 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5107 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5108 : /* See comments for ATPrepAlterColumnType */
5109 1306 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5110 : AT_PASS_UNSET, context);
5111 : Assert(cmd != NULL);
5112 : /* Performs own recursion */
5113 1300 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5114 : lockmode, context);
5115 1102 : pass = AT_PASS_ALTER_TYPE;
5116 1102 : break;
5117 172 : case AT_AlterColumnGenericOptions:
5118 172 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5119 : /* This command never recurses */
5120 : /* No command-specific prep needed */
5121 172 : pass = AT_PASS_MISC;
5122 172 : break;
5123 3586 : case AT_ChangeOwner: /* ALTER OWNER */
5124 : /* This command never recurses */
5125 : /* No command-specific prep needed */
5126 3586 : pass = AT_PASS_MISC;
5127 3586 : break;
5128 64 : case AT_ClusterOn: /* CLUSTER ON */
5129 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5130 64 : ATSimplePermissions(cmd->subtype, rel,
5131 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5132 : /* These commands never recurse */
5133 : /* No command-specific prep needed */
5134 64 : pass = AT_PASS_MISC;
5135 64 : break;
5136 112 : case AT_SetLogged: /* SET LOGGED */
5137 : case AT_SetUnLogged: /* SET UNLOGGED */
5138 112 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5139 100 : if (tab->chgPersistence)
5140 0 : ereport(ERROR,
5141 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5142 : errmsg("cannot change persistence setting twice")));
5143 100 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5144 88 : pass = AT_PASS_MISC;
5145 88 : break;
5146 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5147 6 : ATSimplePermissions(cmd->subtype, rel,
5148 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5149 6 : pass = AT_PASS_DROP;
5150 6 : break;
5151 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5152 128 : ATSimplePermissions(cmd->subtype, rel,
5153 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5154 :
5155 : /* check if another access method change was already requested */
5156 128 : if (tab->chgAccessMethod)
5157 18 : ereport(ERROR,
5158 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5159 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5160 :
5161 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5162 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5163 110 : break;
5164 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5165 158 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5166 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5167 : /* This command never recurses */
5168 158 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5169 158 : pass = AT_PASS_MISC; /* doesn't actually matter */
5170 158 : break;
5171 960 : case AT_SetRelOptions: /* SET (...) */
5172 : case AT_ResetRelOptions: /* RESET (...) */
5173 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5174 960 : ATSimplePermissions(cmd->subtype, rel,
5175 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5176 : ATT_MATVIEW | ATT_INDEX);
5177 : /* This command never recurses */
5178 : /* No command-specific prep needed */
5179 958 : pass = AT_PASS_MISC;
5180 958 : break;
5181 444 : case AT_AddInherit: /* INHERIT */
5182 444 : ATSimplePermissions(cmd->subtype, rel,
5183 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5184 : /* This command never recurses */
5185 444 : ATPrepAddInherit(rel);
5186 426 : pass = AT_PASS_MISC;
5187 426 : break;
5188 86 : case AT_DropInherit: /* NO INHERIT */
5189 86 : ATSimplePermissions(cmd->subtype, rel,
5190 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5191 : /* This command never recurses */
5192 : /* No command-specific prep needed */
5193 86 : pass = AT_PASS_MISC;
5194 86 : break;
5195 294 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5196 294 : ATSimplePermissions(cmd->subtype, rel,
5197 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5198 : /* Recursion occurs during execution phase */
5199 288 : if (recurse)
5200 288 : cmd->recurse = true;
5201 288 : pass = AT_PASS_MISC;
5202 288 : break;
5203 476 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5204 476 : ATSimplePermissions(cmd->subtype, rel,
5205 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5206 : /* Recursion occurs during execution phase */
5207 : /* No command-specific prep needed except saving recurse flag */
5208 476 : if (recurse)
5209 476 : cmd->recurse = true;
5210 476 : pass = AT_PASS_MISC;
5211 476 : break;
5212 490 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5213 490 : ATSimplePermissions(cmd->subtype, rel,
5214 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5215 490 : pass = AT_PASS_MISC;
5216 : /* This command never recurses */
5217 : /* No command-specific prep needed */
5218 490 : break;
5219 346 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5220 : case AT_EnableAlwaysTrig:
5221 : case AT_EnableReplicaTrig:
5222 : case AT_EnableTrigAll:
5223 : case AT_EnableTrigUser:
5224 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5225 : case AT_DisableTrigAll:
5226 : case AT_DisableTrigUser:
5227 346 : ATSimplePermissions(cmd->subtype, rel,
5228 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5229 : /* Set up recursion for phase 2; no other prep needed */
5230 346 : if (recurse)
5231 318 : cmd->recurse = true;
5232 346 : pass = AT_PASS_MISC;
5233 346 : break;
5234 576 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5235 : case AT_EnableAlwaysRule:
5236 : case AT_EnableReplicaRule:
5237 : case AT_DisableRule:
5238 : case AT_AddOf: /* OF */
5239 : case AT_DropOf: /* NOT OF */
5240 : case AT_EnableRowSecurity:
5241 : case AT_DisableRowSecurity:
5242 : case AT_ForceRowSecurity:
5243 : case AT_NoForceRowSecurity:
5244 576 : ATSimplePermissions(cmd->subtype, rel,
5245 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5246 : /* These commands never recurse */
5247 : /* No command-specific prep needed */
5248 576 : pass = AT_PASS_MISC;
5249 576 : break;
5250 58 : case AT_GenericOptions:
5251 58 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5252 : /* No command-specific prep needed */
5253 58 : pass = AT_PASS_MISC;
5254 58 : break;
5255 3302 : case AT_AttachPartition:
5256 3302 : ATSimplePermissions(cmd->subtype, rel,
5257 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5258 : /* No command-specific prep needed */
5259 3296 : pass = AT_PASS_MISC;
5260 3296 : break;
5261 588 : case AT_DetachPartition:
5262 588 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5263 : /* No command-specific prep needed */
5264 570 : pass = AT_PASS_MISC;
5265 570 : break;
5266 20 : case AT_DetachPartitionFinalize:
5267 20 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5268 : /* No command-specific prep needed */
5269 14 : pass = AT_PASS_MISC;
5270 14 : break;
5271 0 : default: /* oops */
5272 0 : elog(ERROR, "unrecognized alter table type: %d",
5273 : (int) cmd->subtype);
5274 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5275 : break;
5276 : }
5277 : Assert(pass > AT_PASS_UNSET);
5278 :
5279 : /* Add the subcommand to the appropriate list for phase 2 */
5280 36264 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5281 36264 : }
5282 :
5283 : /*
5284 : * ATRewriteCatalogs
5285 : *
5286 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5287 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5288 : * conflicts).
5289 : */
5290 : static void
5291 34170 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5292 : AlterTableUtilityContext *context)
5293 : {
5294 : ListCell *ltab;
5295 :
5296 : /*
5297 : * We process all the tables "in parallel", one pass at a time. This is
5298 : * needed because we may have to propagate work from one table to another
5299 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5300 : * re-adding of the foreign key constraint to the other table). Work can
5301 : * only be propagated into later passes, however.
5302 : */
5303 431614 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5304 : {
5305 : /* Go through each table that needs to be processed */
5306 814010 : foreach(ltab, *wqueue)
5307 : {
5308 416566 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5309 416566 : List *subcmds = tab->subcmds[pass];
5310 : ListCell *lcmd;
5311 :
5312 416566 : if (subcmds == NIL)
5313 358584 : continue;
5314 :
5315 : /*
5316 : * Open the relation and store it in tab. This allows subroutines
5317 : * close and reopen, if necessary. Appropriate lock was obtained
5318 : * by phase 1, needn't get it again.
5319 : */
5320 57982 : tab->rel = relation_open(tab->relid, NoLock);
5321 :
5322 116800 : foreach(lcmd, subcmds)
5323 61640 : ATExecCmd(wqueue, tab,
5324 61640 : lfirst_node(AlterTableCmd, lcmd),
5325 : lockmode, pass, context);
5326 :
5327 : /*
5328 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5329 : * (this is not done in ATExecAlterColumnType since it should be
5330 : * done only once if multiple columns of a table are altered).
5331 : */
5332 55160 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5333 1132 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5334 :
5335 55160 : if (tab->rel)
5336 : {
5337 55160 : relation_close(tab->rel, NoLock);
5338 55160 : tab->rel = NULL;
5339 : }
5340 : }
5341 : }
5342 :
5343 : /* Check to see if a toast table must be added. */
5344 67308 : foreach(ltab, *wqueue)
5345 : {
5346 35960 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5347 :
5348 : /*
5349 : * If the table is source table of ATTACH PARTITION command, we did
5350 : * not modify anything about it that will change its toasting
5351 : * requirement, so no need to check.
5352 : */
5353 35960 : if (((tab->relkind == RELKIND_RELATION ||
5354 7000 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5355 33778 : tab->partition_constraint == NULL) ||
5356 4602 : tab->relkind == RELKIND_MATVIEW)
5357 31426 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5358 : }
5359 31348 : }
5360 :
5361 : /*
5362 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5363 : */
5364 : static void
5365 61640 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5366 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5367 : AlterTableUtilityContext *context)
5368 : {
5369 61640 : ObjectAddress address = InvalidObjectAddress;
5370 61640 : Relation rel = tab->rel;
5371 :
5372 61640 : switch (cmd->subtype)
5373 : {
5374 2170 : case AT_AddColumn: /* ADD COLUMN */
5375 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5376 2170 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5377 2170 : cmd->recurse, false,
5378 : lockmode, cur_pass, context);
5379 2032 : break;
5380 612 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5381 612 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5382 546 : break;
5383 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5384 80 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5385 80 : break;
5386 210 : case AT_AddIdentity:
5387 210 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5388 : cur_pass, context);
5389 : Assert(cmd != NULL);
5390 198 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5391 150 : break;
5392 62 : case AT_SetIdentity:
5393 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5394 : cur_pass, context);
5395 : Assert(cmd != NULL);
5396 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5397 38 : break;
5398 56 : case AT_DropIdentity:
5399 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5400 38 : break;
5401 268 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5402 268 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5403 166 : break;
5404 408 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5405 408 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5406 408 : cmd->recurse, false, lockmode);
5407 378 : break;
5408 180 : case AT_SetExpression:
5409 180 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5410 150 : break;
5411 56 : case AT_DropExpression:
5412 56 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5413 32 : break;
5414 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5415 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5416 116 : break;
5417 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5418 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5419 26 : break;
5420 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5421 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5422 6 : break;
5423 246 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5424 246 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5425 234 : break;
5426 90 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5427 90 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5428 : lockmode);
5429 84 : break;
5430 1640 : case AT_DropColumn: /* DROP COLUMN */
5431 1640 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5432 1640 : cmd->behavior, cmd->recurse, false,
5433 1640 : cmd->missing_ok, lockmode,
5434 : NULL);
5435 1460 : break;
5436 1444 : case AT_AddIndex: /* ADD INDEX */
5437 1444 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5438 : lockmode);
5439 1274 : break;
5440 444 : case AT_ReAddIndex: /* ADD INDEX */
5441 444 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5442 : lockmode);
5443 444 : break;
5444 14 : case AT_ReAddStatistics: /* ADD STATISTICS */
5445 14 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5446 : true, lockmode);
5447 14 : break;
5448 28756 : case AT_AddConstraint: /* ADD CONSTRAINT */
5449 : /* Transform the command only during initial examination */
5450 28756 : if (cur_pass == AT_PASS_ADD_CONSTR)
5451 16250 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5452 16280 : cmd->recurse, lockmode,
5453 : cur_pass, context);
5454 : /* Depending on constraint type, might be no more work to do now */
5455 28726 : if (cmd != NULL)
5456 : address =
5457 12476 : ATExecAddConstraint(wqueue, tab, rel,
5458 12476 : (Constraint *) cmd->def,
5459 12476 : cmd->recurse, false, lockmode);
5460 28046 : break;
5461 332 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5462 : address =
5463 332 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5464 : true, true, lockmode);
5465 320 : break;
5466 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5467 : * constraint */
5468 : address =
5469 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5470 14 : ((AlterDomainStmt *) cmd->def)->def,
5471 : NULL);
5472 8 : break;
5473 78 : case AT_ReAddComment: /* Re-add existing comment */
5474 78 : address = CommentObject((CommentStmt *) cmd->def);
5475 78 : break;
5476 10640 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5477 10640 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5478 : lockmode);
5479 10628 : break;
5480 288 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5481 288 : address = ATExecAlterConstraint(wqueue, rel,
5482 288 : castNode(ATAlterConstraint, cmd->def),
5483 288 : cmd->recurse, lockmode);
5484 222 : break;
5485 476 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5486 476 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5487 : false, lockmode);
5488 470 : break;
5489 792 : case AT_DropConstraint: /* DROP CONSTRAINT */
5490 792 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5491 792 : cmd->recurse,
5492 792 : cmd->missing_ok, lockmode);
5493 582 : break;
5494 1066 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5495 : /* parse transformation was done earlier */
5496 1066 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5497 1024 : break;
5498 172 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5499 : address =
5500 172 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5501 172 : (List *) cmd->def, lockmode);
5502 166 : break;
5503 3586 : case AT_ChangeOwner: /* ALTER OWNER */
5504 3580 : ATExecChangeOwner(RelationGetRelid(rel),
5505 3586 : get_rolespec_oid(cmd->newowner, false),
5506 : false, lockmode);
5507 3568 : break;
5508 64 : case AT_ClusterOn: /* CLUSTER ON */
5509 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5510 58 : break;
5511 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5512 18 : ATExecDropCluster(rel, lockmode);
5513 12 : break;
5514 88 : case AT_SetLogged: /* SET LOGGED */
5515 : case AT_SetUnLogged: /* SET UNLOGGED */
5516 88 : break;
5517 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5518 : /* nothing to do here, oid columns don't exist anymore */
5519 6 : break;
5520 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5521 :
5522 : /*
5523 : * Only do this for partitioned tables, for which this is just a
5524 : * catalog change. Tables with storage are handled by Phase 3.
5525 : */
5526 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5527 50 : tab->chgAccessMethod)
5528 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5529 92 : break;
5530 158 : case AT_SetTableSpace: /* SET TABLESPACE */
5531 :
5532 : /*
5533 : * Only do this for partitioned tables and indexes, for which this
5534 : * is just a catalog change. Other relation types which have
5535 : * storage are handled by Phase 3.
5536 : */
5537 158 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5538 146 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5539 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5540 :
5541 152 : break;
5542 958 : case AT_SetRelOptions: /* SET (...) */
5543 : case AT_ResetRelOptions: /* RESET (...) */
5544 : case AT_ReplaceRelOptions: /* replace entire option list */
5545 958 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5546 906 : break;
5547 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5548 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5549 : TRIGGER_FIRES_ON_ORIGIN, false,
5550 122 : cmd->recurse,
5551 : lockmode);
5552 122 : break;
5553 44 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5554 44 : ATExecEnableDisableTrigger(rel, cmd->name,
5555 : TRIGGER_FIRES_ALWAYS, false,
5556 44 : cmd->recurse,
5557 : lockmode);
5558 44 : break;
5559 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5560 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5561 : TRIGGER_FIRES_ON_REPLICA, false,
5562 16 : cmd->recurse,
5563 : lockmode);
5564 16 : break;
5565 140 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5566 140 : ATExecEnableDisableTrigger(rel, cmd->name,
5567 : TRIGGER_DISABLED, false,
5568 140 : cmd->recurse,
5569 : lockmode);
5570 140 : break;
5571 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5572 0 : ATExecEnableDisableTrigger(rel, NULL,
5573 : TRIGGER_FIRES_ON_ORIGIN, false,
5574 0 : cmd->recurse,
5575 : lockmode);
5576 0 : break;
5577 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5578 12 : ATExecEnableDisableTrigger(rel, NULL,
5579 : TRIGGER_DISABLED, false,
5580 12 : cmd->recurse,
5581 : lockmode);
5582 12 : break;
5583 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5584 0 : ATExecEnableDisableTrigger(rel, NULL,
5585 : TRIGGER_FIRES_ON_ORIGIN, true,
5586 0 : cmd->recurse,
5587 : lockmode);
5588 0 : break;
5589 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5590 12 : ATExecEnableDisableTrigger(rel, NULL,
5591 : TRIGGER_DISABLED, true,
5592 12 : cmd->recurse,
5593 : lockmode);
5594 12 : break;
5595 :
5596 8 : case AT_EnableRule: /* ENABLE RULE name */
5597 8 : ATExecEnableDisableRule(rel, cmd->name,
5598 : RULE_FIRES_ON_ORIGIN, lockmode);
5599 8 : break;
5600 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5601 0 : ATExecEnableDisableRule(rel, cmd->name,
5602 : RULE_FIRES_ALWAYS, lockmode);
5603 0 : break;
5604 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5605 6 : ATExecEnableDisableRule(rel, cmd->name,
5606 : RULE_FIRES_ON_REPLICA, lockmode);
5607 6 : break;
5608 38 : case AT_DisableRule: /* DISABLE RULE name */
5609 38 : ATExecEnableDisableRule(rel, cmd->name,
5610 : RULE_DISABLED, lockmode);
5611 38 : break;
5612 :
5613 426 : case AT_AddInherit:
5614 426 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5615 306 : break;
5616 86 : case AT_DropInherit:
5617 86 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5618 80 : break;
5619 66 : case AT_AddOf:
5620 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5621 30 : break;
5622 6 : case AT_DropOf:
5623 6 : ATExecDropOf(rel, lockmode);
5624 6 : break;
5625 508 : case AT_ReplicaIdentity:
5626 508 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5627 460 : break;
5628 308 : case AT_EnableRowSecurity:
5629 308 : ATExecSetRowSecurity(rel, true);
5630 308 : break;
5631 10 : case AT_DisableRowSecurity:
5632 10 : ATExecSetRowSecurity(rel, false);
5633 10 : break;
5634 102 : case AT_ForceRowSecurity:
5635 102 : ATExecForceNoForceRowSecurity(rel, true);
5636 102 : break;
5637 32 : case AT_NoForceRowSecurity:
5638 32 : ATExecForceNoForceRowSecurity(rel, false);
5639 32 : break;
5640 58 : case AT_GenericOptions:
5641 58 : ATExecGenericOptions(rel, (List *) cmd->def);
5642 56 : break;
5643 3296 : case AT_AttachPartition:
5644 3296 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5645 : cur_pass, context);
5646 : Assert(cmd != NULL);
5647 3272 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5648 2796 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5649 : context);
5650 : else
5651 476 : address = ATExecAttachPartitionIdx(wqueue, rel,
5652 476 : ((PartitionCmd *) cmd->def)->name);
5653 2882 : break;
5654 570 : case AT_DetachPartition:
5655 570 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5656 : cur_pass, context);
5657 : Assert(cmd != NULL);
5658 : /* ATPrepCmd ensures it must be a table */
5659 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5660 570 : address = ATExecDetachPartition(wqueue, tab, rel,
5661 570 : ((PartitionCmd *) cmd->def)->name,
5662 570 : ((PartitionCmd *) cmd->def)->concurrent);
5663 440 : break;
5664 14 : case AT_DetachPartitionFinalize:
5665 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5666 14 : break;
5667 0 : default: /* oops */
5668 0 : elog(ERROR, "unrecognized alter table type: %d",
5669 : (int) cmd->subtype);
5670 : break;
5671 : }
5672 :
5673 : /*
5674 : * Report the subcommand to interested event triggers.
5675 : */
5676 58818 : if (cmd)
5677 42568 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5678 :
5679 : /*
5680 : * Bump the command counter to ensure the next subcommand in the sequence
5681 : * can see the changes so far
5682 : */
5683 58818 : CommandCounterIncrement();
5684 58818 : }
5685 :
5686 : /*
5687 : * ATParseTransformCmd: perform parse transformation for one subcommand
5688 : *
5689 : * Returns the transformed subcommand tree, if there is one, else NULL.
5690 : *
5691 : * The parser may hand back additional AlterTableCmd(s) and/or other
5692 : * utility statements, either before or after the original subcommand.
5693 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5694 : * AlteredTableInfo (they had better be for later passes than the current one).
5695 : * Utility statements that are supposed to happen before the AlterTableCmd
5696 : * are executed immediately. Those that are supposed to happen afterwards
5697 : * are added to the tab->afterStmts list to be done at the very end.
5698 : */
5699 : static AlterTableCmd *
5700 23774 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5701 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5702 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5703 : {
5704 23774 : AlterTableCmd *newcmd = NULL;
5705 23774 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5706 : List *beforeStmts;
5707 : List *afterStmts;
5708 : ListCell *lc;
5709 :
5710 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5711 23774 : atstmt->relation =
5712 23774 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5713 23774 : pstrdup(RelationGetRelationName(rel)),
5714 : -1);
5715 23774 : atstmt->relation->inh = recurse;
5716 23774 : atstmt->cmds = list_make1(cmd);
5717 23774 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5718 23774 : atstmt->missing_ok = false;
5719 :
5720 : /* Transform the AlterTableStmt */
5721 23774 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5722 : atstmt,
5723 : context->queryString,
5724 : &beforeStmts,
5725 : &afterStmts);
5726 :
5727 : /* Execute any statements that should happen before these subcommand(s) */
5728 24270 : foreach(lc, beforeStmts)
5729 : {
5730 574 : Node *stmt = (Node *) lfirst(lc);
5731 :
5732 574 : ProcessUtilityForAlterTable(stmt, context);
5733 562 : CommandCounterIncrement();
5734 : }
5735 :
5736 : /* Examine the transformed subcommands and schedule them appropriately */
5737 55738 : foreach(lc, atstmt->cmds)
5738 : {
5739 32042 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5740 : AlterTablePass pass;
5741 :
5742 : /*
5743 : * This switch need only cover the subcommand types that can be added
5744 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5745 : * executing the subcommand immediately, as a substitute for the
5746 : * original subcommand. (Note, however, that this does cause
5747 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5748 : * which is important for index and foreign key constraints.)
5749 : *
5750 : * We assume we needn't do any phase-1 checks for added subcommands.
5751 : */
5752 32042 : switch (cmd2->subtype)
5753 : {
5754 1468 : case AT_AddIndex:
5755 1468 : pass = AT_PASS_ADD_INDEX;
5756 1468 : break;
5757 10640 : case AT_AddIndexConstraint:
5758 10640 : pass = AT_PASS_ADD_INDEXCONSTR;
5759 10640 : break;
5760 12488 : case AT_AddConstraint:
5761 : /* Recursion occurs during execution phase */
5762 12488 : if (recurse)
5763 12414 : cmd2->recurse = true;
5764 12488 : switch (castNode(Constraint, cmd2->def)->contype)
5765 : {
5766 8996 : case CONSTR_NOTNULL:
5767 8996 : pass = AT_PASS_COL_ATTRS;
5768 8996 : break;
5769 0 : case CONSTR_PRIMARY:
5770 : case CONSTR_UNIQUE:
5771 : case CONSTR_EXCLUSION:
5772 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5773 0 : break;
5774 3492 : default:
5775 3492 : pass = AT_PASS_ADD_OTHERCONSTR;
5776 3492 : break;
5777 : }
5778 12488 : break;
5779 0 : case AT_AlterColumnGenericOptions:
5780 : /* This command never recurses */
5781 : /* No command-specific prep needed */
5782 0 : pass = AT_PASS_MISC;
5783 0 : break;
5784 7446 : default:
5785 7446 : pass = cur_pass;
5786 7446 : break;
5787 : }
5788 :
5789 32042 : if (pass < cur_pass)
5790 : {
5791 : /* Cannot schedule into a pass we already finished */
5792 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5793 : pass);
5794 : }
5795 32042 : else if (pass > cur_pass)
5796 : {
5797 : /* OK, queue it up for later */
5798 24596 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5799 : }
5800 : else
5801 : {
5802 : /*
5803 : * We should see at most one subcommand for the current pass,
5804 : * which is the transformed version of the original subcommand.
5805 : */
5806 7446 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5807 : {
5808 : /* Found the transformed version of our subcommand */
5809 7446 : newcmd = cmd2;
5810 : }
5811 : else
5812 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5813 : pass);
5814 : }
5815 : }
5816 :
5817 : /* Queue up any after-statements to happen at the end */
5818 23696 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5819 :
5820 23696 : return newcmd;
5821 : }
5822 :
5823 : /*
5824 : * ATRewriteTables: ALTER TABLE phase 3
5825 : */
5826 : static void
5827 31348 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5828 : AlterTableUtilityContext *context)
5829 : {
5830 : ListCell *ltab;
5831 :
5832 : /* Go through each table that needs to be checked or rewritten */
5833 66920 : foreach(ltab, *wqueue)
5834 : {
5835 35924 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5836 :
5837 : /* Relations without storage may be ignored here */
5838 35924 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5839 6612 : continue;
5840 :
5841 : /*
5842 : * If we change column data types, the operation has to be propagated
5843 : * to tables that use this table's rowtype as a column type.
5844 : * tab->newvals will also be non-NULL in the case where we're adding a
5845 : * column with a default. We choose to forbid that case as well,
5846 : * since composite types might eventually support defaults.
5847 : *
5848 : * (Eventually we'll probably need to check for composite type
5849 : * dependencies even when we're just scanning the table without a
5850 : * rewrite, but at the moment a composite type does not enforce any
5851 : * constraints, so it's not necessary/appropriate to enforce them just
5852 : * during ALTER.)
5853 : */
5854 29312 : if (tab->newvals != NIL || tab->rewrite > 0)
5855 : {
5856 : Relation rel;
5857 :
5858 1704 : rel = table_open(tab->relid, NoLock);
5859 1704 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5860 1680 : table_close(rel, NoLock);
5861 : }
5862 :
5863 : /*
5864 : * We only need to rewrite the table if at least one column needs to
5865 : * be recomputed, or we are changing its persistence or access method.
5866 : *
5867 : * There are two reasons for requiring a rewrite when changing
5868 : * persistence: on one hand, we need to ensure that the buffers
5869 : * belonging to each of the two relations are marked with or without
5870 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5871 : * and assigns a new relfilenumber, we automatically create or drop an
5872 : * init fork for the relation as appropriate.
5873 : */
5874 29288 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5875 934 : {
5876 : /* Build a temporary relation and copy data */
5877 : Relation OldHeap;
5878 : Oid OIDNewHeap;
5879 : Oid NewAccessMethod;
5880 : Oid NewTableSpace;
5881 : char persistence;
5882 :
5883 990 : OldHeap = table_open(tab->relid, NoLock);
5884 :
5885 : /*
5886 : * We don't support rewriting of system catalogs; there are too
5887 : * many corner cases and too little benefit. In particular this
5888 : * is certainly not going to work for mapped catalogs.
5889 : */
5890 990 : if (IsSystemRelation(OldHeap))
5891 0 : ereport(ERROR,
5892 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5893 : errmsg("cannot rewrite system relation \"%s\"",
5894 : RelationGetRelationName(OldHeap))));
5895 :
5896 990 : if (RelationIsUsedAsCatalogTable(OldHeap))
5897 2 : ereport(ERROR,
5898 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5899 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5900 : RelationGetRelationName(OldHeap))));
5901 :
5902 : /*
5903 : * Don't allow rewrite on temp tables of other backends ... their
5904 : * local buffer manager is not going to cope. (This is redundant
5905 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5906 : * check here too.)
5907 : */
5908 988 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5909 0 : ereport(ERROR,
5910 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5911 : errmsg("cannot rewrite temporary tables of other sessions")));
5912 :
5913 : /*
5914 : * Select destination tablespace (same as original unless user
5915 : * requested a change)
5916 : */
5917 988 : if (tab->newTableSpace)
5918 0 : NewTableSpace = tab->newTableSpace;
5919 : else
5920 988 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5921 :
5922 : /*
5923 : * Select destination access method (same as original unless user
5924 : * requested a change)
5925 : */
5926 988 : if (tab->chgAccessMethod)
5927 36 : NewAccessMethod = tab->newAccessMethod;
5928 : else
5929 952 : NewAccessMethod = OldHeap->rd_rel->relam;
5930 :
5931 : /*
5932 : * Select persistence of transient table (same as original unless
5933 : * user requested a change)
5934 : */
5935 988 : persistence = tab->chgPersistence ?
5936 936 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5937 :
5938 988 : table_close(OldHeap, NoLock);
5939 :
5940 : /*
5941 : * Fire off an Event Trigger now, before actually rewriting the
5942 : * table.
5943 : *
5944 : * We don't support Event Trigger for nested commands anywhere,
5945 : * here included, and parsetree is given NULL when coming from
5946 : * AlterTableInternal.
5947 : *
5948 : * And fire it only once.
5949 : */
5950 988 : if (parsetree)
5951 988 : EventTriggerTableRewrite((Node *) parsetree,
5952 : tab->relid,
5953 : tab->rewrite);
5954 :
5955 : /*
5956 : * Create transient table that will receive the modified data.
5957 : *
5958 : * Ensure it is marked correctly as logged or unlogged. We have
5959 : * to do this here so that buffers for the new relfilenumber will
5960 : * have the right persistence set, and at the same time ensure
5961 : * that the original filenumbers's buffers will get read in with
5962 : * the correct setting (i.e. the original one). Otherwise a
5963 : * rollback after the rewrite would possibly result with buffers
5964 : * for the original filenumbers having the wrong persistence
5965 : * setting.
5966 : *
5967 : * NB: This relies on swap_relation_files() also swapping the
5968 : * persistence. That wouldn't work for pg_class, but that can't be
5969 : * unlogged anyway.
5970 : */
5971 982 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5972 : persistence, lockmode);
5973 :
5974 : /*
5975 : * Copy the heap data into the new table with the desired
5976 : * modifications, and test the current data within the table
5977 : * against new constraints generated by ALTER TABLE commands.
5978 : */
5979 982 : ATRewriteTable(tab, OIDNewHeap);
5980 :
5981 : /*
5982 : * Swap the physical files of the old and new heaps, then rebuild
5983 : * indexes and discard the old heap. We can use RecentXmin for
5984 : * the table's new relfrozenxid because we rewrote all the tuples
5985 : * in ATRewriteTable, so no older Xid remains in the table. Also,
5986 : * we never try to swap toast tables by content, since we have no
5987 : * interest in letting this code work on system catalogs.
5988 : */
5989 940 : finish_heap_swap(tab->relid, OIDNewHeap,
5990 : false, false, true,
5991 940 : !OidIsValid(tab->newTableSpace),
5992 : RecentXmin,
5993 : ReadNextMultiXactId(),
5994 : persistence);
5995 :
5996 934 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5997 : }
5998 28298 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5999 : {
6000 24 : if (tab->chgPersistence)
6001 24 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
6002 : }
6003 : else
6004 : {
6005 : /*
6006 : * If required, test the current data within the table against new
6007 : * constraints generated by ALTER TABLE commands, but don't
6008 : * rebuild data.
6009 : */
6010 28274 : if (tab->constraints != NIL || tab->verify_new_notnull ||
6011 25342 : tab->partition_constraint != NULL)
6012 5198 : ATRewriteTable(tab, InvalidOid);
6013 :
6014 : /*
6015 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
6016 : * just do a block-by-block copy.
6017 : */
6018 28002 : if (tab->newTableSpace)
6019 122 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6020 : }
6021 :
6022 : /*
6023 : * Also change persistence of owned sequences, so that it matches the
6024 : * table persistence.
6025 : */
6026 28960 : if (tab->chgPersistence)
6027 : {
6028 76 : List *seqlist = getOwnedSequences(tab->relid);
6029 : ListCell *lc;
6030 :
6031 124 : foreach(lc, seqlist)
6032 : {
6033 48 : Oid seq_relid = lfirst_oid(lc);
6034 :
6035 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
6036 : }
6037 : }
6038 : }
6039 :
6040 : /*
6041 : * Foreign key constraints are checked in a final pass, since (a) it's
6042 : * generally best to examine each one separately, and (b) it's at least
6043 : * theoretically possible that we have changed both relations of the
6044 : * foreign key, and we'd better have finished both rewrites before we try
6045 : * to read the tables.
6046 : */
6047 66310 : foreach(ltab, *wqueue)
6048 : {
6049 35406 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6050 35406 : Relation rel = NULL;
6051 : ListCell *lcon;
6052 :
6053 : /* Relations without storage may be ignored here too */
6054 35406 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6055 6508 : continue;
6056 :
6057 30756 : foreach(lcon, tab->constraints)
6058 : {
6059 1950 : NewConstraint *con = lfirst(lcon);
6060 :
6061 1950 : if (con->contype == CONSTR_FOREIGN)
6062 : {
6063 1230 : Constraint *fkconstraint = (Constraint *) con->qual;
6064 : Relation refrel;
6065 :
6066 1230 : if (rel == NULL)
6067 : {
6068 : /* Long since locked, no need for another */
6069 1218 : rel = table_open(tab->relid, NoLock);
6070 : }
6071 :
6072 1230 : refrel = table_open(con->refrelid, RowShareLock);
6073 :
6074 1230 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6075 : con->refindid,
6076 : con->conid,
6077 1230 : con->conwithperiod);
6078 :
6079 : /*
6080 : * No need to mark the constraint row as validated, we did
6081 : * that when we inserted the row earlier.
6082 : */
6083 :
6084 1138 : table_close(refrel, NoLock);
6085 : }
6086 : }
6087 :
6088 28806 : if (rel)
6089 1126 : table_close(rel, NoLock);
6090 : }
6091 :
6092 : /* Finally, run any afterStmts that were queued up */
6093 66174 : foreach(ltab, *wqueue)
6094 : {
6095 35270 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6096 : ListCell *lc;
6097 :
6098 35356 : foreach(lc, tab->afterStmts)
6099 : {
6100 86 : Node *stmt = (Node *) lfirst(lc);
6101 :
6102 86 : ProcessUtilityForAlterTable(stmt, context);
6103 86 : CommandCounterIncrement();
6104 : }
6105 : }
6106 30904 : }
6107 :
6108 : /*
6109 : * ATRewriteTable: scan or rewrite one table
6110 : *
6111 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6112 : * must already hold AccessExclusiveLock on it.
6113 : */
6114 : static void
6115 6180 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6116 : {
6117 : Relation oldrel;
6118 : Relation newrel;
6119 : TupleDesc oldTupDesc;
6120 : TupleDesc newTupDesc;
6121 6180 : bool needscan = false;
6122 : List *notnull_attrs;
6123 : List *notnull_virtual_attrs;
6124 : int i;
6125 : ListCell *l;
6126 : EState *estate;
6127 : CommandId mycid;
6128 : BulkInsertState bistate;
6129 : int ti_options;
6130 6180 : ExprState *partqualstate = NULL;
6131 :
6132 : /*
6133 : * Open the relation(s). We have surely already locked the existing
6134 : * table.
6135 : */
6136 6180 : oldrel = table_open(tab->relid, NoLock);
6137 6180 : oldTupDesc = tab->oldDesc;
6138 6180 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6139 :
6140 6180 : if (OidIsValid(OIDNewHeap))
6141 : {
6142 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6143 : false));
6144 982 : newrel = table_open(OIDNewHeap, NoLock);
6145 : }
6146 : else
6147 5198 : newrel = NULL;
6148 :
6149 : /*
6150 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6151 : * is empty, so don't bother using it.
6152 : */
6153 6180 : if (newrel)
6154 : {
6155 982 : mycid = GetCurrentCommandId(true);
6156 982 : bistate = GetBulkInsertState();
6157 982 : ti_options = TABLE_INSERT_SKIP_FSM;
6158 : }
6159 : else
6160 : {
6161 : /* keep compiler quiet about using these uninitialized */
6162 5198 : mycid = 0;
6163 5198 : bistate = NULL;
6164 5198 : ti_options = 0;
6165 : }
6166 :
6167 : /*
6168 : * Generate the constraint and default execution states
6169 : */
6170 :
6171 6180 : estate = CreateExecutorState();
6172 :
6173 : /* Build the needed expression execution states */
6174 8250 : foreach(l, tab->constraints)
6175 : {
6176 2070 : NewConstraint *con = lfirst(l);
6177 :
6178 2070 : switch (con->contype)
6179 : {
6180 834 : case CONSTR_CHECK:
6181 834 : needscan = true;
6182 834 : con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6183 834 : break;
6184 1236 : case CONSTR_FOREIGN:
6185 : /* Nothing to do here */
6186 1236 : break;
6187 0 : default:
6188 0 : elog(ERROR, "unrecognized constraint type: %d",
6189 : (int) con->contype);
6190 : }
6191 : }
6192 :
6193 : /* Build expression execution states for partition check quals */
6194 6180 : if (tab->partition_constraint)
6195 : {
6196 2414 : needscan = true;
6197 2414 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6198 : }
6199 :
6200 7224 : foreach(l, tab->newvals)
6201 : {
6202 1044 : NewColumnValue *ex = lfirst(l);
6203 :
6204 : /* expr already planned */
6205 1044 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6206 : }
6207 :
6208 6180 : notnull_attrs = notnull_virtual_attrs = NIL;
6209 6180 : if (newrel || tab->verify_new_notnull)
6210 : {
6211 : /*
6212 : * If we are rebuilding the tuples OR if we added any new but not
6213 : * verified not-null constraints, check all *valid* not-null
6214 : * constraints. This is a bit of overkill but it minimizes risk of
6215 : * bugs.
6216 : *
6217 : * notnull_attrs does *not* collect attribute numbers for valid
6218 : * not-null constraints over virtual generated columns; instead, they
6219 : * are collected in notnull_virtual_attrs for verification elsewhere.
6220 : */
6221 7502 : for (i = 0; i < newTupDesc->natts; i++)
6222 : {
6223 5488 : CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6224 :
6225 5488 : if (attr->attnullability == ATTNULLABLE_VALID &&
6226 2112 : !attr->attisdropped)
6227 : {
6228 2112 : Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6229 :
6230 2112 : if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6231 2022 : notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6232 : else
6233 90 : notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6234 90 : wholeatt->attnum);
6235 : }
6236 : }
6237 2014 : if (notnull_attrs || notnull_virtual_attrs)
6238 1550 : needscan = true;
6239 : }
6240 :
6241 6180 : if (newrel || needscan)
6242 : {
6243 : ExprContext *econtext;
6244 : TupleTableSlot *oldslot;
6245 : TupleTableSlot *newslot;
6246 : TableScanDesc scan;
6247 : MemoryContext oldCxt;
6248 5146 : List *dropped_attrs = NIL;
6249 : ListCell *lc;
6250 : Snapshot snapshot;
6251 5146 : ResultRelInfo *rInfo = NULL;
6252 :
6253 : /*
6254 : * When adding or changing a virtual generated column with a not-null
6255 : * constraint, we need to evaluate whether the generation expression
6256 : * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6257 : * prepare a dummy ResultRelInfo.
6258 : */
6259 5146 : if (notnull_virtual_attrs != NIL)
6260 : {
6261 : MemoryContext oldcontext;
6262 :
6263 : Assert(newTupDesc->constr->has_generated_virtual);
6264 : Assert(newTupDesc->constr->has_not_null);
6265 60 : oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6266 60 : rInfo = makeNode(ResultRelInfo);
6267 60 : InitResultRelInfo(rInfo,
6268 : oldrel,
6269 : 0, /* dummy rangetable index */
6270 : NULL,
6271 : estate->es_instrument);
6272 60 : MemoryContextSwitchTo(oldcontext);
6273 : }
6274 :
6275 5146 : if (newrel)
6276 982 : ereport(DEBUG1,
6277 : (errmsg_internal("rewriting table \"%s\"",
6278 : RelationGetRelationName(oldrel))));
6279 : else
6280 4164 : ereport(DEBUG1,
6281 : (errmsg_internal("verifying table \"%s\"",
6282 : RelationGetRelationName(oldrel))));
6283 :
6284 5146 : if (newrel)
6285 : {
6286 : /*
6287 : * All predicate locks on the tuples or pages are about to be made
6288 : * invalid, because we move tuples around. Promote them to
6289 : * relation locks.
6290 : */
6291 982 : TransferPredicateLocksToHeapRelation(oldrel);
6292 : }
6293 :
6294 5146 : econtext = GetPerTupleExprContext(estate);
6295 :
6296 : /*
6297 : * Create necessary tuple slots. When rewriting, two slots are needed,
6298 : * otherwise one suffices. In the case where one slot suffices, we
6299 : * need to use the new tuple descriptor, otherwise some constraints
6300 : * can't be evaluated. Note that even when the tuple layout is the
6301 : * same and no rewrite is required, the tupDescs might not be
6302 : * (consider ADD COLUMN without a default).
6303 : */
6304 5146 : if (tab->rewrite)
6305 : {
6306 : Assert(newrel != NULL);
6307 982 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6308 : table_slot_callbacks(oldrel));
6309 982 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6310 : table_slot_callbacks(newrel));
6311 :
6312 : /*
6313 : * Set all columns in the new slot to NULL initially, to ensure
6314 : * columns added as part of the rewrite are initialized to NULL.
6315 : * That is necessary as tab->newvals will not contain an
6316 : * expression for columns with a NULL default, e.g. when adding a
6317 : * column without a default together with a column with a default
6318 : * requiring an actual rewrite.
6319 : */
6320 982 : ExecStoreAllNullTuple(newslot);
6321 : }
6322 : else
6323 : {
6324 4164 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6325 : table_slot_callbacks(oldrel));
6326 4164 : newslot = NULL;
6327 : }
6328 :
6329 : /*
6330 : * Any attributes that are dropped according to the new tuple
6331 : * descriptor can be set to NULL. We precompute the list of dropped
6332 : * attributes to avoid needing to do so in the per-tuple loop.
6333 : */
6334 18138 : for (i = 0; i < newTupDesc->natts; i++)
6335 : {
6336 12992 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6337 790 : dropped_attrs = lappend_int(dropped_attrs, i);
6338 : }
6339 :
6340 : /*
6341 : * Scan through the rows, generating a new row if needed and then
6342 : * checking all the constraints.
6343 : */
6344 5146 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6345 5146 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6346 :
6347 : /*
6348 : * Switch to per-tuple memory context and reset it for each tuple
6349 : * produced, so we don't leak memory.
6350 : */
6351 5146 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6352 :
6353 775204 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6354 : {
6355 : TupleTableSlot *insertslot;
6356 :
6357 765226 : if (tab->rewrite > 0)
6358 : {
6359 : /* Extract data from old tuple */
6360 99834 : slot_getallattrs(oldslot);
6361 99834 : ExecClearTuple(newslot);
6362 :
6363 : /* copy attributes */
6364 99834 : memcpy(newslot->tts_values, oldslot->tts_values,
6365 99834 : sizeof(Datum) * oldslot->tts_nvalid);
6366 99834 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6367 99834 : sizeof(bool) * oldslot->tts_nvalid);
6368 :
6369 : /* Set dropped attributes to null in new tuple */
6370 99950 : foreach(lc, dropped_attrs)
6371 116 : newslot->tts_isnull[lfirst_int(lc)] = true;
6372 :
6373 : /*
6374 : * Constraints and GENERATED expressions might reference the
6375 : * tableoid column, so fill tts_tableOid with the desired
6376 : * value. (We must do this each time, because it gets
6377 : * overwritten with newrel's OID during storing.)
6378 : */
6379 99834 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6380 :
6381 : /*
6382 : * Process supplied expressions to replace selected columns.
6383 : *
6384 : * First, evaluate expressions whose inputs come from the old
6385 : * tuple.
6386 : */
6387 99834 : econtext->ecxt_scantuple = oldslot;
6388 :
6389 205620 : foreach(l, tab->newvals)
6390 : {
6391 105798 : NewColumnValue *ex = lfirst(l);
6392 :
6393 105798 : if (ex->is_generated)
6394 312 : continue;
6395 :
6396 105486 : newslot->tts_values[ex->attnum - 1]
6397 105474 : = ExecEvalExpr(ex->exprstate,
6398 : econtext,
6399 105486 : &newslot->tts_isnull[ex->attnum - 1]);
6400 : }
6401 :
6402 99822 : ExecStoreVirtualTuple(newslot);
6403 :
6404 : /*
6405 : * Now, evaluate any expressions whose inputs come from the
6406 : * new tuple. We assume these columns won't reference each
6407 : * other, so that there's no ordering dependency.
6408 : */
6409 99822 : econtext->ecxt_scantuple = newslot;
6410 :
6411 205608 : foreach(l, tab->newvals)
6412 : {
6413 105786 : NewColumnValue *ex = lfirst(l);
6414 :
6415 105786 : if (!ex->is_generated)
6416 105474 : continue;
6417 :
6418 312 : newslot->tts_values[ex->attnum - 1]
6419 312 : = ExecEvalExpr(ex->exprstate,
6420 : econtext,
6421 312 : &newslot->tts_isnull[ex->attnum - 1]);
6422 : }
6423 :
6424 99822 : insertslot = newslot;
6425 : }
6426 : else
6427 : {
6428 : /*
6429 : * If there's no rewrite, old and new table are guaranteed to
6430 : * have the same AM, so we can just use the old slot to verify
6431 : * new constraints etc.
6432 : */
6433 665392 : insertslot = oldslot;
6434 : }
6435 :
6436 : /* Now check any constraints on the possibly-changed tuple */
6437 765214 : econtext->ecxt_scantuple = insertslot;
6438 :
6439 4106698 : foreach_int(attn, notnull_attrs)
6440 : {
6441 2576474 : if (slot_attisnull(insertslot, attn))
6442 : {
6443 102 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6444 :
6445 102 : ereport(ERROR,
6446 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6447 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6448 : NameStr(attr->attname),
6449 : RelationGetRelationName(oldrel)),
6450 : errtablecol(oldrel, attn)));
6451 : }
6452 : }
6453 :
6454 765112 : if (notnull_virtual_attrs != NIL)
6455 : {
6456 : AttrNumber attnum;
6457 :
6458 84 : attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6459 : estate,
6460 : notnull_virtual_attrs);
6461 84 : if (attnum != InvalidAttrNumber)
6462 : {
6463 30 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6464 :
6465 30 : ereport(ERROR,
6466 : errcode(ERRCODE_NOT_NULL_VIOLATION),
6467 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6468 : NameStr(attr->attname),
6469 : RelationGetRelationName(oldrel)),
6470 : errtablecol(oldrel, attnum));
6471 : }
6472 : }
6473 :
6474 773246 : foreach(l, tab->constraints)
6475 : {
6476 8260 : NewConstraint *con = lfirst(l);
6477 :
6478 8260 : switch (con->contype)
6479 : {
6480 8154 : case CONSTR_CHECK:
6481 8154 : if (!ExecCheck(con->qualstate, econtext))
6482 96 : ereport(ERROR,
6483 : (errcode(ERRCODE_CHECK_VIOLATION),
6484 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6485 : con->name,
6486 : RelationGetRelationName(oldrel)),
6487 : errtableconstraint(oldrel, con->name)));
6488 8058 : break;
6489 106 : case CONSTR_NOTNULL:
6490 : case CONSTR_FOREIGN:
6491 : /* Nothing to do here */
6492 106 : break;
6493 0 : default:
6494 0 : elog(ERROR, "unrecognized constraint type: %d",
6495 : (int) con->contype);
6496 : }
6497 : }
6498 :
6499 764986 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6500 : {
6501 74 : if (tab->validate_default)
6502 26 : ereport(ERROR,
6503 : (errcode(ERRCODE_CHECK_VIOLATION),
6504 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6505 : RelationGetRelationName(oldrel)),
6506 : errtable(oldrel)));
6507 : else
6508 48 : ereport(ERROR,
6509 : (errcode(ERRCODE_CHECK_VIOLATION),
6510 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6511 : RelationGetRelationName(oldrel)),
6512 : errtable(oldrel)));
6513 : }
6514 :
6515 : /* Write the tuple out to the new relation */
6516 764912 : if (newrel)
6517 99792 : table_tuple_insert(newrel, insertslot, mycid,
6518 : ti_options, bistate);
6519 :
6520 764912 : ResetExprContext(econtext);
6521 :
6522 764912 : CHECK_FOR_INTERRUPTS();
6523 : }
6524 :
6525 4832 : MemoryContextSwitchTo(oldCxt);
6526 4832 : table_endscan(scan);
6527 4832 : UnregisterSnapshot(snapshot);
6528 :
6529 4832 : ExecDropSingleTupleTableSlot(oldslot);
6530 4832 : if (newslot)
6531 940 : ExecDropSingleTupleTableSlot(newslot);
6532 : }
6533 :
6534 5866 : FreeExecutorState(estate);
6535 :
6536 5866 : table_close(oldrel, NoLock);
6537 5866 : if (newrel)
6538 : {
6539 940 : FreeBulkInsertState(bistate);
6540 :
6541 940 : table_finish_bulk_insert(newrel, ti_options);
6542 :
6543 940 : table_close(newrel, NoLock);
6544 : }
6545 5866 : }
6546 :
6547 : /*
6548 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6549 : */
6550 : static AlteredTableInfo *
6551 44258 : ATGetQueueEntry(List **wqueue, Relation rel)
6552 : {
6553 44258 : Oid relid = RelationGetRelid(rel);
6554 : AlteredTableInfo *tab;
6555 : ListCell *ltab;
6556 :
6557 54458 : foreach(ltab, *wqueue)
6558 : {
6559 15074 : tab = (AlteredTableInfo *) lfirst(ltab);
6560 15074 : if (tab->relid == relid)
6561 4874 : return tab;
6562 : }
6563 :
6564 : /*
6565 : * Not there, so add it. Note that we make a copy of the relation's
6566 : * existing descriptor before anything interesting can happen to it.
6567 : */
6568 39384 : tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6569 39384 : tab->relid = relid;
6570 39384 : tab->rel = NULL; /* set later */
6571 39384 : tab->relkind = rel->rd_rel->relkind;
6572 39384 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6573 39384 : tab->newAccessMethod = InvalidOid;
6574 39384 : tab->chgAccessMethod = false;
6575 39384 : tab->newTableSpace = InvalidOid;
6576 39384 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6577 39384 : tab->chgPersistence = false;
6578 :
6579 39384 : *wqueue = lappend(*wqueue, tab);
6580 :
6581 39384 : return tab;
6582 : }
6583 :
6584 : static const char *
6585 80 : alter_table_type_to_string(AlterTableType cmdtype)
6586 : {
6587 80 : switch (cmdtype)
6588 : {
6589 0 : case AT_AddColumn:
6590 : case AT_AddColumnToView:
6591 0 : return "ADD COLUMN";
6592 0 : case AT_ColumnDefault:
6593 : case AT_CookedColumnDefault:
6594 0 : return "ALTER COLUMN ... SET DEFAULT";
6595 6 : case AT_DropNotNull:
6596 6 : return "ALTER COLUMN ... DROP NOT NULL";
6597 6 : case AT_SetNotNull:
6598 6 : return "ALTER COLUMN ... SET NOT NULL";
6599 0 : case AT_SetExpression:
6600 0 : return "ALTER COLUMN ... SET EXPRESSION";
6601 0 : case AT_DropExpression:
6602 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6603 0 : case AT_SetStatistics:
6604 0 : return "ALTER COLUMN ... SET STATISTICS";
6605 12 : case AT_SetOptions:
6606 12 : return "ALTER COLUMN ... SET";
6607 0 : case AT_ResetOptions:
6608 0 : return "ALTER COLUMN ... RESET";
6609 0 : case AT_SetStorage:
6610 0 : return "ALTER COLUMN ... SET STORAGE";
6611 0 : case AT_SetCompression:
6612 0 : return "ALTER COLUMN ... SET COMPRESSION";
6613 6 : case AT_DropColumn:
6614 6 : return "DROP COLUMN";
6615 0 : case AT_AddIndex:
6616 : case AT_ReAddIndex:
6617 0 : return NULL; /* not real grammar */
6618 0 : case AT_AddConstraint:
6619 : case AT_ReAddConstraint:
6620 : case AT_ReAddDomainConstraint:
6621 : case AT_AddIndexConstraint:
6622 0 : return "ADD CONSTRAINT";
6623 6 : case AT_AlterConstraint:
6624 6 : return "ALTER CONSTRAINT";
6625 0 : case AT_ValidateConstraint:
6626 0 : return "VALIDATE CONSTRAINT";
6627 0 : case AT_DropConstraint:
6628 0 : return "DROP CONSTRAINT";
6629 0 : case AT_ReAddComment:
6630 0 : return NULL; /* not real grammar */
6631 0 : case AT_AlterColumnType:
6632 0 : return "ALTER COLUMN ... SET DATA TYPE";
6633 0 : case AT_AlterColumnGenericOptions:
6634 0 : return "ALTER COLUMN ... OPTIONS";
6635 0 : case AT_ChangeOwner:
6636 0 : return "OWNER TO";
6637 0 : case AT_ClusterOn:
6638 0 : return "CLUSTER ON";
6639 0 : case AT_DropCluster:
6640 0 : return "SET WITHOUT CLUSTER";
6641 0 : case AT_SetAccessMethod:
6642 0 : return "SET ACCESS METHOD";
6643 6 : case AT_SetLogged:
6644 6 : return "SET LOGGED";
6645 6 : case AT_SetUnLogged:
6646 6 : return "SET UNLOGGED";
6647 0 : case AT_DropOids:
6648 0 : return "SET WITHOUT OIDS";
6649 0 : case AT_SetTableSpace:
6650 0 : return "SET TABLESPACE";
6651 2 : case AT_SetRelOptions:
6652 2 : return "SET";
6653 0 : case AT_ResetRelOptions:
6654 0 : return "RESET";
6655 0 : case AT_ReplaceRelOptions:
6656 0 : return NULL; /* not real grammar */
6657 0 : case AT_EnableTrig:
6658 0 : return "ENABLE TRIGGER";
6659 0 : case AT_EnableAlwaysTrig:
6660 0 : return "ENABLE ALWAYS TRIGGER";
6661 0 : case AT_EnableReplicaTrig:
6662 0 : return "ENABLE REPLICA TRIGGER";
6663 0 : case AT_DisableTrig:
6664 0 : return "DISABLE TRIGGER";
6665 0 : case AT_EnableTrigAll:
6666 0 : return "ENABLE TRIGGER ALL";
6667 0 : case AT_DisableTrigAll:
6668 0 : return "DISABLE TRIGGER ALL";
6669 0 : case AT_EnableTrigUser:
6670 0 : return "ENABLE TRIGGER USER";
6671 0 : case AT_DisableTrigUser:
6672 0 : return "DISABLE TRIGGER USER";
6673 0 : case AT_EnableRule:
6674 0 : return "ENABLE RULE";
6675 0 : case AT_EnableAlwaysRule:
6676 0 : return "ENABLE ALWAYS RULE";
6677 0 : case AT_EnableReplicaRule:
6678 0 : return "ENABLE REPLICA RULE";
6679 0 : case AT_DisableRule:
6680 0 : return "DISABLE RULE";
6681 0 : case AT_AddInherit:
6682 0 : return "INHERIT";
6683 0 : case AT_DropInherit:
6684 0 : return "NO INHERIT";
6685 0 : case AT_AddOf:
6686 0 : return "OF";
6687 0 : case AT_DropOf:
6688 0 : return "NOT OF";
6689 0 : case AT_ReplicaIdentity:
6690 0 : return "REPLICA IDENTITY";
6691 0 : case AT_EnableRowSecurity:
6692 0 : return "ENABLE ROW SECURITY";
6693 0 : case AT_DisableRowSecurity:
6694 0 : return "DISABLE ROW SECURITY";
6695 0 : case AT_ForceRowSecurity:
6696 0 : return "FORCE ROW SECURITY";
6697 0 : case AT_NoForceRowSecurity:
6698 0 : return "NO FORCE ROW SECURITY";
6699 0 : case AT_GenericOptions:
6700 0 : return "OPTIONS";
6701 6 : case AT_AttachPartition:
6702 6 : return "ATTACH PARTITION";
6703 18 : case AT_DetachPartition:
6704 18 : return "DETACH PARTITION";
6705 6 : case AT_DetachPartitionFinalize:
6706 6 : return "DETACH PARTITION ... FINALIZE";
6707 0 : case AT_AddIdentity:
6708 0 : return "ALTER COLUMN ... ADD IDENTITY";
6709 0 : case AT_SetIdentity:
6710 0 : return "ALTER COLUMN ... SET";
6711 0 : case AT_DropIdentity:
6712 0 : return "ALTER COLUMN ... DROP IDENTITY";
6713 0 : case AT_ReAddStatistics:
6714 0 : return NULL; /* not real grammar */
6715 : }
6716 :
6717 0 : return NULL;
6718 : }
6719 :
6720 : /*
6721 : * ATSimplePermissions
6722 : *
6723 : * - Ensure that it is a relation (or possibly a view)
6724 : * - Ensure this user is the owner
6725 : * - Ensure that it is not a system table
6726 : */
6727 : static void
6728 38930 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6729 : {
6730 : int actual_target;
6731 :
6732 38930 : switch (rel->rd_rel->relkind)
6733 : {
6734 30444 : case RELKIND_RELATION:
6735 30444 : actual_target = ATT_TABLE;
6736 30444 : break;
6737 6118 : case RELKIND_PARTITIONED_TABLE:
6738 6118 : actual_target = ATT_PARTITIONED_TABLE;
6739 6118 : break;
6740 406 : case RELKIND_VIEW:
6741 406 : actual_target = ATT_VIEW;
6742 406 : break;
6743 48 : case RELKIND_MATVIEW:
6744 48 : actual_target = ATT_MATVIEW;
6745 48 : break;
6746 228 : case RELKIND_INDEX:
6747 228 : actual_target = ATT_INDEX;
6748 228 : break;
6749 518 : case RELKIND_PARTITIONED_INDEX:
6750 518 : actual_target = ATT_PARTITIONED_INDEX;
6751 518 : break;
6752 216 : case RELKIND_COMPOSITE_TYPE:
6753 216 : actual_target = ATT_COMPOSITE_TYPE;
6754 216 : break;
6755 926 : case RELKIND_FOREIGN_TABLE:
6756 926 : actual_target = ATT_FOREIGN_TABLE;
6757 926 : break;
6758 24 : case RELKIND_SEQUENCE:
6759 24 : actual_target = ATT_SEQUENCE;
6760 24 : break;
6761 2 : default:
6762 2 : actual_target = 0;
6763 2 : break;
6764 : }
6765 :
6766 : /* Wrong target type? */
6767 38930 : if ((actual_target & allowed_targets) == 0)
6768 : {
6769 80 : const char *action_str = alter_table_type_to_string(cmdtype);
6770 :
6771 80 : if (action_str)
6772 80 : ereport(ERROR,
6773 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6774 : /* translator: %s is a group of some SQL keywords */
6775 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6776 : action_str, RelationGetRelationName(rel)),
6777 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6778 : else
6779 : /* internal error? */
6780 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6781 : RelationGetRelationName(rel));
6782 : }
6783 :
6784 : /* Permissions checks */
6785 38850 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6786 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6787 12 : RelationGetRelationName(rel));
6788 :
6789 38838 : if (!allowSystemTableMods && IsSystemRelation(rel))
6790 0 : ereport(ERROR,
6791 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6792 : errmsg("permission denied: \"%s\" is a system catalog",
6793 : RelationGetRelationName(rel))));
6794 38838 : }
6795 :
6796 : /*
6797 : * ATSimpleRecursion
6798 : *
6799 : * Simple table recursion sufficient for most ALTER TABLE operations.
6800 : * All direct and indirect children are processed in an unspecified order.
6801 : * Note that if a child inherits from the original table via multiple
6802 : * inheritance paths, it will be visited just once.
6803 : */
6804 : static void
6805 1324 : ATSimpleRecursion(List **wqueue, Relation rel,
6806 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6807 : AlterTableUtilityContext *context)
6808 : {
6809 : /*
6810 : * Propagate to children, if desired and if there are (or might be) any
6811 : * children.
6812 : */
6813 1324 : if (recurse && rel->rd_rel->relhassubclass)
6814 : {
6815 84 : Oid relid = RelationGetRelid(rel);
6816 : ListCell *child;
6817 : List *children;
6818 :
6819 84 : children = find_all_inheritors(relid, lockmode, NULL);
6820 :
6821 : /*
6822 : * find_all_inheritors does the recursive search of the inheritance
6823 : * hierarchy, so all we have to do is process all of the relids in the
6824 : * list that it returns.
6825 : */
6826 366 : foreach(child, children)
6827 : {
6828 282 : Oid childrelid = lfirst_oid(child);
6829 : Relation childrel;
6830 :
6831 282 : if (childrelid == relid)
6832 84 : continue;
6833 : /* find_all_inheritors already got lock */
6834 198 : childrel = relation_open(childrelid, NoLock);
6835 198 : CheckAlterTableIsSafe(childrel);
6836 198 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6837 198 : relation_close(childrel, NoLock);
6838 : }
6839 : }
6840 1324 : }
6841 :
6842 : /*
6843 : * Obtain list of partitions of the given table, locking them all at the given
6844 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6845 : *
6846 : * This function is a no-op if the given relation is not a partitioned table;
6847 : * in particular, nothing is done if it's a legacy inheritance parent.
6848 : */
6849 : static void
6850 798 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6851 : {
6852 798 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6853 : {
6854 : List *inh;
6855 : ListCell *cell;
6856 :
6857 176 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6858 : /* first element is the parent rel; must ignore it */
6859 574 : for_each_from(cell, inh, 1)
6860 : {
6861 : Relation childrel;
6862 :
6863 : /* find_all_inheritors already got lock */
6864 404 : childrel = table_open(lfirst_oid(cell), NoLock);
6865 404 : CheckAlterTableIsSafe(childrel);
6866 398 : table_close(childrel, NoLock);
6867 : }
6868 170 : list_free(inh);
6869 : }
6870 792 : }
6871 :
6872 : /*
6873 : * ATTypedTableRecursion
6874 : *
6875 : * Propagate ALTER TYPE operations to the typed tables of that type.
6876 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6877 : * recursion to inheritance children of the typed tables.
6878 : */
6879 : static void
6880 192 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6881 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6882 : {
6883 : ListCell *child;
6884 : List *children;
6885 :
6886 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6887 :
6888 192 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6889 192 : RelationGetRelationName(rel),
6890 : cmd->behavior);
6891 :
6892 204 : foreach(child, children)
6893 : {
6894 30 : Oid childrelid = lfirst_oid(child);
6895 : Relation childrel;
6896 :
6897 30 : childrel = relation_open(childrelid, lockmode);
6898 30 : CheckAlterTableIsSafe(childrel);
6899 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6900 30 : relation_close(childrel, NoLock);
6901 : }
6902 174 : }
6903 :
6904 :
6905 : /*
6906 : * find_composite_type_dependencies
6907 : *
6908 : * Check to see if the type "typeOid" is being used as a column in some table
6909 : * (possibly nested several levels deep in composite types, arrays, etc!).
6910 : * Eventually, we'd like to propagate the check or rewrite operation
6911 : * into such tables, but for now, just error out if we find any.
6912 : *
6913 : * Caller should provide either the associated relation of a rowtype,
6914 : * or a type name (not both) for use in the error message, if any.
6915 : *
6916 : * Note that "typeOid" is not necessarily a composite type; it could also be
6917 : * another container type such as an array or range, or a domain over one of
6918 : * these things. The name of this function is therefore somewhat historical,
6919 : * but it's not worth changing.
6920 : *
6921 : * We assume that functions and views depending on the type are not reasons
6922 : * to reject the ALTER. (How safe is this really?)
6923 : */
6924 : void
6925 4464 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6926 : const char *origTypeName)
6927 : {
6928 : Relation depRel;
6929 : ScanKeyData key[2];
6930 : SysScanDesc depScan;
6931 : HeapTuple depTup;
6932 :
6933 : /* since this function recurses, it could be driven to stack overflow */
6934 4464 : check_stack_depth();
6935 :
6936 : /*
6937 : * We scan pg_depend to find those things that depend on the given type.
6938 : * (We assume we can ignore refobjsubid for a type.)
6939 : */
6940 4464 : depRel = table_open(DependRelationId, AccessShareLock);
6941 :
6942 4464 : ScanKeyInit(&key[0],
6943 : Anum_pg_depend_refclassid,
6944 : BTEqualStrategyNumber, F_OIDEQ,
6945 : ObjectIdGetDatum(TypeRelationId));
6946 4464 : ScanKeyInit(&key[1],
6947 : Anum_pg_depend_refobjid,
6948 : BTEqualStrategyNumber, F_OIDEQ,
6949 : ObjectIdGetDatum(typeOid));
6950 :
6951 4464 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6952 : NULL, 2, key);
6953 :
6954 6864 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6955 : {
6956 2526 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6957 : Relation rel;
6958 : TupleDesc tupleDesc;
6959 : Form_pg_attribute att;
6960 :
6961 : /* Check for directly dependent types */
6962 2526 : if (pg_depend->classid == TypeRelationId)
6963 : {
6964 : /*
6965 : * This must be an array, domain, or range containing the given
6966 : * type, so recursively check for uses of this type. Note that
6967 : * any error message will mention the original type not the
6968 : * container; this is intentional.
6969 : */
6970 2150 : find_composite_type_dependencies(pg_depend->objid,
6971 : origRelation, origTypeName);
6972 2126 : continue;
6973 : }
6974 :
6975 : /* Else, ignore dependees that aren't relations */
6976 376 : if (pg_depend->classid != RelationRelationId)
6977 122 : continue;
6978 :
6979 254 : rel = relation_open(pg_depend->objid, AccessShareLock);
6980 254 : tupleDesc = RelationGetDescr(rel);
6981 :
6982 : /*
6983 : * If objsubid identifies a specific column, refer to that in error
6984 : * messages. Otherwise, search to see if there's a user column of the
6985 : * type. (We assume system columns are never of interesting types.)
6986 : * The search is needed because an index containing an expression
6987 : * column of the target type will just be recorded as a whole-relation
6988 : * dependency. If we do not find a column of the type, the dependency
6989 : * must indicate that the type is transiently referenced in an index
6990 : * expression but not stored on disk, which we assume is OK, just as
6991 : * we do for references in views. (It could also be that the target
6992 : * type is embedded in some container type that is stored in an index
6993 : * column, but the previous recursion should catch such cases.)
6994 : */
6995 254 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6996 96 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6997 : else
6998 : {
6999 158 : att = NULL;
7000 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
7001 : {
7002 254 : att = TupleDescAttr(tupleDesc, attno - 1);
7003 254 : if (att->atttypid == typeOid && !att->attisdropped)
7004 6 : break;
7005 248 : att = NULL;
7006 : }
7007 158 : if (att == NULL)
7008 : {
7009 : /* No such column, so assume OK */
7010 152 : relation_close(rel, AccessShareLock);
7011 152 : continue;
7012 : }
7013 : }
7014 :
7015 : /*
7016 : * We definitely should reject if the relation has storage. If it's
7017 : * partitioned, then perhaps we don't have to reject: if there are
7018 : * partitions then we'll fail when we find one, else there is no
7019 : * stored data to worry about. However, it's possible that the type
7020 : * change would affect conclusions about whether the type is sortable
7021 : * or hashable and thus (if it's a partitioning column) break the
7022 : * partitioning rule. For now, reject for partitioned rels too.
7023 : */
7024 102 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7025 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7026 : {
7027 102 : if (origTypeName)
7028 30 : ereport(ERROR,
7029 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7030 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7031 : origTypeName,
7032 : RelationGetRelationName(rel),
7033 : NameStr(att->attname))));
7034 72 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7035 18 : ereport(ERROR,
7036 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7037 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7038 : RelationGetRelationName(origRelation),
7039 : RelationGetRelationName(rel),
7040 : NameStr(att->attname))));
7041 54 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7042 6 : ereport(ERROR,
7043 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7044 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7045 : RelationGetRelationName(origRelation),
7046 : RelationGetRelationName(rel),
7047 : NameStr(att->attname))));
7048 : else
7049 48 : ereport(ERROR,
7050 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7051 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7052 : RelationGetRelationName(origRelation),
7053 : RelationGetRelationName(rel),
7054 : NameStr(att->attname))));
7055 : }
7056 0 : else if (OidIsValid(rel->rd_rel->reltype))
7057 : {
7058 : /*
7059 : * A view or composite type itself isn't a problem, but we must
7060 : * recursively check for indirect dependencies via its rowtype.
7061 : */
7062 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
7063 : origRelation, origTypeName);
7064 : }
7065 :
7066 0 : relation_close(rel, AccessShareLock);
7067 : }
7068 :
7069 4338 : systable_endscan(depScan);
7070 :
7071 4338 : relation_close(depRel, AccessShareLock);
7072 4338 : }
7073 :
7074 :
7075 : /*
7076 : * find_typed_table_dependencies
7077 : *
7078 : * Check to see if a composite type is being used as the type of a
7079 : * typed table. Abort if any are found and behavior is RESTRICT.
7080 : * Else return the list of tables.
7081 : */
7082 : static List *
7083 216 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7084 : {
7085 : Relation classRel;
7086 : ScanKeyData key[1];
7087 : TableScanDesc scan;
7088 : HeapTuple tuple;
7089 216 : List *result = NIL;
7090 :
7091 216 : classRel = table_open(RelationRelationId, AccessShareLock);
7092 :
7093 216 : ScanKeyInit(&key[0],
7094 : Anum_pg_class_reloftype,
7095 : BTEqualStrategyNumber, F_OIDEQ,
7096 : ObjectIdGetDatum(typeOid));
7097 :
7098 216 : scan = table_beginscan_catalog(classRel, 1, key);
7099 :
7100 252 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7101 : {
7102 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7103 :
7104 60 : if (behavior == DROP_RESTRICT)
7105 24 : ereport(ERROR,
7106 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7107 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7108 : typeName),
7109 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7110 : else
7111 36 : result = lappend_oid(result, classform->oid);
7112 : }
7113 :
7114 192 : table_endscan(scan);
7115 192 : table_close(classRel, AccessShareLock);
7116 :
7117 192 : return result;
7118 : }
7119 :
7120 :
7121 : /*
7122 : * check_of_type
7123 : *
7124 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7125 : * isn't suitable, throw an error. Currently, we require that the type
7126 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7127 : * would require handling a number of extra corner cases in the DDL commands.
7128 : * (Also, allowing domain-over-composite would open up a can of worms about
7129 : * whether and how the domain's constraints should apply to derived tables.)
7130 : */
7131 : void
7132 194 : check_of_type(HeapTuple typetuple)
7133 : {
7134 194 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7135 194 : bool typeOk = false;
7136 :
7137 194 : if (typ->typtype == TYPTYPE_COMPOSITE)
7138 : {
7139 : Relation typeRelation;
7140 :
7141 : Assert(OidIsValid(typ->typrelid));
7142 188 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7143 188 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7144 :
7145 : /*
7146 : * Close the parent rel, but keep our AccessShareLock on it until xact
7147 : * commit. That will prevent someone else from deleting or ALTERing
7148 : * the type before the typed table creation/conversion commits.
7149 : */
7150 188 : relation_close(typeRelation, NoLock);
7151 :
7152 188 : if (!typeOk)
7153 6 : ereport(ERROR,
7154 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7155 : errmsg("type %s is the row type of another table",
7156 : format_type_be(typ->oid)),
7157 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7158 : }
7159 : else
7160 6 : ereport(ERROR,
7161 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7162 : errmsg("type %s is not a composite type",
7163 : format_type_be(typ->oid))));
7164 182 : }
7165 :
7166 :
7167 : /*
7168 : * ALTER TABLE ADD COLUMN
7169 : *
7170 : * Adds an additional attribute to a relation making the assumption that
7171 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7172 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7173 : * AlterTableCmd's.
7174 : *
7175 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7176 : * have to decide at runtime whether to recurse or not depending on whether we
7177 : * actually add a column or merely merge with an existing column. (We can't
7178 : * check this in a static pre-pass because it won't handle multiple inheritance
7179 : * situations correctly.)
7180 : */
7181 : static void
7182 2188 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7183 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7184 : AlterTableUtilityContext *context)
7185 : {
7186 2188 : if (rel->rd_rel->reloftype && !recursing)
7187 6 : ereport(ERROR,
7188 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7189 : errmsg("cannot add column to typed table")));
7190 :
7191 2182 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7192 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7193 :
7194 2176 : if (recurse && !is_view)
7195 2076 : cmd->recurse = true;
7196 2176 : }
7197 :
7198 : /*
7199 : * Add a column to a table. The return value is the address of the
7200 : * new column in the parent relation.
7201 : *
7202 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7203 : * copy (but that happens only after we check for IF NOT EXISTS).
7204 : */
7205 : static ObjectAddress
7206 2908 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7207 : AlterTableCmd **cmd, bool recurse, bool recursing,
7208 : LOCKMODE lockmode, AlterTablePass cur_pass,
7209 : AlterTableUtilityContext *context)
7210 : {
7211 2908 : Oid myrelid = RelationGetRelid(rel);
7212 2908 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7213 2908 : bool if_not_exists = (*cmd)->missing_ok;
7214 : Relation pgclass,
7215 : attrdesc;
7216 : HeapTuple reltup;
7217 : Form_pg_class relform;
7218 : Form_pg_attribute attribute;
7219 : int newattnum;
7220 : char relkind;
7221 : Expr *defval;
7222 : List *children;
7223 : ListCell *child;
7224 : AlterTableCmd *childcmd;
7225 : ObjectAddress address;
7226 : TupleDesc tupdesc;
7227 :
7228 : /* since this function recurses, it could be driven to stack overflow */
7229 2908 : check_stack_depth();
7230 :
7231 : /* At top level, permission check was done in ATPrepCmd, else do it */
7232 2908 : if (recursing)
7233 738 : ATSimplePermissions((*cmd)->subtype, rel,
7234 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7235 :
7236 2908 : if (rel->rd_rel->relispartition && !recursing)
7237 12 : ereport(ERROR,
7238 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7239 : errmsg("cannot add column to a partition")));
7240 :
7241 2896 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7242 :
7243 : /*
7244 : * Are we adding the column to a recursion child? If so, check whether to
7245 : * merge with an existing definition for the column. If we do merge, we
7246 : * must not recurse. Children will already have the column, and recursing
7247 : * into them would mess up attinhcount.
7248 : */
7249 2896 : if (colDef->inhcount > 0)
7250 : {
7251 : HeapTuple tuple;
7252 :
7253 : /* Does child already have a column by this name? */
7254 738 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7255 738 : if (HeapTupleIsValid(tuple))
7256 : {
7257 60 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7258 : Oid ctypeId;
7259 : int32 ctypmod;
7260 : Oid ccollid;
7261 :
7262 : /* Child column must match on type, typmod, and collation */
7263 60 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7264 60 : if (ctypeId != childatt->atttypid ||
7265 60 : ctypmod != childatt->atttypmod)
7266 0 : ereport(ERROR,
7267 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7268 : errmsg("child table \"%s\" has different type for column \"%s\"",
7269 : RelationGetRelationName(rel), colDef->colname)));
7270 60 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7271 60 : if (ccollid != childatt->attcollation)
7272 0 : ereport(ERROR,
7273 : (errcode(ERRCODE_COLLATION_MISMATCH),
7274 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7275 : RelationGetRelationName(rel), colDef->colname),
7276 : errdetail("\"%s\" versus \"%s\"",
7277 : get_collation_name(ccollid),
7278 : get_collation_name(childatt->attcollation))));
7279 :
7280 : /* Bump the existing child att's inhcount */
7281 60 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7282 : &childatt->attinhcount))
7283 0 : ereport(ERROR,
7284 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7285 : errmsg("too many inheritance parents"));
7286 60 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7287 :
7288 60 : heap_freetuple(tuple);
7289 :
7290 : /* Inform the user about the merge */
7291 60 : ereport(NOTICE,
7292 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7293 : colDef->colname, RelationGetRelationName(rel))));
7294 :
7295 60 : table_close(attrdesc, RowExclusiveLock);
7296 :
7297 : /* Make the child column change visible */
7298 60 : CommandCounterIncrement();
7299 :
7300 60 : return InvalidObjectAddress;
7301 : }
7302 : }
7303 :
7304 : /* skip if the name already exists and if_not_exists is true */
7305 2836 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7306 : {
7307 54 : table_close(attrdesc, RowExclusiveLock);
7308 54 : return InvalidObjectAddress;
7309 : }
7310 :
7311 : /*
7312 : * Okay, we need to add the column, so go ahead and do parse
7313 : * transformation. This can result in queueing up, or even immediately
7314 : * executing, subsidiary operations (such as creation of unique indexes);
7315 : * so we mustn't do it until we have made the if_not_exists check.
7316 : *
7317 : * When recursing, the command was already transformed and we needn't do
7318 : * so again. Also, if context isn't given we can't transform. (That
7319 : * currently happens only for AT_AddColumnToView; we expect that view.c
7320 : * passed us a ColumnDef that doesn't need work.)
7321 : */
7322 2752 : if (context != NULL && !recursing)
7323 : {
7324 2050 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7325 : cur_pass, context);
7326 : Assert(*cmd != NULL);
7327 2044 : colDef = castNode(ColumnDef, (*cmd)->def);
7328 : }
7329 :
7330 : /*
7331 : * Regular inheritance children are independent enough not to inherit the
7332 : * identity column from parent hence cannot recursively add identity
7333 : * column if the table has inheritance children.
7334 : *
7335 : * Partitions, on the other hand, are integral part of a partitioned table
7336 : * and inherit identity column. Hence propagate identity column down the
7337 : * partition hierarchy.
7338 : */
7339 2746 : if (colDef->identity &&
7340 54 : recurse &&
7341 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7342 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7343 6 : ereport(ERROR,
7344 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7345 : errmsg("cannot recursively add identity column to table that has child tables")));
7346 :
7347 2740 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7348 :
7349 2740 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7350 2740 : if (!HeapTupleIsValid(reltup))
7351 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7352 2740 : relform = (Form_pg_class) GETSTRUCT(reltup);
7353 2740 : relkind = relform->relkind;
7354 :
7355 : /* Determine the new attribute's number */
7356 2740 : newattnum = relform->relnatts + 1;
7357 2740 : if (newattnum > MaxHeapAttributeNumber)
7358 0 : ereport(ERROR,
7359 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7360 : errmsg("tables can have at most %d columns",
7361 : MaxHeapAttributeNumber)));
7362 :
7363 : /*
7364 : * Construct new attribute's pg_attribute entry.
7365 : */
7366 2740 : tupdesc = BuildDescForRelation(list_make1(colDef));
7367 :
7368 2728 : attribute = TupleDescAttr(tupdesc, 0);
7369 :
7370 : /* Fix up attribute number */
7371 2728 : attribute->attnum = newattnum;
7372 :
7373 : /* make sure datatype is legal for a column */
7374 5456 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7375 2728 : list_make1_oid(rel->rd_rel->reltype),
7376 2728 : (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7377 :
7378 2692 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7379 :
7380 2692 : table_close(attrdesc, RowExclusiveLock);
7381 :
7382 : /*
7383 : * Update pg_class tuple as appropriate
7384 : */
7385 2692 : relform->relnatts = newattnum;
7386 :
7387 2692 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7388 :
7389 2692 : heap_freetuple(reltup);
7390 :
7391 : /* Post creation hook for new attribute */
7392 2692 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7393 :
7394 2692 : table_close(pgclass, RowExclusiveLock);
7395 :
7396 : /* Make the attribute's catalog entry visible */
7397 2692 : CommandCounterIncrement();
7398 :
7399 : /*
7400 : * Store the DEFAULT, if any, in the catalogs
7401 : */
7402 2692 : if (colDef->raw_default)
7403 : {
7404 : RawColumnDefault *rawEnt;
7405 :
7406 924 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7407 924 : rawEnt->attnum = attribute->attnum;
7408 924 : rawEnt->raw_default = copyObject(colDef->raw_default);
7409 924 : rawEnt->generated = colDef->generated;
7410 :
7411 : /*
7412 : * This function is intended for CREATE TABLE, so it processes a
7413 : * _list_ of defaults, but we just do one.
7414 : */
7415 924 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7416 : false, true, false, NULL);
7417 :
7418 : /* Make the additional catalog changes visible */
7419 900 : CommandCounterIncrement();
7420 : }
7421 :
7422 : /*
7423 : * Tell Phase 3 to fill in the default expression, if there is one.
7424 : *
7425 : * If there is no default, Phase 3 doesn't have to do anything, because
7426 : * that effectively means that the default is NULL. The heap tuple access
7427 : * routines always check for attnum > # of attributes in tuple, and return
7428 : * NULL if so, so without any modification of the tuple data we will get
7429 : * the effect of NULL values in the new column.
7430 : *
7431 : * An exception occurs when the new column is of a domain type: the domain
7432 : * might have a not-null constraint, or a check constraint that indirectly
7433 : * rejects nulls. If there are any domain constraints then we construct
7434 : * an explicit NULL default value that will be passed through
7435 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7436 : * rewriting the table which we really wouldn't have to do; but we do it
7437 : * to preserve the historical behavior that such a failure will be raised
7438 : * only if the table currently contains some rows.)
7439 : *
7440 : * Note: we use build_column_default, and not just the cooked default
7441 : * returned by AddRelationNewConstraints, so that the right thing happens
7442 : * when a datatype's default applies.
7443 : *
7444 : * Note: it might seem that this should happen at the end of Phase 2, so
7445 : * that the effects of subsequent subcommands can be taken into account.
7446 : * It's intentional that we do it now, though. The new column should be
7447 : * filled according to what is said in the ADD COLUMN subcommand, so that
7448 : * the effects are the same as if this subcommand had been run by itself
7449 : * and the later subcommands had been issued in new ALTER TABLE commands.
7450 : *
7451 : * We can skip this entirely for relations without storage, since Phase 3
7452 : * is certainly not going to touch them.
7453 : */
7454 2668 : if (RELKIND_HAS_STORAGE(relkind))
7455 : {
7456 : bool has_domain_constraints;
7457 2292 : bool has_missing = false;
7458 :
7459 : /*
7460 : * For an identity column, we can't use build_column_default(),
7461 : * because the sequence ownership isn't set yet. So do it manually.
7462 : */
7463 2292 : if (colDef->identity)
7464 : {
7465 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7466 :
7467 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7468 42 : nve->typeId = attribute->atttypid;
7469 :
7470 42 : defval = (Expr *) nve;
7471 : }
7472 : else
7473 2250 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7474 :
7475 : /* Build CoerceToDomain(NULL) expression if needed */
7476 2292 : has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7477 2292 : if (!defval && has_domain_constraints)
7478 : {
7479 : Oid baseTypeId;
7480 : int32 baseTypeMod;
7481 : Oid baseTypeColl;
7482 :
7483 6 : baseTypeMod = attribute->atttypmod;
7484 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7485 6 : baseTypeColl = get_typcollation(baseTypeId);
7486 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7487 6 : defval = (Expr *) coerce_to_target_type(NULL,
7488 : (Node *) defval,
7489 : baseTypeId,
7490 : attribute->atttypid,
7491 : attribute->atttypmod,
7492 : COERCION_ASSIGNMENT,
7493 : COERCE_IMPLICIT_CAST,
7494 : -1);
7495 6 : if (defval == NULL) /* should not happen */
7496 0 : elog(ERROR, "failed to coerce base type to domain");
7497 : }
7498 :
7499 2292 : if (defval)
7500 : {
7501 : NewColumnValue *newval;
7502 :
7503 : /* Prepare defval for execution, either here or in Phase 3 */
7504 808 : defval = expression_planner(defval);
7505 :
7506 : /* Add the new default to the newvals list */
7507 808 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7508 808 : newval->attnum = attribute->attnum;
7509 808 : newval->expr = defval;
7510 808 : newval->is_generated = (colDef->generated != '\0');
7511 :
7512 808 : tab->newvals = lappend(tab->newvals, newval);
7513 :
7514 : /*
7515 : * Attempt to skip a complete table rewrite by storing the
7516 : * specified DEFAULT value outside of the heap. This is only
7517 : * allowed for plain relations and non-generated columns, and the
7518 : * default expression can't be volatile (stable is OK). Note that
7519 : * contain_volatile_functions deems CoerceToDomain immutable, but
7520 : * here we consider that coercion to a domain with constraints is
7521 : * volatile; else it might fail even when the table is empty.
7522 : */
7523 808 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
7524 808 : !colDef->generated &&
7525 682 : !has_domain_constraints &&
7526 670 : !contain_volatile_functions((Node *) defval))
7527 502 : {
7528 : EState *estate;
7529 : ExprState *exprState;
7530 : Datum missingval;
7531 : bool missingIsNull;
7532 :
7533 : /* Evaluate the default expression */
7534 502 : estate = CreateExecutorState();
7535 502 : exprState = ExecPrepareExpr(defval, estate);
7536 502 : missingval = ExecEvalExpr(exprState,
7537 502 : GetPerTupleExprContext(estate),
7538 : &missingIsNull);
7539 : /* If it turns out NULL, nothing to do; else store it */
7540 502 : if (!missingIsNull)
7541 : {
7542 502 : StoreAttrMissingVal(rel, attribute->attnum, missingval);
7543 : /* Make the additional catalog change visible */
7544 502 : CommandCounterIncrement();
7545 502 : has_missing = true;
7546 : }
7547 502 : FreeExecutorState(estate);
7548 : }
7549 : else
7550 : {
7551 : /*
7552 : * Failed to use missing mode. We have to do a table rewrite
7553 : * to install the value --- unless it's a virtual generated
7554 : * column.
7555 : */
7556 306 : if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7557 216 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7558 : }
7559 : }
7560 :
7561 2292 : if (!has_missing)
7562 : {
7563 : /*
7564 : * If the new column is NOT NULL, and there is no missing value,
7565 : * tell Phase 3 it needs to check for NULLs.
7566 : */
7567 1790 : tab->verify_new_notnull |= colDef->is_not_null;
7568 : }
7569 : }
7570 :
7571 : /*
7572 : * Add needed dependency entries for the new column.
7573 : */
7574 2668 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7575 2668 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7576 :
7577 : /*
7578 : * Propagate to children as appropriate. Unlike most other ALTER
7579 : * routines, we have to do this one level of recursion at a time; we can't
7580 : * use find_all_inheritors to do it in one pass.
7581 : */
7582 : children =
7583 2668 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7584 :
7585 : /*
7586 : * If we are told not to recurse, there had better not be any child
7587 : * tables; else the addition would put them out of step.
7588 : */
7589 2668 : if (children && !recurse)
7590 12 : ereport(ERROR,
7591 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7592 : errmsg("column must be added to child tables too")));
7593 :
7594 : /* Children should see column as singly inherited */
7595 2656 : if (!recursing)
7596 : {
7597 1978 : childcmd = copyObject(*cmd);
7598 1978 : colDef = castNode(ColumnDef, childcmd->def);
7599 1978 : colDef->inhcount = 1;
7600 1978 : colDef->is_local = false;
7601 : }
7602 : else
7603 678 : childcmd = *cmd; /* no need to copy again */
7604 :
7605 3394 : foreach(child, children)
7606 : {
7607 738 : Oid childrelid = lfirst_oid(child);
7608 : Relation childrel;
7609 : AlteredTableInfo *childtab;
7610 :
7611 : /* find_inheritance_children already got lock */
7612 738 : childrel = table_open(childrelid, NoLock);
7613 738 : CheckAlterTableIsSafe(childrel);
7614 :
7615 : /* Find or create work queue entry for this table */
7616 738 : childtab = ATGetQueueEntry(wqueue, childrel);
7617 :
7618 : /* Recurse to child; return value is ignored */
7619 738 : ATExecAddColumn(wqueue, childtab, childrel,
7620 : &childcmd, recurse, true,
7621 : lockmode, cur_pass, context);
7622 :
7623 738 : table_close(childrel, NoLock);
7624 : }
7625 :
7626 2656 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7627 2656 : return address;
7628 : }
7629 :
7630 : /*
7631 : * If a new or renamed column will collide with the name of an existing
7632 : * column and if_not_exists is false then error out, else do nothing.
7633 : */
7634 : static bool
7635 3286 : check_for_column_name_collision(Relation rel, const char *colname,
7636 : bool if_not_exists)
7637 : {
7638 : HeapTuple attTuple;
7639 : int attnum;
7640 :
7641 : /*
7642 : * this test is deliberately not attisdropped-aware, since if one tries to
7643 : * add a column matching a dropped column name, it's gonna fail anyway.
7644 : */
7645 3286 : attTuple = SearchSysCache2(ATTNAME,
7646 : ObjectIdGetDatum(RelationGetRelid(rel)),
7647 : PointerGetDatum(colname));
7648 3286 : if (!HeapTupleIsValid(attTuple))
7649 3190 : return true;
7650 :
7651 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7652 96 : ReleaseSysCache(attTuple);
7653 :
7654 : /*
7655 : * We throw a different error message for conflicts with system column
7656 : * names, since they are normally not shown and the user might otherwise
7657 : * be confused about the reason for the conflict.
7658 : */
7659 96 : if (attnum <= 0)
7660 12 : ereport(ERROR,
7661 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7662 : errmsg("column name \"%s\" conflicts with a system column name",
7663 : colname)));
7664 : else
7665 : {
7666 84 : if (if_not_exists)
7667 : {
7668 54 : ereport(NOTICE,
7669 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7670 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7671 : colname, RelationGetRelationName(rel))));
7672 54 : return false;
7673 : }
7674 :
7675 30 : ereport(ERROR,
7676 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7677 : errmsg("column \"%s\" of relation \"%s\" already exists",
7678 : colname, RelationGetRelationName(rel))));
7679 : }
7680 :
7681 : return true;
7682 : }
7683 :
7684 : /*
7685 : * Install a column's dependency on its datatype.
7686 : */
7687 : static void
7688 3692 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7689 : {
7690 : ObjectAddress myself,
7691 : referenced;
7692 :
7693 3692 : myself.classId = RelationRelationId;
7694 3692 : myself.objectId = relid;
7695 3692 : myself.objectSubId = attnum;
7696 3692 : referenced.classId = TypeRelationId;
7697 3692 : referenced.objectId = typid;
7698 3692 : referenced.objectSubId = 0;
7699 3692 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7700 3692 : }
7701 :
7702 : /*
7703 : * Install a column's dependency on its collation.
7704 : */
7705 : static void
7706 3692 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7707 : {
7708 : ObjectAddress myself,
7709 : referenced;
7710 :
7711 : /* We know the default collation is pinned, so don't bother recording it */
7712 3692 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7713 : {
7714 18 : myself.classId = RelationRelationId;
7715 18 : myself.objectId = relid;
7716 18 : myself.objectSubId = attnum;
7717 18 : referenced.classId = CollationRelationId;
7718 18 : referenced.objectId = collid;
7719 18 : referenced.objectSubId = 0;
7720 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7721 : }
7722 3692 : }
7723 :
7724 : /*
7725 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7726 : *
7727 : * Return the address of the modified column. If the column was already
7728 : * nullable, InvalidObjectAddress is returned.
7729 : */
7730 : static ObjectAddress
7731 268 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7732 : LOCKMODE lockmode)
7733 : {
7734 : HeapTuple tuple;
7735 : HeapTuple conTup;
7736 : Form_pg_attribute attTup;
7737 : AttrNumber attnum;
7738 : Relation attr_rel;
7739 : ObjectAddress address;
7740 :
7741 : /*
7742 : * lookup the attribute
7743 : */
7744 268 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7745 :
7746 268 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7747 268 : if (!HeapTupleIsValid(tuple))
7748 18 : ereport(ERROR,
7749 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7750 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7751 : colName, RelationGetRelationName(rel))));
7752 250 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7753 250 : attnum = attTup->attnum;
7754 250 : ObjectAddressSubSet(address, RelationRelationId,
7755 : RelationGetRelid(rel), attnum);
7756 :
7757 : /* If the column is already nullable there's nothing to do. */
7758 250 : if (!attTup->attnotnull)
7759 : {
7760 0 : table_close(attr_rel, RowExclusiveLock);
7761 0 : return InvalidObjectAddress;
7762 : }
7763 :
7764 : /* Prevent them from altering a system attribute */
7765 250 : if (attnum <= 0)
7766 0 : ereport(ERROR,
7767 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7768 : errmsg("cannot alter system column \"%s\"",
7769 : colName)));
7770 :
7771 250 : if (attTup->attidentity)
7772 18 : ereport(ERROR,
7773 : (errcode(ERRCODE_SYNTAX_ERROR),
7774 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7775 : colName, RelationGetRelationName(rel))));
7776 :
7777 : /*
7778 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7779 : */
7780 232 : if (rel->rd_rel->relispartition)
7781 : {
7782 12 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7783 12 : Relation parent = table_open(parentId, AccessShareLock);
7784 12 : TupleDesc tupDesc = RelationGetDescr(parent);
7785 : AttrNumber parent_attnum;
7786 :
7787 12 : parent_attnum = get_attnum(parentId, colName);
7788 12 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7789 12 : ereport(ERROR,
7790 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7791 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7792 : colName)));
7793 0 : table_close(parent, AccessShareLock);
7794 : }
7795 :
7796 : /*
7797 : * Find the constraint that makes this column NOT NULL, and drop it.
7798 : * dropconstraint_internal() resets attnotnull.
7799 : */
7800 220 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7801 220 : if (conTup == NULL)
7802 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7803 : colName, RelationGetRelationName(rel));
7804 :
7805 : /* The normal case: we have a pg_constraint row, remove it */
7806 220 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7807 : false, lockmode);
7808 166 : heap_freetuple(conTup);
7809 :
7810 166 : InvokeObjectPostAlterHook(RelationRelationId,
7811 : RelationGetRelid(rel), attnum);
7812 :
7813 166 : table_close(attr_rel, RowExclusiveLock);
7814 :
7815 166 : return address;
7816 : }
7817 :
7818 : /*
7819 : * set_attnotnull
7820 : * Helper to update/validate the pg_attribute status of a not-null
7821 : * constraint
7822 : *
7823 : * pg_attribute.attnotnull is set true, if it isn't already.
7824 : * If queue_validation is true, also set up wqueue to validate the constraint.
7825 : * wqueue may be given as NULL when validation is not needed (e.g., on table
7826 : * creation).
7827 : */
7828 : static void
7829 25496 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7830 : bool is_valid, bool queue_validation)
7831 : {
7832 : Form_pg_attribute attr;
7833 : CompactAttribute *thisatt;
7834 :
7835 : Assert(!queue_validation || wqueue);
7836 :
7837 25496 : CheckAlterTableIsSafe(rel);
7838 :
7839 : /*
7840 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7841 : * attribute.
7842 : */
7843 25496 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7844 25496 : if (attr->attisdropped)
7845 0 : return;
7846 :
7847 25496 : if (!attr->attnotnull)
7848 : {
7849 : Relation attr_rel;
7850 : HeapTuple tuple;
7851 :
7852 1478 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7853 :
7854 1478 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7855 1478 : if (!HeapTupleIsValid(tuple))
7856 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7857 : attnum, RelationGetRelid(rel));
7858 :
7859 1478 : thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7860 1478 : thisatt->attnullability = ATTNULLABLE_VALID;
7861 :
7862 1478 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7863 :
7864 1478 : attr->attnotnull = true;
7865 1478 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7866 :
7867 : /*
7868 : * If the nullness isn't already proven by validated constraints, have
7869 : * ALTER TABLE phase 3 test for it.
7870 : */
7871 1478 : if (queue_validation && wqueue &&
7872 1238 : !NotNullImpliedByRelConstraints(rel, attr))
7873 : {
7874 : AlteredTableInfo *tab;
7875 :
7876 1188 : tab = ATGetQueueEntry(wqueue, rel);
7877 1188 : tab->verify_new_notnull = true;
7878 : }
7879 :
7880 1478 : CommandCounterIncrement();
7881 :
7882 1478 : table_close(attr_rel, RowExclusiveLock);
7883 1478 : heap_freetuple(tuple);
7884 : }
7885 : else
7886 : {
7887 24018 : CacheInvalidateRelcache(rel);
7888 : }
7889 : }
7890 :
7891 : /*
7892 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7893 : *
7894 : * Add a not-null constraint to a single table and its children. Returns
7895 : * the address of the constraint added to the parent relation, if one gets
7896 : * added, or InvalidObjectAddress otherwise.
7897 : *
7898 : * We must recurse to child tables during execution, rather than using
7899 : * ALTER TABLE's normal prep-time recursion.
7900 : */
7901 : static ObjectAddress
7902 706 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7903 : bool recurse, bool recursing, LOCKMODE lockmode)
7904 : {
7905 : HeapTuple tuple;
7906 : AttrNumber attnum;
7907 : ObjectAddress address;
7908 : Constraint *constraint;
7909 : CookedConstraint *ccon;
7910 : List *cooked;
7911 706 : bool is_no_inherit = false;
7912 :
7913 : /* Guard against stack overflow due to overly deep inheritance tree. */
7914 706 : check_stack_depth();
7915 :
7916 : /* At top level, permission check was done in ATPrepCmd, else do it */
7917 706 : if (recursing)
7918 : {
7919 298 : ATSimplePermissions(AT_AddConstraint, rel,
7920 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7921 : Assert(conName != NULL);
7922 : }
7923 :
7924 706 : attnum = get_attnum(RelationGetRelid(rel), colName);
7925 706 : if (attnum == InvalidAttrNumber)
7926 18 : ereport(ERROR,
7927 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7928 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7929 : colName, RelationGetRelationName(rel))));
7930 :
7931 : /* Prevent them from altering a system attribute */
7932 688 : if (attnum <= 0)
7933 0 : ereport(ERROR,
7934 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7935 : errmsg("cannot alter system column \"%s\"",
7936 : colName)));
7937 :
7938 : /* See if there's already a constraint */
7939 688 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7940 688 : if (HeapTupleIsValid(tuple))
7941 : {
7942 158 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7943 158 : bool changed = false;
7944 :
7945 : /*
7946 : * Don't let a NO INHERIT constraint be changed into inherit.
7947 : */
7948 158 : if (conForm->connoinherit && recurse)
7949 12 : ereport(ERROR,
7950 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7951 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7952 : NameStr(conForm->conname),
7953 : RelationGetRelationName(rel)));
7954 :
7955 : /*
7956 : * If we find an appropriate constraint, we're almost done, but just
7957 : * need to change some properties on it: if we're recursing, increment
7958 : * coninhcount; if not, set conislocal if not already set.
7959 : */
7960 146 : if (recursing)
7961 : {
7962 102 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
7963 : &conForm->coninhcount))
7964 0 : ereport(ERROR,
7965 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7966 : errmsg("too many inheritance parents"));
7967 102 : changed = true;
7968 : }
7969 44 : else if (!conForm->conislocal)
7970 : {
7971 0 : conForm->conislocal = true;
7972 0 : changed = true;
7973 : }
7974 44 : else if (!conForm->convalidated)
7975 : {
7976 : /*
7977 : * Flip attnotnull and convalidated, and also validate the
7978 : * constraint.
7979 : */
7980 24 : return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
7981 : recurse, recursing, lockmode);
7982 : }
7983 :
7984 122 : if (changed)
7985 : {
7986 : Relation constr_rel;
7987 :
7988 102 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7989 :
7990 102 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7991 102 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7992 102 : table_close(constr_rel, RowExclusiveLock);
7993 : }
7994 :
7995 122 : if (changed)
7996 102 : return address;
7997 : else
7998 20 : return InvalidObjectAddress;
7999 : }
8000 :
8001 : /*
8002 : * If we're asked not to recurse, and children exist, raise an error for
8003 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
8004 : * specified.
8005 : */
8006 554 : if (!recurse &&
8007 24 : find_inheritance_children(RelationGetRelid(rel),
8008 : NoLock) != NIL)
8009 : {
8010 18 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8011 6 : ereport(ERROR,
8012 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8013 : errmsg("constraint must be added to child tables too"),
8014 : errhint("Do not specify the ONLY keyword."));
8015 : else
8016 12 : is_no_inherit = true;
8017 : }
8018 :
8019 : /*
8020 : * No constraint exists; we must add one. First determine a name to use,
8021 : * if we haven't already.
8022 : */
8023 524 : if (!recursing)
8024 : {
8025 : Assert(conName == NULL);
8026 334 : conName = ChooseConstraintName(RelationGetRelationName(rel),
8027 : colName, "not_null",
8028 334 : RelationGetNamespace(rel),
8029 : NIL);
8030 : }
8031 :
8032 524 : constraint = makeNotNullConstraint(makeString(colName));
8033 524 : constraint->is_no_inherit = is_no_inherit;
8034 524 : constraint->conname = conName;
8035 :
8036 : /* and do it */
8037 524 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8038 524 : false, !recursing, false, NULL);
8039 524 : ccon = linitial(cooked);
8040 524 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8041 :
8042 524 : InvokeObjectPostAlterHook(RelationRelationId,
8043 : RelationGetRelid(rel), attnum);
8044 :
8045 : /* Mark pg_attribute.attnotnull for the column and queue validation */
8046 524 : set_attnotnull(wqueue, rel, attnum, true, true);
8047 :
8048 : /*
8049 : * Recurse to propagate the constraint to children that don't have one.
8050 : */
8051 524 : if (recurse)
8052 : {
8053 : List *children;
8054 :
8055 506 : children = find_inheritance_children(RelationGetRelid(rel),
8056 : lockmode);
8057 :
8058 1244 : foreach_oid(childoid, children)
8059 : {
8060 244 : Relation childrel = table_open(childoid, NoLock);
8061 :
8062 244 : CommandCounterIncrement();
8063 :
8064 244 : ATExecSetNotNull(wqueue, childrel, conName, colName,
8065 : recurse, true, lockmode);
8066 238 : table_close(childrel, NoLock);
8067 : }
8068 : }
8069 :
8070 518 : return address;
8071 : }
8072 :
8073 : /*
8074 : * NotNullImpliedByRelConstraints
8075 : * Does rel's existing constraints imply NOT NULL for the given attribute?
8076 : */
8077 : static bool
8078 1238 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8079 : {
8080 1238 : NullTest *nnulltest = makeNode(NullTest);
8081 :
8082 2476 : nnulltest->arg = (Expr *) makeVar(1,
8083 1238 : attr->attnum,
8084 : attr->atttypid,
8085 : attr->atttypmod,
8086 : attr->attcollation,
8087 : 0);
8088 1238 : nnulltest->nulltesttype = IS_NOT_NULL;
8089 :
8090 : /*
8091 : * argisrow = false is correct even for a composite column, because
8092 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8093 : * case, just IS DISTINCT FROM NULL.
8094 : */
8095 1238 : nnulltest->argisrow = false;
8096 1238 : nnulltest->location = -1;
8097 :
8098 1238 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8099 : {
8100 50 : ereport(DEBUG1,
8101 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8102 : RelationGetRelationName(rel), NameStr(attr->attname))));
8103 50 : return true;
8104 : }
8105 :
8106 1188 : return false;
8107 : }
8108 :
8109 : /*
8110 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8111 : *
8112 : * Return the address of the affected column.
8113 : */
8114 : static ObjectAddress
8115 612 : ATExecColumnDefault(Relation rel, const char *colName,
8116 : Node *newDefault, LOCKMODE lockmode)
8117 : {
8118 612 : TupleDesc tupdesc = RelationGetDescr(rel);
8119 : AttrNumber attnum;
8120 : ObjectAddress address;
8121 :
8122 : /*
8123 : * get the number of the attribute
8124 : */
8125 612 : attnum = get_attnum(RelationGetRelid(rel), colName);
8126 612 : if (attnum == InvalidAttrNumber)
8127 30 : ereport(ERROR,
8128 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8129 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8130 : colName, RelationGetRelationName(rel))));
8131 :
8132 : /* Prevent them from altering a system attribute */
8133 582 : if (attnum <= 0)
8134 0 : ereport(ERROR,
8135 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8136 : errmsg("cannot alter system column \"%s\"",
8137 : colName)));
8138 :
8139 582 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8140 18 : ereport(ERROR,
8141 : (errcode(ERRCODE_SYNTAX_ERROR),
8142 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8143 : colName, RelationGetRelationName(rel)),
8144 : /* translator: %s is an SQL ALTER command */
8145 : newDefault ? 0 : errhint("Use %s instead.",
8146 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8147 :
8148 564 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8149 12 : ereport(ERROR,
8150 : (errcode(ERRCODE_SYNTAX_ERROR),
8151 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8152 : colName, RelationGetRelationName(rel)),
8153 : newDefault ?
8154 : /* translator: %s is an SQL ALTER command */
8155 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8156 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8157 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8158 :
8159 : /*
8160 : * Remove any old default for the column. We use RESTRICT here for
8161 : * safety, but at present we do not expect anything to depend on the
8162 : * default.
8163 : *
8164 : * We treat removing the existing default as an internal operation when it
8165 : * is preparatory to adding a new default, but as a user-initiated
8166 : * operation when the user asked for a drop.
8167 : */
8168 552 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8169 : newDefault != NULL);
8170 :
8171 552 : if (newDefault)
8172 : {
8173 : /* SET DEFAULT */
8174 : RawColumnDefault *rawEnt;
8175 :
8176 378 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8177 378 : rawEnt->attnum = attnum;
8178 378 : rawEnt->raw_default = newDefault;
8179 378 : rawEnt->generated = '\0';
8180 :
8181 : /*
8182 : * This function is intended for CREATE TABLE, so it processes a
8183 : * _list_ of defaults, but we just do one.
8184 : */
8185 378 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8186 : false, true, false, NULL);
8187 : }
8188 :
8189 546 : ObjectAddressSubSet(address, RelationRelationId,
8190 : RelationGetRelid(rel), attnum);
8191 546 : return address;
8192 : }
8193 :
8194 : /*
8195 : * Add a pre-cooked default expression.
8196 : *
8197 : * Return the address of the affected column.
8198 : */
8199 : static ObjectAddress
8200 80 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8201 : Node *newDefault)
8202 : {
8203 : ObjectAddress address;
8204 :
8205 : /* We assume no checking is required */
8206 :
8207 : /*
8208 : * Remove any old default for the column. We use RESTRICT here for
8209 : * safety, but at present we do not expect anything to depend on the
8210 : * default. (In ordinary cases, there could not be a default in place
8211 : * anyway, but it's possible when combining LIKE with inheritance.)
8212 : */
8213 80 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8214 : true);
8215 :
8216 80 : (void) StoreAttrDefault(rel, attnum, newDefault, true);
8217 :
8218 80 : ObjectAddressSubSet(address, RelationRelationId,
8219 : RelationGetRelid(rel), attnum);
8220 80 : return address;
8221 : }
8222 :
8223 : /*
8224 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8225 : *
8226 : * Return the address of the affected column.
8227 : */
8228 : static ObjectAddress
8229 204 : ATExecAddIdentity(Relation rel, const char *colName,
8230 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8231 : {
8232 : Relation attrelation;
8233 : HeapTuple tuple;
8234 : Form_pg_attribute attTup;
8235 : AttrNumber attnum;
8236 : ObjectAddress address;
8237 204 : ColumnDef *cdef = castNode(ColumnDef, def);
8238 : bool ispartitioned;
8239 :
8240 204 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8241 204 : if (ispartitioned && !recurse)
8242 6 : ereport(ERROR,
8243 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8244 : errmsg("cannot add identity to a column of only the partitioned table"),
8245 : errhint("Do not specify the ONLY keyword.")));
8246 :
8247 198 : if (rel->rd_rel->relispartition && !recursing)
8248 12 : ereport(ERROR,
8249 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8250 : errmsg("cannot add identity to a column of a partition"));
8251 :
8252 186 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8253 :
8254 186 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8255 186 : if (!HeapTupleIsValid(tuple))
8256 0 : ereport(ERROR,
8257 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8258 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8259 : colName, RelationGetRelationName(rel))));
8260 186 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8261 186 : attnum = attTup->attnum;
8262 :
8263 : /* Can't alter a system attribute */
8264 186 : if (attnum <= 0)
8265 0 : ereport(ERROR,
8266 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8267 : errmsg("cannot alter system column \"%s\"",
8268 : colName)));
8269 :
8270 : /*
8271 : * Creating a column as identity implies NOT NULL, so adding the identity
8272 : * to an existing column that is not NOT NULL would create a state that
8273 : * cannot be reproduced without contortions.
8274 : */
8275 186 : if (!attTup->attnotnull)
8276 6 : ereport(ERROR,
8277 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8278 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8279 : colName, RelationGetRelationName(rel))));
8280 :
8281 180 : if (attTup->attidentity)
8282 18 : ereport(ERROR,
8283 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8284 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8285 : colName, RelationGetRelationName(rel))));
8286 :
8287 162 : if (attTup->atthasdef)
8288 6 : ereport(ERROR,
8289 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8290 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8291 : colName, RelationGetRelationName(rel))));
8292 :
8293 156 : attTup->attidentity = cdef->identity;
8294 156 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8295 :
8296 156 : InvokeObjectPostAlterHook(RelationRelationId,
8297 : RelationGetRelid(rel),
8298 : attTup->attnum);
8299 156 : ObjectAddressSubSet(address, RelationRelationId,
8300 : RelationGetRelid(rel), attnum);
8301 156 : heap_freetuple(tuple);
8302 :
8303 156 : table_close(attrelation, RowExclusiveLock);
8304 :
8305 : /*
8306 : * Recurse to propagate the identity column to partitions. Identity is
8307 : * not inherited in regular inheritance children.
8308 : */
8309 156 : if (recurse && ispartitioned)
8310 : {
8311 : List *children;
8312 : ListCell *lc;
8313 :
8314 14 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8315 :
8316 20 : foreach(lc, children)
8317 : {
8318 : Relation childrel;
8319 :
8320 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8321 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8322 6 : table_close(childrel, NoLock);
8323 : }
8324 : }
8325 :
8326 156 : return address;
8327 : }
8328 :
8329 : /*
8330 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8331 : *
8332 : * Return the address of the affected column.
8333 : */
8334 : static ObjectAddress
8335 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8336 : LOCKMODE lockmode, bool recurse, bool recursing)
8337 : {
8338 : ListCell *option;
8339 74 : DefElem *generatedEl = NULL;
8340 : HeapTuple tuple;
8341 : Form_pg_attribute attTup;
8342 : AttrNumber attnum;
8343 : Relation attrelation;
8344 : ObjectAddress address;
8345 : bool ispartitioned;
8346 :
8347 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8348 74 : if (ispartitioned && !recurse)
8349 6 : ereport(ERROR,
8350 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8351 : errmsg("cannot change identity column of only the partitioned table"),
8352 : errhint("Do not specify the ONLY keyword.")));
8353 :
8354 68 : if (rel->rd_rel->relispartition && !recursing)
8355 12 : ereport(ERROR,
8356 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8357 : errmsg("cannot change identity column of a partition"));
8358 :
8359 100 : foreach(option, castNode(List, def))
8360 : {
8361 44 : DefElem *defel = lfirst_node(DefElem, option);
8362 :
8363 44 : if (strcmp(defel->defname, "generated") == 0)
8364 : {
8365 44 : if (generatedEl)
8366 0 : ereport(ERROR,
8367 : (errcode(ERRCODE_SYNTAX_ERROR),
8368 : errmsg("conflicting or redundant options")));
8369 44 : generatedEl = defel;
8370 : }
8371 : else
8372 0 : elog(ERROR, "option \"%s\" not recognized",
8373 : defel->defname);
8374 : }
8375 :
8376 : /*
8377 : * Even if there is nothing to change here, we run all the checks. There
8378 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8379 : * there.
8380 : */
8381 :
8382 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8383 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8384 56 : if (!HeapTupleIsValid(tuple))
8385 0 : ereport(ERROR,
8386 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8387 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8388 : colName, RelationGetRelationName(rel))));
8389 :
8390 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8391 56 : attnum = attTup->attnum;
8392 :
8393 56 : if (attnum <= 0)
8394 0 : ereport(ERROR,
8395 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8396 : errmsg("cannot alter system column \"%s\"",
8397 : colName)));
8398 :
8399 56 : if (!attTup->attidentity)
8400 6 : ereport(ERROR,
8401 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8402 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8403 : colName, RelationGetRelationName(rel))));
8404 :
8405 50 : if (generatedEl)
8406 : {
8407 44 : attTup->attidentity = defGetInt32(generatedEl);
8408 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8409 :
8410 44 : InvokeObjectPostAlterHook(RelationRelationId,
8411 : RelationGetRelid(rel),
8412 : attTup->attnum);
8413 44 : ObjectAddressSubSet(address, RelationRelationId,
8414 : RelationGetRelid(rel), attnum);
8415 : }
8416 : else
8417 6 : address = InvalidObjectAddress;
8418 :
8419 50 : heap_freetuple(tuple);
8420 50 : table_close(attrelation, RowExclusiveLock);
8421 :
8422 : /*
8423 : * Recurse to propagate the identity change to partitions. Identity is not
8424 : * inherited in regular inheritance children.
8425 : */
8426 50 : if (generatedEl && recurse && ispartitioned)
8427 : {
8428 : List *children;
8429 : ListCell *lc;
8430 :
8431 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8432 :
8433 18 : foreach(lc, children)
8434 : {
8435 : Relation childrel;
8436 :
8437 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8438 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8439 12 : table_close(childrel, NoLock);
8440 : }
8441 : }
8442 :
8443 50 : return address;
8444 : }
8445 :
8446 : /*
8447 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8448 : *
8449 : * Return the address of the affected column.
8450 : */
8451 : static ObjectAddress
8452 68 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8453 : bool recurse, bool recursing)
8454 : {
8455 : HeapTuple tuple;
8456 : Form_pg_attribute attTup;
8457 : AttrNumber attnum;
8458 : Relation attrelation;
8459 : ObjectAddress address;
8460 : Oid seqid;
8461 : ObjectAddress seqaddress;
8462 : bool ispartitioned;
8463 :
8464 68 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8465 68 : if (ispartitioned && !recurse)
8466 6 : ereport(ERROR,
8467 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8468 : errmsg("cannot drop identity from a column of only the partitioned table"),
8469 : errhint("Do not specify the ONLY keyword.")));
8470 :
8471 62 : if (rel->rd_rel->relispartition && !recursing)
8472 6 : ereport(ERROR,
8473 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8474 : errmsg("cannot drop identity from a column of a partition"));
8475 :
8476 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8477 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8478 56 : if (!HeapTupleIsValid(tuple))
8479 0 : ereport(ERROR,
8480 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8481 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8482 : colName, RelationGetRelationName(rel))));
8483 :
8484 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8485 56 : attnum = attTup->attnum;
8486 :
8487 56 : if (attnum <= 0)
8488 0 : ereport(ERROR,
8489 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8490 : errmsg("cannot alter system column \"%s\"",
8491 : colName)));
8492 :
8493 56 : if (!attTup->attidentity)
8494 : {
8495 12 : if (!missing_ok)
8496 6 : ereport(ERROR,
8497 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8498 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8499 : colName, RelationGetRelationName(rel))));
8500 : else
8501 : {
8502 6 : ereport(NOTICE,
8503 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8504 : colName, RelationGetRelationName(rel))));
8505 6 : heap_freetuple(tuple);
8506 6 : table_close(attrelation, RowExclusiveLock);
8507 6 : return InvalidObjectAddress;
8508 : }
8509 : }
8510 :
8511 44 : attTup->attidentity = '\0';
8512 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8513 :
8514 44 : InvokeObjectPostAlterHook(RelationRelationId,
8515 : RelationGetRelid(rel),
8516 : attTup->attnum);
8517 44 : ObjectAddressSubSet(address, RelationRelationId,
8518 : RelationGetRelid(rel), attnum);
8519 44 : heap_freetuple(tuple);
8520 :
8521 44 : table_close(attrelation, RowExclusiveLock);
8522 :
8523 : /*
8524 : * Recurse to drop the identity from column in partitions. Identity is
8525 : * not inherited in regular inheritance children so ignore them.
8526 : */
8527 44 : if (recurse && ispartitioned)
8528 : {
8529 : List *children;
8530 : ListCell *lc;
8531 :
8532 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8533 :
8534 12 : foreach(lc, children)
8535 : {
8536 : Relation childrel;
8537 :
8538 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8539 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8540 6 : table_close(childrel, NoLock);
8541 : }
8542 : }
8543 :
8544 44 : if (!recursing)
8545 : {
8546 : /* drop the internal sequence */
8547 32 : seqid = getIdentitySequence(rel, attnum, false);
8548 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8549 : RelationRelationId, DEPENDENCY_INTERNAL);
8550 32 : CommandCounterIncrement();
8551 32 : seqaddress.classId = RelationRelationId;
8552 32 : seqaddress.objectId = seqid;
8553 32 : seqaddress.objectSubId = 0;
8554 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8555 : }
8556 :
8557 44 : return address;
8558 : }
8559 :
8560 : /*
8561 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8562 : *
8563 : * Return the address of the affected column.
8564 : */
8565 : static ObjectAddress
8566 180 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8567 : Node *newExpr, LOCKMODE lockmode)
8568 : {
8569 : HeapTuple tuple;
8570 : Form_pg_attribute attTup;
8571 : AttrNumber attnum;
8572 : char attgenerated;
8573 : bool rewrite;
8574 : Oid attrdefoid;
8575 : ObjectAddress address;
8576 : Expr *defval;
8577 : NewColumnValue *newval;
8578 : RawColumnDefault *rawEnt;
8579 :
8580 180 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8581 180 : if (!HeapTupleIsValid(tuple))
8582 0 : ereport(ERROR,
8583 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8584 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8585 : colName, RelationGetRelationName(rel))));
8586 :
8587 180 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8588 :
8589 180 : attnum = attTup->attnum;
8590 180 : if (attnum <= 0)
8591 0 : ereport(ERROR,
8592 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8593 : errmsg("cannot alter system column \"%s\"",
8594 : colName)));
8595 :
8596 180 : attgenerated = attTup->attgenerated;
8597 180 : if (!attgenerated)
8598 12 : ereport(ERROR,
8599 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8600 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8601 : colName, RelationGetRelationName(rel))));
8602 :
8603 : /*
8604 : * TODO: This could be done, just need to recheck any constraints
8605 : * afterwards.
8606 : */
8607 168 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8608 90 : rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8609 12 : ereport(ERROR,
8610 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8611 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
8612 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8613 : colName, RelationGetRelationName(rel))));
8614 :
8615 156 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8616 24 : tab->verify_new_notnull = true;
8617 :
8618 : /*
8619 : * We need to prevent this because a change of expression could affect a
8620 : * row filter and inject expressions that are not permitted in a row
8621 : * filter. XXX We could try to have a more precise check to catch only
8622 : * publications with row filters, or even re-verify the row filter
8623 : * expressions.
8624 : */
8625 234 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8626 78 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
8627 6 : ereport(ERROR,
8628 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8629 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8630 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8631 : colName, RelationGetRelationName(rel))));
8632 :
8633 150 : rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8634 :
8635 150 : ReleaseSysCache(tuple);
8636 :
8637 150 : if (rewrite)
8638 : {
8639 : /*
8640 : * Clear all the missing values if we're rewriting the table, since
8641 : * this renders them pointless.
8642 : */
8643 78 : RelationClearMissing(rel);
8644 :
8645 : /* make sure we don't conflict with later attribute modifications */
8646 78 : CommandCounterIncrement();
8647 :
8648 : /*
8649 : * Find everything that depends on the column (constraints, indexes,
8650 : * etc), and record enough information to let us recreate the objects
8651 : * after rewrite.
8652 : */
8653 78 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8654 : }
8655 :
8656 : /*
8657 : * Drop the dependency records of the GENERATED expression, in particular
8658 : * its INTERNAL dependency on the column, which would otherwise cause
8659 : * dependency.c to refuse to perform the deletion.
8660 : */
8661 150 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8662 150 : if (!OidIsValid(attrdefoid))
8663 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8664 : RelationGetRelid(rel), attnum);
8665 150 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8666 :
8667 : /* Make above changes visible */
8668 150 : CommandCounterIncrement();
8669 :
8670 : /*
8671 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8672 : * safety, but at present we do not expect anything to depend on the
8673 : * expression.
8674 : */
8675 150 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8676 : false, false);
8677 :
8678 : /* Prepare to store the new expression, in the catalogs */
8679 150 : rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8680 150 : rawEnt->attnum = attnum;
8681 150 : rawEnt->raw_default = newExpr;
8682 150 : rawEnt->generated = attgenerated;
8683 :
8684 : /* Store the generated expression */
8685 150 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8686 : false, true, false, NULL);
8687 :
8688 : /* Make above new expression visible */
8689 150 : CommandCounterIncrement();
8690 :
8691 150 : if (rewrite)
8692 : {
8693 : /* Prepare for table rewrite */
8694 78 : defval = (Expr *) build_column_default(rel, attnum);
8695 :
8696 78 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8697 78 : newval->attnum = attnum;
8698 78 : newval->expr = expression_planner(defval);
8699 78 : newval->is_generated = true;
8700 :
8701 78 : tab->newvals = lappend(tab->newvals, newval);
8702 78 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8703 : }
8704 :
8705 : /* Drop any pg_statistic entry for the column */
8706 150 : RemoveStatistics(RelationGetRelid(rel), attnum);
8707 :
8708 150 : InvokeObjectPostAlterHook(RelationRelationId,
8709 : RelationGetRelid(rel), attnum);
8710 :
8711 150 : ObjectAddressSubSet(address, RelationRelationId,
8712 : RelationGetRelid(rel), attnum);
8713 150 : return address;
8714 : }
8715 :
8716 : /*
8717 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8718 : */
8719 : static void
8720 86 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8721 : {
8722 : /*
8723 : * Reject ONLY if there are child tables. We could implement this, but it
8724 : * is a bit complicated. GENERATED clauses must be attached to the column
8725 : * definition and cannot be added later like DEFAULT, so if a child table
8726 : * has a generation expression that the parent does not have, the child
8727 : * column will necessarily be an attislocal column. So to implement ONLY
8728 : * here, we'd need extra code to update attislocal of the direct child
8729 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8730 : * resulting state can be properly dumped and restored.
8731 : */
8732 110 : if (!recurse &&
8733 24 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8734 12 : ereport(ERROR,
8735 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8736 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8737 :
8738 : /*
8739 : * Cannot drop generation expression from inherited columns.
8740 : */
8741 74 : if (!recursing)
8742 : {
8743 : HeapTuple tuple;
8744 : Form_pg_attribute attTup;
8745 :
8746 62 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8747 62 : if (!HeapTupleIsValid(tuple))
8748 0 : ereport(ERROR,
8749 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8750 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8751 : cmd->name, RelationGetRelationName(rel))));
8752 :
8753 62 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8754 :
8755 62 : if (attTup->attinhcount > 0)
8756 12 : ereport(ERROR,
8757 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8758 : errmsg("cannot drop generation expression from inherited column")));
8759 : }
8760 62 : }
8761 :
8762 : /*
8763 : * Return the address of the affected column.
8764 : */
8765 : static ObjectAddress
8766 56 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8767 : {
8768 : HeapTuple tuple;
8769 : Form_pg_attribute attTup;
8770 : AttrNumber attnum;
8771 : Relation attrelation;
8772 : Oid attrdefoid;
8773 : ObjectAddress address;
8774 :
8775 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8776 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8777 56 : if (!HeapTupleIsValid(tuple))
8778 0 : ereport(ERROR,
8779 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8780 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8781 : colName, RelationGetRelationName(rel))));
8782 :
8783 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8784 56 : attnum = attTup->attnum;
8785 :
8786 56 : if (attnum <= 0)
8787 0 : ereport(ERROR,
8788 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8789 : errmsg("cannot alter system column \"%s\"",
8790 : colName)));
8791 :
8792 : /*
8793 : * TODO: This could be done, but it would need a table rewrite to
8794 : * materialize the generated values. Note that for the time being, we
8795 : * still error with missing_ok, so that we don't silently leave the column
8796 : * as generated.
8797 : */
8798 56 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8799 12 : ereport(ERROR,
8800 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8801 : errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8802 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8803 : colName, RelationGetRelationName(rel))));
8804 :
8805 44 : if (!attTup->attgenerated)
8806 : {
8807 24 : if (!missing_ok)
8808 12 : ereport(ERROR,
8809 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8810 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8811 : colName, RelationGetRelationName(rel))));
8812 : else
8813 : {
8814 12 : ereport(NOTICE,
8815 : (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8816 : colName, RelationGetRelationName(rel))));
8817 12 : heap_freetuple(tuple);
8818 12 : table_close(attrelation, RowExclusiveLock);
8819 12 : return InvalidObjectAddress;
8820 : }
8821 : }
8822 :
8823 : /*
8824 : * Mark the column as no longer generated. (The atthasdef flag needs to
8825 : * get cleared too, but RemoveAttrDefault will handle that.)
8826 : */
8827 20 : attTup->attgenerated = '\0';
8828 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8829 :
8830 20 : InvokeObjectPostAlterHook(RelationRelationId,
8831 : RelationGetRelid(rel),
8832 : attnum);
8833 20 : heap_freetuple(tuple);
8834 :
8835 20 : table_close(attrelation, RowExclusiveLock);
8836 :
8837 : /*
8838 : * Drop the dependency records of the GENERATED expression, in particular
8839 : * its INTERNAL dependency on the column, which would otherwise cause
8840 : * dependency.c to refuse to perform the deletion.
8841 : */
8842 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8843 20 : if (!OidIsValid(attrdefoid))
8844 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8845 : RelationGetRelid(rel), attnum);
8846 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8847 :
8848 : /* Make above changes visible */
8849 20 : CommandCounterIncrement();
8850 :
8851 : /*
8852 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8853 : * safety, but at present we do not expect anything to depend on the
8854 : * default.
8855 : */
8856 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8857 : false, false);
8858 :
8859 20 : ObjectAddressSubSet(address, RelationRelationId,
8860 : RelationGetRelid(rel), attnum);
8861 20 : return address;
8862 : }
8863 :
8864 : /*
8865 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8866 : *
8867 : * Return value is the address of the modified column
8868 : */
8869 : static ObjectAddress
8870 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8871 : {
8872 164 : int newtarget = 0;
8873 : bool newtarget_default;
8874 : Relation attrelation;
8875 : HeapTuple tuple,
8876 : newtuple;
8877 : Form_pg_attribute attrtuple;
8878 : AttrNumber attnum;
8879 : ObjectAddress address;
8880 : Datum repl_val[Natts_pg_attribute];
8881 : bool repl_null[Natts_pg_attribute];
8882 : bool repl_repl[Natts_pg_attribute];
8883 :
8884 : /*
8885 : * We allow referencing columns by numbers only for indexes, since table
8886 : * column numbers could contain gaps if columns are later dropped.
8887 : */
8888 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8889 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8890 : !colName)
8891 0 : ereport(ERROR,
8892 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8893 : errmsg("cannot refer to non-index column by number")));
8894 :
8895 : /* -1 was used in previous versions for the default setting */
8896 164 : if (newValue && intVal(newValue) != -1)
8897 : {
8898 120 : newtarget = intVal(newValue);
8899 120 : newtarget_default = false;
8900 : }
8901 : else
8902 44 : newtarget_default = true;
8903 :
8904 164 : if (!newtarget_default)
8905 : {
8906 : /*
8907 : * Limit target to a sane range
8908 : */
8909 120 : if (newtarget < 0)
8910 : {
8911 0 : ereport(ERROR,
8912 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8913 : errmsg("statistics target %d is too low",
8914 : newtarget)));
8915 : }
8916 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8917 : {
8918 0 : newtarget = MAX_STATISTICS_TARGET;
8919 0 : ereport(WARNING,
8920 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8921 : errmsg("lowering statistics target to %d",
8922 : newtarget)));
8923 : }
8924 : }
8925 :
8926 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8927 :
8928 164 : if (colName)
8929 : {
8930 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8931 :
8932 100 : if (!HeapTupleIsValid(tuple))
8933 12 : ereport(ERROR,
8934 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8935 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8936 : colName, RelationGetRelationName(rel))));
8937 : }
8938 : else
8939 : {
8940 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8941 :
8942 64 : if (!HeapTupleIsValid(tuple))
8943 12 : ereport(ERROR,
8944 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8945 : errmsg("column number %d of relation \"%s\" does not exist",
8946 : colNum, RelationGetRelationName(rel))));
8947 : }
8948 :
8949 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8950 :
8951 140 : attnum = attrtuple->attnum;
8952 140 : if (attnum <= 0)
8953 0 : ereport(ERROR,
8954 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8955 : errmsg("cannot alter system column \"%s\"",
8956 : colName)));
8957 :
8958 : /*
8959 : * Prevent this as long as the ANALYZE code skips virtual generated
8960 : * columns.
8961 : */
8962 140 : if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8963 0 : ereport(ERROR,
8964 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8965 : errmsg("cannot alter statistics on virtual generated column \"%s\"",
8966 : colName)));
8967 :
8968 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
8969 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8970 : {
8971 52 : if (attnum > rel->rd_index->indnkeyatts)
8972 6 : ereport(ERROR,
8973 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8974 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8975 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8976 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8977 18 : ereport(ERROR,
8978 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8979 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8980 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8981 : errhint("Alter statistics on table column instead.")));
8982 : }
8983 :
8984 : /* Build new tuple. */
8985 116 : memset(repl_null, false, sizeof(repl_null));
8986 116 : memset(repl_repl, false, sizeof(repl_repl));
8987 116 : if (!newtarget_default)
8988 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8989 : else
8990 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8991 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8992 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8993 : repl_val, repl_null, repl_repl);
8994 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8995 :
8996 116 : InvokeObjectPostAlterHook(RelationRelationId,
8997 : RelationGetRelid(rel),
8998 : attrtuple->attnum);
8999 116 : ObjectAddressSubSet(address, RelationRelationId,
9000 : RelationGetRelid(rel), attnum);
9001 :
9002 116 : heap_freetuple(newtuple);
9003 :
9004 116 : ReleaseSysCache(tuple);
9005 :
9006 116 : table_close(attrelation, RowExclusiveLock);
9007 :
9008 116 : return address;
9009 : }
9010 :
9011 : /*
9012 : * Return value is the address of the modified column
9013 : */
9014 : static ObjectAddress
9015 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
9016 : bool isReset, LOCKMODE lockmode)
9017 : {
9018 : Relation attrelation;
9019 : HeapTuple tuple,
9020 : newtuple;
9021 : Form_pg_attribute attrtuple;
9022 : AttrNumber attnum;
9023 : Datum datum,
9024 : newOptions;
9025 : bool isnull;
9026 : ObjectAddress address;
9027 : Datum repl_val[Natts_pg_attribute];
9028 : bool repl_null[Natts_pg_attribute];
9029 : bool repl_repl[Natts_pg_attribute];
9030 :
9031 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9032 :
9033 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9034 :
9035 32 : if (!HeapTupleIsValid(tuple))
9036 0 : ereport(ERROR,
9037 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9038 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9039 : colName, RelationGetRelationName(rel))));
9040 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9041 :
9042 32 : attnum = attrtuple->attnum;
9043 32 : if (attnum <= 0)
9044 0 : ereport(ERROR,
9045 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9046 : errmsg("cannot alter system column \"%s\"",
9047 : colName)));
9048 :
9049 : /* Generate new proposed attoptions (text array) */
9050 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9051 : &isnull);
9052 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9053 : castNode(List, options), NULL, NULL,
9054 : false, isReset);
9055 : /* Validate new options */
9056 32 : (void) attribute_reloptions(newOptions, true);
9057 :
9058 : /* Build new tuple. */
9059 32 : memset(repl_null, false, sizeof(repl_null));
9060 32 : memset(repl_repl, false, sizeof(repl_repl));
9061 32 : if (newOptions != (Datum) 0)
9062 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9063 : else
9064 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
9065 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9066 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9067 : repl_val, repl_null, repl_repl);
9068 :
9069 : /* Update system catalog. */
9070 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9071 :
9072 32 : InvokeObjectPostAlterHook(RelationRelationId,
9073 : RelationGetRelid(rel),
9074 : attrtuple->attnum);
9075 32 : ObjectAddressSubSet(address, RelationRelationId,
9076 : RelationGetRelid(rel), attnum);
9077 :
9078 32 : heap_freetuple(newtuple);
9079 :
9080 32 : ReleaseSysCache(tuple);
9081 :
9082 32 : table_close(attrelation, RowExclusiveLock);
9083 :
9084 32 : return address;
9085 : }
9086 :
9087 : /*
9088 : * Helper function for ATExecSetStorage and ATExecSetCompression
9089 : *
9090 : * Set the attstorage and/or attcompression fields for index columns
9091 : * associated with the specified table column.
9092 : */
9093 : static void
9094 318 : SetIndexStorageProperties(Relation rel, Relation attrelation,
9095 : AttrNumber attnum,
9096 : bool setstorage, char newstorage,
9097 : bool setcompression, char newcompression,
9098 : LOCKMODE lockmode)
9099 : {
9100 : ListCell *lc;
9101 :
9102 402 : foreach(lc, RelationGetIndexList(rel))
9103 : {
9104 84 : Oid indexoid = lfirst_oid(lc);
9105 : Relation indrel;
9106 84 : AttrNumber indattnum = 0;
9107 : HeapTuple tuple;
9108 :
9109 84 : indrel = index_open(indexoid, lockmode);
9110 :
9111 144 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9112 : {
9113 90 : if (indrel->rd_index->indkey.values[i] == attnum)
9114 : {
9115 30 : indattnum = i + 1;
9116 30 : break;
9117 : }
9118 : }
9119 :
9120 84 : if (indattnum == 0)
9121 : {
9122 54 : index_close(indrel, lockmode);
9123 54 : continue;
9124 : }
9125 :
9126 30 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9127 :
9128 30 : if (HeapTupleIsValid(tuple))
9129 : {
9130 30 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9131 :
9132 30 : if (setstorage)
9133 24 : attrtuple->attstorage = newstorage;
9134 :
9135 30 : if (setcompression)
9136 6 : attrtuple->attcompression = newcompression;
9137 :
9138 30 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9139 :
9140 30 : InvokeObjectPostAlterHook(RelationRelationId,
9141 : RelationGetRelid(rel),
9142 : attrtuple->attnum);
9143 :
9144 30 : heap_freetuple(tuple);
9145 : }
9146 :
9147 30 : index_close(indrel, lockmode);
9148 : }
9149 318 : }
9150 :
9151 : /*
9152 : * ALTER TABLE ALTER COLUMN SET STORAGE
9153 : *
9154 : * Return value is the address of the modified column
9155 : */
9156 : static ObjectAddress
9157 246 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9158 : {
9159 : Relation attrelation;
9160 : HeapTuple tuple;
9161 : Form_pg_attribute attrtuple;
9162 : AttrNumber attnum;
9163 : ObjectAddress address;
9164 :
9165 246 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9166 :
9167 246 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9168 :
9169 246 : if (!HeapTupleIsValid(tuple))
9170 12 : ereport(ERROR,
9171 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9172 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9173 : colName, RelationGetRelationName(rel))));
9174 234 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9175 :
9176 234 : attnum = attrtuple->attnum;
9177 234 : if (attnum <= 0)
9178 0 : ereport(ERROR,
9179 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9180 : errmsg("cannot alter system column \"%s\"",
9181 : colName)));
9182 :
9183 234 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9184 :
9185 234 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9186 :
9187 234 : InvokeObjectPostAlterHook(RelationRelationId,
9188 : RelationGetRelid(rel),
9189 : attrtuple->attnum);
9190 :
9191 : /*
9192 : * Apply the change to indexes as well (only for simple index columns,
9193 : * matching behavior of index.c ConstructTupleDescriptor()).
9194 : */
9195 234 : SetIndexStorageProperties(rel, attrelation, attnum,
9196 234 : true, attrtuple->attstorage,
9197 : false, 0,
9198 : lockmode);
9199 :
9200 234 : heap_freetuple(tuple);
9201 :
9202 234 : table_close(attrelation, RowExclusiveLock);
9203 :
9204 234 : ObjectAddressSubSet(address, RelationRelationId,
9205 : RelationGetRelid(rel), attnum);
9206 234 : return address;
9207 : }
9208 :
9209 :
9210 : /*
9211 : * ALTER TABLE DROP COLUMN
9212 : *
9213 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9214 : * because we have to decide at runtime whether to recurse or not depending
9215 : * on whether attinhcount goes to zero or not. (We can't check this in a
9216 : * static pre-pass because it won't handle multiple inheritance situations
9217 : * correctly.)
9218 : */
9219 : static void
9220 1652 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9221 : AlterTableCmd *cmd, LOCKMODE lockmode,
9222 : AlterTableUtilityContext *context)
9223 : {
9224 1652 : if (rel->rd_rel->reloftype && !recursing)
9225 6 : ereport(ERROR,
9226 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9227 : errmsg("cannot drop column from typed table")));
9228 :
9229 1646 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9230 84 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9231 :
9232 1640 : if (recurse)
9233 1360 : cmd->recurse = true;
9234 1640 : }
9235 :
9236 : /*
9237 : * Drops column 'colName' from relation 'rel' and returns the address of the
9238 : * dropped column. The column is also dropped (or marked as no longer
9239 : * inherited from relation) from the relation's inheritance children, if any.
9240 : *
9241 : * In the recursive invocations for inheritance child relations, instead of
9242 : * dropping the column directly (if to be dropped at all), its object address
9243 : * is added to 'addrs', which must be non-NULL in such invocations. All
9244 : * columns are dropped at the same time after all the children have been
9245 : * checked recursively.
9246 : */
9247 : static ObjectAddress
9248 2196 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9249 : DropBehavior behavior,
9250 : bool recurse, bool recursing,
9251 : bool missing_ok, LOCKMODE lockmode,
9252 : ObjectAddresses *addrs)
9253 : {
9254 : HeapTuple tuple;
9255 : Form_pg_attribute targetatt;
9256 : AttrNumber attnum;
9257 : List *children;
9258 : ObjectAddress object;
9259 : bool is_expr;
9260 :
9261 : /* At top level, permission check was done in ATPrepCmd, else do it */
9262 2196 : if (recursing)
9263 556 : ATSimplePermissions(AT_DropColumn, rel,
9264 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9265 :
9266 : /* Initialize addrs on the first invocation */
9267 : Assert(!recursing || addrs != NULL);
9268 :
9269 : /* since this function recurses, it could be driven to stack overflow */
9270 2196 : check_stack_depth();
9271 :
9272 2196 : if (!recursing)
9273 1640 : addrs = new_object_addresses();
9274 :
9275 : /*
9276 : * get the number of the attribute
9277 : */
9278 2196 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9279 2196 : if (!HeapTupleIsValid(tuple))
9280 : {
9281 54 : if (!missing_ok)
9282 : {
9283 36 : ereport(ERROR,
9284 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9285 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9286 : colName, RelationGetRelationName(rel))));
9287 : }
9288 : else
9289 : {
9290 18 : ereport(NOTICE,
9291 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9292 : colName, RelationGetRelationName(rel))));
9293 18 : return InvalidObjectAddress;
9294 : }
9295 : }
9296 2142 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9297 :
9298 2142 : attnum = targetatt->attnum;
9299 :
9300 : /* Can't drop a system attribute */
9301 2142 : if (attnum <= 0)
9302 6 : ereport(ERROR,
9303 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9304 : errmsg("cannot drop system column \"%s\"",
9305 : colName)));
9306 :
9307 : /*
9308 : * Don't drop inherited columns, unless recursing (presumably from a drop
9309 : * of the parent column)
9310 : */
9311 2136 : if (targetatt->attinhcount > 0 && !recursing)
9312 48 : ereport(ERROR,
9313 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9314 : errmsg("cannot drop inherited column \"%s\"",
9315 : colName)));
9316 :
9317 : /*
9318 : * Don't drop columns used in the partition key, either. (If we let this
9319 : * go through, the key column's dependencies would cause a cascaded drop
9320 : * of the whole table, which is surely not what the user expected.)
9321 : */
9322 2088 : if (has_partition_attrs(rel,
9323 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9324 : &is_expr))
9325 30 : ereport(ERROR,
9326 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9327 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9328 : colName, RelationGetRelationName(rel))));
9329 :
9330 2058 : ReleaseSysCache(tuple);
9331 :
9332 : /*
9333 : * Propagate to children as appropriate. Unlike most other ALTER
9334 : * routines, we have to do this one level of recursion at a time; we can't
9335 : * use find_all_inheritors to do it in one pass.
9336 : */
9337 : children =
9338 2058 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9339 :
9340 2058 : if (children)
9341 : {
9342 : Relation attr_rel;
9343 : ListCell *child;
9344 :
9345 : /*
9346 : * In case of a partitioned table, the column must be dropped from the
9347 : * partitions as well.
9348 : */
9349 302 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9350 6 : ereport(ERROR,
9351 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9352 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9353 : errhint("Do not specify the ONLY keyword.")));
9354 :
9355 296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9356 882 : foreach(child, children)
9357 : {
9358 592 : Oid childrelid = lfirst_oid(child);
9359 : Relation childrel;
9360 : Form_pg_attribute childatt;
9361 :
9362 : /* find_inheritance_children already got lock */
9363 592 : childrel = table_open(childrelid, NoLock);
9364 592 : CheckAlterTableIsSafe(childrel);
9365 :
9366 592 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9367 592 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9368 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9369 : colName, childrelid);
9370 592 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9371 :
9372 592 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9373 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9374 : childrelid, colName);
9375 :
9376 592 : if (recurse)
9377 : {
9378 : /*
9379 : * If the child column has other definition sources, just
9380 : * decrement its inheritance count; if not, recurse to delete
9381 : * it.
9382 : */
9383 568 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9384 : {
9385 : /* Time to delete this child column, too */
9386 556 : ATExecDropColumn(wqueue, childrel, colName,
9387 : behavior, true, true,
9388 : false, lockmode, addrs);
9389 : }
9390 : else
9391 : {
9392 : /* Child column must survive my deletion */
9393 12 : childatt->attinhcount--;
9394 :
9395 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9396 :
9397 : /* Make update visible */
9398 12 : CommandCounterIncrement();
9399 : }
9400 : }
9401 : else
9402 : {
9403 : /*
9404 : * If we were told to drop ONLY in this table (no recursion),
9405 : * we need to mark the inheritors' attributes as locally
9406 : * defined rather than inherited.
9407 : */
9408 24 : childatt->attinhcount--;
9409 24 : childatt->attislocal = true;
9410 :
9411 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9412 :
9413 : /* Make update visible */
9414 24 : CommandCounterIncrement();
9415 : }
9416 :
9417 586 : heap_freetuple(tuple);
9418 :
9419 586 : table_close(childrel, NoLock);
9420 : }
9421 290 : table_close(attr_rel, RowExclusiveLock);
9422 : }
9423 :
9424 : /* Add object to delete */
9425 2046 : object.classId = RelationRelationId;
9426 2046 : object.objectId = RelationGetRelid(rel);
9427 2046 : object.objectSubId = attnum;
9428 2046 : add_exact_object_address(&object, addrs);
9429 :
9430 2046 : if (!recursing)
9431 : {
9432 : /* Recursion has ended, drop everything that was collected */
9433 1496 : performMultipleDeletions(addrs, behavior, 0);
9434 1442 : free_object_addresses(addrs);
9435 : }
9436 :
9437 1992 : return object;
9438 : }
9439 :
9440 : /*
9441 : * Prepare to add a primary key on a table, by adding not-null constraints
9442 : * on all columns.
9443 : *
9444 : * The not-null constraints for a primary key must cover the whole inheritance
9445 : * hierarchy (failing to ensure that leads to funny corner cases). For the
9446 : * normal case where we're asked to recurse, this routine checks if the
9447 : * not-null constraints exist already, and if not queues a requirement for
9448 : * them to be created by phase 2.
9449 : *
9450 : * For the case where we're asked not to recurse, we verify that a not-null
9451 : * constraint exists on each column of each (direct) child table, throwing an
9452 : * error if not. Not throwing an error would also work, because a not-null
9453 : * constraint would be created anyway, but it'd cause a silent scan of the
9454 : * child table to verify absence of nulls. We prefer to let the user know so
9455 : * that they can add the constraint manually without having to hold
9456 : * AccessExclusiveLock while at it.
9457 : *
9458 : * However, it's also important that we do not acquire locks on children if
9459 : * the not-null constraints already exist on the parent, to avoid risking
9460 : * deadlocks during parallel pg_restore of PKs on partitioned tables.
9461 : */
9462 : static void
9463 16310 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9464 : bool recurse, LOCKMODE lockmode,
9465 : AlterTableUtilityContext *context)
9466 : {
9467 : Constraint *pkconstr;
9468 16310 : List *children = NIL;
9469 16310 : bool got_children = false;
9470 :
9471 16310 : pkconstr = castNode(Constraint, cmd->def);
9472 16310 : if (pkconstr->contype != CONSTR_PRIMARY)
9473 9208 : return;
9474 :
9475 : /* Verify that columns are not-null, or request that they be made so */
9476 15468 : foreach_node(String, column, pkconstr->keys)
9477 : {
9478 : AlterTableCmd *newcmd;
9479 : Constraint *nnconstr;
9480 : HeapTuple tuple;
9481 :
9482 : /*
9483 : * First check if a suitable constraint exists. If it does, we don't
9484 : * need to request another one. We do need to bail out if it's not
9485 : * valid, though.
9486 : */
9487 1324 : tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9488 1324 : if (tuple != NULL)
9489 : {
9490 814 : verifyNotNullPKCompatible(tuple, strVal(column));
9491 :
9492 : /* All good with this one; don't request another */
9493 802 : heap_freetuple(tuple);
9494 802 : continue;
9495 : }
9496 510 : else if (!recurse)
9497 : {
9498 : /*
9499 : * No constraint on this column. Asked not to recurse, we won't
9500 : * create one here, but verify that all children have one.
9501 : */
9502 36 : if (!got_children)
9503 : {
9504 36 : children = find_inheritance_children(RelationGetRelid(rel),
9505 : lockmode);
9506 : /* only search for children on the first time through */
9507 36 : got_children = true;
9508 : }
9509 :
9510 72 : foreach_oid(childrelid, children)
9511 : {
9512 : HeapTuple tup;
9513 :
9514 36 : tup = findNotNullConstraint(childrelid, strVal(column));
9515 36 : if (!tup)
9516 6 : ereport(ERROR,
9517 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9518 : strVal(column), get_rel_name(childrelid)));
9519 : /* verify it's good enough */
9520 30 : verifyNotNullPKCompatible(tup, strVal(column));
9521 : }
9522 : }
9523 :
9524 : /* This column is not already not-null, so add it to the queue */
9525 492 : nnconstr = makeNotNullConstraint(column);
9526 :
9527 492 : newcmd = makeNode(AlterTableCmd);
9528 492 : newcmd->subtype = AT_AddConstraint;
9529 : /* note we force recurse=true here; see above */
9530 492 : newcmd->recurse = true;
9531 492 : newcmd->def = (Node *) nnconstr;
9532 :
9533 492 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9534 : }
9535 : }
9536 :
9537 : /*
9538 : * Verify whether the given not-null constraint is compatible with a
9539 : * primary key. If not, an error is thrown.
9540 : */
9541 : static void
9542 844 : verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9543 : {
9544 844 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
9545 :
9546 844 : if (conForm->contype != CONSTRAINT_NOTNULL)
9547 0 : elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9548 :
9549 : /* a NO INHERIT constraint is no good */
9550 844 : if (conForm->connoinherit)
9551 12 : ereport(ERROR,
9552 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9553 : errmsg("cannot create primary key on column \"%s\"", colname),
9554 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9555 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9556 : NameStr(conForm->conname), colname,
9557 : get_rel_name(conForm->conrelid), "NO INHERIT"),
9558 : errhint("You might need to make the existing constraint inheritable using %s.",
9559 : "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9560 :
9561 : /* an unvalidated constraint is no good */
9562 832 : if (!conForm->convalidated)
9563 12 : ereport(ERROR,
9564 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9565 : errmsg("cannot create primary key on column \"%s\"", colname),
9566 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9567 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9568 : NameStr(conForm->conname), colname,
9569 : get_rel_name(conForm->conrelid), "NOT VALID"),
9570 : errhint("You might need to validate it using %s.",
9571 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
9572 820 : }
9573 :
9574 : /*
9575 : * ALTER TABLE ADD INDEX
9576 : *
9577 : * There is no such command in the grammar, but parse_utilcmd.c converts
9578 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9579 : * us schedule creation of the index at the appropriate time during ALTER.
9580 : *
9581 : * Return value is the address of the new index.
9582 : */
9583 : static ObjectAddress
9584 1888 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9585 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9586 : {
9587 : bool check_rights;
9588 : bool skip_build;
9589 : bool quiet;
9590 : ObjectAddress address;
9591 :
9592 : Assert(IsA(stmt, IndexStmt));
9593 : Assert(!stmt->concurrent);
9594 :
9595 : /* The IndexStmt has already been through transformIndexStmt */
9596 : Assert(stmt->transformed);
9597 :
9598 : /* suppress schema rights check when rebuilding existing index */
9599 1888 : check_rights = !is_rebuild;
9600 : /* skip index build if phase 3 will do it or we're reusing an old one */
9601 1888 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9602 : /* suppress notices when rebuilding existing index */
9603 1888 : quiet = is_rebuild;
9604 :
9605 1888 : address = DefineIndex(RelationGetRelid(rel),
9606 : stmt,
9607 : InvalidOid, /* no predefined OID */
9608 : InvalidOid, /* no parent index */
9609 : InvalidOid, /* no parent constraint */
9610 : -1, /* total_parts unknown */
9611 : true, /* is_alter_table */
9612 : check_rights,
9613 : false, /* check_not_in_use - we did it already */
9614 : skip_build,
9615 : quiet);
9616 :
9617 : /*
9618 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9619 : * new index instead of building from scratch. Restore associated fields.
9620 : * This may store InvalidSubTransactionId in both fields, in which case
9621 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9622 : * this after the CCI that made catalog rows visible to any rebuild. The
9623 : * DROP of the old edition of this index will have scheduled the storage
9624 : * for deletion at commit, so cancel that pending deletion.
9625 : */
9626 1718 : if (RelFileNumberIsValid(stmt->oldNumber))
9627 : {
9628 74 : Relation irel = index_open(address.objectId, NoLock);
9629 :
9630 74 : irel->rd_createSubid = stmt->oldCreateSubid;
9631 74 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9632 74 : RelationPreserveStorage(irel->rd_locator, true);
9633 74 : index_close(irel, NoLock);
9634 : }
9635 :
9636 1718 : return address;
9637 : }
9638 :
9639 : /*
9640 : * ALTER TABLE ADD STATISTICS
9641 : *
9642 : * This is no such command in the grammar, but we use this internally to add
9643 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9644 : * column type change.
9645 : */
9646 : static ObjectAddress
9647 14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9648 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9649 : {
9650 : ObjectAddress address;
9651 :
9652 : Assert(IsA(stmt, CreateStatsStmt));
9653 :
9654 : /* The CreateStatsStmt has already been through transformStatsStmt */
9655 : Assert(stmt->transformed);
9656 :
9657 14 : address = CreateStatistics(stmt);
9658 :
9659 14 : return address;
9660 : }
9661 :
9662 : /*
9663 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9664 : *
9665 : * Returns the address of the new constraint.
9666 : */
9667 : static ObjectAddress
9668 10640 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9669 : IndexStmt *stmt, LOCKMODE lockmode)
9670 : {
9671 10640 : Oid index_oid = stmt->indexOid;
9672 : Relation indexRel;
9673 : char *indexName;
9674 : IndexInfo *indexInfo;
9675 : char *constraintName;
9676 : char constraintType;
9677 : ObjectAddress address;
9678 : bits16 flags;
9679 :
9680 : Assert(IsA(stmt, IndexStmt));
9681 : Assert(OidIsValid(index_oid));
9682 : Assert(stmt->isconstraint);
9683 :
9684 : /*
9685 : * Doing this on partitioned tables is not a simple feature to implement,
9686 : * so let's punt for now.
9687 : */
9688 10640 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9689 6 : ereport(ERROR,
9690 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9691 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9692 :
9693 10634 : indexRel = index_open(index_oid, AccessShareLock);
9694 :
9695 10634 : indexName = pstrdup(RelationGetRelationName(indexRel));
9696 :
9697 10634 : indexInfo = BuildIndexInfo(indexRel);
9698 :
9699 : /* this should have been checked at parse time */
9700 10634 : if (!indexInfo->ii_Unique)
9701 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9702 :
9703 : /*
9704 : * Determine name to assign to constraint. We require a constraint to
9705 : * have the same name as the underlying index; therefore, use the index's
9706 : * existing name as the default constraint name, and if the user
9707 : * explicitly gives some other name for the constraint, rename the index
9708 : * to match.
9709 : */
9710 10634 : constraintName = stmt->idxname;
9711 10634 : if (constraintName == NULL)
9712 10608 : constraintName = indexName;
9713 26 : else if (strcmp(constraintName, indexName) != 0)
9714 : {
9715 20 : ereport(NOTICE,
9716 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9717 : indexName, constraintName)));
9718 20 : RenameRelationInternal(index_oid, constraintName, false, true);
9719 : }
9720 :
9721 : /* Extra checks needed if making primary key */
9722 10634 : if (stmt->primary)
9723 6006 : index_check_primary_key(rel, indexInfo, true, stmt);
9724 :
9725 : /* Note we currently don't support EXCLUSION constraints here */
9726 10628 : if (stmt->primary)
9727 6000 : constraintType = CONSTRAINT_PRIMARY;
9728 : else
9729 4628 : constraintType = CONSTRAINT_UNIQUE;
9730 :
9731 : /* Create the catalog entries for the constraint */
9732 10628 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9733 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9734 21256 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9735 10628 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9736 10628 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9737 :
9738 10628 : address = index_constraint_create(rel,
9739 : index_oid,
9740 : InvalidOid,
9741 : indexInfo,
9742 : constraintName,
9743 : constraintType,
9744 : flags,
9745 : allowSystemTableMods,
9746 : false); /* is_internal */
9747 :
9748 10628 : index_close(indexRel, NoLock);
9749 :
9750 10628 : return address;
9751 : }
9752 :
9753 : /*
9754 : * ALTER TABLE ADD CONSTRAINT
9755 : *
9756 : * Return value is the address of the new constraint; if no constraint was
9757 : * added, InvalidObjectAddress is returned.
9758 : */
9759 : static ObjectAddress
9760 12808 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9761 : Constraint *newConstraint, bool recurse, bool is_readd,
9762 : LOCKMODE lockmode)
9763 : {
9764 12808 : ObjectAddress address = InvalidObjectAddress;
9765 :
9766 : Assert(IsA(newConstraint, Constraint));
9767 :
9768 : /*
9769 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9770 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9771 : * parse_utilcmd.c).
9772 : */
9773 12808 : switch (newConstraint->contype)
9774 : {
9775 10158 : case CONSTR_CHECK:
9776 : case CONSTR_NOTNULL:
9777 : address =
9778 10158 : ATAddCheckNNConstraint(wqueue, tab, rel,
9779 : newConstraint, recurse, false, is_readd,
9780 : lockmode);
9781 10014 : break;
9782 :
9783 2650 : case CONSTR_FOREIGN:
9784 :
9785 : /*
9786 : * Assign or validate constraint name
9787 : */
9788 2650 : if (newConstraint->conname)
9789 : {
9790 1238 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9791 : RelationGetRelid(rel),
9792 1238 : newConstraint->conname))
9793 0 : ereport(ERROR,
9794 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9795 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9796 : newConstraint->conname,
9797 : RelationGetRelationName(rel))));
9798 : }
9799 : else
9800 1412 : newConstraint->conname =
9801 1412 : ChooseConstraintName(RelationGetRelationName(rel),
9802 1412 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9803 : "fkey",
9804 1412 : RelationGetNamespace(rel),
9805 : NIL);
9806 :
9807 2650 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9808 : newConstraint,
9809 : recurse, false,
9810 : lockmode);
9811 2102 : break;
9812 :
9813 0 : default:
9814 0 : elog(ERROR, "unrecognized constraint type: %d",
9815 : (int) newConstraint->contype);
9816 : }
9817 :
9818 12116 : return address;
9819 : }
9820 :
9821 : /*
9822 : * Generate the column-name portion of the constraint name for a new foreign
9823 : * key given the list of column names that reference the referenced
9824 : * table. This will be passed to ChooseConstraintName along with the parent
9825 : * table name and the "fkey" suffix.
9826 : *
9827 : * We know that less than NAMEDATALEN characters will actually be used, so we
9828 : * can truncate the result once we've generated that many.
9829 : *
9830 : * XXX see also ChooseExtendedStatisticNameAddition and
9831 : * ChooseIndexNameAddition.
9832 : */
9833 : static char *
9834 1412 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9835 : {
9836 : char buf[NAMEDATALEN * 2];
9837 1412 : int buflen = 0;
9838 : ListCell *lc;
9839 :
9840 1412 : buf[0] = '\0';
9841 3216 : foreach(lc, colnames)
9842 : {
9843 1804 : const char *name = strVal(lfirst(lc));
9844 :
9845 1804 : if (buflen > 0)
9846 392 : buf[buflen++] = '_'; /* insert _ between names */
9847 :
9848 : /*
9849 : * At this point we have buflen <= NAMEDATALEN. name should be less
9850 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9851 : */
9852 1804 : strlcpy(buf + buflen, name, NAMEDATALEN);
9853 1804 : buflen += strlen(buf + buflen);
9854 1804 : if (buflen >= NAMEDATALEN)
9855 0 : break;
9856 : }
9857 1412 : return pstrdup(buf);
9858 : }
9859 :
9860 : /*
9861 : * Add a check or not-null constraint to a single table and its children.
9862 : * Returns the address of the constraint added to the parent relation,
9863 : * if one gets added, or InvalidObjectAddress otherwise.
9864 : *
9865 : * Subroutine for ATExecAddConstraint.
9866 : *
9867 : * We must recurse to child tables during execution, rather than using
9868 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9869 : * constraints *must* be given the same name, else they won't be seen as
9870 : * related later. If the user didn't explicitly specify a name, then
9871 : * AddRelationNewConstraints would normally assign different names to the
9872 : * child constraints. To fix that, we must capture the name assigned at
9873 : * the parent table and pass that down.
9874 : */
9875 : static ObjectAddress
9876 11110 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9877 : Constraint *constr, bool recurse, bool recursing,
9878 : bool is_readd, LOCKMODE lockmode)
9879 : {
9880 : List *newcons;
9881 : ListCell *lcon;
9882 : List *children;
9883 : ListCell *child;
9884 11110 : ObjectAddress address = InvalidObjectAddress;
9885 :
9886 : /* Guard against stack overflow due to overly deep inheritance tree. */
9887 11110 : check_stack_depth();
9888 :
9889 : /* At top level, permission check was done in ATPrepCmd, else do it */
9890 11110 : if (recursing)
9891 818 : ATSimplePermissions(AT_AddConstraint, rel,
9892 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9893 :
9894 : /*
9895 : * Call AddRelationNewConstraints to do the work, making sure it works on
9896 : * a copy of the Constraint so transformExpr can't modify the original. It
9897 : * returns a list of cooked constraints.
9898 : *
9899 : * If the constraint ends up getting merged with a pre-existing one, it's
9900 : * omitted from the returned list, which is what we want: we do not need
9901 : * to do any validation work. That can only happen at child tables,
9902 : * though, since we disallow merging at the top level.
9903 : */
9904 11110 : newcons = AddRelationNewConstraints(rel, NIL,
9905 11110 : list_make1(copyObject(constr)),
9906 11110 : recursing || is_readd, /* allow_merge */
9907 11110 : !recursing, /* is_local */
9908 : is_readd, /* is_internal */
9909 11110 : NULL); /* queryString not available
9910 : * here */
9911 :
9912 : /* we don't expect more than one constraint here */
9913 : Assert(list_length(newcons) <= 1);
9914 :
9915 : /* Add each to-be-validated constraint to Phase 3's queue */
9916 21734 : foreach(lcon, newcons)
9917 : {
9918 10762 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9919 :
9920 10762 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9921 : {
9922 : NewConstraint *newcon;
9923 :
9924 894 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9925 894 : newcon->name = ccon->name;
9926 894 : newcon->contype = ccon->contype;
9927 894 : newcon->qual = ccon->expr;
9928 :
9929 894 : tab->constraints = lappend(tab->constraints, newcon);
9930 : }
9931 :
9932 : /* Save the actually assigned name if it was defaulted */
9933 10762 : if (constr->conname == NULL)
9934 9032 : constr->conname = ccon->name;
9935 :
9936 : /*
9937 : * If adding a valid not-null constraint, set the pg_attribute flag
9938 : * and tell phase 3 to verify existing rows, if needed. For an
9939 : * invalid constraint, just set attnotnull, without queueing
9940 : * verification.
9941 : */
9942 10762 : if (constr->contype == CONSTR_NOTNULL)
9943 9328 : set_attnotnull(wqueue, rel, ccon->attnum,
9944 9328 : !constr->skip_validation,
9945 9328 : !constr->skip_validation);
9946 :
9947 10762 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9948 : }
9949 :
9950 : /* At this point we must have a locked-down name to use */
9951 : Assert(newcons == NIL || constr->conname != NULL);
9952 :
9953 : /* Advance command counter in case same table is visited multiple times */
9954 10972 : CommandCounterIncrement();
9955 :
9956 : /*
9957 : * If the constraint got merged with an existing constraint, we're done.
9958 : * We mustn't recurse to child tables in this case, because they've
9959 : * already got the constraint, and visiting them again would lead to an
9960 : * incorrect value for coninhcount.
9961 : */
9962 10972 : if (newcons == NIL)
9963 210 : return address;
9964 :
9965 : /*
9966 : * If adding a NO INHERIT constraint, no need to find our children.
9967 : */
9968 10762 : if (constr->is_no_inherit)
9969 84 : return address;
9970 :
9971 : /*
9972 : * Propagate to children as appropriate. Unlike most other ALTER
9973 : * routines, we have to do this one level of recursion at a time; we can't
9974 : * use find_all_inheritors to do it in one pass.
9975 : */
9976 : children =
9977 10678 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9978 :
9979 : /*
9980 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
9981 : * constraint creation only if there are no children currently. Error out
9982 : * otherwise.
9983 : */
9984 10678 : if (!recurse && children != NIL)
9985 6 : ereport(ERROR,
9986 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9987 : errmsg("constraint must be added to child tables too")));
9988 :
9989 : /*
9990 : * Recurse to create the constraint on each child.
9991 : */
9992 11460 : foreach(child, children)
9993 : {
9994 818 : Oid childrelid = lfirst_oid(child);
9995 : Relation childrel;
9996 : AlteredTableInfo *childtab;
9997 :
9998 : /* find_inheritance_children already got lock */
9999 818 : childrel = table_open(childrelid, NoLock);
10000 818 : CheckAlterTableIsSafe(childrel);
10001 :
10002 : /* Find or create work queue entry for this table */
10003 818 : childtab = ATGetQueueEntry(wqueue, childrel);
10004 :
10005 : /* Recurse to this child */
10006 818 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
10007 : constr, recurse, true, is_readd, lockmode);
10008 :
10009 788 : table_close(childrel, NoLock);
10010 : }
10011 :
10012 10642 : return address;
10013 : }
10014 :
10015 : /*
10016 : * Add a foreign-key constraint to a single table; return the new constraint's
10017 : * address.
10018 : *
10019 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
10020 : * lock on the rel, and have done appropriate validity checks for it.
10021 : * We do permissions checks here, however.
10022 : *
10023 : * When the referenced or referencing tables (or both) are partitioned,
10024 : * multiple pg_constraint rows are required -- one for each partitioned table
10025 : * and each partition on each side (fortunately, not one for every combination
10026 : * thereof). We also need action triggers on each leaf partition on the
10027 : * referenced side, and check triggers on each leaf partition on the
10028 : * referencing side.
10029 : */
10030 : static ObjectAddress
10031 2650 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
10032 : Constraint *fkconstraint,
10033 : bool recurse, bool recursing, LOCKMODE lockmode)
10034 : {
10035 : Relation pkrel;
10036 2650 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
10037 2650 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
10038 2650 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
10039 2650 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
10040 2650 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10041 2650 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10042 2650 : Oid opclasses[INDEX_MAX_KEYS] = {0};
10043 2650 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10044 2650 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10045 2650 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10046 2650 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10047 : bool with_period;
10048 : bool pk_has_without_overlaps;
10049 : int i;
10050 : int numfks,
10051 : numpks,
10052 : numfkdelsetcols;
10053 : Oid indexOid;
10054 : bool old_check_ok;
10055 : ObjectAddress address;
10056 2650 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10057 :
10058 : /*
10059 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10060 : * delete rows out from under us.
10061 : */
10062 2650 : if (OidIsValid(fkconstraint->old_pktable_oid))
10063 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10064 : else
10065 2578 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10066 :
10067 : /*
10068 : * Validity checks (permission checks wait till we have the column
10069 : * numbers)
10070 : */
10071 2644 : if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10072 6 : ereport(ERROR,
10073 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
10074 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10075 : RelationGetRelationName(rel),
10076 : RelationGetRelationName(pkrel)));
10077 :
10078 2638 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10079 352 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10080 0 : ereport(ERROR,
10081 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10082 : errmsg("referenced relation \"%s\" is not a table",
10083 : RelationGetRelationName(pkrel))));
10084 :
10085 2638 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
10086 2 : ereport(ERROR,
10087 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10088 : errmsg("permission denied: \"%s\" is a system catalog",
10089 : RelationGetRelationName(pkrel))));
10090 :
10091 : /*
10092 : * References from permanent or unlogged tables to temp tables, and from
10093 : * permanent tables to unlogged tables, are disallowed because the
10094 : * referenced data can vanish out from under us. References from temp
10095 : * tables to any other table type are also disallowed, because other
10096 : * backends might need to run the RI triggers on the perm table, but they
10097 : * can't reliably see tuples in the local buffers of other backends.
10098 : */
10099 2636 : switch (rel->rd_rel->relpersistence)
10100 : {
10101 2346 : case RELPERSISTENCE_PERMANENT:
10102 2346 : if (!RelationIsPermanent(pkrel))
10103 0 : ereport(ERROR,
10104 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10105 : errmsg("constraints on permanent tables may reference only permanent tables")));
10106 2346 : break;
10107 12 : case RELPERSISTENCE_UNLOGGED:
10108 12 : if (!RelationIsPermanent(pkrel)
10109 12 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10110 0 : ereport(ERROR,
10111 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10112 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10113 12 : break;
10114 278 : case RELPERSISTENCE_TEMP:
10115 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10116 0 : ereport(ERROR,
10117 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10118 : errmsg("constraints on temporary tables may reference only temporary tables")));
10119 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10120 0 : ereport(ERROR,
10121 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10122 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
10123 278 : break;
10124 : }
10125 :
10126 : /*
10127 : * Look up the referencing attributes to make sure they exist, and record
10128 : * their attnums and type and collation OIDs.
10129 : */
10130 2636 : numfks = transformColumnNameList(RelationGetRelid(rel),
10131 : fkconstraint->fk_attrs,
10132 : fkattnum, fktypoid, fkcolloid);
10133 2606 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10134 2606 : if (with_period && !fkconstraint->fk_with_period)
10135 24 : ereport(ERROR,
10136 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10137 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10138 :
10139 2582 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10140 : fkconstraint->fk_del_set_cols,
10141 : fkdelsetcols, NULL, NULL);
10142 2576 : numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10143 : numfkdelsetcols,
10144 : fkdelsetcols,
10145 : fkconstraint->fk_del_set_cols);
10146 :
10147 : /*
10148 : * If the attribute list for the referenced table was omitted, lookup the
10149 : * definition of the primary key and use it. Otherwise, validate the
10150 : * supplied attribute list. In either case, discover the index OID and
10151 : * index opclasses, and the attnums and type and collation OIDs of the
10152 : * attributes.
10153 : */
10154 2570 : if (fkconstraint->pk_attrs == NIL)
10155 : {
10156 1196 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10157 : &fkconstraint->pk_attrs,
10158 : pkattnum, pktypoid, pkcolloid,
10159 : opclasses, &pk_has_without_overlaps);
10160 :
10161 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10162 1196 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10163 24 : ereport(ERROR,
10164 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10165 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10166 : }
10167 : else
10168 : {
10169 1374 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
10170 : fkconstraint->pk_attrs,
10171 : pkattnum, pktypoid, pkcolloid);
10172 :
10173 : /* Since we got pk_attrs, one should be a period. */
10174 1344 : if (with_period && !fkconstraint->pk_with_period)
10175 24 : ereport(ERROR,
10176 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10177 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10178 :
10179 : /* Look for an index matching the column list */
10180 1320 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10181 : with_period, opclasses, &pk_has_without_overlaps);
10182 : }
10183 :
10184 : /*
10185 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10186 : * must use PERIOD.
10187 : */
10188 2456 : if (pk_has_without_overlaps && !with_period)
10189 12 : ereport(ERROR,
10190 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10191 : errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10192 :
10193 : /*
10194 : * Now we can check permissions.
10195 : */
10196 2444 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10197 :
10198 : /*
10199 : * Check some things for generated columns.
10200 : */
10201 5740 : for (i = 0; i < numfks; i++)
10202 : {
10203 3326 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10204 :
10205 3326 : if (attgenerated)
10206 : {
10207 : /*
10208 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10209 : */
10210 48 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10211 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10212 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10213 12 : ereport(ERROR,
10214 : (errcode(ERRCODE_SYNTAX_ERROR),
10215 : errmsg("invalid %s action for foreign key constraint containing generated column",
10216 : "ON UPDATE")));
10217 36 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10218 24 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10219 12 : ereport(ERROR,
10220 : (errcode(ERRCODE_SYNTAX_ERROR),
10221 : errmsg("invalid %s action for foreign key constraint containing generated column",
10222 : "ON DELETE")));
10223 : }
10224 :
10225 : /*
10226 : * FKs on virtual columns are not supported. This would require
10227 : * various additional support in ri_triggers.c, including special
10228 : * handling in ri_NullCheck(), ri_KeysEqual(),
10229 : * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10230 : * as NULL there). Also not really practical as long as you can't
10231 : * index virtual columns.
10232 : */
10233 3302 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10234 6 : ereport(ERROR,
10235 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10236 : errmsg("foreign key constraints on virtual generated columns are not supported")));
10237 : }
10238 :
10239 : /*
10240 : * Some actions are currently unsupported for foreign keys using PERIOD.
10241 : */
10242 2414 : if (fkconstraint->fk_with_period)
10243 : {
10244 248 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10245 236 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10246 218 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10247 200 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10248 66 : ereport(ERROR,
10249 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10250 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10251 : "ON UPDATE"));
10252 :
10253 182 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10254 176 : fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10255 176 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10256 176 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10257 6 : ereport(ERROR,
10258 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10259 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10260 : "ON DELETE"));
10261 : }
10262 :
10263 : /*
10264 : * Look up the equality operators to use in the constraint.
10265 : *
10266 : * Note that we have to be careful about the difference between the actual
10267 : * PK column type and the opclass' declared input type, which might be
10268 : * only binary-compatible with it. The declared opcintype is the right
10269 : * thing to probe pg_amop with.
10270 : */
10271 2342 : if (numfks != numpks)
10272 0 : ereport(ERROR,
10273 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10274 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10275 :
10276 : /*
10277 : * On the strength of a previous constraint, we might avoid scanning
10278 : * tables to validate this one. See below.
10279 : */
10280 2342 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10281 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10282 :
10283 5110 : for (i = 0; i < numpks; i++)
10284 : {
10285 3008 : Oid pktype = pktypoid[i];
10286 3008 : Oid fktype = fktypoid[i];
10287 : Oid fktyped;
10288 3008 : Oid pkcoll = pkcolloid[i];
10289 3008 : Oid fkcoll = fkcolloid[i];
10290 : HeapTuple cla_ht;
10291 : Form_pg_opclass cla_tup;
10292 : Oid amid;
10293 : Oid opfamily;
10294 : Oid opcintype;
10295 : bool for_overlaps;
10296 : CompareType cmptype;
10297 : Oid pfeqop;
10298 : Oid ppeqop;
10299 : Oid ffeqop;
10300 : int16 eqstrategy;
10301 : Oid pfeqop_right;
10302 :
10303 : /* We need several fields out of the pg_opclass entry */
10304 3008 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10305 3008 : if (!HeapTupleIsValid(cla_ht))
10306 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10307 3008 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10308 3008 : amid = cla_tup->opcmethod;
10309 3008 : opfamily = cla_tup->opcfamily;
10310 3008 : opcintype = cla_tup->opcintype;
10311 3008 : ReleaseSysCache(cla_ht);
10312 :
10313 : /*
10314 : * Get strategy number from index AM.
10315 : *
10316 : * For a normal foreign-key constraint, this should not fail, since we
10317 : * already checked that the index is unique and should therefore have
10318 : * appropriate equal operators. For a period foreign key, this could
10319 : * fail if we selected a non-matching exclusion constraint earlier.
10320 : * (XXX Maybe we should do these lookups earlier so we don't end up
10321 : * doing that.)
10322 : */
10323 3008 : for_overlaps = with_period && i == numpks - 1;
10324 3008 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10325 3008 : eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10326 3008 : if (eqstrategy == InvalidStrategy)
10327 0 : ereport(ERROR,
10328 : errcode(ERRCODE_UNDEFINED_OBJECT),
10329 : for_overlaps
10330 : ? errmsg("could not identify an overlaps operator for foreign key")
10331 : : errmsg("could not identify an equality operator for foreign key"),
10332 : errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10333 : cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10334 :
10335 : /*
10336 : * There had better be a primary equality operator for the index.
10337 : * We'll use it for PK = PK comparisons.
10338 : */
10339 3008 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10340 : eqstrategy);
10341 :
10342 3008 : if (!OidIsValid(ppeqop))
10343 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10344 : eqstrategy, opcintype, opcintype, opfamily);
10345 :
10346 : /*
10347 : * Are there equality operators that take exactly the FK type? Assume
10348 : * we should look through any domain here.
10349 : */
10350 3008 : fktyped = getBaseType(fktype);
10351 :
10352 3008 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10353 : eqstrategy);
10354 3008 : if (OidIsValid(pfeqop))
10355 : {
10356 2368 : pfeqop_right = fktyped;
10357 2368 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10358 : eqstrategy);
10359 : }
10360 : else
10361 : {
10362 : /* keep compiler quiet */
10363 640 : pfeqop_right = InvalidOid;
10364 640 : ffeqop = InvalidOid;
10365 : }
10366 :
10367 3008 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10368 : {
10369 : /*
10370 : * Otherwise, look for an implicit cast from the FK type to the
10371 : * opcintype, and if found, use the primary equality operator.
10372 : * This is a bit tricky because opcintype might be a polymorphic
10373 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10374 : * whether the two actual column types can be concurrently cast to
10375 : * that type. (Otherwise, we'd fail to reject combinations such
10376 : * as int[] and point[].)
10377 : */
10378 : Oid input_typeids[2];
10379 : Oid target_typeids[2];
10380 :
10381 640 : input_typeids[0] = pktype;
10382 640 : input_typeids[1] = fktype;
10383 640 : target_typeids[0] = opcintype;
10384 640 : target_typeids[1] = opcintype;
10385 640 : if (can_coerce_type(2, input_typeids, target_typeids,
10386 : COERCION_IMPLICIT))
10387 : {
10388 412 : pfeqop = ffeqop = ppeqop;
10389 412 : pfeqop_right = opcintype;
10390 : }
10391 : }
10392 :
10393 3008 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10394 228 : ereport(ERROR,
10395 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10396 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10397 : fkconstraint->conname),
10398 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10399 : "are of incompatible types: %s and %s.",
10400 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10401 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10402 : format_type_be(fktype),
10403 : format_type_be(pktype))));
10404 :
10405 : /*
10406 : * This shouldn't be possible, but better check to make sure we have a
10407 : * consistent state for the check below.
10408 : */
10409 2780 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10410 0 : elog(ERROR, "key columns are not both collatable");
10411 :
10412 2780 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10413 : {
10414 : bool pkcolldet;
10415 : bool fkcolldet;
10416 :
10417 106 : pkcolldet = get_collation_isdeterministic(pkcoll);
10418 106 : fkcolldet = get_collation_isdeterministic(fkcoll);
10419 :
10420 : /*
10421 : * SQL requires that both collations are the same. This is
10422 : * because we need a consistent notion of equality on both
10423 : * columns. We relax this by allowing different collations if
10424 : * they are both deterministic. (This is also for backward
10425 : * compatibility, because PostgreSQL has always allowed this.)
10426 : */
10427 106 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10428 12 : ereport(ERROR,
10429 : (errcode(ERRCODE_COLLATION_MISMATCH),
10430 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10431 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10432 : "have incompatible collations: \"%s\" and \"%s\". "
10433 : "If either collation is nondeterministic, then both collations have to be the same.",
10434 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10435 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10436 : get_collation_name(fkcoll),
10437 : get_collation_name(pkcoll))));
10438 : }
10439 :
10440 2768 : if (old_check_ok)
10441 : {
10442 : /*
10443 : * When a pfeqop changes, revalidate the constraint. We could
10444 : * permit intra-opfamily changes, but that adds subtle complexity
10445 : * without any concrete benefit for core types. We need not
10446 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10447 : */
10448 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10449 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10450 : old_pfeqop_item);
10451 : }
10452 2768 : if (old_check_ok)
10453 : {
10454 : Oid old_fktype;
10455 : Oid new_fktype;
10456 : CoercionPathType old_pathtype;
10457 : CoercionPathType new_pathtype;
10458 : Oid old_castfunc;
10459 : Oid new_castfunc;
10460 : Oid old_fkcoll;
10461 : Oid new_fkcoll;
10462 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10463 6 : fkattnum[i] - 1);
10464 :
10465 : /*
10466 : * Identify coercion pathways from each of the old and new FK-side
10467 : * column types to the right (foreign) operand type of the pfeqop.
10468 : * We may assume that pg_constraint.conkey is not changing.
10469 : */
10470 6 : old_fktype = attr->atttypid;
10471 6 : new_fktype = fktype;
10472 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10473 : &old_castfunc);
10474 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10475 : &new_castfunc);
10476 :
10477 6 : old_fkcoll = attr->attcollation;
10478 6 : new_fkcoll = fkcoll;
10479 :
10480 : /*
10481 : * Upon a change to the cast from the FK column to its pfeqop
10482 : * operand, revalidate the constraint. For this evaluation, a
10483 : * binary coercion cast is equivalent to no cast at all. While
10484 : * type implementors should design implicit casts with an eye
10485 : * toward consistency of operations like equality, we cannot
10486 : * assume here that they have done so.
10487 : *
10488 : * A function with a polymorphic argument could change behavior
10489 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10490 : * when the cast destination is polymorphic, we only avoid
10491 : * revalidation if the input type has not changed at all. Given
10492 : * just the core data types and operator classes, this requirement
10493 : * prevents no would-be optimizations.
10494 : *
10495 : * If the cast converts from a base type to a domain thereon, then
10496 : * that domain type must be the opcintype of the unique index.
10497 : * Necessarily, the primary key column must then be of the domain
10498 : * type. Since the constraint was previously valid, all values on
10499 : * the foreign side necessarily exist on the primary side and in
10500 : * turn conform to the domain. Consequently, we need not treat
10501 : * domains specially here.
10502 : *
10503 : * If the collation changes, revalidation is required, unless both
10504 : * collations are deterministic, because those share the same
10505 : * notion of equality (because texteq reduces to bitwise
10506 : * equality).
10507 : *
10508 : * We need not directly consider the PK type. It's necessarily
10509 : * binary coercible to the opcintype of the unique index column,
10510 : * and ri_triggers.c will only deal with PK datums in terms of
10511 : * that opcintype. Changing the opcintype also changes pfeqop.
10512 : */
10513 6 : old_check_ok = (new_pathtype == old_pathtype &&
10514 6 : new_castfunc == old_castfunc &&
10515 6 : (!IsPolymorphicType(pfeqop_right) ||
10516 12 : new_fktype == old_fktype) &&
10517 0 : (new_fkcoll == old_fkcoll ||
10518 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10519 : }
10520 :
10521 2768 : pfeqoperators[i] = pfeqop;
10522 2768 : ppeqoperators[i] = ppeqop;
10523 2768 : ffeqoperators[i] = ffeqop;
10524 : }
10525 :
10526 : /*
10527 : * For FKs with PERIOD we need additional operators to check whether the
10528 : * referencing row's range is contained by the aggregated ranges of the
10529 : * referenced row(s). For rangetypes and multirangetypes this is
10530 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10531 : * support for now. FKs will look these up at "runtime", but we should
10532 : * make sure the lookup works here, even if we don't use the values.
10533 : */
10534 2102 : if (with_period)
10535 : {
10536 : Oid periodoperoid;
10537 : Oid aggedperiodoperoid;
10538 : Oid intersectoperoid;
10539 :
10540 158 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10541 : &intersectoperoid);
10542 : }
10543 :
10544 : /* First, create the constraint catalog entry itself. */
10545 2102 : address = addFkConstraint(addFkBothSides,
10546 : fkconstraint->conname, fkconstraint, rel, pkrel,
10547 : indexOid,
10548 : InvalidOid, /* no parent constraint */
10549 : numfks,
10550 : pkattnum,
10551 : fkattnum,
10552 : pfeqoperators,
10553 : ppeqoperators,
10554 : ffeqoperators,
10555 : numfkdelsetcols,
10556 : fkdelsetcols,
10557 : false,
10558 : with_period);
10559 :
10560 : /* Next process the action triggers at the referenced side and recurse */
10561 2102 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10562 : indexOid,
10563 : address.objectId,
10564 : numfks,
10565 : pkattnum,
10566 : fkattnum,
10567 : pfeqoperators,
10568 : ppeqoperators,
10569 : ffeqoperators,
10570 : numfkdelsetcols,
10571 : fkdelsetcols,
10572 : old_check_ok,
10573 : InvalidOid, InvalidOid,
10574 : with_period);
10575 :
10576 : /* Lastly create the check triggers at the referencing side and recurse */
10577 2102 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10578 : indexOid,
10579 : address.objectId,
10580 : numfks,
10581 : pkattnum,
10582 : fkattnum,
10583 : pfeqoperators,
10584 : ppeqoperators,
10585 : ffeqoperators,
10586 : numfkdelsetcols,
10587 : fkdelsetcols,
10588 : old_check_ok,
10589 : lockmode,
10590 : InvalidOid, InvalidOid,
10591 : with_period);
10592 :
10593 : /*
10594 : * Done. Close pk table, but keep lock until we've committed.
10595 : */
10596 2102 : table_close(pkrel, NoLock);
10597 :
10598 2102 : return address;
10599 : }
10600 :
10601 : /*
10602 : * validateFkOnDeleteSetColumns
10603 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10604 : * column lists are valid.
10605 : *
10606 : * If there are duplicates in the fksetcolsattnums[] array, this silently
10607 : * removes the dups. The new count of numfksetcols is returned.
10608 : */
10609 : static int
10610 2576 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10611 : int numfksetcols, int16 *fksetcolsattnums,
10612 : List *fksetcols)
10613 : {
10614 2576 : int numcolsout = 0;
10615 :
10616 2606 : for (int i = 0; i < numfksetcols; i++)
10617 : {
10618 36 : int16 setcol_attnum = fksetcolsattnums[i];
10619 36 : bool seen = false;
10620 :
10621 : /* Make sure it's in fkattnums[] */
10622 66 : for (int j = 0; j < numfks; j++)
10623 : {
10624 60 : if (fkattnums[j] == setcol_attnum)
10625 : {
10626 30 : seen = true;
10627 30 : break;
10628 : }
10629 : }
10630 :
10631 36 : if (!seen)
10632 : {
10633 6 : char *col = strVal(list_nth(fksetcols, i));
10634 :
10635 6 : ereport(ERROR,
10636 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10637 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10638 : }
10639 :
10640 : /* Now check for dups */
10641 30 : seen = false;
10642 30 : for (int j = 0; j < numcolsout; j++)
10643 : {
10644 6 : if (fksetcolsattnums[j] == setcol_attnum)
10645 : {
10646 6 : seen = true;
10647 6 : break;
10648 : }
10649 : }
10650 30 : if (!seen)
10651 24 : fksetcolsattnums[numcolsout++] = setcol_attnum;
10652 : }
10653 2570 : return numcolsout;
10654 : }
10655 :
10656 : /*
10657 : * addFkConstraint
10658 : * Install pg_constraint entries to implement a foreign key constraint.
10659 : * Caller must separately invoke addFkRecurseReferenced and
10660 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10661 : * and (for partitioned tables) recurse to partitions.
10662 : *
10663 : * fkside: the side of the FK (or both) to create. Caller should
10664 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10665 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10666 : * addFkBothSides.
10667 : * constraintname: the base name for the constraint being added,
10668 : * copied to fkconstraint->conname if the latter is not set
10669 : * fkconstraint: the constraint being added
10670 : * rel: the root referencing relation
10671 : * pkrel: the referenced relation; might be a partition, if recursing
10672 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10673 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10674 : * top-level constraint
10675 : * numfks: the number of columns in the foreign key
10676 : * pkattnum: the attnum array of referenced attributes
10677 : * fkattnum: the attnum array of referencing attributes
10678 : * pf/pp/ffeqoperators: OID array of operators between columns
10679 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10680 : * (...) clause
10681 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10682 : * NULL/DEFAULT clause
10683 : * with_period: true if this is a temporal FK
10684 : */
10685 : static ObjectAddress
10686 4146 : addFkConstraint(addFkConstraintSides fkside,
10687 : char *constraintname, Constraint *fkconstraint,
10688 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10689 : int numfks, int16 *pkattnum,
10690 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10691 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10692 : bool is_internal, bool with_period)
10693 : {
10694 : ObjectAddress address;
10695 : Oid constrOid;
10696 : char *conname;
10697 : bool conislocal;
10698 : int16 coninhcount;
10699 : bool connoinherit;
10700 :
10701 : /*
10702 : * Verify relkind for each referenced partition. At the top level, this
10703 : * is redundant with a previous check, but we need it when recursing.
10704 : */
10705 4146 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10706 904 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10707 0 : ereport(ERROR,
10708 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10709 : errmsg("referenced relation \"%s\" is not a table",
10710 : RelationGetRelationName(pkrel))));
10711 :
10712 : /*
10713 : * Caller supplies us with a constraint name; however, it may be used in
10714 : * this partition, so come up with a different one in that case. Unless
10715 : * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10716 : * supplied name with an underscore and digit(s) appended.
10717 : */
10718 4146 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10719 : RelationGetRelid(rel),
10720 : constraintname))
10721 1208 : conname = ChooseConstraintName(constraintname,
10722 : NULL,
10723 : "",
10724 1208 : RelationGetNamespace(rel), NIL);
10725 : else
10726 2938 : conname = constraintname;
10727 :
10728 4146 : if (fkconstraint->conname == NULL)
10729 424 : fkconstraint->conname = pstrdup(conname);
10730 :
10731 4146 : if (OidIsValid(parentConstr))
10732 : {
10733 2044 : conislocal = false;
10734 2044 : coninhcount = 1;
10735 2044 : connoinherit = false;
10736 : }
10737 : else
10738 : {
10739 2102 : conislocal = true;
10740 2102 : coninhcount = 0;
10741 :
10742 : /*
10743 : * always inherit for partitioned tables, never for legacy inheritance
10744 : */
10745 2102 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10746 : }
10747 :
10748 : /*
10749 : * Record the FK constraint in pg_constraint.
10750 : */
10751 4146 : constrOid = CreateConstraintEntry(conname,
10752 4146 : RelationGetNamespace(rel),
10753 : CONSTRAINT_FOREIGN,
10754 4146 : fkconstraint->deferrable,
10755 4146 : fkconstraint->initdeferred,
10756 4146 : fkconstraint->is_enforced,
10757 4146 : fkconstraint->initially_valid,
10758 : parentConstr,
10759 : RelationGetRelid(rel),
10760 : fkattnum,
10761 : numfks,
10762 : numfks,
10763 : InvalidOid, /* not a domain constraint */
10764 : indexOid,
10765 : RelationGetRelid(pkrel),
10766 : pkattnum,
10767 : pfeqoperators,
10768 : ppeqoperators,
10769 : ffeqoperators,
10770 : numfks,
10771 4146 : fkconstraint->fk_upd_action,
10772 4146 : fkconstraint->fk_del_action,
10773 : fkdelsetcols,
10774 : numfkdelsetcols,
10775 4146 : fkconstraint->fk_matchtype,
10776 : NULL, /* no exclusion constraint */
10777 : NULL, /* no check constraint */
10778 : NULL,
10779 : conislocal, /* islocal */
10780 : coninhcount, /* inhcount */
10781 : connoinherit, /* conNoInherit */
10782 : with_period, /* conPeriod */
10783 : is_internal); /* is_internal */
10784 :
10785 4146 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10786 :
10787 : /*
10788 : * In partitioning cases, create the dependency entries for this
10789 : * constraint. (For non-partitioned cases, relevant entries were created
10790 : * by CreateConstraintEntry.)
10791 : *
10792 : * On the referenced side, we need the constraint to have an internal
10793 : * dependency on its parent constraint; this means that this constraint
10794 : * cannot be dropped on its own -- only through the parent constraint. It
10795 : * also means the containing partition cannot be dropped on its own, but
10796 : * it can be detached, at which point this dependency is removed (after
10797 : * verifying that no rows are referenced via this FK.)
10798 : *
10799 : * When processing the referencing side, we link the constraint via the
10800 : * special partitioning dependencies: the parent constraint is the primary
10801 : * dependent, and the partition on which the foreign key exists is the
10802 : * secondary dependency. That way, this constraint is dropped if either
10803 : * of these objects is.
10804 : *
10805 : * Note that this is only necessary for the subsidiary pg_constraint rows
10806 : * in partitions; the topmost row doesn't need any of this.
10807 : */
10808 4146 : if (OidIsValid(parentConstr))
10809 : {
10810 : ObjectAddress referenced;
10811 :
10812 2044 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10813 :
10814 : Assert(fkside != addFkBothSides);
10815 2044 : if (fkside == addFkReferencedSide)
10816 1202 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10817 : else
10818 : {
10819 842 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10820 842 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10821 842 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10822 : }
10823 : }
10824 :
10825 : /* make new constraint visible, in case we add more */
10826 4146 : CommandCounterIncrement();
10827 :
10828 4146 : return address;
10829 : }
10830 :
10831 : /*
10832 : * addFkRecurseReferenced
10833 : * Recursive helper for the referenced side of foreign key creation,
10834 : * which creates the action triggers and recurses
10835 : *
10836 : * If the referenced relation is a plain relation, create the necessary action
10837 : * triggers that implement the constraint. If the referenced relation is a
10838 : * partitioned table, then we create a pg_constraint row referencing the parent
10839 : * of the referencing side for it and recurse on this routine for each
10840 : * partition.
10841 : *
10842 : * fkconstraint: the constraint being added
10843 : * rel: the root referencing relation
10844 : * pkrel: the referenced relation; might be a partition, if recursing
10845 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10846 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10847 : * top-level constraint
10848 : * numfks: the number of columns in the foreign key
10849 : * pkattnum: the attnum array of referenced attributes
10850 : * fkattnum: the attnum array of referencing attributes
10851 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10852 : * NULL/DEFAULT (...) clause
10853 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10854 : * NULL/DEFAULT clause
10855 : * pf/pp/ffeqoperators: OID array of operators between columns
10856 : * old_check_ok: true if this constraint replaces an existing one that
10857 : * was already validated (thus this one doesn't need validation)
10858 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10859 : * partition, the OIDs of the parent action triggers for DELETE and
10860 : * UPDATE respectively.
10861 : * with_period: true if this is a temporal FK
10862 : */
10863 : static void
10864 3406 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10865 : Relation pkrel, Oid indexOid, Oid parentConstr,
10866 : int numfks,
10867 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10868 : Oid *ppeqoperators, Oid *ffeqoperators,
10869 : int numfkdelsetcols, int16 *fkdelsetcols,
10870 : bool old_check_ok,
10871 : Oid parentDelTrigger, Oid parentUpdTrigger,
10872 : bool with_period)
10873 : {
10874 3406 : Oid deleteTriggerOid = InvalidOid,
10875 3406 : updateTriggerOid = InvalidOid;
10876 :
10877 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10878 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10879 :
10880 : /*
10881 : * Create action triggers to enforce the constraint, or skip them if the
10882 : * constraint is NOT ENFORCED.
10883 : */
10884 3406 : if (fkconstraint->is_enforced)
10885 3358 : createForeignKeyActionTriggers(RelationGetRelid(rel),
10886 : RelationGetRelid(pkrel),
10887 : fkconstraint,
10888 : parentConstr, indexOid,
10889 : parentDelTrigger, parentUpdTrigger,
10890 : &deleteTriggerOid, &updateTriggerOid);
10891 :
10892 : /*
10893 : * If the referenced table is partitioned, recurse on ourselves to handle
10894 : * each partition. We need one pg_constraint row created for each
10895 : * partition in addition to the pg_constraint row for the parent table.
10896 : */
10897 3406 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10898 : {
10899 570 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10900 :
10901 1550 : for (int i = 0; i < pd->nparts; i++)
10902 : {
10903 : Relation partRel;
10904 : AttrMap *map;
10905 : AttrNumber *mapped_pkattnum;
10906 : Oid partIndexId;
10907 : ObjectAddress address;
10908 :
10909 : /* XXX would it be better to acquire these locks beforehand? */
10910 980 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10911 :
10912 : /*
10913 : * Map the attribute numbers in the referenced side of the FK
10914 : * definition to match the partition's column layout.
10915 : */
10916 980 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10917 : RelationGetDescr(pkrel),
10918 : false);
10919 980 : if (map)
10920 : {
10921 130 : mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10922 272 : for (int j = 0; j < numfks; j++)
10923 142 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10924 : }
10925 : else
10926 850 : mapped_pkattnum = pkattnum;
10927 :
10928 : /* Determine the index to use at this level */
10929 980 : partIndexId = index_get_partition(partRel, indexOid);
10930 980 : if (!OidIsValid(partIndexId))
10931 0 : elog(ERROR, "index for %u not found in partition %s",
10932 : indexOid, RelationGetRelationName(partRel));
10933 :
10934 : /* Create entry at this level ... */
10935 980 : address = addFkConstraint(addFkReferencedSide,
10936 : fkconstraint->conname, fkconstraint, rel,
10937 : partRel, partIndexId, parentConstr,
10938 : numfks, mapped_pkattnum,
10939 : fkattnum, pfeqoperators, ppeqoperators,
10940 : ffeqoperators, numfkdelsetcols,
10941 : fkdelsetcols, true, with_period);
10942 : /* ... and recurse to our children */
10943 980 : addFkRecurseReferenced(fkconstraint, rel, partRel,
10944 : partIndexId, address.objectId, numfks,
10945 : mapped_pkattnum, fkattnum,
10946 : pfeqoperators, ppeqoperators, ffeqoperators,
10947 : numfkdelsetcols, fkdelsetcols,
10948 : old_check_ok,
10949 : deleteTriggerOid, updateTriggerOid,
10950 : with_period);
10951 :
10952 : /* Done -- clean up (but keep the lock) */
10953 980 : table_close(partRel, NoLock);
10954 980 : if (map)
10955 : {
10956 130 : pfree(mapped_pkattnum);
10957 130 : free_attrmap(map);
10958 : }
10959 : }
10960 : }
10961 3406 : }
10962 :
10963 : /*
10964 : * addFkRecurseReferencing
10965 : * Recursive helper for the referencing side of foreign key creation,
10966 : * which creates the check triggers and recurses
10967 : *
10968 : * If the referencing relation is a plain relation, create the necessary check
10969 : * triggers that implement the constraint, and set up for Phase 3 constraint
10970 : * verification. If the referencing relation is a partitioned table, then
10971 : * we create a pg_constraint row for it and recurse on this routine for each
10972 : * partition.
10973 : *
10974 : * We assume that the referenced relation is locked against concurrent
10975 : * deletions. If it's a partitioned relation, every partition must be so
10976 : * locked.
10977 : *
10978 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
10979 : * of an ALTER TABLE sequence.
10980 : * fkconstraint: the constraint being added
10981 : * rel: the referencing relation; might be a partition, if recursing
10982 : * pkrel: the root referenced relation
10983 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10984 : * parentConstr: the OID of the parent constraint (there is always one)
10985 : * numfks: the number of columns in the foreign key
10986 : * pkattnum: the attnum array of referenced attributes
10987 : * fkattnum: the attnum array of referencing attributes
10988 : * pf/pp/ffeqoperators: OID array of operators between columns
10989 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10990 : * (...) clause
10991 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10992 : * NULL/DEFAULT clause
10993 : * old_check_ok: true if this constraint replaces an existing one that
10994 : * was already validated (thus this one doesn't need validation)
10995 : * lockmode: the lockmode to acquire on partitions when recursing
10996 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
10997 : * a partition, the OIDs of the parent check triggers for INSERT and
10998 : * UPDATE respectively.
10999 : * with_period: true if this is a temporal FK
11000 : */
11001 : static void
11002 2944 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
11003 : Relation pkrel, Oid indexOid, Oid parentConstr,
11004 : int numfks, int16 *pkattnum, int16 *fkattnum,
11005 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11006 : int numfkdelsetcols, int16 *fkdelsetcols,
11007 : bool old_check_ok, LOCKMODE lockmode,
11008 : Oid parentInsTrigger, Oid parentUpdTrigger,
11009 : bool with_period)
11010 : {
11011 2944 : Oid insertTriggerOid = InvalidOid,
11012 2944 : updateTriggerOid = InvalidOid;
11013 :
11014 : Assert(OidIsValid(parentConstr));
11015 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
11016 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
11017 :
11018 2944 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11019 0 : ereport(ERROR,
11020 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11021 : errmsg("foreign key constraints are not supported on foreign tables")));
11022 :
11023 : /*
11024 : * Add check triggers if the constraint is ENFORCED, and if needed,
11025 : * schedule them to be checked in Phase 3.
11026 : *
11027 : * If the relation is partitioned, drill down to do it to its partitions.
11028 : */
11029 2944 : if (fkconstraint->is_enforced)
11030 2902 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
11031 : RelationGetRelid(pkrel),
11032 : fkconstraint,
11033 : parentConstr,
11034 : indexOid,
11035 : parentInsTrigger, parentUpdTrigger,
11036 : &insertTriggerOid, &updateTriggerOid);
11037 :
11038 2944 : if (rel->rd_rel->relkind == RELKIND_RELATION)
11039 : {
11040 : /*
11041 : * Tell Phase 3 to check that the constraint is satisfied by existing
11042 : * rows. We can skip this during table creation, when constraint is
11043 : * specified as NOT ENFORCED, or when requested explicitly by
11044 : * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11045 : * recreating a constraint following a SET DATA TYPE operation that
11046 : * did not impugn its validity.
11047 : */
11048 2444 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11049 836 : fkconstraint->is_enforced)
11050 : {
11051 : NewConstraint *newcon;
11052 : AlteredTableInfo *tab;
11053 :
11054 836 : tab = ATGetQueueEntry(wqueue, rel);
11055 :
11056 836 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11057 836 : newcon->name = get_constraint_name(parentConstr);
11058 836 : newcon->contype = CONSTR_FOREIGN;
11059 836 : newcon->refrelid = RelationGetRelid(pkrel);
11060 836 : newcon->refindid = indexOid;
11061 836 : newcon->conid = parentConstr;
11062 836 : newcon->conwithperiod = fkconstraint->fk_with_period;
11063 836 : newcon->qual = (Node *) fkconstraint;
11064 :
11065 836 : tab->constraints = lappend(tab->constraints, newcon);
11066 : }
11067 : }
11068 500 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11069 : {
11070 500 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
11071 : Relation trigrel;
11072 :
11073 : /*
11074 : * Triggers of the foreign keys will be manipulated a bunch of times
11075 : * in the loop below. To avoid repeatedly opening/closing the trigger
11076 : * catalog relation, we open it here and pass it to the subroutines
11077 : * called below.
11078 : */
11079 500 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11080 :
11081 : /*
11082 : * Recurse to take appropriate action on each partition; either we
11083 : * find an existing constraint to reparent to ours, or we create a new
11084 : * one.
11085 : */
11086 930 : for (int i = 0; i < pd->nparts; i++)
11087 : {
11088 436 : Relation partition = table_open(pd->oids[i], lockmode);
11089 : List *partFKs;
11090 : AttrMap *attmap;
11091 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11092 : bool attached;
11093 : ObjectAddress address;
11094 :
11095 436 : CheckAlterTableIsSafe(partition);
11096 :
11097 430 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
11098 : RelationGetDescr(rel),
11099 : false);
11100 1106 : for (int j = 0; j < numfks; j++)
11101 676 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11102 :
11103 : /* Check whether an existing constraint can be repurposed */
11104 430 : partFKs = copyObject(RelationGetFKeyList(partition));
11105 430 : attached = false;
11106 884 : foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11107 : {
11108 36 : if (tryAttachPartitionForeignKey(wqueue,
11109 : fk,
11110 : partition,
11111 : parentConstr,
11112 : numfks,
11113 : mapped_fkattnum,
11114 : pkattnum,
11115 : pfeqoperators,
11116 : insertTriggerOid,
11117 : updateTriggerOid,
11118 : trigrel))
11119 : {
11120 12 : attached = true;
11121 12 : break;
11122 : }
11123 : }
11124 430 : if (attached)
11125 : {
11126 12 : table_close(partition, NoLock);
11127 12 : continue;
11128 : }
11129 :
11130 : /*
11131 : * No luck finding a good constraint to reuse; create our own.
11132 : */
11133 418 : address = addFkConstraint(addFkReferencingSide,
11134 : fkconstraint->conname, fkconstraint,
11135 : partition, pkrel, indexOid, parentConstr,
11136 : numfks, pkattnum,
11137 : mapped_fkattnum, pfeqoperators,
11138 : ppeqoperators, ffeqoperators,
11139 : numfkdelsetcols, fkdelsetcols, true,
11140 : with_period);
11141 :
11142 : /* call ourselves to finalize the creation and we're done */
11143 418 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11144 : indexOid,
11145 : address.objectId,
11146 : numfks,
11147 : pkattnum,
11148 : mapped_fkattnum,
11149 : pfeqoperators,
11150 : ppeqoperators,
11151 : ffeqoperators,
11152 : numfkdelsetcols,
11153 : fkdelsetcols,
11154 : old_check_ok,
11155 : lockmode,
11156 : insertTriggerOid,
11157 : updateTriggerOid,
11158 : with_period);
11159 :
11160 418 : table_close(partition, NoLock);
11161 : }
11162 :
11163 494 : table_close(trigrel, RowExclusiveLock);
11164 : }
11165 2938 : }
11166 :
11167 : /*
11168 : * CloneForeignKeyConstraints
11169 : * Clone foreign keys from a partitioned table to a newly acquired
11170 : * partition.
11171 : *
11172 : * partitionRel is a partition of parentRel, so we can be certain that it has
11173 : * the same columns with the same datatypes. The columns may be in different
11174 : * order, though.
11175 : *
11176 : * wqueue must be passed to set up phase 3 constraint checking, unless the
11177 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
11178 : * PARTITION OF).
11179 : */
11180 : static void
11181 9980 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
11182 : Relation partitionRel)
11183 : {
11184 : /* This only works for declarative partitioning */
11185 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11186 :
11187 : /*
11188 : * First, clone constraints where the parent is on the referencing side.
11189 : */
11190 9980 : CloneFkReferencing(wqueue, parentRel, partitionRel);
11191 :
11192 : /*
11193 : * Clone constraints for which the parent is on the referenced side.
11194 : */
11195 9962 : CloneFkReferenced(parentRel, partitionRel);
11196 9962 : }
11197 :
11198 : /*
11199 : * CloneFkReferenced
11200 : * Subroutine for CloneForeignKeyConstraints
11201 : *
11202 : * Find all the FKs that have the parent relation on the referenced side;
11203 : * clone those constraints to the given partition. This is to be called
11204 : * when the partition is being created or attached.
11205 : *
11206 : * This recurses to partitions, if the relation being attached is partitioned.
11207 : * Recursion is done by calling addFkRecurseReferenced.
11208 : */
11209 : static void
11210 9962 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
11211 : {
11212 : Relation pg_constraint;
11213 : AttrMap *attmap;
11214 : ListCell *cell;
11215 : SysScanDesc scan;
11216 : ScanKeyData key[2];
11217 : HeapTuple tuple;
11218 9962 : List *clone = NIL;
11219 : Relation trigrel;
11220 :
11221 : /*
11222 : * Search for any constraints where this partition's parent is in the
11223 : * referenced side. However, we must not clone any constraint whose
11224 : * parent constraint is also going to be cloned, to avoid duplicates. So
11225 : * do it in two steps: first construct the list of constraints to clone,
11226 : * then go over that list cloning those whose parents are not in the list.
11227 : * (We must not rely on the parent being seen first, since the catalog
11228 : * scan could return children first.)
11229 : */
11230 9962 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11231 9962 : ScanKeyInit(&key[0],
11232 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11233 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11234 9962 : ScanKeyInit(&key[1],
11235 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11236 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11237 : /* This is a seqscan, as we don't have a usable index ... */
11238 9962 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11239 : NULL, 2, key);
11240 10406 : while ((tuple = systable_getnext(scan)) != NULL)
11241 : {
11242 444 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11243 :
11244 444 : clone = lappend_oid(clone, constrForm->oid);
11245 : }
11246 9962 : systable_endscan(scan);
11247 9962 : table_close(pg_constraint, RowShareLock);
11248 :
11249 : /*
11250 : * Triggers of the foreign keys will be manipulated a bunch of times in
11251 : * the loop below. To avoid repeatedly opening/closing the trigger
11252 : * catalog relation, we open it here and pass it to the subroutines called
11253 : * below.
11254 : */
11255 9962 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11256 :
11257 9962 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11258 : RelationGetDescr(parentRel),
11259 : false);
11260 10406 : foreach(cell, clone)
11261 : {
11262 444 : Oid constrOid = lfirst_oid(cell);
11263 : Form_pg_constraint constrForm;
11264 : Relation fkRel;
11265 : Oid indexOid;
11266 : Oid partIndexId;
11267 : int numfks;
11268 : AttrNumber conkey[INDEX_MAX_KEYS];
11269 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11270 : AttrNumber confkey[INDEX_MAX_KEYS];
11271 : Oid conpfeqop[INDEX_MAX_KEYS];
11272 : Oid conppeqop[INDEX_MAX_KEYS];
11273 : Oid conffeqop[INDEX_MAX_KEYS];
11274 : int numfkdelsetcols;
11275 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11276 : Constraint *fkconstraint;
11277 : ObjectAddress address;
11278 444 : Oid deleteTriggerOid = InvalidOid,
11279 444 : updateTriggerOid = InvalidOid;
11280 :
11281 444 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11282 444 : if (!HeapTupleIsValid(tuple))
11283 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11284 444 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11285 :
11286 : /*
11287 : * As explained above: don't try to clone a constraint for which we're
11288 : * going to clone the parent.
11289 : */
11290 444 : if (list_member_oid(clone, constrForm->conparentid))
11291 : {
11292 222 : ReleaseSysCache(tuple);
11293 222 : continue;
11294 : }
11295 :
11296 : /* We need the same lock level that CreateTrigger will acquire */
11297 222 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11298 :
11299 222 : indexOid = constrForm->conindid;
11300 222 : DeconstructFkConstraintRow(tuple,
11301 : &numfks,
11302 : conkey,
11303 : confkey,
11304 : conpfeqop,
11305 : conppeqop,
11306 : conffeqop,
11307 : &numfkdelsetcols,
11308 : confdelsetcols);
11309 :
11310 486 : for (int i = 0; i < numfks; i++)
11311 264 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11312 :
11313 222 : fkconstraint = makeNode(Constraint);
11314 222 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11315 222 : fkconstraint->conname = NameStr(constrForm->conname);
11316 222 : fkconstraint->deferrable = constrForm->condeferrable;
11317 222 : fkconstraint->initdeferred = constrForm->condeferred;
11318 222 : fkconstraint->location = -1;
11319 222 : fkconstraint->pktable = NULL;
11320 : /* ->fk_attrs determined below */
11321 222 : fkconstraint->pk_attrs = NIL;
11322 222 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11323 222 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11324 222 : fkconstraint->fk_del_action = constrForm->confdeltype;
11325 222 : fkconstraint->fk_del_set_cols = NIL;
11326 222 : fkconstraint->old_conpfeqop = NIL;
11327 222 : fkconstraint->old_pktable_oid = InvalidOid;
11328 222 : fkconstraint->is_enforced = constrForm->conenforced;
11329 222 : fkconstraint->skip_validation = false;
11330 222 : fkconstraint->initially_valid = constrForm->convalidated;
11331 :
11332 : /* set up colnames that are used to generate the constraint name */
11333 486 : for (int i = 0; i < numfks; i++)
11334 : {
11335 : Form_pg_attribute att;
11336 :
11337 264 : att = TupleDescAttr(RelationGetDescr(fkRel),
11338 264 : conkey[i] - 1);
11339 264 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11340 264 : makeString(NameStr(att->attname)));
11341 : }
11342 :
11343 : /*
11344 : * Add the new foreign key constraint pointing to the new partition.
11345 : * Because this new partition appears in the referenced side of the
11346 : * constraint, we don't need to set up for Phase 3 check.
11347 : */
11348 222 : partIndexId = index_get_partition(partitionRel, indexOid);
11349 222 : if (!OidIsValid(partIndexId))
11350 0 : elog(ERROR, "index for %u not found in partition %s",
11351 : indexOid, RelationGetRelationName(partitionRel));
11352 :
11353 : /*
11354 : * Get the "action" triggers belonging to the constraint to pass as
11355 : * parent OIDs for similar triggers that will be created on the
11356 : * partition in addFkRecurseReferenced().
11357 : */
11358 222 : if (constrForm->conenforced)
11359 222 : GetForeignKeyActionTriggers(trigrel, constrOid,
11360 : constrForm->confrelid, constrForm->conrelid,
11361 : &deleteTriggerOid, &updateTriggerOid);
11362 :
11363 : /* Add this constraint ... */
11364 222 : address = addFkConstraint(addFkReferencedSide,
11365 : fkconstraint->conname, fkconstraint, fkRel,
11366 : partitionRel, partIndexId, constrOid,
11367 : numfks, mapped_confkey,
11368 : conkey, conpfeqop, conppeqop, conffeqop,
11369 : numfkdelsetcols, confdelsetcols, false,
11370 222 : constrForm->conperiod);
11371 : /* ... and recurse */
11372 222 : addFkRecurseReferenced(fkconstraint,
11373 : fkRel,
11374 : partitionRel,
11375 : partIndexId,
11376 : address.objectId,
11377 : numfks,
11378 : mapped_confkey,
11379 : conkey,
11380 : conpfeqop,
11381 : conppeqop,
11382 : conffeqop,
11383 : numfkdelsetcols,
11384 : confdelsetcols,
11385 : true,
11386 : deleteTriggerOid,
11387 : updateTriggerOid,
11388 222 : constrForm->conperiod);
11389 :
11390 222 : table_close(fkRel, NoLock);
11391 222 : ReleaseSysCache(tuple);
11392 : }
11393 :
11394 9962 : table_close(trigrel, RowExclusiveLock);
11395 9962 : }
11396 :
11397 : /*
11398 : * CloneFkReferencing
11399 : * Subroutine for CloneForeignKeyConstraints
11400 : *
11401 : * For each FK constraint of the parent relation in the given list, find an
11402 : * equivalent constraint in its partition relation that can be reparented;
11403 : * if one cannot be found, create a new constraint in the partition as its
11404 : * child.
11405 : *
11406 : * If wqueue is given, it is used to set up phase-3 verification for each
11407 : * cloned constraint; omit it if such verification is not needed
11408 : * (example: the partition is being created anew).
11409 : */
11410 : static void
11411 9980 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11412 : {
11413 : AttrMap *attmap;
11414 : List *partFKs;
11415 9980 : List *clone = NIL;
11416 : ListCell *cell;
11417 : Relation trigrel;
11418 :
11419 : /* obtain a list of constraints that we need to clone */
11420 11284 : foreach(cell, RelationGetFKeyList(parentRel))
11421 : {
11422 1310 : ForeignKeyCacheInfo *fk = lfirst(cell);
11423 :
11424 : /*
11425 : * Refuse to attach a table as partition that this partitioned table
11426 : * already has a foreign key to. This isn't useful schema, which is
11427 : * proven by the fact that there have been no user complaints that
11428 : * it's already impossible to achieve this in the opposite direction,
11429 : * i.e., creating a foreign key that references a partition. This
11430 : * restriction allows us to dodge some complexities around
11431 : * pg_constraint and pg_trigger row creations that would be needed
11432 : * during ATTACH/DETACH for this kind of relationship.
11433 : */
11434 1310 : if (fk->confrelid == RelationGetRelid(partRel))
11435 6 : ereport(ERROR,
11436 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11437 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11438 : RelationGetRelationName(partRel),
11439 : get_constraint_name(fk->conoid))));
11440 :
11441 1304 : clone = lappend_oid(clone, fk->conoid);
11442 : }
11443 :
11444 : /*
11445 : * Silently do nothing if there's nothing to do. In particular, this
11446 : * avoids throwing a spurious error for foreign tables.
11447 : */
11448 9974 : if (clone == NIL)
11449 9430 : return;
11450 :
11451 544 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11452 0 : ereport(ERROR,
11453 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11454 : errmsg("foreign key constraints are not supported on foreign tables")));
11455 :
11456 : /*
11457 : * Triggers of the foreign keys will be manipulated a bunch of times in
11458 : * the loop below. To avoid repeatedly opening/closing the trigger
11459 : * catalog relation, we open it here and pass it to the subroutines called
11460 : * below.
11461 : */
11462 544 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11463 :
11464 : /*
11465 : * The constraint key may differ, if the columns in the partition are
11466 : * different. This map is used to convert them.
11467 : */
11468 544 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11469 : RelationGetDescr(parentRel),
11470 : false);
11471 :
11472 544 : partFKs = copyObject(RelationGetFKeyList(partRel));
11473 :
11474 1836 : foreach(cell, clone)
11475 : {
11476 1304 : Oid parentConstrOid = lfirst_oid(cell);
11477 : Form_pg_constraint constrForm;
11478 : Relation pkrel;
11479 : HeapTuple tuple;
11480 : int numfks;
11481 : AttrNumber conkey[INDEX_MAX_KEYS];
11482 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11483 : AttrNumber confkey[INDEX_MAX_KEYS];
11484 : Oid conpfeqop[INDEX_MAX_KEYS];
11485 : Oid conppeqop[INDEX_MAX_KEYS];
11486 : Oid conffeqop[INDEX_MAX_KEYS];
11487 : int numfkdelsetcols;
11488 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11489 : Constraint *fkconstraint;
11490 : bool attached;
11491 : Oid indexOid;
11492 : ObjectAddress address;
11493 : ListCell *lc;
11494 1304 : Oid insertTriggerOid = InvalidOid,
11495 1304 : updateTriggerOid = InvalidOid;
11496 : bool with_period;
11497 :
11498 1304 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11499 1304 : if (!HeapTupleIsValid(tuple))
11500 0 : elog(ERROR, "cache lookup failed for constraint %u",
11501 : parentConstrOid);
11502 1304 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11503 :
11504 : /* Don't clone constraints whose parents are being cloned */
11505 1304 : if (list_member_oid(clone, constrForm->conparentid))
11506 : {
11507 724 : ReleaseSysCache(tuple);
11508 874 : continue;
11509 : }
11510 :
11511 : /*
11512 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11513 : * relation, that means to lock all partitions.
11514 : */
11515 580 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11516 580 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11517 250 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11518 : ShareRowExclusiveLock, NULL);
11519 :
11520 580 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11521 : conpfeqop, conppeqop, conffeqop,
11522 : &numfkdelsetcols, confdelsetcols);
11523 1394 : for (int i = 0; i < numfks; i++)
11524 814 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11525 :
11526 : /*
11527 : * Get the "check" triggers belonging to the constraint, if it is
11528 : * ENFORCED, to pass as parent OIDs for similar triggers that will be
11529 : * created on the partition in addFkRecurseReferencing(). They are
11530 : * also passed to tryAttachPartitionForeignKey() below to simply
11531 : * assign as parents to the partition's existing "check" triggers,
11532 : * that is, if the corresponding constraints is deemed attachable to
11533 : * the parent constraint.
11534 : */
11535 580 : if (constrForm->conenforced)
11536 568 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11537 : constrForm->confrelid, constrForm->conrelid,
11538 : &insertTriggerOid, &updateTriggerOid);
11539 :
11540 : /*
11541 : * Before creating a new constraint, see whether any existing FKs are
11542 : * fit for the purpose. If one is, attach the parent constraint to
11543 : * it, and don't clone anything. This way we avoid the expensive
11544 : * verification step and don't end up with a duplicate FK, and we
11545 : * don't need to recurse to partitions for this constraint.
11546 : */
11547 580 : attached = false;
11548 670 : foreach(lc, partFKs)
11549 : {
11550 246 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11551 :
11552 246 : if (tryAttachPartitionForeignKey(wqueue,
11553 : fk,
11554 : partRel,
11555 : parentConstrOid,
11556 : numfks,
11557 : mapped_conkey,
11558 : confkey,
11559 : conpfeqop,
11560 : insertTriggerOid,
11561 : updateTriggerOid,
11562 : trigrel))
11563 : {
11564 150 : attached = true;
11565 150 : table_close(pkrel, NoLock);
11566 150 : break;
11567 : }
11568 : }
11569 574 : if (attached)
11570 : {
11571 150 : ReleaseSysCache(tuple);
11572 150 : continue;
11573 : }
11574 :
11575 : /* No dice. Set up to create our own constraint */
11576 424 : fkconstraint = makeNode(Constraint);
11577 424 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11578 : /* ->conname determined below */
11579 424 : fkconstraint->deferrable = constrForm->condeferrable;
11580 424 : fkconstraint->initdeferred = constrForm->condeferred;
11581 424 : fkconstraint->location = -1;
11582 424 : fkconstraint->pktable = NULL;
11583 : /* ->fk_attrs determined below */
11584 424 : fkconstraint->pk_attrs = NIL;
11585 424 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11586 424 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11587 424 : fkconstraint->fk_del_action = constrForm->confdeltype;
11588 424 : fkconstraint->fk_del_set_cols = NIL;
11589 424 : fkconstraint->old_conpfeqop = NIL;
11590 424 : fkconstraint->old_pktable_oid = InvalidOid;
11591 424 : fkconstraint->is_enforced = constrForm->conenforced;
11592 424 : fkconstraint->skip_validation = false;
11593 424 : fkconstraint->initially_valid = constrForm->convalidated;
11594 968 : for (int i = 0; i < numfks; i++)
11595 : {
11596 : Form_pg_attribute att;
11597 :
11598 544 : att = TupleDescAttr(RelationGetDescr(partRel),
11599 544 : mapped_conkey[i] - 1);
11600 544 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11601 544 : makeString(NameStr(att->attname)));
11602 : }
11603 :
11604 424 : indexOid = constrForm->conindid;
11605 424 : with_period = constrForm->conperiod;
11606 :
11607 : /* Create the pg_constraint entry at this level */
11608 424 : address = addFkConstraint(addFkReferencingSide,
11609 424 : NameStr(constrForm->conname), fkconstraint,
11610 : partRel, pkrel, indexOid, parentConstrOid,
11611 : numfks, confkey,
11612 : mapped_conkey, conpfeqop,
11613 : conppeqop, conffeqop,
11614 : numfkdelsetcols, confdelsetcols,
11615 : false, with_period);
11616 :
11617 : /* Done with the cloned constraint's tuple */
11618 424 : ReleaseSysCache(tuple);
11619 :
11620 : /* Create the check triggers, and recurse to partitions, if any */
11621 424 : addFkRecurseReferencing(wqueue,
11622 : fkconstraint,
11623 : partRel,
11624 : pkrel,
11625 : indexOid,
11626 : address.objectId,
11627 : numfks,
11628 : confkey,
11629 : mapped_conkey,
11630 : conpfeqop,
11631 : conppeqop,
11632 : conffeqop,
11633 : numfkdelsetcols,
11634 : confdelsetcols,
11635 : false, /* no old check exists */
11636 : AccessExclusiveLock,
11637 : insertTriggerOid,
11638 : updateTriggerOid,
11639 : with_period);
11640 418 : table_close(pkrel, NoLock);
11641 : }
11642 :
11643 532 : table_close(trigrel, RowExclusiveLock);
11644 : }
11645 :
11646 : /*
11647 : * When the parent of a partition receives [the referencing side of] a foreign
11648 : * key, we must propagate that foreign key to the partition. However, the
11649 : * partition might already have an equivalent foreign key; this routine
11650 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11651 : * by the other parameters. If they are equivalent, create the link between
11652 : * the two constraints and return true.
11653 : *
11654 : * If the given FK does not match the one defined by rest of the params,
11655 : * return false.
11656 : */
11657 : static bool
11658 282 : tryAttachPartitionForeignKey(List **wqueue,
11659 : ForeignKeyCacheInfo *fk,
11660 : Relation partition,
11661 : Oid parentConstrOid,
11662 : int numfks,
11663 : AttrNumber *mapped_conkey,
11664 : AttrNumber *confkey,
11665 : Oid *conpfeqop,
11666 : Oid parentInsTrigger,
11667 : Oid parentUpdTrigger,
11668 : Relation trigrel)
11669 : {
11670 : HeapTuple parentConstrTup;
11671 : Form_pg_constraint parentConstr;
11672 : HeapTuple partcontup;
11673 : Form_pg_constraint partConstr;
11674 :
11675 282 : parentConstrTup = SearchSysCache1(CONSTROID,
11676 : ObjectIdGetDatum(parentConstrOid));
11677 282 : if (!HeapTupleIsValid(parentConstrTup))
11678 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11679 282 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11680 :
11681 : /*
11682 : * Do some quick & easy initial checks. If any of these fail, we cannot
11683 : * use this constraint.
11684 : */
11685 282 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11686 : {
11687 0 : ReleaseSysCache(parentConstrTup);
11688 0 : return false;
11689 : }
11690 786 : for (int i = 0; i < numfks; i++)
11691 : {
11692 504 : if (fk->conkey[i] != mapped_conkey[i] ||
11693 504 : fk->confkey[i] != confkey[i] ||
11694 504 : fk->conpfeqop[i] != conpfeqop[i])
11695 : {
11696 0 : ReleaseSysCache(parentConstrTup);
11697 0 : return false;
11698 : }
11699 : }
11700 :
11701 : /* Looks good so far; perform more extensive checks. */
11702 282 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11703 282 : if (!HeapTupleIsValid(partcontup))
11704 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11705 282 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11706 :
11707 : /*
11708 : * An error should be raised if the constraint enforceability is
11709 : * different. Returning false without raising an error, as we do for other
11710 : * attributes, could lead to a duplicate constraint with the same
11711 : * enforceability as the parent. While this may be acceptable, it may not
11712 : * be ideal. Therefore, it's better to raise an error and allow the user
11713 : * to correct the enforceability before proceeding.
11714 : */
11715 282 : if (partConstr->conenforced != parentConstr->conenforced)
11716 6 : ereport(ERROR,
11717 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11718 : errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11719 : NameStr(parentConstr->conname),
11720 : NameStr(partConstr->conname),
11721 : RelationGetRelationName(partition))));
11722 :
11723 276 : if (OidIsValid(partConstr->conparentid) ||
11724 240 : partConstr->condeferrable != parentConstr->condeferrable ||
11725 212 : partConstr->condeferred != parentConstr->condeferred ||
11726 212 : partConstr->confupdtype != parentConstr->confupdtype ||
11727 176 : partConstr->confdeltype != parentConstr->confdeltype ||
11728 176 : partConstr->confmatchtype != parentConstr->confmatchtype)
11729 : {
11730 114 : ReleaseSysCache(parentConstrTup);
11731 114 : ReleaseSysCache(partcontup);
11732 114 : return false;
11733 : }
11734 :
11735 162 : ReleaseSysCache(parentConstrTup);
11736 162 : ReleaseSysCache(partcontup);
11737 :
11738 : /* Looks good! Attach this constraint. */
11739 162 : AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11740 : parentConstrOid, parentInsTrigger,
11741 : parentUpdTrigger, trigrel);
11742 :
11743 162 : return true;
11744 : }
11745 :
11746 : /*
11747 : * AttachPartitionForeignKey
11748 : *
11749 : * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11750 : * attaching the constraint, removing redundant triggers and entries from
11751 : * pg_constraint, and setting the constraint's parent.
11752 : */
11753 : static void
11754 162 : AttachPartitionForeignKey(List **wqueue,
11755 : Relation partition,
11756 : Oid partConstrOid,
11757 : Oid parentConstrOid,
11758 : Oid parentInsTrigger,
11759 : Oid parentUpdTrigger,
11760 : Relation trigrel)
11761 : {
11762 : HeapTuple parentConstrTup;
11763 : Form_pg_constraint parentConstr;
11764 : HeapTuple partcontup;
11765 : Form_pg_constraint partConstr;
11766 : bool queueValidation;
11767 : Oid partConstrFrelid;
11768 : Oid partConstrRelid;
11769 : bool parentConstrIsEnforced;
11770 :
11771 : /* Fetch the parent constraint tuple */
11772 162 : parentConstrTup = SearchSysCache1(CONSTROID,
11773 : ObjectIdGetDatum(parentConstrOid));
11774 162 : if (!HeapTupleIsValid(parentConstrTup))
11775 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11776 162 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11777 162 : parentConstrIsEnforced = parentConstr->conenforced;
11778 :
11779 : /* Fetch the child constraint tuple */
11780 162 : partcontup = SearchSysCache1(CONSTROID,
11781 : ObjectIdGetDatum(partConstrOid));
11782 162 : if (!HeapTupleIsValid(partcontup))
11783 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11784 162 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11785 162 : partConstrFrelid = partConstr->confrelid;
11786 162 : partConstrRelid = partConstr->conrelid;
11787 :
11788 : /*
11789 : * If the referenced table is partitioned, then the partition we're
11790 : * attaching now has extra pg_constraint rows and action triggers that are
11791 : * no longer needed. Remove those.
11792 : */
11793 162 : if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11794 : {
11795 36 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11796 :
11797 36 : RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11798 : partConstrRelid);
11799 :
11800 36 : table_close(pg_constraint, RowShareLock);
11801 : }
11802 :
11803 : /*
11804 : * Will we need to validate this constraint? A valid parent constraint
11805 : * implies that all child constraints have been validated, so if this one
11806 : * isn't, we must trigger phase 3 validation.
11807 : */
11808 162 : queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11809 :
11810 162 : ReleaseSysCache(partcontup);
11811 162 : ReleaseSysCache(parentConstrTup);
11812 :
11813 : /*
11814 : * The action triggers in the new partition become redundant -- the parent
11815 : * table already has equivalent ones, and those will be able to reach the
11816 : * partition. Remove the ones in the partition. We identify them because
11817 : * they have our constraint OID, as well as being on the referenced rel.
11818 : */
11819 162 : DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11820 : partConstrRelid);
11821 :
11822 162 : ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11823 : RelationGetRelid(partition));
11824 :
11825 : /*
11826 : * Like the constraint, attach partition's "check" triggers to the
11827 : * corresponding parent triggers if the constraint is ENFORCED. NOT
11828 : * ENFORCED constraints do not have these triggers.
11829 : */
11830 162 : if (parentConstrIsEnforced)
11831 : {
11832 : Oid insertTriggerOid,
11833 : updateTriggerOid;
11834 :
11835 150 : GetForeignKeyCheckTriggers(trigrel,
11836 : partConstrOid, partConstrFrelid, partConstrRelid,
11837 : &insertTriggerOid, &updateTriggerOid);
11838 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11839 150 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11840 : RelationGetRelid(partition));
11841 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11842 150 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11843 : RelationGetRelid(partition));
11844 : }
11845 :
11846 : /*
11847 : * We updated this pg_constraint row above to set its parent; validating
11848 : * it will cause its convalidated flag to change, so we need CCI here. In
11849 : * addition, we need it unconditionally for the rare case where the parent
11850 : * table has *two* identical constraints; when reaching this function for
11851 : * the second one, we must have made our changes visible, otherwise we
11852 : * would try to attach both to this one.
11853 : */
11854 162 : CommandCounterIncrement();
11855 :
11856 : /* If validation is needed, put it in the queue now. */
11857 162 : if (queueValidation)
11858 : {
11859 : Relation conrel;
11860 : Oid confrelid;
11861 :
11862 18 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11863 :
11864 18 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11865 18 : if (!HeapTupleIsValid(partcontup))
11866 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11867 :
11868 18 : confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11869 :
11870 : /* Use the same lock as for AT_ValidateConstraint */
11871 18 : QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11872 : partcontup, ShareUpdateExclusiveLock);
11873 18 : ReleaseSysCache(partcontup);
11874 18 : table_close(conrel, RowExclusiveLock);
11875 : }
11876 162 : }
11877 :
11878 : /*
11879 : * RemoveInheritedConstraint
11880 : *
11881 : * Removes the constraint and its associated trigger from the specified
11882 : * relation, which inherited the given constraint.
11883 : */
11884 : static void
11885 36 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
11886 : Oid conrelid)
11887 : {
11888 : ObjectAddresses *objs;
11889 : HeapTuple consttup;
11890 : ScanKeyData key;
11891 : SysScanDesc scan;
11892 : HeapTuple trigtup;
11893 :
11894 36 : ScanKeyInit(&key,
11895 : Anum_pg_constraint_conrelid,
11896 : BTEqualStrategyNumber, F_OIDEQ,
11897 : ObjectIdGetDatum(conrelid));
11898 :
11899 36 : scan = systable_beginscan(conrel,
11900 : ConstraintRelidTypidNameIndexId,
11901 : true, NULL, 1, &key);
11902 36 : objs = new_object_addresses();
11903 324 : while ((consttup = systable_getnext(scan)) != NULL)
11904 : {
11905 288 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11906 :
11907 288 : if (conform->conparentid != conoid)
11908 210 : continue;
11909 : else
11910 : {
11911 : ObjectAddress addr;
11912 : SysScanDesc scan2;
11913 : ScanKeyData key2;
11914 : int n PG_USED_FOR_ASSERTS_ONLY;
11915 :
11916 78 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11917 78 : add_exact_object_address(&addr, objs);
11918 :
11919 : /*
11920 : * First we must delete the dependency record that binds the
11921 : * constraint records together.
11922 : */
11923 78 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11924 : conform->oid,
11925 : DEPENDENCY_INTERNAL,
11926 : ConstraintRelationId,
11927 : conoid);
11928 : Assert(n == 1); /* actually only one is expected */
11929 :
11930 : /*
11931 : * Now search for the triggers for this constraint and set them up
11932 : * for deletion too
11933 : */
11934 78 : ScanKeyInit(&key2,
11935 : Anum_pg_trigger_tgconstraint,
11936 : BTEqualStrategyNumber, F_OIDEQ,
11937 : ObjectIdGetDatum(conform->oid));
11938 78 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11939 : true, NULL, 1, &key2);
11940 234 : while ((trigtup = systable_getnext(scan2)) != NULL)
11941 : {
11942 156 : ObjectAddressSet(addr, TriggerRelationId,
11943 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11944 156 : add_exact_object_address(&addr, objs);
11945 : }
11946 78 : systable_endscan(scan2);
11947 : }
11948 : }
11949 : /* make the dependency deletions visible */
11950 36 : CommandCounterIncrement();
11951 36 : performMultipleDeletions(objs, DROP_RESTRICT,
11952 : PERFORM_DELETION_INTERNAL);
11953 36 : systable_endscan(scan);
11954 36 : }
11955 :
11956 : /*
11957 : * DropForeignKeyConstraintTriggers
11958 : *
11959 : * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11960 : * action triggers for the foreign key constraint.
11961 : *
11962 : * If valid confrelid and conrelid values are not provided, the respective
11963 : * trigger check will be skipped, and the trigger will be considered for
11964 : * removal.
11965 : */
11966 : static void
11967 234 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
11968 : Oid conrelid)
11969 : {
11970 : ScanKeyData key;
11971 : SysScanDesc scan;
11972 : HeapTuple trigtup;
11973 :
11974 234 : ScanKeyInit(&key,
11975 : Anum_pg_trigger_tgconstraint,
11976 : BTEqualStrategyNumber, F_OIDEQ,
11977 : ObjectIdGetDatum(conoid));
11978 234 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11979 : NULL, 1, &key);
11980 1014 : while ((trigtup = systable_getnext(scan)) != NULL)
11981 : {
11982 780 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11983 : ObjectAddress trigger;
11984 :
11985 : /* Invalid if trigger is not for a referential integrity constraint */
11986 780 : if (!OidIsValid(trgform->tgconstrrelid))
11987 300 : continue;
11988 780 : if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
11989 300 : continue;
11990 480 : if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
11991 0 : continue;
11992 :
11993 : /* We should be dropping trigger related to foreign key constraint */
11994 : Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
11995 : trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
11996 : trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
11997 : trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
11998 : trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
11999 : trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12000 : trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12001 : trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12002 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12003 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12004 : trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12005 : trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12006 :
12007 : /*
12008 : * The constraint is originally set up to contain this trigger as an
12009 : * implementation object, so there's a dependency record that links
12010 : * the two; however, since the trigger is no longer needed, we remove
12011 : * the dependency link in order to be able to drop the trigger while
12012 : * keeping the constraint intact.
12013 : */
12014 480 : deleteDependencyRecordsFor(TriggerRelationId,
12015 : trgform->oid,
12016 : false);
12017 : /* make dependency deletion visible to performDeletion */
12018 480 : CommandCounterIncrement();
12019 480 : ObjectAddressSet(trigger, TriggerRelationId,
12020 : trgform->oid);
12021 480 : performDeletion(&trigger, DROP_RESTRICT, 0);
12022 : /* make trigger drop visible, in case the loop iterates */
12023 480 : CommandCounterIncrement();
12024 : }
12025 :
12026 234 : systable_endscan(scan);
12027 234 : }
12028 :
12029 : /*
12030 : * GetForeignKeyActionTriggers
12031 : * Returns delete and update "action" triggers of the given relation
12032 : * belonging to the given constraint
12033 : */
12034 : static void
12035 222 : GetForeignKeyActionTriggers(Relation trigrel,
12036 : Oid conoid, Oid confrelid, Oid conrelid,
12037 : Oid *deleteTriggerOid,
12038 : Oid *updateTriggerOid)
12039 : {
12040 : ScanKeyData key;
12041 : SysScanDesc scan;
12042 : HeapTuple trigtup;
12043 :
12044 222 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12045 222 : ScanKeyInit(&key,
12046 : Anum_pg_trigger_tgconstraint,
12047 : BTEqualStrategyNumber, F_OIDEQ,
12048 : ObjectIdGetDatum(conoid));
12049 :
12050 222 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12051 : NULL, 1, &key);
12052 458 : while ((trigtup = systable_getnext(scan)) != NULL)
12053 : {
12054 458 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12055 :
12056 458 : if (trgform->tgconstrrelid != conrelid)
12057 10 : continue;
12058 448 : if (trgform->tgrelid != confrelid)
12059 0 : continue;
12060 : /* Only ever look at "action" triggers on the PK side. */
12061 448 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12062 4 : continue;
12063 444 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
12064 : {
12065 : Assert(*deleteTriggerOid == InvalidOid);
12066 222 : *deleteTriggerOid = trgform->oid;
12067 : }
12068 222 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12069 : {
12070 : Assert(*updateTriggerOid == InvalidOid);
12071 222 : *updateTriggerOid = trgform->oid;
12072 : }
12073 : #ifndef USE_ASSERT_CHECKING
12074 : /* In an assert-enabled build, continue looking to find duplicates */
12075 444 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12076 222 : break;
12077 : #endif
12078 : }
12079 :
12080 222 : if (!OidIsValid(*deleteTriggerOid))
12081 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12082 : conoid);
12083 222 : if (!OidIsValid(*updateTriggerOid))
12084 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12085 : conoid);
12086 :
12087 222 : systable_endscan(scan);
12088 222 : }
12089 :
12090 : /*
12091 : * GetForeignKeyCheckTriggers
12092 : * Returns insert and update "check" triggers of the given relation
12093 : * belonging to the given constraint
12094 : */
12095 : static void
12096 820 : GetForeignKeyCheckTriggers(Relation trigrel,
12097 : Oid conoid, Oid confrelid, Oid conrelid,
12098 : Oid *insertTriggerOid,
12099 : Oid *updateTriggerOid)
12100 : {
12101 : ScanKeyData key;
12102 : SysScanDesc scan;
12103 : HeapTuple trigtup;
12104 :
12105 820 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
12106 820 : ScanKeyInit(&key,
12107 : Anum_pg_trigger_tgconstraint,
12108 : BTEqualStrategyNumber, F_OIDEQ,
12109 : ObjectIdGetDatum(conoid));
12110 :
12111 820 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12112 : NULL, 1, &key);
12113 2628 : while ((trigtup = systable_getnext(scan)) != NULL)
12114 : {
12115 2628 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12116 :
12117 2628 : if (trgform->tgconstrrelid != confrelid)
12118 884 : continue;
12119 1744 : if (trgform->tgrelid != conrelid)
12120 0 : continue;
12121 : /* Only ever look at "check" triggers on the FK side. */
12122 1744 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12123 104 : continue;
12124 1640 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
12125 : {
12126 : Assert(*insertTriggerOid == InvalidOid);
12127 820 : *insertTriggerOid = trgform->oid;
12128 : }
12129 820 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12130 : {
12131 : Assert(*updateTriggerOid == InvalidOid);
12132 820 : *updateTriggerOid = trgform->oid;
12133 : }
12134 : #ifndef USE_ASSERT_CHECKING
12135 : /* In an assert-enabled build, continue looking to find duplicates. */
12136 1640 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12137 820 : break;
12138 : #endif
12139 : }
12140 :
12141 820 : if (!OidIsValid(*insertTriggerOid))
12142 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12143 : conoid);
12144 820 : if (!OidIsValid(*updateTriggerOid))
12145 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12146 : conoid);
12147 :
12148 820 : systable_endscan(scan);
12149 820 : }
12150 :
12151 : /*
12152 : * ALTER TABLE ALTER CONSTRAINT
12153 : *
12154 : * Update the attributes of a constraint.
12155 : *
12156 : * Currently only works for Foreign Key and not null constraints.
12157 : *
12158 : * If the constraint is modified, returns its address; otherwise, return
12159 : * InvalidObjectAddress.
12160 : */
12161 : static ObjectAddress
12162 288 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
12163 : bool recurse, LOCKMODE lockmode)
12164 : {
12165 : Relation conrel;
12166 : Relation tgrel;
12167 : SysScanDesc scan;
12168 : ScanKeyData skey[3];
12169 : HeapTuple contuple;
12170 : Form_pg_constraint currcon;
12171 : ObjectAddress address;
12172 :
12173 : /*
12174 : * Disallow altering ONLY a partitioned table, as it would make no sense.
12175 : * This is okay for legacy inheritance.
12176 : */
12177 288 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12178 0 : ereport(ERROR,
12179 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12180 : errmsg("constraint must be altered in child tables too"),
12181 : errhint("Do not specify the ONLY keyword."));
12182 :
12183 :
12184 288 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12185 288 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12186 :
12187 : /*
12188 : * Find and check the target constraint
12189 : */
12190 288 : ScanKeyInit(&skey[0],
12191 : Anum_pg_constraint_conrelid,
12192 : BTEqualStrategyNumber, F_OIDEQ,
12193 : ObjectIdGetDatum(RelationGetRelid(rel)));
12194 288 : ScanKeyInit(&skey[1],
12195 : Anum_pg_constraint_contypid,
12196 : BTEqualStrategyNumber, F_OIDEQ,
12197 : ObjectIdGetDatum(InvalidOid));
12198 288 : ScanKeyInit(&skey[2],
12199 : Anum_pg_constraint_conname,
12200 : BTEqualStrategyNumber, F_NAMEEQ,
12201 288 : CStringGetDatum(cmdcon->conname));
12202 288 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12203 : true, NULL, 3, skey);
12204 :
12205 : /* There can be at most one matching row */
12206 288 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12207 6 : ereport(ERROR,
12208 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12209 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12210 : cmdcon->conname, RelationGetRelationName(rel))));
12211 :
12212 282 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12213 282 : if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12214 0 : ereport(ERROR,
12215 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12216 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12217 : cmdcon->conname, RelationGetRelationName(rel))));
12218 282 : if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12219 12 : ereport(ERROR,
12220 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12221 : errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12222 : cmdcon->conname, RelationGetRelationName(rel))));
12223 270 : if (cmdcon->alterInheritability &&
12224 90 : currcon->contype != CONSTRAINT_NOTNULL)
12225 24 : ereport(ERROR,
12226 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12227 : errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12228 : cmdcon->conname, RelationGetRelationName(rel)));
12229 :
12230 : /* Refuse to modify inheritability of inherited constraints */
12231 246 : if (cmdcon->alterInheritability &&
12232 66 : cmdcon->noinherit && currcon->coninhcount > 0)
12233 6 : ereport(ERROR,
12234 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12235 : errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12236 : NameStr(currcon->conname),
12237 : RelationGetRelationName(rel)));
12238 :
12239 : /*
12240 : * If it's not the topmost constraint, raise an error.
12241 : *
12242 : * Altering a non-topmost constraint leaves some triggers untouched, since
12243 : * they are not directly connected to this constraint; also, pg_dump would
12244 : * ignore the deferrability status of the individual constraint, since it
12245 : * only dumps topmost constraints. Avoid these problems by refusing this
12246 : * operation and telling the user to alter the parent constraint instead.
12247 : */
12248 240 : if (OidIsValid(currcon->conparentid))
12249 : {
12250 : HeapTuple tp;
12251 12 : Oid parent = currcon->conparentid;
12252 12 : char *ancestorname = NULL;
12253 12 : char *ancestortable = NULL;
12254 :
12255 : /* Loop to find the topmost constraint */
12256 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12257 : {
12258 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
12259 :
12260 : /* If no parent, this is the constraint we want */
12261 24 : if (!OidIsValid(contup->conparentid))
12262 : {
12263 12 : ancestorname = pstrdup(NameStr(contup->conname));
12264 12 : ancestortable = get_rel_name(contup->conrelid);
12265 12 : ReleaseSysCache(tp);
12266 12 : break;
12267 : }
12268 :
12269 12 : parent = contup->conparentid;
12270 12 : ReleaseSysCache(tp);
12271 : }
12272 :
12273 12 : ereport(ERROR,
12274 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12275 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12276 : cmdcon->conname, RelationGetRelationName(rel)),
12277 : ancestorname && ancestortable ?
12278 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12279 : cmdcon->conname, ancestorname, ancestortable) : 0,
12280 : errhint("You may alter the constraint it derives from instead.")));
12281 : }
12282 :
12283 228 : address = InvalidObjectAddress;
12284 :
12285 : /*
12286 : * Do the actual catalog work, and recurse if necessary.
12287 : */
12288 228 : if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12289 : contuple, recurse, lockmode))
12290 216 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12291 :
12292 222 : systable_endscan(scan);
12293 :
12294 222 : table_close(tgrel, RowExclusiveLock);
12295 222 : table_close(conrel, RowExclusiveLock);
12296 :
12297 222 : return address;
12298 : }
12299 :
12300 : /*
12301 : * A subroutine of ATExecAlterConstraint that calls the respective routines for
12302 : * altering constraint's enforceability, deferrability or inheritability.
12303 : */
12304 : static bool
12305 228 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
12306 : Relation conrel, Relation tgrel, Relation rel,
12307 : HeapTuple contuple, bool recurse,
12308 : LOCKMODE lockmode)
12309 : {
12310 : Form_pg_constraint currcon;
12311 228 : bool changed = false;
12312 228 : List *otherrelids = NIL;
12313 :
12314 228 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12315 :
12316 : /*
12317 : * Do the catalog work for the enforceability or deferrability change,
12318 : * recurse if necessary.
12319 : *
12320 : * Note that even if deferrability is requested to be altered along with
12321 : * enforceability, we don't need to explicitly update multiple entries in
12322 : * pg_trigger related to deferrability.
12323 : *
12324 : * Modifying enforceability involves either creating or dropping the
12325 : * trigger, during which the deferrability setting will be adjusted
12326 : * automatically.
12327 : */
12328 300 : if (cmdcon->alterEnforceability &&
12329 72 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12330 : currcon->conrelid, currcon->confrelid,
12331 : contuple, lockmode, InvalidOid,
12332 : InvalidOid, InvalidOid, InvalidOid))
12333 66 : changed = true;
12334 :
12335 258 : else if (cmdcon->alterDeferrability &&
12336 96 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12337 : contuple, recurse, &otherrelids,
12338 : lockmode))
12339 : {
12340 : /*
12341 : * AlterConstrUpdateConstraintEntry already invalidated relcache for
12342 : * the relations having the constraint itself; here we also invalidate
12343 : * for relations that have any triggers that are part of the
12344 : * constraint.
12345 : */
12346 306 : foreach_oid(relid, otherrelids)
12347 114 : CacheInvalidateRelcacheByRelid(relid);
12348 :
12349 96 : changed = true;
12350 : }
12351 :
12352 : /*
12353 : * Do the catalog work for the inheritability change.
12354 : */
12355 282 : if (cmdcon->alterInheritability &&
12356 60 : ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12357 : lockmode))
12358 54 : changed = true;
12359 :
12360 222 : return changed;
12361 : }
12362 :
12363 : /*
12364 : * Returns true if the constraint's enforceability is altered.
12365 : *
12366 : * Depending on whether the constraint is being set to ENFORCED or NOT
12367 : * ENFORCED, it creates or drops the trigger accordingly.
12368 : *
12369 : * Note that we must recurse even when trying to change a constraint to not
12370 : * enforced if it is already not enforced, in case descendant constraints
12371 : * might be enforced and need to be changed to not enforced. Conversely, we
12372 : * should do nothing if a constraint is being set to enforced and is already
12373 : * enforced, as descendant constraints cannot be different in that case.
12374 : */
12375 : static bool
12376 168 : ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
12377 : Relation conrel, Relation tgrel,
12378 : Oid fkrelid, Oid pkrelid,
12379 : HeapTuple contuple, LOCKMODE lockmode,
12380 : Oid ReferencedParentDelTrigger,
12381 : Oid ReferencedParentUpdTrigger,
12382 : Oid ReferencingParentInsTrigger,
12383 : Oid ReferencingParentUpdTrigger)
12384 : {
12385 : Form_pg_constraint currcon;
12386 : Oid conoid;
12387 : Relation rel;
12388 168 : bool changed = false;
12389 :
12390 : /* Since this function recurses, it could be driven to stack overflow */
12391 168 : check_stack_depth();
12392 :
12393 : Assert(cmdcon->alterEnforceability);
12394 :
12395 168 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12396 168 : conoid = currcon->oid;
12397 :
12398 : /* Should be foreign key constraint */
12399 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12400 :
12401 168 : rel = table_open(currcon->conrelid, lockmode);
12402 :
12403 168 : if (currcon->conenforced != cmdcon->is_enforced)
12404 : {
12405 162 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12406 162 : changed = true;
12407 : }
12408 :
12409 : /* Drop triggers */
12410 168 : if (!cmdcon->is_enforced)
12411 : {
12412 : /*
12413 : * When setting a constraint to NOT ENFORCED, the constraint triggers
12414 : * need to be dropped. Therefore, we must process the child relations
12415 : * first, followed by the parent, to account for dependencies.
12416 : */
12417 126 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12418 54 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12419 18 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12420 : fkrelid, pkrelid, contuple,
12421 : lockmode, InvalidOid, InvalidOid,
12422 : InvalidOid, InvalidOid);
12423 :
12424 : /* Drop all the triggers */
12425 72 : DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
12426 : }
12427 96 : else if (changed) /* Create triggers */
12428 : {
12429 96 : Oid ReferencedDelTriggerOid = InvalidOid,
12430 96 : ReferencedUpdTriggerOid = InvalidOid,
12431 96 : ReferencingInsTriggerOid = InvalidOid,
12432 96 : ReferencingUpdTriggerOid = InvalidOid;
12433 :
12434 : /* Prepare the minimal information required for trigger creation. */
12435 96 : Constraint *fkconstraint = makeNode(Constraint);
12436 :
12437 96 : fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12438 96 : fkconstraint->fk_matchtype = currcon->confmatchtype;
12439 96 : fkconstraint->fk_upd_action = currcon->confupdtype;
12440 96 : fkconstraint->fk_del_action = currcon->confdeltype;
12441 :
12442 : /* Create referenced triggers */
12443 96 : if (currcon->conrelid == fkrelid)
12444 54 : createForeignKeyActionTriggers(currcon->conrelid,
12445 : currcon->confrelid,
12446 : fkconstraint,
12447 : conoid,
12448 : currcon->conindid,
12449 : ReferencedParentDelTrigger,
12450 : ReferencedParentUpdTrigger,
12451 : &ReferencedDelTriggerOid,
12452 : &ReferencedUpdTriggerOid);
12453 :
12454 : /* Create referencing triggers */
12455 96 : if (currcon->confrelid == pkrelid)
12456 84 : createForeignKeyCheckTriggers(currcon->conrelid,
12457 : pkrelid,
12458 : fkconstraint,
12459 : conoid,
12460 : currcon->conindid,
12461 : ReferencingParentInsTrigger,
12462 : ReferencingParentUpdTrigger,
12463 : &ReferencingInsTriggerOid,
12464 : &ReferencingUpdTriggerOid);
12465 :
12466 : /*
12467 : * Tell Phase 3 to check that the constraint is satisfied by existing
12468 : * rows. Only applies to leaf partitions, and (for constraints that
12469 : * reference a partitioned table) only if this is not one of the
12470 : * pg_constraint rows that exist solely to support action triggers.
12471 : */
12472 96 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
12473 78 : currcon->confrelid == pkrelid)
12474 : {
12475 : AlteredTableInfo *tab;
12476 : NewConstraint *newcon;
12477 :
12478 66 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12479 66 : newcon->name = fkconstraint->conname;
12480 66 : newcon->contype = CONSTR_FOREIGN;
12481 66 : newcon->refrelid = currcon->confrelid;
12482 66 : newcon->refindid = currcon->conindid;
12483 66 : newcon->conid = currcon->oid;
12484 66 : newcon->qual = (Node *) fkconstraint;
12485 :
12486 : /* Find or create work queue entry for this table */
12487 66 : tab = ATGetQueueEntry(wqueue, rel);
12488 66 : tab->constraints = lappend(tab->constraints, newcon);
12489 : }
12490 :
12491 : /*
12492 : * If the table at either end of the constraint is partitioned, we
12493 : * need to recurse and create triggers for each constraint that is a
12494 : * child of this one.
12495 : */
12496 174 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12497 78 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12498 24 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12499 : fkrelid, pkrelid, contuple,
12500 : lockmode, ReferencedDelTriggerOid,
12501 : ReferencedUpdTriggerOid,
12502 : ReferencingInsTriggerOid,
12503 : ReferencingUpdTriggerOid);
12504 : }
12505 :
12506 168 : table_close(rel, NoLock);
12507 :
12508 168 : return changed;
12509 : }
12510 :
12511 : /*
12512 : * Returns true if the constraint's deferrability is altered.
12513 : *
12514 : * *otherrelids is appended OIDs of relations containing affected triggers.
12515 : *
12516 : * Note that we must recurse even when the values are correct, in case
12517 : * indirect descendants have had their constraints altered locally.
12518 : * (This could be avoided if we forbade altering constraints in partitions
12519 : * but existing releases don't do that.)
12520 : */
12521 : static bool
12522 162 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
12523 : Relation conrel, Relation tgrel, Relation rel,
12524 : HeapTuple contuple, bool recurse,
12525 : List **otherrelids, LOCKMODE lockmode)
12526 : {
12527 : Form_pg_constraint currcon;
12528 : Oid refrelid;
12529 162 : bool changed = false;
12530 :
12531 : /* since this function recurses, it could be driven to stack overflow */
12532 162 : check_stack_depth();
12533 :
12534 : Assert(cmdcon->alterDeferrability);
12535 :
12536 162 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12537 162 : refrelid = currcon->confrelid;
12538 :
12539 : /* Should be foreign key constraint */
12540 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12541 :
12542 : /*
12543 : * If called to modify a constraint that's already in the desired state,
12544 : * silently do nothing.
12545 : */
12546 162 : if (currcon->condeferrable != cmdcon->deferrable ||
12547 6 : currcon->condeferred != cmdcon->initdeferred)
12548 : {
12549 162 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12550 162 : changed = true;
12551 :
12552 : /*
12553 : * Now we need to update the multiple entries in pg_trigger that
12554 : * implement the constraint.
12555 : */
12556 162 : AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12557 162 : cmdcon->deferrable,
12558 162 : cmdcon->initdeferred, otherrelids);
12559 : }
12560 :
12561 : /*
12562 : * If the table at either end of the constraint is partitioned, we need to
12563 : * handle every constraint that is a child of this one.
12564 : */
12565 162 : if (recurse && changed &&
12566 300 : (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12567 138 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12568 42 : AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12569 : contuple, recurse, otherrelids,
12570 : lockmode);
12571 :
12572 162 : return changed;
12573 : }
12574 :
12575 : /*
12576 : * Returns true if the constraint's inheritability is altered.
12577 : */
12578 : static bool
12579 60 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
12580 : Relation conrel, Relation rel,
12581 : HeapTuple contuple, LOCKMODE lockmode)
12582 : {
12583 : Form_pg_constraint currcon;
12584 : AttrNumber colNum;
12585 : char *colName;
12586 : List *children;
12587 :
12588 : Assert(cmdcon->alterInheritability);
12589 :
12590 60 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12591 :
12592 : /* The current implementation only works for NOT NULL constraints */
12593 : Assert(currcon->contype == CONSTRAINT_NOTNULL);
12594 :
12595 : /*
12596 : * If called to modify a constraint that's already in the desired state,
12597 : * silently do nothing.
12598 : */
12599 60 : if (cmdcon->noinherit == currcon->connoinherit)
12600 0 : return false;
12601 :
12602 60 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12603 60 : CommandCounterIncrement();
12604 :
12605 : /* Fetch the column number and name */
12606 60 : colNum = extractNotNullColumn(contuple);
12607 60 : colName = get_attname(currcon->conrelid, colNum, false);
12608 :
12609 : /*
12610 : * Propagate the change to children. For this subcommand type we don't
12611 : * recursively affect children, just the immediate level.
12612 : */
12613 60 : children = find_inheritance_children(RelationGetRelid(rel),
12614 : lockmode);
12615 192 : foreach_oid(childoid, children)
12616 : {
12617 : ObjectAddress addr;
12618 :
12619 84 : if (cmdcon->noinherit)
12620 : {
12621 : HeapTuple childtup;
12622 : Form_pg_constraint childcon;
12623 :
12624 30 : childtup = findNotNullConstraint(childoid, colName);
12625 30 : if (!childtup)
12626 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12627 : colName, childoid);
12628 30 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12629 : Assert(childcon->coninhcount > 0);
12630 30 : childcon->coninhcount--;
12631 30 : childcon->conislocal = true;
12632 30 : CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12633 30 : heap_freetuple(childtup);
12634 : }
12635 : else
12636 : {
12637 54 : Relation childrel = table_open(childoid, NoLock);
12638 :
12639 54 : addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12640 : colName, true, true, lockmode);
12641 48 : if (OidIsValid(addr.objectId))
12642 48 : CommandCounterIncrement();
12643 48 : table_close(childrel, NoLock);
12644 : }
12645 : }
12646 :
12647 54 : return true;
12648 : }
12649 :
12650 : /*
12651 : * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12652 : * trigger's deferrability.
12653 : *
12654 : * The arguments to this function have the same meaning as the arguments to
12655 : * ATExecAlterConstrDeferrability.
12656 : */
12657 : static void
12658 162 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12659 : bool deferrable, bool initdeferred,
12660 : List **otherrelids)
12661 : {
12662 : HeapTuple tgtuple;
12663 : ScanKeyData tgkey;
12664 : SysScanDesc tgscan;
12665 :
12666 162 : ScanKeyInit(&tgkey,
12667 : Anum_pg_trigger_tgconstraint,
12668 : BTEqualStrategyNumber, F_OIDEQ,
12669 : ObjectIdGetDatum(conoid));
12670 162 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12671 : NULL, 1, &tgkey);
12672 630 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12673 : {
12674 468 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12675 : Form_pg_trigger copy_tg;
12676 : HeapTuple tgCopyTuple;
12677 :
12678 : /*
12679 : * Remember OIDs of other relation(s) involved in FK constraint.
12680 : * (Note: it's likely that we could skip forcing a relcache inval for
12681 : * other rels that don't have a trigger whose properties change, but
12682 : * let's be conservative.)
12683 : */
12684 468 : if (tgform->tgrelid != RelationGetRelid(rel))
12685 228 : *otherrelids = list_append_unique_oid(*otherrelids,
12686 : tgform->tgrelid);
12687 :
12688 : /*
12689 : * Update enable status and deferrability of RI_FKey_noaction_del,
12690 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12691 : * triggers, but not others; see createForeignKeyActionTriggers and
12692 : * CreateFKCheckTrigger.
12693 : */
12694 468 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12695 372 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12696 258 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12697 138 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12698 18 : continue;
12699 :
12700 450 : tgCopyTuple = heap_copytuple(tgtuple);
12701 450 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12702 :
12703 450 : copy_tg->tgdeferrable = deferrable;
12704 450 : copy_tg->tginitdeferred = initdeferred;
12705 450 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12706 :
12707 450 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12708 :
12709 450 : heap_freetuple(tgCopyTuple);
12710 : }
12711 :
12712 162 : systable_endscan(tgscan);
12713 162 : }
12714 :
12715 : /*
12716 : * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12717 : * the specified constraint.
12718 : *
12719 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12720 : * list of child relations and recursing; instead it uses the conparentid
12721 : * relationships. This may need to be reconsidered.
12722 : *
12723 : * The arguments to this function have the same meaning as the arguments to
12724 : * ATExecAlterConstrEnforceability.
12725 : */
12726 : static void
12727 42 : AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12728 : Relation conrel, Relation tgrel,
12729 : Oid fkrelid, Oid pkrelid,
12730 : HeapTuple contuple, LOCKMODE lockmode,
12731 : Oid ReferencedParentDelTrigger,
12732 : Oid ReferencedParentUpdTrigger,
12733 : Oid ReferencingParentInsTrigger,
12734 : Oid ReferencingParentUpdTrigger)
12735 : {
12736 : Form_pg_constraint currcon;
12737 : Oid conoid;
12738 : ScanKeyData pkey;
12739 : SysScanDesc pscan;
12740 : HeapTuple childtup;
12741 :
12742 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12743 42 : conoid = currcon->oid;
12744 :
12745 42 : ScanKeyInit(&pkey,
12746 : Anum_pg_constraint_conparentid,
12747 : BTEqualStrategyNumber, F_OIDEQ,
12748 : ObjectIdGetDatum(conoid));
12749 :
12750 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12751 : true, NULL, 1, &pkey);
12752 :
12753 138 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12754 96 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12755 : pkrelid, childtup, lockmode,
12756 : ReferencedParentDelTrigger,
12757 : ReferencedParentUpdTrigger,
12758 : ReferencingParentInsTrigger,
12759 : ReferencingParentUpdTrigger);
12760 :
12761 42 : systable_endscan(pscan);
12762 42 : }
12763 :
12764 : /*
12765 : * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12766 : * the specified constraint.
12767 : *
12768 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12769 : * list of child relations and recursing; instead it uses the conparentid
12770 : * relationships. This may need to be reconsidered.
12771 : *
12772 : * The arguments to this function have the same meaning as the arguments to
12773 : * ATExecAlterConstrDeferrability.
12774 : */
12775 : static void
12776 42 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12777 : Relation conrel, Relation tgrel, Relation rel,
12778 : HeapTuple contuple, bool recurse,
12779 : List **otherrelids, LOCKMODE lockmode)
12780 : {
12781 : Form_pg_constraint currcon;
12782 : Oid conoid;
12783 : ScanKeyData pkey;
12784 : SysScanDesc pscan;
12785 : HeapTuple childtup;
12786 :
12787 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12788 42 : conoid = currcon->oid;
12789 :
12790 42 : ScanKeyInit(&pkey,
12791 : Anum_pg_constraint_conparentid,
12792 : BTEqualStrategyNumber, F_OIDEQ,
12793 : ObjectIdGetDatum(conoid));
12794 :
12795 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12796 : true, NULL, 1, &pkey);
12797 :
12798 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12799 : {
12800 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12801 : Relation childrel;
12802 :
12803 66 : childrel = table_open(childcon->conrelid, lockmode);
12804 :
12805 66 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12806 : childtup, recurse, otherrelids, lockmode);
12807 66 : table_close(childrel, NoLock);
12808 : }
12809 :
12810 42 : systable_endscan(pscan);
12811 42 : }
12812 :
12813 : /*
12814 : * Update the constraint entry for the given ATAlterConstraint command, and
12815 : * invoke the appropriate hooks.
12816 : */
12817 : static void
12818 384 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
12819 : HeapTuple contuple)
12820 : {
12821 : HeapTuple copyTuple;
12822 : Form_pg_constraint copy_con;
12823 :
12824 : Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12825 : cmdcon->alterInheritability);
12826 :
12827 384 : copyTuple = heap_copytuple(contuple);
12828 384 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12829 :
12830 384 : if (cmdcon->alterEnforceability)
12831 : {
12832 162 : copy_con->conenforced = cmdcon->is_enforced;
12833 :
12834 : /*
12835 : * NB: The convalidated status is irrelevant when the constraint is
12836 : * set to NOT ENFORCED, but for consistency, it should still be set
12837 : * appropriately. Similarly, if the constraint is later changed to
12838 : * ENFORCED, validation will be performed during phase 3, so it makes
12839 : * sense to mark it as valid in that case.
12840 : */
12841 162 : copy_con->convalidated = cmdcon->is_enforced;
12842 : }
12843 384 : if (cmdcon->alterDeferrability)
12844 : {
12845 168 : copy_con->condeferrable = cmdcon->deferrable;
12846 168 : copy_con->condeferred = cmdcon->initdeferred;
12847 : }
12848 384 : if (cmdcon->alterInheritability)
12849 60 : copy_con->connoinherit = cmdcon->noinherit;
12850 :
12851 384 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12852 384 : InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12853 :
12854 : /* Make new constraint flags visible to others */
12855 384 : CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12856 :
12857 384 : heap_freetuple(copyTuple);
12858 384 : }
12859 :
12860 : /*
12861 : * ALTER TABLE VALIDATE CONSTRAINT
12862 : *
12863 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12864 : * there's no good way to skip recursing when handling foreign keys: there is
12865 : * no need to lock children in that case, yet we wouldn't be able to avoid
12866 : * doing so at that level.
12867 : *
12868 : * Return value is the address of the validated constraint. If the constraint
12869 : * was already validated, InvalidObjectAddress is returned.
12870 : */
12871 : static ObjectAddress
12872 584 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12873 : bool recurse, bool recursing, LOCKMODE lockmode)
12874 : {
12875 : Relation conrel;
12876 : SysScanDesc scan;
12877 : ScanKeyData skey[3];
12878 : HeapTuple tuple;
12879 : Form_pg_constraint con;
12880 : ObjectAddress address;
12881 :
12882 584 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12883 :
12884 : /*
12885 : * Find and check the target constraint
12886 : */
12887 584 : ScanKeyInit(&skey[0],
12888 : Anum_pg_constraint_conrelid,
12889 : BTEqualStrategyNumber, F_OIDEQ,
12890 : ObjectIdGetDatum(RelationGetRelid(rel)));
12891 584 : ScanKeyInit(&skey[1],
12892 : Anum_pg_constraint_contypid,
12893 : BTEqualStrategyNumber, F_OIDEQ,
12894 : ObjectIdGetDatum(InvalidOid));
12895 584 : ScanKeyInit(&skey[2],
12896 : Anum_pg_constraint_conname,
12897 : BTEqualStrategyNumber, F_NAMEEQ,
12898 : CStringGetDatum(constrName));
12899 584 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12900 : true, NULL, 3, skey);
12901 :
12902 : /* There can be at most one matching row */
12903 584 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12904 0 : ereport(ERROR,
12905 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12906 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12907 : constrName, RelationGetRelationName(rel))));
12908 :
12909 584 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12910 584 : if (con->contype != CONSTRAINT_FOREIGN &&
12911 256 : con->contype != CONSTRAINT_CHECK &&
12912 112 : con->contype != CONSTRAINT_NOTNULL)
12913 0 : ereport(ERROR,
12914 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12915 : errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12916 : constrName, RelationGetRelationName(rel)),
12917 : errdetail("This operation is not supported for this type of constraint."));
12918 :
12919 584 : if (!con->conenforced)
12920 6 : ereport(ERROR,
12921 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12922 : errmsg("cannot validate NOT ENFORCED constraint")));
12923 :
12924 578 : if (!con->convalidated)
12925 : {
12926 560 : if (con->contype == CONSTRAINT_FOREIGN)
12927 : {
12928 322 : QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12929 : tuple, lockmode);
12930 : }
12931 238 : else if (con->contype == CONSTRAINT_CHECK)
12932 : {
12933 126 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12934 : tuple, recurse, recursing, lockmode);
12935 : }
12936 112 : else if (con->contype == CONSTRAINT_NOTNULL)
12937 : {
12938 112 : QueueNNConstraintValidation(wqueue, conrel, rel,
12939 : tuple, recurse, recursing, lockmode);
12940 : }
12941 :
12942 560 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
12943 : }
12944 : else
12945 18 : address = InvalidObjectAddress; /* already validated */
12946 :
12947 578 : systable_endscan(scan);
12948 :
12949 578 : table_close(conrel, RowExclusiveLock);
12950 :
12951 578 : return address;
12952 : }
12953 :
12954 : /*
12955 : * QueueFKConstraintValidation
12956 : *
12957 : * Add an entry to the wqueue to validate the given foreign key constraint in
12958 : * Phase 3 and update the convalidated field in the pg_constraint catalog
12959 : * for the specified relation and all its children.
12960 : */
12961 : static void
12962 394 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
12963 : Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
12964 : {
12965 : Form_pg_constraint con;
12966 : AlteredTableInfo *tab;
12967 : HeapTuple copyTuple;
12968 : Form_pg_constraint copy_con;
12969 :
12970 394 : con = (Form_pg_constraint) GETSTRUCT(contuple);
12971 : Assert(con->contype == CONSTRAINT_FOREIGN);
12972 : Assert(!con->convalidated);
12973 :
12974 : /*
12975 : * Add the validation to phase 3's queue; not needed for partitioned
12976 : * tables themselves, only for their partitions.
12977 : *
12978 : * When the referenced table (pkrelid) is partitioned, the referencing
12979 : * table (fkrel) has one pg_constraint row pointing to each partition
12980 : * thereof. These rows are there only to support action triggers and no
12981 : * table scan is needed, therefore skip this for them as well.
12982 : */
12983 394 : if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
12984 346 : con->confrelid == pkrelid)
12985 : {
12986 : NewConstraint *newcon;
12987 : Constraint *fkconstraint;
12988 :
12989 : /* Queue validation for phase 3 */
12990 334 : fkconstraint = makeNode(Constraint);
12991 : /* for now this is all we need */
12992 334 : fkconstraint->conname = pstrdup(NameStr(con->conname));
12993 :
12994 334 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12995 334 : newcon->name = fkconstraint->conname;
12996 334 : newcon->contype = CONSTR_FOREIGN;
12997 334 : newcon->refrelid = con->confrelid;
12998 334 : newcon->refindid = con->conindid;
12999 334 : newcon->conid = con->oid;
13000 334 : newcon->qual = (Node *) fkconstraint;
13001 :
13002 : /* Find or create work queue entry for this table */
13003 334 : tab = ATGetQueueEntry(wqueue, fkrel);
13004 334 : tab->constraints = lappend(tab->constraints, newcon);
13005 : }
13006 :
13007 : /*
13008 : * If the table at either end of the constraint is partitioned, we need to
13009 : * recurse and handle every unvalidate constraint that is a child of this
13010 : * constraint.
13011 : */
13012 740 : if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13013 346 : get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13014 : {
13015 : ScanKeyData pkey;
13016 : SysScanDesc pscan;
13017 : HeapTuple childtup;
13018 :
13019 72 : ScanKeyInit(&pkey,
13020 : Anum_pg_constraint_conparentid,
13021 : BTEqualStrategyNumber, F_OIDEQ,
13022 : ObjectIdGetDatum(con->oid));
13023 :
13024 72 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13025 : true, NULL, 1, &pkey);
13026 :
13027 144 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13028 : {
13029 : Form_pg_constraint childcon;
13030 : Relation childrel;
13031 :
13032 72 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13033 :
13034 : /*
13035 : * If the child constraint has already been validated, no further
13036 : * action is required for it or its descendants, as they are all
13037 : * valid.
13038 : */
13039 72 : if (childcon->convalidated)
13040 18 : continue;
13041 :
13042 54 : childrel = table_open(childcon->conrelid, lockmode);
13043 :
13044 : /*
13045 : * NB: Note that pkrelid should be passed as-is during recursion,
13046 : * as it is required to identify the root referenced table.
13047 : */
13048 54 : QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13049 : childtup, lockmode);
13050 54 : table_close(childrel, NoLock);
13051 : }
13052 :
13053 72 : systable_endscan(pscan);
13054 : }
13055 :
13056 : /*
13057 : * Now mark the pg_constraint row as validated (even if we didn't check,
13058 : * notably the ones for partitions on the referenced side).
13059 : *
13060 : * We rely on transaction abort to roll back this change if phase 3
13061 : * ultimately finds violating rows. This is a bit ugly.
13062 : */
13063 394 : copyTuple = heap_copytuple(contuple);
13064 394 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13065 394 : copy_con->convalidated = true;
13066 394 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13067 :
13068 394 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13069 :
13070 394 : heap_freetuple(copyTuple);
13071 394 : }
13072 :
13073 : /*
13074 : * QueueCheckConstraintValidation
13075 : *
13076 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
13077 : * and update the convalidated field in the pg_constraint catalog for the
13078 : * specified relation and all its inheriting children.
13079 : */
13080 : static void
13081 126 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13082 : char *constrName, HeapTuple contuple,
13083 : bool recurse, bool recursing, LOCKMODE lockmode)
13084 : {
13085 : Form_pg_constraint con;
13086 : AlteredTableInfo *tab;
13087 : HeapTuple copyTuple;
13088 : Form_pg_constraint copy_con;
13089 :
13090 126 : List *children = NIL;
13091 : ListCell *child;
13092 : NewConstraint *newcon;
13093 : Datum val;
13094 : char *conbin;
13095 :
13096 126 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13097 : Assert(con->contype == CONSTRAINT_CHECK);
13098 :
13099 : /*
13100 : * If we're recursing, the parent has already done this, so skip it. Also,
13101 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13102 : * for it in the children.
13103 : */
13104 126 : if (!recursing && !con->connoinherit)
13105 72 : children = find_all_inheritors(RelationGetRelid(rel),
13106 : lockmode, NULL);
13107 :
13108 : /*
13109 : * For CHECK constraints, we must ensure that we only mark the constraint
13110 : * as validated on the parent if it's already validated on the children.
13111 : *
13112 : * We recurse before validating on the parent, to reduce risk of
13113 : * deadlocks.
13114 : */
13115 246 : foreach(child, children)
13116 : {
13117 120 : Oid childoid = lfirst_oid(child);
13118 : Relation childrel;
13119 :
13120 120 : if (childoid == RelationGetRelid(rel))
13121 72 : continue;
13122 :
13123 : /*
13124 : * If we are told not to recurse, there had better not be any child
13125 : * tables, because we can't mark the constraint on the parent valid
13126 : * unless it is valid for all child tables.
13127 : */
13128 48 : if (!recurse)
13129 0 : ereport(ERROR,
13130 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13131 : errmsg("constraint must be validated on child tables too")));
13132 :
13133 : /* find_all_inheritors already got lock */
13134 48 : childrel = table_open(childoid, NoLock);
13135 :
13136 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
13137 : true, lockmode);
13138 48 : table_close(childrel, NoLock);
13139 : }
13140 :
13141 : /* Queue validation for phase 3 */
13142 126 : newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13143 126 : newcon->name = constrName;
13144 126 : newcon->contype = CONSTR_CHECK;
13145 126 : newcon->refrelid = InvalidOid;
13146 126 : newcon->refindid = InvalidOid;
13147 126 : newcon->conid = con->oid;
13148 :
13149 126 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13150 : Anum_pg_constraint_conbin);
13151 126 : conbin = TextDatumGetCString(val);
13152 126 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13153 :
13154 : /* Find or create work queue entry for this table */
13155 126 : tab = ATGetQueueEntry(wqueue, rel);
13156 126 : tab->constraints = lappend(tab->constraints, newcon);
13157 :
13158 : /*
13159 : * Invalidate relcache so that others see the new validated constraint.
13160 : */
13161 126 : CacheInvalidateRelcache(rel);
13162 :
13163 : /*
13164 : * Now update the catalog, while we have the door open.
13165 : */
13166 126 : copyTuple = heap_copytuple(contuple);
13167 126 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13168 126 : copy_con->convalidated = true;
13169 126 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13170 :
13171 126 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13172 :
13173 126 : heap_freetuple(copyTuple);
13174 126 : }
13175 :
13176 : /*
13177 : * QueueNNConstraintValidation
13178 : *
13179 : * Add an entry to the wqueue to validate the given not-null constraint in
13180 : * Phase 3 and update the convalidated field in the pg_constraint catalog for
13181 : * the specified relation and all its inheriting children.
13182 : */
13183 : static void
13184 112 : QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13185 : HeapTuple contuple, bool recurse, bool recursing,
13186 : LOCKMODE lockmode)
13187 : {
13188 : Form_pg_constraint con;
13189 : AlteredTableInfo *tab;
13190 : HeapTuple copyTuple;
13191 : Form_pg_constraint copy_con;
13192 112 : List *children = NIL;
13193 : AttrNumber attnum;
13194 : char *colname;
13195 :
13196 112 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13197 : Assert(con->contype == CONSTRAINT_NOTNULL);
13198 :
13199 112 : attnum = extractNotNullColumn(contuple);
13200 :
13201 : /*
13202 : * If we're recursing, we've already done this for parent, so skip it.
13203 : * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13204 : * look for it in the children.
13205 : *
13206 : * We recurse before validating on the parent, to reduce risk of
13207 : * deadlocks.
13208 : */
13209 112 : if (!recursing && !con->connoinherit)
13210 76 : children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13211 :
13212 112 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13213 378 : foreach_oid(childoid, children)
13214 : {
13215 : Relation childrel;
13216 : HeapTuple contup;
13217 : Form_pg_constraint childcon;
13218 : char *conname;
13219 :
13220 154 : if (childoid == RelationGetRelid(rel))
13221 76 : continue;
13222 :
13223 : /*
13224 : * If we are told not to recurse, there had better not be any child
13225 : * tables, because we can't mark the constraint on the parent valid
13226 : * unless it is valid for all child tables.
13227 : */
13228 78 : if (!recurse)
13229 0 : ereport(ERROR,
13230 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13231 : errmsg("constraint must be validated on child tables too"));
13232 :
13233 : /*
13234 : * The column on child might have a different attnum, so search by
13235 : * column name.
13236 : */
13237 78 : contup = findNotNullConstraint(childoid, colname);
13238 78 : if (!contup)
13239 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13240 : colname, get_rel_name(childoid));
13241 78 : childcon = (Form_pg_constraint) GETSTRUCT(contup);
13242 78 : if (childcon->convalidated)
13243 42 : continue;
13244 :
13245 : /* find_all_inheritors already got lock */
13246 36 : childrel = table_open(childoid, NoLock);
13247 36 : conname = pstrdup(NameStr(childcon->conname));
13248 :
13249 : /* XXX improve ATExecValidateConstraint API to avoid double search */
13250 36 : ATExecValidateConstraint(wqueue, childrel, conname,
13251 : false, true, lockmode);
13252 36 : table_close(childrel, NoLock);
13253 : }
13254 :
13255 : /* Set attnotnull appropriately without queueing another validation */
13256 112 : set_attnotnull(NULL, rel, attnum, true, false);
13257 :
13258 112 : tab = ATGetQueueEntry(wqueue, rel);
13259 112 : tab->verify_new_notnull = true;
13260 :
13261 : /*
13262 : * Invalidate relcache so that others see the new validated constraint.
13263 : */
13264 112 : CacheInvalidateRelcache(rel);
13265 :
13266 : /*
13267 : * Now update the catalogs, while we have the door open.
13268 : */
13269 112 : copyTuple = heap_copytuple(contuple);
13270 112 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13271 112 : copy_con->convalidated = true;
13272 112 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13273 :
13274 112 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13275 :
13276 112 : heap_freetuple(copyTuple);
13277 112 : }
13278 :
13279 : /*
13280 : * transformColumnNameList - transform list of column names
13281 : *
13282 : * Lookup each name and return its attnum and, optionally, type and collation
13283 : * OIDs
13284 : *
13285 : * Note: the name of this function suggests that it's general-purpose,
13286 : * but actually it's only used to look up names appearing in foreign-key
13287 : * clauses. The error messages would need work to use it in other cases,
13288 : * and perhaps the validity checks as well.
13289 : */
13290 : static int
13291 6592 : transformColumnNameList(Oid relId, List *colList,
13292 : int16 *attnums, Oid *atttypids, Oid *attcollids)
13293 : {
13294 : ListCell *l;
13295 : int attnum;
13296 :
13297 6592 : attnum = 0;
13298 12068 : foreach(l, colList)
13299 : {
13300 5542 : char *attname = strVal(lfirst(l));
13301 : HeapTuple atttuple;
13302 : Form_pg_attribute attform;
13303 :
13304 5542 : atttuple = SearchSysCacheAttName(relId, attname);
13305 5542 : if (!HeapTupleIsValid(atttuple))
13306 54 : ereport(ERROR,
13307 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13308 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13309 : attname)));
13310 5488 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13311 5488 : if (attform->attnum < 0)
13312 12 : ereport(ERROR,
13313 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13314 : errmsg("system columns cannot be used in foreign keys")));
13315 5476 : if (attnum >= INDEX_MAX_KEYS)
13316 0 : ereport(ERROR,
13317 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
13318 : errmsg("cannot have more than %d keys in a foreign key",
13319 : INDEX_MAX_KEYS)));
13320 5476 : attnums[attnum] = attform->attnum;
13321 5476 : if (atttypids != NULL)
13322 5440 : atttypids[attnum] = attform->atttypid;
13323 5476 : if (attcollids != NULL)
13324 5440 : attcollids[attnum] = attform->attcollation;
13325 5476 : ReleaseSysCache(atttuple);
13326 5476 : attnum++;
13327 : }
13328 :
13329 6526 : return attnum;
13330 : }
13331 :
13332 : /*
13333 : * transformFkeyGetPrimaryKey -
13334 : *
13335 : * Look up the names, attnums, types, and collations of the primary key attributes
13336 : * for the pkrel. Also return the index OID and index opclasses of the
13337 : * index supporting the primary key. Also return whether the index has
13338 : * WITHOUT OVERLAPS.
13339 : *
13340 : * All parameters except pkrel are output parameters. Also, the function
13341 : * return value is the number of attributes in the primary key.
13342 : *
13343 : * Used when the column list in the REFERENCES specification is omitted.
13344 : */
13345 : static int
13346 1196 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
13347 : List **attnamelist,
13348 : int16 *attnums, Oid *atttypids, Oid *attcollids,
13349 : Oid *opclasses, bool *pk_has_without_overlaps)
13350 : {
13351 : List *indexoidlist;
13352 : ListCell *indexoidscan;
13353 1196 : HeapTuple indexTuple = NULL;
13354 1196 : Form_pg_index indexStruct = NULL;
13355 : Datum indclassDatum;
13356 : oidvector *indclass;
13357 : int i;
13358 :
13359 : /*
13360 : * Get the list of index OIDs for the table from the relcache, and look up
13361 : * each one in the pg_index syscache until we find one marked primary key
13362 : * (hopefully there isn't more than one such). Insist it's valid, too.
13363 : */
13364 1196 : *indexOid = InvalidOid;
13365 :
13366 1196 : indexoidlist = RelationGetIndexList(pkrel);
13367 :
13368 1202 : foreach(indexoidscan, indexoidlist)
13369 : {
13370 1202 : Oid indexoid = lfirst_oid(indexoidscan);
13371 :
13372 1202 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13373 1202 : if (!HeapTupleIsValid(indexTuple))
13374 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13375 1202 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13376 1202 : if (indexStruct->indisprimary && indexStruct->indisvalid)
13377 : {
13378 : /*
13379 : * Refuse to use a deferrable primary key. This is per SQL spec,
13380 : * and there would be a lot of interesting semantic problems if we
13381 : * tried to allow it.
13382 : */
13383 1196 : if (!indexStruct->indimmediate)
13384 0 : ereport(ERROR,
13385 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13386 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13387 : RelationGetRelationName(pkrel))));
13388 :
13389 1196 : *indexOid = indexoid;
13390 1196 : break;
13391 : }
13392 6 : ReleaseSysCache(indexTuple);
13393 : }
13394 :
13395 1196 : list_free(indexoidlist);
13396 :
13397 : /*
13398 : * Check that we found it
13399 : */
13400 1196 : if (!OidIsValid(*indexOid))
13401 0 : ereport(ERROR,
13402 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13403 : errmsg("there is no primary key for referenced table \"%s\"",
13404 : RelationGetRelationName(pkrel))));
13405 :
13406 : /* Must get indclass the hard way */
13407 1196 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13408 : Anum_pg_index_indclass);
13409 1196 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13410 :
13411 : /*
13412 : * Now build the list of PK attributes from the indkey definition (we
13413 : * assume a primary key cannot have expressional elements)
13414 : */
13415 1196 : *attnamelist = NIL;
13416 2834 : for (i = 0; i < indexStruct->indnkeyatts; i++)
13417 : {
13418 1638 : int pkattno = indexStruct->indkey.values[i];
13419 :
13420 1638 : attnums[i] = pkattno;
13421 1638 : atttypids[i] = attnumTypeId(pkrel, pkattno);
13422 1638 : attcollids[i] = attnumCollationId(pkrel, pkattno);
13423 1638 : opclasses[i] = indclass->values[i];
13424 1638 : *attnamelist = lappend(*attnamelist,
13425 1638 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13426 : }
13427 :
13428 1196 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13429 :
13430 1196 : ReleaseSysCache(indexTuple);
13431 :
13432 1196 : return i;
13433 : }
13434 :
13435 : /*
13436 : * transformFkeyCheckAttrs -
13437 : *
13438 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13439 : * reference as part of a foreign key constraint.
13440 : *
13441 : * Returns the OID of the unique index supporting the constraint and
13442 : * populates the caller-provided 'opclasses' array with the opclasses
13443 : * associated with the index columns. Also sets whether the index
13444 : * uses WITHOUT OVERLAPS.
13445 : *
13446 : * Raises an ERROR on validation failure.
13447 : */
13448 : static Oid
13449 1320 : transformFkeyCheckAttrs(Relation pkrel,
13450 : int numattrs, int16 *attnums,
13451 : bool with_period, Oid *opclasses,
13452 : bool *pk_has_without_overlaps)
13453 : {
13454 1320 : Oid indexoid = InvalidOid;
13455 1320 : bool found = false;
13456 1320 : bool found_deferrable = false;
13457 : List *indexoidlist;
13458 : ListCell *indexoidscan;
13459 : int i,
13460 : j;
13461 :
13462 : /*
13463 : * Reject duplicate appearances of columns in the referenced-columns list.
13464 : * Such a case is forbidden by the SQL standard, and even if we thought it
13465 : * useful to allow it, there would be ambiguity about how to match the
13466 : * list to unique indexes (in particular, it'd be unclear which index
13467 : * opclass goes with which FK column).
13468 : */
13469 3092 : for (i = 0; i < numattrs; i++)
13470 : {
13471 2346 : for (j = i + 1; j < numattrs; j++)
13472 : {
13473 574 : if (attnums[i] == attnums[j])
13474 24 : ereport(ERROR,
13475 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13476 : errmsg("foreign key referenced-columns list must not contain duplicates")));
13477 : }
13478 : }
13479 :
13480 : /*
13481 : * Get the list of index OIDs for the table from the relcache, and look up
13482 : * each one in the pg_index syscache, and match unique indexes to the list
13483 : * of attnums we are given.
13484 : */
13485 1296 : indexoidlist = RelationGetIndexList(pkrel);
13486 :
13487 1478 : foreach(indexoidscan, indexoidlist)
13488 : {
13489 : HeapTuple indexTuple;
13490 : Form_pg_index indexStruct;
13491 :
13492 1466 : indexoid = lfirst_oid(indexoidscan);
13493 1466 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13494 1466 : if (!HeapTupleIsValid(indexTuple))
13495 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13496 1466 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13497 :
13498 : /*
13499 : * Must have the right number of columns; must be unique (or if
13500 : * temporal then exclusion instead) and not a partial index; forget it
13501 : * if there are any expressions, too. Invalid indexes are out as well.
13502 : */
13503 2824 : if (indexStruct->indnkeyatts == numattrs &&
13504 1358 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13505 2684 : indexStruct->indisvalid &&
13506 2684 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13507 1342 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13508 : {
13509 : Datum indclassDatum;
13510 : oidvector *indclass;
13511 :
13512 : /* Must get indclass the hard way */
13513 1342 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13514 : Anum_pg_index_indclass);
13515 1342 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13516 :
13517 : /*
13518 : * The given attnum list may match the index columns in any order.
13519 : * Check for a match, and extract the appropriate opclasses while
13520 : * we're at it.
13521 : *
13522 : * We know that attnums[] is duplicate-free per the test at the
13523 : * start of this function, and we checked above that the number of
13524 : * index columns agrees, so if we find a match for each attnums[]
13525 : * entry then we must have a one-to-one match in some order.
13526 : */
13527 3102 : for (i = 0; i < numattrs; i++)
13528 : {
13529 1818 : found = false;
13530 2426 : for (j = 0; j < numattrs; j++)
13531 : {
13532 2368 : if (attnums[i] == indexStruct->indkey.values[j])
13533 : {
13534 1760 : opclasses[i] = indclass->values[j];
13535 1760 : found = true;
13536 1760 : break;
13537 : }
13538 : }
13539 1818 : if (!found)
13540 58 : break;
13541 : }
13542 : /* The last attribute in the index must be the PERIOD FK part */
13543 1342 : if (found && with_period)
13544 : {
13545 128 : int16 periodattnum = attnums[numattrs - 1];
13546 :
13547 128 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13548 : }
13549 :
13550 : /*
13551 : * Refuse to use a deferrable unique/primary key. This is per SQL
13552 : * spec, and there would be a lot of interesting semantic problems
13553 : * if we tried to allow it.
13554 : */
13555 1342 : if (found && !indexStruct->indimmediate)
13556 : {
13557 : /*
13558 : * Remember that we found an otherwise matching index, so that
13559 : * we can generate a more appropriate error message.
13560 : */
13561 0 : found_deferrable = true;
13562 0 : found = false;
13563 : }
13564 :
13565 : /* We need to know whether the index has WITHOUT OVERLAPS */
13566 1342 : if (found)
13567 1284 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13568 : }
13569 1466 : ReleaseSysCache(indexTuple);
13570 1466 : if (found)
13571 1284 : break;
13572 : }
13573 :
13574 1296 : if (!found)
13575 : {
13576 12 : if (found_deferrable)
13577 0 : ereport(ERROR,
13578 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13579 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13580 : RelationGetRelationName(pkrel))));
13581 : else
13582 12 : ereport(ERROR,
13583 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13584 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13585 : RelationGetRelationName(pkrel))));
13586 : }
13587 :
13588 1284 : list_free(indexoidlist);
13589 :
13590 1284 : return indexoid;
13591 : }
13592 :
13593 : /*
13594 : * findFkeyCast -
13595 : *
13596 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13597 : * Caller has equal regard for binary coercibility and for an exact match.
13598 : */
13599 : static CoercionPathType
13600 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13601 : {
13602 : CoercionPathType ret;
13603 :
13604 12 : if (targetTypeId == sourceTypeId)
13605 : {
13606 12 : ret = COERCION_PATH_RELABELTYPE;
13607 12 : *funcid = InvalidOid;
13608 : }
13609 : else
13610 : {
13611 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13612 : COERCION_IMPLICIT, funcid);
13613 0 : if (ret == COERCION_PATH_NONE)
13614 : /* A previously-relied-upon cast is now gone. */
13615 0 : elog(ERROR, "could not find cast from %u to %u",
13616 : sourceTypeId, targetTypeId);
13617 : }
13618 :
13619 12 : return ret;
13620 : }
13621 :
13622 : /*
13623 : * Permissions checks on the referenced table for ADD FOREIGN KEY
13624 : *
13625 : * Note: we have already checked that the user owns the referencing table,
13626 : * else we'd have failed much earlier; no additional checks are needed for it.
13627 : */
13628 : static void
13629 2444 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13630 : {
13631 2444 : Oid roleid = GetUserId();
13632 : AclResult aclresult;
13633 : int i;
13634 :
13635 : /* Okay if we have relation-level REFERENCES permission */
13636 2444 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13637 : ACL_REFERENCES);
13638 2444 : if (aclresult == ACLCHECK_OK)
13639 2444 : return;
13640 : /* Else we must have REFERENCES on each column */
13641 0 : for (i = 0; i < natts; i++)
13642 : {
13643 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13644 : roleid, ACL_REFERENCES);
13645 0 : if (aclresult != ACLCHECK_OK)
13646 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13647 0 : RelationGetRelationName(rel));
13648 : }
13649 : }
13650 :
13651 : /*
13652 : * Scan the existing rows in a table to verify they meet a proposed FK
13653 : * constraint.
13654 : *
13655 : * Caller must have opened and locked both relations appropriately.
13656 : */
13657 : static void
13658 1230 : validateForeignKeyConstraint(char *conname,
13659 : Relation rel,
13660 : Relation pkrel,
13661 : Oid pkindOid,
13662 : Oid constraintOid,
13663 : bool hasperiod)
13664 : {
13665 : TupleTableSlot *slot;
13666 : TableScanDesc scan;
13667 1230 : Trigger trig = {0};
13668 : Snapshot snapshot;
13669 : MemoryContext oldcxt;
13670 : MemoryContext perTupCxt;
13671 :
13672 1230 : ereport(DEBUG1,
13673 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13674 :
13675 : /*
13676 : * Build a trigger call structure; we'll need it either way.
13677 : */
13678 1230 : trig.tgoid = InvalidOid;
13679 1230 : trig.tgname = conname;
13680 1230 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13681 1230 : trig.tgisinternal = true;
13682 1230 : trig.tgconstrrelid = RelationGetRelid(pkrel);
13683 1230 : trig.tgconstrindid = pkindOid;
13684 1230 : trig.tgconstraint = constraintOid;
13685 1230 : trig.tgdeferrable = false;
13686 1230 : trig.tginitdeferred = false;
13687 : /* we needn't fill in remaining fields */
13688 :
13689 : /*
13690 : * See if we can do it with a single LEFT JOIN query. A false result
13691 : * indicates we must proceed with the fire-the-trigger method. We can't do
13692 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13693 : * left joins.
13694 : */
13695 1230 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13696 1042 : return;
13697 :
13698 : /*
13699 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13700 : * if that tuple had just been inserted. If any of those fail, it should
13701 : * ereport(ERROR) and that's that.
13702 : */
13703 114 : snapshot = RegisterSnapshot(GetLatestSnapshot());
13704 114 : slot = table_slot_create(rel, NULL);
13705 114 : scan = table_beginscan(rel, snapshot, 0, NULL);
13706 :
13707 114 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
13708 : "validateForeignKeyConstraint",
13709 : ALLOCSET_SMALL_SIZES);
13710 114 : oldcxt = MemoryContextSwitchTo(perTupCxt);
13711 :
13712 200 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13713 : {
13714 104 : LOCAL_FCINFO(fcinfo, 0);
13715 104 : TriggerData trigdata = {0};
13716 :
13717 104 : CHECK_FOR_INTERRUPTS();
13718 :
13719 : /*
13720 : * Make a call to the trigger function
13721 : *
13722 : * No parameters are passed, but we do set a context
13723 : */
13724 520 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13725 :
13726 : /*
13727 : * We assume RI_FKey_check_ins won't look at flinfo...
13728 : */
13729 104 : trigdata.type = T_TriggerData;
13730 104 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
13731 104 : trigdata.tg_relation = rel;
13732 104 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13733 104 : trigdata.tg_trigslot = slot;
13734 104 : trigdata.tg_trigger = &trig;
13735 :
13736 104 : fcinfo->context = (Node *) &trigdata;
13737 :
13738 104 : RI_FKey_check_ins(fcinfo);
13739 :
13740 86 : MemoryContextReset(perTupCxt);
13741 : }
13742 :
13743 96 : MemoryContextSwitchTo(oldcxt);
13744 96 : MemoryContextDelete(perTupCxt);
13745 96 : table_endscan(scan);
13746 96 : UnregisterSnapshot(snapshot);
13747 96 : ExecDropSingleTupleTableSlot(slot);
13748 : }
13749 :
13750 : /*
13751 : * CreateFKCheckTrigger
13752 : * Creates the insert (on_insert=true) or update "check" trigger that
13753 : * implements a given foreign key
13754 : *
13755 : * Returns the OID of the so created trigger.
13756 : */
13757 : static Oid
13758 5972 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13759 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13760 : bool on_insert)
13761 : {
13762 : ObjectAddress trigAddress;
13763 : CreateTrigStmt *fk_trigger;
13764 :
13765 : /*
13766 : * Note: for a self-referential FK (referencing and referenced tables are
13767 : * the same), it is important that the ON UPDATE action fires before the
13768 : * CHECK action, since both triggers will fire on the same row during an
13769 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13770 : * state of the row. Triggers fire in name order, so we ensure this by
13771 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13772 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13773 : */
13774 5972 : fk_trigger = makeNode(CreateTrigStmt);
13775 5972 : fk_trigger->replace = false;
13776 5972 : fk_trigger->isconstraint = true;
13777 5972 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
13778 5972 : fk_trigger->relation = NULL;
13779 :
13780 : /* Either ON INSERT or ON UPDATE */
13781 5972 : if (on_insert)
13782 : {
13783 2986 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13784 2986 : fk_trigger->events = TRIGGER_TYPE_INSERT;
13785 : }
13786 : else
13787 : {
13788 2986 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13789 2986 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13790 : }
13791 :
13792 5972 : fk_trigger->args = NIL;
13793 5972 : fk_trigger->row = true;
13794 5972 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13795 5972 : fk_trigger->columns = NIL;
13796 5972 : fk_trigger->whenClause = NULL;
13797 5972 : fk_trigger->transitionRels = NIL;
13798 5972 : fk_trigger->deferrable = fkconstraint->deferrable;
13799 5972 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13800 5972 : fk_trigger->constrrel = NULL;
13801 :
13802 5972 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13803 : constraintOid, indexOid, InvalidOid,
13804 : parentTrigOid, NULL, true, false);
13805 :
13806 : /* Make changes-so-far visible */
13807 5972 : CommandCounterIncrement();
13808 :
13809 5972 : return trigAddress.objectId;
13810 : }
13811 :
13812 : /*
13813 : * createForeignKeyActionTriggers
13814 : * Create the referenced-side "action" triggers that implement a foreign
13815 : * key.
13816 : *
13817 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
13818 : * *updateTrigOid.
13819 : */
13820 : static void
13821 3412 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13822 : Oid constraintOid, Oid indexOid,
13823 : Oid parentDelTrigger, Oid parentUpdTrigger,
13824 : Oid *deleteTrigOid, Oid *updateTrigOid)
13825 : {
13826 : CreateTrigStmt *fk_trigger;
13827 : ObjectAddress trigAddress;
13828 :
13829 : /*
13830 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13831 : * DELETE action on the referenced table.
13832 : */
13833 3412 : fk_trigger = makeNode(CreateTrigStmt);
13834 3412 : fk_trigger->replace = false;
13835 3412 : fk_trigger->isconstraint = true;
13836 3412 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13837 3412 : fk_trigger->relation = NULL;
13838 3412 : fk_trigger->args = NIL;
13839 3412 : fk_trigger->row = true;
13840 3412 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13841 3412 : fk_trigger->events = TRIGGER_TYPE_DELETE;
13842 3412 : fk_trigger->columns = NIL;
13843 3412 : fk_trigger->whenClause = NULL;
13844 3412 : fk_trigger->transitionRels = NIL;
13845 3412 : fk_trigger->constrrel = NULL;
13846 :
13847 3412 : switch (fkconstraint->fk_del_action)
13848 : {
13849 2778 : case FKCONSTR_ACTION_NOACTION:
13850 2778 : fk_trigger->deferrable = fkconstraint->deferrable;
13851 2778 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13852 2778 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13853 2778 : break;
13854 30 : case FKCONSTR_ACTION_RESTRICT:
13855 30 : fk_trigger->deferrable = false;
13856 30 : fk_trigger->initdeferred = false;
13857 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13858 30 : break;
13859 446 : case FKCONSTR_ACTION_CASCADE:
13860 446 : fk_trigger->deferrable = false;
13861 446 : fk_trigger->initdeferred = false;
13862 446 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13863 446 : break;
13864 98 : case FKCONSTR_ACTION_SETNULL:
13865 98 : fk_trigger->deferrable = false;
13866 98 : fk_trigger->initdeferred = false;
13867 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13868 98 : break;
13869 60 : case FKCONSTR_ACTION_SETDEFAULT:
13870 60 : fk_trigger->deferrable = false;
13871 60 : fk_trigger->initdeferred = false;
13872 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13873 60 : break;
13874 0 : default:
13875 0 : elog(ERROR, "unrecognized FK action type: %d",
13876 : (int) fkconstraint->fk_del_action);
13877 : break;
13878 : }
13879 :
13880 3412 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13881 : constraintOid, indexOid, InvalidOid,
13882 : parentDelTrigger, NULL, true, false);
13883 3412 : if (deleteTrigOid)
13884 3412 : *deleteTrigOid = trigAddress.objectId;
13885 :
13886 : /* Make changes-so-far visible */
13887 3412 : CommandCounterIncrement();
13888 :
13889 : /*
13890 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13891 : * UPDATE action on the referenced table.
13892 : */
13893 3412 : fk_trigger = makeNode(CreateTrigStmt);
13894 3412 : fk_trigger->replace = false;
13895 3412 : fk_trigger->isconstraint = true;
13896 3412 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13897 3412 : fk_trigger->relation = NULL;
13898 3412 : fk_trigger->args = NIL;
13899 3412 : fk_trigger->row = true;
13900 3412 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13901 3412 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13902 3412 : fk_trigger->columns = NIL;
13903 3412 : fk_trigger->whenClause = NULL;
13904 3412 : fk_trigger->transitionRels = NIL;
13905 3412 : fk_trigger->constrrel = NULL;
13906 :
13907 3412 : switch (fkconstraint->fk_upd_action)
13908 : {
13909 2970 : case FKCONSTR_ACTION_NOACTION:
13910 2970 : fk_trigger->deferrable = fkconstraint->deferrable;
13911 2970 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13912 2970 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13913 2970 : break;
13914 36 : case FKCONSTR_ACTION_RESTRICT:
13915 36 : fk_trigger->deferrable = false;
13916 36 : fk_trigger->initdeferred = false;
13917 36 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13918 36 : break;
13919 300 : case FKCONSTR_ACTION_CASCADE:
13920 300 : fk_trigger->deferrable = false;
13921 300 : fk_trigger->initdeferred = false;
13922 300 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13923 300 : break;
13924 64 : case FKCONSTR_ACTION_SETNULL:
13925 64 : fk_trigger->deferrable = false;
13926 64 : fk_trigger->initdeferred = false;
13927 64 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13928 64 : break;
13929 42 : case FKCONSTR_ACTION_SETDEFAULT:
13930 42 : fk_trigger->deferrable = false;
13931 42 : fk_trigger->initdeferred = false;
13932 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13933 42 : break;
13934 0 : default:
13935 0 : elog(ERROR, "unrecognized FK action type: %d",
13936 : (int) fkconstraint->fk_upd_action);
13937 : break;
13938 : }
13939 :
13940 3412 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13941 : constraintOid, indexOid, InvalidOid,
13942 : parentUpdTrigger, NULL, true, false);
13943 3412 : if (updateTrigOid)
13944 3412 : *updateTrigOid = trigAddress.objectId;
13945 3412 : }
13946 :
13947 : /*
13948 : * createForeignKeyCheckTriggers
13949 : * Create the referencing-side "check" triggers that implement a foreign
13950 : * key.
13951 : *
13952 : * Returns the OIDs of the so created triggers in *insertTrigOid and
13953 : * *updateTrigOid.
13954 : */
13955 : static void
13956 2986 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
13957 : Constraint *fkconstraint, Oid constraintOid,
13958 : Oid indexOid,
13959 : Oid parentInsTrigger, Oid parentUpdTrigger,
13960 : Oid *insertTrigOid, Oid *updateTrigOid)
13961 : {
13962 2986 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13963 : constraintOid, indexOid,
13964 : parentInsTrigger, true);
13965 2986 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13966 : constraintOid, indexOid,
13967 : parentUpdTrigger, false);
13968 2986 : }
13969 :
13970 : /*
13971 : * ALTER TABLE DROP CONSTRAINT
13972 : *
13973 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
13974 : */
13975 : static void
13976 792 : ATExecDropConstraint(Relation rel, const char *constrName,
13977 : DropBehavior behavior, bool recurse,
13978 : bool missing_ok, LOCKMODE lockmode)
13979 : {
13980 : Relation conrel;
13981 : SysScanDesc scan;
13982 : ScanKeyData skey[3];
13983 : HeapTuple tuple;
13984 792 : bool found = false;
13985 :
13986 792 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13987 :
13988 : /*
13989 : * Find and drop the target constraint
13990 : */
13991 792 : ScanKeyInit(&skey[0],
13992 : Anum_pg_constraint_conrelid,
13993 : BTEqualStrategyNumber, F_OIDEQ,
13994 : ObjectIdGetDatum(RelationGetRelid(rel)));
13995 792 : ScanKeyInit(&skey[1],
13996 : Anum_pg_constraint_contypid,
13997 : BTEqualStrategyNumber, F_OIDEQ,
13998 : ObjectIdGetDatum(InvalidOid));
13999 792 : ScanKeyInit(&skey[2],
14000 : Anum_pg_constraint_conname,
14001 : BTEqualStrategyNumber, F_NAMEEQ,
14002 : CStringGetDatum(constrName));
14003 792 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14004 : true, NULL, 3, skey);
14005 :
14006 : /* There can be at most one matching row */
14007 792 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14008 : {
14009 756 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
14010 : missing_ok, lockmode);
14011 570 : found = true;
14012 : }
14013 :
14014 606 : systable_endscan(scan);
14015 :
14016 606 : if (!found)
14017 : {
14018 36 : if (!missing_ok)
14019 24 : ereport(ERROR,
14020 : errcode(ERRCODE_UNDEFINED_OBJECT),
14021 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14022 : constrName, RelationGetRelationName(rel)));
14023 : else
14024 12 : ereport(NOTICE,
14025 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14026 : constrName, RelationGetRelationName(rel)));
14027 : }
14028 :
14029 582 : table_close(conrel, RowExclusiveLock);
14030 582 : }
14031 :
14032 : /*
14033 : * Remove a constraint, using its pg_constraint tuple
14034 : *
14035 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14036 : * DROP NOT NULL.
14037 : *
14038 : * Returns the address of the constraint being removed.
14039 : */
14040 : static ObjectAddress
14041 1186 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
14042 : bool recurse, bool recursing, bool missing_ok,
14043 : LOCKMODE lockmode)
14044 : {
14045 : Relation conrel;
14046 : Form_pg_constraint con;
14047 : ObjectAddress conobj;
14048 : List *children;
14049 1186 : bool is_no_inherit_constraint = false;
14050 : char *constrName;
14051 1186 : char *colname = NULL;
14052 :
14053 : /* Guard against stack overflow due to overly deep inheritance tree. */
14054 1186 : check_stack_depth();
14055 :
14056 : /* At top level, permission check was done in ATPrepCmd, else do it */
14057 1186 : if (recursing)
14058 210 : ATSimplePermissions(AT_DropConstraint, rel,
14059 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
14060 :
14061 1180 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14062 :
14063 1180 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14064 1180 : constrName = NameStr(con->conname);
14065 :
14066 : /* Don't allow drop of inherited constraints */
14067 1180 : if (con->coninhcount > 0 && !recursing)
14068 156 : ereport(ERROR,
14069 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14070 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14071 : constrName, RelationGetRelationName(rel))));
14072 :
14073 : /*
14074 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14075 : *
14076 : * While doing that, we're in a good position to disallow dropping a not-
14077 : * null constraint underneath a primary key, a replica identity index, or
14078 : * a generated identity column.
14079 : */
14080 1024 : if (con->contype == CONSTRAINT_NOTNULL)
14081 : {
14082 314 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14083 314 : AttrNumber attnum = extractNotNullColumn(constraintTup);
14084 : Bitmapset *pkattrs;
14085 : Bitmapset *irattrs;
14086 : HeapTuple atttup;
14087 : Form_pg_attribute attForm;
14088 :
14089 : /* save column name for recursion step */
14090 314 : colname = get_attname(RelationGetRelid(rel), attnum, false);
14091 :
14092 : /*
14093 : * Disallow if it's in the primary key. For partitioned tables we
14094 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14095 : * return NULL if the primary key is invalid; but we still need to
14096 : * protect not-null constraints under such a constraint, so check the
14097 : * slow way.
14098 : */
14099 314 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
14100 :
14101 314 : if (pkattrs == NULL &&
14102 278 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14103 : {
14104 18 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14105 :
14106 18 : if (OidIsValid(pkindex))
14107 : {
14108 0 : Relation pk = relation_open(pkindex, AccessShareLock);
14109 :
14110 0 : pkattrs = NULL;
14111 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14112 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14113 :
14114 0 : relation_close(pk, AccessShareLock);
14115 : }
14116 : }
14117 :
14118 350 : if (pkattrs &&
14119 36 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
14120 24 : ereport(ERROR,
14121 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14122 : errmsg("column \"%s\" is in a primary key",
14123 : get_attname(RelationGetRelid(rel), attnum, false)));
14124 :
14125 : /* Disallow if it's in the replica identity */
14126 290 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
14127 290 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
14128 12 : ereport(ERROR,
14129 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14130 : errmsg("column \"%s\" is in index used as replica identity",
14131 : get_attname(RelationGetRelid(rel), attnum, false)));
14132 :
14133 : /* Disallow if it's a GENERATED AS IDENTITY column */
14134 278 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
14135 278 : if (!HeapTupleIsValid(atttup))
14136 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14137 : attnum, RelationGetRelid(rel));
14138 278 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14139 278 : if (attForm->attidentity != '\0')
14140 0 : ereport(ERROR,
14141 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14142 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
14143 : get_attname(RelationGetRelid(rel), attnum,
14144 : false),
14145 : RelationGetRelationName(rel)));
14146 :
14147 : /* All good -- reset attnotnull if needed */
14148 278 : if (attForm->attnotnull)
14149 : {
14150 278 : attForm->attnotnull = false;
14151 278 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14152 : }
14153 :
14154 278 : table_close(attrel, RowExclusiveLock);
14155 : }
14156 :
14157 988 : is_no_inherit_constraint = con->connoinherit;
14158 :
14159 : /*
14160 : * If it's a foreign-key constraint, we'd better lock the referenced table
14161 : * and check that that's not in use, just as we've already done for the
14162 : * constrained table (else we might, eg, be dropping a trigger that has
14163 : * unfired events). But we can/must skip that in the self-referential
14164 : * case.
14165 : */
14166 988 : if (con->contype == CONSTRAINT_FOREIGN &&
14167 168 : con->confrelid != RelationGetRelid(rel))
14168 : {
14169 : Relation frel;
14170 :
14171 : /* Must match lock taken by RemoveTriggerById: */
14172 168 : frel = table_open(con->confrelid, AccessExclusiveLock);
14173 168 : CheckAlterTableIsSafe(frel);
14174 162 : table_close(frel, NoLock);
14175 : }
14176 :
14177 : /*
14178 : * Perform the actual constraint deletion
14179 : */
14180 982 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14181 982 : performDeletion(&conobj, behavior, 0);
14182 :
14183 : /*
14184 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14185 : * are dropped via the dependency mechanism, so we're done here.
14186 : */
14187 946 : if (con->contype != CONSTRAINT_CHECK &&
14188 628 : con->contype != CONSTRAINT_NOTNULL &&
14189 350 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14190 : {
14191 78 : table_close(conrel, RowExclusiveLock);
14192 78 : return conobj;
14193 : }
14194 :
14195 : /*
14196 : * Propagate to children as appropriate. Unlike most other ALTER
14197 : * routines, we have to do this one level of recursion at a time; we can't
14198 : * use find_all_inheritors to do it in one pass.
14199 : */
14200 868 : if (!is_no_inherit_constraint)
14201 584 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14202 : else
14203 284 : children = NIL;
14204 :
14205 2108 : foreach_oid(childrelid, children)
14206 : {
14207 : Relation childrel;
14208 : HeapTuple tuple;
14209 : Form_pg_constraint childcon;
14210 :
14211 : /* find_inheritance_children already got lock */
14212 384 : childrel = table_open(childrelid, NoLock);
14213 384 : CheckAlterTableIsSafe(childrel);
14214 :
14215 : /*
14216 : * We search for not-null constraints by column name, and others by
14217 : * constraint name.
14218 : */
14219 384 : if (con->contype == CONSTRAINT_NOTNULL)
14220 : {
14221 148 : tuple = findNotNullConstraint(childrelid, colname);
14222 148 : if (!HeapTupleIsValid(tuple))
14223 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14224 : colname, RelationGetRelid(childrel));
14225 : }
14226 : else
14227 : {
14228 : SysScanDesc scan;
14229 : ScanKeyData skey[3];
14230 :
14231 236 : ScanKeyInit(&skey[0],
14232 : Anum_pg_constraint_conrelid,
14233 : BTEqualStrategyNumber, F_OIDEQ,
14234 : ObjectIdGetDatum(childrelid));
14235 236 : ScanKeyInit(&skey[1],
14236 : Anum_pg_constraint_contypid,
14237 : BTEqualStrategyNumber, F_OIDEQ,
14238 : ObjectIdGetDatum(InvalidOid));
14239 236 : ScanKeyInit(&skey[2],
14240 : Anum_pg_constraint_conname,
14241 : BTEqualStrategyNumber, F_NAMEEQ,
14242 : CStringGetDatum(constrName));
14243 236 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14244 : true, NULL, 3, skey);
14245 : /* There can only be one, so no need to loop */
14246 236 : tuple = systable_getnext(scan);
14247 236 : if (!HeapTupleIsValid(tuple))
14248 0 : ereport(ERROR,
14249 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14250 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14251 : constrName,
14252 : RelationGetRelationName(childrel))));
14253 236 : tuple = heap_copytuple(tuple);
14254 236 : systable_endscan(scan);
14255 : }
14256 :
14257 384 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14258 :
14259 : /* Right now only CHECK and not-null constraints can be inherited */
14260 384 : if (childcon->contype != CONSTRAINT_CHECK &&
14261 148 : childcon->contype != CONSTRAINT_NOTNULL)
14262 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14263 :
14264 384 : if (childcon->coninhcount <= 0) /* shouldn't happen */
14265 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14266 : childrelid, NameStr(childcon->conname));
14267 :
14268 384 : if (recurse)
14269 : {
14270 : /*
14271 : * If the child constraint has other definition sources, just
14272 : * decrement its inheritance count; if not, recurse to delete it.
14273 : */
14274 282 : if (childcon->coninhcount == 1 && !childcon->conislocal)
14275 : {
14276 : /* Time to delete this child constraint, too */
14277 210 : dropconstraint_internal(childrel, tuple, behavior,
14278 : recurse, true, missing_ok,
14279 : lockmode);
14280 : }
14281 : else
14282 : {
14283 : /* Child constraint must survive my deletion */
14284 72 : childcon->coninhcount--;
14285 72 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14286 :
14287 : /* Make update visible */
14288 72 : CommandCounterIncrement();
14289 : }
14290 : }
14291 : else
14292 : {
14293 : /*
14294 : * If we were told to drop ONLY in this table (no recursion) and
14295 : * there are no further parents for this constraint, we need to
14296 : * mark the inheritors' constraints as locally defined rather than
14297 : * inherited.
14298 : */
14299 102 : childcon->coninhcount--;
14300 102 : if (childcon->coninhcount == 0)
14301 102 : childcon->conislocal = true;
14302 :
14303 102 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14304 :
14305 : /* Make update visible */
14306 102 : CommandCounterIncrement();
14307 : }
14308 :
14309 378 : heap_freetuple(tuple);
14310 :
14311 378 : table_close(childrel, NoLock);
14312 : }
14313 :
14314 862 : table_close(conrel, RowExclusiveLock);
14315 :
14316 862 : return conobj;
14317 : }
14318 :
14319 : /*
14320 : * ALTER COLUMN TYPE
14321 : *
14322 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14323 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14324 : * transformed (and must be, because we rely on some transformed fields).
14325 : *
14326 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14327 : * table will be done "in parallel" during phase 3, so all the USING
14328 : * expressions should be parsed assuming the original column types. Also,
14329 : * this allows a USING expression to refer to a field that will be dropped.
14330 : *
14331 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14332 : * the first two execution steps in phase 2; they must not see the effects
14333 : * of any other subcommand types, since the USING expressions are parsed
14334 : * against the unmodified table's state.
14335 : */
14336 : static void
14337 1300 : ATPrepAlterColumnType(List **wqueue,
14338 : AlteredTableInfo *tab, Relation rel,
14339 : bool recurse, bool recursing,
14340 : AlterTableCmd *cmd, LOCKMODE lockmode,
14341 : AlterTableUtilityContext *context)
14342 : {
14343 1300 : char *colName = cmd->name;
14344 1300 : ColumnDef *def = (ColumnDef *) cmd->def;
14345 1300 : TypeName *typeName = def->typeName;
14346 1300 : Node *transform = def->cooked_default;
14347 : HeapTuple tuple;
14348 : Form_pg_attribute attTup;
14349 : AttrNumber attnum;
14350 : Oid targettype;
14351 : int32 targettypmod;
14352 : Oid targetcollid;
14353 : NewColumnValue *newval;
14354 1300 : ParseState *pstate = make_parsestate(NULL);
14355 : AclResult aclresult;
14356 : bool is_expr;
14357 :
14358 1300 : pstate->p_sourcetext = context->queryString;
14359 :
14360 1300 : if (rel->rd_rel->reloftype && !recursing)
14361 6 : ereport(ERROR,
14362 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14363 : errmsg("cannot alter column type of typed table"),
14364 : parser_errposition(pstate, def->location)));
14365 :
14366 : /* lookup the attribute so we can check inheritance status */
14367 1294 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14368 1294 : if (!HeapTupleIsValid(tuple))
14369 0 : ereport(ERROR,
14370 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14371 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14372 : colName, RelationGetRelationName(rel)),
14373 : parser_errposition(pstate, def->location)));
14374 1294 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14375 1294 : attnum = attTup->attnum;
14376 :
14377 : /* Can't alter a system attribute */
14378 1294 : if (attnum <= 0)
14379 6 : ereport(ERROR,
14380 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14381 : errmsg("cannot alter system column \"%s\"", colName),
14382 : parser_errposition(pstate, def->location)));
14383 :
14384 : /*
14385 : * Cannot specify USING when altering type of a generated column, because
14386 : * that would violate the generation expression.
14387 : */
14388 1288 : if (attTup->attgenerated && def->cooked_default)
14389 12 : ereport(ERROR,
14390 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14391 : errmsg("cannot specify USING when altering type of generated column"),
14392 : errdetail("Column \"%s\" is a generated column.", colName),
14393 : parser_errposition(pstate, def->location)));
14394 :
14395 : /*
14396 : * Don't alter inherited columns. At outer level, there had better not be
14397 : * any inherited definition; when recursing, we assume this was checked at
14398 : * the parent level (see below).
14399 : */
14400 1276 : if (attTup->attinhcount > 0 && !recursing)
14401 6 : ereport(ERROR,
14402 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14403 : errmsg("cannot alter inherited column \"%s\"", colName),
14404 : parser_errposition(pstate, def->location)));
14405 :
14406 : /* Don't alter columns used in the partition key */
14407 1270 : if (has_partition_attrs(rel,
14408 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
14409 : &is_expr))
14410 18 : ereport(ERROR,
14411 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14412 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14413 : colName, RelationGetRelationName(rel)),
14414 : parser_errposition(pstate, def->location)));
14415 :
14416 : /* Look up the target type */
14417 1252 : typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14418 :
14419 1246 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14420 1246 : if (aclresult != ACLCHECK_OK)
14421 12 : aclcheck_error_type(aclresult, targettype);
14422 :
14423 : /* And the collation */
14424 1234 : targetcollid = GetColumnDefCollation(pstate, def, targettype);
14425 :
14426 : /* make sure datatype is legal for a column */
14427 2456 : CheckAttributeType(colName, targettype, targetcollid,
14428 1228 : list_make1_oid(rel->rd_rel->reltype),
14429 1228 : (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14430 :
14431 1216 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14432 : {
14433 : /* do nothing */
14434 : }
14435 1180 : else if (tab->relkind == RELKIND_RELATION ||
14436 202 : tab->relkind == RELKIND_PARTITIONED_TABLE)
14437 : {
14438 : /*
14439 : * Set up an expression to transform the old data value to the new
14440 : * type. If a USING option was given, use the expression as
14441 : * transformed by transformAlterTableStmt, else just take the old
14442 : * value and try to coerce it. We do this first so that type
14443 : * incompatibility can be detected before we waste effort, and because
14444 : * we need the expression to be parsed against the original table row
14445 : * type.
14446 : */
14447 1044 : if (!transform)
14448 : {
14449 816 : transform = (Node *) makeVar(1, attnum,
14450 : attTup->atttypid, attTup->atttypmod,
14451 : attTup->attcollation,
14452 : 0);
14453 : }
14454 :
14455 1044 : transform = coerce_to_target_type(pstate,
14456 : transform, exprType(transform),
14457 : targettype, targettypmod,
14458 : COERCION_ASSIGNMENT,
14459 : COERCE_IMPLICIT_CAST,
14460 : -1);
14461 1044 : if (transform == NULL)
14462 : {
14463 : /* error text depends on whether USING was specified or not */
14464 24 : if (def->cooked_default != NULL)
14465 6 : ereport(ERROR,
14466 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14467 : errmsg("result of USING clause for column \"%s\""
14468 : " cannot be cast automatically to type %s",
14469 : colName, format_type_be(targettype)),
14470 : errhint("You might need to add an explicit cast.")));
14471 : else
14472 18 : ereport(ERROR,
14473 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14474 : errmsg("column \"%s\" cannot be cast automatically to type %s",
14475 : colName, format_type_be(targettype)),
14476 : !attTup->attgenerated ?
14477 : /* translator: USING is SQL, don't translate it */
14478 : errhint("You might need to specify \"USING %s::%s\".",
14479 : quote_identifier(colName),
14480 : format_type_with_typemod(targettype,
14481 : targettypmod)) : 0));
14482 : }
14483 :
14484 : /* Fix collations after all else */
14485 1020 : assign_expr_collations(pstate, transform);
14486 :
14487 : /* Expand virtual generated columns in the expr. */
14488 1020 : transform = expand_generated_columns_in_expr(transform, rel, 1);
14489 :
14490 : /* Plan the expr now so we can accurately assess the need to rewrite. */
14491 1020 : transform = (Node *) expression_planner((Expr *) transform);
14492 :
14493 : /*
14494 : * Add a work queue item to make ATRewriteTable update the column
14495 : * contents.
14496 : */
14497 1020 : newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
14498 1020 : newval->attnum = attnum;
14499 1020 : newval->expr = (Expr *) transform;
14500 1020 : newval->is_generated = false;
14501 :
14502 1020 : tab->newvals = lappend(tab->newvals, newval);
14503 1020 : if (ATColumnChangeRequiresRewrite(transform, attnum))
14504 824 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
14505 : }
14506 136 : else if (transform)
14507 12 : ereport(ERROR,
14508 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14509 : errmsg("\"%s\" is not a table",
14510 : RelationGetRelationName(rel))));
14511 :
14512 1180 : if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14513 : {
14514 : /*
14515 : * For relations or columns without storage, do this check now.
14516 : * Regular tables will check it later when the table is being
14517 : * rewritten.
14518 : */
14519 226 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14520 : }
14521 :
14522 1132 : ReleaseSysCache(tuple);
14523 :
14524 : /*
14525 : * Recurse manually by queueing a new command for each child, if
14526 : * necessary. We cannot apply ATSimpleRecursion here because we need to
14527 : * remap attribute numbers in the USING expression, if any.
14528 : *
14529 : * If we are told not to recurse, there had better not be any child
14530 : * tables; else the alter would put them out of step.
14531 : */
14532 1132 : if (recurse)
14533 : {
14534 874 : Oid relid = RelationGetRelid(rel);
14535 : List *child_oids,
14536 : *child_numparents;
14537 : ListCell *lo,
14538 : *li;
14539 :
14540 874 : child_oids = find_all_inheritors(relid, lockmode,
14541 : &child_numparents);
14542 :
14543 : /*
14544 : * find_all_inheritors does the recursive search of the inheritance
14545 : * hierarchy, so all we have to do is process all of the relids in the
14546 : * list that it returns.
14547 : */
14548 1956 : forboth(lo, child_oids, li, child_numparents)
14549 : {
14550 1106 : Oid childrelid = lfirst_oid(lo);
14551 1106 : int numparents = lfirst_int(li);
14552 : Relation childrel;
14553 : HeapTuple childtuple;
14554 : Form_pg_attribute childattTup;
14555 :
14556 1106 : if (childrelid == relid)
14557 874 : continue;
14558 :
14559 : /* find_all_inheritors already got lock */
14560 232 : childrel = relation_open(childrelid, NoLock);
14561 232 : CheckAlterTableIsSafe(childrel);
14562 :
14563 : /*
14564 : * Verify that the child doesn't have any inherited definitions of
14565 : * this column that came from outside this inheritance hierarchy.
14566 : * (renameatt makes a similar test, though in a different way
14567 : * because of its different recursion mechanism.)
14568 : */
14569 232 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14570 : colName);
14571 232 : if (!HeapTupleIsValid(childtuple))
14572 0 : ereport(ERROR,
14573 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14574 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14575 : colName, RelationGetRelationName(childrel))));
14576 232 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14577 :
14578 232 : if (childattTup->attinhcount > numparents)
14579 6 : ereport(ERROR,
14580 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14581 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14582 : colName, RelationGetRelationName(childrel))));
14583 :
14584 226 : ReleaseSysCache(childtuple);
14585 :
14586 : /*
14587 : * Remap the attribute numbers. If no USING expression was
14588 : * specified, there is no need for this step.
14589 : */
14590 226 : if (def->cooked_default)
14591 : {
14592 : AttrMap *attmap;
14593 : bool found_whole_row;
14594 :
14595 : /* create a copy to scribble on */
14596 78 : cmd = copyObject(cmd);
14597 :
14598 78 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14599 : RelationGetDescr(rel),
14600 : false);
14601 156 : ((ColumnDef *) cmd->def)->cooked_default =
14602 78 : map_variable_attnos(def->cooked_default,
14603 : 1, 0,
14604 : attmap,
14605 : InvalidOid, &found_whole_row);
14606 78 : if (found_whole_row)
14607 6 : ereport(ERROR,
14608 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14609 : errmsg("cannot convert whole-row table reference"),
14610 : errdetail("USING expression contains a whole-row table reference.")));
14611 72 : pfree(attmap);
14612 : }
14613 220 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14614 208 : relation_close(childrel, NoLock);
14615 : }
14616 : }
14617 308 : else if (!recursing &&
14618 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
14619 0 : ereport(ERROR,
14620 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14621 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
14622 : colName)));
14623 :
14624 1108 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14625 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14626 1102 : }
14627 :
14628 : /*
14629 : * When the data type of a column is changed, a rewrite might not be required
14630 : * if the new type is sufficiently identical to the old one, and the USING
14631 : * clause isn't trying to insert some other value. It's safe to skip the
14632 : * rewrite in these cases:
14633 : *
14634 : * - the old type is binary coercible to the new type
14635 : * - the new type is an unconstrained domain over the old type
14636 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14637 : *
14638 : * In the case of a constrained domain, we could get by with scanning the
14639 : * table and checking the constraint rather than actually rewriting it, but we
14640 : * don't currently try to do that.
14641 : */
14642 : static bool
14643 1020 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
14644 : {
14645 : Assert(expr != NULL);
14646 :
14647 : for (;;)
14648 : {
14649 : /* only one varno, so no need to check that */
14650 1138 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14651 196 : return false;
14652 942 : else if (IsA(expr, RelabelType))
14653 106 : expr = (Node *) ((RelabelType *) expr)->arg;
14654 836 : else if (IsA(expr, CoerceToDomain))
14655 : {
14656 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
14657 :
14658 0 : if (DomainHasConstraints(d->resulttype))
14659 0 : return true;
14660 0 : expr = (Node *) d->arg;
14661 : }
14662 836 : else if (IsA(expr, FuncExpr))
14663 : {
14664 630 : FuncExpr *f = (FuncExpr *) expr;
14665 :
14666 630 : switch (f->funcid)
14667 : {
14668 18 : case F_TIMESTAMPTZ_TIMESTAMP:
14669 : case F_TIMESTAMP_TIMESTAMPTZ:
14670 18 : if (TimestampTimestampTzRequiresRewrite())
14671 6 : return true;
14672 : else
14673 12 : expr = linitial(f->args);
14674 12 : break;
14675 612 : default:
14676 612 : return true;
14677 : }
14678 : }
14679 : else
14680 206 : return true;
14681 : }
14682 : }
14683 :
14684 : /*
14685 : * ALTER COLUMN .. SET DATA TYPE
14686 : *
14687 : * Return the address of the modified column.
14688 : */
14689 : static ObjectAddress
14690 1066 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
14691 : AlterTableCmd *cmd, LOCKMODE lockmode)
14692 : {
14693 1066 : char *colName = cmd->name;
14694 1066 : ColumnDef *def = (ColumnDef *) cmd->def;
14695 1066 : TypeName *typeName = def->typeName;
14696 : HeapTuple heapTup;
14697 : Form_pg_attribute attTup,
14698 : attOldTup;
14699 : AttrNumber attnum;
14700 : HeapTuple typeTuple;
14701 : Form_pg_type tform;
14702 : Oid targettype;
14703 : int32 targettypmod;
14704 : Oid targetcollid;
14705 : Node *defaultexpr;
14706 : Relation attrelation;
14707 : Relation depRel;
14708 : ScanKeyData key[3];
14709 : SysScanDesc scan;
14710 : HeapTuple depTup;
14711 : ObjectAddress address;
14712 :
14713 : /*
14714 : * Clear all the missing values if we're rewriting the table, since this
14715 : * renders them pointless.
14716 : */
14717 1066 : if (tab->rewrite)
14718 : {
14719 : Relation newrel;
14720 :
14721 764 : newrel = table_open(RelationGetRelid(rel), NoLock);
14722 764 : RelationClearMissing(newrel);
14723 764 : relation_close(newrel, NoLock);
14724 : /* make sure we don't conflict with later attribute modifications */
14725 764 : CommandCounterIncrement();
14726 : }
14727 :
14728 1066 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14729 :
14730 : /* Look up the target column */
14731 1066 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14732 1066 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14733 0 : ereport(ERROR,
14734 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14735 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14736 : colName, RelationGetRelationName(rel))));
14737 1066 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14738 1066 : attnum = attTup->attnum;
14739 1066 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14740 :
14741 : /* Check for multiple ALTER TYPE on same column --- can't cope */
14742 1066 : if (attTup->atttypid != attOldTup->atttypid ||
14743 1066 : attTup->atttypmod != attOldTup->atttypmod)
14744 0 : ereport(ERROR,
14745 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14746 : errmsg("cannot alter type of column \"%s\" twice",
14747 : colName)));
14748 :
14749 : /* Look up the target type (should not fail, since prep found it) */
14750 1066 : typeTuple = typenameType(NULL, typeName, &targettypmod);
14751 1066 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
14752 1066 : targettype = tform->oid;
14753 : /* And the collation */
14754 1066 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
14755 :
14756 : /*
14757 : * If there is a default expression for the column, get it and ensure we
14758 : * can coerce it to the new datatype. (We must do this before changing
14759 : * the column type, because build_column_default itself will try to
14760 : * coerce, and will not issue the error message we want if it fails.)
14761 : *
14762 : * We remove any implicit coercion steps at the top level of the old
14763 : * default expression; this has been agreed to satisfy the principle of
14764 : * least surprise. (The conversion to the new column type should act like
14765 : * it started from what the user sees as the stored expression, and the
14766 : * implicit coercions aren't going to be shown.)
14767 : */
14768 1066 : if (attTup->atthasdef)
14769 : {
14770 92 : defaultexpr = build_column_default(rel, attnum);
14771 : Assert(defaultexpr);
14772 92 : defaultexpr = strip_implicit_coercions(defaultexpr);
14773 92 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14774 : defaultexpr, exprType(defaultexpr),
14775 : targettype, targettypmod,
14776 : COERCION_ASSIGNMENT,
14777 : COERCE_IMPLICIT_CAST,
14778 : -1);
14779 92 : if (defaultexpr == NULL)
14780 : {
14781 6 : if (attTup->attgenerated)
14782 0 : ereport(ERROR,
14783 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14784 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14785 : colName, format_type_be(targettype))));
14786 : else
14787 6 : ereport(ERROR,
14788 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14789 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14790 : colName, format_type_be(targettype))));
14791 : }
14792 : }
14793 : else
14794 974 : defaultexpr = NULL;
14795 :
14796 : /*
14797 : * Find everything that depends on the column (constraints, indexes, etc),
14798 : * and record enough information to let us recreate the objects.
14799 : *
14800 : * The actual recreation does not happen here, but only after we have
14801 : * performed all the individual ALTER TYPE operations. We have to save
14802 : * the info before executing ALTER TYPE, though, else the deparser will
14803 : * get confused.
14804 : */
14805 1060 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
14806 :
14807 : /*
14808 : * Now scan for dependencies of this column on other things. The only
14809 : * things we should find are the dependency on the column datatype and
14810 : * possibly a collation dependency. Those can be removed.
14811 : */
14812 1024 : depRel = table_open(DependRelationId, RowExclusiveLock);
14813 :
14814 1024 : ScanKeyInit(&key[0],
14815 : Anum_pg_depend_classid,
14816 : BTEqualStrategyNumber, F_OIDEQ,
14817 : ObjectIdGetDatum(RelationRelationId));
14818 1024 : ScanKeyInit(&key[1],
14819 : Anum_pg_depend_objid,
14820 : BTEqualStrategyNumber, F_OIDEQ,
14821 : ObjectIdGetDatum(RelationGetRelid(rel)));
14822 1024 : ScanKeyInit(&key[2],
14823 : Anum_pg_depend_objsubid,
14824 : BTEqualStrategyNumber, F_INT4EQ,
14825 : Int32GetDatum((int32) attnum));
14826 :
14827 1024 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
14828 : NULL, 3, key);
14829 :
14830 1028 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14831 : {
14832 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14833 : ObjectAddress foundObject;
14834 :
14835 4 : foundObject.classId = foundDep->refclassid;
14836 4 : foundObject.objectId = foundDep->refobjid;
14837 4 : foundObject.objectSubId = foundDep->refobjsubid;
14838 :
14839 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
14840 0 : elog(ERROR, "found unexpected dependency type '%c'",
14841 : foundDep->deptype);
14842 4 : if (!(foundDep->refclassid == TypeRelationId &&
14843 4 : foundDep->refobjid == attTup->atttypid) &&
14844 0 : !(foundDep->refclassid == CollationRelationId &&
14845 0 : foundDep->refobjid == attTup->attcollation))
14846 0 : elog(ERROR, "found unexpected dependency for column: %s",
14847 : getObjectDescription(&foundObject, false));
14848 :
14849 4 : CatalogTupleDelete(depRel, &depTup->t_self);
14850 : }
14851 :
14852 1024 : systable_endscan(scan);
14853 :
14854 1024 : table_close(depRel, RowExclusiveLock);
14855 :
14856 : /*
14857 : * Here we go --- change the recorded column type and collation. (Note
14858 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14859 : * fix up the missing value if any.
14860 : */
14861 1024 : if (attTup->atthasmissing)
14862 : {
14863 : Datum missingval;
14864 : bool missingNull;
14865 :
14866 : /* if rewrite is true the missing value should already be cleared */
14867 : Assert(tab->rewrite == 0);
14868 :
14869 : /* Get the missing value datum */
14870 6 : missingval = heap_getattr(heapTup,
14871 : Anum_pg_attribute_attmissingval,
14872 : attrelation->rd_att,
14873 : &missingNull);
14874 :
14875 : /* if it's a null array there is nothing to do */
14876 :
14877 6 : if (!missingNull)
14878 : {
14879 : /*
14880 : * Get the datum out of the array and repack it in a new array
14881 : * built with the new type data. We assume that since the table
14882 : * doesn't need rewriting, the actual Datum doesn't need to be
14883 : * changed, only the array metadata.
14884 : */
14885 :
14886 6 : int one = 1;
14887 : bool isNull;
14888 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
14889 6 : bool nullsAtt[Natts_pg_attribute] = {0};
14890 6 : bool replacesAtt[Natts_pg_attribute] = {0};
14891 : HeapTuple newTup;
14892 :
14893 12 : missingval = array_get_element(missingval,
14894 : 1,
14895 : &one,
14896 : 0,
14897 6 : attTup->attlen,
14898 6 : attTup->attbyval,
14899 6 : attTup->attalign,
14900 : &isNull);
14901 6 : missingval = PointerGetDatum(construct_array(&missingval,
14902 : 1,
14903 : targettype,
14904 6 : tform->typlen,
14905 6 : tform->typbyval,
14906 6 : tform->typalign));
14907 :
14908 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14909 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14910 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14911 :
14912 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14913 : valuesAtt, nullsAtt, replacesAtt);
14914 6 : heap_freetuple(heapTup);
14915 6 : heapTup = newTup;
14916 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14917 : }
14918 : }
14919 :
14920 1024 : attTup->atttypid = targettype;
14921 1024 : attTup->atttypmod = targettypmod;
14922 1024 : attTup->attcollation = targetcollid;
14923 1024 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14924 0 : ereport(ERROR,
14925 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14926 : errmsg("too many array dimensions"));
14927 1024 : attTup->attndims = list_length(typeName->arrayBounds);
14928 1024 : attTup->attlen = tform->typlen;
14929 1024 : attTup->attbyval = tform->typbyval;
14930 1024 : attTup->attalign = tform->typalign;
14931 1024 : attTup->attstorage = tform->typstorage;
14932 1024 : attTup->attcompression = InvalidCompressionMethod;
14933 :
14934 1024 : ReleaseSysCache(typeTuple);
14935 :
14936 1024 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14937 :
14938 1024 : table_close(attrelation, RowExclusiveLock);
14939 :
14940 : /* Install dependencies on new datatype and collation */
14941 1024 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
14942 1024 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
14943 :
14944 : /*
14945 : * Drop any pg_statistic entry for the column, since it's now wrong type
14946 : */
14947 1024 : RemoveStatistics(RelationGetRelid(rel), attnum);
14948 :
14949 1024 : InvokeObjectPostAlterHook(RelationRelationId,
14950 : RelationGetRelid(rel), attnum);
14951 :
14952 : /*
14953 : * Update the default, if present, by brute force --- remove and re-add
14954 : * the default. Probably unsafe to take shortcuts, since the new version
14955 : * may well have additional dependencies. (It's okay to do this now,
14956 : * rather than after other ALTER TYPE commands, since the default won't
14957 : * depend on other column types.)
14958 : */
14959 1024 : if (defaultexpr)
14960 : {
14961 : /*
14962 : * If it's a GENERATED default, drop its dependency records, in
14963 : * particular its INTERNAL dependency on the column, which would
14964 : * otherwise cause dependency.c to refuse to perform the deletion.
14965 : */
14966 86 : if (attTup->attgenerated)
14967 : {
14968 36 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14969 :
14970 36 : if (!OidIsValid(attrdefoid))
14971 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14972 : RelationGetRelid(rel), attnum);
14973 36 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14974 : }
14975 :
14976 : /*
14977 : * Make updates-so-far visible, particularly the new pg_attribute row
14978 : * which will be updated again.
14979 : */
14980 86 : CommandCounterIncrement();
14981 :
14982 : /*
14983 : * We use RESTRICT here for safety, but at present we do not expect
14984 : * anything to depend on the default.
14985 : */
14986 86 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
14987 : true);
14988 :
14989 86 : (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
14990 : }
14991 :
14992 1024 : ObjectAddressSubSet(address, RelationRelationId,
14993 : RelationGetRelid(rel), attnum);
14994 :
14995 : /* Cleanup */
14996 1024 : heap_freetuple(heapTup);
14997 :
14998 1024 : return address;
14999 : }
15000 :
15001 : /*
15002 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
15003 : * that depends on the column (constraints, indexes, etc), and record enough
15004 : * information to let us recreate the objects.
15005 : */
15006 : static void
15007 1138 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
15008 : Relation rel, AttrNumber attnum, const char *colName)
15009 : {
15010 : Relation depRel;
15011 : ScanKeyData key[3];
15012 : SysScanDesc scan;
15013 : HeapTuple depTup;
15014 :
15015 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15016 :
15017 1138 : depRel = table_open(DependRelationId, RowExclusiveLock);
15018 :
15019 1138 : ScanKeyInit(&key[0],
15020 : Anum_pg_depend_refclassid,
15021 : BTEqualStrategyNumber, F_OIDEQ,
15022 : ObjectIdGetDatum(RelationRelationId));
15023 1138 : ScanKeyInit(&key[1],
15024 : Anum_pg_depend_refobjid,
15025 : BTEqualStrategyNumber, F_OIDEQ,
15026 : ObjectIdGetDatum(RelationGetRelid(rel)));
15027 1138 : ScanKeyInit(&key[2],
15028 : Anum_pg_depend_refobjsubid,
15029 : BTEqualStrategyNumber, F_INT4EQ,
15030 : Int32GetDatum((int32) attnum));
15031 :
15032 1138 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15033 : NULL, 3, key);
15034 :
15035 2270 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15036 : {
15037 1168 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15038 : ObjectAddress foundObject;
15039 :
15040 1168 : foundObject.classId = foundDep->classid;
15041 1168 : foundObject.objectId = foundDep->objid;
15042 1168 : foundObject.objectSubId = foundDep->objsubid;
15043 :
15044 1168 : switch (foundObject.classId)
15045 : {
15046 274 : case RelationRelationId:
15047 : {
15048 274 : char relKind = get_rel_relkind(foundObject.objectId);
15049 :
15050 274 : if (relKind == RELKIND_INDEX ||
15051 : relKind == RELKIND_PARTITIONED_INDEX)
15052 : {
15053 : Assert(foundObject.objectSubId == 0);
15054 236 : RememberIndexForRebuilding(foundObject.objectId, tab);
15055 : }
15056 38 : else if (relKind == RELKIND_SEQUENCE)
15057 : {
15058 : /*
15059 : * This must be a SERIAL column's sequence. We need
15060 : * not do anything to it.
15061 : */
15062 : Assert(foundObject.objectSubId == 0);
15063 : }
15064 : else
15065 : {
15066 : /* Not expecting any other direct dependencies... */
15067 0 : elog(ERROR, "unexpected object depending on column: %s",
15068 : getObjectDescription(&foundObject, false));
15069 : }
15070 274 : break;
15071 : }
15072 :
15073 680 : case ConstraintRelationId:
15074 : Assert(foundObject.objectSubId == 0);
15075 680 : RememberConstraintForRebuilding(foundObject.objectId, tab);
15076 680 : break;
15077 :
15078 0 : case ProcedureRelationId:
15079 :
15080 : /*
15081 : * A new-style SQL function can depend on a column, if that
15082 : * column is referenced in the parsed function body. Ideally
15083 : * we'd automatically update the function by deparsing and
15084 : * reparsing it, but that's risky and might well fail anyhow.
15085 : * FIXME someday.
15086 : *
15087 : * This is only a problem for AT_AlterColumnType, not
15088 : * AT_SetExpression.
15089 : */
15090 0 : if (subtype == AT_AlterColumnType)
15091 0 : ereport(ERROR,
15092 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15093 : errmsg("cannot alter type of a column used by a function or procedure"),
15094 : errdetail("%s depends on column \"%s\"",
15095 : getObjectDescription(&foundObject, false),
15096 : colName)));
15097 0 : break;
15098 :
15099 12 : case RewriteRelationId:
15100 :
15101 : /*
15102 : * View/rule bodies have pretty much the same issues as
15103 : * function bodies. FIXME someday.
15104 : */
15105 12 : if (subtype == AT_AlterColumnType)
15106 12 : ereport(ERROR,
15107 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15108 : errmsg("cannot alter type of a column used by a view or rule"),
15109 : errdetail("%s depends on column \"%s\"",
15110 : getObjectDescription(&foundObject, false),
15111 : colName)));
15112 0 : break;
15113 :
15114 0 : case TriggerRelationId:
15115 :
15116 : /*
15117 : * A trigger can depend on a column because the column is
15118 : * specified as an update target, or because the column is
15119 : * used in the trigger's WHEN condition. The first case would
15120 : * not require any extra work, but the second case would
15121 : * require updating the WHEN expression, which has the same
15122 : * issues as above. Since we can't easily tell which case
15123 : * applies, we punt for both. FIXME someday.
15124 : */
15125 0 : if (subtype == AT_AlterColumnType)
15126 0 : ereport(ERROR,
15127 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15128 : errmsg("cannot alter type of a column used in a trigger definition"),
15129 : errdetail("%s depends on column \"%s\"",
15130 : getObjectDescription(&foundObject, false),
15131 : colName)));
15132 0 : break;
15133 :
15134 0 : case PolicyRelationId:
15135 :
15136 : /*
15137 : * A policy can depend on a column because the column is
15138 : * specified in the policy's USING or WITH CHECK qual
15139 : * expressions. It might be possible to rewrite and recheck
15140 : * the policy expression, but punt for now. It's certainly
15141 : * easy enough to remove and recreate the policy; still, FIXME
15142 : * someday.
15143 : */
15144 0 : if (subtype == AT_AlterColumnType)
15145 0 : ereport(ERROR,
15146 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15147 : errmsg("cannot alter type of a column used in a policy definition"),
15148 : errdetail("%s depends on column \"%s\"",
15149 : getObjectDescription(&foundObject, false),
15150 : colName)));
15151 0 : break;
15152 :
15153 188 : case AttrDefaultRelationId:
15154 : {
15155 188 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
15156 :
15157 188 : if (col.objectId == RelationGetRelid(rel) &&
15158 188 : col.objectSubId == attnum)
15159 : {
15160 : /*
15161 : * Ignore the column's own default expression. The
15162 : * caller deals with it.
15163 : */
15164 : }
15165 : else
15166 : {
15167 : /*
15168 : * This must be a reference from the expression of a
15169 : * generated column elsewhere in the same table.
15170 : * Changing the type/generated expression of a column
15171 : * that is used by a generated column is not allowed
15172 : * by SQL standard, so just punt for now. It might be
15173 : * doable with some thinking and effort.
15174 : */
15175 24 : if (subtype == AT_AlterColumnType)
15176 24 : ereport(ERROR,
15177 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15178 : errmsg("cannot alter type of a column used by a generated column"),
15179 : errdetail("Column \"%s\" is used by generated column \"%s\".",
15180 : colName,
15181 : get_attname(col.objectId,
15182 : col.objectSubId,
15183 : false))));
15184 : }
15185 164 : break;
15186 : }
15187 :
15188 14 : case StatisticExtRelationId:
15189 :
15190 : /*
15191 : * Give the extended-stats machinery a chance to fix anything
15192 : * that this column type change would break.
15193 : */
15194 14 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
15195 14 : break;
15196 :
15197 0 : case PublicationRelRelationId:
15198 :
15199 : /*
15200 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15201 : * clause. Same issues as above. FIXME someday.
15202 : */
15203 0 : if (subtype == AT_AlterColumnType)
15204 0 : ereport(ERROR,
15205 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15206 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
15207 : errdetail("%s depends on column \"%s\"",
15208 : getObjectDescription(&foundObject, false),
15209 : colName)));
15210 0 : break;
15211 :
15212 0 : default:
15213 :
15214 : /*
15215 : * We don't expect any other sorts of objects to depend on a
15216 : * column.
15217 : */
15218 0 : elog(ERROR, "unexpected object depending on column: %s",
15219 : getObjectDescription(&foundObject, false));
15220 : break;
15221 : }
15222 : }
15223 :
15224 1102 : systable_endscan(scan);
15225 1102 : table_close(depRel, NoLock);
15226 1102 : }
15227 :
15228 : /*
15229 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
15230 : * needs to be reset.
15231 : */
15232 : static void
15233 444 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
15234 : {
15235 444 : if (!get_index_isreplident(indoid))
15236 426 : return;
15237 :
15238 18 : if (tab->replicaIdentityIndex)
15239 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15240 :
15241 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
15242 : }
15243 :
15244 : /*
15245 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
15246 : */
15247 : static void
15248 444 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
15249 : {
15250 444 : if (!get_index_isclustered(indoid))
15251 426 : return;
15252 :
15253 18 : if (tab->clusterOnIndex)
15254 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15255 :
15256 18 : tab->clusterOnIndex = get_rel_name(indoid);
15257 : }
15258 :
15259 : /*
15260 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15261 : * to be rebuilt (which we might already know).
15262 : */
15263 : static void
15264 692 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
15265 : {
15266 : /*
15267 : * This de-duplication check is critical for two independent reasons: we
15268 : * mustn't try to recreate the same constraint twice, and if a constraint
15269 : * depends on more than one column whose type is to be altered, we must
15270 : * capture its definition string before applying any of the column type
15271 : * changes. ruleutils.c will get confused if we ask again later.
15272 : */
15273 692 : if (!list_member_oid(tab->changedConstraintOids, conoid))
15274 : {
15275 : /* OK, capture the constraint's existing definition string */
15276 602 : char *defstring = pg_get_constraintdef_command(conoid);
15277 : Oid indoid;
15278 :
15279 : /*
15280 : * It is critical to create not-null constraints ahead of primary key
15281 : * indexes; otherwise, the not-null constraint would be created by the
15282 : * primary key, and the constraint name would be wrong.
15283 : */
15284 602 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15285 : {
15286 198 : tab->changedConstraintOids = lcons_oid(conoid,
15287 : tab->changedConstraintOids);
15288 198 : tab->changedConstraintDefs = lcons(defstring,
15289 : tab->changedConstraintDefs);
15290 : }
15291 : else
15292 : {
15293 :
15294 404 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
15295 : conoid);
15296 404 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
15297 : defstring);
15298 : }
15299 :
15300 : /*
15301 : * For the index of a constraint, if any, remember if it is used for
15302 : * the table's replica identity or if it is a clustered index, so that
15303 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15304 : * those properties.
15305 : */
15306 602 : indoid = get_constraint_index(conoid);
15307 602 : if (OidIsValid(indoid))
15308 : {
15309 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
15310 228 : RememberClusterOnForRebuilding(indoid, tab);
15311 : }
15312 : }
15313 692 : }
15314 :
15315 : /*
15316 : * Subroutine for ATExecAlterColumnType: remember that an index needs
15317 : * to be rebuilt (which we might already know).
15318 : */
15319 : static void
15320 236 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
15321 : {
15322 : /*
15323 : * This de-duplication check is critical for two independent reasons: we
15324 : * mustn't try to recreate the same index twice, and if an index depends
15325 : * on more than one column whose type is to be altered, we must capture
15326 : * its definition string before applying any of the column type changes.
15327 : * ruleutils.c will get confused if we ask again later.
15328 : */
15329 236 : if (!list_member_oid(tab->changedIndexOids, indoid))
15330 : {
15331 : /*
15332 : * Before adding it as an index-to-rebuild, we'd better see if it
15333 : * belongs to a constraint, and if so rebuild the constraint instead.
15334 : * Typically this check fails, because constraint indexes normally
15335 : * have only dependencies on their constraint. But it's possible for
15336 : * such an index to also have direct dependencies on table columns,
15337 : * for example with a partial exclusion constraint.
15338 : */
15339 228 : Oid conoid = get_index_constraint(indoid);
15340 :
15341 228 : if (OidIsValid(conoid))
15342 : {
15343 12 : RememberConstraintForRebuilding(conoid, tab);
15344 : }
15345 : else
15346 : {
15347 : /* OK, capture the index's existing definition string */
15348 216 : char *defstring = pg_get_indexdef_string(indoid);
15349 :
15350 216 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
15351 : indoid);
15352 216 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
15353 : defstring);
15354 :
15355 : /*
15356 : * Remember if this index is used for the table's replica identity
15357 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15358 : * can queue up commands necessary to restore those properties.
15359 : */
15360 216 : RememberReplicaIdentityForRebuilding(indoid, tab);
15361 216 : RememberClusterOnForRebuilding(indoid, tab);
15362 : }
15363 : }
15364 236 : }
15365 :
15366 : /*
15367 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
15368 : * needs to be rebuilt (which we might already know).
15369 : */
15370 : static void
15371 14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
15372 : {
15373 : /*
15374 : * This de-duplication check is critical for two independent reasons: we
15375 : * mustn't try to recreate the same statistics object twice, and if the
15376 : * statistics object depends on more than one column whose type is to be
15377 : * altered, we must capture its definition string before applying any of
15378 : * the type changes. ruleutils.c will get confused if we ask again later.
15379 : */
15380 14 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15381 : {
15382 : /* OK, capture the statistics object's existing definition string */
15383 14 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
15384 :
15385 14 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
15386 : stxoid);
15387 14 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
15388 : defstring);
15389 : }
15390 14 : }
15391 :
15392 : /*
15393 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15394 : * operations for a particular relation. We have to drop and recreate all the
15395 : * indexes and constraints that depend on the altered columns. We do the
15396 : * actual dropping here, but re-creation is managed by adding work queue
15397 : * entries to do those steps later.
15398 : */
15399 : static void
15400 1132 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
15401 : {
15402 : ObjectAddress obj;
15403 : ObjectAddresses *objects;
15404 : ListCell *def_item;
15405 : ListCell *oid_item;
15406 :
15407 : /*
15408 : * Collect all the constraints and indexes to drop so we can process them
15409 : * in a single call. That way we don't have to worry about dependencies
15410 : * among them.
15411 : */
15412 1132 : objects = new_object_addresses();
15413 :
15414 : /*
15415 : * Re-parse the index and constraint definitions, and attach them to the
15416 : * appropriate work queue entries. We do this before dropping because in
15417 : * the case of a constraint on another table, we might not yet have
15418 : * exclusive lock on the table the constraint is attached to, and we need
15419 : * to get that before reparsing/dropping. (That's possible at least for
15420 : * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15421 : * requires a dependency on the target table's composite type in the other
15422 : * table's constraint expressions.)
15423 : *
15424 : * We can't rely on the output of deparsing to tell us which relation to
15425 : * operate on, because concurrent activity might have made the name
15426 : * resolve differently. Instead, we've got to use the OID of the
15427 : * constraint or index we're processing to figure out which relation to
15428 : * operate on.
15429 : */
15430 1734 : forboth(oid_item, tab->changedConstraintOids,
15431 : def_item, tab->changedConstraintDefs)
15432 : {
15433 602 : Oid oldId = lfirst_oid(oid_item);
15434 : HeapTuple tup;
15435 : Form_pg_constraint con;
15436 : Oid relid;
15437 : Oid confrelid;
15438 : bool conislocal;
15439 :
15440 602 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15441 602 : if (!HeapTupleIsValid(tup)) /* should not happen */
15442 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15443 602 : con = (Form_pg_constraint) GETSTRUCT(tup);
15444 602 : if (OidIsValid(con->conrelid))
15445 588 : relid = con->conrelid;
15446 : else
15447 : {
15448 : /* must be a domain constraint */
15449 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
15450 14 : if (!OidIsValid(relid))
15451 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15452 : }
15453 602 : confrelid = con->confrelid;
15454 602 : conislocal = con->conislocal;
15455 602 : ReleaseSysCache(tup);
15456 :
15457 602 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
15458 602 : add_exact_object_address(&obj, objects);
15459 :
15460 : /*
15461 : * If the constraint is inherited (only), we don't want to inject a
15462 : * new definition here; it'll get recreated when
15463 : * ATAddCheckNNConstraint recurses from adding the parent table's
15464 : * constraint. But we had to carry the info this far so that we can
15465 : * drop the constraint below.
15466 : */
15467 602 : if (!conislocal)
15468 28 : continue;
15469 :
15470 : /*
15471 : * When rebuilding another table's constraint that references the
15472 : * table we're modifying, we might not yet have any lock on the other
15473 : * table, so get one now. We'll need AccessExclusiveLock for the DROP
15474 : * CONSTRAINT step, so there's no value in asking for anything weaker.
15475 : */
15476 574 : if (relid != tab->relid)
15477 42 : LockRelationOid(relid, AccessExclusiveLock);
15478 :
15479 574 : ATPostAlterTypeParse(oldId, relid, confrelid,
15480 574 : (char *) lfirst(def_item),
15481 574 : wqueue, lockmode, tab->rewrite);
15482 : }
15483 1348 : forboth(oid_item, tab->changedIndexOids,
15484 : def_item, tab->changedIndexDefs)
15485 : {
15486 216 : Oid oldId = lfirst_oid(oid_item);
15487 : Oid relid;
15488 :
15489 216 : relid = IndexGetRelation(oldId, false);
15490 216 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15491 216 : (char *) lfirst(def_item),
15492 216 : wqueue, lockmode, tab->rewrite);
15493 :
15494 216 : ObjectAddressSet(obj, RelationRelationId, oldId);
15495 216 : add_exact_object_address(&obj, objects);
15496 : }
15497 :
15498 : /* add dependencies for new statistics */
15499 1146 : forboth(oid_item, tab->changedStatisticsOids,
15500 : def_item, tab->changedStatisticsDefs)
15501 : {
15502 14 : Oid oldId = lfirst_oid(oid_item);
15503 : Oid relid;
15504 :
15505 14 : relid = StatisticsGetRelation(oldId, false);
15506 14 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15507 14 : (char *) lfirst(def_item),
15508 14 : wqueue, lockmode, tab->rewrite);
15509 :
15510 14 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15511 14 : add_exact_object_address(&obj, objects);
15512 : }
15513 :
15514 : /*
15515 : * Queue up command to restore replica identity index marking
15516 : */
15517 1132 : if (tab->replicaIdentityIndex)
15518 : {
15519 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15520 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
15521 :
15522 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15523 18 : subcmd->name = tab->replicaIdentityIndex;
15524 18 : cmd->subtype = AT_ReplicaIdentity;
15525 18 : cmd->def = (Node *) subcmd;
15526 :
15527 : /* do it after indexes and constraints */
15528 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15529 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15530 : }
15531 :
15532 : /*
15533 : * Queue up command to restore marking of index used for cluster.
15534 : */
15535 1132 : if (tab->clusterOnIndex)
15536 : {
15537 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15538 :
15539 18 : cmd->subtype = AT_ClusterOn;
15540 18 : cmd->name = tab->clusterOnIndex;
15541 :
15542 : /* do it after indexes and constraints */
15543 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15544 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15545 : }
15546 :
15547 : /*
15548 : * It should be okay to use DROP_RESTRICT here, since nothing else should
15549 : * be depending on these objects.
15550 : */
15551 1132 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
15552 :
15553 1132 : free_object_addresses(objects);
15554 :
15555 : /*
15556 : * The objects will get recreated during subsequent passes over the work
15557 : * queue.
15558 : */
15559 1132 : }
15560 :
15561 : /*
15562 : * Parse the previously-saved definition string for a constraint, index or
15563 : * statistics object against the newly-established column data type(s), and
15564 : * queue up the resulting command parsetrees for execution.
15565 : *
15566 : * This might fail if, for example, you have a WHERE clause that uses an
15567 : * operator that's not available for the new column type.
15568 : */
15569 : static void
15570 804 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15571 : List **wqueue, LOCKMODE lockmode, bool rewrite)
15572 : {
15573 : List *raw_parsetree_list;
15574 : List *querytree_list;
15575 : ListCell *list_item;
15576 : Relation rel;
15577 :
15578 : /*
15579 : * We expect that we will get only ALTER TABLE and CREATE INDEX
15580 : * statements. Hence, there is no need to pass them through
15581 : * parse_analyze_*() or the rewriter, but instead we need to pass them
15582 : * through parse_utilcmd.c to make them ready for execution.
15583 : */
15584 804 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15585 804 : querytree_list = NIL;
15586 1608 : foreach(list_item, raw_parsetree_list)
15587 : {
15588 804 : RawStmt *rs = lfirst_node(RawStmt, list_item);
15589 804 : Node *stmt = rs->stmt;
15590 :
15591 804 : if (IsA(stmt, IndexStmt))
15592 216 : querytree_list = lappend(querytree_list,
15593 216 : transformIndexStmt(oldRelId,
15594 : (IndexStmt *) stmt,
15595 : cmd));
15596 588 : else if (IsA(stmt, AlterTableStmt))
15597 : {
15598 : List *beforeStmts;
15599 : List *afterStmts;
15600 :
15601 560 : stmt = (Node *) transformAlterTableStmt(oldRelId,
15602 : (AlterTableStmt *) stmt,
15603 : cmd,
15604 : &beforeStmts,
15605 : &afterStmts);
15606 560 : querytree_list = list_concat(querytree_list, beforeStmts);
15607 560 : querytree_list = lappend(querytree_list, stmt);
15608 560 : querytree_list = list_concat(querytree_list, afterStmts);
15609 : }
15610 28 : else if (IsA(stmt, CreateStatsStmt))
15611 14 : querytree_list = lappend(querytree_list,
15612 14 : transformStatsStmt(oldRelId,
15613 : (CreateStatsStmt *) stmt,
15614 : cmd));
15615 : else
15616 14 : querytree_list = lappend(querytree_list, stmt);
15617 : }
15618 :
15619 : /* Caller should already have acquired whatever lock we need. */
15620 804 : rel = relation_open(oldRelId, NoLock);
15621 :
15622 : /*
15623 : * Attach each generated command to the proper place in the work queue.
15624 : * Note this could result in creation of entirely new work-queue entries.
15625 : *
15626 : * Also note that we have to tweak the command subtypes, because it turns
15627 : * out that re-creation of indexes and constraints has to act a bit
15628 : * differently from initial creation.
15629 : */
15630 1608 : foreach(list_item, querytree_list)
15631 : {
15632 804 : Node *stm = (Node *) lfirst(list_item);
15633 : AlteredTableInfo *tab;
15634 :
15635 804 : tab = ATGetQueueEntry(wqueue, rel);
15636 :
15637 804 : if (IsA(stm, IndexStmt))
15638 : {
15639 216 : IndexStmt *stmt = (IndexStmt *) stm;
15640 : AlterTableCmd *newcmd;
15641 :
15642 216 : if (!rewrite)
15643 56 : TryReuseIndex(oldId, stmt);
15644 216 : stmt->reset_default_tblspc = true;
15645 : /* keep the index's comment */
15646 216 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15647 :
15648 216 : newcmd = makeNode(AlterTableCmd);
15649 216 : newcmd->subtype = AT_ReAddIndex;
15650 216 : newcmd->def = (Node *) stmt;
15651 216 : tab->subcmds[AT_PASS_OLD_INDEX] =
15652 216 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15653 : }
15654 588 : else if (IsA(stm, AlterTableStmt))
15655 : {
15656 560 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
15657 : ListCell *lcmd;
15658 :
15659 1120 : foreach(lcmd, stmt->cmds)
15660 : {
15661 560 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
15662 :
15663 560 : if (cmd->subtype == AT_AddIndex)
15664 : {
15665 : IndexStmt *indstmt;
15666 : Oid indoid;
15667 :
15668 228 : indstmt = castNode(IndexStmt, cmd->def);
15669 228 : indoid = get_constraint_index(oldId);
15670 :
15671 228 : if (!rewrite)
15672 48 : TryReuseIndex(indoid, indstmt);
15673 : /* keep any comment on the index */
15674 228 : indstmt->idxcomment = GetComment(indoid,
15675 : RelationRelationId, 0);
15676 228 : indstmt->reset_default_tblspc = true;
15677 :
15678 228 : cmd->subtype = AT_ReAddIndex;
15679 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
15680 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15681 :
15682 : /* recreate any comment on the constraint */
15683 228 : RebuildConstraintComment(tab,
15684 : AT_PASS_OLD_INDEX,
15685 : oldId,
15686 : rel,
15687 : NIL,
15688 228 : indstmt->idxname);
15689 : }
15690 332 : else if (cmd->subtype == AT_AddConstraint)
15691 : {
15692 332 : Constraint *con = castNode(Constraint, cmd->def);
15693 :
15694 332 : con->old_pktable_oid = refRelId;
15695 : /* rewriting neither side of a FK */
15696 332 : if (con->contype == CONSTR_FOREIGN &&
15697 72 : !rewrite && tab->rewrite == 0)
15698 6 : TryReuseForeignKey(oldId, con);
15699 332 : con->reset_default_tblspc = true;
15700 332 : cmd->subtype = AT_ReAddConstraint;
15701 332 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15702 332 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15703 :
15704 : /*
15705 : * Recreate any comment on the constraint. If we have
15706 : * recreated a primary key, then transformTableConstraint
15707 : * has added an unnamed not-null constraint here; skip
15708 : * this in that case.
15709 : */
15710 332 : if (con->conname)
15711 332 : RebuildConstraintComment(tab,
15712 : AT_PASS_OLD_CONSTR,
15713 : oldId,
15714 : rel,
15715 : NIL,
15716 332 : con->conname);
15717 : else
15718 : Assert(con->contype == CONSTR_NOTNULL);
15719 : }
15720 : else
15721 0 : elog(ERROR, "unexpected statement subtype: %d",
15722 : (int) cmd->subtype);
15723 : }
15724 : }
15725 28 : else if (IsA(stm, AlterDomainStmt))
15726 : {
15727 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
15728 :
15729 14 : if (stmt->subtype == 'C') /* ADD CONSTRAINT */
15730 : {
15731 14 : Constraint *con = castNode(Constraint, stmt->def);
15732 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15733 :
15734 14 : cmd->subtype = AT_ReAddDomainConstraint;
15735 14 : cmd->def = (Node *) stmt;
15736 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15737 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15738 :
15739 : /* recreate any comment on the constraint */
15740 14 : RebuildConstraintComment(tab,
15741 : AT_PASS_OLD_CONSTR,
15742 : oldId,
15743 : NULL,
15744 : stmt->typeName,
15745 14 : con->conname);
15746 : }
15747 : else
15748 0 : elog(ERROR, "unexpected statement subtype: %d",
15749 : (int) stmt->subtype);
15750 : }
15751 14 : else if (IsA(stm, CreateStatsStmt))
15752 : {
15753 14 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
15754 : AlterTableCmd *newcmd;
15755 :
15756 : /* keep the statistics object's comment */
15757 14 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15758 :
15759 14 : newcmd = makeNode(AlterTableCmd);
15760 14 : newcmd->subtype = AT_ReAddStatistics;
15761 14 : newcmd->def = (Node *) stmt;
15762 14 : tab->subcmds[AT_PASS_MISC] =
15763 14 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15764 : }
15765 : else
15766 0 : elog(ERROR, "unexpected statement type: %d",
15767 : (int) nodeTag(stm));
15768 : }
15769 :
15770 804 : relation_close(rel, NoLock);
15771 804 : }
15772 :
15773 : /*
15774 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15775 : * for a table or domain constraint that is being rebuilt.
15776 : *
15777 : * objid is the OID of the constraint.
15778 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15779 : * as a string list) for a domain constraint.
15780 : * (We could dig that info, as well as the conname, out of the pg_constraint
15781 : * entry; but callers already have them so might as well pass them.)
15782 : */
15783 : static void
15784 574 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
15785 : Relation rel, List *domname,
15786 : const char *conname)
15787 : {
15788 : CommentStmt *cmd;
15789 : char *comment_str;
15790 : AlterTableCmd *newcmd;
15791 :
15792 : /* Look for comment for object wanted, and leave if none */
15793 574 : comment_str = GetComment(objid, ConstraintRelationId, 0);
15794 574 : if (comment_str == NULL)
15795 484 : return;
15796 :
15797 : /* Build CommentStmt node, copying all input data for safety */
15798 90 : cmd = makeNode(CommentStmt);
15799 90 : if (rel)
15800 : {
15801 78 : cmd->objtype = OBJECT_TABCONSTRAINT;
15802 78 : cmd->object = (Node *)
15803 78 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
15804 : makeString(pstrdup(RelationGetRelationName(rel))),
15805 : makeString(pstrdup(conname)));
15806 : }
15807 : else
15808 : {
15809 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
15810 12 : cmd->object = (Node *)
15811 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
15812 : makeString(pstrdup(conname)));
15813 : }
15814 90 : cmd->comment = comment_str;
15815 :
15816 : /* Append it to list of commands */
15817 90 : newcmd = makeNode(AlterTableCmd);
15818 90 : newcmd->subtype = AT_ReAddComment;
15819 90 : newcmd->def = (Node *) cmd;
15820 90 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15821 : }
15822 :
15823 : /*
15824 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15825 : * for the real analysis, then mutates the IndexStmt based on that verdict.
15826 : */
15827 : static void
15828 104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
15829 : {
15830 104 : if (CheckIndexCompatible(oldId,
15831 104 : stmt->accessMethod,
15832 104 : stmt->indexParams,
15833 104 : stmt->excludeOpNames,
15834 104 : stmt->iswithoutoverlaps))
15835 : {
15836 104 : Relation irel = index_open(oldId, NoLock);
15837 :
15838 : /* If it's a partitioned index, there is no storage to share. */
15839 104 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15840 : {
15841 74 : stmt->oldNumber = irel->rd_locator.relNumber;
15842 74 : stmt->oldCreateSubid = irel->rd_createSubid;
15843 74 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15844 : }
15845 104 : index_close(irel, NoLock);
15846 : }
15847 104 : }
15848 :
15849 : /*
15850 : * Subroutine for ATPostAlterTypeParse().
15851 : *
15852 : * Stash the old P-F equality operator into the Constraint node, for possible
15853 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15854 : * this constraint can be skipped.
15855 : */
15856 : static void
15857 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
15858 : {
15859 : HeapTuple tup;
15860 : Datum adatum;
15861 : ArrayType *arr;
15862 : Oid *rawarr;
15863 : int numkeys;
15864 : int i;
15865 :
15866 : Assert(con->contype == CONSTR_FOREIGN);
15867 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15868 :
15869 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15870 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
15871 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15872 :
15873 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15874 : Anum_pg_constraint_conpfeqop);
15875 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15876 6 : numkeys = ARR_DIMS(arr)[0];
15877 : /* test follows the one in ri_FetchConstraintInfo() */
15878 6 : if (ARR_NDIM(arr) != 1 ||
15879 6 : ARR_HASNULL(arr) ||
15880 6 : ARR_ELEMTYPE(arr) != OIDOID)
15881 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
15882 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
15883 :
15884 : /* stash a List of the operator Oids in our Constraint node */
15885 12 : for (i = 0; i < numkeys; i++)
15886 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15887 :
15888 6 : ReleaseSysCache(tup);
15889 6 : }
15890 :
15891 : /*
15892 : * ALTER COLUMN .. OPTIONS ( ... )
15893 : *
15894 : * Returns the address of the modified column
15895 : */
15896 : static ObjectAddress
15897 172 : ATExecAlterColumnGenericOptions(Relation rel,
15898 : const char *colName,
15899 : List *options,
15900 : LOCKMODE lockmode)
15901 : {
15902 : Relation ftrel;
15903 : Relation attrel;
15904 : ForeignServer *server;
15905 : ForeignDataWrapper *fdw;
15906 : HeapTuple tuple;
15907 : HeapTuple newtuple;
15908 : bool isnull;
15909 : Datum repl_val[Natts_pg_attribute];
15910 : bool repl_null[Natts_pg_attribute];
15911 : bool repl_repl[Natts_pg_attribute];
15912 : Datum datum;
15913 : Form_pg_foreign_table fttableform;
15914 : Form_pg_attribute atttableform;
15915 : AttrNumber attnum;
15916 : ObjectAddress address;
15917 :
15918 172 : if (options == NIL)
15919 0 : return InvalidObjectAddress;
15920 :
15921 : /* First, determine FDW validator associated to the foreign table. */
15922 172 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15923 172 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15924 172 : if (!HeapTupleIsValid(tuple))
15925 0 : ereport(ERROR,
15926 : (errcode(ERRCODE_UNDEFINED_OBJECT),
15927 : errmsg("foreign table \"%s\" does not exist",
15928 : RelationGetRelationName(rel))));
15929 172 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15930 172 : server = GetForeignServer(fttableform->ftserver);
15931 172 : fdw = GetForeignDataWrapper(server->fdwid);
15932 :
15933 172 : table_close(ftrel, AccessShareLock);
15934 172 : ReleaseSysCache(tuple);
15935 :
15936 172 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
15937 172 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15938 172 : if (!HeapTupleIsValid(tuple))
15939 0 : ereport(ERROR,
15940 : (errcode(ERRCODE_UNDEFINED_COLUMN),
15941 : errmsg("column \"%s\" of relation \"%s\" does not exist",
15942 : colName, RelationGetRelationName(rel))));
15943 :
15944 : /* Prevent them from altering a system attribute */
15945 172 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15946 172 : attnum = atttableform->attnum;
15947 172 : if (attnum <= 0)
15948 6 : ereport(ERROR,
15949 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15950 : errmsg("cannot alter system column \"%s\"", colName)));
15951 :
15952 :
15953 : /* Initialize buffers for new tuple values */
15954 166 : memset(repl_val, 0, sizeof(repl_val));
15955 166 : memset(repl_null, false, sizeof(repl_null));
15956 166 : memset(repl_repl, false, sizeof(repl_repl));
15957 :
15958 : /* Extract the current options */
15959 166 : datum = SysCacheGetAttr(ATTNAME,
15960 : tuple,
15961 : Anum_pg_attribute_attfdwoptions,
15962 : &isnull);
15963 166 : if (isnull)
15964 156 : datum = PointerGetDatum(NULL);
15965 :
15966 : /* Transform the options */
15967 166 : datum = transformGenericOptions(AttributeRelationId,
15968 : datum,
15969 : options,
15970 : fdw->fdwvalidator);
15971 :
15972 166 : if (PointerIsValid(DatumGetPointer(datum)))
15973 166 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15974 : else
15975 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15976 :
15977 166 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15978 :
15979 : /* Everything looks good - update the tuple */
15980 :
15981 166 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15982 : repl_val, repl_null, repl_repl);
15983 :
15984 166 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15985 :
15986 166 : InvokeObjectPostAlterHook(RelationRelationId,
15987 : RelationGetRelid(rel),
15988 : atttableform->attnum);
15989 166 : ObjectAddressSubSet(address, RelationRelationId,
15990 : RelationGetRelid(rel), attnum);
15991 :
15992 166 : ReleaseSysCache(tuple);
15993 :
15994 166 : table_close(attrel, RowExclusiveLock);
15995 :
15996 166 : heap_freetuple(newtuple);
15997 :
15998 166 : return address;
15999 : }
16000 :
16001 : /*
16002 : * ALTER TABLE OWNER
16003 : *
16004 : * recursing is true if we are recursing from a table to its indexes,
16005 : * sequences, or toast table. We don't allow the ownership of those things to
16006 : * be changed separately from the parent table. Also, we can skip permission
16007 : * checks (this is necessary not just an optimization, else we'd fail to
16008 : * handle toast tables properly).
16009 : *
16010 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
16011 : * free-standing composite type.
16012 : */
16013 : void
16014 3812 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
16015 : {
16016 : Relation target_rel;
16017 : Relation class_rel;
16018 : HeapTuple tuple;
16019 : Form_pg_class tuple_class;
16020 :
16021 : /*
16022 : * Get exclusive lock till end of transaction on the target table. Use
16023 : * relation_open so that we can work on indexes and sequences.
16024 : */
16025 3812 : target_rel = relation_open(relationOid, lockmode);
16026 :
16027 : /* Get its pg_class tuple, too */
16028 3812 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
16029 :
16030 3812 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16031 3812 : if (!HeapTupleIsValid(tuple))
16032 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
16033 3812 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16034 :
16035 : /* Can we change the ownership of this tuple? */
16036 3812 : switch (tuple_class->relkind)
16037 : {
16038 3466 : case RELKIND_RELATION:
16039 : case RELKIND_VIEW:
16040 : case RELKIND_MATVIEW:
16041 : case RELKIND_FOREIGN_TABLE:
16042 : case RELKIND_PARTITIONED_TABLE:
16043 : /* ok to change owner */
16044 3466 : break;
16045 96 : case RELKIND_INDEX:
16046 96 : if (!recursing)
16047 : {
16048 : /*
16049 : * Because ALTER INDEX OWNER used to be allowed, and in fact
16050 : * is generated by old versions of pg_dump, we give a warning
16051 : * and do nothing rather than erroring out. Also, to avoid
16052 : * unnecessary chatter while restoring those old dumps, say
16053 : * nothing at all if the command would be a no-op anyway.
16054 : */
16055 0 : if (tuple_class->relowner != newOwnerId)
16056 0 : ereport(WARNING,
16057 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16058 : errmsg("cannot change owner of index \"%s\"",
16059 : NameStr(tuple_class->relname)),
16060 : errhint("Change the ownership of the index's table instead.")));
16061 : /* quick hack to exit via the no-op path */
16062 0 : newOwnerId = tuple_class->relowner;
16063 : }
16064 96 : break;
16065 20 : case RELKIND_PARTITIONED_INDEX:
16066 20 : if (recursing)
16067 20 : break;
16068 0 : ereport(ERROR,
16069 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16070 : errmsg("cannot change owner of index \"%s\"",
16071 : NameStr(tuple_class->relname)),
16072 : errhint("Change the ownership of the index's table instead.")));
16073 : break;
16074 180 : case RELKIND_SEQUENCE:
16075 180 : if (!recursing &&
16076 132 : tuple_class->relowner != newOwnerId)
16077 : {
16078 : /* if it's an owned sequence, disallow changing it by itself */
16079 : Oid tableId;
16080 : int32 colId;
16081 :
16082 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16083 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16084 0 : ereport(ERROR,
16085 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16086 : errmsg("cannot change owner of sequence \"%s\"",
16087 : NameStr(tuple_class->relname)),
16088 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
16089 : NameStr(tuple_class->relname),
16090 : get_rel_name(tableId))));
16091 : }
16092 180 : break;
16093 8 : case RELKIND_COMPOSITE_TYPE:
16094 8 : if (recursing)
16095 8 : break;
16096 0 : ereport(ERROR,
16097 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16098 : errmsg("\"%s\" is a composite type",
16099 : NameStr(tuple_class->relname)),
16100 : /* translator: %s is an SQL ALTER command */
16101 : errhint("Use %s instead.",
16102 : "ALTER TYPE")));
16103 : break;
16104 42 : case RELKIND_TOASTVALUE:
16105 42 : if (recursing)
16106 42 : break;
16107 : /* FALL THRU */
16108 : default:
16109 0 : ereport(ERROR,
16110 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16111 : errmsg("cannot change owner of relation \"%s\"",
16112 : NameStr(tuple_class->relname)),
16113 : errdetail_relkind_not_supported(tuple_class->relkind)));
16114 : }
16115 :
16116 : /*
16117 : * If the new owner is the same as the existing owner, consider the
16118 : * command to have succeeded. This is for dump restoration purposes.
16119 : */
16120 3812 : if (tuple_class->relowner != newOwnerId)
16121 : {
16122 : Datum repl_val[Natts_pg_class];
16123 : bool repl_null[Natts_pg_class];
16124 : bool repl_repl[Natts_pg_class];
16125 : Acl *newAcl;
16126 : Datum aclDatum;
16127 : bool isNull;
16128 : HeapTuple newtuple;
16129 :
16130 : /* skip permission checks when recursing to index or toast table */
16131 500 : if (!recursing)
16132 : {
16133 : /* Superusers can always do it */
16134 282 : if (!superuser())
16135 : {
16136 42 : Oid namespaceOid = tuple_class->relnamespace;
16137 : AclResult aclresult;
16138 :
16139 : /* Otherwise, must be owner of the existing object */
16140 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16141 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
16142 0 : RelationGetRelationName(target_rel));
16143 :
16144 : /* Must be able to become new owner */
16145 42 : check_can_set_role(GetUserId(), newOwnerId);
16146 :
16147 : /* New owner must have CREATE privilege on namespace */
16148 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16149 : ACL_CREATE);
16150 30 : if (aclresult != ACLCHECK_OK)
16151 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
16152 0 : get_namespace_name(namespaceOid));
16153 : }
16154 : }
16155 :
16156 488 : memset(repl_null, false, sizeof(repl_null));
16157 488 : memset(repl_repl, false, sizeof(repl_repl));
16158 :
16159 488 : repl_repl[Anum_pg_class_relowner - 1] = true;
16160 488 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16161 :
16162 : /*
16163 : * Determine the modified ACL for the new owner. This is only
16164 : * necessary when the ACL is non-null.
16165 : */
16166 488 : aclDatum = SysCacheGetAttr(RELOID, tuple,
16167 : Anum_pg_class_relacl,
16168 : &isNull);
16169 488 : if (!isNull)
16170 : {
16171 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16172 : tuple_class->relowner, newOwnerId);
16173 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
16174 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16175 : }
16176 :
16177 488 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16178 :
16179 488 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16180 :
16181 488 : heap_freetuple(newtuple);
16182 :
16183 : /*
16184 : * We must similarly update any per-column ACLs to reflect the new
16185 : * owner; for neatness reasons that's split out as a subroutine.
16186 : */
16187 488 : change_owner_fix_column_acls(relationOid,
16188 : tuple_class->relowner,
16189 : newOwnerId);
16190 :
16191 : /*
16192 : * Update owner dependency reference, if any. A composite type has
16193 : * none, because it's tracked for the pg_type entry instead of here;
16194 : * indexes and TOAST tables don't have their own entries either.
16195 : */
16196 488 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16197 480 : tuple_class->relkind != RELKIND_INDEX &&
16198 384 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16199 364 : tuple_class->relkind != RELKIND_TOASTVALUE)
16200 322 : changeDependencyOnOwner(RelationRelationId, relationOid,
16201 : newOwnerId);
16202 :
16203 : /*
16204 : * Also change the ownership of the table's row type, if it has one
16205 : */
16206 488 : if (OidIsValid(tuple_class->reltype))
16207 296 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16208 :
16209 : /*
16210 : * If we are operating on a table or materialized view, also change
16211 : * the ownership of any indexes and sequences that belong to the
16212 : * relation, as well as its toast table (if it has one).
16213 : */
16214 488 : if (tuple_class->relkind == RELKIND_RELATION ||
16215 262 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16216 224 : tuple_class->relkind == RELKIND_MATVIEW ||
16217 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
16218 : {
16219 : List *index_oid_list;
16220 : ListCell *i;
16221 :
16222 : /* Find all the indexes belonging to this relation */
16223 306 : index_oid_list = RelationGetIndexList(target_rel);
16224 :
16225 : /* For each index, recursively change its ownership */
16226 422 : foreach(i, index_oid_list)
16227 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16228 :
16229 306 : list_free(index_oid_list);
16230 : }
16231 :
16232 : /* If it has a toast table, recurse to change its ownership */
16233 488 : if (tuple_class->reltoastrelid != InvalidOid)
16234 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16235 : true, lockmode);
16236 :
16237 : /* If it has dependent sequences, recurse to change them too */
16238 488 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16239 : }
16240 :
16241 3800 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16242 :
16243 3800 : ReleaseSysCache(tuple);
16244 3800 : table_close(class_rel, RowExclusiveLock);
16245 3800 : relation_close(target_rel, NoLock);
16246 3800 : }
16247 :
16248 : /*
16249 : * change_owner_fix_column_acls
16250 : *
16251 : * Helper function for ATExecChangeOwner. Scan the columns of the table
16252 : * and fix any non-null column ACLs to reflect the new owner.
16253 : */
16254 : static void
16255 488 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16256 : {
16257 : Relation attRelation;
16258 : SysScanDesc scan;
16259 : ScanKeyData key[1];
16260 : HeapTuple attributeTuple;
16261 :
16262 488 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16263 488 : ScanKeyInit(&key[0],
16264 : Anum_pg_attribute_attrelid,
16265 : BTEqualStrategyNumber, F_OIDEQ,
16266 : ObjectIdGetDatum(relationOid));
16267 488 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16268 : true, NULL, 1, key);
16269 3386 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16270 : {
16271 2898 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16272 : Datum repl_val[Natts_pg_attribute];
16273 : bool repl_null[Natts_pg_attribute];
16274 : bool repl_repl[Natts_pg_attribute];
16275 : Acl *newAcl;
16276 : Datum aclDatum;
16277 : bool isNull;
16278 : HeapTuple newtuple;
16279 :
16280 : /* Ignore dropped columns */
16281 2898 : if (att->attisdropped)
16282 2896 : continue;
16283 :
16284 2898 : aclDatum = heap_getattr(attributeTuple,
16285 : Anum_pg_attribute_attacl,
16286 : RelationGetDescr(attRelation),
16287 : &isNull);
16288 : /* Null ACLs do not require changes */
16289 2898 : if (isNull)
16290 2896 : continue;
16291 :
16292 2 : memset(repl_null, false, sizeof(repl_null));
16293 2 : memset(repl_repl, false, sizeof(repl_repl));
16294 :
16295 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16296 : oldOwnerId, newOwnerId);
16297 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
16298 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16299 :
16300 2 : newtuple = heap_modify_tuple(attributeTuple,
16301 : RelationGetDescr(attRelation),
16302 : repl_val, repl_null, repl_repl);
16303 :
16304 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16305 :
16306 2 : heap_freetuple(newtuple);
16307 : }
16308 488 : systable_endscan(scan);
16309 488 : table_close(attRelation, RowExclusiveLock);
16310 488 : }
16311 :
16312 : /*
16313 : * change_owner_recurse_to_sequences
16314 : *
16315 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
16316 : * for sequences that are dependent on serial columns, and changes their
16317 : * ownership.
16318 : */
16319 : static void
16320 488 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16321 : {
16322 : Relation depRel;
16323 : SysScanDesc scan;
16324 : ScanKeyData key[2];
16325 : HeapTuple tup;
16326 :
16327 : /*
16328 : * SERIAL sequences are those having an auto dependency on one of the
16329 : * table's columns (we don't care *which* column, exactly).
16330 : */
16331 488 : depRel = table_open(DependRelationId, AccessShareLock);
16332 :
16333 488 : ScanKeyInit(&key[0],
16334 : Anum_pg_depend_refclassid,
16335 : BTEqualStrategyNumber, F_OIDEQ,
16336 : ObjectIdGetDatum(RelationRelationId));
16337 488 : ScanKeyInit(&key[1],
16338 : Anum_pg_depend_refobjid,
16339 : BTEqualStrategyNumber, F_OIDEQ,
16340 : ObjectIdGetDatum(relationOid));
16341 : /* we leave refobjsubid unspecified */
16342 :
16343 488 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16344 : NULL, 2, key);
16345 :
16346 1378 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
16347 : {
16348 890 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16349 : Relation seqRel;
16350 :
16351 : /* skip dependencies other than auto dependencies on columns */
16352 890 : if (depForm->refobjsubid == 0 ||
16353 352 : depForm->classid != RelationRelationId ||
16354 142 : depForm->objsubid != 0 ||
16355 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16356 748 : continue;
16357 :
16358 : /* Use relation_open just in case it's an index */
16359 142 : seqRel = relation_open(depForm->objid, lockmode);
16360 :
16361 : /* skip non-sequence relations */
16362 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16363 : {
16364 : /* No need to keep the lock */
16365 116 : relation_close(seqRel, lockmode);
16366 116 : continue;
16367 : }
16368 :
16369 : /* We don't need to close the sequence while we alter it. */
16370 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16371 :
16372 : /* Now we can close it. Keep the lock till end of transaction. */
16373 26 : relation_close(seqRel, NoLock);
16374 : }
16375 :
16376 488 : systable_endscan(scan);
16377 :
16378 488 : relation_close(depRel, AccessShareLock);
16379 488 : }
16380 :
16381 : /*
16382 : * ALTER TABLE CLUSTER ON
16383 : *
16384 : * The only thing we have to do is to change the indisclustered bits.
16385 : *
16386 : * Return the address of the new clustering index.
16387 : */
16388 : static ObjectAddress
16389 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16390 : {
16391 : Oid indexOid;
16392 : ObjectAddress address;
16393 :
16394 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16395 :
16396 64 : if (!OidIsValid(indexOid))
16397 0 : ereport(ERROR,
16398 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16399 : errmsg("index \"%s\" for table \"%s\" does not exist",
16400 : indexName, RelationGetRelationName(rel))));
16401 :
16402 : /* Check index is valid to cluster on */
16403 64 : check_index_is_clusterable(rel, indexOid, lockmode);
16404 :
16405 : /* And do the work */
16406 64 : mark_index_clustered(rel, indexOid, false);
16407 :
16408 58 : ObjectAddressSet(address,
16409 : RelationRelationId, indexOid);
16410 :
16411 58 : return address;
16412 : }
16413 :
16414 : /*
16415 : * ALTER TABLE SET WITHOUT CLUSTER
16416 : *
16417 : * We have to find any indexes on the table that have indisclustered bit
16418 : * set and turn it off.
16419 : */
16420 : static void
16421 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
16422 : {
16423 18 : mark_index_clustered(rel, InvalidOid, false);
16424 12 : }
16425 :
16426 : /*
16427 : * Preparation phase for SET ACCESS METHOD
16428 : *
16429 : * Check that the access method exists and determine whether a change is
16430 : * actually needed.
16431 : */
16432 : static void
16433 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
16434 : {
16435 : Oid amoid;
16436 :
16437 : /*
16438 : * Look up the access method name and check that it differs from the
16439 : * table's current AM. If DEFAULT was specified for a partitioned table
16440 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16441 : */
16442 110 : if (amname != NULL)
16443 74 : amoid = get_table_am_oid(amname, false);
16444 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16445 18 : amoid = InvalidOid;
16446 : else
16447 18 : amoid = get_table_am_oid(default_table_access_method, false);
16448 :
16449 : /* if it's a match, phase 3 doesn't need to do anything */
16450 110 : if (rel->rd_rel->relam == amoid)
16451 12 : return;
16452 :
16453 : /* Save info for Phase 3 to do the real work */
16454 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
16455 98 : tab->newAccessMethod = amoid;
16456 98 : tab->chgAccessMethod = true;
16457 : }
16458 :
16459 : /*
16460 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16461 : * storage that have an interest in preserving AM.
16462 : *
16463 : * Since these have no storage, setting the access method is a catalog only
16464 : * operation.
16465 : */
16466 : static void
16467 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
16468 : {
16469 : Relation pg_class;
16470 : Oid oldAccessMethodId;
16471 : HeapTuple tuple;
16472 : Form_pg_class rd_rel;
16473 44 : Oid reloid = RelationGetRelid(rel);
16474 :
16475 : /*
16476 : * Shouldn't be called on relations having storage; these are processed in
16477 : * phase 3.
16478 : */
16479 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16480 :
16481 : /* Get a modifiable copy of the relation's pg_class row. */
16482 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
16483 :
16484 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16485 44 : if (!HeapTupleIsValid(tuple))
16486 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
16487 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16488 :
16489 : /* Update the pg_class row. */
16490 44 : oldAccessMethodId = rd_rel->relam;
16491 44 : rd_rel->relam = newAccessMethodId;
16492 :
16493 : /* Leave if no update required */
16494 44 : if (rd_rel->relam == oldAccessMethodId)
16495 : {
16496 0 : heap_freetuple(tuple);
16497 0 : table_close(pg_class, RowExclusiveLock);
16498 0 : return;
16499 : }
16500 :
16501 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16502 :
16503 : /*
16504 : * Update the dependency on the new access method. No dependency is added
16505 : * if the new access method is InvalidOid (default case). Be very careful
16506 : * that this has to compare the previous value stored in pg_class with the
16507 : * new one.
16508 : */
16509 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16510 20 : {
16511 : ObjectAddress relobj,
16512 : referenced;
16513 :
16514 : /*
16515 : * New access method is defined and there was no dependency
16516 : * previously, so record a new one.
16517 : */
16518 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
16519 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16520 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16521 : }
16522 24 : else if (OidIsValid(oldAccessMethodId) &&
16523 24 : !OidIsValid(rd_rel->relam))
16524 : {
16525 : /*
16526 : * There was an access method defined, and no new one, so just remove
16527 : * the existing dependency.
16528 : */
16529 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
16530 : AccessMethodRelationId,
16531 : DEPENDENCY_NORMAL);
16532 : }
16533 : else
16534 : {
16535 : Assert(OidIsValid(oldAccessMethodId) &&
16536 : OidIsValid(rd_rel->relam));
16537 :
16538 : /* Both are valid, so update the dependency */
16539 12 : changeDependencyFor(RelationRelationId, reloid,
16540 : AccessMethodRelationId,
16541 : oldAccessMethodId, rd_rel->relam);
16542 : }
16543 :
16544 : /* make the relam and dependency changes visible */
16545 44 : CommandCounterIncrement();
16546 :
16547 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16548 :
16549 44 : heap_freetuple(tuple);
16550 44 : table_close(pg_class, RowExclusiveLock);
16551 : }
16552 :
16553 : /*
16554 : * ALTER TABLE SET TABLESPACE
16555 : */
16556 : static void
16557 158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16558 : {
16559 : Oid tablespaceId;
16560 :
16561 : /* Check that the tablespace exists */
16562 158 : tablespaceId = get_tablespace_oid(tablespacename, false);
16563 :
16564 : /* Check permissions except when moving to database's default */
16565 158 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16566 : {
16567 : AclResult aclresult;
16568 :
16569 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16570 66 : if (aclresult != ACLCHECK_OK)
16571 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16572 : }
16573 :
16574 : /* Save info for Phase 3 to do the real work */
16575 158 : if (OidIsValid(tab->newTableSpace))
16576 0 : ereport(ERROR,
16577 : (errcode(ERRCODE_SYNTAX_ERROR),
16578 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
16579 :
16580 158 : tab->newTableSpace = tablespaceId;
16581 158 : }
16582 :
16583 : /*
16584 : * Set, reset, or replace reloptions.
16585 : */
16586 : static void
16587 958 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
16588 : LOCKMODE lockmode)
16589 : {
16590 : Oid relid;
16591 : Relation pgclass;
16592 : HeapTuple tuple;
16593 : HeapTuple newtuple;
16594 : Datum datum;
16595 : Datum newOptions;
16596 : Datum repl_val[Natts_pg_class];
16597 : bool repl_null[Natts_pg_class];
16598 : bool repl_repl[Natts_pg_class];
16599 958 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16600 :
16601 958 : if (defList == NIL && operation != AT_ReplaceRelOptions)
16602 0 : return; /* nothing to do */
16603 :
16604 958 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
16605 :
16606 : /* Fetch heap tuple */
16607 958 : relid = RelationGetRelid(rel);
16608 958 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16609 958 : if (!HeapTupleIsValid(tuple))
16610 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16611 :
16612 958 : if (operation == AT_ReplaceRelOptions)
16613 : {
16614 : /*
16615 : * If we're supposed to replace the reloptions list, we just pretend
16616 : * there were none before.
16617 : */
16618 198 : datum = (Datum) 0;
16619 : }
16620 : else
16621 : {
16622 : bool isnull;
16623 :
16624 : /* Get the old reloptions */
16625 760 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16626 : &isnull);
16627 760 : if (isnull)
16628 472 : datum = (Datum) 0;
16629 : }
16630 :
16631 : /* Generate new proposed reloptions (text array) */
16632 958 : newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16633 : operation == AT_ResetRelOptions);
16634 :
16635 : /* Validate */
16636 952 : switch (rel->rd_rel->relkind)
16637 : {
16638 530 : case RELKIND_RELATION:
16639 : case RELKIND_MATVIEW:
16640 530 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16641 530 : break;
16642 6 : case RELKIND_PARTITIONED_TABLE:
16643 6 : (void) partitioned_table_reloptions(newOptions, true);
16644 0 : break;
16645 300 : case RELKIND_VIEW:
16646 300 : (void) view_reloptions(newOptions, true);
16647 282 : break;
16648 116 : case RELKIND_INDEX:
16649 : case RELKIND_PARTITIONED_INDEX:
16650 116 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16651 94 : break;
16652 0 : case RELKIND_TOASTVALUE:
16653 : /* fall through to error -- shouldn't ever get here */
16654 : default:
16655 0 : ereport(ERROR,
16656 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16657 : errmsg("cannot set options for relation \"%s\"",
16658 : RelationGetRelationName(rel)),
16659 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16660 : break;
16661 : }
16662 :
16663 : /* Special-case validation of view options */
16664 906 : if (rel->rd_rel->relkind == RELKIND_VIEW)
16665 : {
16666 282 : Query *view_query = get_view_query(rel);
16667 282 : List *view_options = untransformRelOptions(newOptions);
16668 : ListCell *cell;
16669 282 : bool check_option = false;
16670 :
16671 384 : foreach(cell, view_options)
16672 : {
16673 102 : DefElem *defel = (DefElem *) lfirst(cell);
16674 :
16675 102 : if (strcmp(defel->defname, "check_option") == 0)
16676 24 : check_option = true;
16677 : }
16678 :
16679 : /*
16680 : * If the check option is specified, look to see if the view is
16681 : * actually auto-updatable or not.
16682 : */
16683 282 : if (check_option)
16684 : {
16685 : const char *view_updatable_error =
16686 24 : view_query_is_auto_updatable(view_query, true);
16687 :
16688 24 : if (view_updatable_error)
16689 0 : ereport(ERROR,
16690 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16691 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16692 : errhint("%s", _(view_updatable_error))));
16693 : }
16694 : }
16695 :
16696 : /*
16697 : * All we need do here is update the pg_class row; the new options will be
16698 : * propagated into relcaches during post-commit cache inval.
16699 : */
16700 906 : memset(repl_val, 0, sizeof(repl_val));
16701 906 : memset(repl_null, false, sizeof(repl_null));
16702 906 : memset(repl_repl, false, sizeof(repl_repl));
16703 :
16704 906 : if (newOptions != (Datum) 0)
16705 608 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16706 : else
16707 298 : repl_null[Anum_pg_class_reloptions - 1] = true;
16708 :
16709 906 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16710 :
16711 906 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16712 : repl_val, repl_null, repl_repl);
16713 :
16714 906 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16715 906 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16716 :
16717 906 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16718 :
16719 906 : heap_freetuple(newtuple);
16720 :
16721 906 : ReleaseSysCache(tuple);
16722 :
16723 : /* repeat the whole exercise for the toast table, if there's one */
16724 906 : if (OidIsValid(rel->rd_rel->reltoastrelid))
16725 : {
16726 : Relation toastrel;
16727 262 : Oid toastid = rel->rd_rel->reltoastrelid;
16728 :
16729 262 : toastrel = table_open(toastid, lockmode);
16730 :
16731 : /* Fetch heap tuple */
16732 262 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16733 262 : if (!HeapTupleIsValid(tuple))
16734 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
16735 :
16736 262 : if (operation == AT_ReplaceRelOptions)
16737 : {
16738 : /*
16739 : * If we're supposed to replace the reloptions list, we just
16740 : * pretend there were none before.
16741 : */
16742 0 : datum = (Datum) 0;
16743 : }
16744 : else
16745 : {
16746 : bool isnull;
16747 :
16748 : /* Get the old reloptions */
16749 262 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16750 : &isnull);
16751 262 : if (isnull)
16752 226 : datum = (Datum) 0;
16753 : }
16754 :
16755 262 : newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16756 : false, operation == AT_ResetRelOptions);
16757 :
16758 262 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16759 :
16760 262 : memset(repl_val, 0, sizeof(repl_val));
16761 262 : memset(repl_null, false, sizeof(repl_null));
16762 262 : memset(repl_repl, false, sizeof(repl_repl));
16763 :
16764 262 : if (newOptions != (Datum) 0)
16765 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16766 : else
16767 220 : repl_null[Anum_pg_class_reloptions - 1] = true;
16768 :
16769 262 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16770 :
16771 262 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16772 : repl_val, repl_null, repl_repl);
16773 :
16774 262 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16775 :
16776 262 : InvokeObjectPostAlterHookArg(RelationRelationId,
16777 : RelationGetRelid(toastrel), 0,
16778 : InvalidOid, true);
16779 :
16780 262 : heap_freetuple(newtuple);
16781 :
16782 262 : ReleaseSysCache(tuple);
16783 :
16784 262 : table_close(toastrel, NoLock);
16785 : }
16786 :
16787 906 : table_close(pgclass, RowExclusiveLock);
16788 : }
16789 :
16790 : /*
16791 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16792 : * rewriting to be done, so we just want to copy the data as fast as possible.
16793 : */
16794 : static void
16795 162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16796 : {
16797 : Relation rel;
16798 : Oid reltoastrelid;
16799 : RelFileNumber newrelfilenumber;
16800 : RelFileLocator newrlocator;
16801 162 : List *reltoastidxids = NIL;
16802 : ListCell *lc;
16803 :
16804 : /*
16805 : * Need lock here in case we are recursing to toast table or index
16806 : */
16807 162 : rel = relation_open(tableOid, lockmode);
16808 :
16809 : /* Check first if relation can be moved to new tablespace */
16810 162 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16811 : {
16812 2 : InvokeObjectPostAlterHook(RelationRelationId,
16813 : RelationGetRelid(rel), 0);
16814 2 : relation_close(rel, NoLock);
16815 2 : return;
16816 : }
16817 :
16818 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
16819 : /* Fetch the list of indexes on toast relation if necessary */
16820 160 : if (OidIsValid(reltoastrelid))
16821 : {
16822 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
16823 :
16824 20 : reltoastidxids = RelationGetIndexList(toastRel);
16825 20 : relation_close(toastRel, lockmode);
16826 : }
16827 :
16828 : /*
16829 : * Relfilenumbers are not unique in databases across tablespaces, so we
16830 : * need to allocate a new one in the new tablespace.
16831 : */
16832 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16833 160 : rel->rd_rel->relpersistence);
16834 :
16835 : /* Open old and new relation */
16836 160 : newrlocator = rel->rd_locator;
16837 160 : newrlocator.relNumber = newrelfilenumber;
16838 160 : newrlocator.spcOid = newTableSpace;
16839 :
16840 : /* hand off to AM to actually create new rel storage and copy the data */
16841 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
16842 : {
16843 62 : index_copy_data(rel, newrlocator);
16844 : }
16845 : else
16846 : {
16847 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16848 98 : table_relation_copy_data(rel, &newrlocator);
16849 : }
16850 :
16851 : /*
16852 : * Update the pg_class row.
16853 : *
16854 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16855 : * executed on pg_class or its indexes (the above copy wouldn't contain
16856 : * the updated pg_class entry), but that's forbidden with
16857 : * CheckRelationTableSpaceMove().
16858 : */
16859 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16860 :
16861 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16862 :
16863 160 : RelationAssumeNewRelfilelocator(rel);
16864 :
16865 160 : relation_close(rel, NoLock);
16866 :
16867 : /* Make sure the reltablespace change is visible */
16868 160 : CommandCounterIncrement();
16869 :
16870 : /* Move associated toast relation and/or indexes, too */
16871 160 : if (OidIsValid(reltoastrelid))
16872 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16873 180 : foreach(lc, reltoastidxids)
16874 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16875 :
16876 : /* Clean up */
16877 160 : list_free(reltoastidxids);
16878 : }
16879 :
16880 : /*
16881 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16882 : * storage that have an interest in preserving tablespace.
16883 : *
16884 : * Since these have no storage the tablespace can be updated with a simple
16885 : * metadata only operation to update the tablespace.
16886 : */
16887 : static void
16888 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
16889 : {
16890 : /*
16891 : * Shouldn't be called on relations having storage; these are processed in
16892 : * phase 3.
16893 : */
16894 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16895 :
16896 : /* check if relation can be moved to its new tablespace */
16897 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16898 : {
16899 0 : InvokeObjectPostAlterHook(RelationRelationId,
16900 : RelationGetRelid(rel),
16901 : 0);
16902 0 : return;
16903 : }
16904 :
16905 : /* Update can be done, so change reltablespace */
16906 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16907 :
16908 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16909 :
16910 : /* Make sure the reltablespace change is visible */
16911 30 : CommandCounterIncrement();
16912 : }
16913 :
16914 : /*
16915 : * Alter Table ALL ... SET TABLESPACE
16916 : *
16917 : * Allows a user to move all objects of some type in a given tablespace in the
16918 : * current database to another tablespace. Objects can be chosen based on the
16919 : * owner of the object also, to allow users to move only their objects.
16920 : * The user must have CREATE rights on the new tablespace, as usual. The main
16921 : * permissions handling is done by the lower-level table move function.
16922 : *
16923 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
16924 : * lock can't be acquired then we ereport(ERROR).
16925 : */
16926 : Oid
16927 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
16928 : {
16929 30 : List *relations = NIL;
16930 : ListCell *l;
16931 : ScanKeyData key[1];
16932 : Relation rel;
16933 : TableScanDesc scan;
16934 : HeapTuple tuple;
16935 : Oid orig_tablespaceoid;
16936 : Oid new_tablespaceoid;
16937 30 : List *role_oids = roleSpecsToIds(stmt->roles);
16938 :
16939 : /* Ensure we were not asked to move something we can't */
16940 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16941 12 : stmt->objtype != OBJECT_MATVIEW)
16942 0 : ereport(ERROR,
16943 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16944 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16945 :
16946 : /* Get the orig and new tablespace OIDs */
16947 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16948 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16949 :
16950 : /* Can't move shared relations in to or out of pg_global */
16951 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16952 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16953 : new_tablespaceoid == GLOBALTABLESPACE_OID)
16954 0 : ereport(ERROR,
16955 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16956 : errmsg("cannot move relations in to or out of pg_global tablespace")));
16957 :
16958 : /*
16959 : * Must have CREATE rights on the new tablespace, unless it is the
16960 : * database default tablespace (which all users implicitly have CREATE
16961 : * rights on).
16962 : */
16963 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16964 : {
16965 : AclResult aclresult;
16966 :
16967 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16968 : ACL_CREATE);
16969 0 : if (aclresult != ACLCHECK_OK)
16970 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
16971 0 : get_tablespace_name(new_tablespaceoid));
16972 : }
16973 :
16974 : /*
16975 : * Now that the checks are done, check if we should set either to
16976 : * InvalidOid because it is our database's default tablespace.
16977 : */
16978 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
16979 0 : orig_tablespaceoid = InvalidOid;
16980 :
16981 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
16982 30 : new_tablespaceoid = InvalidOid;
16983 :
16984 : /* no-op */
16985 30 : if (orig_tablespaceoid == new_tablespaceoid)
16986 0 : return new_tablespaceoid;
16987 :
16988 : /*
16989 : * Walk the list of objects in the tablespace and move them. This will
16990 : * only find objects in our database, of course.
16991 : */
16992 30 : ScanKeyInit(&key[0],
16993 : Anum_pg_class_reltablespace,
16994 : BTEqualStrategyNumber, F_OIDEQ,
16995 : ObjectIdGetDatum(orig_tablespaceoid));
16996 :
16997 30 : rel = table_open(RelationRelationId, AccessShareLock);
16998 30 : scan = table_beginscan_catalog(rel, 1, key);
16999 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17000 : {
17001 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17002 102 : Oid relOid = relForm->oid;
17003 :
17004 : /*
17005 : * Do not move objects in pg_catalog as part of this, if an admin
17006 : * really wishes to do so, they can issue the individual ALTER
17007 : * commands directly.
17008 : *
17009 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
17010 : * (TOAST will be moved with the main table).
17011 : */
17012 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
17013 204 : relForm->relisshared ||
17014 204 : isAnyTempNamespace(relForm->relnamespace) ||
17015 102 : IsToastNamespace(relForm->relnamespace))
17016 0 : continue;
17017 :
17018 : /* Only move the object type requested */
17019 102 : if ((stmt->objtype == OBJECT_TABLE &&
17020 60 : relForm->relkind != RELKIND_RELATION &&
17021 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17022 66 : (stmt->objtype == OBJECT_INDEX &&
17023 36 : relForm->relkind != RELKIND_INDEX &&
17024 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17025 60 : (stmt->objtype == OBJECT_MATVIEW &&
17026 6 : relForm->relkind != RELKIND_MATVIEW))
17027 42 : continue;
17028 :
17029 : /* Check if we are only moving objects owned by certain roles */
17030 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17031 0 : continue;
17032 :
17033 : /*
17034 : * Handle permissions-checking here since we are locking the tables
17035 : * and also to avoid doing a bunch of work only to fail part-way. Note
17036 : * that permissions will also be checked by AlterTableInternal().
17037 : *
17038 : * Caller must be considered an owner on the table to move it.
17039 : */
17040 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17041 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
17042 0 : NameStr(relForm->relname));
17043 :
17044 60 : if (stmt->nowait &&
17045 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
17046 0 : ereport(ERROR,
17047 : (errcode(ERRCODE_OBJECT_IN_USE),
17048 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
17049 : get_namespace_name(relForm->relnamespace),
17050 : NameStr(relForm->relname))));
17051 : else
17052 60 : LockRelationOid(relOid, AccessExclusiveLock);
17053 :
17054 : /* Add to our list of objects to move */
17055 60 : relations = lappend_oid(relations, relOid);
17056 : }
17057 :
17058 30 : table_endscan(scan);
17059 30 : table_close(rel, AccessShareLock);
17060 :
17061 30 : if (relations == NIL)
17062 12 : ereport(NOTICE,
17063 : (errcode(ERRCODE_NO_DATA_FOUND),
17064 : errmsg("no matching relations in tablespace \"%s\" found",
17065 : orig_tablespaceoid == InvalidOid ? "(database default)" :
17066 : get_tablespace_name(orig_tablespaceoid))));
17067 :
17068 : /* Everything is locked, loop through and move all of the relations. */
17069 90 : foreach(l, relations)
17070 : {
17071 60 : List *cmds = NIL;
17072 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
17073 :
17074 60 : cmd->subtype = AT_SetTableSpace;
17075 60 : cmd->name = stmt->new_tablespacename;
17076 :
17077 60 : cmds = lappend(cmds, cmd);
17078 :
17079 60 : EventTriggerAlterTableStart((Node *) stmt);
17080 : /* OID is set by AlterTableInternal */
17081 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
17082 60 : EventTriggerAlterTableEnd();
17083 : }
17084 :
17085 30 : return new_tablespaceoid;
17086 : }
17087 :
17088 : static void
17089 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
17090 : {
17091 : SMgrRelation dstrel;
17092 :
17093 : /*
17094 : * Since we copy the file directly without looking at the shared buffers,
17095 : * we'd better first flush out any pages of the source relation that are
17096 : * in shared buffers. We assume no new changes will be made while we are
17097 : * holding exclusive lock on the rel.
17098 : */
17099 62 : FlushRelationBuffers(rel);
17100 :
17101 : /*
17102 : * Create and copy all forks of the relation, and schedule unlinking of
17103 : * old physical files.
17104 : *
17105 : * NOTE: any conflict in relfilenumber value will be caught in
17106 : * RelationCreateStorage().
17107 : */
17108 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17109 :
17110 : /* copy main fork */
17111 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
17112 62 : rel->rd_rel->relpersistence);
17113 :
17114 : /* copy those extra forks that exist */
17115 62 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17116 248 : forkNum <= MAX_FORKNUM; forkNum++)
17117 : {
17118 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
17119 : {
17120 0 : smgrcreate(dstrel, forkNum, false);
17121 :
17122 : /*
17123 : * WAL log creation if the relation is persistent, or this is the
17124 : * init fork of an unlogged relation.
17125 : */
17126 0 : if (RelationIsPermanent(rel) ||
17127 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17128 : forkNum == INIT_FORKNUM))
17129 0 : log_smgrcreate(&newrlocator, forkNum);
17130 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17131 0 : rel->rd_rel->relpersistence);
17132 : }
17133 : }
17134 :
17135 : /* drop old relation, and close new one */
17136 62 : RelationDropStorage(rel);
17137 62 : smgrclose(dstrel);
17138 62 : }
17139 :
17140 : /*
17141 : * ALTER TABLE ENABLE/DISABLE TRIGGER
17142 : *
17143 : * We just pass this off to trigger.c.
17144 : */
17145 : static void
17146 346 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17147 : char fires_when, bool skip_system, bool recurse,
17148 : LOCKMODE lockmode)
17149 : {
17150 346 : EnableDisableTrigger(rel, trigname, InvalidOid,
17151 : fires_when, skip_system, recurse,
17152 : lockmode);
17153 :
17154 346 : InvokeObjectPostAlterHook(RelationRelationId,
17155 : RelationGetRelid(rel), 0);
17156 346 : }
17157 :
17158 : /*
17159 : * ALTER TABLE ENABLE/DISABLE RULE
17160 : *
17161 : * We just pass this off to rewriteDefine.c.
17162 : */
17163 : static void
17164 52 : ATExecEnableDisableRule(Relation rel, const char *rulename,
17165 : char fires_when, LOCKMODE lockmode)
17166 : {
17167 52 : EnableDisableRule(rel, rulename, fires_when);
17168 :
17169 52 : InvokeObjectPostAlterHook(RelationRelationId,
17170 : RelationGetRelid(rel), 0);
17171 52 : }
17172 :
17173 : /*
17174 : * ALTER TABLE INHERIT
17175 : *
17176 : * Add a parent to the child's parents. This verifies that all the columns and
17177 : * check constraints of the parent appear in the child and that they have the
17178 : * same data types and expressions.
17179 : */
17180 : static void
17181 444 : ATPrepAddInherit(Relation child_rel)
17182 : {
17183 444 : if (child_rel->rd_rel->reloftype)
17184 6 : ereport(ERROR,
17185 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17186 : errmsg("cannot change inheritance of typed table")));
17187 :
17188 438 : if (child_rel->rd_rel->relispartition)
17189 6 : ereport(ERROR,
17190 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17191 : errmsg("cannot change inheritance of a partition")));
17192 :
17193 432 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17194 6 : ereport(ERROR,
17195 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17196 : errmsg("cannot change inheritance of partitioned table")));
17197 426 : }
17198 :
17199 : /*
17200 : * Return the address of the new parent relation.
17201 : */
17202 : static ObjectAddress
17203 426 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17204 : {
17205 : Relation parent_rel;
17206 : List *children;
17207 : ObjectAddress address;
17208 : const char *trigger_name;
17209 :
17210 : /*
17211 : * A self-exclusive lock is needed here. See the similar case in
17212 : * MergeAttributes() for a full explanation.
17213 : */
17214 426 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17215 :
17216 : /*
17217 : * Must be owner of both parent and child -- child was checked by
17218 : * ATSimplePermissions call in ATPrepCmd
17219 : */
17220 426 : ATSimplePermissions(AT_AddInherit, parent_rel,
17221 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
17222 :
17223 : /* Permanent rels cannot inherit from temporary ones */
17224 426 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17225 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17226 0 : ereport(ERROR,
17227 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17228 : errmsg("cannot inherit from temporary relation \"%s\"",
17229 : RelationGetRelationName(parent_rel))));
17230 :
17231 : /* If parent rel is temp, it must belong to this session */
17232 426 : if (RELATION_IS_OTHER_TEMP(parent_rel))
17233 0 : ereport(ERROR,
17234 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17235 : errmsg("cannot inherit from temporary relation of another session")));
17236 :
17237 : /* Ditto for the child */
17238 426 : if (RELATION_IS_OTHER_TEMP(child_rel))
17239 0 : ereport(ERROR,
17240 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17241 : errmsg("cannot inherit to temporary relation of another session")));
17242 :
17243 : /* Prevent partitioned tables from becoming inheritance parents */
17244 426 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17245 6 : ereport(ERROR,
17246 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17247 : errmsg("cannot inherit from partitioned table \"%s\"",
17248 : parent->relname)));
17249 :
17250 : /* Likewise for partitions */
17251 420 : if (parent_rel->rd_rel->relispartition)
17252 6 : ereport(ERROR,
17253 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17254 : errmsg("cannot inherit from a partition")));
17255 :
17256 : /*
17257 : * Prevent circularity by seeing if proposed parent inherits from child.
17258 : * (In particular, this disallows making a rel inherit from itself.)
17259 : *
17260 : * This is not completely bulletproof because of race conditions: in
17261 : * multi-level inheritance trees, someone else could concurrently be
17262 : * making another inheritance link that closes the loop but does not join
17263 : * either of the rels we have locked. Preventing that seems to require
17264 : * exclusive locks on the entire inheritance tree, which is a cure worse
17265 : * than the disease. find_all_inheritors() will cope with circularity
17266 : * anyway, so don't sweat it too much.
17267 : *
17268 : * We use weakest lock we can on child's children, namely AccessShareLock.
17269 : */
17270 414 : children = find_all_inheritors(RelationGetRelid(child_rel),
17271 : AccessShareLock, NULL);
17272 :
17273 414 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
17274 12 : ereport(ERROR,
17275 : (errcode(ERRCODE_DUPLICATE_TABLE),
17276 : errmsg("circular inheritance not allowed"),
17277 : errdetail("\"%s\" is already a child of \"%s\".",
17278 : parent->relname,
17279 : RelationGetRelationName(child_rel))));
17280 :
17281 : /*
17282 : * If child_rel has row-level triggers with transition tables, we
17283 : * currently don't allow it to become an inheritance child. See also
17284 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
17285 : */
17286 402 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17287 402 : if (trigger_name != NULL)
17288 6 : ereport(ERROR,
17289 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17290 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17291 : trigger_name, RelationGetRelationName(child_rel)),
17292 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17293 :
17294 : /* OK to create inheritance */
17295 396 : CreateInheritance(child_rel, parent_rel, false);
17296 :
17297 306 : ObjectAddressSet(address, RelationRelationId,
17298 : RelationGetRelid(parent_rel));
17299 :
17300 : /* keep our lock on the parent relation until commit */
17301 306 : table_close(parent_rel, NoLock);
17302 :
17303 306 : return address;
17304 : }
17305 :
17306 : /*
17307 : * CreateInheritance
17308 : * Catalog manipulation portion of creating inheritance between a child
17309 : * table and a parent table.
17310 : *
17311 : * Common to ATExecAddInherit() and ATExecAttachPartition().
17312 : */
17313 : static void
17314 3036 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17315 : {
17316 : Relation catalogRelation;
17317 : SysScanDesc scan;
17318 : ScanKeyData key;
17319 : HeapTuple inheritsTuple;
17320 : int32 inhseqno;
17321 :
17322 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17323 3036 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17324 :
17325 : /*
17326 : * Check for duplicates in the list of parents, and determine the highest
17327 : * inhseqno already present; we'll use the next one for the new parent.
17328 : * Also, if proposed child is a partition, it cannot already be
17329 : * inheriting.
17330 : *
17331 : * Note: we do not reject the case where the child already inherits from
17332 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17333 : */
17334 3036 : ScanKeyInit(&key,
17335 : Anum_pg_inherits_inhrelid,
17336 : BTEqualStrategyNumber, F_OIDEQ,
17337 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17338 3036 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17339 : true, NULL, 1, &key);
17340 :
17341 : /* inhseqno sequences start at 1 */
17342 3036 : inhseqno = 0;
17343 3104 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17344 : {
17345 74 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17346 :
17347 74 : if (inh->inhparent == RelationGetRelid(parent_rel))
17348 6 : ereport(ERROR,
17349 : (errcode(ERRCODE_DUPLICATE_TABLE),
17350 : errmsg("relation \"%s\" would be inherited from more than once",
17351 : RelationGetRelationName(parent_rel))));
17352 :
17353 68 : if (inh->inhseqno > inhseqno)
17354 68 : inhseqno = inh->inhseqno;
17355 : }
17356 3030 : systable_endscan(scan);
17357 :
17358 : /* Match up the columns and bump attinhcount as needed */
17359 3030 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17360 :
17361 : /* Match up the constraints and bump coninhcount as needed */
17362 2898 : MergeConstraintsIntoExisting(child_rel, parent_rel);
17363 :
17364 : /*
17365 : * OK, it looks valid. Make the catalog entries that show inheritance.
17366 : */
17367 2838 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
17368 : RelationGetRelid(parent_rel),
17369 : inhseqno + 1,
17370 : catalogRelation,
17371 2838 : parent_rel->rd_rel->relkind ==
17372 : RELKIND_PARTITIONED_TABLE);
17373 :
17374 : /* Now we're done with pg_inherits */
17375 2838 : table_close(catalogRelation, RowExclusiveLock);
17376 2838 : }
17377 :
17378 : /*
17379 : * Obtain the source-text form of the constraint expression for a check
17380 : * constraint, given its pg_constraint tuple
17381 : */
17382 : static char *
17383 188 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
17384 : {
17385 : Form_pg_constraint con;
17386 : bool isnull;
17387 : Datum attr;
17388 : Datum expr;
17389 :
17390 188 : con = (Form_pg_constraint) GETSTRUCT(contup);
17391 188 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17392 188 : if (isnull)
17393 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
17394 :
17395 188 : expr = DirectFunctionCall2(pg_get_expr, attr,
17396 : ObjectIdGetDatum(con->conrelid));
17397 188 : return TextDatumGetCString(expr);
17398 : }
17399 :
17400 : /*
17401 : * Determine whether two check constraints are functionally equivalent
17402 : *
17403 : * The test we apply is to see whether they reverse-compile to the same
17404 : * source string. This insulates us from issues like whether attributes
17405 : * have the same physical column numbers in parent and child relations.
17406 : *
17407 : * Note that we ignore enforceability as there are cases where constraints
17408 : * with differing enforceability are allowed.
17409 : */
17410 : static bool
17411 94 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
17412 : {
17413 94 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
17414 94 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
17415 :
17416 94 : if (acon->condeferrable != bcon->condeferrable ||
17417 94 : acon->condeferred != bcon->condeferred ||
17418 94 : strcmp(decompile_conbin(a, tupleDesc),
17419 94 : decompile_conbin(b, tupleDesc)) != 0)
17420 6 : return false;
17421 : else
17422 88 : return true;
17423 : }
17424 :
17425 : /*
17426 : * Check columns in child table match up with columns in parent, and increment
17427 : * their attinhcount.
17428 : *
17429 : * Called by CreateInheritance
17430 : *
17431 : * Currently all parent columns must be found in child. Missing columns are an
17432 : * error. One day we might consider creating new columns like CREATE TABLE
17433 : * does. However, that is widely unpopular --- in the common use case of
17434 : * partitioned tables it's a foot-gun.
17435 : *
17436 : * The data type must match exactly. If the parent column is NOT NULL then
17437 : * the child must be as well. Defaults are not compared, however.
17438 : */
17439 : static void
17440 3030 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17441 : {
17442 : Relation attrrel;
17443 : TupleDesc parent_desc;
17444 :
17445 3030 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17446 3030 : parent_desc = RelationGetDescr(parent_rel);
17447 :
17448 9736 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17449 : {
17450 6838 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17451 6838 : char *parent_attname = NameStr(parent_att->attname);
17452 : HeapTuple tuple;
17453 :
17454 : /* Ignore dropped columns in the parent. */
17455 6838 : if (parent_att->attisdropped)
17456 296 : continue;
17457 :
17458 : /* Find same column in child (matching on column name). */
17459 6542 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17460 6542 : if (HeapTupleIsValid(tuple))
17461 : {
17462 6530 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17463 :
17464 6530 : if (parent_att->atttypid != child_att->atttypid ||
17465 6524 : parent_att->atttypmod != child_att->atttypmod)
17466 12 : ereport(ERROR,
17467 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17468 : errmsg("child table \"%s\" has different type for column \"%s\"",
17469 : RelationGetRelationName(child_rel), parent_attname)));
17470 :
17471 6518 : if (parent_att->attcollation != child_att->attcollation)
17472 6 : ereport(ERROR,
17473 : (errcode(ERRCODE_COLLATION_MISMATCH),
17474 : errmsg("child table \"%s\" has different collation for column \"%s\"",
17475 : RelationGetRelationName(child_rel), parent_attname)));
17476 :
17477 : /*
17478 : * If the parent has a not-null constraint that's not NO INHERIT,
17479 : * make sure the child has one too.
17480 : *
17481 : * Other constraints are checked elsewhere.
17482 : */
17483 6512 : if (parent_att->attnotnull && !child_att->attnotnull)
17484 : {
17485 : HeapTuple contup;
17486 :
17487 48 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17488 48 : parent_att->attnum);
17489 48 : if (HeapTupleIsValid(contup) &&
17490 48 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17491 30 : ereport(ERROR,
17492 : errcode(ERRCODE_DATATYPE_MISMATCH),
17493 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17494 : parent_attname, RelationGetRelationName(child_rel)));
17495 : }
17496 :
17497 : /*
17498 : * Child column must be generated if and only if parent column is.
17499 : */
17500 6482 : if (parent_att->attgenerated && !child_att->attgenerated)
17501 36 : ereport(ERROR,
17502 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17503 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17504 6446 : if (child_att->attgenerated && !parent_att->attgenerated)
17505 24 : ereport(ERROR,
17506 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17507 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17508 :
17509 6422 : if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17510 12 : ereport(ERROR,
17511 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17512 : errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17513 : errdetail("Parent column is %s, child column is %s.",
17514 : parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17515 : child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17516 :
17517 : /*
17518 : * Regular inheritance children are independent enough not to
17519 : * inherit identity columns. But partitions are integral part of
17520 : * a partitioned table and inherit identity column.
17521 : */
17522 6410 : if (ispartition)
17523 5716 : child_att->attidentity = parent_att->attidentity;
17524 :
17525 : /*
17526 : * OK, bump the child column's inheritance count. (If we fail
17527 : * later on, this change will just roll back.)
17528 : */
17529 6410 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
17530 : &child_att->attinhcount))
17531 0 : ereport(ERROR,
17532 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17533 : errmsg("too many inheritance parents"));
17534 :
17535 : /*
17536 : * In case of partitions, we must enforce that value of attislocal
17537 : * is same in all partitions. (Note: there are only inherited
17538 : * attributes in partitions)
17539 : */
17540 6410 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17541 : {
17542 : Assert(child_att->attinhcount == 1);
17543 5716 : child_att->attislocal = false;
17544 : }
17545 :
17546 6410 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17547 6410 : heap_freetuple(tuple);
17548 : }
17549 : else
17550 : {
17551 12 : ereport(ERROR,
17552 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17553 : errmsg("child table is missing column \"%s\"", parent_attname)));
17554 : }
17555 : }
17556 :
17557 2898 : table_close(attrrel, RowExclusiveLock);
17558 2898 : }
17559 :
17560 : /*
17561 : * Check constraints in child table match up with constraints in parent,
17562 : * and increment their coninhcount.
17563 : *
17564 : * Constraints that are marked ONLY in the parent are ignored.
17565 : *
17566 : * Called by CreateInheritance
17567 : *
17568 : * Currently all constraints in parent must be present in the child. One day we
17569 : * may consider adding new constraints like CREATE TABLE does.
17570 : *
17571 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
17572 : * constraints. As long as tables have more like 10 constraints it shouldn't be
17573 : * a problem though. Even 100 constraints ought not be the end of the world.
17574 : *
17575 : * XXX See MergeWithExistingConstraint too if you change this code.
17576 : */
17577 : static void
17578 2898 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
17579 : {
17580 : Relation constraintrel;
17581 : SysScanDesc parent_scan;
17582 : ScanKeyData parent_key;
17583 : HeapTuple parent_tuple;
17584 2898 : Oid parent_relid = RelationGetRelid(parent_rel);
17585 : AttrMap *attmap;
17586 :
17587 2898 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17588 :
17589 : /* Outer loop scans through the parent's constraint definitions */
17590 2898 : ScanKeyInit(&parent_key,
17591 : Anum_pg_constraint_conrelid,
17592 : BTEqualStrategyNumber, F_OIDEQ,
17593 : ObjectIdGetDatum(parent_relid));
17594 2898 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17595 : true, NULL, 1, &parent_key);
17596 :
17597 2898 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17598 : RelationGetDescr(child_rel),
17599 : true);
17600 :
17601 4968 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17602 : {
17603 2130 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17604 : SysScanDesc child_scan;
17605 : ScanKeyData child_key;
17606 : HeapTuple child_tuple;
17607 : AttrNumber parent_attno;
17608 2130 : bool found = false;
17609 :
17610 2130 : if (parent_con->contype != CONSTRAINT_CHECK &&
17611 1992 : parent_con->contype != CONSTRAINT_NOTNULL)
17612 994 : continue;
17613 :
17614 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17615 1180 : if (parent_con->connoinherit)
17616 44 : continue;
17617 :
17618 1136 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17619 1018 : parent_attno = extractNotNullColumn(parent_tuple);
17620 : else
17621 118 : parent_attno = InvalidAttrNumber;
17622 :
17623 : /* Search for a child constraint matching this one */
17624 1136 : ScanKeyInit(&child_key,
17625 : Anum_pg_constraint_conrelid,
17626 : BTEqualStrategyNumber, F_OIDEQ,
17627 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17628 1136 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17629 : true, NULL, 1, &child_key);
17630 :
17631 1734 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17632 : {
17633 1710 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17634 : HeapTuple child_copy;
17635 :
17636 1710 : if (child_con->contype != parent_con->contype)
17637 284 : continue;
17638 :
17639 : /*
17640 : * CHECK constraint are matched by constraint name, NOT NULL ones
17641 : * by attribute number.
17642 : */
17643 1426 : if (child_con->contype == CONSTRAINT_CHECK)
17644 : {
17645 154 : if (strcmp(NameStr(parent_con->conname),
17646 124 : NameStr(child_con->conname)) != 0)
17647 30 : continue;
17648 : }
17649 1302 : else if (child_con->contype == CONSTRAINT_NOTNULL)
17650 : {
17651 : Form_pg_attribute parent_attr;
17652 : Form_pg_attribute child_attr;
17653 : AttrNumber child_attno;
17654 :
17655 1302 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17656 1302 : child_attno = extractNotNullColumn(child_tuple);
17657 1302 : if (parent_attno != attmap->attnums[child_attno - 1])
17658 284 : continue;
17659 :
17660 1018 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17661 : /* there shouldn't be constraints on dropped columns */
17662 1018 : if (parent_attr->attisdropped || child_attr->attisdropped)
17663 0 : elog(ERROR, "found not-null constraint on dropped columns");
17664 : }
17665 :
17666 1112 : if (child_con->contype == CONSTRAINT_CHECK &&
17667 94 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17668 6 : ereport(ERROR,
17669 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17670 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17671 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17672 :
17673 : /*
17674 : * If the child constraint is "no inherit" then cannot merge
17675 : */
17676 1106 : if (child_con->connoinherit)
17677 12 : ereport(ERROR,
17678 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17679 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17680 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17681 :
17682 : /*
17683 : * If the child constraint is "not valid" then cannot merge with a
17684 : * valid parent constraint
17685 : */
17686 1094 : if (parent_con->convalidated && child_con->conenforced &&
17687 1040 : !child_con->convalidated)
17688 12 : ereport(ERROR,
17689 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17690 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17691 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17692 :
17693 : /*
17694 : * A NOT ENFORCED child constraint cannot be merged with an
17695 : * ENFORCED parent constraint. However, the reverse is allowed,
17696 : * where the child constraint is ENFORCED.
17697 : */
17698 1082 : if (parent_con->conenforced && !child_con->conenforced)
17699 6 : ereport(ERROR,
17700 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17701 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17702 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17703 :
17704 : /*
17705 : * OK, bump the child constraint's inheritance count. (If we fail
17706 : * later on, this change will just roll back.)
17707 : */
17708 1076 : child_copy = heap_copytuple(child_tuple);
17709 1076 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17710 :
17711 1076 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
17712 : &child_con->coninhcount))
17713 0 : ereport(ERROR,
17714 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17715 : errmsg("too many inheritance parents"));
17716 :
17717 : /*
17718 : * In case of partitions, an inherited constraint must be
17719 : * inherited only once since it cannot have multiple parents and
17720 : * it is never considered local.
17721 : */
17722 1076 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17723 : {
17724 : Assert(child_con->coninhcount == 1);
17725 928 : child_con->conislocal = false;
17726 : }
17727 :
17728 1076 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17729 1076 : heap_freetuple(child_copy);
17730 :
17731 1076 : found = true;
17732 1076 : break;
17733 : }
17734 :
17735 1100 : systable_endscan(child_scan);
17736 :
17737 1100 : if (!found)
17738 : {
17739 24 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17740 0 : ereport(ERROR,
17741 : errcode(ERRCODE_DATATYPE_MISMATCH),
17742 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17743 : get_attname(parent_relid,
17744 : extractNotNullColumn(parent_tuple),
17745 : false),
17746 : RelationGetRelationName(child_rel)));
17747 :
17748 24 : ereport(ERROR,
17749 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17750 : errmsg("child table is missing constraint \"%s\"",
17751 : NameStr(parent_con->conname))));
17752 : }
17753 : }
17754 :
17755 2838 : systable_endscan(parent_scan);
17756 2838 : table_close(constraintrel, RowExclusiveLock);
17757 2838 : }
17758 :
17759 : /*
17760 : * ALTER TABLE NO INHERIT
17761 : *
17762 : * Return value is the address of the relation that is no longer parent.
17763 : */
17764 : static ObjectAddress
17765 86 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
17766 : {
17767 : ObjectAddress address;
17768 : Relation parent_rel;
17769 :
17770 86 : if (rel->rd_rel->relispartition)
17771 0 : ereport(ERROR,
17772 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17773 : errmsg("cannot change inheritance of a partition")));
17774 :
17775 : /*
17776 : * AccessShareLock on the parent is probably enough, seeing that DROP
17777 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
17778 : * be inspecting the parent's schema.
17779 : */
17780 86 : parent_rel = table_openrv(parent, AccessShareLock);
17781 :
17782 : /*
17783 : * We don't bother to check ownership of the parent table --- ownership of
17784 : * the child is presumed enough rights.
17785 : */
17786 :
17787 : /* Off to RemoveInheritance() where most of the work happens */
17788 86 : RemoveInheritance(rel, parent_rel, false);
17789 :
17790 80 : ObjectAddressSet(address, RelationRelationId,
17791 : RelationGetRelid(parent_rel));
17792 :
17793 : /* keep our lock on the parent relation until commit */
17794 80 : table_close(parent_rel, NoLock);
17795 :
17796 80 : return address;
17797 : }
17798 :
17799 : /*
17800 : * MarkInheritDetached
17801 : *
17802 : * Set inhdetachpending for a partition, for ATExecDetachPartition
17803 : * in concurrent mode. While at it, verify that no other partition is
17804 : * already pending detach.
17805 : */
17806 : static void
17807 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
17808 : {
17809 : Relation catalogRelation;
17810 : SysScanDesc scan;
17811 : ScanKeyData key;
17812 : HeapTuple inheritsTuple;
17813 146 : bool found = false;
17814 :
17815 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17816 :
17817 : /*
17818 : * Find pg_inherits entries by inhparent. (We need to scan them all in
17819 : * order to verify that no other partition is pending detach.)
17820 : */
17821 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17822 146 : ScanKeyInit(&key,
17823 : Anum_pg_inherits_inhparent,
17824 : BTEqualStrategyNumber, F_OIDEQ,
17825 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17826 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17827 : true, NULL, 1, &key);
17828 :
17829 576 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17830 : {
17831 : Form_pg_inherits inhForm;
17832 :
17833 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17834 286 : if (inhForm->inhdetachpending)
17835 2 : ereport(ERROR,
17836 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17837 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17838 : get_rel_name(inhForm->inhrelid),
17839 : get_namespace_name(parent_rel->rd_rel->relnamespace),
17840 : RelationGetRelationName(parent_rel)),
17841 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17842 :
17843 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
17844 : {
17845 : HeapTuple newtup;
17846 :
17847 144 : newtup = heap_copytuple(inheritsTuple);
17848 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17849 :
17850 144 : CatalogTupleUpdate(catalogRelation,
17851 : &inheritsTuple->t_self,
17852 : newtup);
17853 144 : found = true;
17854 144 : heap_freetuple(newtup);
17855 : /* keep looking, to ensure we catch others pending detach */
17856 : }
17857 : }
17858 :
17859 : /* Done */
17860 144 : systable_endscan(scan);
17861 144 : table_close(catalogRelation, RowExclusiveLock);
17862 :
17863 144 : if (!found)
17864 0 : ereport(ERROR,
17865 : (errcode(ERRCODE_UNDEFINED_TABLE),
17866 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17867 : RelationGetRelationName(child_rel),
17868 : RelationGetRelationName(parent_rel))));
17869 144 : }
17870 :
17871 : /*
17872 : * RemoveInheritance
17873 : *
17874 : * Drop a parent from the child's parents. This just adjusts the attinhcount
17875 : * and attislocal of the columns and removes the pg_inherit and pg_depend
17876 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17877 : *
17878 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17879 : * up attislocal stays true, which means if a child is ever removed from a
17880 : * parent then its columns will never be automatically dropped which may
17881 : * surprise. But at least we'll never surprise by dropping columns someone
17882 : * isn't expecting to be dropped which would actually mean data loss.
17883 : *
17884 : * coninhcount and conislocal for inherited constraints are adjusted in
17885 : * exactly the same way.
17886 : *
17887 : * Common to ATExecDropInherit() and ATExecDetachPartition().
17888 : */
17889 : static void
17890 590 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17891 : {
17892 : Relation catalogRelation;
17893 : SysScanDesc scan;
17894 : ScanKeyData key[3];
17895 : HeapTuple attributeTuple,
17896 : constraintTuple;
17897 : AttrMap *attmap;
17898 : List *connames;
17899 : List *nncolumns;
17900 : bool found;
17901 : bool is_partitioning;
17902 :
17903 590 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17904 :
17905 590 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17906 : RelationGetRelid(parent_rel),
17907 : expect_detached,
17908 590 : RelationGetRelationName(child_rel));
17909 590 : if (!found)
17910 : {
17911 24 : if (is_partitioning)
17912 18 : ereport(ERROR,
17913 : (errcode(ERRCODE_UNDEFINED_TABLE),
17914 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17915 : RelationGetRelationName(child_rel),
17916 : RelationGetRelationName(parent_rel))));
17917 : else
17918 6 : ereport(ERROR,
17919 : (errcode(ERRCODE_UNDEFINED_TABLE),
17920 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17921 : RelationGetRelationName(parent_rel),
17922 : RelationGetRelationName(child_rel))));
17923 : }
17924 :
17925 : /*
17926 : * Search through child columns looking for ones matching parent rel
17927 : */
17928 566 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17929 566 : ScanKeyInit(&key[0],
17930 : Anum_pg_attribute_attrelid,
17931 : BTEqualStrategyNumber, F_OIDEQ,
17932 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17933 566 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17934 : true, NULL, 1, key);
17935 5054 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17936 : {
17937 4488 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17938 :
17939 : /* Ignore if dropped or not inherited */
17940 4488 : if (att->attisdropped)
17941 6 : continue;
17942 4482 : if (att->attinhcount <= 0)
17943 3426 : continue;
17944 :
17945 1056 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
17946 1056 : NameStr(att->attname)))
17947 : {
17948 : /* Decrement inhcount and possibly set islocal to true */
17949 1002 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
17950 1002 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17951 :
17952 1002 : copy_att->attinhcount--;
17953 1002 : if (copy_att->attinhcount == 0)
17954 972 : copy_att->attislocal = true;
17955 :
17956 1002 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
17957 1002 : heap_freetuple(copyTuple);
17958 : }
17959 : }
17960 566 : systable_endscan(scan);
17961 566 : table_close(catalogRelation, RowExclusiveLock);
17962 :
17963 : /*
17964 : * Likewise, find inherited check and not-null constraints and disinherit
17965 : * them. To do this, we first need a list of the names of the parent's
17966 : * check constraints. (We cheat a bit by only checking for name matches,
17967 : * assuming that the expressions will match.)
17968 : *
17969 : * For NOT NULL columns, we store column numbers to match, mapping them in
17970 : * to the child rel's attribute numbers.
17971 : */
17972 566 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17973 : RelationGetDescr(parent_rel),
17974 : false);
17975 :
17976 566 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17977 566 : ScanKeyInit(&key[0],
17978 : Anum_pg_constraint_conrelid,
17979 : BTEqualStrategyNumber, F_OIDEQ,
17980 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17981 566 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17982 : true, NULL, 1, key);
17983 :
17984 566 : connames = NIL;
17985 566 : nncolumns = NIL;
17986 :
17987 1220 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17988 : {
17989 654 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17990 :
17991 654 : if (con->connoinherit)
17992 110 : continue;
17993 :
17994 544 : if (con->contype == CONSTRAINT_CHECK)
17995 12 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
17996 544 : if (con->contype == CONSTRAINT_NOTNULL)
17997 : {
17998 208 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
17999 :
18000 208 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18001 : }
18002 : }
18003 :
18004 566 : systable_endscan(scan);
18005 :
18006 : /* Now scan the child's constraints to find matches */
18007 566 : ScanKeyInit(&key[0],
18008 : Anum_pg_constraint_conrelid,
18009 : BTEqualStrategyNumber, F_OIDEQ,
18010 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
18011 566 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18012 : true, NULL, 1, key);
18013 :
18014 1320 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18015 : {
18016 754 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18017 754 : bool match = false;
18018 :
18019 : /*
18020 : * Match CHECK constraints by name, not-null constraints by column
18021 : * number, and ignore all others.
18022 : */
18023 754 : if (con->contype == CONSTRAINT_CHECK)
18024 : {
18025 350 : foreach_ptr(char, chkname, connames)
18026 : {
18027 18 : if (con->contype == CONSTRAINT_CHECK &&
18028 18 : strcmp(NameStr(con->conname), chkname) == 0)
18029 : {
18030 12 : match = true;
18031 12 : connames = foreach_delete_current(connames, chkname);
18032 12 : break;
18033 : }
18034 : }
18035 : }
18036 582 : else if (con->contype == CONSTRAINT_NOTNULL)
18037 : {
18038 268 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18039 :
18040 542 : foreach_int(prevattno, nncolumns)
18041 : {
18042 214 : if (prevattno == child_attno)
18043 : {
18044 208 : match = true;
18045 208 : nncolumns = foreach_delete_current(nncolumns, prevattno);
18046 208 : break;
18047 : }
18048 : }
18049 : }
18050 : else
18051 314 : continue;
18052 :
18053 440 : if (match)
18054 : {
18055 : /* Decrement inhcount and possibly set islocal to true */
18056 220 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
18057 220 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18058 :
18059 220 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
18060 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18061 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
18062 :
18063 220 : copy_con->coninhcount--;
18064 220 : if (copy_con->coninhcount == 0)
18065 202 : copy_con->conislocal = true;
18066 :
18067 220 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18068 220 : heap_freetuple(copyTuple);
18069 : }
18070 : }
18071 :
18072 : /* We should have matched all constraints */
18073 566 : if (connames != NIL || nncolumns != NIL)
18074 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18075 : list_length(connames) + list_length(nncolumns),
18076 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18077 :
18078 566 : systable_endscan(scan);
18079 566 : table_close(catalogRelation, RowExclusiveLock);
18080 :
18081 566 : drop_parent_dependency(RelationGetRelid(child_rel),
18082 : RelationRelationId,
18083 : RelationGetRelid(parent_rel),
18084 : child_dependency_type(is_partitioning));
18085 :
18086 : /*
18087 : * Post alter hook of this inherits. Since object_access_hook doesn't take
18088 : * multiple object identifiers, we relay oid of parent relation using
18089 : * auxiliary_id argument.
18090 : */
18091 566 : InvokeObjectPostAlterHookArg(InheritsRelationId,
18092 : RelationGetRelid(child_rel), 0,
18093 : RelationGetRelid(parent_rel), false);
18094 566 : }
18095 :
18096 : /*
18097 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18098 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18099 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18100 : * be TypeRelationId). There's no convenient way to do this, so go trawling
18101 : * through pg_depend.
18102 : */
18103 : static void
18104 578 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18105 : DependencyType deptype)
18106 : {
18107 : Relation catalogRelation;
18108 : SysScanDesc scan;
18109 : ScanKeyData key[3];
18110 : HeapTuple depTuple;
18111 :
18112 578 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18113 :
18114 578 : ScanKeyInit(&key[0],
18115 : Anum_pg_depend_classid,
18116 : BTEqualStrategyNumber, F_OIDEQ,
18117 : ObjectIdGetDatum(RelationRelationId));
18118 578 : ScanKeyInit(&key[1],
18119 : Anum_pg_depend_objid,
18120 : BTEqualStrategyNumber, F_OIDEQ,
18121 : ObjectIdGetDatum(relid));
18122 578 : ScanKeyInit(&key[2],
18123 : Anum_pg_depend_objsubid,
18124 : BTEqualStrategyNumber, F_INT4EQ,
18125 : Int32GetDatum(0));
18126 :
18127 578 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18128 : NULL, 3, key);
18129 :
18130 1794 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18131 : {
18132 1216 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18133 :
18134 1216 : if (dep->refclassid == refclassid &&
18135 620 : dep->refobjid == refobjid &&
18136 578 : dep->refobjsubid == 0 &&
18137 578 : dep->deptype == deptype)
18138 578 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18139 : }
18140 :
18141 578 : systable_endscan(scan);
18142 578 : table_close(catalogRelation, RowExclusiveLock);
18143 578 : }
18144 :
18145 : /*
18146 : * ALTER TABLE OF
18147 : *
18148 : * Attach a table to a composite type, as though it had been created with CREATE
18149 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18150 : * subject table must not have inheritance parents. These restrictions ensure
18151 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18152 : *
18153 : * The address of the type is returned.
18154 : */
18155 : static ObjectAddress
18156 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18157 : {
18158 66 : Oid relid = RelationGetRelid(rel);
18159 : Type typetuple;
18160 : Form_pg_type typeform;
18161 : Oid typeid;
18162 : Relation inheritsRelation,
18163 : relationRelation;
18164 : SysScanDesc scan;
18165 : ScanKeyData key;
18166 : AttrNumber table_attno,
18167 : type_attno;
18168 : TupleDesc typeTupleDesc,
18169 : tableTupleDesc;
18170 : ObjectAddress tableobj,
18171 : typeobj;
18172 : HeapTuple classtuple;
18173 :
18174 : /* Validate the type. */
18175 66 : typetuple = typenameType(NULL, ofTypename, NULL);
18176 66 : check_of_type(typetuple);
18177 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
18178 66 : typeid = typeform->oid;
18179 :
18180 : /* Fail if the table has any inheritance parents. */
18181 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18182 66 : ScanKeyInit(&key,
18183 : Anum_pg_inherits_inhrelid,
18184 : BTEqualStrategyNumber, F_OIDEQ,
18185 : ObjectIdGetDatum(relid));
18186 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18187 : true, NULL, 1, &key);
18188 66 : if (HeapTupleIsValid(systable_getnext(scan)))
18189 6 : ereport(ERROR,
18190 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18191 : errmsg("typed tables cannot inherit")));
18192 60 : systable_endscan(scan);
18193 60 : table_close(inheritsRelation, AccessShareLock);
18194 :
18195 : /*
18196 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
18197 : * require that the order also match. However, attnotnull need not match.
18198 : */
18199 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18200 60 : tableTupleDesc = RelationGetDescr(rel);
18201 60 : table_attno = 1;
18202 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18203 : {
18204 : Form_pg_attribute type_attr,
18205 : table_attr;
18206 : const char *type_attname,
18207 : *table_attname;
18208 :
18209 : /* Get the next non-dropped type attribute. */
18210 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18211 154 : if (type_attr->attisdropped)
18212 44 : continue;
18213 110 : type_attname = NameStr(type_attr->attname);
18214 :
18215 : /* Get the next non-dropped table attribute. */
18216 : do
18217 : {
18218 122 : if (table_attno > tableTupleDesc->natts)
18219 6 : ereport(ERROR,
18220 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18221 : errmsg("table is missing column \"%s\"",
18222 : type_attname)));
18223 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18224 116 : table_attno++;
18225 116 : } while (table_attr->attisdropped);
18226 104 : table_attname = NameStr(table_attr->attname);
18227 :
18228 : /* Compare name. */
18229 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18230 6 : ereport(ERROR,
18231 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18232 : errmsg("table has column \"%s\" where type requires \"%s\"",
18233 : table_attname, type_attname)));
18234 :
18235 : /* Compare type. */
18236 98 : if (table_attr->atttypid != type_attr->atttypid ||
18237 92 : table_attr->atttypmod != type_attr->atttypmod ||
18238 86 : table_attr->attcollation != type_attr->attcollation)
18239 12 : ereport(ERROR,
18240 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18241 : errmsg("table \"%s\" has different type for column \"%s\"",
18242 : RelationGetRelationName(rel), type_attname)));
18243 : }
18244 36 : ReleaseTupleDesc(typeTupleDesc);
18245 :
18246 : /* Any remaining columns at the end of the table had better be dropped. */
18247 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
18248 : {
18249 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18250 : table_attno - 1);
18251 :
18252 6 : if (!table_attr->attisdropped)
18253 6 : ereport(ERROR,
18254 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18255 : errmsg("table has extra column \"%s\"",
18256 : NameStr(table_attr->attname))));
18257 : }
18258 :
18259 : /* If the table was already typed, drop the existing dependency. */
18260 30 : if (rel->rd_rel->reloftype)
18261 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18262 : DEPENDENCY_NORMAL);
18263 :
18264 : /* Record a dependency on the new type. */
18265 30 : tableobj.classId = RelationRelationId;
18266 30 : tableobj.objectId = relid;
18267 30 : tableobj.objectSubId = 0;
18268 30 : typeobj.classId = TypeRelationId;
18269 30 : typeobj.objectId = typeid;
18270 30 : typeobj.objectSubId = 0;
18271 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18272 :
18273 : /* Update pg_class.reloftype */
18274 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18275 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18276 30 : if (!HeapTupleIsValid(classtuple))
18277 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18278 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18279 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18280 :
18281 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18282 :
18283 30 : heap_freetuple(classtuple);
18284 30 : table_close(relationRelation, RowExclusiveLock);
18285 :
18286 30 : ReleaseSysCache(typetuple);
18287 :
18288 30 : return typeobj;
18289 : }
18290 :
18291 : /*
18292 : * ALTER TABLE NOT OF
18293 : *
18294 : * Detach a typed table from its originating type. Just clear reloftype and
18295 : * remove the dependency.
18296 : */
18297 : static void
18298 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
18299 : {
18300 6 : Oid relid = RelationGetRelid(rel);
18301 : Relation relationRelation;
18302 : HeapTuple tuple;
18303 :
18304 6 : if (!OidIsValid(rel->rd_rel->reloftype))
18305 0 : ereport(ERROR,
18306 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18307 : errmsg("\"%s\" is not a typed table",
18308 : RelationGetRelationName(rel))));
18309 :
18310 : /*
18311 : * We don't bother to check ownership of the type --- ownership of the
18312 : * table is presumed enough rights. No lock required on the type, either.
18313 : */
18314 :
18315 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18316 : DEPENDENCY_NORMAL);
18317 :
18318 : /* Clear pg_class.reloftype */
18319 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18320 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18321 6 : if (!HeapTupleIsValid(tuple))
18322 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18323 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18324 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18325 :
18326 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18327 :
18328 6 : heap_freetuple(tuple);
18329 6 : table_close(relationRelation, RowExclusiveLock);
18330 6 : }
18331 :
18332 : /*
18333 : * relation_mark_replica_identity: Update a table's replica identity
18334 : *
18335 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18336 : * index. Otherwise, it must be InvalidOid.
18337 : *
18338 : * Caller had better hold an exclusive lock on the relation, as the results
18339 : * of running two of these concurrently wouldn't be pretty.
18340 : */
18341 : static void
18342 460 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18343 : bool is_internal)
18344 : {
18345 : Relation pg_index;
18346 : Relation pg_class;
18347 : HeapTuple pg_class_tuple;
18348 : HeapTuple pg_index_tuple;
18349 : Form_pg_class pg_class_form;
18350 : Form_pg_index pg_index_form;
18351 : ListCell *index;
18352 :
18353 : /*
18354 : * Check whether relreplident has changed, and update it if so.
18355 : */
18356 460 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18357 460 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
18358 : ObjectIdGetDatum(RelationGetRelid(rel)));
18359 460 : if (!HeapTupleIsValid(pg_class_tuple))
18360 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
18361 : RelationGetRelationName(rel));
18362 460 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18363 460 : if (pg_class_form->relreplident != ri_type)
18364 : {
18365 410 : pg_class_form->relreplident = ri_type;
18366 410 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18367 : }
18368 460 : table_close(pg_class, RowExclusiveLock);
18369 460 : heap_freetuple(pg_class_tuple);
18370 :
18371 : /*
18372 : * Update the per-index indisreplident flags correctly.
18373 : */
18374 460 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
18375 1180 : foreach(index, RelationGetIndexList(rel))
18376 : {
18377 720 : Oid thisIndexOid = lfirst_oid(index);
18378 720 : bool dirty = false;
18379 :
18380 720 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18381 : ObjectIdGetDatum(thisIndexOid));
18382 720 : if (!HeapTupleIsValid(pg_index_tuple))
18383 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18384 720 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18385 :
18386 720 : if (thisIndexOid == indexOid)
18387 : {
18388 : /* Set the bit if not already set. */
18389 240 : if (!pg_index_form->indisreplident)
18390 : {
18391 222 : dirty = true;
18392 222 : pg_index_form->indisreplident = true;
18393 : }
18394 : }
18395 : else
18396 : {
18397 : /* Unset the bit if set. */
18398 480 : if (pg_index_form->indisreplident)
18399 : {
18400 52 : dirty = true;
18401 52 : pg_index_form->indisreplident = false;
18402 : }
18403 : }
18404 :
18405 720 : if (dirty)
18406 : {
18407 274 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18408 274 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18409 : InvalidOid, is_internal);
18410 :
18411 : /*
18412 : * Invalidate the relcache for the table, so that after we commit
18413 : * all sessions will refresh the table's replica identity index
18414 : * before attempting any UPDATE or DELETE on the table. (If we
18415 : * changed the table's pg_class row above, then a relcache inval
18416 : * is already queued due to that; but we might not have.)
18417 : */
18418 274 : CacheInvalidateRelcache(rel);
18419 : }
18420 720 : heap_freetuple(pg_index_tuple);
18421 : }
18422 :
18423 460 : table_close(pg_index, RowExclusiveLock);
18424 460 : }
18425 :
18426 : /*
18427 : * ALTER TABLE <name> REPLICA IDENTITY ...
18428 : */
18429 : static void
18430 508 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
18431 : {
18432 : Oid indexOid;
18433 : Relation indexRel;
18434 : int key;
18435 :
18436 508 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18437 : {
18438 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18439 6 : return;
18440 : }
18441 502 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18442 : {
18443 166 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18444 166 : return;
18445 : }
18446 336 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18447 : {
18448 48 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18449 48 : return;
18450 : }
18451 288 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18452 : {
18453 : /* fallthrough */ ;
18454 : }
18455 : else
18456 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18457 :
18458 : /* Check that the index exists */
18459 288 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18460 288 : if (!OidIsValid(indexOid))
18461 0 : ereport(ERROR,
18462 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18463 : errmsg("index \"%s\" for table \"%s\" does not exist",
18464 : stmt->name, RelationGetRelationName(rel))));
18465 :
18466 288 : indexRel = index_open(indexOid, ShareLock);
18467 :
18468 : /* Check that the index is on the relation we're altering. */
18469 288 : if (indexRel->rd_index == NULL ||
18470 288 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
18471 6 : ereport(ERROR,
18472 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18473 : errmsg("\"%s\" is not an index for table \"%s\"",
18474 : RelationGetRelationName(indexRel),
18475 : RelationGetRelationName(rel))));
18476 :
18477 : /*
18478 : * The AM must support uniqueness, and the index must in fact be unique.
18479 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18480 : * exclusion), we can use that too.
18481 : */
18482 282 : if ((!indexRel->rd_indam->amcanunique ||
18483 262 : !indexRel->rd_index->indisunique) &&
18484 26 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18485 12 : ereport(ERROR,
18486 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18487 : errmsg("cannot use non-unique index \"%s\" as replica identity",
18488 : RelationGetRelationName(indexRel))));
18489 : /* Deferred indexes are not guaranteed to be always unique. */
18490 270 : if (!indexRel->rd_index->indimmediate)
18491 12 : ereport(ERROR,
18492 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18493 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
18494 : RelationGetRelationName(indexRel))));
18495 : /* Expression indexes aren't supported. */
18496 258 : if (RelationGetIndexExpressions(indexRel) != NIL)
18497 6 : ereport(ERROR,
18498 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18499 : errmsg("cannot use expression index \"%s\" as replica identity",
18500 : RelationGetRelationName(indexRel))));
18501 : /* Predicate indexes aren't supported. */
18502 252 : if (RelationGetIndexPredicate(indexRel) != NIL)
18503 6 : ereport(ERROR,
18504 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18505 : errmsg("cannot use partial index \"%s\" as replica identity",
18506 : RelationGetRelationName(indexRel))));
18507 :
18508 : /* Check index for nullable columns. */
18509 552 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18510 : {
18511 312 : int16 attno = indexRel->rd_index->indkey.values[key];
18512 : Form_pg_attribute attr;
18513 :
18514 : /*
18515 : * Reject any other system columns. (Going forward, we'll disallow
18516 : * indexes containing such columns in the first place, but they might
18517 : * exist in older branches.)
18518 : */
18519 312 : if (attno <= 0)
18520 0 : ereport(ERROR,
18521 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18522 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18523 : RelationGetRelationName(indexRel), attno)));
18524 :
18525 312 : attr = TupleDescAttr(rel->rd_att, attno - 1);
18526 312 : if (!attr->attnotnull)
18527 6 : ereport(ERROR,
18528 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18529 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18530 : RelationGetRelationName(indexRel),
18531 : NameStr(attr->attname))));
18532 : }
18533 :
18534 : /* This index is suitable for use as a replica identity. Mark it. */
18535 240 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18536 :
18537 240 : index_close(indexRel, NoLock);
18538 : }
18539 :
18540 : /*
18541 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18542 : */
18543 : static void
18544 318 : ATExecSetRowSecurity(Relation rel, bool rls)
18545 : {
18546 : Relation pg_class;
18547 : Oid relid;
18548 : HeapTuple tuple;
18549 :
18550 318 : relid = RelationGetRelid(rel);
18551 :
18552 : /* Pull the record for this relation and update it */
18553 318 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18554 :
18555 318 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18556 :
18557 318 : if (!HeapTupleIsValid(tuple))
18558 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18559 :
18560 318 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18561 318 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18562 :
18563 318 : InvokeObjectPostAlterHook(RelationRelationId,
18564 : RelationGetRelid(rel), 0);
18565 :
18566 318 : table_close(pg_class, RowExclusiveLock);
18567 318 : heap_freetuple(tuple);
18568 318 : }
18569 :
18570 : /*
18571 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18572 : */
18573 : static void
18574 134 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
18575 : {
18576 : Relation pg_class;
18577 : Oid relid;
18578 : HeapTuple tuple;
18579 :
18580 134 : relid = RelationGetRelid(rel);
18581 :
18582 134 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18583 :
18584 134 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18585 :
18586 134 : if (!HeapTupleIsValid(tuple))
18587 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18588 :
18589 134 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18590 134 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18591 :
18592 134 : InvokeObjectPostAlterHook(RelationRelationId,
18593 : RelationGetRelid(rel), 0);
18594 :
18595 134 : table_close(pg_class, RowExclusiveLock);
18596 134 : heap_freetuple(tuple);
18597 134 : }
18598 :
18599 : /*
18600 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
18601 : */
18602 : static void
18603 58 : ATExecGenericOptions(Relation rel, List *options)
18604 : {
18605 : Relation ftrel;
18606 : ForeignServer *server;
18607 : ForeignDataWrapper *fdw;
18608 : HeapTuple tuple;
18609 : bool isnull;
18610 : Datum repl_val[Natts_pg_foreign_table];
18611 : bool repl_null[Natts_pg_foreign_table];
18612 : bool repl_repl[Natts_pg_foreign_table];
18613 : Datum datum;
18614 : Form_pg_foreign_table tableform;
18615 :
18616 58 : if (options == NIL)
18617 0 : return;
18618 :
18619 58 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18620 :
18621 58 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18622 : ObjectIdGetDatum(rel->rd_id));
18623 58 : if (!HeapTupleIsValid(tuple))
18624 0 : ereport(ERROR,
18625 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18626 : errmsg("foreign table \"%s\" does not exist",
18627 : RelationGetRelationName(rel))));
18628 58 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18629 58 : server = GetForeignServer(tableform->ftserver);
18630 58 : fdw = GetForeignDataWrapper(server->fdwid);
18631 :
18632 58 : memset(repl_val, 0, sizeof(repl_val));
18633 58 : memset(repl_null, false, sizeof(repl_null));
18634 58 : memset(repl_repl, false, sizeof(repl_repl));
18635 :
18636 : /* Extract the current options */
18637 58 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
18638 : tuple,
18639 : Anum_pg_foreign_table_ftoptions,
18640 : &isnull);
18641 58 : if (isnull)
18642 4 : datum = PointerGetDatum(NULL);
18643 :
18644 : /* Transform the options */
18645 58 : datum = transformGenericOptions(ForeignTableRelationId,
18646 : datum,
18647 : options,
18648 : fdw->fdwvalidator);
18649 :
18650 56 : if (PointerIsValid(DatumGetPointer(datum)))
18651 56 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18652 : else
18653 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18654 :
18655 56 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18656 :
18657 : /* Everything looks good - update the tuple */
18658 :
18659 56 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18660 : repl_val, repl_null, repl_repl);
18661 :
18662 56 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18663 :
18664 : /*
18665 : * Invalidate relcache so that all sessions will refresh any cached plans
18666 : * that might depend on the old options.
18667 : */
18668 56 : CacheInvalidateRelcache(rel);
18669 :
18670 56 : InvokeObjectPostAlterHook(ForeignTableRelationId,
18671 : RelationGetRelid(rel), 0);
18672 :
18673 56 : table_close(ftrel, RowExclusiveLock);
18674 :
18675 56 : heap_freetuple(tuple);
18676 : }
18677 :
18678 : /*
18679 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
18680 : *
18681 : * Return value is the address of the modified column
18682 : */
18683 : static ObjectAddress
18684 90 : ATExecSetCompression(Relation rel,
18685 : const char *column,
18686 : Node *newValue,
18687 : LOCKMODE lockmode)
18688 : {
18689 : Relation attrel;
18690 : HeapTuple tuple;
18691 : Form_pg_attribute atttableform;
18692 : AttrNumber attnum;
18693 : char *compression;
18694 : char cmethod;
18695 : ObjectAddress address;
18696 :
18697 90 : compression = strVal(newValue);
18698 :
18699 90 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
18700 :
18701 : /* copy the cache entry so we can scribble on it below */
18702 90 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18703 90 : if (!HeapTupleIsValid(tuple))
18704 0 : ereport(ERROR,
18705 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18706 : errmsg("column \"%s\" of relation \"%s\" does not exist",
18707 : column, RelationGetRelationName(rel))));
18708 :
18709 : /* prevent them from altering a system attribute */
18710 90 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18711 90 : attnum = atttableform->attnum;
18712 90 : if (attnum <= 0)
18713 0 : ereport(ERROR,
18714 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18715 : errmsg("cannot alter system column \"%s\"", column)));
18716 :
18717 : /*
18718 : * Check that column type is compressible, then get the attribute
18719 : * compression method code
18720 : */
18721 90 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18722 :
18723 : /* update pg_attribute entry */
18724 84 : atttableform->attcompression = cmethod;
18725 84 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18726 :
18727 84 : InvokeObjectPostAlterHook(RelationRelationId,
18728 : RelationGetRelid(rel),
18729 : attnum);
18730 :
18731 : /*
18732 : * Apply the change to indexes as well (only for simple index columns,
18733 : * matching behavior of index.c ConstructTupleDescriptor()).
18734 : */
18735 84 : SetIndexStorageProperties(rel, attrel, attnum,
18736 : false, 0,
18737 : true, cmethod,
18738 : lockmode);
18739 :
18740 84 : heap_freetuple(tuple);
18741 :
18742 84 : table_close(attrel, RowExclusiveLock);
18743 :
18744 : /* make changes visible */
18745 84 : CommandCounterIncrement();
18746 :
18747 84 : ObjectAddressSubSet(address, RelationRelationId,
18748 : RelationGetRelid(rel), attnum);
18749 84 : return address;
18750 : }
18751 :
18752 :
18753 : /*
18754 : * Preparation phase for SET LOGGED/UNLOGGED
18755 : *
18756 : * This verifies that we're not trying to change a temp table. Also,
18757 : * existing foreign key constraints are checked to avoid ending up with
18758 : * permanent tables referencing unlogged tables.
18759 : */
18760 : static void
18761 100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
18762 : {
18763 : Relation pg_constraint;
18764 : HeapTuple tuple;
18765 : SysScanDesc scan;
18766 : ScanKeyData skey[1];
18767 :
18768 : /*
18769 : * Disallow changing status for a temp table. Also verify whether we can
18770 : * get away with doing nothing; in such cases we don't need to run the
18771 : * checks below, either.
18772 : */
18773 100 : switch (rel->rd_rel->relpersistence)
18774 : {
18775 0 : case RELPERSISTENCE_TEMP:
18776 0 : ereport(ERROR,
18777 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18778 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
18779 : RelationGetRelationName(rel)),
18780 : errtable(rel)));
18781 : break;
18782 56 : case RELPERSISTENCE_PERMANENT:
18783 56 : if (toLogged)
18784 : /* nothing to do */
18785 12 : return;
18786 50 : break;
18787 44 : case RELPERSISTENCE_UNLOGGED:
18788 44 : if (!toLogged)
18789 : /* nothing to do */
18790 6 : return;
18791 38 : break;
18792 : }
18793 :
18794 : /*
18795 : * Check that the table is not part of any publication when changing to
18796 : * UNLOGGED, as UNLOGGED tables can't be published.
18797 : */
18798 138 : if (!toLogged &&
18799 50 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
18800 0 : ereport(ERROR,
18801 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18802 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18803 : RelationGetRelationName(rel)),
18804 : errdetail("Unlogged relations cannot be replicated.")));
18805 :
18806 : /*
18807 : * Check existing foreign key constraints to preserve the invariant that
18808 : * permanent tables cannot reference unlogged ones. Self-referencing
18809 : * foreign keys can safely be ignored.
18810 : */
18811 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18812 :
18813 : /*
18814 : * Scan conrelid if changing to permanent, else confrelid. This also
18815 : * determines whether a useful index exists.
18816 : */
18817 88 : ScanKeyInit(&skey[0],
18818 : toLogged ? Anum_pg_constraint_conrelid :
18819 : Anum_pg_constraint_confrelid,
18820 : BTEqualStrategyNumber, F_OIDEQ,
18821 : ObjectIdGetDatum(RelationGetRelid(rel)));
18822 88 : scan = systable_beginscan(pg_constraint,
18823 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18824 : true, NULL, 1, skey);
18825 :
18826 142 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18827 : {
18828 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
18829 :
18830 66 : if (con->contype == CONSTRAINT_FOREIGN)
18831 : {
18832 : Oid foreignrelid;
18833 : Relation foreignrel;
18834 :
18835 : /* the opposite end of what we used as scankey */
18836 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
18837 :
18838 : /* ignore if self-referencing */
18839 30 : if (RelationGetRelid(rel) == foreignrelid)
18840 12 : continue;
18841 :
18842 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
18843 :
18844 18 : if (toLogged)
18845 : {
18846 6 : if (!RelationIsPermanent(foreignrel))
18847 6 : ereport(ERROR,
18848 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18849 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18850 : RelationGetRelationName(rel),
18851 : RelationGetRelationName(foreignrel)),
18852 : errtableconstraint(rel, NameStr(con->conname))));
18853 : }
18854 : else
18855 : {
18856 12 : if (RelationIsPermanent(foreignrel))
18857 6 : ereport(ERROR,
18858 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18859 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18860 : RelationGetRelationName(rel),
18861 : RelationGetRelationName(foreignrel)),
18862 : errtableconstraint(rel, NameStr(con->conname))));
18863 : }
18864 :
18865 6 : relation_close(foreignrel, AccessShareLock);
18866 : }
18867 : }
18868 :
18869 76 : systable_endscan(scan);
18870 :
18871 76 : table_close(pg_constraint, AccessShareLock);
18872 :
18873 : /* force rewrite if necessary; see comment in ATRewriteTables */
18874 76 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
18875 76 : if (toLogged)
18876 32 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18877 : else
18878 44 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18879 76 : tab->chgPersistence = true;
18880 : }
18881 :
18882 : /*
18883 : * Execute ALTER TABLE SET SCHEMA
18884 : */
18885 : ObjectAddress
18886 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
18887 : {
18888 : Relation rel;
18889 : Oid relid;
18890 : Oid oldNspOid;
18891 : Oid nspOid;
18892 : RangeVar *newrv;
18893 : ObjectAddresses *objsMoved;
18894 : ObjectAddress myself;
18895 :
18896 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
18897 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
18898 : RangeVarCallbackForAlterRelation,
18899 : stmt);
18900 :
18901 102 : if (!OidIsValid(relid))
18902 : {
18903 12 : ereport(NOTICE,
18904 : (errmsg("relation \"%s\" does not exist, skipping",
18905 : stmt->relation->relname)));
18906 12 : return InvalidObjectAddress;
18907 : }
18908 :
18909 90 : rel = relation_open(relid, NoLock);
18910 :
18911 90 : oldNspOid = RelationGetNamespace(rel);
18912 :
18913 : /* If it's an owned sequence, disallow moving it by itself. */
18914 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18915 : {
18916 : Oid tableId;
18917 : int32 colId;
18918 :
18919 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18920 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18921 6 : ereport(ERROR,
18922 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18923 : errmsg("cannot move an owned sequence into another schema"),
18924 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
18925 : RelationGetRelationName(rel),
18926 : get_rel_name(tableId))));
18927 : }
18928 :
18929 : /* Get and lock schema OID and check its permissions. */
18930 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18931 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18932 :
18933 : /* common checks on switching namespaces */
18934 84 : CheckSetNamespace(oldNspOid, nspOid);
18935 :
18936 84 : objsMoved = new_object_addresses();
18937 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18938 84 : free_object_addresses(objsMoved);
18939 :
18940 84 : ObjectAddressSet(myself, RelationRelationId, relid);
18941 :
18942 84 : if (oldschema)
18943 84 : *oldschema = oldNspOid;
18944 :
18945 : /* close rel, but keep lock until commit */
18946 84 : relation_close(rel, NoLock);
18947 :
18948 84 : return myself;
18949 : }
18950 :
18951 : /*
18952 : * The guts of relocating a table or materialized view to another namespace:
18953 : * besides moving the relation itself, its dependent objects are relocated to
18954 : * the new schema.
18955 : */
18956 : void
18957 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
18958 : ObjectAddresses *objsMoved)
18959 : {
18960 : Relation classRel;
18961 :
18962 : Assert(objsMoved != NULL);
18963 :
18964 : /* OK, modify the pg_class row and pg_depend entry */
18965 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
18966 :
18967 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18968 : nspOid, true, objsMoved);
18969 :
18970 : /* Fix the table's row type too, if it has one */
18971 86 : if (OidIsValid(rel->rd_rel->reltype))
18972 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18973 : false, /* isImplicitArray */
18974 : false, /* ignoreDependent */
18975 : false, /* errorOnTableType */
18976 : objsMoved);
18977 :
18978 : /* Fix other dependent stuff */
18979 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18980 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18981 : objsMoved, AccessExclusiveLock);
18982 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18983 : false, objsMoved);
18984 :
18985 86 : table_close(classRel, RowExclusiveLock);
18986 86 : }
18987 :
18988 : /*
18989 : * The guts of relocating a relation to another namespace: fix the pg_class
18990 : * entry, and the pg_depend entry if any. Caller must already have
18991 : * opened and write-locked pg_class.
18992 : */
18993 : void
18994 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
18995 : Oid oldNspOid, Oid newNspOid,
18996 : bool hasDependEntry,
18997 : ObjectAddresses *objsMoved)
18998 : {
18999 : HeapTuple classTup;
19000 : Form_pg_class classForm;
19001 : ObjectAddress thisobj;
19002 188 : bool already_done = false;
19003 :
19004 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19005 188 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19006 188 : if (!HeapTupleIsValid(classTup))
19007 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
19008 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
19009 :
19010 : Assert(classForm->relnamespace == oldNspOid);
19011 :
19012 188 : thisobj.classId = RelationRelationId;
19013 188 : thisobj.objectId = relOid;
19014 188 : thisobj.objectSubId = 0;
19015 :
19016 : /*
19017 : * If the object has already been moved, don't move it again. If it's
19018 : * already in the right place, don't move it, but still fire the object
19019 : * access hook.
19020 : */
19021 188 : already_done = object_address_present(&thisobj, objsMoved);
19022 188 : if (!already_done && oldNspOid != newNspOid)
19023 146 : {
19024 146 : ItemPointerData otid = classTup->t_self;
19025 :
19026 : /* check for duplicate name (more friendly than unique-index failure) */
19027 146 : if (get_relname_relid(NameStr(classForm->relname),
19028 : newNspOid) != InvalidOid)
19029 0 : ereport(ERROR,
19030 : (errcode(ERRCODE_DUPLICATE_TABLE),
19031 : errmsg("relation \"%s\" already exists in schema \"%s\"",
19032 : NameStr(classForm->relname),
19033 : get_namespace_name(newNspOid))));
19034 :
19035 : /* classTup is a copy, so OK to scribble on */
19036 146 : classForm->relnamespace = newNspOid;
19037 :
19038 146 : CatalogTupleUpdate(classRel, &otid, classTup);
19039 146 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19040 :
19041 :
19042 : /* Update dependency on schema if caller said so */
19043 250 : if (hasDependEntry &&
19044 104 : changeDependencyFor(RelationRelationId,
19045 : relOid,
19046 : NamespaceRelationId,
19047 : oldNspOid,
19048 : newNspOid) != 1)
19049 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
19050 : NameStr(classForm->relname));
19051 : }
19052 : else
19053 42 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19054 188 : if (!already_done)
19055 : {
19056 188 : add_exact_object_address(&thisobj, objsMoved);
19057 :
19058 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19059 : }
19060 :
19061 188 : heap_freetuple(classTup);
19062 188 : }
19063 :
19064 : /*
19065 : * Move all indexes for the specified relation to another namespace.
19066 : *
19067 : * Note: we assume adequate permission checking was done by the caller,
19068 : * and that the caller has a suitable lock on the owning relation.
19069 : */
19070 : static void
19071 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
19072 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19073 : {
19074 : List *indexList;
19075 : ListCell *l;
19076 :
19077 86 : indexList = RelationGetIndexList(rel);
19078 :
19079 132 : foreach(l, indexList)
19080 : {
19081 46 : Oid indexOid = lfirst_oid(l);
19082 : ObjectAddress thisobj;
19083 :
19084 46 : thisobj.classId = RelationRelationId;
19085 46 : thisobj.objectId = indexOid;
19086 46 : thisobj.objectSubId = 0;
19087 :
19088 : /*
19089 : * Note: currently, the index will not have its own dependency on the
19090 : * namespace, so we don't need to do changeDependencyFor(). There's no
19091 : * row type in pg_type, either.
19092 : *
19093 : * XXX this objsMoved test may be pointless -- surely we have a single
19094 : * dependency link from a relation to each index?
19095 : */
19096 46 : if (!object_address_present(&thisobj, objsMoved))
19097 : {
19098 46 : AlterRelationNamespaceInternal(classRel, indexOid,
19099 : oldNspOid, newNspOid,
19100 : false, objsMoved);
19101 46 : add_exact_object_address(&thisobj, objsMoved);
19102 : }
19103 : }
19104 :
19105 86 : list_free(indexList);
19106 86 : }
19107 :
19108 : /*
19109 : * Move all identity and SERIAL-column sequences of the specified relation to another
19110 : * namespace.
19111 : *
19112 : * Note: we assume adequate permission checking was done by the caller,
19113 : * and that the caller has a suitable lock on the owning relation.
19114 : */
19115 : static void
19116 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
19117 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19118 : LOCKMODE lockmode)
19119 : {
19120 : Relation depRel;
19121 : SysScanDesc scan;
19122 : ScanKeyData key[2];
19123 : HeapTuple tup;
19124 :
19125 : /*
19126 : * SERIAL sequences are those having an auto dependency on one of the
19127 : * table's columns (we don't care *which* column, exactly).
19128 : */
19129 86 : depRel = table_open(DependRelationId, AccessShareLock);
19130 :
19131 86 : ScanKeyInit(&key[0],
19132 : Anum_pg_depend_refclassid,
19133 : BTEqualStrategyNumber, F_OIDEQ,
19134 : ObjectIdGetDatum(RelationRelationId));
19135 86 : ScanKeyInit(&key[1],
19136 : Anum_pg_depend_refobjid,
19137 : BTEqualStrategyNumber, F_OIDEQ,
19138 : ObjectIdGetDatum(RelationGetRelid(rel)));
19139 : /* we leave refobjsubid unspecified */
19140 :
19141 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19142 : NULL, 2, key);
19143 :
19144 616 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
19145 : {
19146 530 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19147 : Relation seqRel;
19148 :
19149 : /* skip dependencies other than auto dependencies on columns */
19150 530 : if (depForm->refobjsubid == 0 ||
19151 382 : depForm->classid != RelationRelationId ||
19152 42 : depForm->objsubid != 0 ||
19153 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19154 488 : continue;
19155 :
19156 : /* Use relation_open just in case it's an index */
19157 42 : seqRel = relation_open(depForm->objid, lockmode);
19158 :
19159 : /* skip non-sequence relations */
19160 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19161 : {
19162 : /* No need to keep the lock */
19163 0 : relation_close(seqRel, lockmode);
19164 0 : continue;
19165 : }
19166 :
19167 : /* Fix the pg_class and pg_depend entries */
19168 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
19169 : oldNspOid, newNspOid,
19170 : true, objsMoved);
19171 :
19172 : /*
19173 : * Sequences used to have entries in pg_type, but no longer do. If we
19174 : * ever re-instate that, we'll need to move the pg_type entry to the
19175 : * new namespace, too (using AlterTypeNamespaceInternal).
19176 : */
19177 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19178 :
19179 : /* Now we can close it. Keep the lock till end of transaction. */
19180 42 : relation_close(seqRel, NoLock);
19181 : }
19182 :
19183 86 : systable_endscan(scan);
19184 :
19185 86 : relation_close(depRel, AccessShareLock);
19186 86 : }
19187 :
19188 :
19189 : /*
19190 : * This code supports
19191 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19192 : *
19193 : * Because we only support this for TEMP tables, it's sufficient to remember
19194 : * the state in a backend-local data structure.
19195 : */
19196 :
19197 : /*
19198 : * Register a newly-created relation's ON COMMIT action.
19199 : */
19200 : void
19201 176 : register_on_commit_action(Oid relid, OnCommitAction action)
19202 : {
19203 : OnCommitItem *oc;
19204 : MemoryContext oldcxt;
19205 :
19206 : /*
19207 : * We needn't bother registering the relation unless there is an ON COMMIT
19208 : * action we need to take.
19209 : */
19210 176 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19211 24 : return;
19212 :
19213 152 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
19214 :
19215 152 : oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19216 152 : oc->relid = relid;
19217 152 : oc->oncommit = action;
19218 152 : oc->creating_subid = GetCurrentSubTransactionId();
19219 152 : oc->deleting_subid = InvalidSubTransactionId;
19220 :
19221 : /*
19222 : * We use lcons() here so that ON COMMIT actions are processed in reverse
19223 : * order of registration. That might not be essential but it seems
19224 : * reasonable.
19225 : */
19226 152 : on_commits = lcons(oc, on_commits);
19227 :
19228 152 : MemoryContextSwitchTo(oldcxt);
19229 : }
19230 :
19231 : /*
19232 : * Unregister any ON COMMIT action when a relation is deleted.
19233 : *
19234 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19235 : */
19236 : void
19237 47878 : remove_on_commit_action(Oid relid)
19238 : {
19239 : ListCell *l;
19240 :
19241 48024 : foreach(l, on_commits)
19242 : {
19243 286 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19244 :
19245 286 : if (oc->relid == relid)
19246 : {
19247 140 : oc->deleting_subid = GetCurrentSubTransactionId();
19248 140 : break;
19249 : }
19250 : }
19251 47878 : }
19252 :
19253 : /*
19254 : * Perform ON COMMIT actions.
19255 : *
19256 : * This is invoked just before actually committing, since it's possible
19257 : * to encounter errors.
19258 : */
19259 : void
19260 1056216 : PreCommit_on_commit_actions(void)
19261 : {
19262 : ListCell *l;
19263 1056216 : List *oids_to_truncate = NIL;
19264 1056216 : List *oids_to_drop = NIL;
19265 :
19266 1057038 : foreach(l, on_commits)
19267 : {
19268 822 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19269 :
19270 : /* Ignore entry if already dropped in this xact */
19271 822 : if (oc->deleting_subid != InvalidSubTransactionId)
19272 74 : continue;
19273 :
19274 748 : switch (oc->oncommit)
19275 : {
19276 0 : case ONCOMMIT_NOOP:
19277 : case ONCOMMIT_PRESERVE_ROWS:
19278 : /* Do nothing (there shouldn't be such entries, actually) */
19279 0 : break;
19280 694 : case ONCOMMIT_DELETE_ROWS:
19281 :
19282 : /*
19283 : * If this transaction hasn't accessed any temporary
19284 : * relations, we can skip truncating ON COMMIT DELETE ROWS
19285 : * tables, as they must still be empty.
19286 : */
19287 694 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
19288 448 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19289 694 : break;
19290 54 : case ONCOMMIT_DROP:
19291 54 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19292 54 : break;
19293 : }
19294 : }
19295 :
19296 : /*
19297 : * Truncate relations before dropping so that all dependencies between
19298 : * relations are removed after they are worked on. Doing it like this
19299 : * might be a waste as it is possible that a relation being truncated will
19300 : * be dropped anyway due to its parent being dropped, but this makes the
19301 : * code more robust because of not having to re-check that the relation
19302 : * exists at truncation time.
19303 : */
19304 1056216 : if (oids_to_truncate != NIL)
19305 382 : heap_truncate(oids_to_truncate);
19306 :
19307 1056210 : if (oids_to_drop != NIL)
19308 : {
19309 48 : ObjectAddresses *targetObjects = new_object_addresses();
19310 :
19311 102 : foreach(l, oids_to_drop)
19312 : {
19313 : ObjectAddress object;
19314 :
19315 54 : object.classId = RelationRelationId;
19316 54 : object.objectId = lfirst_oid(l);
19317 54 : object.objectSubId = 0;
19318 :
19319 : Assert(!object_address_present(&object, targetObjects));
19320 :
19321 54 : add_exact_object_address(&object, targetObjects);
19322 : }
19323 :
19324 : /*
19325 : * Object deletion might involve toast table access (to clean up
19326 : * toasted catalog entries), so ensure we have a valid snapshot.
19327 : */
19328 48 : PushActiveSnapshot(GetTransactionSnapshot());
19329 :
19330 : /*
19331 : * Since this is an automatic drop, rather than one directly initiated
19332 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19333 : */
19334 48 : performMultipleDeletions(targetObjects, DROP_CASCADE,
19335 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
19336 :
19337 48 : PopActiveSnapshot();
19338 :
19339 : #ifdef USE_ASSERT_CHECKING
19340 :
19341 : /*
19342 : * Note that table deletion will call remove_on_commit_action, so the
19343 : * entry should get marked as deleted.
19344 : */
19345 : foreach(l, on_commits)
19346 : {
19347 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19348 :
19349 : if (oc->oncommit != ONCOMMIT_DROP)
19350 : continue;
19351 :
19352 : Assert(oc->deleting_subid != InvalidSubTransactionId);
19353 : }
19354 : #endif
19355 : }
19356 1056210 : }
19357 :
19358 : /*
19359 : * Post-commit or post-abort cleanup for ON COMMIT management.
19360 : *
19361 : * All we do here is remove no-longer-needed OnCommitItem entries.
19362 : *
19363 : * During commit, remove entries that were deleted during this transaction;
19364 : * during abort, remove those created during this transaction.
19365 : */
19366 : void
19367 1104870 : AtEOXact_on_commit_actions(bool isCommit)
19368 : {
19369 : ListCell *cur_item;
19370 :
19371 1105722 : foreach(cur_item, on_commits)
19372 : {
19373 852 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19374 :
19375 954 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19376 102 : oc->creating_subid != InvalidSubTransactionId)
19377 : {
19378 : /* cur_item must be removed */
19379 152 : on_commits = foreach_delete_current(on_commits, cur_item);
19380 152 : pfree(oc);
19381 : }
19382 : else
19383 : {
19384 : /* cur_item must be preserved */
19385 700 : oc->creating_subid = InvalidSubTransactionId;
19386 700 : oc->deleting_subid = InvalidSubTransactionId;
19387 : }
19388 : }
19389 1104870 : }
19390 :
19391 : /*
19392 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19393 : *
19394 : * During subabort, we can immediately remove entries created during this
19395 : * subtransaction. During subcommit, just relabel entries marked during
19396 : * this subtransaction as being the parent's responsibility.
19397 : */
19398 : void
19399 20052 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
19400 : SubTransactionId parentSubid)
19401 : {
19402 : ListCell *cur_item;
19403 :
19404 20052 : foreach(cur_item, on_commits)
19405 : {
19406 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19407 :
19408 0 : if (!isCommit && oc->creating_subid == mySubid)
19409 : {
19410 : /* cur_item must be removed */
19411 0 : on_commits = foreach_delete_current(on_commits, cur_item);
19412 0 : pfree(oc);
19413 : }
19414 : else
19415 : {
19416 : /* cur_item must be preserved */
19417 0 : if (oc->creating_subid == mySubid)
19418 0 : oc->creating_subid = parentSubid;
19419 0 : if (oc->deleting_subid == mySubid)
19420 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19421 : }
19422 : }
19423 20052 : }
19424 :
19425 : /*
19426 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19427 : * the relation to be locked only if (1) it's a plain or partitioned table,
19428 : * materialized view, or TOAST table and (2) the current user is the owner (or
19429 : * the superuser) or has been granted MAINTAIN. This meets the
19430 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19431 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19432 : */
19433 : void
19434 1018 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
19435 : Oid relId, Oid oldRelId, void *arg)
19436 : {
19437 : char relkind;
19438 : AclResult aclresult;
19439 :
19440 : /* Nothing to do if the relation was not found. */
19441 1018 : if (!OidIsValid(relId))
19442 6 : return;
19443 :
19444 : /*
19445 : * If the relation does exist, check whether it's an index. But note that
19446 : * the relation might have been dropped between the time we did the name
19447 : * lookup and now. In that case, there's nothing to do.
19448 : */
19449 1012 : relkind = get_rel_relkind(relId);
19450 1012 : if (!relkind)
19451 0 : return;
19452 1012 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19453 138 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19454 28 : ereport(ERROR,
19455 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19456 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19457 :
19458 : /* Check permissions */
19459 984 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19460 984 : if (aclresult != ACLCHECK_OK)
19461 30 : aclcheck_error(aclresult,
19462 30 : get_relkind_objtype(get_rel_relkind(relId)),
19463 30 : relation->relname);
19464 : }
19465 :
19466 : /*
19467 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19468 : */
19469 : static void
19470 3588 : RangeVarCallbackForTruncate(const RangeVar *relation,
19471 : Oid relId, Oid oldRelId, void *arg)
19472 : {
19473 : HeapTuple tuple;
19474 :
19475 : /* Nothing to do if the relation was not found. */
19476 3588 : if (!OidIsValid(relId))
19477 0 : return;
19478 :
19479 3588 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19480 3588 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19481 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19482 :
19483 3588 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
19484 3582 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
19485 :
19486 3550 : ReleaseSysCache(tuple);
19487 : }
19488 :
19489 : /*
19490 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19491 : * the owner of the relation, or superuser.
19492 : */
19493 : void
19494 16070 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
19495 : Oid relId, Oid oldRelId, void *arg)
19496 : {
19497 : HeapTuple tuple;
19498 :
19499 : /* Nothing to do if the relation was not found. */
19500 16070 : if (!OidIsValid(relId))
19501 12 : return;
19502 :
19503 16058 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19504 16058 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19505 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19506 :
19507 16058 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19508 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
19509 6 : relation->relname);
19510 :
19511 31984 : if (!allowSystemTableMods &&
19512 15932 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19513 2 : ereport(ERROR,
19514 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19515 : errmsg("permission denied: \"%s\" is a system catalog",
19516 : relation->relname)));
19517 :
19518 16050 : ReleaseSysCache(tuple);
19519 : }
19520 :
19521 : /*
19522 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
19523 : * processing.
19524 : */
19525 : static void
19526 36242 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
19527 : void *arg)
19528 : {
19529 36242 : Node *stmt = (Node *) arg;
19530 : ObjectType reltype;
19531 : HeapTuple tuple;
19532 : Form_pg_class classform;
19533 : AclResult aclresult;
19534 : char relkind;
19535 :
19536 36242 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19537 36242 : if (!HeapTupleIsValid(tuple))
19538 222 : return; /* concurrently dropped */
19539 36020 : classform = (Form_pg_class) GETSTRUCT(tuple);
19540 36020 : relkind = classform->relkind;
19541 :
19542 : /* Must own relation. */
19543 36020 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19544 60 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
19545 :
19546 : /* No system table modifications unless explicitly allowed. */
19547 35960 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
19548 30 : ereport(ERROR,
19549 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19550 : errmsg("permission denied: \"%s\" is a system catalog",
19551 : rv->relname)));
19552 :
19553 : /*
19554 : * Extract the specified relation type from the statement parse tree.
19555 : *
19556 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
19557 : * have CREATE rights on the containing namespace.
19558 : */
19559 35930 : if (IsA(stmt, RenameStmt))
19560 : {
19561 496 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19562 : GetUserId(), ACL_CREATE);
19563 496 : if (aclresult != ACLCHECK_OK)
19564 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
19565 0 : get_namespace_name(classform->relnamespace));
19566 496 : reltype = ((RenameStmt *) stmt)->renameType;
19567 : }
19568 35434 : else if (IsA(stmt, AlterObjectSchemaStmt))
19569 92 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19570 :
19571 35342 : else if (IsA(stmt, AlterTableStmt))
19572 35342 : reltype = ((AlterTableStmt *) stmt)->objtype;
19573 : else
19574 : {
19575 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19576 : reltype = OBJECT_TABLE; /* placate compiler */
19577 : }
19578 :
19579 : /*
19580 : * For compatibility with prior releases, we allow ALTER TABLE to be used
19581 : * with most other types of relations (but not composite types). We allow
19582 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
19583 : * otherwise. Otherwise, the user must select the correct form of the
19584 : * command for the relation at issue.
19585 : */
19586 35930 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19587 0 : ereport(ERROR,
19588 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19589 : errmsg("\"%s\" is not a sequence", rv->relname)));
19590 :
19591 35930 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19592 0 : ereport(ERROR,
19593 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19594 : errmsg("\"%s\" is not a view", rv->relname)));
19595 :
19596 35930 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19597 0 : ereport(ERROR,
19598 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19599 : errmsg("\"%s\" is not a materialized view", rv->relname)));
19600 :
19601 35930 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19602 0 : ereport(ERROR,
19603 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19604 : errmsg("\"%s\" is not a foreign table", rv->relname)));
19605 :
19606 35930 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19607 0 : ereport(ERROR,
19608 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19609 : errmsg("\"%s\" is not a composite type", rv->relname)));
19610 :
19611 35930 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19612 : relkind != RELKIND_PARTITIONED_INDEX
19613 36 : && !IsA(stmt, RenameStmt))
19614 6 : ereport(ERROR,
19615 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19616 : errmsg("\"%s\" is not an index", rv->relname)));
19617 :
19618 : /*
19619 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19620 : * TYPE for that.
19621 : */
19622 35924 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19623 0 : ereport(ERROR,
19624 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19625 : errmsg("\"%s\" is a composite type", rv->relname),
19626 : /* translator: %s is an SQL ALTER command */
19627 : errhint("Use %s instead.",
19628 : "ALTER TYPE")));
19629 :
19630 : /*
19631 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19632 : * to a different schema, such as indexes and TOAST tables.
19633 : */
19634 35924 : if (IsA(stmt, AlterObjectSchemaStmt))
19635 : {
19636 92 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19637 0 : ereport(ERROR,
19638 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19639 : errmsg("cannot change schema of index \"%s\"",
19640 : rv->relname),
19641 : errhint("Change the schema of the table instead.")));
19642 92 : else if (relkind == RELKIND_COMPOSITE_TYPE)
19643 0 : ereport(ERROR,
19644 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19645 : errmsg("cannot change schema of composite type \"%s\"",
19646 : rv->relname),
19647 : /* translator: %s is an SQL ALTER command */
19648 : errhint("Use %s instead.",
19649 : "ALTER TYPE")));
19650 92 : else if (relkind == RELKIND_TOASTVALUE)
19651 0 : ereport(ERROR,
19652 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19653 : errmsg("cannot change schema of TOAST table \"%s\"",
19654 : rv->relname),
19655 : errhint("Change the schema of the table instead.")));
19656 : }
19657 :
19658 35924 : ReleaseSysCache(tuple);
19659 : }
19660 :
19661 : /*
19662 : * Transform any expressions present in the partition key
19663 : *
19664 : * Returns a transformed PartitionSpec.
19665 : */
19666 : static PartitionSpec *
19667 5098 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
19668 : {
19669 : PartitionSpec *newspec;
19670 : ParseState *pstate;
19671 : ParseNamespaceItem *nsitem;
19672 : ListCell *l;
19673 :
19674 5098 : newspec = makeNode(PartitionSpec);
19675 :
19676 5098 : newspec->strategy = partspec->strategy;
19677 5098 : newspec->partParams = NIL;
19678 5098 : newspec->location = partspec->location;
19679 :
19680 : /* Check valid number of columns for strategy */
19681 7656 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19682 2558 : list_length(partspec->partParams) != 1)
19683 6 : ereport(ERROR,
19684 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19685 : errmsg("cannot use \"list\" partition strategy with more than one column")));
19686 :
19687 : /*
19688 : * Create a dummy ParseState and insert the target relation as its sole
19689 : * rangetable entry. We need a ParseState for transformExpr.
19690 : */
19691 5092 : pstate = make_parsestate(NULL);
19692 5092 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19693 : NULL, false, true);
19694 5092 : addNSItemToQuery(pstate, nsitem, true, true, true);
19695 :
19696 : /* take care of any partition expressions */
19697 10620 : foreach(l, partspec->partParams)
19698 : {
19699 5552 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
19700 :
19701 5552 : if (pelem->expr)
19702 : {
19703 : /* Copy, to avoid scribbling on the input */
19704 320 : pelem = copyObject(pelem);
19705 :
19706 : /* Now do parse transformation of the expression */
19707 320 : pelem->expr = transformExpr(pstate, pelem->expr,
19708 : EXPR_KIND_PARTITION_EXPRESSION);
19709 :
19710 : /* we have to fix its collations too */
19711 296 : assign_expr_collations(pstate, pelem->expr);
19712 : }
19713 :
19714 5528 : newspec->partParams = lappend(newspec->partParams, pelem);
19715 : }
19716 :
19717 5068 : return newspec;
19718 : }
19719 :
19720 : /*
19721 : * Compute per-partition-column information from a list of PartitionElems.
19722 : * Expressions in the PartitionElems must be parse-analyzed already.
19723 : */
19724 : static void
19725 5068 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19726 : List **partexprs, Oid *partopclass, Oid *partcollation,
19727 : PartitionStrategy strategy)
19728 : {
19729 : int attn;
19730 : ListCell *lc;
19731 : Oid am_oid;
19732 :
19733 5068 : attn = 0;
19734 10500 : foreach(lc, partParams)
19735 : {
19736 5528 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
19737 : Oid atttype;
19738 : Oid attcollation;
19739 :
19740 5528 : if (pelem->name != NULL)
19741 : {
19742 : /* Simple attribute reference */
19743 : HeapTuple atttuple;
19744 : Form_pg_attribute attform;
19745 :
19746 5232 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
19747 5232 : pelem->name);
19748 5232 : if (!HeapTupleIsValid(atttuple))
19749 12 : ereport(ERROR,
19750 : (errcode(ERRCODE_UNDEFINED_COLUMN),
19751 : errmsg("column \"%s\" named in partition key does not exist",
19752 : pelem->name),
19753 : parser_errposition(pstate, pelem->location)));
19754 5220 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19755 :
19756 5220 : if (attform->attnum <= 0)
19757 6 : ereport(ERROR,
19758 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19759 : errmsg("cannot use system column \"%s\" in partition key",
19760 : pelem->name),
19761 : parser_errposition(pstate, pelem->location)));
19762 :
19763 : /*
19764 : * Stored generated columns cannot work: They are computed after
19765 : * BEFORE triggers, but partition routing is done before all
19766 : * triggers. Maybe virtual generated columns could be made to
19767 : * work, but then they would need to be handled as an expression
19768 : * below.
19769 : */
19770 5214 : if (attform->attgenerated)
19771 12 : ereport(ERROR,
19772 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19773 : errmsg("cannot use generated column in partition key"),
19774 : errdetail("Column \"%s\" is a generated column.",
19775 : pelem->name),
19776 : parser_errposition(pstate, pelem->location)));
19777 :
19778 5202 : partattrs[attn] = attform->attnum;
19779 5202 : atttype = attform->atttypid;
19780 5202 : attcollation = attform->attcollation;
19781 5202 : ReleaseSysCache(atttuple);
19782 : }
19783 : else
19784 : {
19785 : /* Expression */
19786 296 : Node *expr = pelem->expr;
19787 : char partattname[16];
19788 :
19789 : Assert(expr != NULL);
19790 296 : atttype = exprType(expr);
19791 296 : attcollation = exprCollation(expr);
19792 :
19793 : /*
19794 : * The expression must be of a storable type (e.g., not RECORD).
19795 : * The test is the same as for whether a table column is of a safe
19796 : * type (which is why we needn't check for the non-expression
19797 : * case).
19798 : */
19799 296 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19800 296 : CheckAttributeType(partattname,
19801 : atttype, attcollation,
19802 : NIL, CHKATYPE_IS_PARTKEY);
19803 :
19804 : /*
19805 : * Strip any top-level COLLATE clause. This ensures that we treat
19806 : * "x COLLATE y" and "(x COLLATE y)" alike.
19807 : */
19808 284 : while (IsA(expr, CollateExpr))
19809 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
19810 :
19811 284 : if (IsA(expr, Var) &&
19812 12 : ((Var *) expr)->varattno > 0)
19813 : {
19814 : /*
19815 : * User wrote "(column)" or "(column COLLATE something)".
19816 : * Treat it like simple attribute anyway.
19817 : */
19818 6 : partattrs[attn] = ((Var *) expr)->varattno;
19819 : }
19820 : else
19821 : {
19822 278 : Bitmapset *expr_attrs = NULL;
19823 : int i;
19824 :
19825 278 : partattrs[attn] = 0; /* marks the column as expression */
19826 278 : *partexprs = lappend(*partexprs, expr);
19827 :
19828 : /*
19829 : * transformPartitionSpec() should have already rejected
19830 : * subqueries, aggregates, window functions, and SRFs, based
19831 : * on the EXPR_KIND_ for partition expressions.
19832 : */
19833 :
19834 : /*
19835 : * Cannot allow system column references, since that would
19836 : * make partition routing impossible: their values won't be
19837 : * known yet when we need to do that.
19838 : */
19839 278 : pull_varattnos(expr, 1, &expr_attrs);
19840 2224 : for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
19841 : {
19842 1946 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
19843 : expr_attrs))
19844 0 : ereport(ERROR,
19845 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19846 : errmsg("partition key expressions cannot contain system column references")));
19847 : }
19848 :
19849 : /*
19850 : * Stored generated columns cannot work: They are computed
19851 : * after BEFORE triggers, but partition routing is done before
19852 : * all triggers. Virtual generated columns could probably
19853 : * work, but it would require more work elsewhere (for example
19854 : * SET EXPRESSION would need to check whether the column is
19855 : * used in partition keys). Seems safer to prohibit for now.
19856 : */
19857 278 : i = -1;
19858 612 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
19859 : {
19860 346 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
19861 :
19862 346 : if (attno > 0 &&
19863 340 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19864 12 : ereport(ERROR,
19865 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19866 : errmsg("cannot use generated column in partition key"),
19867 : errdetail("Column \"%s\" is a generated column.",
19868 : get_attname(RelationGetRelid(rel), attno, false)),
19869 : parser_errposition(pstate, pelem->location)));
19870 : }
19871 :
19872 : /*
19873 : * Preprocess the expression before checking for mutability.
19874 : * This is essential for the reasons described in
19875 : * contain_mutable_functions_after_planning. However, we call
19876 : * expression_planner for ourselves rather than using that
19877 : * function, because if constant-folding reduces the
19878 : * expression to a constant, we'd like to know that so we can
19879 : * complain below.
19880 : *
19881 : * Like contain_mutable_functions_after_planning, assume that
19882 : * expression_planner won't scribble on its input, so this
19883 : * won't affect the partexprs entry we saved above.
19884 : */
19885 266 : expr = (Node *) expression_planner((Expr *) expr);
19886 :
19887 : /*
19888 : * Partition expressions cannot contain mutable functions,
19889 : * because a given row must always map to the same partition
19890 : * as long as there is no change in the partition boundary
19891 : * structure.
19892 : */
19893 266 : if (contain_mutable_functions(expr))
19894 6 : ereport(ERROR,
19895 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19896 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
19897 :
19898 : /*
19899 : * While it is not exactly *wrong* for a partition expression
19900 : * to be a constant, it seems better to reject such keys.
19901 : */
19902 260 : if (IsA(expr, Const))
19903 12 : ereport(ERROR,
19904 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19905 : errmsg("cannot use constant expression as partition key")));
19906 : }
19907 : }
19908 :
19909 : /*
19910 : * Apply collation override if any
19911 : */
19912 5456 : if (pelem->collation)
19913 54 : attcollation = get_collation_oid(pelem->collation, false);
19914 :
19915 : /*
19916 : * Check we have a collation iff it's a collatable type. The only
19917 : * expected failures here are (1) COLLATE applied to a noncollatable
19918 : * type, or (2) partition expression had an unresolved collation. But
19919 : * we might as well code this to be a complete consistency check.
19920 : */
19921 5456 : if (type_is_collatable(atttype))
19922 : {
19923 640 : if (!OidIsValid(attcollation))
19924 0 : ereport(ERROR,
19925 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
19926 : errmsg("could not determine which collation to use for partition expression"),
19927 : errhint("Use the COLLATE clause to set the collation explicitly.")));
19928 : }
19929 : else
19930 : {
19931 4816 : if (OidIsValid(attcollation))
19932 0 : ereport(ERROR,
19933 : (errcode(ERRCODE_DATATYPE_MISMATCH),
19934 : errmsg("collations are not supported by type %s",
19935 : format_type_be(atttype))));
19936 : }
19937 :
19938 5456 : partcollation[attn] = attcollation;
19939 :
19940 : /*
19941 : * Identify the appropriate operator class. For list and range
19942 : * partitioning, we use a btree operator class; hash partitioning uses
19943 : * a hash operator class.
19944 : */
19945 5456 : if (strategy == PARTITION_STRATEGY_HASH)
19946 314 : am_oid = HASH_AM_OID;
19947 : else
19948 5142 : am_oid = BTREE_AM_OID;
19949 :
19950 5456 : if (!pelem->opclass)
19951 : {
19952 5324 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19953 :
19954 5324 : if (!OidIsValid(partopclass[attn]))
19955 : {
19956 12 : if (strategy == PARTITION_STRATEGY_HASH)
19957 0 : ereport(ERROR,
19958 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19959 : errmsg("data type %s has no default operator class for access method \"%s\"",
19960 : format_type_be(atttype), "hash"),
19961 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19962 : else
19963 12 : ereport(ERROR,
19964 : (errcode(ERRCODE_UNDEFINED_OBJECT),
19965 : errmsg("data type %s has no default operator class for access method \"%s\"",
19966 : format_type_be(atttype), "btree"),
19967 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19968 : }
19969 : }
19970 : else
19971 132 : partopclass[attn] = ResolveOpClass(pelem->opclass,
19972 : atttype,
19973 : am_oid == HASH_AM_OID ? "hash" : "btree",
19974 : am_oid);
19975 :
19976 5432 : attn++;
19977 : }
19978 4972 : }
19979 :
19980 : /*
19981 : * PartConstraintImpliedByRelConstraint
19982 : * Do scanrel's existing constraints imply the partition constraint?
19983 : *
19984 : * "Existing constraints" include its check constraints and column-level
19985 : * not-null constraints. partConstraint describes the partition constraint,
19986 : * in implicit-AND form.
19987 : */
19988 : bool
19989 3602 : PartConstraintImpliedByRelConstraint(Relation scanrel,
19990 : List *partConstraint)
19991 : {
19992 3602 : List *existConstraint = NIL;
19993 3602 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19994 : int i;
19995 :
19996 3602 : if (constr && constr->has_not_null)
19997 : {
19998 956 : int natts = scanrel->rd_att->natts;
19999 :
20000 3038 : for (i = 1; i <= natts; i++)
20001 : {
20002 2082 : CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20003 :
20004 : /* invalid not-null constraint must be ignored here */
20005 2082 : if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20006 : {
20007 1274 : Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20008 1274 : NullTest *ntest = makeNode(NullTest);
20009 :
20010 1274 : ntest->arg = (Expr *) makeVar(1,
20011 : i,
20012 : wholeatt->atttypid,
20013 : wholeatt->atttypmod,
20014 : wholeatt->attcollation,
20015 : 0);
20016 1274 : ntest->nulltesttype = IS_NOT_NULL;
20017 :
20018 : /*
20019 : * argisrow=false is correct even for a composite column,
20020 : * because attnotnull does not represent a SQL-spec IS NOT
20021 : * NULL test in such a case, just IS DISTINCT FROM NULL.
20022 : */
20023 1274 : ntest->argisrow = false;
20024 1274 : ntest->location = -1;
20025 1274 : existConstraint = lappend(existConstraint, ntest);
20026 : }
20027 : }
20028 : }
20029 :
20030 3602 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20031 : }
20032 :
20033 : /*
20034 : * ConstraintImpliedByRelConstraint
20035 : * Do scanrel's existing constraints imply the given constraint?
20036 : *
20037 : * testConstraint is the constraint to validate. provenConstraint is a
20038 : * caller-provided list of conditions which this function may assume
20039 : * to be true. Both provenConstraint and testConstraint must be in
20040 : * implicit-AND form, must only contain immutable clauses, and must
20041 : * contain only Vars with varno = 1.
20042 : */
20043 : bool
20044 4840 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20045 : {
20046 4840 : List *existConstraint = list_copy(provenConstraint);
20047 4840 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20048 : int num_check,
20049 : i;
20050 :
20051 4840 : num_check = (constr != NULL) ? constr->num_check : 0;
20052 5360 : for (i = 0; i < num_check; i++)
20053 : {
20054 : Node *cexpr;
20055 :
20056 : /*
20057 : * If this constraint hasn't been fully validated yet, we must ignore
20058 : * it here.
20059 : */
20060 520 : if (!constr->check[i].ccvalid)
20061 6 : continue;
20062 :
20063 : /*
20064 : * NOT ENFORCED constraints are always marked as invalid, which should
20065 : * have been ignored.
20066 : */
20067 : Assert(constr->check[i].ccenforced);
20068 :
20069 514 : cexpr = stringToNode(constr->check[i].ccbin);
20070 :
20071 : /*
20072 : * Run each expression through const-simplification and
20073 : * canonicalization. It is necessary, because we will be comparing it
20074 : * to similarly-processed partition constraint expressions, and may
20075 : * fail to detect valid matches without this.
20076 : */
20077 514 : cexpr = eval_const_expressions(NULL, cexpr);
20078 514 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20079 :
20080 514 : existConstraint = list_concat(existConstraint,
20081 514 : make_ands_implicit((Expr *) cexpr));
20082 : }
20083 :
20084 : /*
20085 : * Try to make the proof. Since we are comparing CHECK constraints, we
20086 : * need to use weak implication, i.e., we assume existConstraint is
20087 : * not-false and try to prove the same for testConstraint.
20088 : *
20089 : * Note that predicate_implied_by assumes its first argument is known
20090 : * immutable. That should always be true for both NOT NULL and partition
20091 : * constraints, so we don't test it here.
20092 : */
20093 4840 : return predicate_implied_by(testConstraint, existConstraint, true);
20094 : }
20095 :
20096 : /*
20097 : * QueuePartitionConstraintValidation
20098 : *
20099 : * Add an entry to wqueue to have the given partition constraint validated by
20100 : * Phase 3, for the given relation, and all its children.
20101 : *
20102 : * We first verify whether the given constraint is implied by pre-existing
20103 : * relation constraints; if it is, there's no need to scan the table to
20104 : * validate, so don't queue in that case.
20105 : */
20106 : static void
20107 2972 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
20108 : List *partConstraint,
20109 : bool validate_default)
20110 : {
20111 : /*
20112 : * Based on the table's existing constraints, determine whether or not we
20113 : * may skip scanning the table.
20114 : */
20115 2972 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20116 : {
20117 92 : if (!validate_default)
20118 70 : ereport(DEBUG1,
20119 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20120 : RelationGetRelationName(scanrel))));
20121 : else
20122 22 : ereport(DEBUG1,
20123 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20124 : RelationGetRelationName(scanrel))));
20125 92 : return;
20126 : }
20127 :
20128 : /*
20129 : * Constraints proved insufficient. For plain relations, queue a
20130 : * validation item now; for partitioned tables, recurse to process each
20131 : * partition.
20132 : */
20133 2880 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20134 : {
20135 : AlteredTableInfo *tab;
20136 :
20137 : /* Grab a work queue entry. */
20138 2420 : tab = ATGetQueueEntry(wqueue, scanrel);
20139 : Assert(tab->partition_constraint == NULL);
20140 2420 : tab->partition_constraint = (Expr *) linitial(partConstraint);
20141 2420 : tab->validate_default = validate_default;
20142 : }
20143 460 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20144 : {
20145 412 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20146 : int i;
20147 :
20148 808 : for (i = 0; i < partdesc->nparts; i++)
20149 : {
20150 : Relation part_rel;
20151 : List *thisPartConstraint;
20152 :
20153 : /*
20154 : * This is the minimum lock we need to prevent deadlocks.
20155 : */
20156 396 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20157 :
20158 : /*
20159 : * Adjust the constraint for scanrel so that it matches this
20160 : * partition's attribute numbers.
20161 : */
20162 : thisPartConstraint =
20163 396 : map_partition_varattnos(partConstraint, 1,
20164 : part_rel, scanrel);
20165 :
20166 396 : QueuePartitionConstraintValidation(wqueue, part_rel,
20167 : thisPartConstraint,
20168 : validate_default);
20169 396 : table_close(part_rel, NoLock); /* keep lock till commit */
20170 : }
20171 : }
20172 : }
20173 :
20174 : /*
20175 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20176 : *
20177 : * Return the address of the newly attached partition.
20178 : */
20179 : static ObjectAddress
20180 2796 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
20181 : AlterTableUtilityContext *context)
20182 : {
20183 : Relation attachrel,
20184 : catalog;
20185 : List *attachrel_children;
20186 : List *partConstraint;
20187 : SysScanDesc scan;
20188 : ScanKeyData skey;
20189 : AttrNumber attno;
20190 : int natts;
20191 : TupleDesc tupleDesc;
20192 : ObjectAddress address;
20193 : const char *trigger_name;
20194 : Oid defaultPartOid;
20195 : List *partBoundConstraint;
20196 2796 : ParseState *pstate = make_parsestate(NULL);
20197 :
20198 2796 : pstate->p_sourcetext = context->queryString;
20199 :
20200 : /*
20201 : * We must lock the default partition if one exists, because attaching a
20202 : * new partition will change its partition constraint.
20203 : */
20204 : defaultPartOid =
20205 2796 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20206 2796 : if (OidIsValid(defaultPartOid))
20207 184 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20208 :
20209 2796 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20210 :
20211 : /*
20212 : * XXX I think it'd be a good idea to grab locks on all tables referenced
20213 : * by FKs at this point also.
20214 : */
20215 :
20216 : /*
20217 : * Must be owner of both parent and source table -- parent was checked by
20218 : * ATSimplePermissions call in ATPrepCmd
20219 : */
20220 2790 : ATSimplePermissions(AT_AttachPartition, attachrel,
20221 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
20222 :
20223 : /* A partition can only have one parent */
20224 2784 : if (attachrel->rd_rel->relispartition)
20225 6 : ereport(ERROR,
20226 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20227 : errmsg("\"%s\" is already a partition",
20228 : RelationGetRelationName(attachrel))));
20229 :
20230 2778 : if (OidIsValid(attachrel->rd_rel->reloftype))
20231 6 : ereport(ERROR,
20232 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20233 : errmsg("cannot attach a typed table as partition")));
20234 :
20235 : /*
20236 : * Table being attached should not already be part of inheritance; either
20237 : * as a child table...
20238 : */
20239 2772 : catalog = table_open(InheritsRelationId, AccessShareLock);
20240 2772 : ScanKeyInit(&skey,
20241 : Anum_pg_inherits_inhrelid,
20242 : BTEqualStrategyNumber, F_OIDEQ,
20243 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20244 2772 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20245 : NULL, 1, &skey);
20246 2772 : if (HeapTupleIsValid(systable_getnext(scan)))
20247 6 : ereport(ERROR,
20248 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20249 : errmsg("cannot attach inheritance child as partition")));
20250 2766 : systable_endscan(scan);
20251 :
20252 : /* ...or as a parent table (except the case when it is partitioned) */
20253 2766 : ScanKeyInit(&skey,
20254 : Anum_pg_inherits_inhparent,
20255 : BTEqualStrategyNumber, F_OIDEQ,
20256 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20257 2766 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20258 : 1, &skey);
20259 2766 : if (HeapTupleIsValid(systable_getnext(scan)) &&
20260 264 : attachrel->rd_rel->relkind == RELKIND_RELATION)
20261 6 : ereport(ERROR,
20262 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20263 : errmsg("cannot attach inheritance parent as partition")));
20264 2760 : systable_endscan(scan);
20265 2760 : table_close(catalog, AccessShareLock);
20266 :
20267 : /*
20268 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
20269 : * particular, this disallows making a rel a partition of itself.)
20270 : *
20271 : * We do that by checking if rel is a member of the list of attachrel's
20272 : * partitions provided the latter is partitioned at all. We want to avoid
20273 : * having to construct this list again, so we request the strongest lock
20274 : * on all partitions. We need the strongest lock, because we may decide
20275 : * to scan them if we find out that the table being attached (or its leaf
20276 : * partitions) may contain rows that violate the partition constraint. If
20277 : * the table has a constraint that would prevent such rows, which by
20278 : * definition is present in all the partitions, we need not scan the
20279 : * table, nor its partitions. But we cannot risk a deadlock by taking a
20280 : * weaker lock now and the stronger one only when needed.
20281 : */
20282 2760 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20283 : AccessExclusiveLock, NULL);
20284 2760 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20285 12 : ereport(ERROR,
20286 : (errcode(ERRCODE_DUPLICATE_TABLE),
20287 : errmsg("circular inheritance not allowed"),
20288 : errdetail("\"%s\" is already a child of \"%s\".",
20289 : RelationGetRelationName(rel),
20290 : RelationGetRelationName(attachrel))));
20291 :
20292 : /* If the parent is permanent, so must be all of its partitions. */
20293 2748 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20294 2706 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20295 6 : ereport(ERROR,
20296 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20297 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20298 : RelationGetRelationName(rel))));
20299 :
20300 : /* Temp parent cannot have a partition that is itself not a temp */
20301 2742 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20302 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20303 18 : ereport(ERROR,
20304 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20305 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20306 : RelationGetRelationName(rel))));
20307 :
20308 : /* If the parent is temp, it must belong to this session */
20309 2724 : if (RELATION_IS_OTHER_TEMP(rel))
20310 0 : ereport(ERROR,
20311 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20312 : errmsg("cannot attach as partition of temporary relation of another session")));
20313 :
20314 : /* Ditto for the partition */
20315 2724 : if (RELATION_IS_OTHER_TEMP(attachrel))
20316 0 : ereport(ERROR,
20317 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20318 : errmsg("cannot attach temporary relation of another session as partition")));
20319 :
20320 : /*
20321 : * Check if attachrel has any identity columns or any columns that aren't
20322 : * in the parent.
20323 : */
20324 2724 : tupleDesc = RelationGetDescr(attachrel);
20325 2724 : natts = tupleDesc->natts;
20326 9266 : for (attno = 1; attno <= natts; attno++)
20327 : {
20328 6584 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20329 6584 : char *attributeName = NameStr(attribute->attname);
20330 :
20331 : /* Ignore dropped */
20332 6584 : if (attribute->attisdropped)
20333 580 : continue;
20334 :
20335 6004 : if (attribute->attidentity)
20336 24 : ereport(ERROR,
20337 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20338 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20339 : RelationGetRelationName(attachrel), attributeName),
20340 : errdetail("The new partition may not contain an identity column."));
20341 :
20342 : /* Try to find the column in parent (matching on column name) */
20343 5980 : if (!SearchSysCacheExists2(ATTNAME,
20344 : ObjectIdGetDatum(RelationGetRelid(rel)),
20345 : CStringGetDatum(attributeName)))
20346 18 : ereport(ERROR,
20347 : (errcode(ERRCODE_DATATYPE_MISMATCH),
20348 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20349 : RelationGetRelationName(attachrel), attributeName,
20350 : RelationGetRelationName(rel)),
20351 : errdetail("The new partition may contain only the columns present in parent.")));
20352 : }
20353 :
20354 : /*
20355 : * If child_rel has row-level triggers with transition tables, we
20356 : * currently don't allow it to become a partition. See also prohibitions
20357 : * in ATExecAddInherit() and CreateTrigger().
20358 : */
20359 2682 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20360 2682 : if (trigger_name != NULL)
20361 6 : ereport(ERROR,
20362 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20363 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20364 : trigger_name, RelationGetRelationName(attachrel)),
20365 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
20366 :
20367 : /*
20368 : * Check that the new partition's bound is valid and does not overlap any
20369 : * of existing partitions of the parent - note that it does not return on
20370 : * error.
20371 : */
20372 2676 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
20373 : cmd->bound, pstate);
20374 :
20375 : /* OK to create inheritance. Rest of the checks performed there */
20376 2640 : CreateInheritance(attachrel, rel, true);
20377 :
20378 : /* Update the pg_class entry. */
20379 2532 : StorePartitionBound(attachrel, rel, cmd->bound);
20380 :
20381 : /* Ensure there exists a correct set of indexes in the partition. */
20382 2532 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20383 :
20384 : /* and triggers */
20385 2502 : CloneRowTriggersToPartition(rel, attachrel);
20386 :
20387 : /*
20388 : * Clone foreign key constraints. Callee is responsible for setting up
20389 : * for phase 3 constraint verification.
20390 : */
20391 2496 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
20392 :
20393 : /*
20394 : * Generate partition constraint from the partition bound specification.
20395 : * If the parent itself is a partition, make sure to include its
20396 : * constraint as well.
20397 : */
20398 2478 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20399 :
20400 : /*
20401 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20402 : * since it's needed later to construct the constraint expression for
20403 : * validating against the default partition, if any.
20404 : */
20405 2478 : partConstraint = list_concat_copy(partBoundConstraint,
20406 2478 : RelationGetPartitionQual(rel));
20407 :
20408 : /* Skip validation if there are no constraints to validate. */
20409 2478 : if (partConstraint)
20410 : {
20411 : /*
20412 : * Run the partition quals through const-simplification similar to
20413 : * check constraints. We skip canonicalize_qual, though, because
20414 : * partition quals should be in canonical form already.
20415 : */
20416 : partConstraint =
20417 2428 : (List *) eval_const_expressions(NULL,
20418 : (Node *) partConstraint);
20419 :
20420 : /* XXX this sure looks wrong */
20421 2428 : partConstraint = list_make1(make_ands_explicit(partConstraint));
20422 :
20423 : /*
20424 : * Adjust the generated constraint to match this partition's attribute
20425 : * numbers.
20426 : */
20427 2428 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20428 : rel);
20429 :
20430 : /* Validate partition constraints against the table being attached. */
20431 2428 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20432 : false);
20433 : }
20434 :
20435 : /*
20436 : * If we're attaching a partition other than the default partition and a
20437 : * default one exists, then that partition's partition constraint changes,
20438 : * so add an entry to the work queue to validate it, too. (We must not do
20439 : * this when the partition being attached is the default one; we already
20440 : * did it above!)
20441 : */
20442 2478 : if (OidIsValid(defaultPartOid))
20443 : {
20444 : Relation defaultrel;
20445 : List *defPartConstraint;
20446 :
20447 : Assert(!cmd->bound->is_default);
20448 :
20449 : /* we already hold a lock on the default partition */
20450 148 : defaultrel = table_open(defaultPartOid, NoLock);
20451 : defPartConstraint =
20452 148 : get_proposed_default_constraint(partBoundConstraint);
20453 :
20454 : /*
20455 : * Map the Vars in the constraint expression from rel's attnos to
20456 : * defaultrel's.
20457 : */
20458 : defPartConstraint =
20459 148 : map_partition_varattnos(defPartConstraint,
20460 : 1, defaultrel, rel);
20461 148 : QueuePartitionConstraintValidation(wqueue, defaultrel,
20462 : defPartConstraint, true);
20463 :
20464 : /* keep our lock until commit. */
20465 148 : table_close(defaultrel, NoLock);
20466 : }
20467 :
20468 2478 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20469 :
20470 : /*
20471 : * If the partition we just attached is partitioned itself, invalidate
20472 : * relcache for all descendent partitions too to ensure that their
20473 : * rd_partcheck expression trees are rebuilt; partitions already locked at
20474 : * the beginning of this function.
20475 : */
20476 2478 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20477 : {
20478 : ListCell *l;
20479 :
20480 1124 : foreach(l, attachrel_children)
20481 : {
20482 738 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
20483 : }
20484 : }
20485 :
20486 : /* keep our lock until commit */
20487 2478 : table_close(attachrel, NoLock);
20488 :
20489 2478 : return address;
20490 : }
20491 :
20492 : /*
20493 : * AttachPartitionEnsureIndexes
20494 : * subroutine for ATExecAttachPartition to create/match indexes
20495 : *
20496 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20497 : * PARTITION: every partition must have an index attached to each index on the
20498 : * partitioned table.
20499 : */
20500 : static void
20501 2532 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
20502 : {
20503 : List *idxes;
20504 : List *attachRelIdxs;
20505 : Relation *attachrelIdxRels;
20506 : IndexInfo **attachInfos;
20507 : ListCell *cell;
20508 : MemoryContext cxt;
20509 : MemoryContext oldcxt;
20510 :
20511 2532 : cxt = AllocSetContextCreate(CurrentMemoryContext,
20512 : "AttachPartitionEnsureIndexes",
20513 : ALLOCSET_DEFAULT_SIZES);
20514 2532 : oldcxt = MemoryContextSwitchTo(cxt);
20515 :
20516 2532 : idxes = RelationGetIndexList(rel);
20517 2532 : attachRelIdxs = RelationGetIndexList(attachrel);
20518 2532 : attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20519 2532 : attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20520 :
20521 : /* Build arrays of all existing indexes and their IndexInfos */
20522 5446 : foreach_oid(cldIdxId, attachRelIdxs)
20523 : {
20524 382 : int i = foreach_current_index(cldIdxId);
20525 :
20526 382 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20527 382 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20528 : }
20529 :
20530 : /*
20531 : * If we're attaching a foreign table, we must fail if any of the indexes
20532 : * is a constraint index; otherwise, there's nothing to do here. Do this
20533 : * before starting work, to avoid wasting the effort of building a few
20534 : * non-unique indexes before coming across a unique one.
20535 : */
20536 2532 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20537 : {
20538 86 : foreach(cell, idxes)
20539 : {
20540 36 : Oid idx = lfirst_oid(cell);
20541 36 : Relation idxRel = index_open(idx, AccessShareLock);
20542 :
20543 36 : if (idxRel->rd_index->indisunique ||
20544 24 : idxRel->rd_index->indisprimary)
20545 12 : ereport(ERROR,
20546 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20547 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20548 : RelationGetRelationName(attachrel),
20549 : RelationGetRelationName(rel)),
20550 : errdetail("Partitioned table \"%s\" contains unique indexes.",
20551 : RelationGetRelationName(rel))));
20552 24 : index_close(idxRel, AccessShareLock);
20553 : }
20554 :
20555 50 : goto out;
20556 : }
20557 :
20558 : /*
20559 : * For each index on the partitioned table, find a matching one in the
20560 : * partition-to-be; if one is not found, create one.
20561 : */
20562 2902 : foreach(cell, idxes)
20563 : {
20564 450 : Oid idx = lfirst_oid(cell);
20565 450 : Relation idxRel = index_open(idx, AccessShareLock);
20566 : IndexInfo *info;
20567 : AttrMap *attmap;
20568 450 : bool found = false;
20569 : Oid constraintOid;
20570 :
20571 : /*
20572 : * Ignore indexes in the partitioned table other than partitioned
20573 : * indexes.
20574 : */
20575 450 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20576 : {
20577 0 : index_close(idxRel, AccessShareLock);
20578 0 : continue;
20579 : }
20580 :
20581 : /* construct an indexinfo to compare existing indexes against */
20582 450 : info = BuildIndexInfo(idxRel);
20583 450 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20584 : RelationGetDescr(rel),
20585 : false);
20586 450 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
20587 :
20588 : /*
20589 : * Scan the list of existing indexes in the partition-to-be, and mark
20590 : * the first matching, valid, unattached one we find, if any, as
20591 : * partition of the parent index. If we find one, we're done.
20592 : */
20593 510 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20594 : {
20595 274 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20596 274 : Oid cldConstrOid = InvalidOid;
20597 :
20598 : /* does this index have a parent? if so, can't use it */
20599 274 : if (attachrelIdxRels[i]->rd_rel->relispartition)
20600 12 : continue;
20601 :
20602 : /* If this index is invalid, can't use it */
20603 262 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
20604 6 : continue;
20605 :
20606 256 : if (CompareIndexInfo(attachInfos[i], info,
20607 256 : attachrelIdxRels[i]->rd_indcollation,
20608 256 : idxRel->rd_indcollation,
20609 256 : attachrelIdxRels[i]->rd_opfamily,
20610 256 : idxRel->rd_opfamily,
20611 : attmap))
20612 : {
20613 : /*
20614 : * If this index is being created in the parent because of a
20615 : * constraint, then the child needs to have a constraint also,
20616 : * so look for one. If there is no such constraint, this
20617 : * index is no good, so keep looking.
20618 : */
20619 220 : if (OidIsValid(constraintOid))
20620 : {
20621 : cldConstrOid =
20622 122 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
20623 : cldIdxId);
20624 : /* no dice */
20625 122 : if (!OidIsValid(cldConstrOid))
20626 6 : continue;
20627 :
20628 : /* Ensure they're both the same type of constraint */
20629 232 : if (get_constraint_type(constraintOid) !=
20630 116 : get_constraint_type(cldConstrOid))
20631 0 : continue;
20632 : }
20633 :
20634 : /* bingo. */
20635 214 : IndexSetParentIndex(attachrelIdxRels[i], idx);
20636 214 : if (OidIsValid(constraintOid))
20637 116 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20638 : RelationGetRelid(attachrel));
20639 214 : found = true;
20640 :
20641 214 : CommandCounterIncrement();
20642 214 : break;
20643 : }
20644 : }
20645 :
20646 : /*
20647 : * If no suitable index was found in the partition-to-be, create one
20648 : * now. Note that if this is a PK, not-null constraints must already
20649 : * exist.
20650 : */
20651 450 : if (!found)
20652 : {
20653 : IndexStmt *stmt;
20654 : Oid conOid;
20655 :
20656 236 : stmt = generateClonedIndexStmt(NULL,
20657 : idxRel, attmap,
20658 : &conOid);
20659 236 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
20660 : RelationGetRelid(idxRel),
20661 : conOid,
20662 : -1,
20663 : true, false, false, false, false);
20664 : }
20665 :
20666 432 : index_close(idxRel, AccessShareLock);
20667 : }
20668 :
20669 2502 : out:
20670 : /* Clean up. */
20671 2872 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20672 370 : index_close(attachrelIdxRels[i], AccessShareLock);
20673 2502 : MemoryContextSwitchTo(oldcxt);
20674 2502 : MemoryContextDelete(cxt);
20675 2502 : }
20676 :
20677 : /*
20678 : * CloneRowTriggersToPartition
20679 : * subroutine for ATExecAttachPartition/DefineRelation to create row
20680 : * triggers on partitions
20681 : */
20682 : static void
20683 2934 : CloneRowTriggersToPartition(Relation parent, Relation partition)
20684 : {
20685 : Relation pg_trigger;
20686 : ScanKeyData key;
20687 : SysScanDesc scan;
20688 : HeapTuple tuple;
20689 : MemoryContext perTupCxt;
20690 :
20691 2934 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20692 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20693 2934 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20694 2934 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20695 : true, NULL, 1, &key);
20696 :
20697 2934 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
20698 : "clone trig", ALLOCSET_SMALL_SIZES);
20699 :
20700 4754 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20701 : {
20702 1826 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20703 : CreateTrigStmt *trigStmt;
20704 1826 : Node *qual = NULL;
20705 : Datum value;
20706 : bool isnull;
20707 1826 : List *cols = NIL;
20708 1826 : List *trigargs = NIL;
20709 : MemoryContext oldcxt;
20710 :
20711 : /*
20712 : * Ignore statement-level triggers; those are not cloned.
20713 : */
20714 1826 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20715 1670 : continue;
20716 :
20717 : /*
20718 : * Don't clone internal triggers, because the constraint cloning code
20719 : * will.
20720 : */
20721 1826 : if (trigForm->tgisinternal)
20722 1670 : continue;
20723 :
20724 : /*
20725 : * Complain if we find an unexpected trigger type.
20726 : */
20727 156 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20728 138 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
20729 0 : elog(ERROR, "unexpected trigger \"%s\" found",
20730 : NameStr(trigForm->tgname));
20731 :
20732 : /* Use short-lived context for CREATE TRIGGER */
20733 156 : oldcxt = MemoryContextSwitchTo(perTupCxt);
20734 :
20735 : /*
20736 : * If there is a WHEN clause, generate a 'cooked' version of it that's
20737 : * appropriate for the partition.
20738 : */
20739 156 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20740 : RelationGetDescr(pg_trigger), &isnull);
20741 156 : if (!isnull)
20742 : {
20743 6 : qual = stringToNode(TextDatumGetCString(value));
20744 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20745 : partition, parent);
20746 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20747 : partition, parent);
20748 : }
20749 :
20750 : /*
20751 : * If there is a column list, transform it to a list of column names.
20752 : * Note we don't need to map this list in any way ...
20753 : */
20754 156 : if (trigForm->tgattr.dim1 > 0)
20755 : {
20756 : int i;
20757 :
20758 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
20759 : {
20760 : Form_pg_attribute col;
20761 :
20762 6 : col = TupleDescAttr(parent->rd_att,
20763 6 : trigForm->tgattr.values[i] - 1);
20764 6 : cols = lappend(cols,
20765 6 : makeString(pstrdup(NameStr(col->attname))));
20766 : }
20767 : }
20768 :
20769 : /* Reconstruct trigger arguments list. */
20770 156 : if (trigForm->tgnargs > 0)
20771 : {
20772 : char *p;
20773 :
20774 12 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20775 : RelationGetDescr(pg_trigger), &isnull);
20776 12 : if (isnull)
20777 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20778 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
20779 :
20780 12 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20781 :
20782 36 : for (int i = 0; i < trigForm->tgnargs; i++)
20783 : {
20784 24 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
20785 24 : p += strlen(p) + 1;
20786 : }
20787 : }
20788 :
20789 156 : trigStmt = makeNode(CreateTrigStmt);
20790 156 : trigStmt->replace = false;
20791 156 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20792 156 : trigStmt->trigname = NameStr(trigForm->tgname);
20793 156 : trigStmt->relation = NULL;
20794 156 : trigStmt->funcname = NULL; /* passed separately */
20795 156 : trigStmt->args = trigargs;
20796 156 : trigStmt->row = true;
20797 156 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20798 156 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20799 156 : trigStmt->columns = cols;
20800 156 : trigStmt->whenClause = NULL; /* passed separately */
20801 156 : trigStmt->transitionRels = NIL; /* not supported at present */
20802 156 : trigStmt->deferrable = trigForm->tgdeferrable;
20803 156 : trigStmt->initdeferred = trigForm->tginitdeferred;
20804 156 : trigStmt->constrrel = NULL; /* passed separately */
20805 :
20806 156 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20807 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20808 : trigForm->tgfoid, trigForm->oid, qual,
20809 156 : false, true, trigForm->tgenabled);
20810 :
20811 150 : MemoryContextSwitchTo(oldcxt);
20812 150 : MemoryContextReset(perTupCxt);
20813 : }
20814 :
20815 2928 : MemoryContextDelete(perTupCxt);
20816 :
20817 2928 : systable_endscan(scan);
20818 2928 : table_close(pg_trigger, RowExclusiveLock);
20819 2928 : }
20820 :
20821 : /*
20822 : * ALTER TABLE DETACH PARTITION
20823 : *
20824 : * Return the address of the relation that is no longer a partition of rel.
20825 : *
20826 : * If concurrent mode is requested, we run in two transactions. A side-
20827 : * effect is that this command cannot run in a multi-part ALTER TABLE.
20828 : * Currently, that's enforced by the grammar.
20829 : *
20830 : * The strategy for concurrency is to first modify the partition's
20831 : * pg_inherit catalog row to make it visible to everyone that the
20832 : * partition is detached, lock the partition against writes, and commit
20833 : * the transaction; anyone who requests the partition descriptor from
20834 : * that point onwards has to ignore such a partition. In a second
20835 : * transaction, we wait until all transactions that could have seen the
20836 : * partition as attached are gone, then we remove the rest of partition
20837 : * metadata (pg_inherits and pg_class.relpartbounds).
20838 : */
20839 : static ObjectAddress
20840 570 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
20841 : RangeVar *name, bool concurrent)
20842 : {
20843 : Relation partRel;
20844 : ObjectAddress address;
20845 : Oid defaultPartOid;
20846 :
20847 : /*
20848 : * We must lock the default partition, because detaching this partition
20849 : * will change its partition constraint.
20850 : */
20851 : defaultPartOid =
20852 570 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20853 570 : if (OidIsValid(defaultPartOid))
20854 : {
20855 : /*
20856 : * Concurrent detaching when a default partition exists is not
20857 : * supported. The main problem is that the default partition
20858 : * constraint would change. And there's a definitional problem: what
20859 : * should happen to the tuples that are being inserted that belong to
20860 : * the partition being detached? Putting them on the partition being
20861 : * detached would be wrong, since they'd become "lost" after the
20862 : * detaching completes but we cannot put them in the default partition
20863 : * either until we alter its partition constraint.
20864 : *
20865 : * I think we could solve this problem if we effected the constraint
20866 : * change before committing the first transaction. But the lock would
20867 : * have to remain AEL and it would cause concurrent query planning to
20868 : * be blocked, so changing it that way would be even worse.
20869 : */
20870 106 : if (concurrent)
20871 12 : ereport(ERROR,
20872 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20873 : errmsg("cannot detach partitions concurrently when a default partition exists")));
20874 94 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20875 : }
20876 :
20877 : /*
20878 : * In concurrent mode, the partition is locked with share-update-exclusive
20879 : * in the first transaction. This allows concurrent transactions to be
20880 : * doing DML to the partition.
20881 : */
20882 558 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20883 : AccessExclusiveLock);
20884 :
20885 : /*
20886 : * Check inheritance conditions and either delete the pg_inherits row (in
20887 : * non-concurrent mode) or just set the inhdetachpending flag.
20888 : */
20889 546 : if (!concurrent)
20890 400 : RemoveInheritance(partRel, rel, false);
20891 : else
20892 146 : MarkInheritDetached(partRel, rel);
20893 :
20894 : /*
20895 : * Ensure that foreign keys still hold after this detach. This keeps
20896 : * locks on the referencing tables, which prevents concurrent transactions
20897 : * from adding rows that we wouldn't see. For this to work in concurrent
20898 : * mode, it is critical that the partition appears as no longer attached
20899 : * for the RI queries as soon as the first transaction commits.
20900 : */
20901 526 : ATDetachCheckNoForeignKeyRefs(partRel);
20902 :
20903 : /*
20904 : * Concurrent mode has to work harder; first we add a new constraint to
20905 : * the partition that matches the partition constraint. Then we close our
20906 : * existing transaction, and in a new one wait for all processes to catch
20907 : * up on the catalog updates we've done so far; at that point we can
20908 : * complete the operation.
20909 : */
20910 492 : if (concurrent)
20911 : {
20912 : Oid partrelid,
20913 : parentrelid;
20914 : LOCKTAG tag;
20915 : char *parentrelname;
20916 : char *partrelname;
20917 :
20918 : /*
20919 : * Add a new constraint to the partition being detached, which
20920 : * supplants the partition constraint (unless there is one already).
20921 : */
20922 140 : DetachAddConstraintIfNeeded(wqueue, partRel);
20923 :
20924 : /*
20925 : * We're almost done now; the only traces that remain are the
20926 : * pg_inherits tuple and the partition's relpartbounds. Before we can
20927 : * remove those, we need to wait until all transactions that know that
20928 : * this is a partition are gone.
20929 : */
20930 :
20931 : /*
20932 : * Remember relation OIDs to re-acquire them later; and relation names
20933 : * too, for error messages if something is dropped in between.
20934 : */
20935 140 : partrelid = RelationGetRelid(partRel);
20936 140 : parentrelid = RelationGetRelid(rel);
20937 140 : parentrelname = MemoryContextStrdup(PortalContext,
20938 140 : RelationGetRelationName(rel));
20939 140 : partrelname = MemoryContextStrdup(PortalContext,
20940 140 : RelationGetRelationName(partRel));
20941 :
20942 : /* Invalidate relcache entries for the parent -- must be before close */
20943 140 : CacheInvalidateRelcache(rel);
20944 :
20945 140 : table_close(partRel, NoLock);
20946 140 : table_close(rel, NoLock);
20947 140 : tab->rel = NULL;
20948 :
20949 : /* Make updated catalog entry visible */
20950 140 : PopActiveSnapshot();
20951 140 : CommitTransactionCommand();
20952 :
20953 140 : StartTransactionCommand();
20954 :
20955 : /*
20956 : * Now wait. This ensures that all queries that were planned
20957 : * including the partition are finished before we remove the rest of
20958 : * catalog entries. We don't need or indeed want to acquire this
20959 : * lock, though -- that would block later queries.
20960 : *
20961 : * We don't need to concern ourselves with waiting for a lock on the
20962 : * partition itself, since we will acquire AccessExclusiveLock below.
20963 : */
20964 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20965 140 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
20966 :
20967 : /*
20968 : * Now acquire locks in both relations again. Note they may have been
20969 : * removed in the meantime, so care is required.
20970 : */
20971 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20972 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
20973 :
20974 : /* If the relations aren't there, something bad happened; bail out */
20975 90 : if (rel == NULL)
20976 : {
20977 0 : if (partRel != NULL) /* shouldn't happen */
20978 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20979 : partrelname);
20980 0 : ereport(ERROR,
20981 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20982 : errmsg("partitioned table \"%s\" was removed concurrently",
20983 : parentrelname)));
20984 : }
20985 90 : if (partRel == NULL)
20986 0 : ereport(ERROR,
20987 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20988 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
20989 :
20990 90 : tab->rel = rel;
20991 : }
20992 :
20993 : /*
20994 : * Detaching the partition might involve TOAST table access, so ensure we
20995 : * have a valid snapshot.
20996 : */
20997 442 : PushActiveSnapshot(GetTransactionSnapshot());
20998 :
20999 : /* Do the final part of detaching */
21000 442 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21001 :
21002 440 : PopActiveSnapshot();
21003 :
21004 440 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21005 :
21006 : /* keep our lock until commit */
21007 440 : table_close(partRel, NoLock);
21008 :
21009 440 : return address;
21010 : }
21011 :
21012 : /*
21013 : * Second part of ALTER TABLE .. DETACH.
21014 : *
21015 : * This is separate so that it can be run independently when the second
21016 : * transaction of the concurrent algorithm fails (crash or abort).
21017 : */
21018 : static void
21019 456 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
21020 : Oid defaultPartOid)
21021 : {
21022 : Relation classRel;
21023 : List *fks;
21024 : ListCell *cell;
21025 : List *indexes;
21026 : Datum new_val[Natts_pg_class];
21027 : bool new_null[Natts_pg_class],
21028 : new_repl[Natts_pg_class];
21029 : HeapTuple tuple,
21030 : newtuple;
21031 456 : Relation trigrel = NULL;
21032 456 : List *fkoids = NIL;
21033 :
21034 456 : if (concurrent)
21035 : {
21036 : /*
21037 : * We can remove the pg_inherits row now. (In the non-concurrent case,
21038 : * this was already done).
21039 : */
21040 104 : RemoveInheritance(partRel, rel, true);
21041 : }
21042 :
21043 : /* Drop any triggers that were cloned on creation/attach. */
21044 456 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
21045 :
21046 : /*
21047 : * Detach any foreign keys that are inherited. This includes creating
21048 : * additional action triggers.
21049 : */
21050 456 : fks = copyObject(RelationGetFKeyList(partRel));
21051 456 : if (fks != NIL)
21052 84 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21053 :
21054 : /*
21055 : * It's possible that the partition being detached has a foreign key that
21056 : * references a partitioned table. When that happens, there are multiple
21057 : * pg_constraint rows for the partition: one points to the partitioned
21058 : * table itself, while the others point to each of its partitions. Only
21059 : * the topmost one is to be considered here; the child constraints must be
21060 : * left alone, because conceptually those aren't coming from our parent
21061 : * partitioned table, but from this partition itself.
21062 : *
21063 : * We implement this by collecting all the constraint OIDs in a first scan
21064 : * of the FK array, and skipping in the loop below those constraints whose
21065 : * parents are listed here.
21066 : */
21067 1080 : foreach_node(ForeignKeyCacheInfo, fk, fks)
21068 168 : fkoids = lappend_oid(fkoids, fk->conoid);
21069 :
21070 624 : foreach(cell, fks)
21071 : {
21072 168 : ForeignKeyCacheInfo *fk = lfirst(cell);
21073 : HeapTuple contup;
21074 : Form_pg_constraint conform;
21075 :
21076 168 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21077 168 : if (!HeapTupleIsValid(contup))
21078 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21079 168 : conform = (Form_pg_constraint) GETSTRUCT(contup);
21080 :
21081 : /*
21082 : * Consider only inherited foreign keys, and only if their parents
21083 : * aren't in the list.
21084 : */
21085 168 : if (conform->contype != CONSTRAINT_FOREIGN ||
21086 312 : !OidIsValid(conform->conparentid) ||
21087 144 : list_member_oid(fkoids, conform->conparentid))
21088 : {
21089 66 : ReleaseSysCache(contup);
21090 66 : continue;
21091 : }
21092 :
21093 : /*
21094 : * The constraint on this table must be marked no longer a child of
21095 : * the parent's constraint, as do its check triggers.
21096 : */
21097 102 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
21098 :
21099 : /*
21100 : * Also, look up the partition's "check" triggers corresponding to the
21101 : * ENFORCED constraint being detached and detach them from the parent
21102 : * triggers. NOT ENFORCED constraints do not have these triggers;
21103 : * therefore, this step is not needed.
21104 : */
21105 102 : if (fk->conenforced)
21106 : {
21107 : Oid insertTriggerOid,
21108 : updateTriggerOid;
21109 :
21110 102 : GetForeignKeyCheckTriggers(trigrel,
21111 : fk->conoid, fk->confrelid, fk->conrelid,
21112 : &insertTriggerOid, &updateTriggerOid);
21113 : Assert(OidIsValid(insertTriggerOid));
21114 102 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21115 : RelationGetRelid(partRel));
21116 : Assert(OidIsValid(updateTriggerOid));
21117 102 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21118 : RelationGetRelid(partRel));
21119 : }
21120 :
21121 : /*
21122 : * Lastly, create the action triggers on the referenced table, using
21123 : * addFkRecurseReferenced, which requires some elaborate setup (so put
21124 : * it in a separate block). While at it, if the table is partitioned,
21125 : * that function will recurse to create the pg_constraint rows and
21126 : * action triggers for each partition.
21127 : *
21128 : * Note there's no need to do addFkConstraint() here, because the
21129 : * pg_constraint row already exists.
21130 : */
21131 : {
21132 : Constraint *fkconstraint;
21133 : int numfks;
21134 : AttrNumber conkey[INDEX_MAX_KEYS];
21135 : AttrNumber confkey[INDEX_MAX_KEYS];
21136 : Oid conpfeqop[INDEX_MAX_KEYS];
21137 : Oid conppeqop[INDEX_MAX_KEYS];
21138 : Oid conffeqop[INDEX_MAX_KEYS];
21139 : int numfkdelsetcols;
21140 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21141 : Relation refdRel;
21142 :
21143 102 : DeconstructFkConstraintRow(contup,
21144 : &numfks,
21145 : conkey,
21146 : confkey,
21147 : conpfeqop,
21148 : conppeqop,
21149 : conffeqop,
21150 : &numfkdelsetcols,
21151 : confdelsetcols);
21152 :
21153 : /* Create a synthetic node we'll use throughout */
21154 102 : fkconstraint = makeNode(Constraint);
21155 102 : fkconstraint->contype = CONSTRAINT_FOREIGN;
21156 102 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
21157 102 : fkconstraint->deferrable = conform->condeferrable;
21158 102 : fkconstraint->initdeferred = conform->condeferred;
21159 102 : fkconstraint->is_enforced = conform->conenforced;
21160 102 : fkconstraint->skip_validation = true;
21161 102 : fkconstraint->initially_valid = conform->convalidated;
21162 : /* a few irrelevant fields omitted here */
21163 102 : fkconstraint->pktable = NULL;
21164 102 : fkconstraint->fk_attrs = NIL;
21165 102 : fkconstraint->pk_attrs = NIL;
21166 102 : fkconstraint->fk_matchtype = conform->confmatchtype;
21167 102 : fkconstraint->fk_upd_action = conform->confupdtype;
21168 102 : fkconstraint->fk_del_action = conform->confdeltype;
21169 102 : fkconstraint->fk_del_set_cols = NIL;
21170 102 : fkconstraint->old_conpfeqop = NIL;
21171 102 : fkconstraint->old_pktable_oid = InvalidOid;
21172 102 : fkconstraint->location = -1;
21173 :
21174 : /* set up colnames, used to generate the constraint name */
21175 252 : for (int i = 0; i < numfks; i++)
21176 : {
21177 : Form_pg_attribute att;
21178 :
21179 150 : att = TupleDescAttr(RelationGetDescr(partRel),
21180 150 : conkey[i] - 1);
21181 :
21182 150 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21183 150 : makeString(NameStr(att->attname)));
21184 : }
21185 :
21186 102 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
21187 :
21188 102 : addFkRecurseReferenced(fkconstraint, partRel,
21189 : refdRel,
21190 : conform->conindid,
21191 : fk->conoid,
21192 : numfks,
21193 : confkey,
21194 : conkey,
21195 : conpfeqop,
21196 : conppeqop,
21197 : conffeqop,
21198 : numfkdelsetcols,
21199 : confdelsetcols,
21200 : true,
21201 : InvalidOid, InvalidOid,
21202 102 : conform->conperiod);
21203 102 : table_close(refdRel, NoLock); /* keep lock till end of xact */
21204 : }
21205 :
21206 102 : ReleaseSysCache(contup);
21207 : }
21208 456 : list_free_deep(fks);
21209 456 : if (trigrel)
21210 84 : table_close(trigrel, RowExclusiveLock);
21211 :
21212 : /*
21213 : * Any sub-constraints that are in the referenced-side of a larger
21214 : * constraint have to be removed. This partition is no longer part of the
21215 : * key space of the constraint.
21216 : */
21217 516 : foreach(cell, GetParentedForeignKeyRefs(partRel))
21218 : {
21219 62 : Oid constrOid = lfirst_oid(cell);
21220 : ObjectAddress constraint;
21221 :
21222 62 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21223 62 : deleteDependencyRecordsForClass(ConstraintRelationId,
21224 : constrOid,
21225 : ConstraintRelationId,
21226 : DEPENDENCY_INTERNAL);
21227 62 : CommandCounterIncrement();
21228 :
21229 62 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21230 62 : performDeletion(&constraint, DROP_RESTRICT, 0);
21231 : }
21232 :
21233 : /* Now we can detach indexes */
21234 454 : indexes = RelationGetIndexList(partRel);
21235 648 : foreach(cell, indexes)
21236 : {
21237 194 : Oid idxid = lfirst_oid(cell);
21238 : Oid parentidx;
21239 : Relation idx;
21240 : Oid constrOid;
21241 : Oid parentConstrOid;
21242 :
21243 194 : if (!has_superclass(idxid))
21244 12 : continue;
21245 :
21246 182 : parentidx = get_partition_parent(idxid, false);
21247 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21248 :
21249 182 : idx = index_open(idxid, AccessExclusiveLock);
21250 182 : IndexSetParentIndex(idx, InvalidOid);
21251 :
21252 : /*
21253 : * If there's a constraint associated with the index, detach it too.
21254 : * Careful: it is possible for a constraint index in a partition to be
21255 : * the child of a non-constraint index, so verify whether the parent
21256 : * index does actually have a constraint.
21257 : */
21258 182 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
21259 : idxid);
21260 182 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
21261 : parentidx);
21262 182 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21263 84 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21264 :
21265 182 : index_close(idx, NoLock);
21266 : }
21267 :
21268 : /* Update pg_class tuple */
21269 454 : classRel = table_open(RelationRelationId, RowExclusiveLock);
21270 454 : tuple = SearchSysCacheCopy1(RELOID,
21271 : ObjectIdGetDatum(RelationGetRelid(partRel)));
21272 454 : if (!HeapTupleIsValid(tuple))
21273 0 : elog(ERROR, "cache lookup failed for relation %u",
21274 : RelationGetRelid(partRel));
21275 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21276 :
21277 : /* Clear relpartbound and reset relispartition */
21278 454 : memset(new_val, 0, sizeof(new_val));
21279 454 : memset(new_null, false, sizeof(new_null));
21280 454 : memset(new_repl, false, sizeof(new_repl));
21281 454 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21282 454 : new_null[Anum_pg_class_relpartbound - 1] = true;
21283 454 : new_repl[Anum_pg_class_relpartbound - 1] = true;
21284 454 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21285 : new_val, new_null, new_repl);
21286 :
21287 454 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21288 454 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21289 454 : heap_freetuple(newtuple);
21290 454 : table_close(classRel, RowExclusiveLock);
21291 :
21292 : /*
21293 : * Drop identity property from all identity columns of partition.
21294 : */
21295 1294 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21296 : {
21297 840 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21298 :
21299 840 : if (!attr->attisdropped && attr->attidentity)
21300 6 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21301 : AccessExclusiveLock, true, true);
21302 : }
21303 :
21304 454 : if (OidIsValid(defaultPartOid))
21305 : {
21306 : /*
21307 : * If the relation being detached is the default partition itself,
21308 : * remove it from the parent's pg_partitioned_table entry.
21309 : *
21310 : * If not, we must invalidate default partition's relcache entry, as
21311 : * in StorePartitionBound: its partition constraint depends on every
21312 : * other partition's partition constraint.
21313 : */
21314 46 : if (RelationGetRelid(partRel) == defaultPartOid)
21315 2 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
21316 : else
21317 44 : CacheInvalidateRelcacheByRelid(defaultPartOid);
21318 : }
21319 :
21320 : /*
21321 : * Invalidate the parent's relcache so that the partition is no longer
21322 : * included in its partition descriptor.
21323 : */
21324 454 : CacheInvalidateRelcache(rel);
21325 :
21326 : /*
21327 : * If the partition we just detached is partitioned itself, invalidate
21328 : * relcache for all descendent partitions too to ensure that their
21329 : * rd_partcheck expression trees are rebuilt; must lock partitions before
21330 : * doing so, using the same lockmode as what partRel has been locked with
21331 : * by the caller.
21332 : */
21333 454 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21334 : {
21335 : List *children;
21336 :
21337 62 : children = find_all_inheritors(RelationGetRelid(partRel),
21338 : AccessExclusiveLock, NULL);
21339 204 : foreach(cell, children)
21340 : {
21341 142 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
21342 : }
21343 : }
21344 454 : }
21345 :
21346 : /*
21347 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21348 : *
21349 : * To use when a DETACH PARTITION command previously did not run to
21350 : * completion; this completes the detaching process.
21351 : */
21352 : static ObjectAddress
21353 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
21354 : {
21355 : Relation partRel;
21356 : ObjectAddress address;
21357 14 : Snapshot snap = GetActiveSnapshot();
21358 :
21359 14 : partRel = table_openrv(name, AccessExclusiveLock);
21360 :
21361 : /*
21362 : * Wait until existing snapshots are gone. This is important if the
21363 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21364 : * user could immediately run DETACH FINALIZE without actually waiting for
21365 : * existing transactions. We must not complete the detach action until
21366 : * all such queries are complete (otherwise we would present them with an
21367 : * inconsistent view of catalogs).
21368 : */
21369 14 : WaitForOlderSnapshots(snap->xmin, false);
21370 :
21371 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21372 :
21373 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21374 :
21375 14 : table_close(partRel, NoLock);
21376 :
21377 14 : return address;
21378 : }
21379 :
21380 : /*
21381 : * DetachAddConstraintIfNeeded
21382 : * Subroutine for ATExecDetachPartition. Create a constraint that
21383 : * takes the place of the partition constraint, but avoid creating
21384 : * a dupe if a constraint already exists which implies the needed
21385 : * constraint.
21386 : */
21387 : static void
21388 140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
21389 : {
21390 : List *constraintExpr;
21391 :
21392 140 : constraintExpr = RelationGetPartitionQual(partRel);
21393 140 : constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
21394 :
21395 : /*
21396 : * Avoid adding a new constraint if the needed constraint is implied by an
21397 : * existing constraint
21398 : */
21399 140 : if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
21400 : {
21401 : AlteredTableInfo *tab;
21402 : Constraint *n;
21403 :
21404 134 : tab = ATGetQueueEntry(wqueue, partRel);
21405 :
21406 : /* Add constraint on partition, equivalent to the partition constraint */
21407 134 : n = makeNode(Constraint);
21408 134 : n->contype = CONSTR_CHECK;
21409 134 : n->conname = NULL;
21410 134 : n->location = -1;
21411 134 : n->is_no_inherit = false;
21412 134 : n->raw_expr = NULL;
21413 134 : n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
21414 134 : n->is_enforced = true;
21415 134 : n->initially_valid = true;
21416 134 : n->skip_validation = true;
21417 : /* It's a re-add, since it nominally already exists */
21418 134 : ATAddCheckNNConstraint(wqueue, tab, partRel, n,
21419 : true, false, true, ShareUpdateExclusiveLock);
21420 : }
21421 140 : }
21422 :
21423 : /*
21424 : * DropClonedTriggersFromPartition
21425 : * subroutine for ATExecDetachPartition to remove any triggers that were
21426 : * cloned to the partition when it was created-as-partition or attached.
21427 : * This undoes what CloneRowTriggersToPartition did.
21428 : */
21429 : static void
21430 456 : DropClonedTriggersFromPartition(Oid partitionId)
21431 : {
21432 : ScanKeyData skey;
21433 : SysScanDesc scan;
21434 : HeapTuple trigtup;
21435 : Relation tgrel;
21436 : ObjectAddresses *objects;
21437 :
21438 456 : objects = new_object_addresses();
21439 :
21440 : /*
21441 : * Scan pg_trigger to search for all triggers on this rel.
21442 : */
21443 456 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21444 : F_OIDEQ, ObjectIdGetDatum(partitionId));
21445 456 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21446 456 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21447 : true, NULL, 1, &skey);
21448 850 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21449 : {
21450 394 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21451 : ObjectAddress trig;
21452 :
21453 : /* Ignore triggers that weren't cloned */
21454 394 : if (!OidIsValid(pg_trigger->tgparentid))
21455 376 : continue;
21456 :
21457 : /*
21458 : * Ignore internal triggers that are implementation objects of foreign
21459 : * keys, because these will be detached when the foreign keys
21460 : * themselves are.
21461 : */
21462 346 : if (OidIsValid(pg_trigger->tgconstrrelid))
21463 328 : continue;
21464 :
21465 : /*
21466 : * This is ugly, but necessary: remove the dependency markings on the
21467 : * trigger so that it can be removed.
21468 : */
21469 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21470 : TriggerRelationId,
21471 : DEPENDENCY_PARTITION_PRI);
21472 18 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21473 : RelationRelationId,
21474 : DEPENDENCY_PARTITION_SEC);
21475 :
21476 : /* remember this trigger to remove it below */
21477 18 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21478 18 : add_exact_object_address(&trig, objects);
21479 : }
21480 :
21481 : /* make the dependency removal visible to the deletion below */
21482 456 : CommandCounterIncrement();
21483 456 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
21484 :
21485 : /* done */
21486 456 : free_object_addresses(objects);
21487 456 : systable_endscan(scan);
21488 456 : table_close(tgrel, RowExclusiveLock);
21489 456 : }
21490 :
21491 : /*
21492 : * Before acquiring lock on an index, acquire the same lock on the owning
21493 : * table.
21494 : */
21495 : struct AttachIndexCallbackState
21496 : {
21497 : Oid partitionOid;
21498 : Oid parentTblOid;
21499 : bool lockedParentTbl;
21500 : };
21501 :
21502 : static void
21503 506 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21504 : void *arg)
21505 : {
21506 : struct AttachIndexCallbackState *state;
21507 : Form_pg_class classform;
21508 : HeapTuple tuple;
21509 :
21510 506 : state = (struct AttachIndexCallbackState *) arg;
21511 :
21512 506 : if (!state->lockedParentTbl)
21513 : {
21514 476 : LockRelationOid(state->parentTblOid, AccessShareLock);
21515 476 : state->lockedParentTbl = true;
21516 : }
21517 :
21518 : /*
21519 : * If we previously locked some other heap, and the name we're looking up
21520 : * no longer refers to an index on that relation, release the now-useless
21521 : * lock. XXX maybe we should do *after* we verify whether the index does
21522 : * not actually belong to the same relation ...
21523 : */
21524 506 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21525 : {
21526 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
21527 0 : state->partitionOid = InvalidOid;
21528 : }
21529 :
21530 : /* Didn't find a relation, so no need for locking or permission checks. */
21531 506 : if (!OidIsValid(relOid))
21532 6 : return;
21533 :
21534 500 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21535 500 : if (!HeapTupleIsValid(tuple))
21536 0 : return; /* concurrently dropped, so nothing to do */
21537 500 : classform = (Form_pg_class) GETSTRUCT(tuple);
21538 500 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21539 384 : classform->relkind != RELKIND_INDEX)
21540 6 : ereport(ERROR,
21541 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21542 : errmsg("\"%s\" is not an index", rv->relname)));
21543 494 : ReleaseSysCache(tuple);
21544 :
21545 : /*
21546 : * Since we need only examine the heap's tupledesc, an access share lock
21547 : * on it (preventing any DDL) is sufficient.
21548 : */
21549 494 : state->partitionOid = IndexGetRelation(relOid, false);
21550 494 : LockRelationOid(state->partitionOid, AccessShareLock);
21551 : }
21552 :
21553 : /*
21554 : * ALTER INDEX i1 ATTACH PARTITION i2
21555 : */
21556 : static ObjectAddress
21557 476 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
21558 : {
21559 : Relation partIdx;
21560 : Relation partTbl;
21561 : Relation parentTbl;
21562 : ObjectAddress address;
21563 : Oid partIdxId;
21564 : Oid currParent;
21565 : struct AttachIndexCallbackState state;
21566 :
21567 : /*
21568 : * We need to obtain lock on the index 'name' to modify it, but we also
21569 : * need to read its owning table's tuple descriptor -- so we need to lock
21570 : * both. To avoid deadlocks, obtain lock on the table before doing so on
21571 : * the index. Furthermore, we need to examine the parent table of the
21572 : * partition, so lock that one too.
21573 : */
21574 476 : state.partitionOid = InvalidOid;
21575 476 : state.parentTblOid = parentIdx->rd_index->indrelid;
21576 476 : state.lockedParentTbl = false;
21577 : partIdxId =
21578 476 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
21579 : RangeVarCallbackForAttachIndex,
21580 : &state);
21581 : /* Not there? */
21582 464 : if (!OidIsValid(partIdxId))
21583 0 : ereport(ERROR,
21584 : (errcode(ERRCODE_UNDEFINED_OBJECT),
21585 : errmsg("index \"%s\" does not exist", name->relname)));
21586 :
21587 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21588 464 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
21589 :
21590 : /* we already hold locks on both tables, so this is safe: */
21591 464 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21592 464 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21593 :
21594 464 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21595 :
21596 : /* Silently do nothing if already in the right state */
21597 928 : currParent = partIdx->rd_rel->relispartition ?
21598 464 : get_partition_parent(partIdxId, false) : InvalidOid;
21599 464 : if (currParent != RelationGetRelid(parentIdx))
21600 : {
21601 : IndexInfo *childInfo;
21602 : IndexInfo *parentInfo;
21603 : AttrMap *attmap;
21604 : bool found;
21605 : int i;
21606 : PartitionDesc partDesc;
21607 : Oid constraintOid,
21608 440 : cldConstrId = InvalidOid;
21609 :
21610 : /*
21611 : * If this partition already has an index attached, refuse the
21612 : * operation.
21613 : */
21614 440 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21615 :
21616 434 : if (OidIsValid(currParent))
21617 0 : ereport(ERROR,
21618 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21619 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21620 : RelationGetRelationName(partIdx),
21621 : RelationGetRelationName(parentIdx)),
21622 : errdetail("Index \"%s\" is already attached to another index.",
21623 : RelationGetRelationName(partIdx))));
21624 :
21625 : /* Make sure it indexes a partition of the other index's table */
21626 434 : partDesc = RelationGetPartitionDesc(parentTbl, true);
21627 434 : found = false;
21628 716 : for (i = 0; i < partDesc->nparts; i++)
21629 : {
21630 710 : if (partDesc->oids[i] == state.partitionOid)
21631 : {
21632 428 : found = true;
21633 428 : break;
21634 : }
21635 : }
21636 434 : if (!found)
21637 6 : ereport(ERROR,
21638 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21639 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21640 : RelationGetRelationName(partIdx),
21641 : RelationGetRelationName(parentIdx)),
21642 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21643 : RelationGetRelationName(partIdx),
21644 : RelationGetRelationName(parentTbl))));
21645 :
21646 : /* Ensure the indexes are compatible */
21647 428 : childInfo = BuildIndexInfo(partIdx);
21648 428 : parentInfo = BuildIndexInfo(parentIdx);
21649 428 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21650 : RelationGetDescr(parentTbl),
21651 : false);
21652 428 : if (!CompareIndexInfo(childInfo, parentInfo,
21653 428 : partIdx->rd_indcollation,
21654 428 : parentIdx->rd_indcollation,
21655 428 : partIdx->rd_opfamily,
21656 428 : parentIdx->rd_opfamily,
21657 : attmap))
21658 42 : ereport(ERROR,
21659 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21660 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21661 : RelationGetRelationName(partIdx),
21662 : RelationGetRelationName(parentIdx)),
21663 : errdetail("The index definitions do not match.")));
21664 :
21665 : /*
21666 : * If there is a constraint in the parent, make sure there is one in
21667 : * the child too.
21668 : */
21669 386 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21670 : RelationGetRelid(parentIdx));
21671 :
21672 386 : if (OidIsValid(constraintOid))
21673 : {
21674 184 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
21675 : partIdxId);
21676 184 : if (!OidIsValid(cldConstrId))
21677 6 : ereport(ERROR,
21678 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21679 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21680 : RelationGetRelationName(partIdx),
21681 : RelationGetRelationName(parentIdx)),
21682 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21683 : RelationGetRelationName(parentIdx),
21684 : RelationGetRelationName(parentTbl),
21685 : RelationGetRelationName(partIdx))));
21686 : }
21687 :
21688 : /*
21689 : * If it's a primary key, make sure the columns in the partition are
21690 : * NOT NULL.
21691 : */
21692 380 : if (parentIdx->rd_index->indisprimary)
21693 166 : verifyPartitionIndexNotNull(childInfo, partTbl);
21694 :
21695 : /* All good -- do it */
21696 380 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21697 380 : if (OidIsValid(constraintOid))
21698 178 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
21699 : RelationGetRelid(partTbl));
21700 :
21701 380 : free_attrmap(attmap);
21702 :
21703 380 : validatePartitionedIndex(parentIdx, parentTbl);
21704 : }
21705 :
21706 404 : relation_close(parentTbl, AccessShareLock);
21707 : /* keep these locks till commit */
21708 404 : relation_close(partTbl, NoLock);
21709 404 : relation_close(partIdx, NoLock);
21710 :
21711 404 : return address;
21712 : }
21713 :
21714 : /*
21715 : * Verify whether the given partition already contains an index attached
21716 : * to the given partitioned index. If so, raise an error.
21717 : */
21718 : static void
21719 440 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21720 : {
21721 : Oid existingIdx;
21722 :
21723 440 : existingIdx = index_get_partition(partitionTbl,
21724 : RelationGetRelid(parentIdx));
21725 440 : if (OidIsValid(existingIdx))
21726 6 : ereport(ERROR,
21727 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21728 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21729 : RelationGetRelationName(partIdx),
21730 : RelationGetRelationName(parentIdx)),
21731 : errdetail("Another index is already attached for partition \"%s\".",
21732 : RelationGetRelationName(partitionTbl))));
21733 434 : }
21734 :
21735 : /*
21736 : * Verify whether the set of attached partition indexes to a parent index on
21737 : * a partitioned table is complete. If it is, mark the parent index valid.
21738 : *
21739 : * This should be called each time a partition index is attached.
21740 : */
21741 : static void
21742 422 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
21743 : {
21744 : Relation inheritsRel;
21745 : SysScanDesc scan;
21746 : ScanKeyData key;
21747 422 : int tuples = 0;
21748 : HeapTuple inhTup;
21749 422 : bool updated = false;
21750 :
21751 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21752 :
21753 : /*
21754 : * Scan pg_inherits for this parent index. Count each valid index we find
21755 : * (verifying the pg_index entry for each), and if we reach the total
21756 : * amount we expect, we can mark this parent index as valid.
21757 : */
21758 422 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21759 422 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21760 : BTEqualStrategyNumber, F_OIDEQ,
21761 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21762 422 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21763 : NULL, 1, &key);
21764 1126 : while ((inhTup = systable_getnext(scan)) != NULL)
21765 : {
21766 704 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21767 : HeapTuple indTup;
21768 : Form_pg_index indexForm;
21769 :
21770 704 : indTup = SearchSysCache1(INDEXRELID,
21771 : ObjectIdGetDatum(inhForm->inhrelid));
21772 704 : if (!HeapTupleIsValid(indTup))
21773 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21774 704 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21775 704 : if (indexForm->indisvalid)
21776 642 : tuples += 1;
21777 704 : ReleaseSysCache(indTup);
21778 : }
21779 :
21780 : /* Done with pg_inherits */
21781 422 : systable_endscan(scan);
21782 422 : table_close(inheritsRel, AccessShareLock);
21783 :
21784 : /*
21785 : * If we found as many inherited indexes as the partitioned table has
21786 : * partitions, we're good; update pg_index to set indisvalid.
21787 : */
21788 422 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21789 : {
21790 : Relation idxRel;
21791 : HeapTuple indTup;
21792 : Form_pg_index indexForm;
21793 :
21794 204 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
21795 204 : indTup = SearchSysCacheCopy1(INDEXRELID,
21796 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21797 204 : if (!HeapTupleIsValid(indTup))
21798 0 : elog(ERROR, "cache lookup failed for index %u",
21799 : RelationGetRelid(partedIdx));
21800 204 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21801 :
21802 204 : indexForm->indisvalid = true;
21803 204 : updated = true;
21804 :
21805 204 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21806 :
21807 204 : table_close(idxRel, RowExclusiveLock);
21808 204 : heap_freetuple(indTup);
21809 : }
21810 :
21811 : /*
21812 : * If this index is in turn a partition of a larger index, validating it
21813 : * might cause the parent to become valid also. Try that.
21814 : */
21815 422 : if (updated && partedIdx->rd_rel->relispartition)
21816 : {
21817 : Oid parentIdxId,
21818 : parentTblId;
21819 : Relation parentIdx,
21820 : parentTbl;
21821 :
21822 : /* make sure we see the validation we just did */
21823 42 : CommandCounterIncrement();
21824 :
21825 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21826 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21827 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21828 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21829 : Assert(!parentIdx->rd_index->indisvalid);
21830 :
21831 42 : validatePartitionedIndex(parentIdx, parentTbl);
21832 :
21833 42 : relation_close(parentIdx, AccessExclusiveLock);
21834 42 : relation_close(parentTbl, AccessExclusiveLock);
21835 : }
21836 422 : }
21837 :
21838 : /*
21839 : * When attaching an index as a partition of a partitioned index which is a
21840 : * primary key, verify that all the columns in the partition are marked NOT
21841 : * NULL.
21842 : */
21843 : static void
21844 166 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
21845 : {
21846 336 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21847 : {
21848 170 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
21849 170 : iinfo->ii_IndexAttrNumbers[i] - 1);
21850 :
21851 170 : if (!att->attnotnull)
21852 0 : ereport(ERROR,
21853 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21854 : errmsg("invalid primary key definition"),
21855 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21856 : NameStr(att->attname),
21857 : RelationGetRelationName(partition)));
21858 : }
21859 166 : }
21860 :
21861 : /*
21862 : * Return an OID list of constraints that reference the given relation
21863 : * that are marked as having a parent constraints.
21864 : */
21865 : static List *
21866 982 : GetParentedForeignKeyRefs(Relation partition)
21867 : {
21868 : Relation pg_constraint;
21869 : HeapTuple tuple;
21870 : SysScanDesc scan;
21871 : ScanKeyData key[2];
21872 982 : List *constraints = NIL;
21873 :
21874 : /*
21875 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
21876 : * scan.
21877 : */
21878 1410 : if (RelationGetIndexList(partition) == NIL ||
21879 428 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
21880 : INDEX_ATTR_BITMAP_KEY)))
21881 710 : return NIL;
21882 :
21883 : /* Search for constraints referencing this table */
21884 272 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21885 272 : ScanKeyInit(&key[0],
21886 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21887 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21888 272 : ScanKeyInit(&key[1],
21889 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
21890 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21891 :
21892 : /* XXX This is a seqscan, as we don't have a usable index */
21893 272 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21894 444 : while ((tuple = systable_getnext(scan)) != NULL)
21895 : {
21896 172 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21897 :
21898 : /*
21899 : * We only need to process constraints that are part of larger ones.
21900 : */
21901 172 : if (!OidIsValid(constrForm->conparentid))
21902 0 : continue;
21903 :
21904 172 : constraints = lappend_oid(constraints, constrForm->oid);
21905 : }
21906 :
21907 272 : systable_endscan(scan);
21908 272 : table_close(pg_constraint, AccessShareLock);
21909 :
21910 272 : return constraints;
21911 : }
21912 :
21913 : /*
21914 : * During DETACH PARTITION, verify that any foreign keys pointing to the
21915 : * partitioned table would not become invalid. An error is raised if any
21916 : * referenced values exist.
21917 : */
21918 : static void
21919 526 : ATDetachCheckNoForeignKeyRefs(Relation partition)
21920 : {
21921 : List *constraints;
21922 : ListCell *cell;
21923 :
21924 526 : constraints = GetParentedForeignKeyRefs(partition);
21925 :
21926 602 : foreach(cell, constraints)
21927 : {
21928 110 : Oid constrOid = lfirst_oid(cell);
21929 : HeapTuple tuple;
21930 : Form_pg_constraint constrForm;
21931 : Relation rel;
21932 110 : Trigger trig = {0};
21933 :
21934 110 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21935 110 : if (!HeapTupleIsValid(tuple))
21936 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21937 110 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21938 :
21939 : Assert(OidIsValid(constrForm->conparentid));
21940 : Assert(constrForm->confrelid == RelationGetRelid(partition));
21941 :
21942 : /* prevent data changes into the referencing table until commit */
21943 110 : rel = table_open(constrForm->conrelid, ShareLock);
21944 :
21945 110 : trig.tgoid = InvalidOid;
21946 110 : trig.tgname = NameStr(constrForm->conname);
21947 110 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
21948 110 : trig.tgisinternal = true;
21949 110 : trig.tgconstrrelid = RelationGetRelid(partition);
21950 110 : trig.tgconstrindid = constrForm->conindid;
21951 110 : trig.tgconstraint = constrForm->oid;
21952 110 : trig.tgdeferrable = false;
21953 110 : trig.tginitdeferred = false;
21954 : /* we needn't fill in remaining fields */
21955 :
21956 110 : RI_PartitionRemove_Check(&trig, rel, partition);
21957 :
21958 76 : ReleaseSysCache(tuple);
21959 :
21960 76 : table_close(rel, NoLock);
21961 : }
21962 492 : }
21963 :
21964 : /*
21965 : * resolve column compression specification to compression method.
21966 : */
21967 : static char
21968 260884 : GetAttributeCompression(Oid atttypid, const char *compression)
21969 : {
21970 : char cmethod;
21971 :
21972 260884 : if (compression == NULL || strcmp(compression, "default") == 0)
21973 260704 : return InvalidCompressionMethod;
21974 :
21975 : /*
21976 : * To specify a nondefault method, the column data type must be toastable.
21977 : * Note this says nothing about whether the column's attstorage setting
21978 : * permits compression; we intentionally allow attstorage and
21979 : * attcompression to be independent. But with a non-toastable type,
21980 : * attstorage could not be set to a value that would permit compression.
21981 : *
21982 : * We don't actually need to enforce this, since nothing bad would happen
21983 : * if attcompression were non-default; it would never be consulted. But
21984 : * it seems more user-friendly to complain about a certainly-useless
21985 : * attempt to set the property.
21986 : */
21987 180 : if (!TypeIsToastable(atttypid))
21988 6 : ereport(ERROR,
21989 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21990 : errmsg("column data type %s does not support compression",
21991 : format_type_be(atttypid))));
21992 :
21993 174 : cmethod = CompressionNameToMethod(compression);
21994 174 : if (!CompressionMethodIsValid(cmethod))
21995 12 : ereport(ERROR,
21996 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21997 : errmsg("invalid compression method \"%s\"", compression)));
21998 :
21999 162 : return cmethod;
22000 : }
22001 :
22002 : /*
22003 : * resolve column storage specification
22004 : */
22005 : static char
22006 254 : GetAttributeStorage(Oid atttypid, const char *storagemode)
22007 : {
22008 254 : char cstorage = 0;
22009 :
22010 254 : if (pg_strcasecmp(storagemode, "plain") == 0)
22011 54 : cstorage = TYPSTORAGE_PLAIN;
22012 200 : else if (pg_strcasecmp(storagemode, "external") == 0)
22013 158 : cstorage = TYPSTORAGE_EXTERNAL;
22014 42 : else if (pg_strcasecmp(storagemode, "extended") == 0)
22015 16 : cstorage = TYPSTORAGE_EXTENDED;
22016 26 : else if (pg_strcasecmp(storagemode, "main") == 0)
22017 20 : cstorage = TYPSTORAGE_MAIN;
22018 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
22019 6 : cstorage = get_typstorage(atttypid);
22020 : else
22021 0 : ereport(ERROR,
22022 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22023 : errmsg("invalid storage type \"%s\"",
22024 : storagemode)));
22025 :
22026 : /*
22027 : * safety check: do not allow toasted storage modes unless column datatype
22028 : * is TOAST-aware.
22029 : */
22030 254 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22031 6 : ereport(ERROR,
22032 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22033 : errmsg("column data type %s can only have storage PLAIN",
22034 : format_type_be(atttypid))));
22035 :
22036 248 : return cstorage;
22037 : }
|