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-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/commands/tablecmds.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/attmap.h"
18 : #include "access/genam.h"
19 : #include "access/gist.h"
20 : #include "access/heapam.h"
21 : #include "access/heapam_xlog.h"
22 : #include "access/multixact.h"
23 : #include "access/reloptions.h"
24 : #include "access/relscan.h"
25 : #include "access/sysattr.h"
26 : #include "access/tableam.h"
27 : #include "access/toast_compression.h"
28 : #include "access/xact.h"
29 : #include "access/xlog.h"
30 : #include "access/xloginsert.h"
31 : #include "catalog/catalog.h"
32 : #include "catalog/heap.h"
33 : #include "catalog/index.h"
34 : #include "catalog/namespace.h"
35 : #include "catalog/objectaccess.h"
36 : #include "catalog/partition.h"
37 : #include "catalog/pg_am.h"
38 : #include "catalog/pg_attrdef.h"
39 : #include "catalog/pg_collation.h"
40 : #include "catalog/pg_constraint.h"
41 : #include "catalog/pg_depend.h"
42 : #include "catalog/pg_foreign_table.h"
43 : #include "catalog/pg_inherits.h"
44 : #include "catalog/pg_largeobject.h"
45 : #include "catalog/pg_largeobject_metadata.h"
46 : #include "catalog/pg_namespace.h"
47 : #include "catalog/pg_opclass.h"
48 : #include "catalog/pg_policy.h"
49 : #include "catalog/pg_proc.h"
50 : #include "catalog/pg_publication_rel.h"
51 : #include "catalog/pg_rewrite.h"
52 : #include "catalog/pg_statistic_ext.h"
53 : #include "catalog/pg_tablespace.h"
54 : #include "catalog/pg_trigger.h"
55 : #include "catalog/pg_type.h"
56 : #include "catalog/storage.h"
57 : #include "catalog/storage_xlog.h"
58 : #include "catalog/toasting.h"
59 : #include "commands/cluster.h"
60 : #include "commands/comment.h"
61 : #include "commands/defrem.h"
62 : #include "commands/event_trigger.h"
63 : #include "commands/sequence.h"
64 : #include "commands/tablecmds.h"
65 : #include "commands/tablespace.h"
66 : #include "commands/trigger.h"
67 : #include "commands/typecmds.h"
68 : #include "commands/user.h"
69 : #include "commands/vacuum.h"
70 : #include "common/int.h"
71 : #include "executor/executor.h"
72 : #include "foreign/fdwapi.h"
73 : #include "foreign/foreign.h"
74 : #include "miscadmin.h"
75 : #include "nodes/makefuncs.h"
76 : #include "nodes/nodeFuncs.h"
77 : #include "nodes/parsenodes.h"
78 : #include "optimizer/optimizer.h"
79 : #include "parser/parse_coerce.h"
80 : #include "parser/parse_collate.h"
81 : #include "parser/parse_expr.h"
82 : #include "parser/parse_relation.h"
83 : #include "parser/parse_type.h"
84 : #include "parser/parse_utilcmd.h"
85 : #include "parser/parser.h"
86 : #include "partitioning/partbounds.h"
87 : #include "partitioning/partdesc.h"
88 : #include "pgstat.h"
89 : #include "rewrite/rewriteDefine.h"
90 : #include "rewrite/rewriteHandler.h"
91 : #include "rewrite/rewriteManip.h"
92 : #include "storage/bufmgr.h"
93 : #include "storage/lmgr.h"
94 : #include "storage/lock.h"
95 : #include "storage/predicate.h"
96 : #include "storage/smgr.h"
97 : #include "tcop/utility.h"
98 : #include "utils/acl.h"
99 : #include "utils/builtins.h"
100 : #include "utils/fmgroids.h"
101 : #include "utils/inval.h"
102 : #include "utils/lsyscache.h"
103 : #include "utils/memutils.h"
104 : #include "utils/partcache.h"
105 : #include "utils/relcache.h"
106 : #include "utils/ruleutils.h"
107 : #include "utils/snapmgr.h"
108 : #include "utils/syscache.h"
109 : #include "utils/timestamp.h"
110 : #include "utils/typcache.h"
111 : #include "utils/usercontext.h"
112 :
113 : /*
114 : * ON COMMIT action list
115 : */
116 : typedef struct OnCommitItem
117 : {
118 : Oid relid; /* relid of relation */
119 : OnCommitAction oncommit; /* what to do at end of xact */
120 :
121 : /*
122 : * If this entry was created during the current transaction,
123 : * creating_subid is the ID of the creating subxact; if created in a prior
124 : * transaction, creating_subid is zero. If deleted during the current
125 : * transaction, deleting_subid is the ID of the deleting subxact; if no
126 : * deletion request is pending, deleting_subid is zero.
127 : */
128 : SubTransactionId creating_subid;
129 : SubTransactionId deleting_subid;
130 : } OnCommitItem;
131 :
132 : static List *on_commits = NIL;
133 :
134 :
135 : /*
136 : * State information for ALTER TABLE
137 : *
138 : * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
139 : * structs, one for each table modified by the operation (the named table
140 : * plus any child tables that are affected). We save lists of subcommands
141 : * to apply to this table (possibly modified by parse transformation steps);
142 : * these lists will be executed in Phase 2. If a Phase 3 step is needed,
143 : * necessary information is stored in the constraints and newvals lists.
144 : *
145 : * Phase 2 is divided into multiple passes; subcommands are executed in
146 : * a pass determined by subcommand type.
147 : */
148 :
149 : typedef enum AlterTablePass
150 : {
151 : AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
152 : AT_PASS_DROP, /* DROP (all flavors) */
153 : AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
154 : AT_PASS_ADD_COL, /* ADD COLUMN */
155 : AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
156 : AT_PASS_OLD_INDEX, /* re-add existing indexes */
157 : AT_PASS_OLD_CONSTR, /* re-add existing constraints */
158 : /* We could support a RENAME COLUMN pass here, but not currently used */
159 : AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
160 : AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
161 : AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
162 : AT_PASS_ADD_INDEX, /* ADD indexes */
163 : AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
164 : AT_PASS_MISC, /* other stuff */
165 : } AlterTablePass;
166 :
167 : #define AT_NUM_PASSES (AT_PASS_MISC + 1)
168 :
169 : typedef struct AlteredTableInfo
170 : {
171 : /* Information saved before any work commences: */
172 : Oid relid; /* Relation to work on */
173 : char relkind; /* Its relkind */
174 : TupleDesc oldDesc; /* Pre-modification tuple descriptor */
175 :
176 : /*
177 : * Transiently set during Phase 2, normally set to NULL.
178 : *
179 : * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
180 : * returns control. This can be exploited by ATExecCmd subroutines to
181 : * close/reopen across transaction boundaries.
182 : */
183 : Relation rel;
184 :
185 : /* Information saved by Phase 1 for Phase 2: */
186 : List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
187 : /* Information saved by Phases 1/2 for Phase 3: */
188 : List *constraints; /* List of NewConstraint */
189 : List *newvals; /* List of NewColumnValue */
190 : List *afterStmts; /* List of utility command parsetrees */
191 : bool verify_new_notnull; /* T if we should recheck NOT NULL */
192 : int rewrite; /* Reason for forced rewrite, if any */
193 : bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
194 : Oid newAccessMethod; /* new access method; 0 means no change,
195 : * if above is true */
196 : Oid newTableSpace; /* new tablespace; 0 means no change */
197 : bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
198 : char newrelpersistence; /* if above is true */
199 : Expr *partition_constraint; /* for attach partition validation */
200 : /* true, if validating default due to some other attach/detach */
201 : bool validate_default;
202 : /* Objects to rebuild after completing ALTER TYPE operations */
203 : List *changedConstraintOids; /* OIDs of constraints to rebuild */
204 : List *changedConstraintDefs; /* string definitions of same */
205 : List *changedIndexOids; /* OIDs of indexes to rebuild */
206 : List *changedIndexDefs; /* string definitions of same */
207 : char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
208 : char *clusterOnIndex; /* index to use for CLUSTER */
209 : List *changedStatisticsOids; /* OIDs of statistics to rebuild */
210 : List *changedStatisticsDefs; /* string definitions of same */
211 : } AlteredTableInfo;
212 :
213 : /* Struct describing one new constraint to check in Phase 3 scan */
214 : /* Note: new not-null constraints are handled elsewhere */
215 : typedef struct NewConstraint
216 : {
217 : char *name; /* Constraint name, or NULL if none */
218 : ConstrType contype; /* CHECK or FOREIGN */
219 : Oid refrelid; /* PK rel, if FOREIGN */
220 : Oid refindid; /* OID of PK's index, if FOREIGN */
221 : bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
222 : Oid conid; /* OID of pg_constraint entry, if FOREIGN */
223 : Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
224 : ExprState *qualstate; /* Execution state for CHECK expr */
225 : } NewConstraint;
226 :
227 : /*
228 : * Struct describing one new column value that needs to be computed during
229 : * Phase 3 copy (this could be either a new column with a non-null default, or
230 : * a column that we're changing the type of). Columns without such an entry
231 : * are just copied from the old table during ATRewriteTable. Note that the
232 : * expr is an expression over *old* table values, except when is_generated
233 : * is true; then it is an expression over columns of the *new* tuple.
234 : */
235 : typedef struct NewColumnValue
236 : {
237 : AttrNumber attnum; /* which column */
238 : Expr *expr; /* expression to compute */
239 : ExprState *exprstate; /* execution state */
240 : bool is_generated; /* is it a GENERATED expression? */
241 : } NewColumnValue;
242 :
243 : /*
244 : * Error-reporting support for RemoveRelations
245 : */
246 : struct dropmsgstrings
247 : {
248 : char kind;
249 : int nonexistent_code;
250 : const char *nonexistent_msg;
251 : const char *skipping_msg;
252 : const char *nota_msg;
253 : const char *drophint_msg;
254 : };
255 :
256 : static const struct dropmsgstrings dropmsgstringarray[] = {
257 : {RELKIND_RELATION,
258 : ERRCODE_UNDEFINED_TABLE,
259 : gettext_noop("table \"%s\" does not exist"),
260 : gettext_noop("table \"%s\" does not exist, skipping"),
261 : gettext_noop("\"%s\" is not a table"),
262 : gettext_noop("Use DROP TABLE to remove a table.")},
263 : {RELKIND_SEQUENCE,
264 : ERRCODE_UNDEFINED_TABLE,
265 : gettext_noop("sequence \"%s\" does not exist"),
266 : gettext_noop("sequence \"%s\" does not exist, skipping"),
267 : gettext_noop("\"%s\" is not a sequence"),
268 : gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
269 : {RELKIND_VIEW,
270 : ERRCODE_UNDEFINED_TABLE,
271 : gettext_noop("view \"%s\" does not exist"),
272 : gettext_noop("view \"%s\" does not exist, skipping"),
273 : gettext_noop("\"%s\" is not a view"),
274 : gettext_noop("Use DROP VIEW to remove a view.")},
275 : {RELKIND_MATVIEW,
276 : ERRCODE_UNDEFINED_TABLE,
277 : gettext_noop("materialized view \"%s\" does not exist"),
278 : gettext_noop("materialized view \"%s\" does not exist, skipping"),
279 : gettext_noop("\"%s\" is not a materialized view"),
280 : gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
281 : {RELKIND_INDEX,
282 : ERRCODE_UNDEFINED_OBJECT,
283 : gettext_noop("index \"%s\" does not exist"),
284 : gettext_noop("index \"%s\" does not exist, skipping"),
285 : gettext_noop("\"%s\" is not an index"),
286 : gettext_noop("Use DROP INDEX to remove an index.")},
287 : {RELKIND_COMPOSITE_TYPE,
288 : ERRCODE_UNDEFINED_OBJECT,
289 : gettext_noop("type \"%s\" does not exist"),
290 : gettext_noop("type \"%s\" does not exist, skipping"),
291 : gettext_noop("\"%s\" is not a type"),
292 : gettext_noop("Use DROP TYPE to remove a type.")},
293 : {RELKIND_FOREIGN_TABLE,
294 : ERRCODE_UNDEFINED_OBJECT,
295 : gettext_noop("foreign table \"%s\" does not exist"),
296 : gettext_noop("foreign table \"%s\" does not exist, skipping"),
297 : gettext_noop("\"%s\" is not a foreign table"),
298 : gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
299 : {RELKIND_PARTITIONED_TABLE,
300 : ERRCODE_UNDEFINED_TABLE,
301 : gettext_noop("table \"%s\" does not exist"),
302 : gettext_noop("table \"%s\" does not exist, skipping"),
303 : gettext_noop("\"%s\" is not a table"),
304 : gettext_noop("Use DROP TABLE to remove a table.")},
305 : {RELKIND_PARTITIONED_INDEX,
306 : ERRCODE_UNDEFINED_OBJECT,
307 : gettext_noop("index \"%s\" does not exist"),
308 : gettext_noop("index \"%s\" does not exist, skipping"),
309 : gettext_noop("\"%s\" is not an index"),
310 : gettext_noop("Use DROP INDEX to remove an index.")},
311 : {'\0', 0, NULL, NULL, NULL, NULL}
312 : };
313 :
314 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
315 : struct DropRelationCallbackState
316 : {
317 : /* These fields are set by RemoveRelations: */
318 : char expected_relkind;
319 : LOCKMODE heap_lockmode;
320 : /* These fields are state to track which subsidiary locks are held: */
321 : Oid heapOid;
322 : Oid partParentOid;
323 : /* These fields are passed back by RangeVarCallbackForDropRelation: */
324 : char actual_relkind;
325 : char actual_relpersistence;
326 : };
327 :
328 : /* Alter table target-type flags for ATSimplePermissions */
329 : #define ATT_TABLE 0x0001
330 : #define ATT_VIEW 0x0002
331 : #define ATT_MATVIEW 0x0004
332 : #define ATT_INDEX 0x0008
333 : #define ATT_COMPOSITE_TYPE 0x0010
334 : #define ATT_FOREIGN_TABLE 0x0020
335 : #define ATT_PARTITIONED_INDEX 0x0040
336 : #define ATT_SEQUENCE 0x0080
337 : #define ATT_PARTITIONED_TABLE 0x0100
338 :
339 : /*
340 : * ForeignTruncateInfo
341 : *
342 : * Information related to truncation of foreign tables. This is used for
343 : * the elements in a hash table. It uses the server OID as lookup key,
344 : * and includes a per-server list of all foreign tables involved in the
345 : * truncation.
346 : */
347 : typedef struct ForeignTruncateInfo
348 : {
349 : Oid serverid;
350 : List *rels;
351 : } ForeignTruncateInfo;
352 :
353 : /* Partial or complete FK creation in addFkConstraint() */
354 : typedef enum addFkConstraintSides
355 : {
356 : addFkReferencedSide,
357 : addFkReferencingSide,
358 : addFkBothSides,
359 : } addFkConstraintSides;
360 :
361 : /*
362 : * Partition tables are expected to be dropped when the parent partitioned
363 : * table gets dropped. Hence for partitioning we use AUTO dependency.
364 : * Otherwise, for regular inheritance use NORMAL dependency.
365 : */
366 : #define child_dependency_type(child_is_partition) \
367 : ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
368 :
369 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
370 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
371 : static void truncate_check_activity(Relation rel);
372 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
373 : Oid relId, Oid oldRelId, void *arg);
374 : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
375 : bool is_partition, List **supconstr,
376 : List **supnotnulls);
377 : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
378 : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
379 : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
380 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
381 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
382 : static void StoreCatalogInheritance(Oid relationId, List *supers,
383 : bool child_is_partition);
384 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
385 : int32 seqNumber, Relation inhRelation,
386 : bool child_is_partition);
387 : static int findAttrByName(const char *attributeName, const List *columns);
388 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
389 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
390 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
391 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
392 : LOCKMODE lockmode);
393 : static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
394 : ATAlterConstraint *cmdcon,
395 : bool recurse, LOCKMODE lockmode);
396 : static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
397 : Relation tgrel, Relation rel, HeapTuple contuple,
398 : bool recurse, LOCKMODE lockmode);
399 : static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
400 : Relation conrel, Relation tgrel,
401 : Oid fkrelid, Oid pkrelid,
402 : HeapTuple contuple, LOCKMODE lockmode,
403 : Oid ReferencedParentDelTrigger,
404 : Oid ReferencedParentUpdTrigger,
405 : Oid ReferencingParentInsTrigger,
406 : Oid ReferencingParentUpdTrigger);
407 : static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
408 : Relation conrel, Relation tgrel, Relation rel,
409 : HeapTuple contuple, bool recurse,
410 : List **otherrelids, LOCKMODE lockmode);
411 : static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
412 : Relation conrel, Relation rel,
413 : HeapTuple contuple, LOCKMODE lockmode);
414 : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
415 : bool deferrable, bool initdeferred,
416 : List **otherrelids);
417 : static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
418 : Relation conrel, Relation tgrel,
419 : Oid fkrelid, Oid pkrelid,
420 : HeapTuple contuple, LOCKMODE lockmode,
421 : Oid ReferencedParentDelTrigger,
422 : Oid ReferencedParentUpdTrigger,
423 : Oid ReferencingParentInsTrigger,
424 : Oid ReferencingParentUpdTrigger);
425 : static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
426 : Relation conrel, Relation tgrel, Relation rel,
427 : HeapTuple contuple, bool recurse,
428 : List **otherrelids, LOCKMODE lockmode);
429 : static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
430 : HeapTuple contuple);
431 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
432 : Relation rel, char *constrName,
433 : bool recurse, bool recursing, LOCKMODE lockmode);
434 : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
435 : Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
436 : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
437 : char *constrName, HeapTuple contuple,
438 : bool recurse, bool recursing, LOCKMODE lockmode);
439 : static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
440 : HeapTuple contuple, bool recurse, bool recursing,
441 : LOCKMODE lockmode);
442 : static int transformColumnNameList(Oid relId, List *colList,
443 : int16 *attnums, Oid *atttypids, Oid *attcollids);
444 : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
445 : List **attnamelist,
446 : int16 *attnums, Oid *atttypids, Oid *attcollids,
447 : Oid *opclasses, bool *pk_has_without_overlaps);
448 : static Oid transformFkeyCheckAttrs(Relation pkrel,
449 : int numattrs, int16 *attnums,
450 : bool with_period, Oid *opclasses,
451 : bool *pk_has_without_overlaps);
452 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
453 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
454 : Oid *funcid);
455 : static void validateForeignKeyConstraint(char *conname,
456 : Relation rel, Relation pkrel,
457 : Oid pkindOid, Oid constraintOid, bool hasperiod);
458 : static void CheckAlterTableIsSafe(Relation rel);
459 : static void ATController(AlterTableStmt *parsetree,
460 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
461 : AlterTableUtilityContext *context);
462 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
463 : bool recurse, bool recursing, LOCKMODE lockmode,
464 : AlterTableUtilityContext *context);
465 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
466 : AlterTableUtilityContext *context);
467 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
468 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
469 : AlterTableUtilityContext *context);
470 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
471 : Relation rel, AlterTableCmd *cmd,
472 : bool recurse, LOCKMODE lockmode,
473 : AlterTablePass cur_pass,
474 : AlterTableUtilityContext *context);
475 : static void ATRewriteTables(AlterTableStmt *parsetree,
476 : List **wqueue, LOCKMODE lockmode,
477 : AlterTableUtilityContext *context);
478 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
479 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
480 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
481 : static void ATSimpleRecursion(List **wqueue, Relation rel,
482 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
483 : AlterTableUtilityContext *context);
484 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
485 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
486 : LOCKMODE lockmode,
487 : AlterTableUtilityContext *context);
488 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
489 : DropBehavior behavior);
490 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
491 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
492 : AlterTableUtilityContext *context);
493 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
494 : Relation rel, AlterTableCmd **cmd,
495 : bool recurse, bool recursing,
496 : LOCKMODE lockmode, AlterTablePass cur_pass,
497 : AlterTableUtilityContext *context);
498 : static bool check_for_column_name_collision(Relation rel, const char *colname,
499 : bool if_not_exists);
500 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
501 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
502 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
503 : LOCKMODE lockmode);
504 : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
505 : bool is_valid, bool queue_validation);
506 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
507 : char *conName, char *colName,
508 : bool recurse, bool recursing,
509 : LOCKMODE lockmode);
510 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
511 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
512 : List *testConstraint, List *provenConstraint);
513 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
514 : Node *newDefault, LOCKMODE lockmode);
515 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
516 : Node *newDefault);
517 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
518 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
519 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
520 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
521 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
522 : bool recurse, bool recursing);
523 : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
524 : Node *newExpr, LOCKMODE lockmode);
525 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
526 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
527 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
528 : Node *newValue, LOCKMODE lockmode);
529 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
530 : Node *options, bool isReset, LOCKMODE lockmode);
531 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
532 : Node *newValue, LOCKMODE lockmode);
533 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
534 : AlterTableCmd *cmd, LOCKMODE lockmode,
535 : AlterTableUtilityContext *context);
536 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
537 : DropBehavior behavior,
538 : bool recurse, bool recursing,
539 : bool missing_ok, LOCKMODE lockmode,
540 : ObjectAddresses *addrs);
541 : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
542 : bool recurse, LOCKMODE lockmode,
543 : AlterTableUtilityContext *context);
544 : static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
545 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
546 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
547 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
548 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
549 : static ObjectAddress ATExecAddConstraint(List **wqueue,
550 : AlteredTableInfo *tab, Relation rel,
551 : Constraint *newConstraint, bool recurse, bool is_readd,
552 : LOCKMODE lockmode);
553 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
554 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
555 : IndexStmt *stmt, LOCKMODE lockmode);
556 : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
557 : AlteredTableInfo *tab, Relation rel,
558 : Constraint *constr,
559 : bool recurse, bool recursing, bool is_readd,
560 : LOCKMODE lockmode);
561 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
562 : Relation rel, Constraint *fkconstraint,
563 : bool recurse, bool recursing,
564 : LOCKMODE lockmode);
565 : static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
566 : int numfksetcols, int16 *fksetcolsattnums,
567 : List *fksetcols);
568 : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
569 : char *constraintname,
570 : Constraint *fkconstraint, Relation rel,
571 : Relation pkrel, Oid indexOid,
572 : Oid parentConstr,
573 : int numfks, int16 *pkattnum, int16 *fkattnum,
574 : Oid *pfeqoperators, Oid *ppeqoperators,
575 : Oid *ffeqoperators, int numfkdelsetcols,
576 : int16 *fkdelsetcols, bool is_internal,
577 : bool with_period);
578 : static void addFkRecurseReferenced(Constraint *fkconstraint,
579 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
580 : int numfks, int16 *pkattnum, int16 *fkattnum,
581 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
582 : int numfkdelsetcols, int16 *fkdelsetcols,
583 : bool old_check_ok,
584 : Oid parentDelTrigger, Oid parentUpdTrigger,
585 : bool with_period);
586 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
587 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
588 : int numfks, int16 *pkattnum, int16 *fkattnum,
589 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
590 : int numfkdelsetcols, int16 *fkdelsetcols,
591 : bool old_check_ok, LOCKMODE lockmode,
592 : Oid parentInsTrigger, Oid parentUpdTrigger,
593 : bool with_period);
594 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
595 : Relation partitionRel);
596 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
597 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
598 : Relation partRel);
599 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
600 : Constraint *fkconstraint, Oid constraintOid,
601 : Oid indexOid,
602 : Oid parentInsTrigger, Oid parentUpdTrigger,
603 : Oid *insertTrigOid, Oid *updateTrigOid);
604 : static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
605 : Constraint *fkconstraint, Oid constraintOid,
606 : Oid indexOid,
607 : Oid parentDelTrigger, Oid parentUpdTrigger,
608 : Oid *deleteTrigOid, Oid *updateTrigOid);
609 : static bool tryAttachPartitionForeignKey(List **wqueue,
610 : ForeignKeyCacheInfo *fk,
611 : Relation partition,
612 : Oid parentConstrOid, int numfks,
613 : AttrNumber *mapped_conkey, AttrNumber *confkey,
614 : Oid *conpfeqop,
615 : Oid parentInsTrigger,
616 : Oid parentUpdTrigger,
617 : Relation trigrel);
618 : static void AttachPartitionForeignKey(List **wqueue, Relation partition,
619 : Oid partConstrOid, Oid parentConstrOid,
620 : Oid parentInsTrigger, Oid parentUpdTrigger,
621 : Relation trigrel);
622 : static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
623 : Oid conoid, Oid conrelid);
624 : static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
625 : Oid confrelid, Oid conrelid);
626 : static void GetForeignKeyActionTriggers(Relation trigrel,
627 : Oid conoid, Oid confrelid, Oid conrelid,
628 : Oid *deleteTriggerOid,
629 : Oid *updateTriggerOid);
630 : static void GetForeignKeyCheckTriggers(Relation trigrel,
631 : Oid conoid, Oid confrelid, Oid conrelid,
632 : Oid *insertTriggerOid,
633 : Oid *updateTriggerOid);
634 : static void ATExecDropConstraint(Relation rel, const char *constrName,
635 : DropBehavior behavior, bool recurse,
636 : bool missing_ok, LOCKMODE lockmode);
637 : static ObjectAddress dropconstraint_internal(Relation rel,
638 : HeapTuple constraintTup, DropBehavior behavior,
639 : bool recurse, bool recursing,
640 : bool missing_ok, LOCKMODE lockmode);
641 : static void ATPrepAlterColumnType(List **wqueue,
642 : AlteredTableInfo *tab, Relation rel,
643 : bool recurse, bool recursing,
644 : AlterTableCmd *cmd, LOCKMODE lockmode,
645 : AlterTableUtilityContext *context);
646 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
647 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
648 : AlterTableCmd *cmd, LOCKMODE lockmode);
649 : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
650 : Relation rel, AttrNumber attnum, const char *colName);
651 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
652 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
653 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
654 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
655 : LOCKMODE lockmode);
656 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
657 : char *cmd, List **wqueue, LOCKMODE lockmode,
658 : bool rewrite);
659 : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
660 : Oid objid, Relation rel, List *domname,
661 : const char *conname);
662 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
663 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
664 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
665 : List *options, LOCKMODE lockmode);
666 : static void change_owner_fix_column_acls(Oid relationOid,
667 : Oid oldOwnerId, Oid newOwnerId);
668 : static void change_owner_recurse_to_sequences(Oid relationOid,
669 : Oid newOwnerId, LOCKMODE lockmode);
670 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
671 : LOCKMODE lockmode);
672 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
673 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
674 : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
675 : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
676 : bool toLogged);
677 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
678 : const char *tablespacename, LOCKMODE lockmode);
679 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
680 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
681 : static void ATExecSetRelOptions(Relation rel, List *defList,
682 : AlterTableType operation,
683 : LOCKMODE lockmode);
684 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
685 : char fires_when, bool skip_system, bool recurse,
686 : LOCKMODE lockmode);
687 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
688 : char fires_when, LOCKMODE lockmode);
689 : static void ATPrepAddInherit(Relation child_rel);
690 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
691 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
692 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
693 : DependencyType deptype);
694 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
695 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
696 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
697 : static void ATExecGenericOptions(Relation rel, List *options);
698 : static void ATExecSetRowSecurity(Relation rel, bool rls);
699 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
700 : static ObjectAddress ATExecSetCompression(Relation rel,
701 : const char *column, Node *newValue, LOCKMODE lockmode);
702 :
703 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
704 : static const char *storage_name(char c);
705 :
706 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
707 : Oid oldRelOid, void *arg);
708 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
709 : Oid oldrelid, void *arg);
710 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
711 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
712 : List **partexprs, Oid *partopclass, Oid *partcollation,
713 : PartitionStrategy strategy);
714 : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
715 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
716 : bool expect_detached);
717 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
718 : PartitionCmd *cmd,
719 : AlterTableUtilityContext *context);
720 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
721 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
722 : List *partConstraint,
723 : bool validate_default);
724 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
725 : static void DropClonedTriggersFromPartition(Oid partitionId);
726 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
727 : Relation rel, RangeVar *name,
728 : bool concurrent);
729 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
730 : bool concurrent, Oid defaultPartOid);
731 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
732 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
733 : RangeVar *name);
734 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
735 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
736 : Relation partitionTbl);
737 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
738 : static List *GetParentedForeignKeyRefs(Relation partition);
739 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
740 : static char GetAttributeCompression(Oid atttypid, const char *compression);
741 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
742 :
743 : static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
744 : PartitionCmd *cmd, AlterTableUtilityContext *context);
745 : static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
746 : Relation rel, PartitionCmd *cmd,
747 : AlterTableUtilityContext *context);
748 :
749 : /* ----------------------------------------------------------------
750 : * DefineRelation
751 : * Creates a new relation.
752 : *
753 : * stmt carries parsetree information from an ordinary CREATE TABLE statement.
754 : * The other arguments are used to extend the behavior for other cases:
755 : * relkind: relkind to assign to the new relation
756 : * ownerId: if not InvalidOid, use this as the new relation's owner.
757 : * typaddress: if not null, it's set to the pg_type entry's address.
758 : * queryString: for error reporting
759 : *
760 : * Note that permissions checks are done against current user regardless of
761 : * ownerId. A nonzero ownerId is used when someone is creating a relation
762 : * "on behalf of" someone else, so we still want to see that the current user
763 : * has permissions to do it.
764 : *
765 : * If successful, returns the address of the new relation.
766 : * ----------------------------------------------------------------
767 : */
768 : ObjectAddress
769 32856 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
770 : ObjectAddress *typaddress, const char *queryString)
771 : {
772 : char relname[NAMEDATALEN];
773 : Oid namespaceId;
774 : Oid relationId;
775 : Oid tablespaceId;
776 : Relation rel;
777 : TupleDesc descriptor;
778 : List *inheritOids;
779 : List *old_constraints;
780 : List *old_notnulls;
781 : List *rawDefaults;
782 : List *cookedDefaults;
783 : List *nncols;
784 32856 : List *connames = NIL;
785 : Datum reloptions;
786 : ListCell *listptr;
787 : AttrNumber attnum;
788 : bool partitioned;
789 32856 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
790 : Oid ofTypeId;
791 : ObjectAddress address;
792 : LOCKMODE parentLockmode;
793 32856 : Oid accessMethodId = InvalidOid;
794 :
795 : /*
796 : * Truncate relname to appropriate length (probably a waste of time, as
797 : * parser should have done this already).
798 : */
799 32856 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
800 :
801 : /*
802 : * Check consistency of arguments
803 : */
804 32856 : if (stmt->oncommit != ONCOMMIT_NOOP
805 97 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
806 6 : ereport(ERROR,
807 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
808 : errmsg("ON COMMIT can only be used on temporary tables")));
809 :
810 32850 : if (stmt->partspec != NULL)
811 : {
812 2793 : if (relkind != RELKIND_RELATION)
813 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
814 :
815 2793 : relkind = RELKIND_PARTITIONED_TABLE;
816 2793 : partitioned = true;
817 : }
818 : else
819 30057 : partitioned = false;
820 :
821 32850 : if (relkind == RELKIND_PARTITIONED_TABLE &&
822 2793 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
823 3 : ereport(ERROR,
824 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
825 : errmsg("partitioned tables cannot be unlogged")));
826 :
827 : /*
828 : * Look up the namespace in which we are supposed to create the relation,
829 : * check we have permission to create there, lock it against concurrent
830 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
831 : * namespace is selected.
832 : */
833 : namespaceId =
834 32847 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
835 :
836 : /*
837 : * Security check: disallow creating temp tables from security-restricted
838 : * code. This is needed because calling code might not expect untrusted
839 : * tables to appear in pg_temp at the front of its search path.
840 : */
841 32847 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
842 1696 : && InSecurityRestrictedOperation())
843 0 : ereport(ERROR,
844 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
845 : errmsg("cannot create temporary table within security-restricted operation")));
846 :
847 : /*
848 : * Determine the lockmode to use when scanning parents. A self-exclusive
849 : * lock is needed here.
850 : *
851 : * For regular inheritance, if two backends attempt to add children to the
852 : * same parent simultaneously, and that parent has no pre-existing
853 : * children, then both will attempt to update the parent's relhassubclass
854 : * field, leading to a "tuple concurrently updated" error. Also, this
855 : * interlocks against a concurrent ANALYZE on the parent table, which
856 : * might otherwise be attempting to clear the parent's relhassubclass
857 : * field, if its previous children were recently dropped.
858 : *
859 : * If the child table is a partition, then we instead grab an exclusive
860 : * lock on the parent because its partition descriptor will be changed by
861 : * addition of the new partition.
862 : */
863 32847 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
864 : ShareUpdateExclusiveLock);
865 :
866 : /* Determine the list of OIDs of the parents. */
867 32847 : inheritOids = NIL;
868 38691 : foreach(listptr, stmt->inhRelations)
869 : {
870 5844 : RangeVar *rv = (RangeVar *) lfirst(listptr);
871 : Oid parentOid;
872 :
873 5844 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
874 :
875 : /*
876 : * Reject duplications in the list of parents.
877 : */
878 5844 : if (list_member_oid(inheritOids, parentOid))
879 0 : ereport(ERROR,
880 : (errcode(ERRCODE_DUPLICATE_TABLE),
881 : errmsg("relation \"%s\" would be inherited from more than once",
882 : get_rel_name(parentOid))));
883 :
884 5844 : inheritOids = lappend_oid(inheritOids, parentOid);
885 : }
886 :
887 : /*
888 : * Select tablespace to use: an explicitly indicated one, or (in the case
889 : * of a partitioned table) the parent's, if it has one.
890 : */
891 32847 : if (stmt->tablespacename)
892 : {
893 70 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
894 :
895 67 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
896 3 : ereport(ERROR,
897 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
898 : errmsg("cannot specify default tablespace for partitioned relations")));
899 : }
900 32777 : else if (stmt->partbound)
901 : {
902 : Assert(list_length(inheritOids) == 1);
903 4577 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
904 : }
905 : else
906 28200 : tablespaceId = InvalidOid;
907 :
908 : /* still nothing? use the default */
909 32841 : if (!OidIsValid(tablespaceId))
910 32757 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
911 : partitioned);
912 :
913 : /* Check permissions except when using database's default */
914 32838 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
915 : {
916 : AclResult aclresult;
917 :
918 97 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
919 : ACL_CREATE);
920 97 : if (aclresult != ACLCHECK_OK)
921 3 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
922 3 : get_tablespace_name(tablespaceId));
923 : }
924 :
925 : /* In all cases disallow placing user relations in pg_global */
926 32835 : if (tablespaceId == GLOBALTABLESPACE_OID)
927 9 : ereport(ERROR,
928 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
929 : errmsg("only shared relations can be placed in pg_global tablespace")));
930 :
931 : /* Identify user ID that will own the table */
932 32826 : if (!OidIsValid(ownerId))
933 32703 : ownerId = GetUserId();
934 :
935 : /*
936 : * Parse and validate reloptions, if any.
937 : */
938 32826 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
939 : true, false);
940 :
941 32817 : switch (relkind)
942 : {
943 8547 : case RELKIND_VIEW:
944 8547 : (void) view_reloptions(reloptions, true);
945 8538 : break;
946 2781 : case RELKIND_PARTITIONED_TABLE:
947 2781 : (void) partitioned_table_reloptions(reloptions, true);
948 2778 : break;
949 21489 : default:
950 21489 : (void) heap_reloptions(relkind, reloptions, true);
951 : }
952 :
953 32757 : if (stmt->ofTypename)
954 : {
955 : AclResult aclresult;
956 :
957 43 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
958 :
959 43 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
960 43 : if (aclresult != ACLCHECK_OK)
961 3 : aclcheck_error_type(aclresult, ofTypeId);
962 : }
963 : else
964 32714 : ofTypeId = InvalidOid;
965 :
966 : /*
967 : * Look up inheritance ancestors and generate relation schema, including
968 : * inherited attributes. (Note that stmt->tableElts is destructively
969 : * modified by MergeAttributes.)
970 : */
971 32634 : stmt->tableElts =
972 32754 : MergeAttributes(stmt->tableElts, inheritOids,
973 32754 : stmt->relation->relpersistence,
974 32754 : stmt->partbound != NULL,
975 : &old_constraints, &old_notnulls);
976 :
977 : /*
978 : * Create a tuple descriptor from the relation schema. Note that this
979 : * deals with column names, types, and in-descriptor NOT NULL flags, but
980 : * not default values, NOT NULL or CHECK constraints; we handle those
981 : * below.
982 : */
983 32634 : descriptor = BuildDescForRelation(stmt->tableElts);
984 :
985 : /*
986 : * Find columns with default values and prepare for insertion of the
987 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
988 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
989 : * while raw defaults go into a list of RawColumnDefault structs that will
990 : * be processed by AddRelationNewConstraints. (We can't deal with raw
991 : * expressions until we can do transformExpr.)
992 : */
993 32610 : rawDefaults = NIL;
994 32610 : cookedDefaults = NIL;
995 32610 : attnum = 0;
996 :
997 165224 : foreach(listptr, stmt->tableElts)
998 : {
999 132614 : ColumnDef *colDef = lfirst(listptr);
1000 :
1001 132614 : attnum++;
1002 132614 : if (colDef->raw_default != NULL)
1003 : {
1004 : RawColumnDefault *rawEnt;
1005 :
1006 : Assert(colDef->cooked_default == NULL);
1007 :
1008 1714 : rawEnt = palloc_object(RawColumnDefault);
1009 1714 : rawEnt->attnum = attnum;
1010 1714 : rawEnt->raw_default = colDef->raw_default;
1011 1714 : rawEnt->generated = colDef->generated;
1012 1714 : rawDefaults = lappend(rawDefaults, rawEnt);
1013 : }
1014 130900 : else if (colDef->cooked_default != NULL)
1015 : {
1016 : CookedConstraint *cooked;
1017 :
1018 271 : cooked = palloc_object(CookedConstraint);
1019 271 : cooked->contype = CONSTR_DEFAULT;
1020 271 : cooked->conoid = InvalidOid; /* until created */
1021 271 : cooked->name = NULL;
1022 271 : cooked->attnum = attnum;
1023 271 : cooked->expr = colDef->cooked_default;
1024 271 : cooked->is_enforced = true;
1025 271 : cooked->skip_validation = false;
1026 271 : cooked->is_local = true; /* not used for defaults */
1027 271 : cooked->inhcount = 0; /* ditto */
1028 271 : cooked->is_no_inherit = false;
1029 271 : cookedDefaults = lappend(cookedDefaults, cooked);
1030 : }
1031 : }
1032 :
1033 : /*
1034 : * For relations with table AM and partitioned tables, select access
1035 : * method to use: an explicitly indicated one, or (in the case of a
1036 : * partitioned table) the parent's, if it has one.
1037 : */
1038 32610 : if (stmt->accessMethod != NULL)
1039 : {
1040 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1041 67 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1042 : }
1043 32543 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1044 : {
1045 20538 : if (stmt->partbound)
1046 : {
1047 : Assert(list_length(inheritOids) == 1);
1048 4486 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1049 : }
1050 :
1051 20538 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1052 17750 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1053 : }
1054 :
1055 : /*
1056 : * Create the relation. Inherited defaults and CHECK constraints are
1057 : * passed in for immediate handling --- since they don't need parsing,
1058 : * they can be stored immediately.
1059 : */
1060 32601 : relationId = heap_create_with_catalog(relname,
1061 : namespaceId,
1062 : tablespaceId,
1063 : InvalidOid,
1064 : InvalidOid,
1065 : ofTypeId,
1066 : ownerId,
1067 : accessMethodId,
1068 : descriptor,
1069 : list_concat(cookedDefaults,
1070 : old_constraints),
1071 : relkind,
1072 32601 : stmt->relation->relpersistence,
1073 : false,
1074 : false,
1075 : stmt->oncommit,
1076 : reloptions,
1077 : true,
1078 : allowSystemTableMods,
1079 : false,
1080 : InvalidOid,
1081 : typaddress);
1082 :
1083 : /*
1084 : * We must bump the command counter to make the newly-created relation
1085 : * tuple visible for opening.
1086 : */
1087 32577 : CommandCounterIncrement();
1088 :
1089 : /*
1090 : * Open the new relation and acquire exclusive lock on it. This isn't
1091 : * really necessary for locking out other backends (since they can't see
1092 : * the new rel anyway until we commit), but it keeps the lock manager from
1093 : * complaining about deadlock risks.
1094 : */
1095 32577 : rel = relation_open(relationId, AccessExclusiveLock);
1096 :
1097 : /*
1098 : * Now add any newly specified column default and generation expressions
1099 : * to the new relation. These are passed to us in the form of raw
1100 : * parsetrees; we need to transform them to executable expression trees
1101 : * before they can be added. The most convenient way to do that is to
1102 : * apply the parser's transformExpr routine, but transformExpr doesn't
1103 : * work unless we have a pre-existing relation. So, the transformation has
1104 : * to be postponed to this final step of CREATE TABLE.
1105 : *
1106 : * This needs to be before processing the partitioning clauses because
1107 : * those could refer to generated columns.
1108 : */
1109 32577 : if (rawDefaults)
1110 1453 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1111 : true, true, false, queryString);
1112 :
1113 : /*
1114 : * Make column generation expressions visible for use by partitioning.
1115 : */
1116 32481 : CommandCounterIncrement();
1117 :
1118 : /* Process and store partition bound, if any. */
1119 32481 : if (stmt->partbound)
1120 : {
1121 : PartitionBoundSpec *bound;
1122 : ParseState *pstate;
1123 4538 : Oid parentId = linitial_oid(inheritOids),
1124 : defaultPartOid;
1125 : Relation parent,
1126 4538 : defaultRel = NULL;
1127 : ParseNamespaceItem *nsitem;
1128 :
1129 : /* Already have strong enough lock on the parent */
1130 4538 : parent = table_open(parentId, NoLock);
1131 :
1132 : /*
1133 : * We are going to try to validate the partition bound specification
1134 : * against the partition key of parentRel, so it better have one.
1135 : */
1136 4538 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1137 9 : ereport(ERROR,
1138 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1139 : errmsg("\"%s\" is not partitioned",
1140 : RelationGetRelationName(parent))));
1141 :
1142 : /*
1143 : * The partition constraint of the default partition depends on the
1144 : * partition bounds of every other partition. It is possible that
1145 : * another backend might be about to execute a query on the default
1146 : * partition table, and that the query relies on previously cached
1147 : * default partition constraints. We must therefore take a table lock
1148 : * strong enough to prevent all queries on the default partition from
1149 : * proceeding until we commit and send out a shared-cache-inval notice
1150 : * that will make them update their index lists.
1151 : *
1152 : * Order of locking: The relation being added won't be visible to
1153 : * other backends until it is committed, hence here in
1154 : * DefineRelation() the order of locking the default partition and the
1155 : * relation being added does not matter. But at all other places we
1156 : * need to lock the default relation before we lock the relation being
1157 : * added or removed i.e. we should take the lock in same order at all
1158 : * the places such that lock parent, lock default partition and then
1159 : * lock the partition so as to avoid a deadlock.
1160 : */
1161 : defaultPartOid =
1162 4529 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1163 : true));
1164 4529 : if (OidIsValid(defaultPartOid))
1165 189 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1166 :
1167 : /* Transform the bound values */
1168 4529 : pstate = make_parsestate(NULL);
1169 4529 : pstate->p_sourcetext = queryString;
1170 :
1171 : /*
1172 : * Add an nsitem containing this relation, so that transformExpr
1173 : * called on partition bound expressions is able to report errors
1174 : * using a proper context.
1175 : */
1176 4529 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1177 : NULL, false, false);
1178 4529 : addNSItemToQuery(pstate, nsitem, false, true, true);
1179 :
1180 4529 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1181 :
1182 : /*
1183 : * Check first that the new partition's bound is valid and does not
1184 : * overlap with any of existing partitions of the parent.
1185 : */
1186 4427 : check_new_partition_bound(relname, parent, bound, pstate);
1187 :
1188 : /*
1189 : * If the default partition exists, its partition constraints will
1190 : * change after the addition of this new partition such that it won't
1191 : * allow any row that qualifies for this new partition. So, check that
1192 : * the existing data in the default partition satisfies the constraint
1193 : * as it will exist after adding this partition.
1194 : */
1195 4370 : if (OidIsValid(defaultPartOid))
1196 : {
1197 174 : check_default_partition_contents(parent, defaultRel, bound);
1198 : /* Keep the lock until commit. */
1199 165 : table_close(defaultRel, NoLock);
1200 : }
1201 :
1202 : /* Update the pg_class entry. */
1203 4361 : StorePartitionBound(rel, parent, bound);
1204 :
1205 4361 : table_close(parent, NoLock);
1206 : }
1207 :
1208 : /* Store inheritance information for new rel. */
1209 32304 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1210 :
1211 : /*
1212 : * Process the partitioning specification (if any) and store the partition
1213 : * key information into the catalog.
1214 : */
1215 32304 : if (partitioned)
1216 : {
1217 : ParseState *pstate;
1218 : int partnatts;
1219 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1220 : Oid partopclass[PARTITION_MAX_KEYS];
1221 : Oid partcollation[PARTITION_MAX_KEYS];
1222 2778 : List *partexprs = NIL;
1223 :
1224 2778 : pstate = make_parsestate(NULL);
1225 2778 : pstate->p_sourcetext = queryString;
1226 :
1227 2778 : partnatts = list_length(stmt->partspec->partParams);
1228 :
1229 : /* Protect fixed-size arrays here and in executor */
1230 2778 : if (partnatts > PARTITION_MAX_KEYS)
1231 0 : ereport(ERROR,
1232 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1233 : errmsg("cannot partition using more than %d columns",
1234 : PARTITION_MAX_KEYS)));
1235 :
1236 : /*
1237 : * We need to transform the raw parsetrees corresponding to partition
1238 : * expressions into executable expression trees. Like column defaults
1239 : * and CHECK constraints, we could not have done the transformation
1240 : * earlier.
1241 : */
1242 2778 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1243 :
1244 2763 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1245 : partattrs, &partexprs, partopclass,
1246 2763 : partcollation, stmt->partspec->strategy);
1247 :
1248 2697 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1249 : partexprs,
1250 : partopclass, partcollation);
1251 :
1252 : /* make it all visible */
1253 2697 : CommandCounterIncrement();
1254 : }
1255 :
1256 : /*
1257 : * If we're creating a partition, create now all the indexes, triggers,
1258 : * FKs defined in the parent.
1259 : *
1260 : * We can't do it earlier, because DefineIndex wants to know the partition
1261 : * key which we just stored.
1262 : */
1263 32223 : if (stmt->partbound)
1264 : {
1265 4358 : Oid parentId = linitial_oid(inheritOids);
1266 : Relation parent;
1267 : List *idxlist;
1268 : ListCell *cell;
1269 :
1270 : /* Already have strong enough lock on the parent */
1271 4358 : parent = table_open(parentId, NoLock);
1272 4358 : idxlist = RelationGetIndexList(parent);
1273 :
1274 : /*
1275 : * For each index in the parent table, create one in the partition
1276 : */
1277 5192 : foreach(cell, idxlist)
1278 : {
1279 843 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1280 : AttrMap *attmap;
1281 : IndexStmt *idxstmt;
1282 : Oid constraintOid;
1283 :
1284 843 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1285 : {
1286 18 : if (idxRel->rd_index->indisunique)
1287 6 : ereport(ERROR,
1288 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1289 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1290 : RelationGetRelationName(parent)),
1291 : errdetail("Table \"%s\" contains indexes that are unique.",
1292 : RelationGetRelationName(parent))));
1293 : else
1294 : {
1295 12 : index_close(idxRel, AccessShareLock);
1296 12 : continue;
1297 : }
1298 : }
1299 :
1300 825 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1301 : RelationGetDescr(parent),
1302 : false);
1303 : idxstmt =
1304 825 : generateClonedIndexStmt(NULL, idxRel,
1305 : attmap, &constraintOid);
1306 825 : DefineIndex(NULL,
1307 : RelationGetRelid(rel),
1308 : idxstmt,
1309 : InvalidOid,
1310 : RelationGetRelid(idxRel),
1311 : constraintOid,
1312 : -1,
1313 : false, false, false, false, false);
1314 :
1315 822 : index_close(idxRel, AccessShareLock);
1316 : }
1317 :
1318 4349 : list_free(idxlist);
1319 :
1320 : /*
1321 : * If there are any row-level triggers, clone them to the new
1322 : * partition.
1323 : */
1324 4349 : if (parent->trigdesc != NULL)
1325 237 : CloneRowTriggersToPartition(parent, rel);
1326 :
1327 : /*
1328 : * And foreign keys too. Note that because we're freshly creating the
1329 : * table, there is no need to verify these new constraints.
1330 : */
1331 4349 : CloneForeignKeyConstraints(NULL, parent, rel);
1332 :
1333 4349 : table_close(parent, NoLock);
1334 : }
1335 :
1336 : /*
1337 : * Now add any newly specified CHECK constraints to the new relation. Same
1338 : * as for defaults above, but these need to come after partitioning is set
1339 : * up. We save the constraint names that were used, to avoid dupes below.
1340 : */
1341 32214 : if (stmt->constraints)
1342 : {
1343 : List *conlist;
1344 :
1345 379 : conlist = AddRelationNewConstraints(rel, NIL, stmt->constraints,
1346 : true, true, false, queryString);
1347 1142 : foreach_ptr(CookedConstraint, cons, conlist)
1348 : {
1349 414 : if (cons->name != NULL)
1350 414 : connames = lappend(connames, cons->name);
1351 : }
1352 : }
1353 :
1354 : /*
1355 : * Finally, merge the not-null constraints that are declared directly with
1356 : * those that come from parent relations (making sure to count inheritance
1357 : * appropriately for each), create them, and set the attnotnull flag on
1358 : * columns that don't yet have it.
1359 : */
1360 32199 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1361 : old_notnulls, connames);
1362 72298 : foreach_int(attrnum, nncols)
1363 7978 : set_attnotnull(NULL, rel, attrnum, true, false);
1364 :
1365 32160 : ObjectAddressSet(address, RelationRelationId, relationId);
1366 :
1367 : /*
1368 : * Clean up. We keep lock on new relation (although it shouldn't be
1369 : * visible to anyone else anyway, until commit).
1370 : */
1371 32160 : relation_close(rel, NoLock);
1372 :
1373 32160 : return address;
1374 : }
1375 :
1376 : /*
1377 : * BuildDescForRelation
1378 : *
1379 : * Given a list of ColumnDef nodes, build a TupleDesc.
1380 : *
1381 : * Note: This is only for the limited purpose of table and view creation. Not
1382 : * everything is filled in. A real tuple descriptor should be obtained from
1383 : * the relcache.
1384 : */
1385 : TupleDesc
1386 34473 : BuildDescForRelation(const List *columns)
1387 : {
1388 : int natts;
1389 : AttrNumber attnum;
1390 : ListCell *l;
1391 : TupleDesc desc;
1392 : char *attname;
1393 : Oid atttypid;
1394 : int32 atttypmod;
1395 : Oid attcollation;
1396 : int attdim;
1397 :
1398 : /*
1399 : * allocate a new tuple descriptor
1400 : */
1401 34473 : natts = list_length(columns);
1402 34473 : desc = CreateTemplateTupleDesc(natts);
1403 :
1404 34473 : attnum = 0;
1405 :
1406 169592 : foreach(l, columns)
1407 : {
1408 135149 : ColumnDef *entry = lfirst(l);
1409 : AclResult aclresult;
1410 : Form_pg_attribute att;
1411 :
1412 : /*
1413 : * for each entry in the list, get the name and type information from
1414 : * the list and have TupleDescInitEntry fill in the attribute
1415 : * information we need.
1416 : */
1417 135149 : attnum++;
1418 :
1419 135149 : attname = entry->colname;
1420 135149 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1421 :
1422 135149 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1423 135149 : if (aclresult != ACLCHECK_OK)
1424 21 : aclcheck_error_type(aclresult, atttypid);
1425 :
1426 135128 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1427 135128 : attdim = list_length(entry->typeName->arrayBounds);
1428 135128 : if (attdim > PG_INT16_MAX)
1429 0 : ereport(ERROR,
1430 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1431 : errmsg("too many array dimensions"));
1432 :
1433 135128 : if (entry->typeName->setof)
1434 0 : ereport(ERROR,
1435 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1436 : errmsg("column \"%s\" cannot be declared SETOF",
1437 : attname)));
1438 :
1439 135128 : TupleDescInitEntry(desc, attnum, attname,
1440 : atttypid, atttypmod, attdim);
1441 135128 : att = TupleDescAttr(desc, attnum - 1);
1442 :
1443 : /* Override TupleDescInitEntry's settings as requested */
1444 135128 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1445 :
1446 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1447 135128 : att->attnotnull = entry->is_not_null;
1448 135128 : att->attislocal = entry->is_local;
1449 135128 : att->attinhcount = entry->inhcount;
1450 135128 : att->attidentity = entry->identity;
1451 135128 : att->attgenerated = entry->generated;
1452 135128 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1453 135122 : if (entry->storage)
1454 12544 : att->attstorage = entry->storage;
1455 122578 : else if (entry->storage_name)
1456 29 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1457 :
1458 135119 : populate_compact_attribute(desc, attnum - 1);
1459 : }
1460 :
1461 34443 : return desc;
1462 : }
1463 :
1464 : /*
1465 : * Emit the right error or warning message for a "DROP" command issued on a
1466 : * non-existent relation
1467 : */
1468 : static void
1469 566 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1470 : {
1471 : const struct dropmsgstrings *rentry;
1472 :
1473 626 : if (rel->schemaname != NULL &&
1474 60 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1475 : {
1476 21 : if (!missing_ok)
1477 : {
1478 0 : ereport(ERROR,
1479 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1480 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1481 : }
1482 : else
1483 : {
1484 21 : ereport(NOTICE,
1485 : (errmsg("schema \"%s\" does not exist, skipping",
1486 : rel->schemaname)));
1487 : }
1488 21 : return;
1489 : }
1490 :
1491 705 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1492 : {
1493 705 : if (rentry->kind == rightkind)
1494 : {
1495 545 : if (!missing_ok)
1496 : {
1497 69 : ereport(ERROR,
1498 : (errcode(rentry->nonexistent_code),
1499 : errmsg(rentry->nonexistent_msg, rel->relname)));
1500 : }
1501 : else
1502 : {
1503 476 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1504 476 : break;
1505 : }
1506 : }
1507 : }
1508 :
1509 : Assert(rentry->kind != '\0'); /* Should be impossible */
1510 : }
1511 :
1512 : /*
1513 : * Emit the right error message for a "DROP" command issued on a
1514 : * relation of the wrong type
1515 : */
1516 : static void
1517 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1518 : {
1519 : const struct dropmsgstrings *rentry;
1520 : const struct dropmsgstrings *wentry;
1521 :
1522 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1523 0 : if (rentry->kind == rightkind)
1524 0 : break;
1525 : Assert(rentry->kind != '\0');
1526 :
1527 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1528 0 : if (wentry->kind == wrongkind)
1529 0 : break;
1530 : /* wrongkind could be something we don't have in our table... */
1531 :
1532 0 : ereport(ERROR,
1533 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1534 : errmsg(rentry->nota_msg, relname),
1535 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1536 : }
1537 :
1538 : /*
1539 : * RemoveRelations
1540 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1541 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1542 : */
1543 : void
1544 9138 : RemoveRelations(DropStmt *drop)
1545 : {
1546 : ObjectAddresses *objects;
1547 : char relkind;
1548 : ListCell *cell;
1549 9138 : int flags = 0;
1550 9138 : LOCKMODE lockmode = AccessExclusiveLock;
1551 :
1552 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1553 9138 : if (drop->concurrent)
1554 : {
1555 : /*
1556 : * Note that for temporary relations this lock may get upgraded later
1557 : * on, but as no other session can access a temporary relation, this
1558 : * is actually fine.
1559 : */
1560 93 : lockmode = ShareUpdateExclusiveLock;
1561 : Assert(drop->removeType == OBJECT_INDEX);
1562 93 : if (list_length(drop->objects) != 1)
1563 3 : ereport(ERROR,
1564 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1565 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1566 90 : if (drop->behavior == DROP_CASCADE)
1567 0 : ereport(ERROR,
1568 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1569 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1570 : }
1571 :
1572 : /*
1573 : * First we identify all the relations, then we delete them in a single
1574 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1575 : * RESTRICT errors if one of the relations depends on another.
1576 : */
1577 :
1578 : /* Determine required relkind */
1579 9135 : switch (drop->removeType)
1580 : {
1581 7986 : case OBJECT_TABLE:
1582 7986 : relkind = RELKIND_RELATION;
1583 7986 : break;
1584 :
1585 434 : case OBJECT_INDEX:
1586 434 : relkind = RELKIND_INDEX;
1587 434 : break;
1588 :
1589 89 : case OBJECT_SEQUENCE:
1590 89 : relkind = RELKIND_SEQUENCE;
1591 89 : break;
1592 :
1593 481 : case OBJECT_VIEW:
1594 481 : relkind = RELKIND_VIEW;
1595 481 : break;
1596 :
1597 63 : case OBJECT_MATVIEW:
1598 63 : relkind = RELKIND_MATVIEW;
1599 63 : break;
1600 :
1601 82 : case OBJECT_FOREIGN_TABLE:
1602 82 : relkind = RELKIND_FOREIGN_TABLE;
1603 82 : break;
1604 :
1605 0 : default:
1606 0 : elog(ERROR, "unrecognized drop object type: %d",
1607 : (int) drop->removeType);
1608 : relkind = 0; /* keep compiler quiet */
1609 : break;
1610 : }
1611 :
1612 : /* Lock and validate each relation; build a list of object addresses */
1613 9135 : objects = new_object_addresses();
1614 :
1615 20278 : foreach(cell, drop->objects)
1616 : {
1617 11225 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1618 : Oid relOid;
1619 : ObjectAddress obj;
1620 : struct DropRelationCallbackState state;
1621 :
1622 : /*
1623 : * These next few steps are a great deal like relation_openrv, but we
1624 : * don't bother building a relcache entry since we don't need it.
1625 : *
1626 : * Check for shared-cache-inval messages before trying to access the
1627 : * relation. This is needed to cover the case where the name
1628 : * identifies a rel that has been dropped and recreated since the
1629 : * start of our transaction: if we don't flush the old syscache entry,
1630 : * then we'll latch onto that entry and suffer an error later.
1631 : */
1632 11225 : AcceptInvalidationMessages();
1633 :
1634 : /* Look up the appropriate relation using namespace search. */
1635 11225 : state.expected_relkind = relkind;
1636 22450 : state.heap_lockmode = drop->concurrent ?
1637 11225 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1638 : /* We must initialize these fields to show that no locks are held: */
1639 11225 : state.heapOid = InvalidOid;
1640 11225 : state.partParentOid = InvalidOid;
1641 :
1642 11225 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1643 : RangeVarCallbackForDropRelation,
1644 : &state);
1645 :
1646 : /* Not there? */
1647 11215 : if (!OidIsValid(relOid))
1648 : {
1649 566 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1650 497 : continue;
1651 : }
1652 :
1653 : /*
1654 : * Decide if concurrent mode needs to be used here or not. The
1655 : * callback retrieved the rel's persistence for us.
1656 : */
1657 10649 : if (drop->concurrent &&
1658 87 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1659 : {
1660 : Assert(list_length(drop->objects) == 1 &&
1661 : drop->removeType == OBJECT_INDEX);
1662 78 : flags |= PERFORM_DELETION_CONCURRENTLY;
1663 : }
1664 :
1665 : /*
1666 : * Concurrent index drop cannot be used with partitioned indexes,
1667 : * either.
1668 : */
1669 10649 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1670 78 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1671 3 : ereport(ERROR,
1672 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1673 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1674 : rel->relname)));
1675 :
1676 : /*
1677 : * If we're told to drop a partitioned index, we must acquire lock on
1678 : * all the children of its parent partitioned table before proceeding.
1679 : * Otherwise we'd try to lock the child index partitions before their
1680 : * tables, leading to potential deadlock against other sessions that
1681 : * will lock those objects in the other order.
1682 : */
1683 10646 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1684 38 : (void) find_all_inheritors(state.heapOid,
1685 : state.heap_lockmode,
1686 : NULL);
1687 :
1688 : /* OK, we're ready to delete this one */
1689 10646 : obj.classId = RelationRelationId;
1690 10646 : obj.objectId = relOid;
1691 10646 : obj.objectSubId = 0;
1692 :
1693 10646 : add_exact_object_address(&obj, objects);
1694 : }
1695 :
1696 9053 : performMultipleDeletions(objects, drop->behavior, flags);
1697 :
1698 8982 : free_object_addresses(objects);
1699 8982 : }
1700 :
1701 : /*
1702 : * Before acquiring a table lock, check whether we have sufficient rights.
1703 : * In the case of DROP INDEX, also try to lock the table before the index.
1704 : * Also, if the table to be dropped is a partition, we try to lock the parent
1705 : * first.
1706 : */
1707 : static void
1708 11380 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1709 : void *arg)
1710 : {
1711 : HeapTuple tuple;
1712 : struct DropRelationCallbackState *state;
1713 : char expected_relkind;
1714 : bool is_partition;
1715 : Form_pg_class classform;
1716 : LOCKMODE heap_lockmode;
1717 11380 : bool invalid_system_index = false;
1718 :
1719 11380 : state = (struct DropRelationCallbackState *) arg;
1720 11380 : heap_lockmode = state->heap_lockmode;
1721 :
1722 : /*
1723 : * If we previously locked some other index's heap, and the name we're
1724 : * looking up no longer refers to that relation, release the now-useless
1725 : * lock.
1726 : */
1727 11380 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1728 : {
1729 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1730 0 : state->heapOid = InvalidOid;
1731 : }
1732 :
1733 : /*
1734 : * Similarly, if we previously locked some other partition's heap, and the
1735 : * name we're looking up no longer refers to that relation, release the
1736 : * now-useless lock.
1737 : */
1738 11380 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1739 : {
1740 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1741 0 : state->partParentOid = InvalidOid;
1742 : }
1743 :
1744 : /* Didn't find a relation, so no need for locking or permission checks. */
1745 11380 : if (!OidIsValid(relOid))
1746 571 : return;
1747 :
1748 10809 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1749 10809 : if (!HeapTupleIsValid(tuple))
1750 0 : return; /* concurrently dropped, so nothing to do */
1751 10809 : classform = (Form_pg_class) GETSTRUCT(tuple);
1752 10809 : is_partition = classform->relispartition;
1753 :
1754 : /* Pass back some data to save lookups in RemoveRelations */
1755 10809 : state->actual_relkind = classform->relkind;
1756 10809 : state->actual_relpersistence = classform->relpersistence;
1757 :
1758 : /*
1759 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1760 : * but RemoveRelations() can only pass one relkind for a given relation.
1761 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1762 : * That means we must be careful before giving the wrong type error when
1763 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1764 : * exists with indexes.
1765 : */
1766 10809 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1767 1711 : expected_relkind = RELKIND_RELATION;
1768 9098 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1769 43 : expected_relkind = RELKIND_INDEX;
1770 : else
1771 9055 : expected_relkind = classform->relkind;
1772 :
1773 10809 : if (state->expected_relkind != expected_relkind)
1774 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1775 0 : state->expected_relkind);
1776 :
1777 : /* Allow DROP to either table owner or schema owner */
1778 10809 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1779 9 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1780 9 : aclcheck_error(ACLCHECK_NOT_OWNER,
1781 9 : get_relkind_objtype(classform->relkind),
1782 9 : rel->relname);
1783 :
1784 : /*
1785 : * Check the case of a system index that might have been invalidated by a
1786 : * failed concurrent process and allow its drop. For the time being, this
1787 : * only concerns indexes of toast relations that became invalid during a
1788 : * REINDEX CONCURRENTLY process.
1789 : */
1790 10800 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1791 : {
1792 : HeapTuple locTuple;
1793 : Form_pg_index indexform;
1794 : bool indisvalid;
1795 :
1796 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1797 0 : if (!HeapTupleIsValid(locTuple))
1798 : {
1799 0 : ReleaseSysCache(tuple);
1800 0 : return;
1801 : }
1802 :
1803 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1804 0 : indisvalid = indexform->indisvalid;
1805 0 : ReleaseSysCache(locTuple);
1806 :
1807 : /* Mark object as being an invalid index of system catalogs */
1808 0 : if (!indisvalid)
1809 0 : invalid_system_index = true;
1810 : }
1811 :
1812 : /* In the case of an invalid index, it is fine to bypass this check */
1813 10800 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1814 1 : ereport(ERROR,
1815 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1816 : errmsg("permission denied: \"%s\" is a system catalog",
1817 : rel->relname)));
1818 :
1819 10799 : ReleaseSysCache(tuple);
1820 :
1821 : /*
1822 : * In DROP INDEX, attempt to acquire lock on the parent table before
1823 : * locking the index. index_drop() will need this anyway, and since
1824 : * regular queries lock tables before their indexes, we risk deadlock if
1825 : * we do it the other way around. No error if we don't find a pg_index
1826 : * entry, though --- the relation may have been dropped. Note that this
1827 : * code will execute for either plain or partitioned indexes.
1828 : */
1829 10799 : if (expected_relkind == RELKIND_INDEX &&
1830 : relOid != oldRelOid)
1831 : {
1832 428 : state->heapOid = IndexGetRelation(relOid, true);
1833 428 : if (OidIsValid(state->heapOid))
1834 428 : LockRelationOid(state->heapOid, heap_lockmode);
1835 : }
1836 :
1837 : /*
1838 : * Similarly, if the relation is a partition, we must acquire lock on its
1839 : * parent before locking the partition. That's because queries lock the
1840 : * parent before its partitions, so we risk deadlock if we do it the other
1841 : * way around.
1842 : */
1843 10799 : if (is_partition && relOid != oldRelOid)
1844 : {
1845 310 : state->partParentOid = get_partition_parent(relOid, true);
1846 310 : if (OidIsValid(state->partParentOid))
1847 310 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1848 : }
1849 : }
1850 :
1851 : /*
1852 : * ExecuteTruncate
1853 : * Executes a TRUNCATE command.
1854 : *
1855 : * This is a multi-relation truncate. We first open and grab exclusive
1856 : * lock on all relations involved, checking permissions and otherwise
1857 : * verifying that the relation is OK for truncation. Note that if relations
1858 : * are foreign tables, at this stage, we have not yet checked that their
1859 : * foreign data in external data sources are OK for truncation. These are
1860 : * checked when foreign data are actually truncated later. In CASCADE mode,
1861 : * relations having FK references to the targeted relations are automatically
1862 : * added to the group; in RESTRICT mode, we check that all FK references are
1863 : * internal to the group that's being truncated. Finally all the relations
1864 : * are truncated and reindexed.
1865 : */
1866 : void
1867 926 : ExecuteTruncate(TruncateStmt *stmt)
1868 : {
1869 926 : List *rels = NIL;
1870 926 : List *relids = NIL;
1871 926 : List *relids_logged = NIL;
1872 : ListCell *cell;
1873 :
1874 : /*
1875 : * Open, exclusive-lock, and check all the explicitly-specified relations
1876 : */
1877 1958 : foreach(cell, stmt->relations)
1878 : {
1879 1060 : RangeVar *rv = lfirst(cell);
1880 : Relation rel;
1881 1060 : bool recurse = rv->inh;
1882 : Oid myrelid;
1883 1060 : LOCKMODE lockmode = AccessExclusiveLock;
1884 :
1885 1060 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1886 : 0, RangeVarCallbackForTruncate,
1887 : NULL);
1888 :
1889 : /* don't throw error for "TRUNCATE foo, foo" */
1890 1041 : if (list_member_oid(relids, myrelid))
1891 1 : continue;
1892 :
1893 : /* open the relation, we already hold a lock on it */
1894 1040 : rel = table_open(myrelid, NoLock);
1895 :
1896 : /*
1897 : * RangeVarGetRelidExtended() has done most checks with its callback,
1898 : * but other checks with the now-opened Relation remain.
1899 : */
1900 1040 : truncate_check_activity(rel);
1901 :
1902 1037 : rels = lappend(rels, rel);
1903 1037 : relids = lappend_oid(relids, myrelid);
1904 :
1905 : /* Log this relation only if needed for logical decoding */
1906 1037 : if (RelationIsLogicallyLogged(rel))
1907 37 : relids_logged = lappend_oid(relids_logged, myrelid);
1908 :
1909 1037 : if (recurse)
1910 : {
1911 : ListCell *child;
1912 : List *children;
1913 :
1914 1006 : children = find_all_inheritors(myrelid, lockmode, NULL);
1915 :
1916 2934 : foreach(child, children)
1917 : {
1918 1928 : Oid childrelid = lfirst_oid(child);
1919 :
1920 1928 : if (list_member_oid(relids, childrelid))
1921 1006 : continue;
1922 :
1923 : /* find_all_inheritors already got lock */
1924 922 : rel = table_open(childrelid, NoLock);
1925 :
1926 : /*
1927 : * It is possible that the parent table has children that are
1928 : * temp tables of other backends. We cannot safely access
1929 : * such tables (because of buffering issues), and the best
1930 : * thing to do is to silently ignore them. Note that this
1931 : * check is the same as one of the checks done in
1932 : * truncate_check_activity() called below, still it is kept
1933 : * here for simplicity.
1934 : */
1935 922 : if (RELATION_IS_OTHER_TEMP(rel))
1936 : {
1937 4 : table_close(rel, lockmode);
1938 4 : continue;
1939 : }
1940 :
1941 : /*
1942 : * Inherited TRUNCATE commands perform access permission
1943 : * checks on the parent table only. So we skip checking the
1944 : * children's permissions and don't call
1945 : * truncate_check_perms() here.
1946 : */
1947 918 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1948 918 : truncate_check_activity(rel);
1949 :
1950 918 : rels = lappend(rels, rel);
1951 918 : relids = lappend_oid(relids, childrelid);
1952 :
1953 : /* Log this relation only if needed for logical decoding */
1954 918 : if (RelationIsLogicallyLogged(rel))
1955 11 : relids_logged = lappend_oid(relids_logged, childrelid);
1956 : }
1957 : }
1958 31 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1959 6 : ereport(ERROR,
1960 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1961 : errmsg("cannot truncate only a partitioned table"),
1962 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1963 : }
1964 :
1965 898 : ExecuteTruncateGuts(rels, relids, relids_logged,
1966 898 : stmt->behavior, stmt->restart_seqs, false);
1967 :
1968 : /* And close the rels */
1969 2729 : foreach(cell, rels)
1970 : {
1971 1872 : Relation rel = (Relation) lfirst(cell);
1972 :
1973 1872 : table_close(rel, NoLock);
1974 : }
1975 857 : }
1976 :
1977 : /*
1978 : * ExecuteTruncateGuts
1979 : *
1980 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1981 : * command (see above) as well as replication subscribers that execute a
1982 : * replicated TRUNCATE action.
1983 : *
1984 : * explicit_rels is the list of Relations to truncate that the command
1985 : * specified. relids is the list of Oids corresponding to explicit_rels.
1986 : * relids_logged is the list of Oids (a subset of relids) that require
1987 : * WAL-logging. This is all a bit redundant, but the existing callers have
1988 : * this information handy in this form.
1989 : */
1990 : void
1991 918 : ExecuteTruncateGuts(List *explicit_rels,
1992 : List *relids,
1993 : List *relids_logged,
1994 : DropBehavior behavior, bool restart_seqs,
1995 : bool run_as_table_owner)
1996 : {
1997 : List *rels;
1998 918 : List *seq_relids = NIL;
1999 918 : HTAB *ft_htab = NULL;
2000 : EState *estate;
2001 : ResultRelInfo *resultRelInfos;
2002 : ResultRelInfo *resultRelInfo;
2003 : SubTransactionId mySubid;
2004 : ListCell *cell;
2005 : Oid *logrelids;
2006 :
2007 : /*
2008 : * Check the explicitly-specified relations.
2009 : *
2010 : * In CASCADE mode, suck in all referencing relations as well. This
2011 : * requires multiple iterations to find indirectly-dependent relations. At
2012 : * each phase, we need to exclusive-lock new rels before looking for their
2013 : * dependencies, else we might miss something. Also, we check each rel as
2014 : * soon as we open it, to avoid a faux pas such as holding lock for a long
2015 : * time on a rel we have no permissions for.
2016 : */
2017 918 : rels = list_copy(explicit_rels);
2018 918 : if (behavior == DROP_CASCADE)
2019 : {
2020 : for (;;)
2021 20 : {
2022 : List *newrelids;
2023 :
2024 40 : newrelids = heap_truncate_find_FKs(relids);
2025 40 : if (newrelids == NIL)
2026 20 : break; /* nothing else to add */
2027 :
2028 67 : foreach(cell, newrelids)
2029 : {
2030 47 : Oid relid = lfirst_oid(cell);
2031 : Relation rel;
2032 :
2033 47 : rel = table_open(relid, AccessExclusiveLock);
2034 47 : ereport(NOTICE,
2035 : (errmsg("truncate cascades to table \"%s\"",
2036 : RelationGetRelationName(rel))));
2037 47 : truncate_check_rel(relid, rel->rd_rel);
2038 47 : truncate_check_perms(relid, rel->rd_rel);
2039 47 : truncate_check_activity(rel);
2040 47 : rels = lappend(rels, rel);
2041 47 : relids = lappend_oid(relids, relid);
2042 :
2043 : /* Log this relation only if needed for logical decoding */
2044 47 : if (RelationIsLogicallyLogged(rel))
2045 0 : relids_logged = lappend_oid(relids_logged, relid);
2046 : }
2047 : }
2048 : }
2049 :
2050 : /*
2051 : * Check foreign key references. In CASCADE mode, this should be
2052 : * unnecessary since we just pulled in all the references; but as a
2053 : * cross-check, do it anyway if in an Assert-enabled build.
2054 : */
2055 : #ifdef USE_ASSERT_CHECKING
2056 : heap_truncate_check_FKs(rels, false);
2057 : #else
2058 918 : if (behavior == DROP_RESTRICT)
2059 898 : heap_truncate_check_FKs(rels, false);
2060 : #endif
2061 :
2062 : /*
2063 : * If we are asked to restart sequences, find all the sequences, lock them
2064 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2065 : * We want to do this early since it's pointless to do all the truncation
2066 : * work only to fail on sequence permissions.
2067 : */
2068 881 : if (restart_seqs)
2069 : {
2070 26 : foreach(cell, rels)
2071 : {
2072 13 : Relation rel = (Relation) lfirst(cell);
2073 13 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2074 : ListCell *seqcell;
2075 :
2076 31 : foreach(seqcell, seqlist)
2077 : {
2078 18 : Oid seq_relid = lfirst_oid(seqcell);
2079 : Relation seq_rel;
2080 :
2081 18 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2082 :
2083 : /* This check must match AlterSequence! */
2084 18 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2085 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2086 0 : RelationGetRelationName(seq_rel));
2087 :
2088 18 : seq_relids = lappend_oid(seq_relids, seq_relid);
2089 :
2090 18 : relation_close(seq_rel, NoLock);
2091 : }
2092 : }
2093 : }
2094 :
2095 : /* Prepare to catch AFTER triggers. */
2096 881 : AfterTriggerBeginQuery();
2097 :
2098 : /*
2099 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2100 : * each relation. We don't need to call ExecOpenIndices, though.
2101 : *
2102 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2103 : * though we don't have a range table and don't populate the
2104 : * es_result_relations array. That's a bit bogus, but it's enough to make
2105 : * ExecGetTriggerResultRel() find them.
2106 : */
2107 881 : estate = CreateExecutorState();
2108 : resultRelInfos = (ResultRelInfo *)
2109 881 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2110 881 : resultRelInfo = resultRelInfos;
2111 2840 : foreach(cell, rels)
2112 : {
2113 1959 : Relation rel = (Relation) lfirst(cell);
2114 :
2115 1959 : InitResultRelInfo(resultRelInfo,
2116 : rel,
2117 : 0, /* dummy rangetable index */
2118 : NULL,
2119 : 0);
2120 1959 : estate->es_opened_result_relations =
2121 1959 : lappend(estate->es_opened_result_relations, resultRelInfo);
2122 1959 : resultRelInfo++;
2123 : }
2124 :
2125 : /*
2126 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2127 : * truncating (this is because one of them might throw an error). Also, if
2128 : * we were to allow them to prevent statement execution, that would need
2129 : * to be handled here.
2130 : */
2131 881 : resultRelInfo = resultRelInfos;
2132 2840 : foreach(cell, rels)
2133 : {
2134 : UserContext ucxt;
2135 :
2136 1959 : if (run_as_table_owner)
2137 36 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2138 : &ucxt);
2139 1959 : ExecBSTruncateTriggers(estate, resultRelInfo);
2140 1959 : if (run_as_table_owner)
2141 36 : RestoreUserContext(&ucxt);
2142 1959 : resultRelInfo++;
2143 : }
2144 :
2145 : /*
2146 : * OK, truncate each table.
2147 : */
2148 881 : mySubid = GetCurrentSubTransactionId();
2149 :
2150 2840 : foreach(cell, rels)
2151 : {
2152 1959 : Relation rel = (Relation) lfirst(cell);
2153 :
2154 : /* Skip partitioned tables as there is nothing to do */
2155 1959 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2156 363 : continue;
2157 :
2158 : /*
2159 : * Build the lists of foreign tables belonging to each foreign server
2160 : * and pass each list to the foreign data wrapper's callback function,
2161 : * so that each server can truncate its all foreign tables in bulk.
2162 : * Each list is saved as a single entry in a hash table that uses the
2163 : * server OID as lookup key.
2164 : */
2165 1596 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2166 17 : {
2167 17 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2168 : bool found;
2169 : ForeignTruncateInfo *ft_info;
2170 :
2171 : /* First time through, initialize hashtable for foreign tables */
2172 17 : if (!ft_htab)
2173 : {
2174 : HASHCTL hctl;
2175 :
2176 15 : memset(&hctl, 0, sizeof(HASHCTL));
2177 15 : hctl.keysize = sizeof(Oid);
2178 15 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2179 15 : hctl.hcxt = CurrentMemoryContext;
2180 :
2181 15 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2182 : 32, /* start small and extend */
2183 : &hctl,
2184 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2185 : }
2186 :
2187 : /* Find or create cached entry for the foreign table */
2188 17 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2189 17 : if (!found)
2190 15 : ft_info->rels = NIL;
2191 :
2192 : /*
2193 : * Save the foreign table in the entry of the server that the
2194 : * foreign table belongs to.
2195 : */
2196 17 : ft_info->rels = lappend(ft_info->rels, rel);
2197 17 : continue;
2198 : }
2199 :
2200 : /*
2201 : * Normally, we need a transaction-safe truncation here. However, if
2202 : * the table was either created in the current (sub)transaction or has
2203 : * a new relfilenumber in the current (sub)transaction, then we can
2204 : * just truncate it in-place, because a rollback would cause the whole
2205 : * table or the current physical file to be thrown away anyway.
2206 : */
2207 1579 : if (rel->rd_createSubid == mySubid ||
2208 1560 : rel->rd_newRelfilelocatorSubid == mySubid)
2209 : {
2210 : /* Immediate, non-rollbackable truncation is OK */
2211 51 : heap_truncate_one_rel(rel);
2212 : }
2213 : else
2214 : {
2215 : Oid heap_relid;
2216 : Oid toast_relid;
2217 1528 : ReindexParams reindex_params = {0};
2218 :
2219 : /*
2220 : * This effectively deletes all rows in the table, and may be done
2221 : * in a serializable transaction. In that case we must record a
2222 : * rw-conflict in to this transaction from each transaction
2223 : * holding a predicate lock on the table.
2224 : */
2225 1528 : CheckTableForSerializableConflictIn(rel);
2226 :
2227 : /*
2228 : * Need the full transaction-safe pushups.
2229 : *
2230 : * Create a new empty storage file for the relation, and assign it
2231 : * as the relfilenumber value. The old storage file is scheduled
2232 : * for deletion at commit.
2233 : */
2234 1528 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2235 :
2236 1528 : heap_relid = RelationGetRelid(rel);
2237 :
2238 : /*
2239 : * The same for the toast table, if any.
2240 : */
2241 1528 : toast_relid = rel->rd_rel->reltoastrelid;
2242 1528 : if (OidIsValid(toast_relid))
2243 : {
2244 898 : Relation toastrel = relation_open(toast_relid,
2245 : AccessExclusiveLock);
2246 :
2247 898 : RelationSetNewRelfilenumber(toastrel,
2248 898 : toastrel->rd_rel->relpersistence);
2249 898 : table_close(toastrel, NoLock);
2250 : }
2251 :
2252 : /*
2253 : * Reconstruct the indexes to match, and we're done.
2254 : */
2255 1528 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2256 : &reindex_params);
2257 : }
2258 :
2259 1579 : pgstat_count_truncate(rel);
2260 : }
2261 :
2262 : /* Now go through the hash table, and truncate foreign tables */
2263 881 : if (ft_htab)
2264 : {
2265 : ForeignTruncateInfo *ft_info;
2266 : HASH_SEQ_STATUS seq;
2267 :
2268 15 : hash_seq_init(&seq, ft_htab);
2269 :
2270 15 : PG_TRY();
2271 : {
2272 26 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2273 : {
2274 15 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2275 :
2276 : /* truncate_check_rel() has checked that already */
2277 : Assert(routine->ExecForeignTruncate != NULL);
2278 :
2279 15 : routine->ExecForeignTruncate(ft_info->rels,
2280 : behavior,
2281 : restart_seqs);
2282 : }
2283 : }
2284 4 : PG_FINALLY();
2285 : {
2286 15 : hash_destroy(ft_htab);
2287 : }
2288 15 : PG_END_TRY();
2289 : }
2290 :
2291 : /*
2292 : * Restart owned sequences if we were asked to.
2293 : */
2294 895 : foreach(cell, seq_relids)
2295 : {
2296 18 : Oid seq_relid = lfirst_oid(cell);
2297 :
2298 18 : ResetSequence(seq_relid);
2299 : }
2300 :
2301 : /*
2302 : * Write a WAL record to allow this set of actions to be logically
2303 : * decoded.
2304 : *
2305 : * Assemble an array of relids so we can write a single WAL record for the
2306 : * whole action.
2307 : */
2308 877 : if (relids_logged != NIL)
2309 : {
2310 : xl_heap_truncate xlrec;
2311 31 : int i = 0;
2312 :
2313 : /* should only get here if effective_wal_level is 'logical' */
2314 : Assert(XLogLogicalInfoActive());
2315 :
2316 31 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2317 80 : foreach(cell, relids_logged)
2318 49 : logrelids[i++] = lfirst_oid(cell);
2319 :
2320 31 : xlrec.dbId = MyDatabaseId;
2321 31 : xlrec.nrelids = list_length(relids_logged);
2322 31 : xlrec.flags = 0;
2323 31 : if (behavior == DROP_CASCADE)
2324 1 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2325 31 : if (restart_seqs)
2326 2 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2327 :
2328 31 : XLogBeginInsert();
2329 31 : XLogRegisterData(&xlrec, SizeOfHeapTruncate);
2330 31 : XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2331 :
2332 31 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2333 :
2334 31 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2335 : }
2336 :
2337 : /*
2338 : * Process all AFTER STATEMENT TRUNCATE triggers.
2339 : */
2340 877 : resultRelInfo = resultRelInfos;
2341 2832 : foreach(cell, rels)
2342 : {
2343 : UserContext ucxt;
2344 :
2345 1955 : if (run_as_table_owner)
2346 36 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2347 : &ucxt);
2348 1955 : ExecASTruncateTriggers(estate, resultRelInfo);
2349 1955 : if (run_as_table_owner)
2350 36 : RestoreUserContext(&ucxt);
2351 1955 : resultRelInfo++;
2352 : }
2353 :
2354 : /* Handle queued AFTER triggers */
2355 877 : AfterTriggerEndQuery(estate);
2356 :
2357 : /* We can clean up the EState now */
2358 877 : FreeExecutorState(estate);
2359 :
2360 : /*
2361 : * Close any rels opened by CASCADE (can't do this while EState still
2362 : * holds refs)
2363 : */
2364 877 : rels = list_difference_ptr(rels, explicit_rels);
2365 924 : foreach(cell, rels)
2366 : {
2367 47 : Relation rel = (Relation) lfirst(cell);
2368 :
2369 47 : table_close(rel, NoLock);
2370 : }
2371 877 : }
2372 :
2373 : /*
2374 : * Check that a given relation is safe to truncate. Subroutine for
2375 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2376 : */
2377 : static void
2378 2083 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2379 : {
2380 2083 : char *relname = NameStr(reltuple->relname);
2381 :
2382 : /*
2383 : * Only allow truncate on regular tables, foreign tables using foreign
2384 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2385 : * latter are only being included here for the following checks; no
2386 : * physical truncation will occur in their case.).
2387 : */
2388 2083 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2389 : {
2390 19 : Oid serverid = GetForeignServerIdByRelId(relid);
2391 19 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2392 :
2393 18 : if (!fdwroutine->ExecForeignTruncate)
2394 1 : ereport(ERROR,
2395 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2396 : errmsg("cannot truncate foreign table \"%s\"",
2397 : relname)));
2398 : }
2399 2064 : else if (reltuple->relkind != RELKIND_RELATION &&
2400 367 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2401 0 : ereport(ERROR,
2402 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2403 : errmsg("\"%s\" is not a table", relname)));
2404 :
2405 : /*
2406 : * Most system catalogs can't be truncated at all, or at least not unless
2407 : * allow_system_table_mods=on. As an exception, however, we allow
2408 : * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2409 : * pg_upgrade, because we need to change its relfilenode to match the old
2410 : * cluster, and allowing a TRUNCATE command to be executed is the easiest
2411 : * way of doing that.
2412 : */
2413 2081 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2414 61 : && (!IsBinaryUpgrade ||
2415 30 : (relid != LargeObjectRelationId &&
2416 : relid != LargeObjectMetadataRelationId)))
2417 1 : ereport(ERROR,
2418 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2419 : errmsg("permission denied: \"%s\" is a system catalog",
2420 : relname)));
2421 :
2422 2080 : InvokeObjectTruncateHook(relid);
2423 2080 : }
2424 :
2425 : /*
2426 : * Check that current user has the permission to truncate given relation.
2427 : */
2428 : static void
2429 1162 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2430 : {
2431 1162 : char *relname = NameStr(reltuple->relname);
2432 : AclResult aclresult;
2433 :
2434 : /* Permissions checks */
2435 1162 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2436 1162 : if (aclresult != ACLCHECK_OK)
2437 16 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2438 : relname);
2439 1146 : }
2440 :
2441 : /*
2442 : * Set of extra sanity checks to check if a given relation is safe to
2443 : * truncate. This is split with truncate_check_rel() as
2444 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2445 : */
2446 : static void
2447 2005 : truncate_check_activity(Relation rel)
2448 : {
2449 : /*
2450 : * Don't allow truncate on temp tables of other backends ... their local
2451 : * buffer manager is not going to cope.
2452 : */
2453 2005 : if (RELATION_IS_OTHER_TEMP(rel))
2454 0 : ereport(ERROR,
2455 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2456 : errmsg("cannot truncate temporary tables of other sessions")));
2457 :
2458 : /*
2459 : * Also check for active uses of the relation in the current transaction,
2460 : * including open scans and pending AFTER trigger events.
2461 : */
2462 2005 : CheckTableNotInUse(rel, "TRUNCATE");
2463 2002 : }
2464 :
2465 : /*
2466 : * storage_name
2467 : * returns the name corresponding to a typstorage/attstorage enum value
2468 : */
2469 : static const char *
2470 12 : storage_name(char c)
2471 : {
2472 12 : switch (c)
2473 : {
2474 0 : case TYPSTORAGE_PLAIN:
2475 0 : return "PLAIN";
2476 0 : case TYPSTORAGE_EXTERNAL:
2477 0 : return "EXTERNAL";
2478 6 : case TYPSTORAGE_EXTENDED:
2479 6 : return "EXTENDED";
2480 6 : case TYPSTORAGE_MAIN:
2481 6 : return "MAIN";
2482 0 : default:
2483 0 : return "???";
2484 : }
2485 : }
2486 :
2487 : /*----------
2488 : * MergeAttributes
2489 : * Returns new schema given initial schema and superclasses.
2490 : *
2491 : * Input arguments:
2492 : * 'columns' is the column/attribute definition for the table. (It's a list
2493 : * of ColumnDef's.) It is destructively changed.
2494 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2495 : * 'relpersistence' is the persistence type of the table.
2496 : * 'is_partition' tells if the table is a partition.
2497 : *
2498 : * Output arguments:
2499 : * 'supconstr' receives a list of CookedConstraint representing
2500 : * CHECK constraints belonging to parent relations, updated as
2501 : * necessary to be valid for the child.
2502 : * 'supnotnulls' receives a list of CookedConstraint representing
2503 : * not-null constraints based on those from parent relations.
2504 : *
2505 : * Return value:
2506 : * Completed schema list.
2507 : *
2508 : * Notes:
2509 : * The order in which the attributes are inherited is very important.
2510 : * Intuitively, the inherited attributes should come first. If a table
2511 : * inherits from multiple parents, the order of those attributes are
2512 : * according to the order of the parents specified in CREATE TABLE.
2513 : *
2514 : * Here's an example:
2515 : *
2516 : * create table person (name text, age int4, location point);
2517 : * create table emp (salary int4, manager text) inherits(person);
2518 : * create table student (gpa float8) inherits (person);
2519 : * create table stud_emp (percent int4) inherits (emp, student);
2520 : *
2521 : * The order of the attributes of stud_emp is:
2522 : *
2523 : * person {1:name, 2:age, 3:location}
2524 : * / \
2525 : * {6:gpa} student emp {4:salary, 5:manager}
2526 : * \ /
2527 : * stud_emp {7:percent}
2528 : *
2529 : * If the same attribute name appears multiple times, then it appears
2530 : * in the result table in the proper location for its first appearance.
2531 : *
2532 : * Constraints (including not-null constraints) for the child table
2533 : * are the union of all relevant constraints, from both the child schema
2534 : * and parent tables. In addition, in legacy inheritance, each column that
2535 : * appears in a primary key in any of the parents also gets a NOT NULL
2536 : * constraint (partitioning doesn't need this, because the PK itself gets
2537 : * inherited.)
2538 : *
2539 : * The default value for a child column is defined as:
2540 : * (1) If the child schema specifies a default, that value is used.
2541 : * (2) If neither the child nor any parent specifies a default, then
2542 : * the column will not have a default.
2543 : * (3) If conflicting defaults are inherited from different parents
2544 : * (and not overridden by the child), an error is raised.
2545 : * (4) Otherwise the inherited default is used.
2546 : *
2547 : * Note that the default-value infrastructure is used for generated
2548 : * columns' expressions too, so most of the preceding paragraph applies
2549 : * to generation expressions too. We insist that a child column be
2550 : * generated if and only if its parent(s) are, but it need not have
2551 : * the same generation expression.
2552 : *----------
2553 : */
2554 : static List *
2555 32754 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2556 : bool is_partition, List **supconstr, List **supnotnulls)
2557 : {
2558 32754 : List *inh_columns = NIL;
2559 32754 : List *constraints = NIL;
2560 32754 : List *nnconstraints = NIL;
2561 32754 : bool have_bogus_defaults = false;
2562 : int child_attno;
2563 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2564 32754 : List *saved_columns = NIL;
2565 : ListCell *lc;
2566 :
2567 : /*
2568 : * Check for and reject tables with too many columns. We perform this
2569 : * check relatively early for two reasons: (a) we don't run the risk of
2570 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2571 : * okay if we're processing <= 1600 columns, but could take minutes to
2572 : * execute if the user attempts to create a table with hundreds of
2573 : * thousands of columns.
2574 : *
2575 : * Note that we also need to check that we do not exceed this figure after
2576 : * including columns from inherited relations.
2577 : */
2578 32754 : if (list_length(columns) > MaxHeapAttributeNumber)
2579 0 : ereport(ERROR,
2580 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2581 : errmsg("tables can have at most %d columns",
2582 : MaxHeapAttributeNumber)));
2583 :
2584 : /*
2585 : * Check for duplicate names in the explicit list of attributes.
2586 : *
2587 : * Although we might consider merging such entries in the same way that we
2588 : * handle name conflicts for inherited attributes, it seems to make more
2589 : * sense to assume such conflicts are errors.
2590 : *
2591 : * We don't use foreach() here because we have two nested loops over the
2592 : * columns list, with possible element deletions in the inner one. If we
2593 : * used foreach_delete_current() it could only fix up the state of one of
2594 : * the loops, so it seems cleaner to use looping over list indexes for
2595 : * both loops. Note that any deletion will happen beyond where the outer
2596 : * loop is, so its index never needs adjustment.
2597 : */
2598 154156 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2599 : {
2600 121414 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2601 :
2602 121414 : if (!is_partition && coldef->typeName == NULL)
2603 : {
2604 : /*
2605 : * Typed table column option that does not belong to a column from
2606 : * the type. This works because the columns from the type come
2607 : * first in the list. (We omit this check for partition column
2608 : * lists; those are processed separately below.)
2609 : */
2610 3 : ereport(ERROR,
2611 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2612 : errmsg("column \"%s\" does not exist",
2613 : coldef->colname)));
2614 : }
2615 :
2616 : /* restpos scans all entries beyond coldef; incr is in loop body */
2617 3313739 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2618 : {
2619 3192337 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2620 :
2621 3192337 : if (strcmp(coldef->colname, restdef->colname) == 0)
2622 : {
2623 25 : if (coldef->is_from_type)
2624 : {
2625 : /*
2626 : * merge the column options into the column from the type
2627 : */
2628 16 : coldef->is_not_null = restdef->is_not_null;
2629 16 : coldef->raw_default = restdef->raw_default;
2630 16 : coldef->cooked_default = restdef->cooked_default;
2631 16 : coldef->constraints = restdef->constraints;
2632 16 : coldef->is_from_type = false;
2633 16 : columns = list_delete_nth_cell(columns, restpos);
2634 : }
2635 : else
2636 9 : ereport(ERROR,
2637 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2638 : errmsg("column \"%s\" specified more than once",
2639 : coldef->colname)));
2640 : }
2641 : else
2642 3192312 : restpos++;
2643 : }
2644 : }
2645 :
2646 : /*
2647 : * In case of a partition, there are no new column definitions, only dummy
2648 : * ColumnDefs created for column constraints. Set them aside for now and
2649 : * process them at the end.
2650 : */
2651 32742 : if (is_partition)
2652 : {
2653 4571 : saved_columns = columns;
2654 4571 : columns = NIL;
2655 : }
2656 :
2657 : /*
2658 : * Scan the parents left-to-right, and merge their attributes to form a
2659 : * list of inherited columns (inh_columns).
2660 : */
2661 32742 : child_attno = 0;
2662 38532 : foreach(lc, supers)
2663 : {
2664 5832 : Oid parent = lfirst_oid(lc);
2665 : Relation relation;
2666 : TupleDesc tupleDesc;
2667 : TupleConstr *constr;
2668 : AttrMap *newattmap;
2669 : List *inherited_defaults;
2670 : List *cols_with_defaults;
2671 : List *nnconstrs;
2672 : ListCell *lc1;
2673 : ListCell *lc2;
2674 5832 : Bitmapset *nncols = NULL;
2675 :
2676 : /* caller already got lock */
2677 5832 : relation = table_open(parent, NoLock);
2678 :
2679 : /*
2680 : * Check for active uses of the parent partitioned table in the
2681 : * current transaction, such as being used in some manner by an
2682 : * enclosing command.
2683 : */
2684 5832 : if (is_partition)
2685 4571 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2686 :
2687 : /*
2688 : * We do not allow partitioned tables and partitions to participate in
2689 : * regular inheritance.
2690 : */
2691 5829 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2692 3 : ereport(ERROR,
2693 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2694 : errmsg("cannot inherit from partitioned table \"%s\"",
2695 : RelationGetRelationName(relation))));
2696 5826 : if (relation->rd_rel->relispartition && !is_partition)
2697 3 : ereport(ERROR,
2698 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2699 : errmsg("cannot inherit from partition \"%s\"",
2700 : RelationGetRelationName(relation))));
2701 :
2702 5823 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2703 4569 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2704 4559 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2705 0 : ereport(ERROR,
2706 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2707 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2708 : RelationGetRelationName(relation))));
2709 :
2710 : /*
2711 : * If the parent is permanent, so must be all of its partitions. Note
2712 : * that inheritance allows that case.
2713 : */
2714 5823 : if (is_partition &&
2715 4568 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2716 : relpersistence == RELPERSISTENCE_TEMP)
2717 3 : ereport(ERROR,
2718 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2719 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2720 : RelationGetRelationName(relation))));
2721 :
2722 : /* Permanent rels cannot inherit from temporary ones */
2723 5820 : if (relpersistence != RELPERSISTENCE_TEMP &&
2724 5613 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2725 12 : ereport(ERROR,
2726 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2727 : errmsg(!is_partition
2728 : ? "cannot inherit from temporary relation \"%s\""
2729 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2730 : RelationGetRelationName(relation))));
2731 :
2732 : /* If existing rel is temp, it must belong to this session */
2733 5808 : if (RELATION_IS_OTHER_TEMP(relation))
2734 0 : ereport(ERROR,
2735 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2736 : errmsg(!is_partition
2737 : ? "cannot inherit from temporary relation of another session"
2738 : : "cannot create as partition of temporary relation of another session")));
2739 :
2740 : /*
2741 : * We should have an UNDER permission flag for this, but for now,
2742 : * demand that creator of a child table own the parent.
2743 : */
2744 5808 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2745 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2746 0 : RelationGetRelationName(relation));
2747 :
2748 5808 : tupleDesc = RelationGetDescr(relation);
2749 5808 : constr = tupleDesc->constr;
2750 :
2751 : /*
2752 : * newattmap->attnums[] will contain the child-table attribute numbers
2753 : * for the attributes of this parent table. (They are not the same
2754 : * for parents after the first one, nor if we have dropped columns.)
2755 : */
2756 5808 : newattmap = make_attrmap(tupleDesc->natts);
2757 :
2758 : /* We can't process inherited defaults until newattmap is complete. */
2759 5808 : inherited_defaults = cols_with_defaults = NIL;
2760 :
2761 : /*
2762 : * Request attnotnull on columns that have a not-null constraint
2763 : * that's not marked NO INHERIT (even if not valid).
2764 : */
2765 5808 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2766 : true, false);
2767 12931 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2768 1315 : nncols = bms_add_member(nncols, cc->attnum);
2769 :
2770 17874 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2771 12066 : parent_attno++)
2772 : {
2773 12084 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2774 : parent_attno - 1);
2775 12084 : char *attributeName = NameStr(attribute->attname);
2776 : int exist_attno;
2777 : ColumnDef *newdef;
2778 : ColumnDef *mergeddef;
2779 :
2780 : /*
2781 : * Ignore dropped columns in the parent.
2782 : */
2783 12084 : if (attribute->attisdropped)
2784 99 : continue; /* leave newattmap->attnums entry as zero */
2785 :
2786 : /*
2787 : * Create new column definition
2788 : */
2789 11985 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2790 : attribute->atttypmod, attribute->attcollation);
2791 11985 : newdef->storage = attribute->attstorage;
2792 11985 : newdef->generated = attribute->attgenerated;
2793 11985 : if (CompressionMethodIsValid(attribute->attcompression))
2794 18 : newdef->compression =
2795 18 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2796 :
2797 : /*
2798 : * Regular inheritance children are independent enough not to
2799 : * inherit identity columns. But partitions are integral part of
2800 : * a partitioned table and inherit identity column.
2801 : */
2802 11985 : if (is_partition)
2803 9669 : newdef->identity = attribute->attidentity;
2804 :
2805 : /*
2806 : * Does it match some previously considered column from another
2807 : * parent?
2808 : */
2809 11985 : exist_attno = findAttrByName(attributeName, inh_columns);
2810 11985 : if (exist_attno > 0)
2811 : {
2812 : /*
2813 : * Yes, try to merge the two column definitions.
2814 : */
2815 185 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2816 :
2817 167 : newattmap->attnums[parent_attno - 1] = exist_attno;
2818 :
2819 : /*
2820 : * Partitions have only one parent, so conflict should never
2821 : * occur.
2822 : */
2823 : Assert(!is_partition);
2824 : }
2825 : else
2826 : {
2827 : /*
2828 : * No, create a new inherited column
2829 : */
2830 11800 : newdef->inhcount = 1;
2831 11800 : newdef->is_local = false;
2832 11800 : inh_columns = lappend(inh_columns, newdef);
2833 :
2834 11800 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2835 11800 : mergeddef = newdef;
2836 : }
2837 :
2838 : /*
2839 : * mark attnotnull if parent has it
2840 : */
2841 11967 : if (bms_is_member(parent_attno, nncols))
2842 1315 : mergeddef->is_not_null = true;
2843 :
2844 : /*
2845 : * Locate default/generation expression if any
2846 : */
2847 11967 : if (attribute->atthasdef)
2848 : {
2849 : Node *this_default;
2850 :
2851 429 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2852 429 : if (this_default == NULL)
2853 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2854 : parent_attno, RelationGetRelationName(relation));
2855 :
2856 : /*
2857 : * If it's a GENERATED default, it might contain Vars that
2858 : * need to be mapped to the inherited column(s)' new numbers.
2859 : * We can't do that till newattmap is ready, so just remember
2860 : * all the inherited default expressions for the moment.
2861 : */
2862 429 : inherited_defaults = lappend(inherited_defaults, this_default);
2863 429 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2864 : }
2865 : }
2866 :
2867 : /*
2868 : * Now process any inherited default expressions, adjusting attnos
2869 : * using the completed newattmap map.
2870 : */
2871 6219 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2872 : {
2873 429 : Node *this_default = (Node *) lfirst(lc1);
2874 429 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2875 : bool found_whole_row;
2876 :
2877 : /* Adjust Vars to match new table's column numbering */
2878 429 : this_default = map_variable_attnos(this_default,
2879 : 1, 0,
2880 : newattmap,
2881 : InvalidOid, &found_whole_row);
2882 :
2883 : /*
2884 : * For the moment we have to reject whole-row variables. We could
2885 : * convert them, if we knew the new table's rowtype OID, but that
2886 : * hasn't been assigned yet. (A variable could only appear in a
2887 : * generation expression, so the error message is correct.)
2888 : */
2889 429 : if (found_whole_row)
2890 0 : ereport(ERROR,
2891 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2892 : errmsg("cannot convert whole-row table reference"),
2893 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2894 : def->colname,
2895 : RelationGetRelationName(relation))));
2896 :
2897 : /*
2898 : * If we already had a default from some prior parent, check to
2899 : * see if they are the same. If so, no problem; if not, mark the
2900 : * column as having a bogus default. Below, we will complain if
2901 : * the bogus default isn't overridden by the child columns.
2902 : */
2903 : Assert(def->raw_default == NULL);
2904 429 : if (def->cooked_default == NULL)
2905 408 : def->cooked_default = this_default;
2906 21 : else if (!equal(def->cooked_default, this_default))
2907 : {
2908 18 : def->cooked_default = &bogus_marker;
2909 18 : have_bogus_defaults = true;
2910 : }
2911 : }
2912 :
2913 : /*
2914 : * Now copy the CHECK constraints of this parent, adjusting attnos
2915 : * using the completed newattmap map. Identically named constraints
2916 : * are merged if possible, else we throw error.
2917 : */
2918 5790 : if (constr && constr->num_check > 0)
2919 : {
2920 176 : ConstrCheck *check = constr->check;
2921 :
2922 553 : for (int i = 0; i < constr->num_check; i++)
2923 : {
2924 377 : char *name = check[i].ccname;
2925 : Node *expr;
2926 : bool found_whole_row;
2927 :
2928 : /* ignore if the constraint is non-inheritable */
2929 377 : if (check[i].ccnoinherit)
2930 24 : continue;
2931 :
2932 : /* Adjust Vars to match new table's column numbering */
2933 353 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2934 : 1, 0,
2935 : newattmap,
2936 : InvalidOid, &found_whole_row);
2937 :
2938 : /*
2939 : * For the moment we have to reject whole-row variables. We
2940 : * could convert them, if we knew the new table's rowtype OID,
2941 : * but that hasn't been assigned yet.
2942 : */
2943 353 : if (found_whole_row)
2944 0 : ereport(ERROR,
2945 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2946 : errmsg("cannot convert whole-row table reference"),
2947 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2948 : name,
2949 : RelationGetRelationName(relation))));
2950 :
2951 353 : constraints = MergeCheckConstraint(constraints, name, expr,
2952 353 : check[i].ccenforced);
2953 : }
2954 : }
2955 :
2956 : /*
2957 : * Also copy the not-null constraints from this parent. The
2958 : * attnotnull markings were already installed above.
2959 : */
2960 12895 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2961 : {
2962 : Assert(nn->contype == CONSTR_NOTNULL);
2963 :
2964 1315 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2965 :
2966 1315 : nnconstraints = lappend(nnconstraints, nn);
2967 : }
2968 :
2969 5790 : free_attrmap(newattmap);
2970 :
2971 : /*
2972 : * Close the parent rel, but keep our lock on it until xact commit.
2973 : * That will prevent someone else from deleting or ALTERing the parent
2974 : * before the child is committed.
2975 : */
2976 5790 : table_close(relation, NoLock);
2977 : }
2978 :
2979 : /*
2980 : * If we had no inherited attributes, the result columns are just the
2981 : * explicitly declared columns. Otherwise, we need to merge the declared
2982 : * columns into the inherited column list. Although, we never have any
2983 : * explicitly declared columns if the table is a partition.
2984 : */
2985 32700 : if (inh_columns != NIL)
2986 : {
2987 5572 : int newcol_attno = 0;
2988 :
2989 6059 : foreach(lc, columns)
2990 : {
2991 526 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2992 526 : char *attributeName = newdef->colname;
2993 : int exist_attno;
2994 :
2995 : /*
2996 : * Partitions have only one parent and have no column definitions
2997 : * of their own, so conflict should never occur.
2998 : */
2999 : Assert(!is_partition);
3000 :
3001 526 : newcol_attno++;
3002 :
3003 : /*
3004 : * Does it match some inherited column?
3005 : */
3006 526 : exist_attno = findAttrByName(attributeName, inh_columns);
3007 526 : if (exist_attno > 0)
3008 : {
3009 : /*
3010 : * Yes, try to merge the two column definitions.
3011 : */
3012 190 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
3013 : }
3014 : else
3015 : {
3016 : /*
3017 : * No, attach new column unchanged to result columns.
3018 : */
3019 336 : inh_columns = lappend(inh_columns, newdef);
3020 : }
3021 : }
3022 :
3023 5533 : columns = inh_columns;
3024 :
3025 : /*
3026 : * Check that we haven't exceeded the legal # of columns after merging
3027 : * in inherited columns.
3028 : */
3029 5533 : if (list_length(columns) > MaxHeapAttributeNumber)
3030 0 : ereport(ERROR,
3031 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
3032 : errmsg("tables can have at most %d columns",
3033 : MaxHeapAttributeNumber)));
3034 : }
3035 :
3036 : /*
3037 : * Now that we have the column definition list for a partition, we can
3038 : * check whether the columns referenced in the column constraint specs
3039 : * actually exist. Also, merge column defaults.
3040 : */
3041 32661 : if (is_partition)
3042 : {
3043 4666 : foreach(lc, saved_columns)
3044 : {
3045 128 : ColumnDef *restdef = lfirst(lc);
3046 128 : bool found = false;
3047 : ListCell *l;
3048 :
3049 485 : foreach(l, columns)
3050 : {
3051 375 : ColumnDef *coldef = lfirst(l);
3052 :
3053 375 : if (strcmp(coldef->colname, restdef->colname) == 0)
3054 : {
3055 128 : found = true;
3056 :
3057 : /*
3058 : * Check for conflicts related to generated columns.
3059 : *
3060 : * Same rules as above: generated-ness has to match the
3061 : * parent, but the contents of the generation expression
3062 : * can be different.
3063 : */
3064 128 : if (coldef->generated)
3065 : {
3066 74 : if (restdef->raw_default && !restdef->generated)
3067 6 : ereport(ERROR,
3068 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3069 : errmsg("column \"%s\" inherits from generated column but specifies default",
3070 : restdef->colname)));
3071 68 : if (restdef->identity)
3072 0 : ereport(ERROR,
3073 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3074 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3075 : restdef->colname)));
3076 : }
3077 : else
3078 : {
3079 54 : if (restdef->generated)
3080 6 : ereport(ERROR,
3081 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3082 : errmsg("child column \"%s\" specifies generation expression",
3083 : restdef->colname),
3084 : errhint("A child table column cannot be generated unless its parent column is.")));
3085 : }
3086 :
3087 116 : if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3088 6 : ereport(ERROR,
3089 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3090 : errmsg("column \"%s\" inherits from generated column of different kind",
3091 : restdef->colname),
3092 : errdetail("Parent column is %s, child column is %s.",
3093 : coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3094 : restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3095 :
3096 : /*
3097 : * Override the parent's default value for this column
3098 : * (coldef->cooked_default) with the partition's local
3099 : * definition (restdef->raw_default), if there's one. It
3100 : * should be physically impossible to get a cooked default
3101 : * in the local definition or a raw default in the
3102 : * inherited definition, but make sure they're nulls, for
3103 : * future-proofing.
3104 : */
3105 : Assert(restdef->cooked_default == NULL);
3106 : Assert(coldef->raw_default == NULL);
3107 110 : if (restdef->raw_default)
3108 : {
3109 74 : coldef->raw_default = restdef->raw_default;
3110 74 : coldef->cooked_default = NULL;
3111 : }
3112 : }
3113 : }
3114 :
3115 : /* complain for constraints on columns not in parent */
3116 110 : if (!found)
3117 0 : ereport(ERROR,
3118 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3119 : errmsg("column \"%s\" does not exist",
3120 : restdef->colname)));
3121 : }
3122 : }
3123 :
3124 : /*
3125 : * If we found any conflicting parent default values, check to make sure
3126 : * they were overridden by the child.
3127 : */
3128 32643 : if (have_bogus_defaults)
3129 : {
3130 45 : foreach(lc, columns)
3131 : {
3132 36 : ColumnDef *def = lfirst(lc);
3133 :
3134 36 : if (def->cooked_default == &bogus_marker)
3135 : {
3136 9 : if (def->generated)
3137 6 : ereport(ERROR,
3138 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3139 : errmsg("column \"%s\" inherits conflicting generation expressions",
3140 : def->colname),
3141 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3142 : else
3143 3 : ereport(ERROR,
3144 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3145 : errmsg("column \"%s\" inherits conflicting default values",
3146 : def->colname),
3147 : errhint("To resolve the conflict, specify a default explicitly.")));
3148 : }
3149 : }
3150 : }
3151 :
3152 32634 : *supconstr = constraints;
3153 32634 : *supnotnulls = nnconstraints;
3154 :
3155 32634 : return columns;
3156 : }
3157 :
3158 :
3159 : /*
3160 : * MergeCheckConstraint
3161 : * Try to merge an inherited CHECK constraint with previous ones
3162 : *
3163 : * If we inherit identically-named constraints from multiple parents, we must
3164 : * merge them, or throw an error if they don't have identical definitions.
3165 : *
3166 : * constraints is a list of CookedConstraint structs for previous constraints.
3167 : *
3168 : * If the new constraint matches an existing one, then the existing
3169 : * constraint's inheritance count is updated. If there is a conflict (same
3170 : * name but different expression), throw an error. If the constraint neither
3171 : * matches nor conflicts with an existing one, a new constraint is appended to
3172 : * the list.
3173 : */
3174 : static List *
3175 353 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3176 : {
3177 : ListCell *lc;
3178 : CookedConstraint *newcon;
3179 :
3180 1115 : foreach(lc, constraints)
3181 : {
3182 837 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3183 :
3184 : Assert(ccon->contype == CONSTR_CHECK);
3185 :
3186 : /* Non-matching names never conflict */
3187 837 : if (strcmp(ccon->name, name) != 0)
3188 762 : continue;
3189 :
3190 75 : if (equal(expr, ccon->expr))
3191 : {
3192 : /* OK to merge constraint with existing */
3193 75 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3194 : &ccon->inhcount))
3195 0 : ereport(ERROR,
3196 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3197 : errmsg("too many inheritance parents"));
3198 :
3199 : /*
3200 : * When enforceability differs, the merged constraint should be
3201 : * marked as ENFORCED because one of the parents is ENFORCED.
3202 : */
3203 75 : if (!ccon->is_enforced && is_enforced)
3204 : {
3205 24 : ccon->is_enforced = true;
3206 24 : ccon->skip_validation = false;
3207 : }
3208 :
3209 75 : return constraints;
3210 : }
3211 :
3212 0 : ereport(ERROR,
3213 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3214 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3215 : name)));
3216 : }
3217 :
3218 : /*
3219 : * Constraint couldn't be merged with an existing one and also didn't
3220 : * conflict with an existing one, so add it as a new one to the list.
3221 : */
3222 278 : newcon = palloc0_object(CookedConstraint);
3223 278 : newcon->contype = CONSTR_CHECK;
3224 278 : newcon->name = pstrdup(name);
3225 278 : newcon->expr = expr;
3226 278 : newcon->inhcount = 1;
3227 278 : newcon->is_enforced = is_enforced;
3228 278 : newcon->skip_validation = !is_enforced;
3229 278 : return lappend(constraints, newcon);
3230 : }
3231 :
3232 : /*
3233 : * MergeChildAttribute
3234 : * Merge given child attribute definition into given inherited attribute.
3235 : *
3236 : * Input arguments:
3237 : * 'inh_columns' is the list of inherited ColumnDefs.
3238 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3239 : * 'newcol_attno' is the attribute number in child table's schema definition
3240 : * 'newdef' is the column/attribute definition from the child table.
3241 : *
3242 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3243 : * ColumnDef remains unchanged.
3244 : *
3245 : * Notes:
3246 : * - The attribute is merged according to the rules laid out in the prologue
3247 : * of MergeAttributes().
3248 : * - If matching inherited attribute exists but the child attribute can not be
3249 : * merged into it, the function throws respective errors.
3250 : * - A partition can not have its own column definitions. Hence this function
3251 : * is applicable only to a regular inheritance child.
3252 : */
3253 : static void
3254 190 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3255 : {
3256 190 : char *attributeName = newdef->colname;
3257 : ColumnDef *inhdef;
3258 : Oid inhtypeid,
3259 : newtypeid;
3260 : int32 inhtypmod,
3261 : newtypmod;
3262 : Oid inhcollid,
3263 : newcollid;
3264 :
3265 190 : if (exist_attno == newcol_attno)
3266 173 : ereport(NOTICE,
3267 : (errmsg("merging column \"%s\" with inherited definition",
3268 : attributeName)));
3269 : else
3270 17 : ereport(NOTICE,
3271 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3272 : errdetail("User-specified column moved to the position of the inherited column.")));
3273 :
3274 190 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3275 :
3276 : /*
3277 : * Must have the same type and typmod
3278 : */
3279 190 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3280 190 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3281 190 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3282 6 : ereport(ERROR,
3283 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3284 : errmsg("column \"%s\" has a type conflict",
3285 : attributeName),
3286 : errdetail("%s versus %s",
3287 : format_type_with_typemod(inhtypeid, inhtypmod),
3288 : format_type_with_typemod(newtypeid, newtypmod))));
3289 :
3290 : /*
3291 : * Must have the same collation
3292 : */
3293 184 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3294 184 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3295 184 : if (inhcollid != newcollid)
3296 3 : ereport(ERROR,
3297 : (errcode(ERRCODE_COLLATION_MISMATCH),
3298 : errmsg("column \"%s\" has a collation conflict",
3299 : attributeName),
3300 : errdetail("\"%s\" versus \"%s\"",
3301 : get_collation_name(inhcollid),
3302 : get_collation_name(newcollid))));
3303 :
3304 : /*
3305 : * Identity is never inherited by a regular inheritance child. Pick
3306 : * child's identity definition if there's one.
3307 : */
3308 181 : inhdef->identity = newdef->identity;
3309 :
3310 : /*
3311 : * Copy storage parameter
3312 : */
3313 181 : if (inhdef->storage == 0)
3314 0 : inhdef->storage = newdef->storage;
3315 181 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3316 3 : ereport(ERROR,
3317 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3318 : errmsg("column \"%s\" has a storage parameter conflict",
3319 : attributeName),
3320 : errdetail("%s versus %s",
3321 : storage_name(inhdef->storage),
3322 : storage_name(newdef->storage))));
3323 :
3324 : /*
3325 : * Copy compression parameter
3326 : */
3327 178 : if (inhdef->compression == NULL)
3328 175 : inhdef->compression = newdef->compression;
3329 3 : else if (newdef->compression != NULL)
3330 : {
3331 3 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3332 3 : ereport(ERROR,
3333 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3334 : errmsg("column \"%s\" has a compression method conflict",
3335 : attributeName),
3336 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3337 : }
3338 :
3339 : /*
3340 : * Merge of not-null constraints = OR 'em together
3341 : */
3342 175 : inhdef->is_not_null |= newdef->is_not_null;
3343 :
3344 : /*
3345 : * Check for conflicts related to generated columns.
3346 : *
3347 : * If the parent column is generated, the child column will be made a
3348 : * generated column if it isn't already. If it is a generated column,
3349 : * we'll take its generation expression in preference to the parent's. We
3350 : * must check that the child column doesn't specify a default value or
3351 : * identity, which matches the rules for a single column in
3352 : * parse_utilcmd.c.
3353 : *
3354 : * Conversely, if the parent column is not generated, the child column
3355 : * can't be either. (We used to allow that, but it results in being able
3356 : * to override the generation expression via UPDATEs through the parent.)
3357 : */
3358 175 : if (inhdef->generated)
3359 : {
3360 31 : if (newdef->raw_default && !newdef->generated)
3361 6 : ereport(ERROR,
3362 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3363 : errmsg("column \"%s\" inherits from generated column but specifies default",
3364 : inhdef->colname)));
3365 25 : if (newdef->identity)
3366 6 : ereport(ERROR,
3367 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3368 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3369 : inhdef->colname)));
3370 : }
3371 : else
3372 : {
3373 144 : if (newdef->generated)
3374 6 : ereport(ERROR,
3375 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3376 : errmsg("child column \"%s\" specifies generation expression",
3377 : inhdef->colname),
3378 : errhint("A child table column cannot be generated unless its parent column is.")));
3379 : }
3380 :
3381 157 : if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3382 6 : ereport(ERROR,
3383 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3384 : errmsg("column \"%s\" inherits from generated column of different kind",
3385 : inhdef->colname),
3386 : errdetail("Parent column is %s, child column is %s.",
3387 : inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3388 : newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3389 :
3390 : /*
3391 : * If new def has a default, override previous default
3392 : */
3393 151 : if (newdef->raw_default != NULL)
3394 : {
3395 15 : inhdef->raw_default = newdef->raw_default;
3396 15 : inhdef->cooked_default = newdef->cooked_default;
3397 : }
3398 :
3399 : /* Mark the column as locally defined */
3400 151 : inhdef->is_local = true;
3401 151 : }
3402 :
3403 : /*
3404 : * MergeInheritedAttribute
3405 : * Merge given parent attribute definition into specified attribute
3406 : * inherited from the previous parents.
3407 : *
3408 : * Input arguments:
3409 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3410 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3411 : * 'newdef' is the new parent column/attribute definition to be merged.
3412 : *
3413 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3414 : *
3415 : * Notes:
3416 : * - The attribute is merged according to the rules laid out in the prologue
3417 : * of MergeAttributes().
3418 : * - If matching inherited attribute exists but the new attribute can not be
3419 : * merged into it, the function throws respective errors.
3420 : * - A partition inherits from only a single parent. Hence this function is
3421 : * applicable only to a regular inheritance.
3422 : */
3423 : static ColumnDef *
3424 185 : MergeInheritedAttribute(List *inh_columns,
3425 : int exist_attno,
3426 : const ColumnDef *newdef)
3427 : {
3428 185 : char *attributeName = newdef->colname;
3429 : ColumnDef *prevdef;
3430 : Oid prevtypeid,
3431 : newtypeid;
3432 : int32 prevtypmod,
3433 : newtypmod;
3434 : Oid prevcollid,
3435 : newcollid;
3436 :
3437 185 : ereport(NOTICE,
3438 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3439 : attributeName)));
3440 185 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3441 :
3442 : /*
3443 : * Must have the same type and typmod
3444 : */
3445 185 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3446 185 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3447 185 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3448 0 : ereport(ERROR,
3449 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3450 : errmsg("inherited column \"%s\" has a type conflict",
3451 : attributeName),
3452 : errdetail("%s versus %s",
3453 : format_type_with_typemod(prevtypeid, prevtypmod),
3454 : format_type_with_typemod(newtypeid, newtypmod))));
3455 :
3456 : /*
3457 : * Must have the same collation
3458 : */
3459 185 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3460 185 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3461 185 : if (prevcollid != newcollid)
3462 0 : ereport(ERROR,
3463 : (errcode(ERRCODE_COLLATION_MISMATCH),
3464 : errmsg("inherited column \"%s\" has a collation conflict",
3465 : attributeName),
3466 : errdetail("\"%s\" versus \"%s\"",
3467 : get_collation_name(prevcollid),
3468 : get_collation_name(newcollid))));
3469 :
3470 : /*
3471 : * Copy/check storage parameter
3472 : */
3473 185 : if (prevdef->storage == 0)
3474 0 : prevdef->storage = newdef->storage;
3475 185 : else if (prevdef->storage != newdef->storage)
3476 3 : ereport(ERROR,
3477 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3478 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3479 : attributeName),
3480 : errdetail("%s versus %s",
3481 : storage_name(prevdef->storage),
3482 : storage_name(newdef->storage))));
3483 :
3484 : /*
3485 : * Copy/check compression parameter
3486 : */
3487 182 : if (prevdef->compression == NULL)
3488 173 : prevdef->compression = newdef->compression;
3489 9 : else if (newdef->compression != NULL)
3490 : {
3491 3 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3492 3 : ereport(ERROR,
3493 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3494 : errmsg("column \"%s\" has a compression method conflict",
3495 : attributeName),
3496 : errdetail("%s versus %s",
3497 : prevdef->compression, newdef->compression)));
3498 : }
3499 :
3500 : /*
3501 : * Check for GENERATED conflicts
3502 : */
3503 179 : if (prevdef->generated != newdef->generated)
3504 12 : ereport(ERROR,
3505 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3506 : errmsg("inherited column \"%s\" has a generation conflict",
3507 : attributeName)));
3508 :
3509 : /*
3510 : * Default and other constraints are handled by the caller.
3511 : */
3512 :
3513 167 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3514 : &prevdef->inhcount))
3515 0 : ereport(ERROR,
3516 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3517 : errmsg("too many inheritance parents"));
3518 :
3519 167 : return prevdef;
3520 : }
3521 :
3522 : /*
3523 : * StoreCatalogInheritance
3524 : * Updates the system catalogs with proper inheritance information.
3525 : *
3526 : * supers is a list of the OIDs of the new relation's direct ancestors.
3527 : */
3528 : static void
3529 32304 : StoreCatalogInheritance(Oid relationId, List *supers,
3530 : bool child_is_partition)
3531 : {
3532 : Relation relation;
3533 : int32 seqNumber;
3534 : ListCell *entry;
3535 :
3536 : /*
3537 : * sanity checks
3538 : */
3539 : Assert(OidIsValid(relationId));
3540 :
3541 32304 : if (supers == NIL)
3542 26951 : return;
3543 :
3544 : /*
3545 : * Store INHERITS information in pg_inherits using direct ancestors only.
3546 : * Also enter dependencies on the direct ancestors, and make sure they are
3547 : * marked with relhassubclass = true.
3548 : *
3549 : * (Once upon a time, both direct and indirect ancestors were found here
3550 : * and then entered into pg_ipl. Since that catalog doesn't exist
3551 : * anymore, there's no need to look for indirect ancestors.)
3552 : */
3553 5353 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3554 :
3555 5353 : seqNumber = 1;
3556 10873 : foreach(entry, supers)
3557 : {
3558 5520 : Oid parentOid = lfirst_oid(entry);
3559 :
3560 5520 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3561 : child_is_partition);
3562 5520 : seqNumber++;
3563 : }
3564 :
3565 5353 : table_close(relation, RowExclusiveLock);
3566 : }
3567 :
3568 : /*
3569 : * Make catalog entries showing relationId as being an inheritance child
3570 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3571 : */
3572 : static void
3573 7137 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3574 : int32 seqNumber, Relation inhRelation,
3575 : bool child_is_partition)
3576 : {
3577 : ObjectAddress childobject,
3578 : parentobject;
3579 :
3580 : /* store the pg_inherits row */
3581 7137 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3582 :
3583 : /*
3584 : * Store a dependency too
3585 : */
3586 7137 : parentobject.classId = RelationRelationId;
3587 7137 : parentobject.objectId = parentOid;
3588 7137 : parentobject.objectSubId = 0;
3589 7137 : childobject.classId = RelationRelationId;
3590 7137 : childobject.objectId = relationId;
3591 7137 : childobject.objectSubId = 0;
3592 :
3593 7137 : recordDependencyOn(&childobject, &parentobject,
3594 : child_dependency_type(child_is_partition));
3595 :
3596 : /*
3597 : * Post creation hook of this inheritance. Since object_access_hook
3598 : * doesn't take multiple object identifiers, we relay oid of parent
3599 : * relation using auxiliary_id argument.
3600 : */
3601 7137 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3602 : relationId, 0,
3603 : parentOid, false);
3604 :
3605 : /*
3606 : * Mark the parent as having subclasses.
3607 : */
3608 7137 : SetRelationHasSubclass(parentOid, true);
3609 7137 : }
3610 :
3611 : /*
3612 : * Look for an existing column entry with the given name.
3613 : *
3614 : * Returns the index (starting with 1) if attribute already exists in columns,
3615 : * 0 if it doesn't.
3616 : */
3617 : static int
3618 12511 : findAttrByName(const char *attributeName, const List *columns)
3619 : {
3620 : ListCell *lc;
3621 12511 : int i = 1;
3622 :
3623 23060 : foreach(lc, columns)
3624 : {
3625 10924 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3626 375 : return i;
3627 :
3628 10549 : i++;
3629 : }
3630 12136 : return 0;
3631 : }
3632 :
3633 :
3634 : /*
3635 : * SetRelationHasSubclass
3636 : * Set the value of the relation's relhassubclass field in pg_class.
3637 : *
3638 : * It's always safe to set this field to true, because all SQL commands are
3639 : * ready to see true and then find no children. On the other hand, commands
3640 : * generally assume zero children if this is false.
3641 : *
3642 : * Caller must hold any self-exclusive lock until end of transaction. If the
3643 : * new value is false, caller must have acquired that lock before reading the
3644 : * evidence that justified the false value. That way, it properly waits if
3645 : * another backend is simultaneously concluding no need to change the tuple
3646 : * (new and old values are true).
3647 : *
3648 : * NOTE: an important side-effect of this operation is that an SI invalidation
3649 : * message is sent out to all backends --- including me --- causing plans
3650 : * referencing the relation to be rebuilt with the new list of children.
3651 : * This must happen even if we find that no change is needed in the pg_class
3652 : * row.
3653 : */
3654 : void
3655 9017 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3656 : {
3657 : Relation relationRelation;
3658 : HeapTuple tuple;
3659 : Form_pg_class classtuple;
3660 :
3661 : Assert(CheckRelationOidLockedByMe(relationId,
3662 : ShareUpdateExclusiveLock, false) ||
3663 : CheckRelationOidLockedByMe(relationId,
3664 : ShareRowExclusiveLock, true));
3665 :
3666 : /*
3667 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3668 : */
3669 9017 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3670 9017 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3671 9017 : if (!HeapTupleIsValid(tuple))
3672 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3673 9017 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3674 :
3675 9017 : if (classtuple->relhassubclass != relhassubclass)
3676 : {
3677 4259 : classtuple->relhassubclass = relhassubclass;
3678 4259 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3679 : }
3680 : else
3681 : {
3682 : /* no need to change tuple, but force relcache rebuild anyway */
3683 4758 : CacheInvalidateRelcacheByTuple(tuple);
3684 : }
3685 :
3686 9017 : heap_freetuple(tuple);
3687 9017 : table_close(relationRelation, RowExclusiveLock);
3688 9017 : }
3689 :
3690 : /*
3691 : * CheckRelationTableSpaceMove
3692 : * Check if relation can be moved to new tablespace.
3693 : *
3694 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3695 : *
3696 : * Returns true if the relation can be moved to the new tablespace; raises
3697 : * an error if it is not possible to do the move; returns false if the move
3698 : * would have no effect.
3699 : */
3700 : bool
3701 119 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3702 : {
3703 : Oid oldTableSpaceId;
3704 :
3705 : /*
3706 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3707 : * stored as 0.
3708 : */
3709 119 : oldTableSpaceId = rel->rd_rel->reltablespace;
3710 119 : if (newTableSpaceId == oldTableSpaceId ||
3711 115 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3712 8 : return false;
3713 :
3714 : /*
3715 : * We cannot support moving mapped relations into different tablespaces.
3716 : * (In particular this eliminates all shared catalogs.)
3717 : */
3718 111 : if (RelationIsMapped(rel))
3719 0 : ereport(ERROR,
3720 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3721 : errmsg("cannot move system relation \"%s\"",
3722 : RelationGetRelationName(rel))));
3723 :
3724 : /* Cannot move a non-shared relation into pg_global */
3725 111 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3726 6 : ereport(ERROR,
3727 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3728 : errmsg("only shared relations can be placed in pg_global tablespace")));
3729 :
3730 : /*
3731 : * Do not allow moving temp tables of other backends ... their local
3732 : * buffer manager is not going to cope.
3733 : */
3734 105 : if (RELATION_IS_OTHER_TEMP(rel))
3735 0 : ereport(ERROR,
3736 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3737 : errmsg("cannot move temporary tables of other sessions")));
3738 :
3739 105 : return true;
3740 : }
3741 :
3742 : /*
3743 : * SetRelationTableSpace
3744 : * Set new reltablespace and relfilenumber in pg_class entry.
3745 : *
3746 : * newTableSpaceId is the new tablespace for the relation, and
3747 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3748 : * InvalidRelFileNumber, this field is not updated.
3749 : *
3750 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3751 : *
3752 : * The caller of this routine had better check if a relation can be
3753 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3754 : * first, and is responsible for making the change visible with
3755 : * CommandCounterIncrement().
3756 : */
3757 : void
3758 105 : SetRelationTableSpace(Relation rel,
3759 : Oid newTableSpaceId,
3760 : RelFileNumber newRelFilenumber)
3761 : {
3762 : Relation pg_class;
3763 : HeapTuple tuple;
3764 : ItemPointerData otid;
3765 : Form_pg_class rd_rel;
3766 105 : Oid reloid = RelationGetRelid(rel);
3767 :
3768 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3769 :
3770 : /* Get a modifiable copy of the relation's pg_class row. */
3771 105 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3772 :
3773 105 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3774 105 : if (!HeapTupleIsValid(tuple))
3775 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3776 105 : otid = tuple->t_self;
3777 105 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3778 :
3779 : /* Update the pg_class row. */
3780 210 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3781 105 : InvalidOid : newTableSpaceId;
3782 105 : if (RelFileNumberIsValid(newRelFilenumber))
3783 83 : rd_rel->relfilenode = newRelFilenumber;
3784 105 : CatalogTupleUpdate(pg_class, &otid, tuple);
3785 105 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3786 :
3787 : /*
3788 : * Record dependency on tablespace. This is only required for relations
3789 : * that have no physical storage.
3790 : */
3791 105 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3792 15 : changeDependencyOnTablespace(RelationRelationId, reloid,
3793 : rd_rel->reltablespace);
3794 :
3795 105 : heap_freetuple(tuple);
3796 105 : table_close(pg_class, RowExclusiveLock);
3797 105 : }
3798 :
3799 : /*
3800 : * renameatt_check - basic sanity checks before attribute rename
3801 : */
3802 : static void
3803 504 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3804 : {
3805 504 : char relkind = classform->relkind;
3806 :
3807 504 : if (classform->reloftype && !recursing)
3808 3 : ereport(ERROR,
3809 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3810 : errmsg("cannot rename column of typed table")));
3811 :
3812 : /*
3813 : * Renaming the columns of sequences or toast tables doesn't actually
3814 : * break anything from the system's point of view, since internal
3815 : * references are by attnum. But it doesn't seem right to allow users to
3816 : * change names that are hardcoded into the system, hence the following
3817 : * restriction.
3818 : */
3819 501 : if (relkind != RELKIND_RELATION &&
3820 42 : relkind != RELKIND_VIEW &&
3821 42 : relkind != RELKIND_MATVIEW &&
3822 18 : relkind != RELKIND_COMPOSITE_TYPE &&
3823 18 : relkind != RELKIND_INDEX &&
3824 18 : relkind != RELKIND_PARTITIONED_INDEX &&
3825 0 : relkind != RELKIND_FOREIGN_TABLE &&
3826 : relkind != RELKIND_PARTITIONED_TABLE)
3827 0 : ereport(ERROR,
3828 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3829 : errmsg("cannot rename columns of relation \"%s\"",
3830 : NameStr(classform->relname)),
3831 : errdetail_relkind_not_supported(relkind)));
3832 :
3833 : /*
3834 : * permissions checking. only the owner of a class can change its schema.
3835 : */
3836 501 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3837 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3838 0 : NameStr(classform->relname));
3839 501 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3840 1 : ereport(ERROR,
3841 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3842 : errmsg("permission denied: \"%s\" is a system catalog",
3843 : NameStr(classform->relname))));
3844 500 : }
3845 :
3846 : /*
3847 : * renameatt_internal - workhorse for renameatt
3848 : *
3849 : * Return value is the attribute number in the 'myrelid' relation.
3850 : */
3851 : static AttrNumber
3852 276 : renameatt_internal(Oid myrelid,
3853 : const char *oldattname,
3854 : const char *newattname,
3855 : bool recurse,
3856 : bool recursing,
3857 : int expected_parents,
3858 : DropBehavior behavior)
3859 : {
3860 : Relation targetrelation;
3861 : Relation attrelation;
3862 : HeapTuple atttup;
3863 : Form_pg_attribute attform;
3864 : AttrNumber attnum;
3865 :
3866 : /*
3867 : * Grab an exclusive lock on the target table, which we will NOT release
3868 : * until end of transaction.
3869 : */
3870 276 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3871 276 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3872 :
3873 : /*
3874 : * if the 'recurse' flag is set then we are supposed to rename this
3875 : * attribute in all classes that inherit from 'relname' (as well as in
3876 : * 'relname').
3877 : *
3878 : * any permissions or problems with duplicate attributes will cause the
3879 : * whole transaction to abort, which is what we want -- all or nothing.
3880 : */
3881 276 : if (recurse)
3882 : {
3883 : List *child_oids,
3884 : *child_numparents;
3885 : ListCell *lo,
3886 : *li;
3887 :
3888 : /*
3889 : * we need the number of parents for each child so that the recursive
3890 : * calls to renameatt() can determine whether there are any parents
3891 : * outside the inheritance hierarchy being processed.
3892 : */
3893 124 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3894 : &child_numparents);
3895 :
3896 : /*
3897 : * find_all_inheritors does the recursive search of the inheritance
3898 : * hierarchy, so all we have to do is process all of the relids in the
3899 : * list that it returns.
3900 : */
3901 367 : forboth(lo, child_oids, li, child_numparents)
3902 : {
3903 258 : Oid childrelid = lfirst_oid(lo);
3904 258 : int numparents = lfirst_int(li);
3905 :
3906 258 : if (childrelid == myrelid)
3907 124 : continue;
3908 : /* note we need not recurse again */
3909 134 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3910 : }
3911 : }
3912 : else
3913 : {
3914 : /*
3915 : * If we are told not to recurse, there had better not be any child
3916 : * tables; else the rename would put them out of step.
3917 : *
3918 : * expected_parents will only be 0 if we are not already recursing.
3919 : */
3920 170 : if (expected_parents == 0 &&
3921 18 : find_inheritance_children(myrelid, NoLock) != NIL)
3922 6 : ereport(ERROR,
3923 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3924 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3925 : oldattname)));
3926 : }
3927 :
3928 : /* rename attributes in typed tables of composite type */
3929 255 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3930 : {
3931 : List *child_oids;
3932 : ListCell *lo;
3933 :
3934 12 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3935 12 : RelationGetRelationName(targetrelation),
3936 : behavior);
3937 :
3938 12 : foreach(lo, child_oids)
3939 3 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3940 : }
3941 :
3942 252 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3943 :
3944 252 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3945 252 : if (!HeapTupleIsValid(atttup))
3946 12 : ereport(ERROR,
3947 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3948 : errmsg("column \"%s\" does not exist",
3949 : oldattname)));
3950 240 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3951 :
3952 240 : attnum = attform->attnum;
3953 240 : if (attnum <= 0)
3954 0 : ereport(ERROR,
3955 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3956 : errmsg("cannot rename system column \"%s\"",
3957 : oldattname)));
3958 :
3959 : /*
3960 : * if the attribute is inherited, forbid the renaming. if this is a
3961 : * top-level call to renameatt(), then expected_parents will be 0, so the
3962 : * effect of this code will be to prohibit the renaming if the attribute
3963 : * is inherited at all. if this is a recursive call to renameatt(),
3964 : * expected_parents will be the number of parents the current relation has
3965 : * within the inheritance hierarchy being processed, so we'll prohibit the
3966 : * renaming only if there are additional parents from elsewhere.
3967 : */
3968 240 : if (attform->attinhcount > expected_parents)
3969 15 : ereport(ERROR,
3970 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3971 : errmsg("cannot rename inherited column \"%s\"",
3972 : oldattname)));
3973 :
3974 : /* new name should not already exist */
3975 225 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3976 :
3977 : /* apply the update */
3978 219 : namestrcpy(&(attform->attname), newattname);
3979 :
3980 219 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3981 :
3982 219 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3983 :
3984 219 : heap_freetuple(atttup);
3985 :
3986 219 : table_close(attrelation, RowExclusiveLock);
3987 :
3988 219 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3989 :
3990 219 : return attnum;
3991 : }
3992 :
3993 : /*
3994 : * Perform permissions and integrity checks before acquiring a relation lock.
3995 : */
3996 : static void
3997 204 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3998 : void *arg)
3999 : {
4000 : HeapTuple tuple;
4001 : Form_pg_class form;
4002 :
4003 204 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
4004 204 : if (!HeapTupleIsValid(tuple))
4005 18 : return; /* concurrently dropped */
4006 186 : form = (Form_pg_class) GETSTRUCT(tuple);
4007 186 : renameatt_check(relid, form, false);
4008 182 : ReleaseSysCache(tuple);
4009 : }
4010 :
4011 : /*
4012 : * renameatt - changes the name of an attribute in a relation
4013 : *
4014 : * The returned ObjectAddress is that of the renamed column.
4015 : */
4016 : ObjectAddress
4017 158 : renameatt(RenameStmt *stmt)
4018 : {
4019 : Oid relid;
4020 : AttrNumber attnum;
4021 : ObjectAddress address;
4022 :
4023 : /* lock level taken here should match renameatt_internal */
4024 158 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4025 158 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4026 : RangeVarCallbackForRenameAttribute,
4027 : NULL);
4028 :
4029 151 : if (!OidIsValid(relid))
4030 : {
4031 12 : ereport(NOTICE,
4032 : (errmsg("relation \"%s\" does not exist, skipping",
4033 : stmt->relation->relname)));
4034 12 : return InvalidObjectAddress;
4035 : }
4036 :
4037 : attnum =
4038 139 : renameatt_internal(relid,
4039 139 : stmt->subname, /* old att name */
4040 139 : stmt->newname, /* new att name */
4041 139 : stmt->relation->inh, /* recursive? */
4042 : false, /* recursing? */
4043 : 0, /* expected inhcount */
4044 : stmt->behavior);
4045 :
4046 97 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4047 :
4048 97 : return address;
4049 : }
4050 :
4051 : /*
4052 : * same logic as renameatt_internal
4053 : */
4054 : static ObjectAddress
4055 45 : rename_constraint_internal(Oid myrelid,
4056 : Oid mytypid,
4057 : const char *oldconname,
4058 : const char *newconname,
4059 : bool recurse,
4060 : bool recursing,
4061 : int expected_parents)
4062 : {
4063 45 : Relation targetrelation = NULL;
4064 : Oid constraintOid;
4065 : HeapTuple tuple;
4066 : Form_pg_constraint con;
4067 : ObjectAddress address;
4068 :
4069 : Assert(!myrelid || !mytypid);
4070 :
4071 45 : if (mytypid)
4072 : {
4073 3 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4074 : }
4075 : else
4076 : {
4077 42 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4078 :
4079 : /*
4080 : * don't tell it whether we're recursing; we allow changing typed
4081 : * tables here
4082 : */
4083 42 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4084 :
4085 42 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4086 : }
4087 :
4088 45 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4089 45 : if (!HeapTupleIsValid(tuple))
4090 0 : elog(ERROR, "cache lookup failed for constraint %u",
4091 : constraintOid);
4092 45 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4093 :
4094 45 : if (myrelid &&
4095 42 : (con->contype == CONSTRAINT_CHECK ||
4096 12 : con->contype == CONSTRAINT_NOTNULL) &&
4097 33 : !con->connoinherit)
4098 : {
4099 27 : if (recurse)
4100 : {
4101 : List *child_oids,
4102 : *child_numparents;
4103 : ListCell *lo,
4104 : *li;
4105 :
4106 18 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4107 : &child_numparents);
4108 :
4109 42 : forboth(lo, child_oids, li, child_numparents)
4110 : {
4111 24 : Oid childrelid = lfirst_oid(lo);
4112 24 : int numparents = lfirst_int(li);
4113 :
4114 24 : if (childrelid == myrelid)
4115 18 : continue;
4116 :
4117 6 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4118 : }
4119 : }
4120 : else
4121 : {
4122 12 : if (expected_parents == 0 &&
4123 3 : find_inheritance_children(myrelid, NoLock) != NIL)
4124 3 : ereport(ERROR,
4125 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4126 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4127 : oldconname)));
4128 : }
4129 :
4130 24 : if (con->coninhcount > expected_parents)
4131 3 : ereport(ERROR,
4132 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4133 : errmsg("cannot rename inherited constraint \"%s\"",
4134 : oldconname)));
4135 : }
4136 :
4137 39 : if (con->conindid
4138 9 : && (con->contype == CONSTRAINT_PRIMARY
4139 3 : || con->contype == CONSTRAINT_UNIQUE
4140 0 : || con->contype == CONSTRAINT_EXCLUSION))
4141 : /* rename the index; this renames the constraint as well */
4142 9 : RenameRelationInternal(con->conindid, newconname, false, true);
4143 : else
4144 30 : RenameConstraintById(constraintOid, newconname);
4145 :
4146 39 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4147 :
4148 39 : ReleaseSysCache(tuple);
4149 :
4150 39 : if (targetrelation)
4151 : {
4152 : /*
4153 : * Invalidate relcache so as others can see the new constraint name.
4154 : */
4155 36 : CacheInvalidateRelcache(targetrelation);
4156 :
4157 36 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4158 : }
4159 :
4160 39 : return address;
4161 : }
4162 :
4163 : ObjectAddress
4164 42 : RenameConstraint(RenameStmt *stmt)
4165 : {
4166 42 : Oid relid = InvalidOid;
4167 42 : Oid typid = InvalidOid;
4168 :
4169 42 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4170 : {
4171 : Relation rel;
4172 : HeapTuple tup;
4173 :
4174 3 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4175 3 : rel = table_open(TypeRelationId, RowExclusiveLock);
4176 3 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4177 3 : if (!HeapTupleIsValid(tup))
4178 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4179 3 : checkDomainOwner(tup);
4180 3 : ReleaseSysCache(tup);
4181 3 : table_close(rel, NoLock);
4182 : }
4183 : else
4184 : {
4185 : /* lock level taken here should match rename_constraint_internal */
4186 39 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4187 39 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4188 : RangeVarCallbackForRenameAttribute,
4189 : NULL);
4190 39 : if (!OidIsValid(relid))
4191 : {
4192 3 : ereport(NOTICE,
4193 : (errmsg("relation \"%s\" does not exist, skipping",
4194 : stmt->relation->relname)));
4195 3 : return InvalidObjectAddress;
4196 : }
4197 : }
4198 :
4199 : return
4200 39 : rename_constraint_internal(relid, typid,
4201 39 : stmt->subname,
4202 39 : stmt->newname,
4203 75 : (stmt->relation &&
4204 36 : stmt->relation->inh), /* recursive? */
4205 : false, /* recursing? */
4206 39 : 0 /* expected inhcount */ );
4207 : }
4208 :
4209 : /*
4210 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4211 : * RENAME
4212 : */
4213 : ObjectAddress
4214 259 : RenameRelation(RenameStmt *stmt)
4215 : {
4216 259 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4217 : Oid relid;
4218 : ObjectAddress address;
4219 :
4220 : /*
4221 : * Grab an exclusive lock on the target table, index, sequence, view,
4222 : * materialized view, or foreign table, which we will NOT release until
4223 : * end of transaction.
4224 : *
4225 : * Lock level used here should match RenameRelationInternal, to avoid lock
4226 : * escalation. However, because ALTER INDEX can be used with any relation
4227 : * type, we mustn't believe without verification.
4228 : */
4229 : for (;;)
4230 6 : {
4231 : LOCKMODE lockmode;
4232 : char relkind;
4233 : bool obj_is_index;
4234 :
4235 265 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4236 :
4237 265 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4238 265 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4239 : RangeVarCallbackForAlterRelation,
4240 : stmt);
4241 :
4242 240 : if (!OidIsValid(relid))
4243 : {
4244 9 : ereport(NOTICE,
4245 : (errmsg("relation \"%s\" does not exist, skipping",
4246 : stmt->relation->relname)));
4247 9 : return InvalidObjectAddress;
4248 : }
4249 :
4250 : /*
4251 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4252 : * to rename a table), but we might've used the wrong lock level. If
4253 : * that happens, retry with the correct lock level. We don't bother
4254 : * if we already acquired AccessExclusiveLock with an index, however.
4255 : */
4256 231 : relkind = get_rel_relkind(relid);
4257 231 : obj_is_index = (relkind == RELKIND_INDEX ||
4258 : relkind == RELKIND_PARTITIONED_INDEX);
4259 231 : if (obj_is_index || is_index_stmt == obj_is_index)
4260 : break;
4261 :
4262 6 : UnlockRelationOid(relid, lockmode);
4263 6 : is_index_stmt = obj_is_index;
4264 : }
4265 :
4266 : /* Do the work */
4267 225 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4268 :
4269 219 : ObjectAddressSet(address, RelationRelationId, relid);
4270 :
4271 219 : return address;
4272 : }
4273 :
4274 : /*
4275 : * RenameRelationInternal - change the name of a relation
4276 : */
4277 : void
4278 875 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4279 : {
4280 : Relation targetrelation;
4281 : Relation relrelation; /* for RELATION relation */
4282 : ItemPointerData otid;
4283 : HeapTuple reltup;
4284 : Form_pg_class relform;
4285 : Oid namespaceId;
4286 :
4287 : /*
4288 : * Grab a lock on the target relation, which we will NOT release until end
4289 : * of transaction. We need at least a self-exclusive lock so that
4290 : * concurrent DDL doesn't overwrite the rename if they start updating
4291 : * while still seeing the old version. The lock also guards against
4292 : * triggering relcache reloads in concurrent sessions, which might not
4293 : * handle this information changing under them. For indexes, we can use a
4294 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4295 : * specially.
4296 : */
4297 875 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4298 875 : namespaceId = RelationGetNamespace(targetrelation);
4299 :
4300 : /*
4301 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4302 : */
4303 875 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4304 :
4305 875 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4306 875 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4307 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4308 875 : otid = reltup->t_self;
4309 875 : relform = (Form_pg_class) GETSTRUCT(reltup);
4310 :
4311 875 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4312 6 : ereport(ERROR,
4313 : (errcode(ERRCODE_DUPLICATE_TABLE),
4314 : errmsg("relation \"%s\" already exists",
4315 : newrelname)));
4316 :
4317 : /*
4318 : * RenameRelation is careful not to believe the caller's idea of the
4319 : * relation kind being handled. We don't have to worry about this, but
4320 : * let's not be totally oblivious to it. We can process an index as
4321 : * not-an-index, but not the other way around.
4322 : */
4323 : Assert(!is_index ||
4324 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4325 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4326 :
4327 : /*
4328 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4329 : * because it's a copy...)
4330 : */
4331 869 : namestrcpy(&(relform->relname), newrelname);
4332 :
4333 869 : CatalogTupleUpdate(relrelation, &otid, reltup);
4334 869 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4335 :
4336 869 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4337 : InvalidOid, is_internal);
4338 :
4339 869 : heap_freetuple(reltup);
4340 869 : table_close(relrelation, RowExclusiveLock);
4341 :
4342 : /*
4343 : * Also rename the associated type, if any.
4344 : */
4345 869 : if (OidIsValid(targetrelation->rd_rel->reltype))
4346 98 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4347 : newrelname, namespaceId);
4348 :
4349 : /*
4350 : * Also rename the associated constraint, if any.
4351 : */
4352 869 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4353 473 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4354 : {
4355 405 : Oid constraintId = get_index_constraint(myrelid);
4356 :
4357 405 : if (OidIsValid(constraintId))
4358 18 : RenameConstraintById(constraintId, newrelname);
4359 : }
4360 :
4361 : /*
4362 : * Close rel, but keep lock!
4363 : */
4364 869 : relation_close(targetrelation, NoLock);
4365 869 : }
4366 :
4367 : /*
4368 : * ResetRelRewrite - reset relrewrite
4369 : */
4370 : void
4371 299 : ResetRelRewrite(Oid myrelid)
4372 : {
4373 : Relation relrelation; /* for RELATION relation */
4374 : HeapTuple reltup;
4375 : Form_pg_class relform;
4376 :
4377 : /*
4378 : * Find relation's pg_class tuple.
4379 : */
4380 299 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4381 :
4382 299 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4383 299 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4384 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4385 299 : relform = (Form_pg_class) GETSTRUCT(reltup);
4386 :
4387 : /*
4388 : * Update pg_class tuple.
4389 : */
4390 299 : relform->relrewrite = InvalidOid;
4391 :
4392 299 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4393 :
4394 299 : heap_freetuple(reltup);
4395 299 : table_close(relrelation, RowExclusiveLock);
4396 299 : }
4397 :
4398 : /*
4399 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4400 : * any open reference to the target table besides the one just acquired by
4401 : * the calling command; this implies there's an open cursor or active plan.
4402 : * We need this check because our lock doesn't protect us against stomping
4403 : * on our own foot, only other people's feet!
4404 : *
4405 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4406 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4407 : * possibly be relaxed to only error out for certain types of alterations.
4408 : * But the use-case for allowing any of these things is not obvious, so we
4409 : * won't work hard at it for now.
4410 : *
4411 : * We also reject these commands if there are any pending AFTER trigger events
4412 : * for the rel. This is certainly necessary for the rewriting variants of
4413 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4414 : * events would try to fetch the wrong tuples. It might be overly cautious
4415 : * in other cases, but again it seems better to err on the side of paranoia.
4416 : *
4417 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4418 : * we are worried about active indexscans on the index. The trigger-event
4419 : * check can be skipped, since we are doing no damage to the parent table.
4420 : *
4421 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4422 : */
4423 : void
4424 89394 : CheckTableNotInUse(Relation rel, const char *stmt)
4425 : {
4426 : int expected_refcnt;
4427 :
4428 89394 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4429 89394 : if (rel->rd_refcnt != expected_refcnt)
4430 21 : ereport(ERROR,
4431 : (errcode(ERRCODE_OBJECT_IN_USE),
4432 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4433 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4434 : stmt, RelationGetRelationName(rel))));
4435 :
4436 89373 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4437 145922 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4438 72399 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4439 9 : ereport(ERROR,
4440 : (errcode(ERRCODE_OBJECT_IN_USE),
4441 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4442 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4443 : stmt, RelationGetRelationName(rel))));
4444 89364 : }
4445 :
4446 : /*
4447 : * CheckAlterTableIsSafe
4448 : * Verify that it's safe to allow ALTER TABLE on this relation.
4449 : *
4450 : * This consists of CheckTableNotInUse() plus a check that the relation
4451 : * isn't another session's temp table. We must split out the temp-table
4452 : * check because there are callers of CheckTableNotInUse() that don't want
4453 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4454 : * an orphaned temp schema.) Compare truncate_check_activity().
4455 : */
4456 : static void
4457 31929 : CheckAlterTableIsSafe(Relation rel)
4458 : {
4459 : /*
4460 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4461 : * manager is not going to cope if we need to change the table's contents.
4462 : * Even if we don't, there may be optimizations that assume temp tables
4463 : * aren't subject to such interference.
4464 : */
4465 31929 : if (RELATION_IS_OTHER_TEMP(rel))
4466 0 : ereport(ERROR,
4467 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4468 : errmsg("cannot alter temporary tables of other sessions")));
4469 :
4470 : /*
4471 : * Also check for active uses of the relation in the current transaction,
4472 : * including open scans and pending AFTER trigger events.
4473 : */
4474 31929 : CheckTableNotInUse(rel, "ALTER TABLE");
4475 31911 : }
4476 :
4477 : /*
4478 : * AlterTableLookupRelation
4479 : * Look up, and lock, the OID for the relation named by an alter table
4480 : * statement.
4481 : */
4482 : Oid
4483 16899 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4484 : {
4485 33748 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4486 16899 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4487 : RangeVarCallbackForAlterRelation,
4488 : stmt);
4489 : }
4490 :
4491 : /*
4492 : * AlterTable
4493 : * Execute ALTER TABLE, which can be a list of subcommands
4494 : *
4495 : * ALTER TABLE is performed in three phases:
4496 : * 1. Examine subcommands and perform pre-transformation checking.
4497 : * 2. Validate and transform subcommands, and update system catalogs.
4498 : * 3. Scan table(s) to check new constraints, and optionally recopy
4499 : * the data into new table(s).
4500 : * Phase 3 is not performed unless one or more of the subcommands requires
4501 : * it. The intention of this design is to allow multiple independent
4502 : * updates of the table schema to be performed with only one pass over the
4503 : * data.
4504 : *
4505 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4506 : * each table to be affected (there may be multiple affected tables if the
4507 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4508 : * validation of the subcommands. Because earlier subcommands may change
4509 : * the catalog state seen by later commands, there are limits to what can
4510 : * be done in this phase. Generally, this phase acquires table locks,
4511 : * checks permissions and relkind, and recurses to find child tables.
4512 : *
4513 : * ATRewriteCatalogs performs phase 2 for each affected table.
4514 : * Certain subcommands need to be performed before others to avoid
4515 : * unnecessary conflicts; for example, DROP COLUMN should come before
4516 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4517 : * lists, one for each logical "pass" of phase 2.
4518 : *
4519 : * ATRewriteTables performs phase 3 for those tables that need it.
4520 : *
4521 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4522 : * since phase 1 already does it. However, for certain subcommand types
4523 : * it is only possible to determine how to recurse at phase 2 time; for
4524 : * those cases, phase 1 sets the cmd->recurse flag.
4525 : *
4526 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4527 : * the whole operation; we don't have to do anything special to clean up.
4528 : *
4529 : * The caller must lock the relation, with an appropriate lock level
4530 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4531 : * or higher. We pass the lock level down
4532 : * so that we can apply it recursively to inherited tables. Note that the
4533 : * lock level we want as we recurse might well be higher than required for
4534 : * that specific subcommand. So we pass down the overall lock requirement,
4535 : * rather than reassess it at lower levels.
4536 : *
4537 : * The caller also provides a "context" which is to be passed back to
4538 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4539 : * Some of the fields therein, such as the relid, are used here as well.
4540 : */
4541 : void
4542 16780 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4543 : AlterTableUtilityContext *context)
4544 : {
4545 : Relation rel;
4546 :
4547 : /* Caller is required to provide an adequate lock. */
4548 16780 : rel = relation_open(context->relid, NoLock);
4549 :
4550 16780 : CheckAlterTableIsSafe(rel);
4551 :
4552 16771 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4553 14737 : }
4554 :
4555 : /*
4556 : * AlterTableInternal
4557 : *
4558 : * ALTER TABLE with target specified by OID
4559 : *
4560 : * We do not reject if the relation is already open, because it's quite
4561 : * likely that one or more layers of caller have it open. That means it
4562 : * is unsafe to use this entry point for alterations that could break
4563 : * existing query plans. On the assumption it's not used for such, we
4564 : * don't have to reject pending AFTER triggers, either.
4565 : *
4566 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4567 : * used for any subcommand types that require parse transformation or
4568 : * could generate subcommands that have to be passed to ProcessUtility.
4569 : */
4570 : void
4571 139 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4572 : {
4573 : Relation rel;
4574 139 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4575 :
4576 139 : rel = relation_open(relid, lockmode);
4577 :
4578 139 : EventTriggerAlterTableRelid(relid);
4579 :
4580 139 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4581 139 : }
4582 :
4583 : /*
4584 : * AlterTableGetLockLevel
4585 : *
4586 : * Sets the overall lock level required for the supplied list of subcommands.
4587 : * Policy for doing this set according to needs of AlterTable(), see
4588 : * comments there for overall explanation.
4589 : *
4590 : * Function is called before and after parsing, so it must give same
4591 : * answer each time it is called. Some subcommands are transformed
4592 : * into other subcommand types, so the transform must never be made to a
4593 : * lower lock level than previously assigned. All transforms are noted below.
4594 : *
4595 : * Since this is called before we lock the table we cannot use table metadata
4596 : * to influence the type of lock we acquire.
4597 : *
4598 : * There should be no lockmodes hardcoded into the subcommand functions. All
4599 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4600 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4601 : * and does not travel through this section of code and cannot be combined with
4602 : * any of the subcommands given here.
4603 : *
4604 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4605 : * so any changes that might affect SELECTs running on standbys need to use
4606 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4607 : * have a solution for that also.
4608 : *
4609 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4610 : * that takes a lock less than AccessExclusiveLock can change object definitions
4611 : * while pg_dump is running. Be careful to check that the appropriate data is
4612 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4613 : * otherwise we might end up with an inconsistent dump that can't restore.
4614 : */
4615 : LOCKMODE
4616 17038 : AlterTableGetLockLevel(List *cmds)
4617 : {
4618 : /*
4619 : * This only works if we read catalog tables using MVCC snapshots.
4620 : */
4621 : ListCell *lcmd;
4622 17038 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4623 :
4624 34677 : foreach(lcmd, cmds)
4625 : {
4626 17639 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4627 17639 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4628 :
4629 17639 : switch (cmd->subtype)
4630 : {
4631 : /*
4632 : * These subcommands rewrite the heap, so require full locks.
4633 : */
4634 1879 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4635 : * to SELECT */
4636 : case AT_SetAccessMethod: /* must rewrite heap */
4637 : case AT_SetTableSpace: /* must rewrite heap */
4638 : case AT_AlterColumnType: /* must rewrite heap */
4639 1879 : cmd_lockmode = AccessExclusiveLock;
4640 1879 : break;
4641 :
4642 : /*
4643 : * These subcommands may require addition of toast tables. If
4644 : * we add a toast table to a table currently being scanned, we
4645 : * might miss data added to the new toast table by concurrent
4646 : * insert transactions.
4647 : */
4648 131 : case AT_SetStorage: /* may add toast tables, see
4649 : * ATRewriteCatalogs() */
4650 131 : cmd_lockmode = AccessExclusiveLock;
4651 131 : break;
4652 :
4653 : /*
4654 : * Removing constraints can affect SELECTs that have been
4655 : * optimized assuming the constraint holds true. See also
4656 : * CloneFkReferenced.
4657 : */
4658 568 : case AT_DropConstraint: /* as DROP INDEX */
4659 : case AT_DropNotNull: /* may change some SQL plans */
4660 568 : cmd_lockmode = AccessExclusiveLock;
4661 568 : break;
4662 :
4663 : /*
4664 : * Subcommands that may be visible to concurrent SELECTs
4665 : */
4666 897 : case AT_DropColumn: /* change visible to SELECT */
4667 : case AT_AddColumnToView: /* CREATE VIEW */
4668 : case AT_DropOids: /* used to equiv to DropColumn */
4669 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4670 : case AT_EnableReplicaRule: /* may change SELECT rules */
4671 : case AT_EnableRule: /* may change SELECT rules */
4672 : case AT_DisableRule: /* may change SELECT rules */
4673 897 : cmd_lockmode = AccessExclusiveLock;
4674 897 : break;
4675 :
4676 : /*
4677 : * Changing owner may remove implicit SELECT privileges
4678 : */
4679 1041 : case AT_ChangeOwner: /* change visible to SELECT */
4680 1041 : cmd_lockmode = AccessExclusiveLock;
4681 1041 : break;
4682 :
4683 : /*
4684 : * Changing foreign table options may affect optimization.
4685 : */
4686 127 : case AT_GenericOptions:
4687 : case AT_AlterColumnGenericOptions:
4688 127 : cmd_lockmode = AccessExclusiveLock;
4689 127 : break;
4690 :
4691 : /*
4692 : * These subcommands affect write operations only.
4693 : */
4694 171 : case AT_EnableTrig:
4695 : case AT_EnableAlwaysTrig:
4696 : case AT_EnableReplicaTrig:
4697 : case AT_EnableTrigAll:
4698 : case AT_EnableTrigUser:
4699 : case AT_DisableTrig:
4700 : case AT_DisableTrigAll:
4701 : case AT_DisableTrigUser:
4702 171 : cmd_lockmode = ShareRowExclusiveLock;
4703 171 : break;
4704 :
4705 : /*
4706 : * These subcommands affect write operations only. XXX
4707 : * Theoretically, these could be ShareRowExclusiveLock.
4708 : */
4709 1484 : case AT_ColumnDefault:
4710 : case AT_CookedColumnDefault:
4711 : case AT_AlterConstraint:
4712 : case AT_AddIndex: /* from ADD CONSTRAINT */
4713 : case AT_AddIndexConstraint:
4714 : case AT_ReplicaIdentity:
4715 : case AT_SetNotNull:
4716 : case AT_EnableRowSecurity:
4717 : case AT_DisableRowSecurity:
4718 : case AT_ForceRowSecurity:
4719 : case AT_NoForceRowSecurity:
4720 : case AT_AddIdentity:
4721 : case AT_DropIdentity:
4722 : case AT_SetIdentity:
4723 : case AT_SetExpression:
4724 : case AT_DropExpression:
4725 : case AT_SetCompression:
4726 1484 : cmd_lockmode = AccessExclusiveLock;
4727 1484 : break;
4728 :
4729 7993 : case AT_AddConstraint:
4730 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4731 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4732 7993 : if (IsA(cmd->def, Constraint))
4733 : {
4734 7993 : Constraint *con = (Constraint *) cmd->def;
4735 :
4736 7993 : switch (con->contype)
4737 : {
4738 6073 : case CONSTR_EXCLUSION:
4739 : case CONSTR_PRIMARY:
4740 : case CONSTR_UNIQUE:
4741 :
4742 : /*
4743 : * Cases essentially the same as CREATE INDEX. We
4744 : * could reduce the lock strength to ShareLock if
4745 : * we can work out how to allow concurrent catalog
4746 : * updates. XXX Might be set down to
4747 : * ShareRowExclusiveLock but requires further
4748 : * analysis.
4749 : */
4750 6073 : cmd_lockmode = AccessExclusiveLock;
4751 6073 : break;
4752 1309 : case CONSTR_FOREIGN:
4753 :
4754 : /*
4755 : * We add triggers to both tables when we add a
4756 : * Foreign Key, so the lock level must be at least
4757 : * as strong as CREATE TRIGGER.
4758 : */
4759 1309 : cmd_lockmode = ShareRowExclusiveLock;
4760 1309 : break;
4761 :
4762 611 : default:
4763 611 : cmd_lockmode = AccessExclusiveLock;
4764 : }
4765 : }
4766 7993 : break;
4767 :
4768 : /*
4769 : * These subcommands affect inheritance behaviour. Queries
4770 : * started before us will continue to see the old inheritance
4771 : * behaviour, while queries started after we commit will see
4772 : * new behaviour. No need to prevent reads or writes to the
4773 : * subtable while we hook it up though. Changing the TupDesc
4774 : * may be a problem, so keep highest lock.
4775 : */
4776 279 : case AT_AddInherit:
4777 : case AT_DropInherit:
4778 279 : cmd_lockmode = AccessExclusiveLock;
4779 279 : break;
4780 :
4781 : /*
4782 : * These subcommands affect implicit row type conversion. They
4783 : * have affects similar to CREATE/DROP CAST on queries. don't
4784 : * provide for invalidating parse trees as a result of such
4785 : * changes, so we keep these at AccessExclusiveLock.
4786 : */
4787 36 : case AT_AddOf:
4788 : case AT_DropOf:
4789 36 : cmd_lockmode = AccessExclusiveLock;
4790 36 : break;
4791 :
4792 : /*
4793 : * Only used by CREATE OR REPLACE VIEW which must conflict
4794 : * with an SELECTs currently using the view.
4795 : */
4796 97 : case AT_ReplaceRelOptions:
4797 97 : cmd_lockmode = AccessExclusiveLock;
4798 97 : break;
4799 :
4800 : /*
4801 : * These subcommands affect general strategies for performance
4802 : * and maintenance, though don't change the semantic results
4803 : * from normal data reads and writes. Delaying an ALTER TABLE
4804 : * behind currently active writes only delays the point where
4805 : * the new strategy begins to take effect, so there is no
4806 : * benefit in waiting. In this case the minimum restriction
4807 : * applies: we don't currently allow concurrent catalog
4808 : * updates.
4809 : */
4810 117 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4811 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4812 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4813 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4814 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4815 117 : cmd_lockmode = ShareUpdateExclusiveLock;
4816 117 : break;
4817 :
4818 56 : case AT_SetLogged:
4819 : case AT_SetUnLogged:
4820 56 : cmd_lockmode = AccessExclusiveLock;
4821 56 : break;
4822 :
4823 241 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4824 241 : cmd_lockmode = ShareUpdateExclusiveLock;
4825 241 : break;
4826 :
4827 : /*
4828 : * Rel options are more complex than first appears. Options
4829 : * are set here for tables, views and indexes; for historical
4830 : * reasons these can all be used with ALTER TABLE, so we can't
4831 : * decide between them using the basic grammar.
4832 : */
4833 400 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4834 : * getTables() */
4835 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4836 : * getTables() */
4837 400 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4838 400 : break;
4839 :
4840 1481 : case AT_AttachPartition:
4841 1481 : cmd_lockmode = ShareUpdateExclusiveLock;
4842 1481 : break;
4843 :
4844 301 : case AT_DetachPartition:
4845 301 : if (((PartitionCmd *) cmd->def)->concurrent)
4846 82 : cmd_lockmode = ShareUpdateExclusiveLock;
4847 : else
4848 219 : cmd_lockmode = AccessExclusiveLock;
4849 301 : break;
4850 :
4851 10 : case AT_DetachPartitionFinalize:
4852 10 : cmd_lockmode = ShareUpdateExclusiveLock;
4853 10 : break;
4854 :
4855 330 : case AT_MergePartitions:
4856 : case AT_SplitPartition:
4857 330 : cmd_lockmode = AccessExclusiveLock;
4858 330 : break;
4859 :
4860 0 : default: /* oops */
4861 0 : elog(ERROR, "unrecognized alter table type: %d",
4862 : (int) cmd->subtype);
4863 : break;
4864 : }
4865 :
4866 : /*
4867 : * Take the greatest lockmode from any subcommand
4868 : */
4869 17639 : if (cmd_lockmode > lockmode)
4870 14808 : lockmode = cmd_lockmode;
4871 : }
4872 :
4873 17038 : return lockmode;
4874 : }
4875 :
4876 : /*
4877 : * ATController provides top level control over the phases.
4878 : *
4879 : * parsetree is passed in to allow it to be passed to event triggers
4880 : * when requested.
4881 : */
4882 : static void
4883 16910 : ATController(AlterTableStmt *parsetree,
4884 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4885 : AlterTableUtilityContext *context)
4886 : {
4887 16910 : List *wqueue = NIL;
4888 : ListCell *lcmd;
4889 :
4890 : /* Phase 1: preliminary examination of commands, create work queue */
4891 34212 : foreach(lcmd, cmds)
4892 : {
4893 17508 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4894 :
4895 17508 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4896 : }
4897 :
4898 : /* Close the relation, but keep lock until commit */
4899 16704 : relation_close(rel, NoLock);
4900 :
4901 : /* Phase 2: update system catalogs */
4902 16704 : ATRewriteCatalogs(&wqueue, lockmode, context);
4903 :
4904 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4905 15131 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4906 14876 : }
4907 :
4908 : /*
4909 : * ATPrepCmd
4910 : *
4911 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4912 : * recursion and permission checks.
4913 : *
4914 : * Caller must have acquired appropriate lock type on relation already.
4915 : * This lock should be held until commit.
4916 : */
4917 : static void
4918 17978 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4919 : bool recurse, bool recursing, LOCKMODE lockmode,
4920 : AlterTableUtilityContext *context)
4921 : {
4922 : AlteredTableInfo *tab;
4923 17978 : AlterTablePass pass = AT_PASS_UNSET;
4924 :
4925 : /* Find or create work queue entry for this table */
4926 17978 : tab = ATGetQueueEntry(wqueue, rel);
4927 :
4928 : /*
4929 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4930 : * partitions that are pending detach.
4931 : */
4932 17978 : if (rel->rd_rel->relispartition &&
4933 1400 : cmd->subtype != AT_DetachPartitionFinalize &&
4934 700 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4935 1 : ereport(ERROR,
4936 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4937 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4938 : RelationGetRelationName(rel)),
4939 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4940 :
4941 : /*
4942 : * Copy the original subcommand for each table, so we can scribble on it.
4943 : * This avoids conflicts when different child tables need to make
4944 : * different parse transformations (for example, the same column may have
4945 : * different column numbers in different children).
4946 : */
4947 17977 : cmd = copyObject(cmd);
4948 :
4949 : /*
4950 : * Do permissions and relkind checking, recursion to child tables if
4951 : * needed, and any additional phase-1 processing needed. (But beware of
4952 : * adding any processing that looks at table details that another
4953 : * subcommand could change. In some cases we reject multiple subcommands
4954 : * that could try to change the same state in contrary ways.)
4955 : */
4956 17977 : switch (cmd->subtype)
4957 : {
4958 1100 : case AT_AddColumn: /* ADD COLUMN */
4959 1100 : ATSimplePermissions(cmd->subtype, rel,
4960 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4961 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4962 1100 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4963 : lockmode, context);
4964 : /* Recursion occurs during execution phase */
4965 1094 : pass = AT_PASS_ADD_COL;
4966 1094 : break;
4967 12 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4968 12 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4969 12 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4970 : lockmode, context);
4971 : /* Recursion occurs during execution phase */
4972 12 : pass = AT_PASS_ADD_COL;
4973 12 : break;
4974 310 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4975 :
4976 : /*
4977 : * We allow defaults on views so that INSERT into a view can have
4978 : * default-ish behavior. This works because the rewriter
4979 : * substitutes default values into INSERTs before it expands
4980 : * rules.
4981 : */
4982 310 : ATSimplePermissions(cmd->subtype, rel,
4983 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4984 : ATT_FOREIGN_TABLE);
4985 310 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4986 : /* No command-specific prep needed */
4987 310 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4988 310 : break;
4989 40 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4990 : /* This is currently used only in CREATE TABLE */
4991 : /* (so the permission check really isn't necessary) */
4992 40 : ATSimplePermissions(cmd->subtype, rel,
4993 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4994 : /* This command never recurses */
4995 40 : pass = AT_PASS_ADD_OTHERCONSTR;
4996 40 : break;
4997 86 : case AT_AddIdentity:
4998 86 : ATSimplePermissions(cmd->subtype, rel,
4999 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5000 : ATT_FOREIGN_TABLE);
5001 : /* Set up recursion for phase 2; no other prep needed */
5002 86 : if (recurse)
5003 83 : cmd->recurse = true;
5004 86 : pass = AT_PASS_ADD_OTHERCONSTR;
5005 86 : break;
5006 31 : case AT_SetIdentity:
5007 31 : ATSimplePermissions(cmd->subtype, rel,
5008 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5009 : ATT_FOREIGN_TABLE);
5010 : /* Set up recursion for phase 2; no other prep needed */
5011 31 : if (recurse)
5012 28 : cmd->recurse = true;
5013 : /* This should run after AddIdentity, so do it in MISC pass */
5014 31 : pass = AT_PASS_MISC;
5015 31 : break;
5016 28 : case AT_DropIdentity:
5017 28 : ATSimplePermissions(cmd->subtype, rel,
5018 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5019 : ATT_FOREIGN_TABLE);
5020 : /* Set up recursion for phase 2; no other prep needed */
5021 28 : if (recurse)
5022 25 : cmd->recurse = true;
5023 28 : pass = AT_PASS_DROP;
5024 28 : break;
5025 137 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5026 137 : ATSimplePermissions(cmd->subtype, rel,
5027 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5028 : /* Set up recursion for phase 2; no other prep needed */
5029 134 : if (recurse)
5030 125 : cmd->recurse = true;
5031 134 : pass = AT_PASS_DROP;
5032 134 : break;
5033 210 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5034 210 : ATSimplePermissions(cmd->subtype, rel,
5035 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5036 : /* Set up recursion for phase 2; no other prep needed */
5037 207 : if (recurse)
5038 192 : cmd->recurse = true;
5039 207 : pass = AT_PASS_COL_ATTRS;
5040 207 : break;
5041 115 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5042 115 : ATSimplePermissions(cmd->subtype, rel,
5043 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5044 115 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5045 115 : pass = AT_PASS_SET_EXPRESSION;
5046 115 : break;
5047 43 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5048 43 : ATSimplePermissions(cmd->subtype, rel,
5049 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5050 43 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5051 43 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5052 31 : pass = AT_PASS_DROP;
5053 31 : break;
5054 82 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5055 82 : ATSimplePermissions(cmd->subtype, rel,
5056 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
5057 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
5058 82 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5059 : /* No command-specific prep needed */
5060 82 : pass = AT_PASS_MISC;
5061 82 : break;
5062 22 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5063 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5064 22 : ATSimplePermissions(cmd->subtype, rel,
5065 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5066 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5067 : /* This command never recurses */
5068 16 : pass = AT_PASS_MISC;
5069 16 : break;
5070 142 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5071 142 : ATSimplePermissions(cmd->subtype, rel,
5072 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5073 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5074 142 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5075 : /* No command-specific prep needed */
5076 142 : pass = AT_PASS_MISC;
5077 142 : break;
5078 39 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5079 39 : ATSimplePermissions(cmd->subtype, rel,
5080 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5081 : /* This command never recurses */
5082 : /* No command-specific prep needed */
5083 39 : pass = AT_PASS_MISC;
5084 39 : break;
5085 850 : case AT_DropColumn: /* DROP COLUMN */
5086 850 : ATSimplePermissions(cmd->subtype, rel,
5087 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5088 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5089 847 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5090 : lockmode, context);
5091 : /* Recursion occurs during execution phase */
5092 841 : pass = AT_PASS_DROP;
5093 841 : break;
5094 0 : case AT_AddIndex: /* ADD INDEX */
5095 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5096 : /* This command never recurses */
5097 : /* No command-specific prep needed */
5098 0 : pass = AT_PASS_ADD_INDEX;
5099 0 : break;
5100 8225 : case AT_AddConstraint: /* ADD CONSTRAINT */
5101 8225 : ATSimplePermissions(cmd->subtype, rel,
5102 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5103 8225 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5104 8210 : if (recurse)
5105 : {
5106 : /* recurses at exec time; lock descendants and set flag */
5107 8013 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5108 8013 : cmd->recurse = true;
5109 : }
5110 8210 : pass = AT_PASS_ADD_CONSTR;
5111 8210 : break;
5112 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5113 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5114 : /* This command never recurses */
5115 : /* No command-specific prep needed */
5116 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5117 0 : break;
5118 412 : case AT_DropConstraint: /* DROP CONSTRAINT */
5119 412 : ATSimplePermissions(cmd->subtype, rel,
5120 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5121 412 : ATCheckPartitionsNotInUse(rel, lockmode);
5122 : /* Other recursion occurs during execution phase */
5123 : /* No command-specific prep needed except saving recurse flag */
5124 409 : if (recurse)
5125 391 : cmd->recurse = true;
5126 409 : pass = AT_PASS_DROP;
5127 409 : break;
5128 716 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5129 716 : ATSimplePermissions(cmd->subtype, rel,
5130 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5131 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5132 : /* See comments for ATPrepAlterColumnType */
5133 716 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5134 : AT_PASS_UNSET, context);
5135 : Assert(cmd != NULL);
5136 : /* Performs own recursion */
5137 713 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5138 : lockmode, context);
5139 614 : pass = AT_PASS_ALTER_TYPE;
5140 614 : break;
5141 86 : case AT_AlterColumnGenericOptions:
5142 86 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5143 : /* This command never recurses */
5144 : /* No command-specific prep needed */
5145 86 : pass = AT_PASS_MISC;
5146 86 : break;
5147 1029 : case AT_ChangeOwner: /* ALTER OWNER */
5148 : /* This command never recurses */
5149 : /* No command-specific prep needed */
5150 1029 : pass = AT_PASS_MISC;
5151 1029 : break;
5152 32 : case AT_ClusterOn: /* CLUSTER ON */
5153 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5154 32 : ATSimplePermissions(cmd->subtype, rel,
5155 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5156 : /* These commands never recurse */
5157 : /* No command-specific prep needed */
5158 32 : pass = AT_PASS_MISC;
5159 32 : break;
5160 56 : case AT_SetLogged: /* SET LOGGED */
5161 : case AT_SetUnLogged: /* SET UNLOGGED */
5162 56 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5163 50 : if (tab->chgPersistence)
5164 0 : ereport(ERROR,
5165 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5166 : errmsg("cannot change persistence setting twice")));
5167 50 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5168 44 : pass = AT_PASS_MISC;
5169 44 : break;
5170 3 : case AT_DropOids: /* SET WITHOUT OIDS */
5171 3 : ATSimplePermissions(cmd->subtype, rel,
5172 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5173 3 : pass = AT_PASS_DROP;
5174 3 : break;
5175 64 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5176 64 : ATSimplePermissions(cmd->subtype, rel,
5177 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5178 :
5179 : /* check if another access method change was already requested */
5180 64 : if (tab->chgAccessMethod)
5181 9 : ereport(ERROR,
5182 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5183 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5184 :
5185 55 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5186 55 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5187 55 : break;
5188 85 : case AT_SetTableSpace: /* SET TABLESPACE */
5189 85 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5190 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5191 : /* This command never recurses */
5192 85 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5193 85 : pass = AT_PASS_MISC; /* doesn't actually matter */
5194 85 : break;
5195 496 : case AT_SetRelOptions: /* SET (...) */
5196 : case AT_ResetRelOptions: /* RESET (...) */
5197 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5198 496 : ATSimplePermissions(cmd->subtype, rel,
5199 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5200 : ATT_MATVIEW | ATT_INDEX);
5201 : /* This command never recurses */
5202 : /* No command-specific prep needed */
5203 495 : pass = AT_PASS_MISC;
5204 495 : break;
5205 232 : case AT_AddInherit: /* INHERIT */
5206 232 : ATSimplePermissions(cmd->subtype, rel,
5207 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5208 : /* This command never recurses */
5209 232 : ATPrepAddInherit(rel);
5210 223 : pass = AT_PASS_MISC;
5211 223 : break;
5212 47 : case AT_DropInherit: /* NO INHERIT */
5213 47 : ATSimplePermissions(cmd->subtype, rel,
5214 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5215 : /* This command never recurses */
5216 : /* No command-specific prep needed */
5217 47 : pass = AT_PASS_MISC;
5218 47 : break;
5219 150 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5220 150 : ATSimplePermissions(cmd->subtype, rel,
5221 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5222 : /* Recursion occurs during execution phase */
5223 147 : if (recurse)
5224 147 : cmd->recurse = true;
5225 147 : pass = AT_PASS_MISC;
5226 147 : break;
5227 241 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5228 241 : ATSimplePermissions(cmd->subtype, rel,
5229 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5230 : /* Recursion occurs during execution phase */
5231 : /* No command-specific prep needed except saving recurse flag */
5232 241 : if (recurse)
5233 241 : cmd->recurse = true;
5234 241 : pass = AT_PASS_MISC;
5235 241 : break;
5236 247 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5237 247 : ATSimplePermissions(cmd->subtype, rel,
5238 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5239 247 : pass = AT_PASS_MISC;
5240 : /* This command never recurses */
5241 : /* No command-specific prep needed */
5242 247 : break;
5243 171 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5244 : case AT_EnableAlwaysTrig:
5245 : case AT_EnableReplicaTrig:
5246 : case AT_EnableTrigAll:
5247 : case AT_EnableTrigUser:
5248 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5249 : case AT_DisableTrigAll:
5250 : case AT_DisableTrigUser:
5251 171 : ATSimplePermissions(cmd->subtype, rel,
5252 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5253 : /* Set up recursion for phase 2; no other prep needed */
5254 171 : if (recurse)
5255 157 : cmd->recurse = true;
5256 171 : pass = AT_PASS_MISC;
5257 171 : break;
5258 299 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5259 : case AT_EnableAlwaysRule:
5260 : case AT_EnableReplicaRule:
5261 : case AT_DisableRule:
5262 : case AT_AddOf: /* OF */
5263 : case AT_DropOf: /* NOT OF */
5264 : case AT_EnableRowSecurity:
5265 : case AT_DisableRowSecurity:
5266 : case AT_ForceRowSecurity:
5267 : case AT_NoForceRowSecurity:
5268 299 : ATSimplePermissions(cmd->subtype, rel,
5269 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5270 : /* These commands never recurse */
5271 : /* No command-specific prep needed */
5272 299 : pass = AT_PASS_MISC;
5273 299 : break;
5274 29 : case AT_GenericOptions:
5275 29 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5276 : /* No command-specific prep needed */
5277 29 : pass = AT_PASS_MISC;
5278 29 : break;
5279 1475 : case AT_AttachPartition:
5280 1475 : ATSimplePermissions(cmd->subtype, rel,
5281 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5282 : /* No command-specific prep needed */
5283 1472 : pass = AT_PASS_MISC;
5284 1472 : break;
5285 301 : case AT_DetachPartition:
5286 301 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5287 : /* No command-specific prep needed */
5288 292 : pass = AT_PASS_MISC;
5289 292 : break;
5290 10 : case AT_DetachPartitionFinalize:
5291 10 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5292 : /* No command-specific prep needed */
5293 7 : pass = AT_PASS_MISC;
5294 7 : break;
5295 324 : case AT_MergePartitions:
5296 : case AT_SplitPartition:
5297 324 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5298 : /* No command-specific prep needed */
5299 321 : pass = AT_PASS_MISC;
5300 321 : break;
5301 0 : default: /* oops */
5302 0 : elog(ERROR, "unrecognized alter table type: %d",
5303 : (int) cmd->subtype);
5304 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5305 : break;
5306 : }
5307 : Assert(pass > AT_PASS_UNSET);
5308 :
5309 : /* Add the subcommand to the appropriate list for phase 2 */
5310 17766 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5311 17766 : }
5312 :
5313 : /*
5314 : * ATRewriteCatalogs
5315 : *
5316 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5317 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5318 : * conflicts).
5319 : */
5320 : static void
5321 16704 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5322 : AlterTableUtilityContext *context)
5323 : {
5324 : ListCell *ltab;
5325 :
5326 : /*
5327 : * We process all the tables "in parallel", one pass at a time. This is
5328 : * needed because we may have to propagate work from one table to another
5329 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5330 : * re-adding of the foreign key constraint to the other table). Work can
5331 : * only be propagated into later passes, however.
5332 : */
5333 210725 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5334 : {
5335 : /* Go through each table that needs to be processed */
5336 397972 : foreach(ltab, *wqueue)
5337 : {
5338 203951 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5339 203951 : List *subcmds = tab->subcmds[pass];
5340 : ListCell *lcmd;
5341 :
5342 203951 : if (subcmds == NIL)
5343 175173 : continue;
5344 :
5345 : /*
5346 : * Open the relation and store it in tab. This allows subroutines
5347 : * close and reopen, if necessary. Appropriate lock was obtained
5348 : * by phase 1, needn't get it again.
5349 : */
5350 28778 : tab->rel = relation_open(tab->relid, NoLock);
5351 :
5352 57874 : foreach(lcmd, subcmds)
5353 30669 : ATExecCmd(wqueue, tab,
5354 30669 : lfirst_node(AlterTableCmd, lcmd),
5355 : lockmode, pass, context);
5356 :
5357 : /*
5358 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5359 : * (this is not done in ATExecAlterColumnType since it should be
5360 : * done only once if multiple columns of a table are altered).
5361 : */
5362 27205 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5363 660 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5364 :
5365 27205 : if (tab->rel)
5366 : {
5367 27205 : relation_close(tab->rel, NoLock);
5368 27205 : tab->rel = NULL;
5369 : }
5370 : }
5371 : }
5372 :
5373 : /* Check to see if a toast table must be added. */
5374 32504 : foreach(ltab, *wqueue)
5375 : {
5376 17373 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5377 :
5378 : /*
5379 : * If the table is source table of ATTACH PARTITION command, we did
5380 : * not modify anything about it that will change its toasting
5381 : * requirement, so no need to check.
5382 : */
5383 17373 : if (((tab->relkind == RELKIND_RELATION ||
5384 3364 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5385 16410 : tab->partition_constraint == NULL) ||
5386 2065 : tab->relkind == RELKIND_MATVIEW)
5387 15333 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5388 : }
5389 15131 : }
5390 :
5391 : /*
5392 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5393 : */
5394 : static void
5395 30669 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5396 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5397 : AlterTableUtilityContext *context)
5398 : {
5399 30669 : ObjectAddress address = InvalidObjectAddress;
5400 30669 : Relation rel = tab->rel;
5401 :
5402 30669 : switch (cmd->subtype)
5403 : {
5404 1103 : case AT_AddColumn: /* ADD COLUMN */
5405 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5406 1103 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5407 1103 : cmd->recurse, false,
5408 : lockmode, cur_pass, context);
5409 1034 : break;
5410 292 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5411 292 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5412 259 : break;
5413 40 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5414 40 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5415 40 : break;
5416 86 : case AT_AddIdentity:
5417 86 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5418 : cur_pass, context);
5419 : Assert(cmd != NULL);
5420 80 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5421 53 : break;
5422 31 : case AT_SetIdentity:
5423 31 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5424 : cur_pass, context);
5425 : Assert(cmd != NULL);
5426 31 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5427 19 : break;
5428 28 : case AT_DropIdentity:
5429 28 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5430 19 : break;
5431 134 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5432 134 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5433 83 : break;
5434 207 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5435 207 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5436 207 : cmd->recurse, false, lockmode);
5437 192 : break;
5438 115 : case AT_SetExpression:
5439 115 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5440 106 : break;
5441 28 : case AT_DropExpression:
5442 28 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5443 16 : break;
5444 82 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5445 82 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5446 58 : break;
5447 13 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5448 13 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5449 13 : break;
5450 3 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5451 3 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5452 3 : break;
5453 142 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5454 142 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5455 136 : break;
5456 39 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5457 39 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5458 : lockmode);
5459 36 : break;
5460 841 : case AT_DropColumn: /* DROP COLUMN */
5461 841 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5462 841 : cmd->behavior, cmd->recurse, false,
5463 841 : cmd->missing_ok, lockmode,
5464 : NULL);
5465 751 : break;
5466 602 : case AT_AddIndex: /* ADD INDEX */
5467 602 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5468 : lockmode);
5469 517 : break;
5470 231 : case AT_ReAddIndex: /* ADD INDEX */
5471 231 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5472 : lockmode);
5473 231 : break;
5474 40 : case AT_ReAddStatistics: /* ADD STATISTICS */
5475 40 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5476 : true, lockmode);
5477 40 : break;
5478 14610 : case AT_AddConstraint: /* ADD CONSTRAINT */
5479 : /* Transform the command only during initial examination */
5480 14610 : if (cur_pass == AT_PASS_ADD_CONSTR)
5481 8195 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5482 8210 : cmd->recurse, lockmode,
5483 : cur_pass, context);
5484 : /* Depending on constraint type, might be no more work to do now */
5485 14595 : if (cmd != NULL)
5486 : address =
5487 6400 : ATExecAddConstraint(wqueue, tab, rel,
5488 6400 : (Constraint *) cmd->def,
5489 6400 : cmd->recurse, false, lockmode);
5490 14252 : break;
5491 187 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5492 : address =
5493 187 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5494 : true, true, lockmode);
5495 181 : break;
5496 7 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5497 : * constraint */
5498 : address =
5499 7 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5500 7 : ((AlterDomainStmt *) cmd->def)->def,
5501 : NULL);
5502 4 : break;
5503 39 : case AT_ReAddComment: /* Re-add existing comment */
5504 39 : address = CommentObject((CommentStmt *) cmd->def);
5505 39 : break;
5506 5430 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5507 5430 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5508 : lockmode);
5509 5424 : break;
5510 147 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5511 147 : address = ATExecAlterConstraint(wqueue, rel,
5512 147 : castNode(ATAlterConstraint, cmd->def),
5513 147 : cmd->recurse, lockmode);
5514 114 : break;
5515 241 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5516 241 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5517 : false, lockmode);
5518 238 : break;
5519 409 : case AT_DropConstraint: /* DROP CONSTRAINT */
5520 409 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5521 409 : cmd->recurse,
5522 409 : cmd->missing_ok, lockmode);
5523 304 : break;
5524 596 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5525 : /* parse transformation was done earlier */
5526 596 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5527 575 : break;
5528 86 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5529 : address =
5530 86 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5531 86 : (List *) cmd->def, lockmode);
5532 83 : break;
5533 1029 : case AT_ChangeOwner: /* ALTER OWNER */
5534 1026 : ATExecChangeOwner(RelationGetRelid(rel),
5535 1029 : get_rolespec_oid(cmd->newowner, false),
5536 : false, lockmode);
5537 1020 : break;
5538 32 : case AT_ClusterOn: /* CLUSTER ON */
5539 32 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5540 29 : break;
5541 9 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5542 9 : ATExecDropCluster(rel, lockmode);
5543 6 : break;
5544 44 : case AT_SetLogged: /* SET LOGGED */
5545 : case AT_SetUnLogged: /* SET UNLOGGED */
5546 44 : break;
5547 3 : case AT_DropOids: /* SET WITHOUT OIDS */
5548 : /* nothing to do here, oid columns don't exist anymore */
5549 3 : break;
5550 46 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5551 :
5552 : /*
5553 : * Only do this for partitioned tables, for which this is just a
5554 : * catalog change. Tables with storage are handled by Phase 3.
5555 : */
5556 46 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5557 25 : tab->chgAccessMethod)
5558 22 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5559 46 : break;
5560 85 : case AT_SetTableSpace: /* SET TABLESPACE */
5561 :
5562 : /*
5563 : * Only do this for partitioned tables and indexes, for which this
5564 : * is just a catalog change. Other relation types which have
5565 : * storage are handled by Phase 3.
5566 : */
5567 85 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5568 79 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5569 18 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5570 :
5571 82 : break;
5572 495 : case AT_SetRelOptions: /* SET (...) */
5573 : case AT_ResetRelOptions: /* RESET (...) */
5574 : case AT_ReplaceRelOptions: /* replace entire option list */
5575 495 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5576 466 : break;
5577 61 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5578 61 : ATExecEnableDisableTrigger(rel, cmd->name,
5579 : TRIGGER_FIRES_ON_ORIGIN, false,
5580 61 : cmd->recurse,
5581 : lockmode);
5582 61 : break;
5583 21 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5584 21 : ATExecEnableDisableTrigger(rel, cmd->name,
5585 : TRIGGER_FIRES_ALWAYS, false,
5586 21 : cmd->recurse,
5587 : lockmode);
5588 21 : break;
5589 8 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5590 8 : ATExecEnableDisableTrigger(rel, cmd->name,
5591 : TRIGGER_FIRES_ON_REPLICA, false,
5592 8 : cmd->recurse,
5593 : lockmode);
5594 8 : break;
5595 69 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5596 69 : ATExecEnableDisableTrigger(rel, cmd->name,
5597 : TRIGGER_DISABLED, false,
5598 69 : cmd->recurse,
5599 : lockmode);
5600 69 : break;
5601 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5602 0 : ATExecEnableDisableTrigger(rel, NULL,
5603 : TRIGGER_FIRES_ON_ORIGIN, false,
5604 0 : cmd->recurse,
5605 : lockmode);
5606 0 : break;
5607 6 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5608 6 : ATExecEnableDisableTrigger(rel, NULL,
5609 : TRIGGER_DISABLED, false,
5610 6 : cmd->recurse,
5611 : lockmode);
5612 6 : break;
5613 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5614 0 : ATExecEnableDisableTrigger(rel, NULL,
5615 : TRIGGER_FIRES_ON_ORIGIN, true,
5616 0 : cmd->recurse,
5617 : lockmode);
5618 0 : break;
5619 6 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5620 6 : ATExecEnableDisableTrigger(rel, NULL,
5621 : TRIGGER_DISABLED, true,
5622 6 : cmd->recurse,
5623 : lockmode);
5624 6 : break;
5625 :
5626 4 : case AT_EnableRule: /* ENABLE RULE name */
5627 4 : ATExecEnableDisableRule(rel, cmd->name,
5628 : RULE_FIRES_ON_ORIGIN, lockmode);
5629 4 : break;
5630 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5631 0 : ATExecEnableDisableRule(rel, cmd->name,
5632 : RULE_FIRES_ALWAYS, lockmode);
5633 0 : break;
5634 3 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5635 3 : ATExecEnableDisableRule(rel, cmd->name,
5636 : RULE_FIRES_ON_REPLICA, lockmode);
5637 3 : break;
5638 16 : case AT_DisableRule: /* DISABLE RULE name */
5639 16 : ATExecEnableDisableRule(rel, cmd->name,
5640 : RULE_DISABLED, lockmode);
5641 16 : break;
5642 :
5643 223 : case AT_AddInherit:
5644 223 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5645 163 : break;
5646 47 : case AT_DropInherit:
5647 47 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5648 44 : break;
5649 33 : case AT_AddOf:
5650 33 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5651 15 : break;
5652 3 : case AT_DropOf:
5653 3 : ATExecDropOf(rel, lockmode);
5654 3 : break;
5655 256 : case AT_ReplicaIdentity:
5656 256 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5657 232 : break;
5658 169 : case AT_EnableRowSecurity:
5659 169 : ATExecSetRowSecurity(rel, true);
5660 169 : break;
5661 5 : case AT_DisableRowSecurity:
5662 5 : ATExecSetRowSecurity(rel, false);
5663 5 : break;
5664 50 : case AT_ForceRowSecurity:
5665 50 : ATExecForceNoForceRowSecurity(rel, true);
5666 50 : break;
5667 16 : case AT_NoForceRowSecurity:
5668 16 : ATExecForceNoForceRowSecurity(rel, false);
5669 16 : break;
5670 29 : case AT_GenericOptions:
5671 29 : ATExecGenericOptions(rel, (List *) cmd->def);
5672 28 : break;
5673 1472 : case AT_AttachPartition:
5674 1472 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5675 : cur_pass, context);
5676 : Assert(cmd != NULL);
5677 1460 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5678 1262 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5679 : context);
5680 : else
5681 198 : address = ATExecAttachPartitionIdx(wqueue, rel,
5682 198 : ((PartitionCmd *) cmd->def)->name);
5683 1265 : break;
5684 292 : case AT_DetachPartition:
5685 292 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5686 : cur_pass, context);
5687 : Assert(cmd != NULL);
5688 : /* ATPrepCmd ensures it must be a table */
5689 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5690 292 : address = ATExecDetachPartition(wqueue, tab, rel,
5691 292 : ((PartitionCmd *) cmd->def)->name,
5692 292 : ((PartitionCmd *) cmd->def)->concurrent);
5693 227 : break;
5694 7 : case AT_DetachPartitionFinalize:
5695 7 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5696 7 : break;
5697 132 : case AT_MergePartitions:
5698 132 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5699 : cur_pass, context);
5700 : Assert(cmd != NULL);
5701 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5702 90 : ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5703 : context);
5704 69 : break;
5705 189 : case AT_SplitPartition:
5706 189 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5707 : cur_pass, context);
5708 : Assert(cmd != NULL);
5709 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5710 99 : ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5711 : context);
5712 93 : break;
5713 0 : default: /* oops */
5714 0 : elog(ERROR, "unrecognized alter table type: %d",
5715 : (int) cmd->subtype);
5716 : break;
5717 : }
5718 :
5719 : /*
5720 : * Report the subcommand to interested event triggers.
5721 : */
5722 29096 : if (cmd)
5723 20901 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5724 :
5725 : /*
5726 : * Bump the command counter to ensure the next subcommand in the sequence
5727 : * can see the changes so far
5728 : */
5729 29096 : CommandCounterIncrement();
5730 29096 : }
5731 :
5732 : /*
5733 : * ATParseTransformCmd: perform parse transformation for one subcommand
5734 : *
5735 : * Returns the transformed subcommand tree, if there is one, else NULL.
5736 : *
5737 : * The parser may hand back additional AlterTableCmd(s) and/or other
5738 : * utility statements, either before or after the original subcommand.
5739 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5740 : * AlteredTableInfo (they had better be for later passes than the current one).
5741 : * Utility statements that are supposed to happen before the AlterTableCmd
5742 : * are executed immediately. Those that are supposed to happen afterwards
5743 : * are added to the tab->afterStmts list to be done at the very end.
5744 : */
5745 : static AlterTableCmd *
5746 12171 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5747 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5748 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5749 : {
5750 12171 : AlterTableCmd *newcmd = NULL;
5751 12171 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5752 : List *beforeStmts;
5753 : List *afterStmts;
5754 : ListCell *lc;
5755 :
5756 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5757 12171 : atstmt->relation =
5758 12171 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5759 12171 : pstrdup(RelationGetRelationName(rel)),
5760 : -1);
5761 12171 : atstmt->relation->inh = recurse;
5762 12171 : atstmt->cmds = list_make1(cmd);
5763 12171 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5764 12171 : atstmt->missing_ok = false;
5765 :
5766 : /* Transform the AlterTableStmt */
5767 12171 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5768 : atstmt,
5769 : context->queryString,
5770 : &beforeStmts,
5771 : &afterStmts);
5772 :
5773 : /* Execute any statements that should happen before these subcommand(s) */
5774 12249 : foreach(lc, beforeStmts)
5775 : {
5776 249 : Node *stmt = (Node *) lfirst(lc);
5777 :
5778 249 : ProcessUtilityForAlterTable(stmt, context);
5779 243 : CommandCounterIncrement();
5780 : }
5781 :
5782 : /* Examine the transformed subcommands and schedule them appropriately */
5783 28255 : foreach(lc, atstmt->cmds)
5784 : {
5785 16255 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5786 : AlterTablePass pass;
5787 :
5788 : /*
5789 : * This switch need only cover the subcommand types that can be added
5790 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5791 : * executing the subcommand immediately, as a substitute for the
5792 : * original subcommand. (Note, however, that this does cause
5793 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5794 : * which is important for index and foreign key constraints.)
5795 : *
5796 : * We assume we needn't do any phase-1 checks for added subcommands.
5797 : */
5798 16255 : switch (cmd2->subtype)
5799 : {
5800 614 : case AT_AddIndex:
5801 614 : pass = AT_PASS_ADD_INDEX;
5802 614 : break;
5803 5430 : case AT_AddIndexConstraint:
5804 5430 : pass = AT_PASS_ADD_INDEXCONSTR;
5805 5430 : break;
5806 6406 : case AT_AddConstraint:
5807 : /* Recursion occurs during execution phase */
5808 6406 : if (recurse)
5809 6382 : cmd2->recurse = true;
5810 6406 : switch (castNode(Constraint, cmd2->def)->contype)
5811 : {
5812 4583 : case CONSTR_NOTNULL:
5813 4583 : pass = AT_PASS_COL_ATTRS;
5814 4583 : break;
5815 0 : case CONSTR_PRIMARY:
5816 : case CONSTR_UNIQUE:
5817 : case CONSTR_EXCLUSION:
5818 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5819 0 : break;
5820 1823 : default:
5821 1823 : pass = AT_PASS_ADD_OTHERCONSTR;
5822 1823 : break;
5823 : }
5824 6406 : break;
5825 0 : case AT_AlterColumnGenericOptions:
5826 : /* This command never recurses */
5827 : /* No command-specific prep needed */
5828 0 : pass = AT_PASS_MISC;
5829 0 : break;
5830 3805 : default:
5831 3805 : pass = cur_pass;
5832 3805 : break;
5833 : }
5834 :
5835 16255 : if (pass < cur_pass)
5836 : {
5837 : /* Cannot schedule into a pass we already finished */
5838 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5839 : pass);
5840 : }
5841 16255 : else if (pass > cur_pass)
5842 : {
5843 : /* OK, queue it up for later */
5844 12450 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5845 : }
5846 : else
5847 : {
5848 : /*
5849 : * We should see at most one subcommand for the current pass,
5850 : * which is the transformed version of the original subcommand.
5851 : */
5852 3805 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5853 : {
5854 : /* Found the transformed version of our subcommand */
5855 3805 : newcmd = cmd2;
5856 : }
5857 : else
5858 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5859 : pass);
5860 : }
5861 : }
5862 :
5863 : /* Queue up any after-statements to happen at the end */
5864 12000 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5865 :
5866 12000 : return newcmd;
5867 : }
5868 :
5869 : /*
5870 : * ATRewriteTables: ALTER TABLE phase 3
5871 : */
5872 : static void
5873 15131 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5874 : AlterTableUtilityContext *context)
5875 : {
5876 : ListCell *ltab;
5877 :
5878 : /* Go through each table that needs to be checked or rewritten */
5879 32259 : foreach(ltab, *wqueue)
5880 : {
5881 17331 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5882 :
5883 : /* Relations without storage may be ignored here */
5884 17331 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5885 3206 : continue;
5886 :
5887 : /*
5888 : * If we change column data types, the operation has to be propagated
5889 : * to tables that use this table's rowtype as a column type.
5890 : * tab->newvals will also be non-NULL in the case where we're adding a
5891 : * column with a default. We choose to forbid that case as well,
5892 : * since composite types might eventually support defaults.
5893 : *
5894 : * (Eventually we'll probably need to check for composite type
5895 : * dependencies even when we're just scanning the table without a
5896 : * rewrite, but at the moment a composite type does not enforce any
5897 : * constraints, so it's not necessary/appropriate to enforce them just
5898 : * during ALTER.)
5899 : */
5900 14125 : if (tab->newvals != NIL || tab->rewrite > 0)
5901 : {
5902 : Relation rel;
5903 :
5904 939 : rel = table_open(tab->relid, NoLock);
5905 939 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5906 912 : table_close(rel, NoLock);
5907 : }
5908 :
5909 : /*
5910 : * We only need to rewrite the table if at least one column needs to
5911 : * be recomputed, or we are changing its persistence or access method.
5912 : *
5913 : * There are two reasons for requiring a rewrite when changing
5914 : * persistence: on one hand, we need to ensure that the buffers
5915 : * belonging to each of the two relations are marked with or without
5916 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5917 : * and assigns a new relfilenumber, we automatically create or drop an
5918 : * init fork for the relation as appropriate.
5919 : */
5920 14098 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5921 525 : {
5922 : /* Build a temporary relation and copy data */
5923 : Relation OldHeap;
5924 : Oid OIDNewHeap;
5925 : Oid NewAccessMethod;
5926 : Oid NewTableSpace;
5927 : char persistence;
5928 :
5929 553 : OldHeap = table_open(tab->relid, NoLock);
5930 :
5931 : /*
5932 : * We don't support rewriting of system catalogs; there are too
5933 : * many corner cases and too little benefit. In particular this
5934 : * is certainly not going to work for mapped catalogs.
5935 : */
5936 553 : if (IsSystemRelation(OldHeap))
5937 0 : ereport(ERROR,
5938 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5939 : errmsg("cannot rewrite system relation \"%s\"",
5940 : RelationGetRelationName(OldHeap))));
5941 :
5942 553 : if (RelationIsUsedAsCatalogTable(OldHeap))
5943 1 : ereport(ERROR,
5944 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5945 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5946 : RelationGetRelationName(OldHeap))));
5947 :
5948 : /*
5949 : * Don't allow rewrite on temp tables of other backends ... their
5950 : * local buffer manager is not going to cope. (This is redundant
5951 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5952 : * check here too.)
5953 : */
5954 552 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5955 0 : ereport(ERROR,
5956 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5957 : errmsg("cannot rewrite temporary tables of other sessions")));
5958 :
5959 : /*
5960 : * Select destination tablespace (same as original unless user
5961 : * requested a change)
5962 : */
5963 552 : if (tab->newTableSpace)
5964 0 : NewTableSpace = tab->newTableSpace;
5965 : else
5966 552 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5967 :
5968 : /*
5969 : * Select destination access method (same as original unless user
5970 : * requested a change)
5971 : */
5972 552 : if (tab->chgAccessMethod)
5973 18 : NewAccessMethod = tab->newAccessMethod;
5974 : else
5975 534 : NewAccessMethod = OldHeap->rd_rel->relam;
5976 :
5977 : /*
5978 : * Select persistence of transient table (same as original unless
5979 : * user requested a change)
5980 : */
5981 552 : persistence = tab->chgPersistence ?
5982 526 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5983 :
5984 552 : table_close(OldHeap, NoLock);
5985 :
5986 : /*
5987 : * Fire off an Event Trigger now, before actually rewriting the
5988 : * table.
5989 : *
5990 : * We don't support Event Trigger for nested commands anywhere,
5991 : * here included, and parsetree is given NULL when coming from
5992 : * AlterTableInternal.
5993 : *
5994 : * And fire it only once.
5995 : */
5996 552 : if (parsetree)
5997 552 : EventTriggerTableRewrite((Node *) parsetree,
5998 : tab->relid,
5999 : tab->rewrite);
6000 :
6001 : /*
6002 : * Create transient table that will receive the modified data.
6003 : *
6004 : * Ensure it is marked correctly as logged or unlogged. We have
6005 : * to do this here so that buffers for the new relfilenumber will
6006 : * have the right persistence set, and at the same time ensure
6007 : * that the original filenumbers's buffers will get read in with
6008 : * the correct setting (i.e. the original one). Otherwise a
6009 : * rollback after the rewrite would possibly result with buffers
6010 : * for the original filenumbers having the wrong persistence
6011 : * setting.
6012 : *
6013 : * NB: This relies on swap_relation_files() also swapping the
6014 : * persistence. That wouldn't work for pg_class, but that can't be
6015 : * unlogged anyway.
6016 : */
6017 549 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
6018 : persistence, lockmode);
6019 :
6020 : /*
6021 : * Copy the heap data into the new table with the desired
6022 : * modifications, and test the current data within the table
6023 : * against new constraints generated by ALTER TABLE commands.
6024 : */
6025 549 : ATRewriteTable(tab, OIDNewHeap);
6026 :
6027 : /*
6028 : * Swap the physical files of the old and new heaps, then rebuild
6029 : * indexes and discard the old heap. We can use RecentXmin for
6030 : * the table's new relfrozenxid because we rewrote all the tuples
6031 : * in ATRewriteTable, so no older Xid remains in the table. Also,
6032 : * we never try to swap toast tables by content, since we have no
6033 : * interest in letting this code work on system catalogs.
6034 : */
6035 528 : finish_heap_swap(tab->relid, OIDNewHeap,
6036 : false, false, true,
6037 528 : !OidIsValid(tab->newTableSpace),
6038 : RecentXmin,
6039 : ReadNextMultiXactId(),
6040 : persistence);
6041 :
6042 525 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
6043 : }
6044 13545 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6045 : {
6046 12 : if (tab->chgPersistence)
6047 12 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
6048 : }
6049 : else
6050 : {
6051 : /*
6052 : * If required, test the current data within the table against new
6053 : * constraints generated by ALTER TABLE commands, but don't
6054 : * rebuild data.
6055 : */
6056 13533 : if (tab->constraints != NIL || tab->verify_new_notnull ||
6057 12015 : tab->partition_constraint != NULL)
6058 2543 : ATRewriteTable(tab, InvalidOid);
6059 :
6060 : /*
6061 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
6062 : * just do a block-by-block copy.
6063 : */
6064 13385 : if (tab->newTableSpace)
6065 67 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6066 : }
6067 :
6068 : /*
6069 : * Also change persistence of owned sequences, so that it matches the
6070 : * table persistence.
6071 : */
6072 13922 : if (tab->chgPersistence)
6073 : {
6074 38 : List *seqlist = getOwnedSequences(tab->relid);
6075 : ListCell *lc;
6076 :
6077 62 : foreach(lc, seqlist)
6078 : {
6079 24 : Oid seq_relid = lfirst_oid(lc);
6080 :
6081 24 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
6082 : }
6083 : }
6084 : }
6085 :
6086 : /*
6087 : * Foreign key constraints are checked in a final pass, since (a) it's
6088 : * generally best to examine each one separately, and (b) it's at least
6089 : * theoretically possible that we have changed both relations of the
6090 : * foreign key, and we'd better have finished both rewrites before we try
6091 : * to read the tables.
6092 : */
6093 31903 : foreach(ltab, *wqueue)
6094 : {
6095 17027 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6096 17027 : Relation rel = NULL;
6097 : ListCell *lcon;
6098 :
6099 : /* Relations without storage may be ignored here too */
6100 17027 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6101 3145 : continue;
6102 :
6103 14832 : foreach(lcon, tab->constraints)
6104 : {
6105 1002 : NewConstraint *con = lfirst(lcon);
6106 :
6107 1002 : if (con->contype == CONSTR_FOREIGN)
6108 : {
6109 589 : Constraint *fkconstraint = (Constraint *) con->qual;
6110 : Relation refrel;
6111 :
6112 589 : if (rel == NULL)
6113 : {
6114 : /* Long since locked, no need for another */
6115 583 : rel = table_open(tab->relid, NoLock);
6116 : }
6117 :
6118 589 : refrel = table_open(con->refrelid, RowShareLock);
6119 :
6120 589 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6121 : con->refindid,
6122 : con->conid,
6123 589 : con->conwithperiod);
6124 :
6125 : /*
6126 : * No need to mark the constraint row as validated, we did
6127 : * that when we inserted the row earlier.
6128 : */
6129 :
6130 537 : table_close(refrel, NoLock);
6131 : }
6132 : }
6133 :
6134 13830 : if (rel)
6135 531 : table_close(rel, NoLock);
6136 : }
6137 :
6138 : /* Finally, run any afterStmts that were queued up */
6139 31829 : foreach(ltab, *wqueue)
6140 : {
6141 16953 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6142 : ListCell *lc;
6143 :
6144 16996 : foreach(lc, tab->afterStmts)
6145 : {
6146 43 : Node *stmt = (Node *) lfirst(lc);
6147 :
6148 43 : ProcessUtilityForAlterTable(stmt, context);
6149 43 : CommandCounterIncrement();
6150 : }
6151 : }
6152 14876 : }
6153 :
6154 : /*
6155 : * ATRewriteTable: scan or rewrite one table
6156 : *
6157 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6158 : * must already hold AccessExclusiveLock on it.
6159 : */
6160 : static void
6161 3092 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6162 : {
6163 : Relation oldrel;
6164 : Relation newrel;
6165 : TupleDesc oldTupDesc;
6166 : TupleDesc newTupDesc;
6167 3092 : bool needscan = false;
6168 : List *notnull_attrs;
6169 : List *notnull_virtual_attrs;
6170 : int i;
6171 : ListCell *l;
6172 : EState *estate;
6173 : CommandId mycid;
6174 : BulkInsertState bistate;
6175 : int ti_options;
6176 3092 : ExprState *partqualstate = NULL;
6177 :
6178 : /*
6179 : * Open the relation(s). We have surely already locked the existing
6180 : * table.
6181 : */
6182 3092 : oldrel = table_open(tab->relid, NoLock);
6183 3092 : oldTupDesc = tab->oldDesc;
6184 3092 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6185 :
6186 3092 : if (OidIsValid(OIDNewHeap))
6187 : {
6188 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6189 : false));
6190 549 : newrel = table_open(OIDNewHeap, NoLock);
6191 : }
6192 : else
6193 2543 : newrel = NULL;
6194 :
6195 : /*
6196 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6197 : * is empty, so don't bother using it.
6198 : */
6199 3092 : if (newrel)
6200 : {
6201 549 : mycid = GetCurrentCommandId(true);
6202 549 : bistate = GetBulkInsertState();
6203 549 : ti_options = TABLE_INSERT_SKIP_FSM;
6204 : }
6205 : else
6206 : {
6207 : /* keep compiler quiet about using these uninitialized */
6208 2543 : mycid = 0;
6209 2543 : bistate = NULL;
6210 2543 : ti_options = 0;
6211 : }
6212 :
6213 : /*
6214 : * Generate the constraint and default execution states
6215 : */
6216 :
6217 3092 : estate = CreateExecutorState();
6218 :
6219 : /* Build the needed expression execution states */
6220 4175 : foreach(l, tab->constraints)
6221 : {
6222 1083 : NewConstraint *con = lfirst(l);
6223 :
6224 1083 : switch (con->contype)
6225 : {
6226 491 : case CONSTR_CHECK:
6227 491 : needscan = true;
6228 491 : con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6229 491 : break;
6230 592 : case CONSTR_FOREIGN:
6231 : /* Nothing to do here */
6232 592 : break;
6233 0 : default:
6234 0 : elog(ERROR, "unrecognized constraint type: %d",
6235 : (int) con->contype);
6236 : }
6237 : }
6238 :
6239 : /* Build expression execution states for partition check quals */
6240 3092 : if (tab->partition_constraint)
6241 : {
6242 1099 : needscan = true;
6243 1099 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6244 : }
6245 :
6246 3675 : foreach(l, tab->newvals)
6247 : {
6248 583 : NewColumnValue *ex = lfirst(l);
6249 :
6250 : /* expr already planned */
6251 583 : ex->exprstate = ExecInitExpr(ex->expr, NULL);
6252 : }
6253 :
6254 3092 : notnull_attrs = notnull_virtual_attrs = NIL;
6255 3092 : if (newrel || tab->verify_new_notnull)
6256 : {
6257 : /*
6258 : * If we are rebuilding the tuples OR if we added any new but not
6259 : * verified not-null constraints, check all *valid* not-null
6260 : * constraints. This is a bit of overkill but it minimizes risk of
6261 : * bugs.
6262 : *
6263 : * notnull_attrs does *not* collect attribute numbers for valid
6264 : * not-null constraints over virtual generated columns; instead, they
6265 : * are collected in notnull_virtual_attrs for verification elsewhere.
6266 : */
6267 3891 : for (i = 0; i < newTupDesc->natts; i++)
6268 : {
6269 2822 : CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6270 :
6271 2822 : if (attr->attnullability == ATTNULLABLE_VALID &&
6272 1060 : !attr->attisdropped)
6273 : {
6274 1060 : Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6275 :
6276 1060 : if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6277 1015 : notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6278 : else
6279 45 : notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6280 45 : wholeatt->attnum);
6281 : }
6282 : }
6283 1069 : if (notnull_attrs || notnull_virtual_attrs)
6284 779 : needscan = true;
6285 : }
6286 :
6287 3092 : if (newrel || needscan)
6288 : {
6289 : ExprContext *econtext;
6290 : TupleTableSlot *oldslot;
6291 : TupleTableSlot *newslot;
6292 : TableScanDesc scan;
6293 : MemoryContext oldCxt;
6294 2601 : List *dropped_attrs = NIL;
6295 : ListCell *lc;
6296 : Snapshot snapshot;
6297 2601 : ResultRelInfo *rInfo = NULL;
6298 :
6299 : /*
6300 : * When adding or changing a virtual generated column with a not-null
6301 : * constraint, we need to evaluate whether the generation expression
6302 : * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6303 : * prepare a dummy ResultRelInfo.
6304 : */
6305 2601 : if (notnull_virtual_attrs != NIL)
6306 : {
6307 : MemoryContext oldcontext;
6308 :
6309 : Assert(newTupDesc->constr->has_generated_virtual);
6310 : Assert(newTupDesc->constr->has_not_null);
6311 30 : oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6312 30 : rInfo = makeNode(ResultRelInfo);
6313 30 : InitResultRelInfo(rInfo,
6314 : oldrel,
6315 : 0, /* dummy rangetable index */
6316 : NULL,
6317 : estate->es_instrument);
6318 30 : MemoryContextSwitchTo(oldcontext);
6319 : }
6320 :
6321 2601 : if (newrel)
6322 549 : ereport(DEBUG1,
6323 : (errmsg_internal("rewriting table \"%s\"",
6324 : RelationGetRelationName(oldrel))));
6325 : else
6326 2052 : ereport(DEBUG1,
6327 : (errmsg_internal("verifying table \"%s\"",
6328 : RelationGetRelationName(oldrel))));
6329 :
6330 2601 : if (newrel)
6331 : {
6332 : /*
6333 : * All predicate locks on the tuples or pages are about to be made
6334 : * invalid, because we move tuples around. Promote them to
6335 : * relation locks.
6336 : */
6337 549 : TransferPredicateLocksToHeapRelation(oldrel);
6338 : }
6339 :
6340 2601 : econtext = GetPerTupleExprContext(estate);
6341 :
6342 : /*
6343 : * Create necessary tuple slots. When rewriting, two slots are needed,
6344 : * otherwise one suffices. In the case where one slot suffices, we
6345 : * need to use the new tuple descriptor, otherwise some constraints
6346 : * can't be evaluated. Note that even when the tuple layout is the
6347 : * same and no rewrite is required, the tupDescs might not be
6348 : * (consider ADD COLUMN without a default).
6349 : */
6350 2601 : if (tab->rewrite)
6351 : {
6352 : Assert(newrel != NULL);
6353 549 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6354 : table_slot_callbacks(oldrel));
6355 549 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6356 : table_slot_callbacks(newrel));
6357 :
6358 : /*
6359 : * Set all columns in the new slot to NULL initially, to ensure
6360 : * columns added as part of the rewrite are initialized to NULL.
6361 : * That is necessary as tab->newvals will not contain an
6362 : * expression for columns with a NULL default, e.g. when adding a
6363 : * column without a default together with a column with a default
6364 : * requiring an actual rewrite.
6365 : */
6366 549 : ExecStoreAllNullTuple(newslot);
6367 : }
6368 : else
6369 : {
6370 2052 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6371 : table_slot_callbacks(oldrel));
6372 2052 : newslot = NULL;
6373 : }
6374 :
6375 : /*
6376 : * Any attributes that are dropped according to the new tuple
6377 : * descriptor can be set to NULL. We precompute the list of dropped
6378 : * attributes to avoid needing to do so in the per-tuple loop.
6379 : */
6380 9152 : for (i = 0; i < newTupDesc->natts; i++)
6381 : {
6382 6551 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6383 413 : dropped_attrs = lappend_int(dropped_attrs, i);
6384 : }
6385 :
6386 : /*
6387 : * Scan through the rows, generating a new row if needed and then
6388 : * checking all the constraints.
6389 : */
6390 2601 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6391 2601 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6392 :
6393 : /*
6394 : * Switch to per-tuple memory context and reset it for each tuple
6395 : * produced, so we don't leak memory.
6396 : */
6397 2601 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6398 :
6399 387842 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6400 : {
6401 : TupleTableSlot *insertslot;
6402 :
6403 382809 : if (tab->rewrite > 0)
6404 : {
6405 : /* Extract data from old tuple */
6406 50061 : slot_getallattrs(oldslot);
6407 50061 : ExecClearTuple(newslot);
6408 :
6409 : /* copy attributes */
6410 50061 : memcpy(newslot->tts_values, oldslot->tts_values,
6411 50061 : sizeof(Datum) * oldslot->tts_nvalid);
6412 50061 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6413 50061 : sizeof(bool) * oldslot->tts_nvalid);
6414 :
6415 : /* Set dropped attributes to null in new tuple */
6416 50119 : foreach(lc, dropped_attrs)
6417 58 : newslot->tts_isnull[lfirst_int(lc)] = true;
6418 :
6419 : /*
6420 : * Constraints and GENERATED expressions might reference the
6421 : * tableoid column, so fill tts_tableOid with the desired
6422 : * value. (We must do this each time, because it gets
6423 : * overwritten with newrel's OID during storing.)
6424 : */
6425 50061 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6426 :
6427 : /*
6428 : * Process supplied expressions to replace selected columns.
6429 : *
6430 : * First, evaluate expressions whose inputs come from the old
6431 : * tuple.
6432 : */
6433 50061 : econtext->ecxt_scantuple = oldslot;
6434 :
6435 103098 : foreach(l, tab->newvals)
6436 : {
6437 53043 : NewColumnValue *ex = lfirst(l);
6438 :
6439 53043 : if (ex->is_generated)
6440 156 : continue;
6441 :
6442 52887 : newslot->tts_values[ex->attnum - 1]
6443 52881 : = ExecEvalExpr(ex->exprstate,
6444 : econtext,
6445 52887 : &newslot->tts_isnull[ex->attnum - 1]);
6446 : }
6447 :
6448 50055 : ExecStoreVirtualTuple(newslot);
6449 :
6450 : /*
6451 : * Now, evaluate any expressions whose inputs come from the
6452 : * new tuple. We assume these columns won't reference each
6453 : * other, so that there's no ordering dependency.
6454 : */
6455 50055 : econtext->ecxt_scantuple = newslot;
6456 :
6457 103092 : foreach(l, tab->newvals)
6458 : {
6459 53037 : NewColumnValue *ex = lfirst(l);
6460 :
6461 53037 : if (!ex->is_generated)
6462 52881 : continue;
6463 :
6464 156 : newslot->tts_values[ex->attnum - 1]
6465 156 : = ExecEvalExpr(ex->exprstate,
6466 : econtext,
6467 156 : &newslot->tts_isnull[ex->attnum - 1]);
6468 : }
6469 :
6470 50055 : insertslot = newslot;
6471 : }
6472 : else
6473 : {
6474 : /*
6475 : * If there's no rewrite, old and new table are guaranteed to
6476 : * have the same AM, so we can just use the old slot to verify
6477 : * new constraints etc.
6478 : */
6479 332748 : insertslot = oldslot;
6480 : }
6481 :
6482 : /* Now check any constraints on the possibly-changed tuple */
6483 382803 : econtext->ecxt_scantuple = insertslot;
6484 :
6485 2053741 : foreach_int(attn, notnull_attrs)
6486 : {
6487 1288237 : if (slot_attisnull(insertslot, attn))
6488 : {
6489 51 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6490 :
6491 51 : ereport(ERROR,
6492 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6493 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6494 : NameStr(attr->attname),
6495 : RelationGetRelationName(oldrel)),
6496 : errtablecol(oldrel, attn)));
6497 : }
6498 : }
6499 :
6500 382752 : if (notnull_virtual_attrs != NIL)
6501 : {
6502 : AttrNumber attnum;
6503 :
6504 42 : attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6505 : estate,
6506 : notnull_virtual_attrs);
6507 42 : if (attnum != InvalidAttrNumber)
6508 : {
6509 15 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6510 :
6511 15 : ereport(ERROR,
6512 : errcode(ERRCODE_NOT_NULL_VIOLATION),
6513 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6514 : NameStr(attr->attname),
6515 : RelationGetRelationName(oldrel)),
6516 : errtablecol(oldrel, attnum));
6517 : }
6518 : }
6519 :
6520 386841 : foreach(l, tab->constraints)
6521 : {
6522 4164 : NewConstraint *con = lfirst(l);
6523 :
6524 4164 : switch (con->contype)
6525 : {
6526 4111 : case CONSTR_CHECK:
6527 4111 : if (!ExecCheck(con->qualstate, econtext))
6528 60 : ereport(ERROR,
6529 : (errcode(ERRCODE_CHECK_VIOLATION),
6530 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6531 : con->name,
6532 : RelationGetRelationName(oldrel)),
6533 : errtableconstraint(oldrel, con->name)));
6534 4051 : break;
6535 53 : case CONSTR_NOTNULL:
6536 : case CONSTR_FOREIGN:
6537 : /* Nothing to do here */
6538 53 : break;
6539 0 : default:
6540 0 : elog(ERROR, "unrecognized constraint type: %d",
6541 : (int) con->contype);
6542 : }
6543 : }
6544 :
6545 382677 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6546 : {
6547 37 : if (tab->validate_default)
6548 13 : ereport(ERROR,
6549 : (errcode(ERRCODE_CHECK_VIOLATION),
6550 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6551 : RelationGetRelationName(oldrel)),
6552 : errtable(oldrel)));
6553 : else
6554 24 : ereport(ERROR,
6555 : (errcode(ERRCODE_CHECK_VIOLATION),
6556 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6557 : RelationGetRelationName(oldrel)),
6558 : errtable(oldrel)));
6559 : }
6560 :
6561 : /* Write the tuple out to the new relation */
6562 382640 : if (newrel)
6563 50040 : table_tuple_insert(newrel, insertslot, mycid,
6564 : ti_options, bistate);
6565 :
6566 382640 : ResetExprContext(econtext);
6567 :
6568 382640 : CHECK_FOR_INTERRUPTS();
6569 : }
6570 :
6571 2432 : MemoryContextSwitchTo(oldCxt);
6572 2432 : table_endscan(scan);
6573 2432 : UnregisterSnapshot(snapshot);
6574 :
6575 2432 : ExecDropSingleTupleTableSlot(oldslot);
6576 2432 : if (newslot)
6577 528 : ExecDropSingleTupleTableSlot(newslot);
6578 : }
6579 :
6580 2923 : FreeExecutorState(estate);
6581 :
6582 2923 : table_close(oldrel, NoLock);
6583 2923 : if (newrel)
6584 : {
6585 528 : FreeBulkInsertState(bistate);
6586 :
6587 528 : table_finish_bulk_insert(newrel, ti_options);
6588 :
6589 528 : table_close(newrel, NoLock);
6590 : }
6591 2923 : }
6592 :
6593 : /*
6594 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6595 : */
6596 : static AlteredTableInfo *
6597 22366 : ATGetQueueEntry(List **wqueue, Relation rel)
6598 : {
6599 22366 : Oid relid = RelationGetRelid(rel);
6600 : AlteredTableInfo *tab;
6601 : ListCell *ltab;
6602 :
6603 28578 : foreach(ltab, *wqueue)
6604 : {
6605 9030 : tab = (AlteredTableInfo *) lfirst(ltab);
6606 9030 : if (tab->relid == relid)
6607 2818 : return tab;
6608 : }
6609 :
6610 : /*
6611 : * Not there, so add it. Note that we make a copy of the relation's
6612 : * existing descriptor before anything interesting can happen to it.
6613 : */
6614 19548 : tab = palloc0_object(AlteredTableInfo);
6615 19548 : tab->relid = relid;
6616 19548 : tab->rel = NULL; /* set later */
6617 19548 : tab->relkind = rel->rd_rel->relkind;
6618 19548 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6619 19548 : tab->newAccessMethod = InvalidOid;
6620 19548 : tab->chgAccessMethod = false;
6621 19548 : tab->newTableSpace = InvalidOid;
6622 19548 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6623 19548 : tab->chgPersistence = false;
6624 :
6625 19548 : *wqueue = lappend(*wqueue, tab);
6626 :
6627 19548 : return tab;
6628 : }
6629 :
6630 : static const char *
6631 43 : alter_table_type_to_string(AlterTableType cmdtype)
6632 : {
6633 43 : switch (cmdtype)
6634 : {
6635 0 : case AT_AddColumn:
6636 : case AT_AddColumnToView:
6637 0 : return "ADD COLUMN";
6638 0 : case AT_ColumnDefault:
6639 : case AT_CookedColumnDefault:
6640 0 : return "ALTER COLUMN ... SET DEFAULT";
6641 3 : case AT_DropNotNull:
6642 3 : return "ALTER COLUMN ... DROP NOT NULL";
6643 3 : case AT_SetNotNull:
6644 3 : return "ALTER COLUMN ... SET NOT NULL";
6645 0 : case AT_SetExpression:
6646 0 : return "ALTER COLUMN ... SET EXPRESSION";
6647 0 : case AT_DropExpression:
6648 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6649 0 : case AT_SetStatistics:
6650 0 : return "ALTER COLUMN ... SET STATISTICS";
6651 6 : case AT_SetOptions:
6652 6 : return "ALTER COLUMN ... SET";
6653 0 : case AT_ResetOptions:
6654 0 : return "ALTER COLUMN ... RESET";
6655 0 : case AT_SetStorage:
6656 0 : return "ALTER COLUMN ... SET STORAGE";
6657 0 : case AT_SetCompression:
6658 0 : return "ALTER COLUMN ... SET COMPRESSION";
6659 3 : case AT_DropColumn:
6660 3 : return "DROP COLUMN";
6661 0 : case AT_AddIndex:
6662 : case AT_ReAddIndex:
6663 0 : return NULL; /* not real grammar */
6664 0 : case AT_AddConstraint:
6665 : case AT_ReAddConstraint:
6666 : case AT_ReAddDomainConstraint:
6667 : case AT_AddIndexConstraint:
6668 0 : return "ADD CONSTRAINT";
6669 3 : case AT_AlterConstraint:
6670 3 : return "ALTER CONSTRAINT";
6671 0 : case AT_ValidateConstraint:
6672 0 : return "VALIDATE CONSTRAINT";
6673 0 : case AT_DropConstraint:
6674 0 : return "DROP CONSTRAINT";
6675 0 : case AT_ReAddComment:
6676 0 : return NULL; /* not real grammar */
6677 0 : case AT_AlterColumnType:
6678 0 : return "ALTER COLUMN ... SET DATA TYPE";
6679 0 : case AT_AlterColumnGenericOptions:
6680 0 : return "ALTER COLUMN ... OPTIONS";
6681 0 : case AT_ChangeOwner:
6682 0 : return "OWNER TO";
6683 0 : case AT_ClusterOn:
6684 0 : return "CLUSTER ON";
6685 0 : case AT_DropCluster:
6686 0 : return "SET WITHOUT CLUSTER";
6687 0 : case AT_SetAccessMethod:
6688 0 : return "SET ACCESS METHOD";
6689 3 : case AT_SetLogged:
6690 3 : return "SET LOGGED";
6691 3 : case AT_SetUnLogged:
6692 3 : return "SET UNLOGGED";
6693 0 : case AT_DropOids:
6694 0 : return "SET WITHOUT OIDS";
6695 0 : case AT_SetTableSpace:
6696 0 : return "SET TABLESPACE";
6697 1 : case AT_SetRelOptions:
6698 1 : return "SET";
6699 0 : case AT_ResetRelOptions:
6700 0 : return "RESET";
6701 0 : case AT_ReplaceRelOptions:
6702 0 : return NULL; /* not real grammar */
6703 0 : case AT_EnableTrig:
6704 0 : return "ENABLE TRIGGER";
6705 0 : case AT_EnableAlwaysTrig:
6706 0 : return "ENABLE ALWAYS TRIGGER";
6707 0 : case AT_EnableReplicaTrig:
6708 0 : return "ENABLE REPLICA TRIGGER";
6709 0 : case AT_DisableTrig:
6710 0 : return "DISABLE TRIGGER";
6711 0 : case AT_EnableTrigAll:
6712 0 : return "ENABLE TRIGGER ALL";
6713 0 : case AT_DisableTrigAll:
6714 0 : return "DISABLE TRIGGER ALL";
6715 0 : case AT_EnableTrigUser:
6716 0 : return "ENABLE TRIGGER USER";
6717 0 : case AT_DisableTrigUser:
6718 0 : return "DISABLE TRIGGER USER";
6719 0 : case AT_EnableRule:
6720 0 : return "ENABLE RULE";
6721 0 : case AT_EnableAlwaysRule:
6722 0 : return "ENABLE ALWAYS RULE";
6723 0 : case AT_EnableReplicaRule:
6724 0 : return "ENABLE REPLICA RULE";
6725 0 : case AT_DisableRule:
6726 0 : return "DISABLE RULE";
6727 0 : case AT_AddInherit:
6728 0 : return "INHERIT";
6729 0 : case AT_DropInherit:
6730 0 : return "NO INHERIT";
6731 0 : case AT_AddOf:
6732 0 : return "OF";
6733 0 : case AT_DropOf:
6734 0 : return "NOT OF";
6735 0 : case AT_ReplicaIdentity:
6736 0 : return "REPLICA IDENTITY";
6737 0 : case AT_EnableRowSecurity:
6738 0 : return "ENABLE ROW SECURITY";
6739 0 : case AT_DisableRowSecurity:
6740 0 : return "DISABLE ROW SECURITY";
6741 0 : case AT_ForceRowSecurity:
6742 0 : return "FORCE ROW SECURITY";
6743 0 : case AT_NoForceRowSecurity:
6744 0 : return "NO FORCE ROW SECURITY";
6745 0 : case AT_GenericOptions:
6746 0 : return "OPTIONS";
6747 3 : case AT_AttachPartition:
6748 3 : return "ATTACH PARTITION";
6749 9 : case AT_DetachPartition:
6750 9 : return "DETACH PARTITION";
6751 3 : case AT_DetachPartitionFinalize:
6752 3 : return "DETACH PARTITION ... FINALIZE";
6753 0 : case AT_MergePartitions:
6754 0 : return "MERGE PARTITIONS";
6755 3 : case AT_SplitPartition:
6756 3 : return "SPLIT PARTITION";
6757 0 : case AT_AddIdentity:
6758 0 : return "ALTER COLUMN ... ADD IDENTITY";
6759 0 : case AT_SetIdentity:
6760 0 : return "ALTER COLUMN ... SET";
6761 0 : case AT_DropIdentity:
6762 0 : return "ALTER COLUMN ... DROP IDENTITY";
6763 0 : case AT_ReAddStatistics:
6764 0 : return NULL; /* not real grammar */
6765 : }
6766 :
6767 0 : return NULL;
6768 : }
6769 :
6770 : /*
6771 : * ATSimplePermissions
6772 : *
6773 : * - Ensure that it is a relation (or possibly a view)
6774 : * - Ensure this user is the owner
6775 : * - Ensure that it is not a system table
6776 : */
6777 : static void
6778 19838 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6779 : {
6780 : int actual_target;
6781 :
6782 19838 : switch (rel->rd_rel->relkind)
6783 : {
6784 15410 : case RELKIND_RELATION:
6785 15410 : actual_target = ATT_TABLE;
6786 15410 : break;
6787 3277 : case RELKIND_PARTITIONED_TABLE:
6788 3277 : actual_target = ATT_PARTITIONED_TABLE;
6789 3277 : break;
6790 201 : case RELKIND_VIEW:
6791 201 : actual_target = ATT_VIEW;
6792 201 : break;
6793 23 : case RELKIND_MATVIEW:
6794 23 : actual_target = ATT_MATVIEW;
6795 23 : break;
6796 121 : case RELKIND_INDEX:
6797 121 : actual_target = ATT_INDEX;
6798 121 : break;
6799 219 : case RELKIND_PARTITIONED_INDEX:
6800 219 : actual_target = ATT_PARTITIONED_INDEX;
6801 219 : break;
6802 108 : case RELKIND_COMPOSITE_TYPE:
6803 108 : actual_target = ATT_COMPOSITE_TYPE;
6804 108 : break;
6805 466 : case RELKIND_FOREIGN_TABLE:
6806 466 : actual_target = ATT_FOREIGN_TABLE;
6807 466 : break;
6808 12 : case RELKIND_SEQUENCE:
6809 12 : actual_target = ATT_SEQUENCE;
6810 12 : break;
6811 1 : default:
6812 1 : actual_target = 0;
6813 1 : break;
6814 : }
6815 :
6816 : /* Wrong target type? */
6817 19838 : if ((actual_target & allowed_targets) == 0)
6818 : {
6819 43 : const char *action_str = alter_table_type_to_string(cmdtype);
6820 :
6821 43 : if (action_str)
6822 43 : ereport(ERROR,
6823 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6824 : /* translator: %s is a group of some SQL keywords */
6825 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6826 : action_str, RelationGetRelationName(rel)),
6827 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6828 : else
6829 : /* internal error? */
6830 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6831 : RelationGetRelationName(rel));
6832 : }
6833 :
6834 : /* Permissions checks */
6835 19795 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6836 6 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6837 6 : RelationGetRelationName(rel));
6838 :
6839 19789 : if (!allowSystemTableMods && IsSystemRelation(rel))
6840 0 : ereport(ERROR,
6841 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6842 : errmsg("permission denied: \"%s\" is a system catalog",
6843 : RelationGetRelationName(rel))));
6844 19789 : }
6845 :
6846 : /*
6847 : * ATSimpleRecursion
6848 : *
6849 : * Simple table recursion sufficient for most ALTER TABLE operations.
6850 : * All direct and indirect children are processed in an unspecified order.
6851 : * Note that if a child inherits from the original table via multiple
6852 : * inheritance paths, it will be visited just once.
6853 : */
6854 : static void
6855 692 : ATSimpleRecursion(List **wqueue, Relation rel,
6856 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6857 : AlterTableUtilityContext *context)
6858 : {
6859 : /*
6860 : * Propagate to children, if desired and if there are (or might be) any
6861 : * children.
6862 : */
6863 692 : if (recurse && rel->rd_rel->relhassubclass)
6864 : {
6865 42 : Oid relid = RelationGetRelid(rel);
6866 : ListCell *child;
6867 : List *children;
6868 :
6869 42 : children = find_all_inheritors(relid, lockmode, NULL);
6870 :
6871 : /*
6872 : * find_all_inheritors does the recursive search of the inheritance
6873 : * hierarchy, so all we have to do is process all of the relids in the
6874 : * list that it returns.
6875 : */
6876 183 : foreach(child, children)
6877 : {
6878 141 : Oid childrelid = lfirst_oid(child);
6879 : Relation childrel;
6880 :
6881 141 : if (childrelid == relid)
6882 42 : continue;
6883 : /* find_all_inheritors already got lock */
6884 99 : childrel = relation_open(childrelid, NoLock);
6885 99 : CheckAlterTableIsSafe(childrel);
6886 99 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6887 99 : relation_close(childrel, NoLock);
6888 : }
6889 : }
6890 692 : }
6891 :
6892 : /*
6893 : * Obtain list of partitions of the given table, locking them all at the given
6894 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6895 : *
6896 : * This function is a no-op if the given relation is not a partitioned table;
6897 : * in particular, nothing is done if it's a legacy inheritance parent.
6898 : */
6899 : static void
6900 412 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6901 : {
6902 412 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6903 : {
6904 : List *inh;
6905 : ListCell *cell;
6906 :
6907 91 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6908 : /* first element is the parent rel; must ignore it */
6909 299 : for_each_from(cell, inh, 1)
6910 : {
6911 : Relation childrel;
6912 :
6913 : /* find_all_inheritors already got lock */
6914 211 : childrel = table_open(lfirst_oid(cell), NoLock);
6915 211 : CheckAlterTableIsSafe(childrel);
6916 208 : table_close(childrel, NoLock);
6917 : }
6918 88 : list_free(inh);
6919 : }
6920 409 : }
6921 :
6922 : /*
6923 : * ATTypedTableRecursion
6924 : *
6925 : * Propagate ALTER TYPE operations to the typed tables of that type.
6926 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6927 : * recursion to inheritance children of the typed tables.
6928 : */
6929 : static void
6930 96 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6931 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6932 : {
6933 : ListCell *child;
6934 : List *children;
6935 :
6936 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6937 :
6938 96 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6939 96 : RelationGetRelationName(rel),
6940 : cmd->behavior);
6941 :
6942 102 : foreach(child, children)
6943 : {
6944 15 : Oid childrelid = lfirst_oid(child);
6945 : Relation childrel;
6946 :
6947 15 : childrel = relation_open(childrelid, lockmode);
6948 15 : CheckAlterTableIsSafe(childrel);
6949 15 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6950 15 : relation_close(childrel, NoLock);
6951 : }
6952 87 : }
6953 :
6954 :
6955 : /*
6956 : * find_composite_type_dependencies
6957 : *
6958 : * Check to see if the type "typeOid" is being used as a column in some table
6959 : * (possibly nested several levels deep in composite types, arrays, etc!).
6960 : * Eventually, we'd like to propagate the check or rewrite operation
6961 : * into such tables, but for now, just error out if we find any.
6962 : *
6963 : * Caller should provide either the associated relation of a rowtype,
6964 : * or a type name (not both) for use in the error message, if any.
6965 : *
6966 : * Note that "typeOid" is not necessarily a composite type; it could also be
6967 : * another container type such as an array or range, or a domain over one of
6968 : * these things. The name of this function is therefore somewhat historical,
6969 : * but it's not worth changing.
6970 : *
6971 : * We assume that functions and views depending on the type are not reasons
6972 : * to reject the ALTER. (How safe is this really?)
6973 : */
6974 : void
6975 2406 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6976 : const char *origTypeName)
6977 : {
6978 : Relation depRel;
6979 : ScanKeyData key[2];
6980 : SysScanDesc depScan;
6981 : HeapTuple depTup;
6982 :
6983 : /* since this function recurses, it could be driven to stack overflow */
6984 2406 : check_stack_depth();
6985 :
6986 : /*
6987 : * We scan pg_depend to find those things that depend on the given type.
6988 : * (We assume we can ignore refobjsubid for a type.)
6989 : */
6990 2406 : depRel = table_open(DependRelationId, AccessShareLock);
6991 :
6992 2406 : ScanKeyInit(&key[0],
6993 : Anum_pg_depend_refclassid,
6994 : BTEqualStrategyNumber, F_OIDEQ,
6995 : ObjectIdGetDatum(TypeRelationId));
6996 2406 : ScanKeyInit(&key[1],
6997 : Anum_pg_depend_refobjid,
6998 : BTEqualStrategyNumber, F_OIDEQ,
6999 : ObjectIdGetDatum(typeOid));
7000 :
7001 2406 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
7002 : NULL, 2, key);
7003 :
7004 3693 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
7005 : {
7006 1365 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
7007 : Relation rel;
7008 : TupleDesc tupleDesc;
7009 : Form_pg_attribute att;
7010 :
7011 : /* Check for directly dependent types */
7012 1365 : if (pg_depend->classid == TypeRelationId)
7013 : {
7014 : /*
7015 : * This must be an array, domain, or range containing the given
7016 : * type, so recursively check for uses of this type. Note that
7017 : * any error message will mention the original type not the
7018 : * container; this is intentional.
7019 : */
7020 1162 : find_composite_type_dependencies(pg_depend->objid,
7021 : origRelation, origTypeName);
7022 1150 : continue;
7023 : }
7024 :
7025 : /* Else, ignore dependees that aren't relations */
7026 203 : if (pg_depend->classid != RelationRelationId)
7027 61 : continue;
7028 :
7029 142 : rel = relation_open(pg_depend->objid, AccessShareLock);
7030 142 : tupleDesc = RelationGetDescr(rel);
7031 :
7032 : /*
7033 : * If objsubid identifies a specific column, refer to that in error
7034 : * messages. Otherwise, search to see if there's a user column of the
7035 : * type. (We assume system columns are never of interesting types.)
7036 : * The search is needed because an index containing an expression
7037 : * column of the target type will just be recorded as a whole-relation
7038 : * dependency. If we do not find a column of the type, the dependency
7039 : * must indicate that the type is transiently referenced in an index
7040 : * expression but not stored on disk, which we assume is OK, just as
7041 : * we do for references in views. (It could also be that the target
7042 : * type is embedded in some container type that is stored in an index
7043 : * column, but the previous recursion should catch such cases.)
7044 : */
7045 142 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
7046 63 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7047 : else
7048 : {
7049 79 : att = NULL;
7050 203 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
7051 : {
7052 127 : att = TupleDescAttr(tupleDesc, attno - 1);
7053 127 : if (att->atttypid == typeOid && !att->attisdropped)
7054 3 : break;
7055 124 : att = NULL;
7056 : }
7057 79 : if (att == NULL)
7058 : {
7059 : /* No such column, so assume OK */
7060 76 : relation_close(rel, AccessShareLock);
7061 76 : continue;
7062 : }
7063 : }
7064 :
7065 : /*
7066 : * We definitely should reject if the relation has storage. If it's
7067 : * partitioned, then perhaps we don't have to reject: if there are
7068 : * partitions then we'll fail when we find one, else there is no
7069 : * stored data to worry about. However, it's possible that the type
7070 : * change would affect conclusions about whether the type is sortable
7071 : * or hashable and thus (if it's a partitioning column) break the
7072 : * partitioning rule. For now, reject for partitioned rels too.
7073 : */
7074 66 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7075 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7076 : {
7077 66 : if (origTypeName)
7078 15 : ereport(ERROR,
7079 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7080 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7081 : origTypeName,
7082 : RelationGetRelationName(rel),
7083 : NameStr(att->attname))));
7084 51 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7085 9 : ereport(ERROR,
7086 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7087 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7088 : RelationGetRelationName(origRelation),
7089 : RelationGetRelationName(rel),
7090 : NameStr(att->attname))));
7091 42 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7092 3 : ereport(ERROR,
7093 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7094 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7095 : RelationGetRelationName(origRelation),
7096 : RelationGetRelationName(rel),
7097 : NameStr(att->attname))));
7098 : else
7099 39 : ereport(ERROR,
7100 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7101 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7102 : RelationGetRelationName(origRelation),
7103 : RelationGetRelationName(rel),
7104 : NameStr(att->attname))));
7105 : }
7106 0 : else if (OidIsValid(rel->rd_rel->reltype))
7107 : {
7108 : /*
7109 : * A view or composite type itself isn't a problem, but we must
7110 : * recursively check for indirect dependencies via its rowtype.
7111 : */
7112 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
7113 : origRelation, origTypeName);
7114 : }
7115 :
7116 0 : relation_close(rel, AccessShareLock);
7117 : }
7118 :
7119 2328 : systable_endscan(depScan);
7120 :
7121 2328 : relation_close(depRel, AccessShareLock);
7122 2328 : }
7123 :
7124 :
7125 : /*
7126 : * find_typed_table_dependencies
7127 : *
7128 : * Check to see if a composite type is being used as the type of a
7129 : * typed table. Abort if any are found and behavior is RESTRICT.
7130 : * Else return the list of tables.
7131 : */
7132 : static List *
7133 108 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7134 : {
7135 : Relation classRel;
7136 : ScanKeyData key[1];
7137 : TableScanDesc scan;
7138 : HeapTuple tuple;
7139 108 : List *result = NIL;
7140 :
7141 108 : classRel = table_open(RelationRelationId, AccessShareLock);
7142 :
7143 108 : ScanKeyInit(&key[0],
7144 : Anum_pg_class_reloftype,
7145 : BTEqualStrategyNumber, F_OIDEQ,
7146 : ObjectIdGetDatum(typeOid));
7147 :
7148 108 : scan = table_beginscan_catalog(classRel, 1, key);
7149 :
7150 126 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7151 : {
7152 30 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7153 :
7154 30 : if (behavior == DROP_RESTRICT)
7155 12 : ereport(ERROR,
7156 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7157 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7158 : typeName),
7159 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7160 : else
7161 18 : result = lappend_oid(result, classform->oid);
7162 : }
7163 :
7164 96 : table_endscan(scan);
7165 96 : table_close(classRel, AccessShareLock);
7166 :
7167 96 : return result;
7168 : }
7169 :
7170 :
7171 : /*
7172 : * check_of_type
7173 : *
7174 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7175 : * isn't suitable, throw an error. Currently, we require that the type
7176 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7177 : * would require handling a number of extra corner cases in the DDL commands.
7178 : * (Also, allowing domain-over-composite would open up a can of worms about
7179 : * whether and how the domain's constraints should apply to derived tables.)
7180 : */
7181 : void
7182 91 : check_of_type(HeapTuple typetuple)
7183 : {
7184 91 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7185 91 : bool typeOk = false;
7186 :
7187 91 : if (typ->typtype == TYPTYPE_COMPOSITE)
7188 : {
7189 : Relation typeRelation;
7190 :
7191 : Assert(OidIsValid(typ->typrelid));
7192 88 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7193 88 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7194 :
7195 : /*
7196 : * Close the parent rel, but keep our AccessShareLock on it until xact
7197 : * commit. That will prevent someone else from deleting or ALTERing
7198 : * the type before the typed table creation/conversion commits.
7199 : */
7200 88 : relation_close(typeRelation, NoLock);
7201 :
7202 88 : if (!typeOk)
7203 3 : ereport(ERROR,
7204 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7205 : errmsg("type %s is the row type of another table",
7206 : format_type_be(typ->oid)),
7207 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7208 : }
7209 : else
7210 3 : ereport(ERROR,
7211 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7212 : errmsg("type %s is not a composite type",
7213 : format_type_be(typ->oid))));
7214 85 : }
7215 :
7216 :
7217 : /*
7218 : * ALTER TABLE ADD COLUMN
7219 : *
7220 : * Adds an additional attribute to a relation making the assumption that
7221 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7222 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7223 : * AlterTableCmd's.
7224 : *
7225 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7226 : * have to decide at runtime whether to recurse or not depending on whether we
7227 : * actually add a column or merely merge with an existing column. (We can't
7228 : * check this in a static pre-pass because it won't handle multiple inheritance
7229 : * situations correctly.)
7230 : */
7231 : static void
7232 1112 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7233 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7234 : AlterTableUtilityContext *context)
7235 : {
7236 1112 : if (rel->rd_rel->reloftype && !recursing)
7237 3 : ereport(ERROR,
7238 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7239 : errmsg("cannot add column to typed table")));
7240 :
7241 1109 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7242 29 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7243 :
7244 1106 : if (recurse && !is_view)
7245 1056 : cmd->recurse = true;
7246 1106 : }
7247 :
7248 : /*
7249 : * Add a column to a table. The return value is the address of the
7250 : * new column in the parent relation.
7251 : *
7252 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7253 : * copy (but that happens only after we check for IF NOT EXISTS).
7254 : */
7255 : static ObjectAddress
7256 1472 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7257 : AlterTableCmd **cmd, bool recurse, bool recursing,
7258 : LOCKMODE lockmode, AlterTablePass cur_pass,
7259 : AlterTableUtilityContext *context)
7260 : {
7261 1472 : Oid myrelid = RelationGetRelid(rel);
7262 1472 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7263 1472 : bool if_not_exists = (*cmd)->missing_ok;
7264 : Relation pgclass,
7265 : attrdesc;
7266 : HeapTuple reltup;
7267 : Form_pg_class relform;
7268 : Form_pg_attribute attribute;
7269 : int newattnum;
7270 : char relkind;
7271 : Expr *defval;
7272 : List *children;
7273 : ListCell *child;
7274 : AlterTableCmd *childcmd;
7275 : ObjectAddress address;
7276 : TupleDesc tupdesc;
7277 :
7278 : /* since this function recurses, it could be driven to stack overflow */
7279 1472 : check_stack_depth();
7280 :
7281 : /* At top level, permission check was done in ATPrepCmd, else do it */
7282 1472 : if (recursing)
7283 369 : ATSimplePermissions((*cmd)->subtype, rel,
7284 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7285 :
7286 1472 : if (rel->rd_rel->relispartition && !recursing)
7287 6 : ereport(ERROR,
7288 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7289 : errmsg("cannot add column to a partition")));
7290 :
7291 1466 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7292 :
7293 : /*
7294 : * Are we adding the column to a recursion child? If so, check whether to
7295 : * merge with an existing definition for the column. If we do merge, we
7296 : * must not recurse. Children will already have the column, and recursing
7297 : * into them would mess up attinhcount.
7298 : */
7299 1466 : if (colDef->inhcount > 0)
7300 : {
7301 : HeapTuple tuple;
7302 :
7303 : /* Does child already have a column by this name? */
7304 369 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7305 369 : if (HeapTupleIsValid(tuple))
7306 : {
7307 30 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7308 : Oid ctypeId;
7309 : int32 ctypmod;
7310 : Oid ccollid;
7311 :
7312 : /* Child column must match on type, typmod, and collation */
7313 30 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7314 30 : if (ctypeId != childatt->atttypid ||
7315 30 : ctypmod != childatt->atttypmod)
7316 0 : ereport(ERROR,
7317 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7318 : errmsg("child table \"%s\" has different type for column \"%s\"",
7319 : RelationGetRelationName(rel), colDef->colname)));
7320 30 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7321 30 : if (ccollid != childatt->attcollation)
7322 0 : ereport(ERROR,
7323 : (errcode(ERRCODE_COLLATION_MISMATCH),
7324 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7325 : RelationGetRelationName(rel), colDef->colname),
7326 : errdetail("\"%s\" versus \"%s\"",
7327 : get_collation_name(ccollid),
7328 : get_collation_name(childatt->attcollation))));
7329 :
7330 : /* Bump the existing child att's inhcount */
7331 30 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7332 : &childatt->attinhcount))
7333 0 : ereport(ERROR,
7334 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7335 : errmsg("too many inheritance parents"));
7336 30 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7337 :
7338 30 : heap_freetuple(tuple);
7339 :
7340 : /* Inform the user about the merge */
7341 30 : ereport(NOTICE,
7342 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7343 : colDef->colname, RelationGetRelationName(rel))));
7344 :
7345 30 : table_close(attrdesc, RowExclusiveLock);
7346 :
7347 : /* Make the child column change visible */
7348 30 : CommandCounterIncrement();
7349 :
7350 30 : return InvalidObjectAddress;
7351 : }
7352 : }
7353 :
7354 : /* skip if the name already exists and if_not_exists is true */
7355 1436 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7356 : {
7357 27 : table_close(attrdesc, RowExclusiveLock);
7358 27 : return InvalidObjectAddress;
7359 : }
7360 :
7361 : /*
7362 : * Okay, we need to add the column, so go ahead and do parse
7363 : * transformation. This can result in queueing up, or even immediately
7364 : * executing, subsidiary operations (such as creation of unique indexes);
7365 : * so we mustn't do it until we have made the if_not_exists check.
7366 : *
7367 : * When recursing, the command was already transformed and we needn't do
7368 : * so again. Also, if context isn't given we can't transform. (That
7369 : * currently happens only for AT_AddColumnToView; we expect that view.c
7370 : * passed us a ColumnDef that doesn't need work.)
7371 : */
7372 1394 : if (context != NULL && !recursing)
7373 : {
7374 1043 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7375 : cur_pass, context);
7376 : Assert(*cmd != NULL);
7377 1040 : colDef = castNode(ColumnDef, (*cmd)->def);
7378 : }
7379 :
7380 : /*
7381 : * Regular inheritance children are independent enough not to inherit the
7382 : * identity column from parent hence cannot recursively add identity
7383 : * column if the table has inheritance children.
7384 : *
7385 : * Partitions, on the other hand, are integral part of a partitioned table
7386 : * and inherit identity column. Hence propagate identity column down the
7387 : * partition hierarchy.
7388 : */
7389 1391 : if (colDef->identity &&
7390 27 : recurse &&
7391 51 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7392 24 : find_inheritance_children(myrelid, NoLock) != NIL)
7393 3 : ereport(ERROR,
7394 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7395 : errmsg("cannot recursively add identity column to table that has child tables")));
7396 :
7397 1388 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7398 :
7399 1388 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7400 1388 : if (!HeapTupleIsValid(reltup))
7401 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7402 1388 : relform = (Form_pg_class) GETSTRUCT(reltup);
7403 1388 : relkind = relform->relkind;
7404 :
7405 : /* Determine the new attribute's number */
7406 1388 : newattnum = relform->relnatts + 1;
7407 1388 : if (newattnum > MaxHeapAttributeNumber)
7408 0 : ereport(ERROR,
7409 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7410 : errmsg("tables can have at most %d columns",
7411 : MaxHeapAttributeNumber)));
7412 :
7413 : /*
7414 : * Construct new attribute's pg_attribute entry.
7415 : */
7416 1388 : tupdesc = BuildDescForRelation(list_make1(colDef));
7417 :
7418 1382 : attribute = TupleDescAttr(tupdesc, 0);
7419 :
7420 : /* Fix up attribute number */
7421 1382 : attribute->attnum = newattnum;
7422 :
7423 : /* make sure datatype is legal for a column */
7424 2764 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7425 1382 : list_make1_oid(rel->rd_rel->reltype),
7426 1382 : (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7427 :
7428 1364 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7429 :
7430 1364 : table_close(attrdesc, RowExclusiveLock);
7431 :
7432 : /*
7433 : * Update pg_class tuple as appropriate
7434 : */
7435 1364 : relform->relnatts = newattnum;
7436 :
7437 1364 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7438 :
7439 1364 : heap_freetuple(reltup);
7440 :
7441 : /* Post creation hook for new attribute */
7442 1364 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7443 :
7444 1364 : table_close(pgclass, RowExclusiveLock);
7445 :
7446 : /* Make the attribute's catalog entry visible */
7447 1364 : CommandCounterIncrement();
7448 :
7449 : /*
7450 : * Store the DEFAULT, if any, in the catalogs
7451 : */
7452 1364 : if (colDef->raw_default)
7453 : {
7454 : RawColumnDefault *rawEnt;
7455 :
7456 480 : rawEnt = palloc_object(RawColumnDefault);
7457 480 : rawEnt->attnum = attribute->attnum;
7458 480 : rawEnt->raw_default = copyObject(colDef->raw_default);
7459 480 : rawEnt->generated = colDef->generated;
7460 :
7461 : /*
7462 : * This function is intended for CREATE TABLE, so it processes a
7463 : * _list_ of defaults, but we just do one.
7464 : */
7465 480 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7466 : false, true, false, NULL);
7467 :
7468 : /* Make the additional catalog changes visible */
7469 468 : CommandCounterIncrement();
7470 : }
7471 :
7472 : /*
7473 : * Tell Phase 3 to fill in the default expression, if there is one.
7474 : *
7475 : * If there is no default, Phase 3 doesn't have to do anything, because
7476 : * that effectively means that the default is NULL. The heap tuple access
7477 : * routines always check for attnum > # of attributes in tuple, and return
7478 : * NULL if so, so without any modification of the tuple data we will get
7479 : * the effect of NULL values in the new column.
7480 : *
7481 : * An exception occurs when the new column is of a domain type: the domain
7482 : * might have a not-null constraint, or a check constraint that indirectly
7483 : * rejects nulls. If there are any domain constraints then we construct
7484 : * an explicit NULL default value that will be passed through
7485 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7486 : * rewriting the table which we really wouldn't have to do; but we do it
7487 : * to preserve the historical behavior that such a failure will be raised
7488 : * only if the table currently contains some rows.)
7489 : *
7490 : * Note: we use build_column_default, and not just the cooked default
7491 : * returned by AddRelationNewConstraints, so that the right thing happens
7492 : * when a datatype's default applies.
7493 : *
7494 : * Note: it might seem that this should happen at the end of Phase 2, so
7495 : * that the effects of subsequent subcommands can be taken into account.
7496 : * It's intentional that we do it now, though. The new column should be
7497 : * filled according to what is said in the ADD COLUMN subcommand, so that
7498 : * the effects are the same as if this subcommand had been run by itself
7499 : * and the later subcommands had been issued in new ALTER TABLE commands.
7500 : *
7501 : * We can skip this entirely for relations without storage, since Phase 3
7502 : * is certainly not going to touch them.
7503 : */
7504 1352 : if (RELKIND_HAS_STORAGE(relkind))
7505 : {
7506 : bool has_domain_constraints;
7507 1164 : bool has_missing = false;
7508 :
7509 : /*
7510 : * For an identity column, we can't use build_column_default(),
7511 : * because the sequence ownership isn't set yet. So do it manually.
7512 : */
7513 1164 : if (colDef->identity)
7514 : {
7515 21 : NextValueExpr *nve = makeNode(NextValueExpr);
7516 :
7517 21 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7518 21 : nve->typeId = attribute->atttypid;
7519 :
7520 21 : defval = (Expr *) nve;
7521 : }
7522 : else
7523 1143 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7524 :
7525 : /* Build CoerceToDomain(NULL) expression if needed */
7526 1164 : has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7527 1164 : if (!defval && has_domain_constraints)
7528 : {
7529 : Oid baseTypeId;
7530 : int32 baseTypeMod;
7531 : Oid baseTypeColl;
7532 :
7533 3 : baseTypeMod = attribute->atttypmod;
7534 3 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7535 3 : baseTypeColl = get_typcollation(baseTypeId);
7536 3 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7537 3 : defval = (Expr *) coerce_to_target_type(NULL,
7538 : (Node *) defval,
7539 : baseTypeId,
7540 : attribute->atttypid,
7541 : attribute->atttypmod,
7542 : COERCION_ASSIGNMENT,
7543 : COERCE_IMPLICIT_CAST,
7544 : -1);
7545 3 : if (defval == NULL) /* should not happen */
7546 0 : elog(ERROR, "failed to coerce base type to domain");
7547 : }
7548 :
7549 1164 : if (defval)
7550 : {
7551 : NewColumnValue *newval;
7552 :
7553 : /* Prepare defval for execution, either here or in Phase 3 */
7554 415 : defval = expression_planner(defval);
7555 :
7556 : /* Add the new default to the newvals list */
7557 415 : newval = palloc0_object(NewColumnValue);
7558 415 : newval->attnum = attribute->attnum;
7559 415 : newval->expr = defval;
7560 415 : newval->is_generated = (colDef->generated != '\0');
7561 :
7562 415 : tab->newvals = lappend(tab->newvals, newval);
7563 :
7564 : /*
7565 : * Attempt to skip a complete table rewrite by storing the
7566 : * specified DEFAULT value outside of the heap. This is only
7567 : * allowed for plain relations and non-generated columns, and the
7568 : * default expression can't be volatile (stable is OK). Note that
7569 : * contain_volatile_functions deems CoerceToDomain immutable, but
7570 : * here we consider that coercion to a domain with constraints is
7571 : * volatile; else it might fail even when the table is empty.
7572 : */
7573 415 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
7574 415 : !colDef->generated &&
7575 351 : !has_domain_constraints &&
7576 345 : !contain_volatile_functions((Node *) defval))
7577 261 : {
7578 : EState *estate;
7579 : ExprState *exprState;
7580 : Datum missingval;
7581 : bool missingIsNull;
7582 :
7583 : /* Evaluate the default expression */
7584 261 : estate = CreateExecutorState();
7585 261 : exprState = ExecPrepareExpr(defval, estate);
7586 261 : missingval = ExecEvalExpr(exprState,
7587 261 : GetPerTupleExprContext(estate),
7588 : &missingIsNull);
7589 : /* If it turns out NULL, nothing to do; else store it */
7590 261 : if (!missingIsNull)
7591 : {
7592 261 : StoreAttrMissingVal(rel, attribute->attnum, missingval);
7593 : /* Make the additional catalog change visible */
7594 261 : CommandCounterIncrement();
7595 261 : has_missing = true;
7596 : }
7597 261 : FreeExecutorState(estate);
7598 : }
7599 : else
7600 : {
7601 : /*
7602 : * Failed to use missing mode. We have to do a table rewrite
7603 : * to install the value --- unless it's a virtual generated
7604 : * column.
7605 : */
7606 154 : if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7607 108 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7608 : }
7609 : }
7610 :
7611 1164 : if (!has_missing)
7612 : {
7613 : /*
7614 : * If the new column is NOT NULL, and there is no missing value,
7615 : * tell Phase 3 it needs to check for NULLs.
7616 : */
7617 903 : tab->verify_new_notnull |= colDef->is_not_null;
7618 : }
7619 : }
7620 :
7621 : /*
7622 : * Add needed dependency entries for the new column.
7623 : */
7624 1352 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7625 1352 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7626 :
7627 : /*
7628 : * Propagate to children as appropriate. Unlike most other ALTER
7629 : * routines, we have to do this one level of recursion at a time; we can't
7630 : * use find_all_inheritors to do it in one pass.
7631 : */
7632 : children =
7633 1352 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7634 :
7635 : /*
7636 : * If we are told not to recurse, there had better not be any child
7637 : * tables; else the addition would put them out of step.
7638 : */
7639 1352 : if (children && !recurse)
7640 6 : ereport(ERROR,
7641 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7642 : errmsg("column must be added to child tables too")));
7643 :
7644 : /* Children should see column as singly inherited */
7645 1346 : if (!recursing)
7646 : {
7647 1007 : childcmd = copyObject(*cmd);
7648 1007 : colDef = castNode(ColumnDef, childcmd->def);
7649 1007 : colDef->inhcount = 1;
7650 1007 : colDef->is_local = false;
7651 : }
7652 : else
7653 339 : childcmd = *cmd; /* no need to copy again */
7654 :
7655 1715 : foreach(child, children)
7656 : {
7657 369 : Oid childrelid = lfirst_oid(child);
7658 : Relation childrel;
7659 : AlteredTableInfo *childtab;
7660 :
7661 : /* find_inheritance_children already got lock */
7662 369 : childrel = table_open(childrelid, NoLock);
7663 369 : CheckAlterTableIsSafe(childrel);
7664 :
7665 : /* Find or create work queue entry for this table */
7666 369 : childtab = ATGetQueueEntry(wqueue, childrel);
7667 :
7668 : /* Recurse to child; return value is ignored */
7669 369 : ATExecAddColumn(wqueue, childtab, childrel,
7670 : &childcmd, recurse, true,
7671 : lockmode, cur_pass, context);
7672 :
7673 369 : table_close(childrel, NoLock);
7674 : }
7675 :
7676 1346 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7677 1346 : return address;
7678 : }
7679 :
7680 : /*
7681 : * If a new or renamed column will collide with the name of an existing
7682 : * column and if_not_exists is false then error out, else do nothing.
7683 : */
7684 : static bool
7685 1661 : check_for_column_name_collision(Relation rel, const char *colname,
7686 : bool if_not_exists)
7687 : {
7688 : HeapTuple attTuple;
7689 : int attnum;
7690 :
7691 : /*
7692 : * this test is deliberately not attisdropped-aware, since if one tries to
7693 : * add a column matching a dropped column name, it's gonna fail anyway.
7694 : */
7695 1661 : attTuple = SearchSysCache2(ATTNAME,
7696 : ObjectIdGetDatum(RelationGetRelid(rel)),
7697 : PointerGetDatum(colname));
7698 1661 : if (!HeapTupleIsValid(attTuple))
7699 1613 : return true;
7700 :
7701 48 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7702 48 : ReleaseSysCache(attTuple);
7703 :
7704 : /*
7705 : * We throw a different error message for conflicts with system column
7706 : * names, since they are normally not shown and the user might otherwise
7707 : * be confused about the reason for the conflict.
7708 : */
7709 48 : if (attnum <= 0)
7710 6 : ereport(ERROR,
7711 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7712 : errmsg("column name \"%s\" conflicts with a system column name",
7713 : colname)));
7714 : else
7715 : {
7716 42 : if (if_not_exists)
7717 : {
7718 27 : ereport(NOTICE,
7719 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7720 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7721 : colname, RelationGetRelationName(rel))));
7722 27 : return false;
7723 : }
7724 :
7725 15 : ereport(ERROR,
7726 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7727 : errmsg("column \"%s\" of relation \"%s\" already exists",
7728 : colname, RelationGetRelationName(rel))));
7729 : }
7730 :
7731 : return true;
7732 : }
7733 :
7734 : /*
7735 : * Install a column's dependency on its datatype.
7736 : */
7737 : static void
7738 1927 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7739 : {
7740 : ObjectAddress myself,
7741 : referenced;
7742 :
7743 1927 : myself.classId = RelationRelationId;
7744 1927 : myself.objectId = relid;
7745 1927 : myself.objectSubId = attnum;
7746 1927 : referenced.classId = TypeRelationId;
7747 1927 : referenced.objectId = typid;
7748 1927 : referenced.objectSubId = 0;
7749 1927 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7750 1927 : }
7751 :
7752 : /*
7753 : * Install a column's dependency on its collation.
7754 : */
7755 : static void
7756 1927 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7757 : {
7758 : ObjectAddress myself,
7759 : referenced;
7760 :
7761 : /* We know the default collation is pinned, so don't bother recording it */
7762 1927 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7763 : {
7764 9 : myself.classId = RelationRelationId;
7765 9 : myself.objectId = relid;
7766 9 : myself.objectSubId = attnum;
7767 9 : referenced.classId = CollationRelationId;
7768 9 : referenced.objectId = collid;
7769 9 : referenced.objectSubId = 0;
7770 9 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7771 : }
7772 1927 : }
7773 :
7774 : /*
7775 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7776 : *
7777 : * Return the address of the modified column. If the column was already
7778 : * nullable, InvalidObjectAddress is returned.
7779 : */
7780 : static ObjectAddress
7781 134 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7782 : LOCKMODE lockmode)
7783 : {
7784 : HeapTuple tuple;
7785 : HeapTuple conTup;
7786 : Form_pg_attribute attTup;
7787 : AttrNumber attnum;
7788 : Relation attr_rel;
7789 : ObjectAddress address;
7790 :
7791 : /*
7792 : * lookup the attribute
7793 : */
7794 134 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7795 :
7796 134 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7797 134 : if (!HeapTupleIsValid(tuple))
7798 9 : ereport(ERROR,
7799 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7800 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7801 : colName, RelationGetRelationName(rel))));
7802 125 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7803 125 : attnum = attTup->attnum;
7804 125 : ObjectAddressSubSet(address, RelationRelationId,
7805 : RelationGetRelid(rel), attnum);
7806 :
7807 : /* If the column is already nullable there's nothing to do. */
7808 125 : if (!attTup->attnotnull)
7809 : {
7810 0 : table_close(attr_rel, RowExclusiveLock);
7811 0 : return InvalidObjectAddress;
7812 : }
7813 :
7814 : /* Prevent them from altering a system attribute */
7815 125 : if (attnum <= 0)
7816 0 : ereport(ERROR,
7817 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7818 : errmsg("cannot alter system column \"%s\"",
7819 : colName)));
7820 :
7821 125 : if (attTup->attidentity)
7822 9 : ereport(ERROR,
7823 : (errcode(ERRCODE_SYNTAX_ERROR),
7824 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7825 : colName, RelationGetRelationName(rel))));
7826 :
7827 : /*
7828 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7829 : */
7830 116 : if (rel->rd_rel->relispartition)
7831 : {
7832 6 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7833 6 : Relation parent = table_open(parentId, AccessShareLock);
7834 6 : TupleDesc tupDesc = RelationGetDescr(parent);
7835 : AttrNumber parent_attnum;
7836 :
7837 6 : parent_attnum = get_attnum(parentId, colName);
7838 6 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7839 6 : ereport(ERROR,
7840 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7841 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7842 : colName)));
7843 0 : table_close(parent, AccessShareLock);
7844 : }
7845 :
7846 : /*
7847 : * Find the constraint that makes this column NOT NULL, and drop it.
7848 : * dropconstraint_internal() resets attnotnull.
7849 : */
7850 110 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7851 110 : if (conTup == NULL)
7852 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7853 : colName, RelationGetRelationName(rel));
7854 :
7855 : /* The normal case: we have a pg_constraint row, remove it */
7856 110 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7857 : false, lockmode);
7858 83 : heap_freetuple(conTup);
7859 :
7860 83 : InvokeObjectPostAlterHook(RelationRelationId,
7861 : RelationGetRelid(rel), attnum);
7862 :
7863 83 : table_close(attr_rel, RowExclusiveLock);
7864 :
7865 83 : return address;
7866 : }
7867 :
7868 : /*
7869 : * set_attnotnull
7870 : * Helper to update/validate the pg_attribute status of a not-null
7871 : * constraint
7872 : *
7873 : * pg_attribute.attnotnull is set true, if it isn't already.
7874 : * If queue_validation is true, also set up wqueue to validate the constraint.
7875 : * wqueue may be given as NULL when validation is not needed (e.g., on table
7876 : * creation).
7877 : */
7878 : static void
7879 13066 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7880 : bool is_valid, bool queue_validation)
7881 : {
7882 : Form_pg_attribute attr;
7883 : CompactAttribute *thisatt;
7884 :
7885 : Assert(!queue_validation || wqueue);
7886 :
7887 13066 : CheckAlterTableIsSafe(rel);
7888 :
7889 : /*
7890 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7891 : * attribute.
7892 : */
7893 13066 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7894 13066 : if (attr->attisdropped)
7895 0 : return;
7896 :
7897 13066 : if (!attr->attnotnull)
7898 : {
7899 : Relation attr_rel;
7900 : HeapTuple tuple;
7901 :
7902 748 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7903 :
7904 748 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7905 748 : if (!HeapTupleIsValid(tuple))
7906 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7907 : attnum, RelationGetRelid(rel));
7908 :
7909 748 : thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7910 748 : thisatt->attnullability = ATTNULLABLE_VALID;
7911 :
7912 748 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7913 :
7914 748 : attr->attnotnull = true;
7915 748 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7916 :
7917 : /*
7918 : * If the nullness isn't already proven by validated constraints, have
7919 : * ALTER TABLE phase 3 test for it.
7920 : */
7921 748 : if (queue_validation && wqueue &&
7922 623 : !NotNullImpliedByRelConstraints(rel, attr))
7923 : {
7924 : AlteredTableInfo *tab;
7925 :
7926 598 : tab = ATGetQueueEntry(wqueue, rel);
7927 598 : tab->verify_new_notnull = true;
7928 : }
7929 :
7930 748 : CommandCounterIncrement();
7931 :
7932 748 : table_close(attr_rel, RowExclusiveLock);
7933 748 : heap_freetuple(tuple);
7934 : }
7935 : else
7936 : {
7937 12318 : CacheInvalidateRelcache(rel);
7938 : }
7939 : }
7940 :
7941 : /*
7942 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7943 : *
7944 : * Add a not-null constraint to a single table and its children. Returns
7945 : * the address of the constraint added to the parent relation, if one gets
7946 : * added, or InvalidObjectAddress otherwise.
7947 : *
7948 : * We must recurse to child tables during execution, rather than using
7949 : * ALTER TABLE's normal prep-time recursion.
7950 : */
7951 : static ObjectAddress
7952 356 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7953 : bool recurse, bool recursing, LOCKMODE lockmode)
7954 : {
7955 : HeapTuple tuple;
7956 : AttrNumber attnum;
7957 : ObjectAddress address;
7958 : Constraint *constraint;
7959 : CookedConstraint *ccon;
7960 : List *cooked;
7961 356 : bool is_no_inherit = false;
7962 :
7963 : /* Guard against stack overflow due to overly deep inheritance tree. */
7964 356 : check_stack_depth();
7965 :
7966 : /* At top level, permission check was done in ATPrepCmd, else do it */
7967 356 : if (recursing)
7968 : {
7969 149 : ATSimplePermissions(AT_AddConstraint, rel,
7970 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7971 : Assert(conName != NULL);
7972 : }
7973 :
7974 356 : attnum = get_attnum(RelationGetRelid(rel), colName);
7975 356 : if (attnum == InvalidAttrNumber)
7976 9 : ereport(ERROR,
7977 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7978 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7979 : colName, RelationGetRelationName(rel))));
7980 :
7981 : /* Prevent them from altering a system attribute */
7982 347 : if (attnum <= 0)
7983 0 : ereport(ERROR,
7984 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7985 : errmsg("cannot alter system column \"%s\"",
7986 : colName)));
7987 :
7988 : /* See if there's already a constraint */
7989 347 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7990 347 : if (HeapTupleIsValid(tuple))
7991 : {
7992 79 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7993 79 : bool changed = false;
7994 :
7995 : /*
7996 : * Don't let a NO INHERIT constraint be changed into inherit.
7997 : */
7998 79 : if (conForm->connoinherit && recurse)
7999 6 : ereport(ERROR,
8000 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8001 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
8002 : NameStr(conForm->conname),
8003 : RelationGetRelationName(rel)));
8004 :
8005 : /*
8006 : * If we find an appropriate constraint, we're almost done, but just
8007 : * need to change some properties on it: if we're recursing, increment
8008 : * coninhcount; if not, set conislocal if not already set.
8009 : */
8010 73 : if (recursing)
8011 : {
8012 51 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
8013 : &conForm->coninhcount))
8014 0 : ereport(ERROR,
8015 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
8016 : errmsg("too many inheritance parents"));
8017 51 : changed = true;
8018 : }
8019 22 : else if (!conForm->conislocal)
8020 : {
8021 0 : conForm->conislocal = true;
8022 0 : changed = true;
8023 : }
8024 22 : else if (!conForm->convalidated)
8025 : {
8026 : /*
8027 : * Flip attnotnull and convalidated, and also validate the
8028 : * constraint.
8029 : */
8030 12 : return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
8031 : recurse, recursing, lockmode);
8032 : }
8033 :
8034 61 : if (changed)
8035 : {
8036 : Relation constr_rel;
8037 :
8038 51 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
8039 :
8040 51 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
8041 51 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
8042 51 : table_close(constr_rel, RowExclusiveLock);
8043 : }
8044 :
8045 61 : if (changed)
8046 51 : return address;
8047 : else
8048 10 : return InvalidObjectAddress;
8049 : }
8050 :
8051 : /*
8052 : * If we're asked not to recurse, and children exist, raise an error for
8053 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
8054 : * specified.
8055 : */
8056 283 : if (!recurse &&
8057 15 : find_inheritance_children(RelationGetRelid(rel),
8058 : NoLock) != NIL)
8059 : {
8060 9 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8061 3 : ereport(ERROR,
8062 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8063 : errmsg("constraint must be added to child tables too"),
8064 : errhint("Do not specify the ONLY keyword."));
8065 : else
8066 6 : is_no_inherit = true;
8067 : }
8068 :
8069 : /*
8070 : * No constraint exists; we must add one. First determine a name to use,
8071 : * if we haven't already.
8072 : */
8073 265 : if (!recursing)
8074 : {
8075 : Assert(conName == NULL);
8076 170 : conName = ChooseConstraintName(RelationGetRelationName(rel),
8077 : colName, "not_null",
8078 170 : RelationGetNamespace(rel),
8079 : NIL);
8080 : }
8081 :
8082 265 : constraint = makeNotNullConstraint(makeString(colName));
8083 265 : constraint->is_no_inherit = is_no_inherit;
8084 265 : constraint->conname = conName;
8085 :
8086 : /* and do it */
8087 265 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8088 265 : false, !recursing, false, NULL);
8089 265 : ccon = linitial(cooked);
8090 265 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8091 :
8092 265 : InvokeObjectPostAlterHook(RelationRelationId,
8093 : RelationGetRelid(rel), attnum);
8094 :
8095 : /* Mark pg_attribute.attnotnull for the column and queue validation */
8096 265 : set_attnotnull(wqueue, rel, attnum, true, true);
8097 :
8098 : /*
8099 : * Recurse to propagate the constraint to children that don't have one.
8100 : */
8101 265 : if (recurse)
8102 : {
8103 : List *children;
8104 :
8105 253 : children = find_inheritance_children(RelationGetRelid(rel),
8106 : lockmode);
8107 :
8108 622 : foreach_oid(childoid, children)
8109 : {
8110 122 : Relation childrel = table_open(childoid, NoLock);
8111 :
8112 122 : CommandCounterIncrement();
8113 :
8114 122 : ATExecSetNotNull(wqueue, childrel, conName, colName,
8115 : recurse, true, lockmode);
8116 119 : table_close(childrel, NoLock);
8117 : }
8118 : }
8119 :
8120 262 : return address;
8121 : }
8122 :
8123 : /*
8124 : * NotNullImpliedByRelConstraints
8125 : * Does rel's existing constraints imply NOT NULL for the given attribute?
8126 : */
8127 : static bool
8128 623 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8129 : {
8130 623 : NullTest *nnulltest = makeNode(NullTest);
8131 :
8132 1246 : nnulltest->arg = (Expr *) makeVar(1,
8133 623 : attr->attnum,
8134 : attr->atttypid,
8135 : attr->atttypmod,
8136 : attr->attcollation,
8137 : 0);
8138 623 : nnulltest->nulltesttype = IS_NOT_NULL;
8139 :
8140 : /*
8141 : * argisrow = false is correct even for a composite column, because
8142 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8143 : * case, just IS DISTINCT FROM NULL.
8144 : */
8145 623 : nnulltest->argisrow = false;
8146 623 : nnulltest->location = -1;
8147 :
8148 623 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8149 : {
8150 25 : ereport(DEBUG1,
8151 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8152 : RelationGetRelationName(rel), NameStr(attr->attname))));
8153 25 : return true;
8154 : }
8155 :
8156 598 : return false;
8157 : }
8158 :
8159 : /*
8160 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8161 : *
8162 : * Return the address of the affected column.
8163 : */
8164 : static ObjectAddress
8165 292 : ATExecColumnDefault(Relation rel, const char *colName,
8166 : Node *newDefault, LOCKMODE lockmode)
8167 : {
8168 292 : TupleDesc tupdesc = RelationGetDescr(rel);
8169 : AttrNumber attnum;
8170 : ObjectAddress address;
8171 :
8172 : /*
8173 : * get the number of the attribute
8174 : */
8175 292 : attnum = get_attnum(RelationGetRelid(rel), colName);
8176 292 : if (attnum == InvalidAttrNumber)
8177 15 : ereport(ERROR,
8178 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8179 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8180 : colName, RelationGetRelationName(rel))));
8181 :
8182 : /* Prevent them from altering a system attribute */
8183 277 : if (attnum <= 0)
8184 0 : ereport(ERROR,
8185 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8186 : errmsg("cannot alter system column \"%s\"",
8187 : colName)));
8188 :
8189 277 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8190 9 : ereport(ERROR,
8191 : (errcode(ERRCODE_SYNTAX_ERROR),
8192 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8193 : colName, RelationGetRelationName(rel)),
8194 : /* translator: %s is an SQL ALTER command */
8195 : newDefault ? 0 : errhint("Use %s instead.",
8196 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8197 :
8198 268 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8199 6 : ereport(ERROR,
8200 : (errcode(ERRCODE_SYNTAX_ERROR),
8201 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8202 : colName, RelationGetRelationName(rel)),
8203 : newDefault ?
8204 : /* translator: %s is an SQL ALTER command */
8205 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8206 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8207 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8208 :
8209 : /*
8210 : * Remove any old default for the column. We use RESTRICT here for
8211 : * safety, but at present we do not expect anything to depend on the
8212 : * default.
8213 : *
8214 : * We treat removing the existing default as an internal operation when it
8215 : * is preparatory to adding a new default, but as a user-initiated
8216 : * operation when the user asked for a drop.
8217 : */
8218 262 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8219 : newDefault != NULL);
8220 :
8221 262 : if (newDefault)
8222 : {
8223 : /* SET DEFAULT */
8224 : RawColumnDefault *rawEnt;
8225 :
8226 175 : rawEnt = palloc_object(RawColumnDefault);
8227 175 : rawEnt->attnum = attnum;
8228 175 : rawEnt->raw_default = newDefault;
8229 175 : rawEnt->generated = '\0';
8230 :
8231 : /*
8232 : * This function is intended for CREATE TABLE, so it processes a
8233 : * _list_ of defaults, but we just do one.
8234 : */
8235 175 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8236 : false, true, false, NULL);
8237 : }
8238 :
8239 259 : ObjectAddressSubSet(address, RelationRelationId,
8240 : RelationGetRelid(rel), attnum);
8241 259 : return address;
8242 : }
8243 :
8244 : /*
8245 : * Add a pre-cooked default expression.
8246 : *
8247 : * Return the address of the affected column.
8248 : */
8249 : static ObjectAddress
8250 40 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8251 : Node *newDefault)
8252 : {
8253 : ObjectAddress address;
8254 :
8255 : /* We assume no checking is required */
8256 :
8257 : /*
8258 : * Remove any old default for the column. We use RESTRICT here for
8259 : * safety, but at present we do not expect anything to depend on the
8260 : * default. (In ordinary cases, there could not be a default in place
8261 : * anyway, but it's possible when combining LIKE with inheritance.)
8262 : */
8263 40 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8264 : true);
8265 :
8266 40 : (void) StoreAttrDefault(rel, attnum, newDefault, true);
8267 :
8268 40 : ObjectAddressSubSet(address, RelationRelationId,
8269 : RelationGetRelid(rel), attnum);
8270 40 : return address;
8271 : }
8272 :
8273 : /*
8274 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8275 : *
8276 : * Return the address of the affected column.
8277 : */
8278 : static ObjectAddress
8279 83 : ATExecAddIdentity(Relation rel, const char *colName,
8280 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8281 : {
8282 : Relation attrelation;
8283 : HeapTuple tuple;
8284 : Form_pg_attribute attTup;
8285 : AttrNumber attnum;
8286 : ObjectAddress address;
8287 83 : ColumnDef *cdef = castNode(ColumnDef, def);
8288 : bool ispartitioned;
8289 :
8290 83 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8291 83 : if (ispartitioned && !recurse)
8292 3 : ereport(ERROR,
8293 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8294 : errmsg("cannot add identity to a column of only the partitioned table"),
8295 : errhint("Do not specify the ONLY keyword.")));
8296 :
8297 80 : if (rel->rd_rel->relispartition && !recursing)
8298 6 : ereport(ERROR,
8299 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8300 : errmsg("cannot add identity to a column of a partition"));
8301 :
8302 74 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8303 :
8304 74 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8305 74 : if (!HeapTupleIsValid(tuple))
8306 0 : ereport(ERROR,
8307 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8308 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8309 : colName, RelationGetRelationName(rel))));
8310 74 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8311 74 : attnum = attTup->attnum;
8312 :
8313 : /* Can't alter a system attribute */
8314 74 : if (attnum <= 0)
8315 0 : ereport(ERROR,
8316 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8317 : errmsg("cannot alter system column \"%s\"",
8318 : colName)));
8319 :
8320 : /*
8321 : * Creating a column as identity implies NOT NULL, so adding the identity
8322 : * to an existing column that is not NOT NULL would create a state that
8323 : * cannot be reproduced without contortions.
8324 : */
8325 74 : if (!attTup->attnotnull)
8326 3 : ereport(ERROR,
8327 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8328 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8329 : colName, RelationGetRelationName(rel))));
8330 :
8331 : /*
8332 : * On the other hand, if a not-null constraint exists, then verify that
8333 : * it's compatible.
8334 : */
8335 71 : if (attTup->attnotnull)
8336 : {
8337 : HeapTuple contup;
8338 : Form_pg_constraint conForm;
8339 :
8340 71 : contup = findNotNullConstraintAttnum(RelationGetRelid(rel),
8341 : attnum);
8342 71 : if (!HeapTupleIsValid(contup))
8343 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8344 : colName, RelationGetRelationName(rel));
8345 :
8346 71 : conForm = (Form_pg_constraint) GETSTRUCT(contup);
8347 71 : if (!conForm->convalidated)
8348 3 : ereport(ERROR,
8349 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8350 : errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8351 : NameStr(conForm->conname), RelationGetRelationName(rel)),
8352 : errhint("You might need to validate it using %s.",
8353 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
8354 : }
8355 :
8356 68 : if (attTup->attidentity)
8357 9 : ereport(ERROR,
8358 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8359 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8360 : colName, RelationGetRelationName(rel))));
8361 :
8362 59 : if (attTup->atthasdef)
8363 3 : ereport(ERROR,
8364 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8365 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8366 : colName, RelationGetRelationName(rel))));
8367 :
8368 56 : attTup->attidentity = cdef->identity;
8369 56 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8370 :
8371 56 : InvokeObjectPostAlterHook(RelationRelationId,
8372 : RelationGetRelid(rel),
8373 : attTup->attnum);
8374 56 : ObjectAddressSubSet(address, RelationRelationId,
8375 : RelationGetRelid(rel), attnum);
8376 56 : heap_freetuple(tuple);
8377 :
8378 56 : table_close(attrelation, RowExclusiveLock);
8379 :
8380 : /*
8381 : * Recurse to propagate the identity column to partitions. Identity is
8382 : * not inherited in regular inheritance children.
8383 : */
8384 56 : if (recurse && ispartitioned)
8385 : {
8386 : List *children;
8387 : ListCell *lc;
8388 :
8389 5 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8390 :
8391 8 : foreach(lc, children)
8392 : {
8393 : Relation childrel;
8394 :
8395 3 : childrel = table_open(lfirst_oid(lc), NoLock);
8396 3 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8397 3 : table_close(childrel, NoLock);
8398 : }
8399 : }
8400 :
8401 56 : return address;
8402 : }
8403 :
8404 : /*
8405 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8406 : *
8407 : * Return the address of the affected column.
8408 : */
8409 : static ObjectAddress
8410 37 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8411 : LOCKMODE lockmode, bool recurse, bool recursing)
8412 : {
8413 : ListCell *option;
8414 37 : DefElem *generatedEl = NULL;
8415 : HeapTuple tuple;
8416 : Form_pg_attribute attTup;
8417 : AttrNumber attnum;
8418 : Relation attrelation;
8419 : ObjectAddress address;
8420 : bool ispartitioned;
8421 :
8422 37 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8423 37 : if (ispartitioned && !recurse)
8424 3 : ereport(ERROR,
8425 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8426 : errmsg("cannot change identity column of only the partitioned table"),
8427 : errhint("Do not specify the ONLY keyword.")));
8428 :
8429 34 : if (rel->rd_rel->relispartition && !recursing)
8430 6 : ereport(ERROR,
8431 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8432 : errmsg("cannot change identity column of a partition"));
8433 :
8434 50 : foreach(option, castNode(List, def))
8435 : {
8436 22 : DefElem *defel = lfirst_node(DefElem, option);
8437 :
8438 22 : if (strcmp(defel->defname, "generated") == 0)
8439 : {
8440 22 : if (generatedEl)
8441 0 : ereport(ERROR,
8442 : (errcode(ERRCODE_SYNTAX_ERROR),
8443 : errmsg("conflicting or redundant options")));
8444 22 : generatedEl = defel;
8445 : }
8446 : else
8447 0 : elog(ERROR, "option \"%s\" not recognized",
8448 : defel->defname);
8449 : }
8450 :
8451 : /*
8452 : * Even if there is nothing to change here, we run all the checks. There
8453 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8454 : * there.
8455 : */
8456 :
8457 28 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8458 28 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8459 28 : if (!HeapTupleIsValid(tuple))
8460 0 : ereport(ERROR,
8461 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8462 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8463 : colName, RelationGetRelationName(rel))));
8464 :
8465 28 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8466 28 : attnum = attTup->attnum;
8467 :
8468 28 : if (attnum <= 0)
8469 0 : ereport(ERROR,
8470 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8471 : errmsg("cannot alter system column \"%s\"",
8472 : colName)));
8473 :
8474 28 : if (!attTup->attidentity)
8475 3 : ereport(ERROR,
8476 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8477 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8478 : colName, RelationGetRelationName(rel))));
8479 :
8480 25 : if (generatedEl)
8481 : {
8482 22 : attTup->attidentity = defGetInt32(generatedEl);
8483 22 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8484 :
8485 22 : InvokeObjectPostAlterHook(RelationRelationId,
8486 : RelationGetRelid(rel),
8487 : attTup->attnum);
8488 22 : ObjectAddressSubSet(address, RelationRelationId,
8489 : RelationGetRelid(rel), attnum);
8490 : }
8491 : else
8492 3 : address = InvalidObjectAddress;
8493 :
8494 25 : heap_freetuple(tuple);
8495 25 : table_close(attrelation, RowExclusiveLock);
8496 :
8497 : /*
8498 : * Recurse to propagate the identity change to partitions. Identity is not
8499 : * inherited in regular inheritance children.
8500 : */
8501 25 : if (generatedEl && recurse && ispartitioned)
8502 : {
8503 : List *children;
8504 : ListCell *lc;
8505 :
8506 3 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8507 :
8508 9 : foreach(lc, children)
8509 : {
8510 : Relation childrel;
8511 :
8512 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8513 6 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8514 6 : table_close(childrel, NoLock);
8515 : }
8516 : }
8517 :
8518 25 : return address;
8519 : }
8520 :
8521 : /*
8522 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8523 : *
8524 : * Return the address of the affected column.
8525 : */
8526 : static ObjectAddress
8527 46 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8528 : bool recurse, bool recursing)
8529 : {
8530 : HeapTuple tuple;
8531 : Form_pg_attribute attTup;
8532 : AttrNumber attnum;
8533 : Relation attrelation;
8534 : ObjectAddress address;
8535 : Oid seqid;
8536 : ObjectAddress seqaddress;
8537 : bool ispartitioned;
8538 :
8539 46 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8540 46 : if (ispartitioned && !recurse)
8541 3 : ereport(ERROR,
8542 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8543 : errmsg("cannot drop identity from a column of only the partitioned table"),
8544 : errhint("Do not specify the ONLY keyword.")));
8545 :
8546 43 : if (rel->rd_rel->relispartition && !recursing)
8547 3 : ereport(ERROR,
8548 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8549 : errmsg("cannot drop identity from a column of a partition"));
8550 :
8551 40 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8552 40 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8553 40 : if (!HeapTupleIsValid(tuple))
8554 0 : ereport(ERROR,
8555 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8556 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8557 : colName, RelationGetRelationName(rel))));
8558 :
8559 40 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8560 40 : attnum = attTup->attnum;
8561 :
8562 40 : if (attnum <= 0)
8563 0 : ereport(ERROR,
8564 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8565 : errmsg("cannot alter system column \"%s\"",
8566 : colName)));
8567 :
8568 40 : if (!attTup->attidentity)
8569 : {
8570 6 : if (!missing_ok)
8571 3 : ereport(ERROR,
8572 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8573 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8574 : colName, RelationGetRelationName(rel))));
8575 : else
8576 : {
8577 3 : ereport(NOTICE,
8578 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8579 : colName, RelationGetRelationName(rel))));
8580 3 : heap_freetuple(tuple);
8581 3 : table_close(attrelation, RowExclusiveLock);
8582 3 : return InvalidObjectAddress;
8583 : }
8584 : }
8585 :
8586 34 : attTup->attidentity = '\0';
8587 34 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8588 :
8589 34 : InvokeObjectPostAlterHook(RelationRelationId,
8590 : RelationGetRelid(rel),
8591 : attTup->attnum);
8592 34 : ObjectAddressSubSet(address, RelationRelationId,
8593 : RelationGetRelid(rel), attnum);
8594 34 : heap_freetuple(tuple);
8595 :
8596 34 : table_close(attrelation, RowExclusiveLock);
8597 :
8598 : /*
8599 : * Recurse to drop the identity from column in partitions. Identity is
8600 : * not inherited in regular inheritance children so ignore them.
8601 : */
8602 34 : if (recurse && ispartitioned)
8603 : {
8604 : List *children;
8605 : ListCell *lc;
8606 :
8607 3 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8608 :
8609 6 : foreach(lc, children)
8610 : {
8611 : Relation childrel;
8612 :
8613 3 : childrel = table_open(lfirst_oid(lc), NoLock);
8614 3 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8615 3 : table_close(childrel, NoLock);
8616 : }
8617 : }
8618 :
8619 34 : if (!recursing)
8620 : {
8621 : /* drop the internal sequence */
8622 16 : seqid = getIdentitySequence(rel, attnum, false);
8623 16 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8624 : RelationRelationId, DEPENDENCY_INTERNAL);
8625 16 : CommandCounterIncrement();
8626 16 : seqaddress.classId = RelationRelationId;
8627 16 : seqaddress.objectId = seqid;
8628 16 : seqaddress.objectSubId = 0;
8629 16 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8630 : }
8631 :
8632 34 : return address;
8633 : }
8634 :
8635 : /*
8636 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8637 : *
8638 : * Return the address of the affected column.
8639 : */
8640 : static ObjectAddress
8641 115 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8642 : Node *newExpr, LOCKMODE lockmode)
8643 : {
8644 : HeapTuple tuple;
8645 : Form_pg_attribute attTup;
8646 : AttrNumber attnum;
8647 : char attgenerated;
8648 : bool rewrite;
8649 : Oid attrdefoid;
8650 : ObjectAddress address;
8651 : Expr *defval;
8652 : NewColumnValue *newval;
8653 : RawColumnDefault *rawEnt;
8654 :
8655 115 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8656 115 : if (!HeapTupleIsValid(tuple))
8657 0 : ereport(ERROR,
8658 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8659 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8660 : colName, RelationGetRelationName(rel))));
8661 :
8662 115 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8663 :
8664 115 : attnum = attTup->attnum;
8665 115 : if (attnum <= 0)
8666 0 : ereport(ERROR,
8667 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8668 : errmsg("cannot alter system column \"%s\"",
8669 : colName)));
8670 :
8671 115 : attgenerated = attTup->attgenerated;
8672 115 : if (!attgenerated)
8673 6 : ereport(ERROR,
8674 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8675 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8676 : colName, RelationGetRelationName(rel))));
8677 :
8678 109 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8679 12 : tab->verify_new_notnull = true;
8680 :
8681 : /*
8682 : * We need to prevent this because a change of expression could affect a
8683 : * row filter and inject expressions that are not permitted in a row
8684 : * filter. XXX We could try to have a more precise check to catch only
8685 : * publications with row filters, or even re-verify the row filter
8686 : * expressions.
8687 : */
8688 166 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8689 57 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
8690 3 : ereport(ERROR,
8691 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8692 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8693 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8694 : colName, RelationGetRelationName(rel))));
8695 :
8696 106 : rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8697 :
8698 106 : ReleaseSysCache(tuple);
8699 :
8700 106 : if (rewrite)
8701 : {
8702 : /*
8703 : * Clear all the missing values if we're rewriting the table, since
8704 : * this renders them pointless.
8705 : */
8706 52 : RelationClearMissing(rel);
8707 :
8708 : /* make sure we don't conflict with later attribute modifications */
8709 52 : CommandCounterIncrement();
8710 : }
8711 :
8712 : /*
8713 : * Find everything that depends on the column (constraints, indexes, etc),
8714 : * and record enough information to let us recreate the objects.
8715 : */
8716 106 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8717 :
8718 : /*
8719 : * Drop the dependency records of the GENERATED expression, in particular
8720 : * its INTERNAL dependency on the column, which would otherwise cause
8721 : * dependency.c to refuse to perform the deletion.
8722 : */
8723 106 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8724 106 : if (!OidIsValid(attrdefoid))
8725 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8726 : RelationGetRelid(rel), attnum);
8727 106 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8728 :
8729 : /* Make above changes visible */
8730 106 : CommandCounterIncrement();
8731 :
8732 : /*
8733 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8734 : * safety, but at present we do not expect anything to depend on the
8735 : * expression.
8736 : */
8737 106 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8738 : false, false);
8739 :
8740 : /* Prepare to store the new expression, in the catalogs */
8741 106 : rawEnt = palloc_object(RawColumnDefault);
8742 106 : rawEnt->attnum = attnum;
8743 106 : rawEnt->raw_default = newExpr;
8744 106 : rawEnt->generated = attgenerated;
8745 :
8746 : /* Store the generated expression */
8747 106 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8748 : false, true, false, NULL);
8749 :
8750 : /* Make above new expression visible */
8751 106 : CommandCounterIncrement();
8752 :
8753 106 : if (rewrite)
8754 : {
8755 : /* Prepare for table rewrite */
8756 52 : defval = (Expr *) build_column_default(rel, attnum);
8757 :
8758 52 : newval = palloc0_object(NewColumnValue);
8759 52 : newval->attnum = attnum;
8760 52 : newval->expr = expression_planner(defval);
8761 52 : newval->is_generated = true;
8762 :
8763 52 : tab->newvals = lappend(tab->newvals, newval);
8764 52 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8765 : }
8766 :
8767 : /* Drop any pg_statistic entry for the column */
8768 106 : RemoveStatistics(RelationGetRelid(rel), attnum);
8769 :
8770 106 : InvokeObjectPostAlterHook(RelationRelationId,
8771 : RelationGetRelid(rel), attnum);
8772 :
8773 106 : ObjectAddressSubSet(address, RelationRelationId,
8774 : RelationGetRelid(rel), attnum);
8775 106 : return address;
8776 : }
8777 :
8778 : /*
8779 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8780 : */
8781 : static void
8782 43 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8783 : {
8784 : /*
8785 : * Reject ONLY if there are child tables. We could implement this, but it
8786 : * is a bit complicated. GENERATED clauses must be attached to the column
8787 : * definition and cannot be added later like DEFAULT, so if a child table
8788 : * has a generation expression that the parent does not have, the child
8789 : * column will necessarily be an attislocal column. So to implement ONLY
8790 : * here, we'd need extra code to update attislocal of the direct child
8791 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8792 : * resulting state can be properly dumped and restored.
8793 : */
8794 55 : if (!recurse &&
8795 12 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8796 6 : ereport(ERROR,
8797 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8798 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8799 :
8800 : /*
8801 : * Cannot drop generation expression from inherited columns.
8802 : */
8803 37 : if (!recursing)
8804 : {
8805 : HeapTuple tuple;
8806 : Form_pg_attribute attTup;
8807 :
8808 31 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8809 31 : if (!HeapTupleIsValid(tuple))
8810 0 : ereport(ERROR,
8811 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8812 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8813 : cmd->name, RelationGetRelationName(rel))));
8814 :
8815 31 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8816 :
8817 31 : if (attTup->attinhcount > 0)
8818 6 : ereport(ERROR,
8819 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8820 : errmsg("cannot drop generation expression from inherited column")));
8821 : }
8822 31 : }
8823 :
8824 : /*
8825 : * Return the address of the affected column.
8826 : */
8827 : static ObjectAddress
8828 28 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8829 : {
8830 : HeapTuple tuple;
8831 : Form_pg_attribute attTup;
8832 : AttrNumber attnum;
8833 : Relation attrelation;
8834 : Oid attrdefoid;
8835 : ObjectAddress address;
8836 :
8837 28 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8838 28 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8839 28 : if (!HeapTupleIsValid(tuple))
8840 0 : ereport(ERROR,
8841 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8842 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8843 : colName, RelationGetRelationName(rel))));
8844 :
8845 28 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8846 28 : attnum = attTup->attnum;
8847 :
8848 28 : if (attnum <= 0)
8849 0 : ereport(ERROR,
8850 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8851 : errmsg("cannot alter system column \"%s\"",
8852 : colName)));
8853 :
8854 : /*
8855 : * TODO: This could be done, but it would need a table rewrite to
8856 : * materialize the generated values. Note that for the time being, we
8857 : * still error with missing_ok, so that we don't silently leave the column
8858 : * as generated.
8859 : */
8860 28 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8861 6 : ereport(ERROR,
8862 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8863 : errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8864 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8865 : colName, RelationGetRelationName(rel))));
8866 :
8867 22 : if (!attTup->attgenerated)
8868 : {
8869 12 : if (!missing_ok)
8870 6 : ereport(ERROR,
8871 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8872 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8873 : colName, RelationGetRelationName(rel))));
8874 : else
8875 : {
8876 6 : ereport(NOTICE,
8877 : (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8878 : colName, RelationGetRelationName(rel))));
8879 6 : heap_freetuple(tuple);
8880 6 : table_close(attrelation, RowExclusiveLock);
8881 6 : return InvalidObjectAddress;
8882 : }
8883 : }
8884 :
8885 : /*
8886 : * Mark the column as no longer generated. (The atthasdef flag needs to
8887 : * get cleared too, but RemoveAttrDefault will handle that.)
8888 : */
8889 10 : attTup->attgenerated = '\0';
8890 10 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8891 :
8892 10 : InvokeObjectPostAlterHook(RelationRelationId,
8893 : RelationGetRelid(rel),
8894 : attnum);
8895 10 : heap_freetuple(tuple);
8896 :
8897 10 : table_close(attrelation, RowExclusiveLock);
8898 :
8899 : /*
8900 : * Drop the dependency records of the GENERATED expression, in particular
8901 : * its INTERNAL dependency on the column, which would otherwise cause
8902 : * dependency.c to refuse to perform the deletion.
8903 : */
8904 10 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8905 10 : if (!OidIsValid(attrdefoid))
8906 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8907 : RelationGetRelid(rel), attnum);
8908 10 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8909 :
8910 : /* Make above changes visible */
8911 10 : CommandCounterIncrement();
8912 :
8913 : /*
8914 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8915 : * safety, but at present we do not expect anything to depend on the
8916 : * default.
8917 : */
8918 10 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8919 : false, false);
8920 :
8921 10 : ObjectAddressSubSet(address, RelationRelationId,
8922 : RelationGetRelid(rel), attnum);
8923 10 : return address;
8924 : }
8925 :
8926 : /*
8927 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8928 : *
8929 : * Return value is the address of the modified column
8930 : */
8931 : static ObjectAddress
8932 82 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8933 : {
8934 82 : int newtarget = 0;
8935 : bool newtarget_default;
8936 : Relation attrelation;
8937 : HeapTuple tuple,
8938 : newtuple;
8939 : Form_pg_attribute attrtuple;
8940 : AttrNumber attnum;
8941 : ObjectAddress address;
8942 : Datum repl_val[Natts_pg_attribute];
8943 : bool repl_null[Natts_pg_attribute];
8944 : bool repl_repl[Natts_pg_attribute];
8945 :
8946 : /*
8947 : * We allow referencing columns by numbers only for indexes, since table
8948 : * column numbers could contain gaps if columns are later dropped.
8949 : */
8950 82 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8951 50 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8952 : !colName)
8953 0 : ereport(ERROR,
8954 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8955 : errmsg("cannot refer to non-index column by number")));
8956 :
8957 : /* -1 was used in previous versions for the default setting */
8958 82 : if (newValue && intVal(newValue) != -1)
8959 : {
8960 60 : newtarget = intVal(newValue);
8961 60 : newtarget_default = false;
8962 : }
8963 : else
8964 22 : newtarget_default = true;
8965 :
8966 82 : if (!newtarget_default)
8967 : {
8968 : /*
8969 : * Limit target to a sane range
8970 : */
8971 60 : if (newtarget < 0)
8972 : {
8973 0 : ereport(ERROR,
8974 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8975 : errmsg("statistics target %d is too low",
8976 : newtarget)));
8977 : }
8978 60 : else if (newtarget > MAX_STATISTICS_TARGET)
8979 : {
8980 0 : newtarget = MAX_STATISTICS_TARGET;
8981 0 : ereport(WARNING,
8982 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8983 : errmsg("lowering statistics target to %d",
8984 : newtarget)));
8985 : }
8986 : }
8987 :
8988 82 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8989 :
8990 82 : if (colName)
8991 : {
8992 50 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8993 :
8994 50 : if (!HeapTupleIsValid(tuple))
8995 6 : ereport(ERROR,
8996 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8997 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8998 : colName, RelationGetRelationName(rel))));
8999 : }
9000 : else
9001 : {
9002 32 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
9003 :
9004 32 : if (!HeapTupleIsValid(tuple))
9005 6 : ereport(ERROR,
9006 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9007 : errmsg("column number %d of relation \"%s\" does not exist",
9008 : colNum, RelationGetRelationName(rel))));
9009 : }
9010 :
9011 70 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9012 :
9013 70 : attnum = attrtuple->attnum;
9014 70 : if (attnum <= 0)
9015 0 : ereport(ERROR,
9016 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9017 : errmsg("cannot alter system column \"%s\"",
9018 : colName)));
9019 :
9020 : /*
9021 : * Prevent this as long as the ANALYZE code skips virtual generated
9022 : * columns.
9023 : */
9024 70 : if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9025 0 : ereport(ERROR,
9026 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9027 : errmsg("cannot alter statistics on virtual generated column \"%s\"",
9028 : colName)));
9029 :
9030 70 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
9031 44 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9032 : {
9033 26 : if (attnum > rel->rd_index->indnkeyatts)
9034 3 : ereport(ERROR,
9035 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9036 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9037 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9038 23 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9039 9 : ereport(ERROR,
9040 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9041 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9042 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9043 : errhint("Alter statistics on table column instead.")));
9044 : }
9045 :
9046 : /* Build new tuple. */
9047 58 : memset(repl_null, false, sizeof(repl_null));
9048 58 : memset(repl_repl, false, sizeof(repl_repl));
9049 58 : if (!newtarget_default)
9050 36 : repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
9051 : else
9052 22 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
9053 58 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
9054 58 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9055 : repl_val, repl_null, repl_repl);
9056 58 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9057 :
9058 58 : InvokeObjectPostAlterHook(RelationRelationId,
9059 : RelationGetRelid(rel),
9060 : attrtuple->attnum);
9061 58 : ObjectAddressSubSet(address, RelationRelationId,
9062 : RelationGetRelid(rel), attnum);
9063 :
9064 58 : heap_freetuple(newtuple);
9065 :
9066 58 : ReleaseSysCache(tuple);
9067 :
9068 58 : table_close(attrelation, RowExclusiveLock);
9069 :
9070 58 : return address;
9071 : }
9072 :
9073 : /*
9074 : * Return value is the address of the modified column
9075 : */
9076 : static ObjectAddress
9077 16 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
9078 : bool isReset, LOCKMODE lockmode)
9079 : {
9080 : Relation attrelation;
9081 : HeapTuple tuple,
9082 : newtuple;
9083 : Form_pg_attribute attrtuple;
9084 : AttrNumber attnum;
9085 : Datum datum,
9086 : newOptions;
9087 : bool isnull;
9088 : ObjectAddress address;
9089 : Datum repl_val[Natts_pg_attribute];
9090 : bool repl_null[Natts_pg_attribute];
9091 : bool repl_repl[Natts_pg_attribute];
9092 :
9093 16 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9094 :
9095 16 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9096 :
9097 16 : if (!HeapTupleIsValid(tuple))
9098 0 : ereport(ERROR,
9099 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9100 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9101 : colName, RelationGetRelationName(rel))));
9102 16 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9103 :
9104 16 : attnum = attrtuple->attnum;
9105 16 : if (attnum <= 0)
9106 0 : ereport(ERROR,
9107 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9108 : errmsg("cannot alter system column \"%s\"",
9109 : colName)));
9110 :
9111 : /* Generate new proposed attoptions (text array) */
9112 16 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9113 : &isnull);
9114 16 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9115 : castNode(List, options), NULL, NULL,
9116 : false, isReset);
9117 : /* Validate new options */
9118 16 : (void) attribute_reloptions(newOptions, true);
9119 :
9120 : /* Build new tuple. */
9121 16 : memset(repl_null, false, sizeof(repl_null));
9122 16 : memset(repl_repl, false, sizeof(repl_repl));
9123 16 : if (newOptions != (Datum) 0)
9124 16 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9125 : else
9126 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
9127 16 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9128 16 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9129 : repl_val, repl_null, repl_repl);
9130 :
9131 : /* Update system catalog. */
9132 16 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9133 :
9134 16 : InvokeObjectPostAlterHook(RelationRelationId,
9135 : RelationGetRelid(rel),
9136 : attrtuple->attnum);
9137 16 : ObjectAddressSubSet(address, RelationRelationId,
9138 : RelationGetRelid(rel), attnum);
9139 :
9140 16 : heap_freetuple(newtuple);
9141 :
9142 16 : ReleaseSysCache(tuple);
9143 :
9144 16 : table_close(attrelation, RowExclusiveLock);
9145 :
9146 16 : return address;
9147 : }
9148 :
9149 : /*
9150 : * Helper function for ATExecSetStorage and ATExecSetCompression
9151 : *
9152 : * Set the attstorage and/or attcompression fields for index columns
9153 : * associated with the specified table column.
9154 : */
9155 : static void
9156 172 : SetIndexStorageProperties(Relation rel, Relation attrelation,
9157 : AttrNumber attnum,
9158 : bool setstorage, char newstorage,
9159 : bool setcompression, char newcompression,
9160 : LOCKMODE lockmode)
9161 : {
9162 : ListCell *lc;
9163 :
9164 218 : foreach(lc, RelationGetIndexList(rel))
9165 : {
9166 46 : Oid indexoid = lfirst_oid(lc);
9167 : Relation indrel;
9168 46 : AttrNumber indattnum = 0;
9169 : HeapTuple tuple;
9170 :
9171 46 : indrel = index_open(indexoid, lockmode);
9172 :
9173 77 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9174 : {
9175 49 : if (indrel->rd_index->indkey.values[i] == attnum)
9176 : {
9177 18 : indattnum = i + 1;
9178 18 : break;
9179 : }
9180 : }
9181 :
9182 46 : if (indattnum == 0)
9183 : {
9184 28 : index_close(indrel, lockmode);
9185 28 : continue;
9186 : }
9187 :
9188 18 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9189 :
9190 18 : if (HeapTupleIsValid(tuple))
9191 : {
9192 18 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9193 :
9194 18 : if (setstorage)
9195 12 : attrtuple->attstorage = newstorage;
9196 :
9197 18 : if (setcompression)
9198 6 : attrtuple->attcompression = newcompression;
9199 :
9200 18 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9201 :
9202 18 : InvokeObjectPostAlterHook(RelationRelationId,
9203 : RelationGetRelid(rel),
9204 : attrtuple->attnum);
9205 :
9206 18 : heap_freetuple(tuple);
9207 : }
9208 :
9209 18 : index_close(indrel, lockmode);
9210 : }
9211 172 : }
9212 :
9213 : /*
9214 : * ALTER TABLE ALTER COLUMN SET STORAGE
9215 : *
9216 : * Return value is the address of the modified column
9217 : */
9218 : static ObjectAddress
9219 142 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9220 : {
9221 : Relation attrelation;
9222 : HeapTuple tuple;
9223 : Form_pg_attribute attrtuple;
9224 : AttrNumber attnum;
9225 : ObjectAddress address;
9226 :
9227 142 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9228 :
9229 142 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9230 :
9231 142 : if (!HeapTupleIsValid(tuple))
9232 6 : ereport(ERROR,
9233 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9234 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9235 : colName, RelationGetRelationName(rel))));
9236 136 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9237 :
9238 136 : attnum = attrtuple->attnum;
9239 136 : if (attnum <= 0)
9240 0 : ereport(ERROR,
9241 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9242 : errmsg("cannot alter system column \"%s\"",
9243 : colName)));
9244 :
9245 136 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9246 :
9247 136 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9248 :
9249 136 : InvokeObjectPostAlterHook(RelationRelationId,
9250 : RelationGetRelid(rel),
9251 : attrtuple->attnum);
9252 :
9253 : /*
9254 : * Apply the change to indexes as well (only for simple index columns,
9255 : * matching behavior of index.c ConstructTupleDescriptor()).
9256 : */
9257 136 : SetIndexStorageProperties(rel, attrelation, attnum,
9258 136 : true, attrtuple->attstorage,
9259 : false, 0,
9260 : lockmode);
9261 :
9262 136 : heap_freetuple(tuple);
9263 :
9264 136 : table_close(attrelation, RowExclusiveLock);
9265 :
9266 136 : ObjectAddressSubSet(address, RelationRelationId,
9267 : RelationGetRelid(rel), attnum);
9268 136 : return address;
9269 : }
9270 :
9271 :
9272 : /*
9273 : * ALTER TABLE DROP COLUMN
9274 : *
9275 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9276 : * because we have to decide at runtime whether to recurse or not depending
9277 : * on whether attinhcount goes to zero or not. (We can't check this in a
9278 : * static pre-pass because it won't handle multiple inheritance situations
9279 : * correctly.)
9280 : */
9281 : static void
9282 847 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9283 : AlterTableCmd *cmd, LOCKMODE lockmode,
9284 : AlterTableUtilityContext *context)
9285 : {
9286 847 : if (rel->rd_rel->reloftype && !recursing)
9287 3 : ereport(ERROR,
9288 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9289 : errmsg("cannot drop column from typed table")));
9290 :
9291 844 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9292 42 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9293 :
9294 841 : if (recurse)
9295 701 : cmd->recurse = true;
9296 841 : }
9297 :
9298 : /*
9299 : * Drops column 'colName' from relation 'rel' and returns the address of the
9300 : * dropped column. The column is also dropped (or marked as no longer
9301 : * inherited from relation) from the relation's inheritance children, if any.
9302 : *
9303 : * In the recursive invocations for inheritance child relations, instead of
9304 : * dropping the column directly (if to be dropped at all), its object address
9305 : * is added to 'addrs', which must be non-NULL in such invocations. All
9306 : * columns are dropped at the same time after all the children have been
9307 : * checked recursively.
9308 : */
9309 : static ObjectAddress
9310 1122 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9311 : DropBehavior behavior,
9312 : bool recurse, bool recursing,
9313 : bool missing_ok, LOCKMODE lockmode,
9314 : ObjectAddresses *addrs)
9315 : {
9316 : HeapTuple tuple;
9317 : Form_pg_attribute targetatt;
9318 : AttrNumber attnum;
9319 : List *children;
9320 : ObjectAddress object;
9321 : bool is_expr;
9322 :
9323 : /* At top level, permission check was done in ATPrepCmd, else do it */
9324 1122 : if (recursing)
9325 281 : ATSimplePermissions(AT_DropColumn, rel,
9326 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9327 :
9328 : /* Initialize addrs on the first invocation */
9329 : Assert(!recursing || addrs != NULL);
9330 :
9331 : /* since this function recurses, it could be driven to stack overflow */
9332 1122 : check_stack_depth();
9333 :
9334 1122 : if (!recursing)
9335 841 : addrs = new_object_addresses();
9336 :
9337 : /*
9338 : * get the number of the attribute
9339 : */
9340 1122 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9341 1122 : if (!HeapTupleIsValid(tuple))
9342 : {
9343 27 : if (!missing_ok)
9344 : {
9345 18 : ereport(ERROR,
9346 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9347 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9348 : colName, RelationGetRelationName(rel))));
9349 : }
9350 : else
9351 : {
9352 9 : ereport(NOTICE,
9353 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9354 : colName, RelationGetRelationName(rel))));
9355 9 : return InvalidObjectAddress;
9356 : }
9357 : }
9358 1095 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9359 :
9360 1095 : attnum = targetatt->attnum;
9361 :
9362 : /* Can't drop a system attribute */
9363 1095 : if (attnum <= 0)
9364 3 : ereport(ERROR,
9365 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9366 : errmsg("cannot drop system column \"%s\"",
9367 : colName)));
9368 :
9369 : /*
9370 : * Don't drop inherited columns, unless recursing (presumably from a drop
9371 : * of the parent column)
9372 : */
9373 1092 : if (targetatt->attinhcount > 0 && !recursing)
9374 24 : ereport(ERROR,
9375 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9376 : errmsg("cannot drop inherited column \"%s\"",
9377 : colName)));
9378 :
9379 : /*
9380 : * Don't drop columns used in the partition key, either. (If we let this
9381 : * go through, the key column's dependencies would cause a cascaded drop
9382 : * of the whole table, which is surely not what the user expected.)
9383 : */
9384 1068 : if (has_partition_attrs(rel,
9385 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9386 : &is_expr))
9387 15 : ereport(ERROR,
9388 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9389 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9390 : colName, RelationGetRelationName(rel))));
9391 :
9392 1053 : ReleaseSysCache(tuple);
9393 :
9394 : /*
9395 : * Propagate to children as appropriate. Unlike most other ALTER
9396 : * routines, we have to do this one level of recursion at a time; we can't
9397 : * use find_all_inheritors to do it in one pass.
9398 : */
9399 : children =
9400 1053 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9401 :
9402 1053 : if (children)
9403 : {
9404 : Relation attr_rel;
9405 : ListCell *child;
9406 :
9407 : /*
9408 : * In case of a partitioned table, the column must be dropped from the
9409 : * partitions as well.
9410 : */
9411 154 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9412 3 : ereport(ERROR,
9413 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9414 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9415 : errhint("Do not specify the ONLY keyword.")));
9416 :
9417 151 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9418 447 : foreach(child, children)
9419 : {
9420 299 : Oid childrelid = lfirst_oid(child);
9421 : Relation childrel;
9422 : Form_pg_attribute childatt;
9423 :
9424 : /* find_inheritance_children already got lock */
9425 299 : childrel = table_open(childrelid, NoLock);
9426 299 : CheckAlterTableIsSafe(childrel);
9427 :
9428 299 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9429 299 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9430 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9431 : colName, childrelid);
9432 299 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9433 :
9434 299 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9435 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9436 : childrelid, colName);
9437 :
9438 299 : if (recurse)
9439 : {
9440 : /*
9441 : * If the child column has other definition sources, just
9442 : * decrement its inheritance count; if not, recurse to delete
9443 : * it.
9444 : */
9445 287 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9446 : {
9447 : /* Time to delete this child column, too */
9448 281 : ATExecDropColumn(wqueue, childrel, colName,
9449 : behavior, true, true,
9450 : false, lockmode, addrs);
9451 : }
9452 : else
9453 : {
9454 : /* Child column must survive my deletion */
9455 6 : childatt->attinhcount--;
9456 :
9457 6 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9458 :
9459 : /* Make update visible */
9460 6 : CommandCounterIncrement();
9461 : }
9462 : }
9463 : else
9464 : {
9465 : /*
9466 : * If we were told to drop ONLY in this table (no recursion),
9467 : * we need to mark the inheritors' attributes as locally
9468 : * defined rather than inherited.
9469 : */
9470 12 : childatt->attinhcount--;
9471 12 : childatt->attislocal = true;
9472 :
9473 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9474 :
9475 : /* Make update visible */
9476 12 : CommandCounterIncrement();
9477 : }
9478 :
9479 296 : heap_freetuple(tuple);
9480 :
9481 296 : table_close(childrel, NoLock);
9482 : }
9483 148 : table_close(attr_rel, RowExclusiveLock);
9484 : }
9485 :
9486 : /* Add object to delete */
9487 1047 : object.classId = RelationRelationId;
9488 1047 : object.objectId = RelationGetRelid(rel);
9489 1047 : object.objectSubId = attnum;
9490 1047 : add_exact_object_address(&object, addrs);
9491 :
9492 1047 : if (!recursing)
9493 : {
9494 : /* Recursion has ended, drop everything that was collected */
9495 769 : performMultipleDeletions(addrs, behavior, 0);
9496 742 : free_object_addresses(addrs);
9497 : }
9498 :
9499 1020 : return object;
9500 : }
9501 :
9502 : /*
9503 : * Prepare to add a primary key on a table, by adding not-null constraints
9504 : * on all columns.
9505 : *
9506 : * The not-null constraints for a primary key must cover the whole inheritance
9507 : * hierarchy (failing to ensure that leads to funny corner cases). For the
9508 : * normal case where we're asked to recurse, this routine checks if the
9509 : * not-null constraints exist already, and if not queues a requirement for
9510 : * them to be created by phase 2.
9511 : *
9512 : * For the case where we're asked not to recurse, we verify that a not-null
9513 : * constraint exists on each column of each (direct) child table, throwing an
9514 : * error if not. Not throwing an error would also work, because a not-null
9515 : * constraint would be created anyway, but it'd cause a silent scan of the
9516 : * child table to verify absence of nulls. We prefer to let the user know so
9517 : * that they can add the constraint manually without having to hold
9518 : * AccessExclusiveLock while at it.
9519 : *
9520 : * However, it's also important that we do not acquire locks on children if
9521 : * the not-null constraints already exist on the parent, to avoid risking
9522 : * deadlocks during parallel pg_restore of PKs on partitioned tables.
9523 : */
9524 : static void
9525 8225 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9526 : bool recurse, LOCKMODE lockmode,
9527 : AlterTableUtilityContext *context)
9528 : {
9529 : Constraint *pkconstr;
9530 8225 : List *children = NIL;
9531 8225 : bool got_children = false;
9532 :
9533 8225 : pkconstr = castNode(Constraint, cmd->def);
9534 8225 : if (pkconstr->contype != CONSTR_PRIMARY)
9535 4732 : return;
9536 :
9537 : /* Verify that columns are not-null, or request that they be made so */
9538 7474 : foreach_node(String, column, pkconstr->keys)
9539 : {
9540 : AlterTableCmd *newcmd;
9541 : Constraint *nnconstr;
9542 : HeapTuple tuple;
9543 :
9544 : /*
9545 : * First check if a suitable constraint exists. If it does, we don't
9546 : * need to request another one. We do need to bail out if it's not
9547 : * valid, though.
9548 : */
9549 518 : tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9550 518 : if (tuple != NULL)
9551 : {
9552 263 : verifyNotNullPKCompatible(tuple, strVal(column));
9553 :
9554 : /* All good with this one; don't request another */
9555 257 : heap_freetuple(tuple);
9556 257 : continue;
9557 : }
9558 255 : else if (!recurse)
9559 : {
9560 : /*
9561 : * No constraint on this column. Asked not to recurse, we won't
9562 : * create one here, but verify that all children have one.
9563 : */
9564 18 : if (!got_children)
9565 : {
9566 18 : children = find_inheritance_children(RelationGetRelid(rel),
9567 : lockmode);
9568 : /* only search for children on the first time through */
9569 18 : got_children = true;
9570 : }
9571 :
9572 36 : foreach_oid(childrelid, children)
9573 : {
9574 : HeapTuple tup;
9575 :
9576 18 : tup = findNotNullConstraint(childrelid, strVal(column));
9577 18 : if (!tup)
9578 3 : ereport(ERROR,
9579 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9580 : strVal(column), get_rel_name(childrelid)));
9581 : /* verify it's good enough */
9582 15 : verifyNotNullPKCompatible(tup, strVal(column));
9583 : }
9584 : }
9585 :
9586 : /* This column is not already not-null, so add it to the queue */
9587 246 : nnconstr = makeNotNullConstraint(column);
9588 :
9589 246 : newcmd = makeNode(AlterTableCmd);
9590 246 : newcmd->subtype = AT_AddConstraint;
9591 : /* note we force recurse=true here; see above */
9592 246 : newcmd->recurse = true;
9593 246 : newcmd->def = (Node *) nnconstr;
9594 :
9595 246 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9596 : }
9597 : }
9598 :
9599 : /*
9600 : * Verify whether the given not-null constraint is compatible with a
9601 : * primary key. If not, an error is thrown.
9602 : */
9603 : static void
9604 278 : verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9605 : {
9606 278 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
9607 :
9608 278 : if (conForm->contype != CONSTRAINT_NOTNULL)
9609 0 : elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9610 :
9611 : /* a NO INHERIT constraint is no good */
9612 278 : if (conForm->connoinherit)
9613 6 : ereport(ERROR,
9614 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9615 : errmsg("cannot create primary key on column \"%s\"", colname),
9616 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9617 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9618 : NameStr(conForm->conname), colname,
9619 : get_rel_name(conForm->conrelid), "NO INHERIT"),
9620 : errhint("You might need to make the existing constraint inheritable using %s.",
9621 : "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9622 :
9623 : /* an unvalidated constraint is no good */
9624 272 : if (!conForm->convalidated)
9625 6 : ereport(ERROR,
9626 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9627 : errmsg("cannot create primary key on column \"%s\"", colname),
9628 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9629 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9630 : NameStr(conForm->conname), colname,
9631 : get_rel_name(conForm->conrelid), "NOT VALID"),
9632 : errhint("You might need to validate it using %s.",
9633 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
9634 266 : }
9635 :
9636 : /*
9637 : * ALTER TABLE ADD INDEX
9638 : *
9639 : * There is no such command in the grammar, but parse_utilcmd.c converts
9640 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9641 : * us schedule creation of the index at the appropriate time during ALTER.
9642 : *
9643 : * Return value is the address of the new index.
9644 : */
9645 : static ObjectAddress
9646 833 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9647 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9648 : {
9649 : bool check_rights;
9650 : bool skip_build;
9651 : bool quiet;
9652 : ObjectAddress address;
9653 :
9654 : Assert(IsA(stmt, IndexStmt));
9655 : Assert(!stmt->concurrent);
9656 :
9657 : /* The IndexStmt has already been through transformIndexStmt */
9658 : Assert(stmt->transformed);
9659 :
9660 : /* suppress schema rights check when rebuilding existing index */
9661 833 : check_rights = !is_rebuild;
9662 : /* skip index build if phase 3 will do it or we're reusing an old one */
9663 833 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9664 : /* suppress notices when rebuilding existing index */
9665 833 : quiet = is_rebuild;
9666 :
9667 833 : address = DefineIndex(NULL,
9668 : RelationGetRelid(rel),
9669 : stmt,
9670 : InvalidOid, /* no predefined OID */
9671 : InvalidOid, /* no parent index */
9672 : InvalidOid, /* no parent constraint */
9673 : -1, /* total_parts unknown */
9674 : true, /* is_alter_table */
9675 : check_rights,
9676 : false, /* check_not_in_use - we did it already */
9677 : skip_build,
9678 : quiet);
9679 :
9680 : /*
9681 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9682 : * new index instead of building from scratch. Restore associated fields.
9683 : * This may store InvalidSubTransactionId in both fields, in which case
9684 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9685 : * this after the CCI that made catalog rows visible to any rebuild. The
9686 : * DROP of the old edition of this index will have scheduled the storage
9687 : * for deletion at commit, so cancel that pending deletion.
9688 : */
9689 748 : if (RelFileNumberIsValid(stmt->oldNumber))
9690 : {
9691 37 : Relation irel = index_open(address.objectId, NoLock);
9692 :
9693 37 : irel->rd_createSubid = stmt->oldCreateSubid;
9694 37 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9695 37 : RelationPreserveStorage(irel->rd_locator, true);
9696 37 : index_close(irel, NoLock);
9697 : }
9698 :
9699 748 : return address;
9700 : }
9701 :
9702 : /*
9703 : * ALTER TABLE ADD STATISTICS
9704 : *
9705 : * This is no such command in the grammar, but we use this internally to add
9706 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9707 : * column type change.
9708 : */
9709 : static ObjectAddress
9710 40 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9711 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9712 : {
9713 : ObjectAddress address;
9714 :
9715 : Assert(IsA(stmt, CreateStatsStmt));
9716 :
9717 : /* The CreateStatsStmt has already been through transformStatsStmt */
9718 : Assert(stmt->transformed);
9719 :
9720 40 : address = CreateStatistics(stmt, !is_rebuild);
9721 :
9722 40 : return address;
9723 : }
9724 :
9725 : /*
9726 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9727 : *
9728 : * Returns the address of the new constraint.
9729 : */
9730 : static ObjectAddress
9731 5430 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9732 : IndexStmt *stmt, LOCKMODE lockmode)
9733 : {
9734 5430 : Oid index_oid = stmt->indexOid;
9735 : Relation indexRel;
9736 : char *indexName;
9737 : IndexInfo *indexInfo;
9738 : char *constraintName;
9739 : char constraintType;
9740 : ObjectAddress address;
9741 : bits16 flags;
9742 :
9743 : Assert(IsA(stmt, IndexStmt));
9744 : Assert(OidIsValid(index_oid));
9745 : Assert(stmt->isconstraint);
9746 :
9747 : /*
9748 : * Doing this on partitioned tables is not a simple feature to implement,
9749 : * so let's punt for now.
9750 : */
9751 5430 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9752 3 : ereport(ERROR,
9753 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9754 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9755 :
9756 5427 : indexRel = index_open(index_oid, AccessShareLock);
9757 :
9758 5427 : indexName = pstrdup(RelationGetRelationName(indexRel));
9759 :
9760 5427 : indexInfo = BuildIndexInfo(indexRel);
9761 :
9762 : /* this should have been checked at parse time */
9763 5427 : if (!indexInfo->ii_Unique)
9764 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9765 :
9766 : /*
9767 : * Determine name to assign to constraint. We require a constraint to
9768 : * have the same name as the underlying index; therefore, use the index's
9769 : * existing name as the default constraint name, and if the user
9770 : * explicitly gives some other name for the constraint, rename the index
9771 : * to match.
9772 : */
9773 5427 : constraintName = stmt->idxname;
9774 5427 : if (constraintName == NULL)
9775 5414 : constraintName = indexName;
9776 13 : else if (strcmp(constraintName, indexName) != 0)
9777 : {
9778 10 : ereport(NOTICE,
9779 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9780 : indexName, constraintName)));
9781 10 : RenameRelationInternal(index_oid, constraintName, false, true);
9782 : }
9783 :
9784 : /* Extra checks needed if making primary key */
9785 5427 : if (stmt->primary)
9786 3065 : index_check_primary_key(rel, indexInfo, true, stmt);
9787 :
9788 : /* Note we currently don't support EXCLUSION constraints here */
9789 5424 : if (stmt->primary)
9790 3062 : constraintType = CONSTRAINT_PRIMARY;
9791 : else
9792 2362 : constraintType = CONSTRAINT_UNIQUE;
9793 :
9794 : /* Create the catalog entries for the constraint */
9795 5424 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9796 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9797 10848 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9798 5424 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9799 5424 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9800 :
9801 5424 : address = index_constraint_create(rel,
9802 : index_oid,
9803 : InvalidOid,
9804 : indexInfo,
9805 : constraintName,
9806 : constraintType,
9807 : flags,
9808 : allowSystemTableMods,
9809 : false); /* is_internal */
9810 :
9811 5424 : index_close(indexRel, NoLock);
9812 :
9813 5424 : return address;
9814 : }
9815 :
9816 : /*
9817 : * ALTER TABLE ADD CONSTRAINT
9818 : *
9819 : * Return value is the address of the new constraint; if no constraint was
9820 : * added, InvalidObjectAddress is returned.
9821 : */
9822 : static ObjectAddress
9823 6587 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9824 : Constraint *newConstraint, bool recurse, bool is_readd,
9825 : LOCKMODE lockmode)
9826 : {
9827 6587 : ObjectAddress address = InvalidObjectAddress;
9828 :
9829 : Assert(IsA(newConstraint, Constraint));
9830 :
9831 : /*
9832 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9833 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9834 : * parse_utilcmd.c).
9835 : */
9836 6587 : switch (newConstraint->contype)
9837 : {
9838 5239 : case CONSTR_CHECK:
9839 : case CONSTR_NOTNULL:
9840 : address =
9841 5239 : ATAddCheckNNConstraint(wqueue, tab, rel,
9842 : newConstraint, recurse, false, is_readd,
9843 : lockmode);
9844 5164 : break;
9845 :
9846 1348 : case CONSTR_FOREIGN:
9847 :
9848 : /*
9849 : * Assign or validate constraint name
9850 : */
9851 1348 : if (newConstraint->conname)
9852 : {
9853 606 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9854 : RelationGetRelid(rel),
9855 606 : newConstraint->conname))
9856 0 : ereport(ERROR,
9857 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9858 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9859 : newConstraint->conname,
9860 : RelationGetRelationName(rel))));
9861 : }
9862 : else
9863 742 : newConstraint->conname =
9864 742 : ChooseConstraintName(RelationGetRelationName(rel),
9865 742 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9866 : "fkey",
9867 742 : RelationGetNamespace(rel),
9868 : NIL);
9869 :
9870 1348 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9871 : newConstraint,
9872 : recurse, false,
9873 : lockmode);
9874 1074 : break;
9875 :
9876 0 : default:
9877 0 : elog(ERROR, "unrecognized constraint type: %d",
9878 : (int) newConstraint->contype);
9879 : }
9880 :
9881 6238 : return address;
9882 : }
9883 :
9884 : /*
9885 : * Generate the column-name portion of the constraint name for a new foreign
9886 : * key given the list of column names that reference the referenced
9887 : * table. This will be passed to ChooseConstraintName along with the parent
9888 : * table name and the "fkey" suffix.
9889 : *
9890 : * We know that less than NAMEDATALEN characters will actually be used, so we
9891 : * can truncate the result once we've generated that many.
9892 : *
9893 : * XXX see also ChooseExtendedStatisticNameAddition and
9894 : * ChooseIndexNameAddition.
9895 : */
9896 : static char *
9897 742 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9898 : {
9899 : char buf[NAMEDATALEN * 2];
9900 742 : int buflen = 0;
9901 : ListCell *lc;
9902 :
9903 742 : buf[0] = '\0';
9904 1698 : foreach(lc, colnames)
9905 : {
9906 956 : const char *name = strVal(lfirst(lc));
9907 :
9908 956 : if (buflen > 0)
9909 214 : buf[buflen++] = '_'; /* insert _ between names */
9910 :
9911 : /*
9912 : * At this point we have buflen <= NAMEDATALEN. name should be less
9913 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9914 : */
9915 956 : strlcpy(buf + buflen, name, NAMEDATALEN);
9916 956 : buflen += strlen(buf + buflen);
9917 956 : if (buflen >= NAMEDATALEN)
9918 0 : break;
9919 : }
9920 742 : return pstrdup(buf);
9921 : }
9922 :
9923 : /*
9924 : * Add a check or not-null constraint to a single table and its children.
9925 : * Returns the address of the constraint added to the parent relation,
9926 : * if one gets added, or InvalidObjectAddress otherwise.
9927 : *
9928 : * Subroutine for ATExecAddConstraint.
9929 : *
9930 : * We must recurse to child tables during execution, rather than using
9931 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9932 : * constraints *must* be given the same name, else they won't be seen as
9933 : * related later. If the user didn't explicitly specify a name, then
9934 : * AddRelationNewConstraints would normally assign different names to the
9935 : * child constraints. To fix that, we must capture the name assigned at
9936 : * the parent table and pass that down.
9937 : */
9938 : static ObjectAddress
9939 5734 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9940 : Constraint *constr, bool recurse, bool recursing,
9941 : bool is_readd, LOCKMODE lockmode)
9942 : {
9943 : List *newcons;
9944 : ListCell *lcon;
9945 : List *children;
9946 : ListCell *child;
9947 5734 : ObjectAddress address = InvalidObjectAddress;
9948 :
9949 : /* Guard against stack overflow due to overly deep inheritance tree. */
9950 5734 : check_stack_depth();
9951 :
9952 : /* At top level, permission check was done in ATPrepCmd, else do it */
9953 5734 : if (recursing)
9954 495 : ATSimplePermissions(AT_AddConstraint, rel,
9955 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9956 :
9957 : /*
9958 : * Call AddRelationNewConstraints to do the work, making sure it works on
9959 : * a copy of the Constraint so transformExpr can't modify the original. It
9960 : * returns a list of cooked constraints.
9961 : *
9962 : * If the constraint ends up getting merged with a pre-existing one, it's
9963 : * omitted from the returned list, which is what we want: we do not need
9964 : * to do any validation work. That can only happen at child tables,
9965 : * though, since we disallow merging at the top level.
9966 : */
9967 5734 : newcons = AddRelationNewConstraints(rel, NIL,
9968 5734 : list_make1(copyObject(constr)),
9969 5734 : recursing || is_readd, /* allow_merge */
9970 : !recursing, /* is_local */
9971 : is_readd, /* is_internal */
9972 11468 : NULL); /* queryString not available
9973 : * here */
9974 :
9975 : /* we don't expect more than one constraint here */
9976 : Assert(list_length(newcons) <= 1);
9977 :
9978 : /* Add each to-be-validated constraint to Phase 3's queue */
9979 11224 : foreach(lcon, newcons)
9980 : {
9981 5562 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9982 :
9983 5562 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9984 : {
9985 : NewConstraint *newcon;
9986 :
9987 563 : newcon = palloc0_object(NewConstraint);
9988 563 : newcon->name = ccon->name;
9989 563 : newcon->contype = ccon->contype;
9990 563 : newcon->qual = ccon->expr;
9991 :
9992 563 : tab->constraints = lappend(tab->constraints, newcon);
9993 : }
9994 :
9995 : /* Save the actually assigned name if it was defaulted */
9996 5562 : if (constr->conname == NULL)
9997 4537 : constr->conname = ccon->name;
9998 :
9999 : /*
10000 : * If adding a valid not-null constraint, set the pg_attribute flag
10001 : * and tell phase 3 to verify existing rows, if needed. For an
10002 : * invalid constraint, just set attnotnull, without queueing
10003 : * verification.
10004 : */
10005 5562 : if (constr->contype == CONSTR_NOTNULL)
10006 4767 : set_attnotnull(wqueue, rel, ccon->attnum,
10007 4767 : !constr->skip_validation,
10008 4767 : !constr->skip_validation);
10009 :
10010 5562 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
10011 : }
10012 :
10013 : /* At this point we must have a locked-down name to use */
10014 : Assert(newcons == NIL || constr->conname != NULL);
10015 :
10016 : /* Advance command counter in case same table is visited multiple times */
10017 5662 : CommandCounterIncrement();
10018 :
10019 : /*
10020 : * If the constraint got merged with an existing constraint, we're done.
10021 : * We mustn't recurse to child tables in this case, because they've
10022 : * already got the constraint, and visiting them again would lead to an
10023 : * incorrect value for coninhcount.
10024 : */
10025 5662 : if (newcons == NIL)
10026 100 : return address;
10027 :
10028 : /*
10029 : * If adding a NO INHERIT constraint, no need to find our children.
10030 : */
10031 5562 : if (constr->is_no_inherit)
10032 42 : return address;
10033 :
10034 : /*
10035 : * Propagate to children as appropriate. Unlike most other ALTER
10036 : * routines, we have to do this one level of recursion at a time; we can't
10037 : * use find_all_inheritors to do it in one pass.
10038 : */
10039 : children =
10040 5520 : find_inheritance_children(RelationGetRelid(rel), lockmode);
10041 :
10042 : /*
10043 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
10044 : * constraint creation only if there are no children currently. Error out
10045 : * otherwise.
10046 : */
10047 5520 : if (!recurse && children != NIL)
10048 3 : ereport(ERROR,
10049 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10050 : errmsg("constraint must be added to child tables too")));
10051 :
10052 : /*
10053 : * Recurse to create the constraint on each child.
10054 : */
10055 5997 : foreach(child, children)
10056 : {
10057 495 : Oid childrelid = lfirst_oid(child);
10058 : Relation childrel;
10059 : AlteredTableInfo *childtab;
10060 :
10061 : /* find_inheritance_children already got lock */
10062 495 : childrel = table_open(childrelid, NoLock);
10063 495 : CheckAlterTableIsSafe(childrel);
10064 :
10065 : /* Find or create work queue entry for this table */
10066 495 : childtab = ATGetQueueEntry(wqueue, childrel);
10067 :
10068 : /* Recurse to this child */
10069 495 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
10070 : constr, recurse, true, is_readd, lockmode);
10071 :
10072 480 : table_close(childrel, NoLock);
10073 : }
10074 :
10075 5502 : return address;
10076 : }
10077 :
10078 : /*
10079 : * Add a foreign-key constraint to a single table; return the new constraint's
10080 : * address.
10081 : *
10082 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
10083 : * lock on the rel, and have done appropriate validity checks for it.
10084 : * We do permissions checks here, however.
10085 : *
10086 : * When the referenced or referencing tables (or both) are partitioned,
10087 : * multiple pg_constraint rows are required -- one for each partitioned table
10088 : * and each partition on each side (fortunately, not one for every combination
10089 : * thereof). We also need action triggers on each leaf partition on the
10090 : * referenced side, and check triggers on each leaf partition on the
10091 : * referencing side.
10092 : */
10093 : static ObjectAddress
10094 1348 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
10095 : Constraint *fkconstraint,
10096 : bool recurse, bool recursing, LOCKMODE lockmode)
10097 : {
10098 : Relation pkrel;
10099 1348 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
10100 1348 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
10101 1348 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
10102 1348 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
10103 1348 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10104 1348 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10105 1348 : Oid opclasses[INDEX_MAX_KEYS] = {0};
10106 1348 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10107 1348 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10108 1348 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10109 1348 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10110 : bool with_period;
10111 : bool pk_has_without_overlaps;
10112 : int i;
10113 : int numfks,
10114 : numpks,
10115 : numfkdelsetcols;
10116 : Oid indexOid;
10117 : bool old_check_ok;
10118 : ObjectAddress address;
10119 1348 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10120 :
10121 : /*
10122 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10123 : * delete rows out from under us.
10124 : */
10125 1348 : if (OidIsValid(fkconstraint->old_pktable_oid))
10126 36 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10127 : else
10128 1312 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10129 :
10130 : /*
10131 : * Validity checks (permission checks wait till we have the column
10132 : * numbers)
10133 : */
10134 1345 : if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10135 3 : ereport(ERROR,
10136 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
10137 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10138 : RelationGetRelationName(rel),
10139 : RelationGetRelationName(pkrel)));
10140 :
10141 1342 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10142 184 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10143 0 : ereport(ERROR,
10144 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10145 : errmsg("referenced relation \"%s\" is not a table",
10146 : RelationGetRelationName(pkrel))));
10147 :
10148 1342 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
10149 1 : ereport(ERROR,
10150 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10151 : errmsg("permission denied: \"%s\" is a system catalog",
10152 : RelationGetRelationName(pkrel))));
10153 :
10154 : /*
10155 : * References from permanent or unlogged tables to temp tables, and from
10156 : * permanent tables to unlogged tables, are disallowed because the
10157 : * referenced data can vanish out from under us. References from temp
10158 : * tables to any other table type are also disallowed, because other
10159 : * backends might need to run the RI triggers on the perm table, but they
10160 : * can't reliably see tuples in the local buffers of other backends.
10161 : */
10162 1341 : switch (rel->rd_rel->relpersistence)
10163 : {
10164 1196 : case RELPERSISTENCE_PERMANENT:
10165 1196 : if (!RelationIsPermanent(pkrel))
10166 0 : ereport(ERROR,
10167 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10168 : errmsg("constraints on permanent tables may reference only permanent tables")));
10169 1196 : break;
10170 6 : case RELPERSISTENCE_UNLOGGED:
10171 6 : if (!RelationIsPermanent(pkrel)
10172 6 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10173 0 : ereport(ERROR,
10174 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10175 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10176 6 : break;
10177 139 : case RELPERSISTENCE_TEMP:
10178 139 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10179 0 : ereport(ERROR,
10180 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10181 : errmsg("constraints on temporary tables may reference only temporary tables")));
10182 139 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10183 0 : ereport(ERROR,
10184 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10185 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
10186 139 : break;
10187 : }
10188 :
10189 : /*
10190 : * Look up the referencing attributes to make sure they exist, and record
10191 : * their attnums and type and collation OIDs.
10192 : */
10193 1341 : numfks = transformColumnNameList(RelationGetRelid(rel),
10194 : fkconstraint->fk_attrs,
10195 : fkattnum, fktypoid, fkcolloid);
10196 1326 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10197 1326 : if (with_period && !fkconstraint->fk_with_period)
10198 12 : ereport(ERROR,
10199 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10200 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10201 :
10202 1314 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10203 : fkconstraint->fk_del_set_cols,
10204 : fkdelsetcols, NULL, NULL);
10205 1311 : numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10206 : numfkdelsetcols,
10207 : fkdelsetcols,
10208 : fkconstraint->fk_del_set_cols);
10209 :
10210 : /*
10211 : * If the attribute list for the referenced table was omitted, lookup the
10212 : * definition of the primary key and use it. Otherwise, validate the
10213 : * supplied attribute list. In either case, discover the index OID and
10214 : * index opclasses, and the attnums and type and collation OIDs of the
10215 : * attributes.
10216 : */
10217 1308 : if (fkconstraint->pk_attrs == NIL)
10218 : {
10219 634 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10220 : &fkconstraint->pk_attrs,
10221 : pkattnum, pktypoid, pkcolloid,
10222 : opclasses, &pk_has_without_overlaps);
10223 :
10224 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10225 634 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10226 12 : ereport(ERROR,
10227 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10228 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10229 : }
10230 : else
10231 : {
10232 674 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
10233 : fkconstraint->pk_attrs,
10234 : pkattnum, pktypoid, pkcolloid);
10235 :
10236 : /* Since we got pk_attrs, one should be a period. */
10237 659 : if (with_period && !fkconstraint->pk_with_period)
10238 12 : ereport(ERROR,
10239 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10240 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10241 :
10242 : /* Look for an index matching the column list */
10243 647 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10244 : with_period, opclasses, &pk_has_without_overlaps);
10245 : }
10246 :
10247 : /*
10248 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10249 : * must use PERIOD.
10250 : */
10251 1251 : if (pk_has_without_overlaps && !with_period)
10252 6 : ereport(ERROR,
10253 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10254 : errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10255 :
10256 : /*
10257 : * Now we can check permissions.
10258 : */
10259 1245 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10260 :
10261 : /*
10262 : * Check some things for generated columns.
10263 : */
10264 2922 : for (i = 0; i < numfks; i++)
10265 : {
10266 1692 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10267 :
10268 1692 : if (attgenerated)
10269 : {
10270 : /*
10271 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10272 : */
10273 24 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10274 24 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10275 24 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10276 6 : ereport(ERROR,
10277 : (errcode(ERRCODE_SYNTAX_ERROR),
10278 : errmsg("invalid %s action for foreign key constraint containing generated column",
10279 : "ON UPDATE")));
10280 18 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10281 12 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10282 6 : ereport(ERROR,
10283 : (errcode(ERRCODE_SYNTAX_ERROR),
10284 : errmsg("invalid %s action for foreign key constraint containing generated column",
10285 : "ON DELETE")));
10286 : }
10287 :
10288 : /*
10289 : * FKs on virtual columns are not supported. This would require
10290 : * various additional support in ri_triggers.c, including special
10291 : * handling in ri_NullCheck(), ri_KeysEqual(),
10292 : * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10293 : * as NULL there). Also not really practical as long as you can't
10294 : * index virtual columns.
10295 : */
10296 1680 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10297 3 : ereport(ERROR,
10298 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10299 : errmsg("foreign key constraints on virtual generated columns are not supported")));
10300 : }
10301 :
10302 : /*
10303 : * Some actions are currently unsupported for foreign keys using PERIOD.
10304 : */
10305 1230 : if (fkconstraint->fk_with_period)
10306 : {
10307 139 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10308 133 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10309 124 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10310 115 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10311 33 : ereport(ERROR,
10312 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10313 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10314 : "ON UPDATE"));
10315 :
10316 106 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10317 103 : fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10318 103 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10319 103 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10320 3 : ereport(ERROR,
10321 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10322 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10323 : "ON DELETE"));
10324 : }
10325 :
10326 : /*
10327 : * Look up the equality operators to use in the constraint.
10328 : *
10329 : * Note that we have to be careful about the difference between the actual
10330 : * PK column type and the opclass' declared input type, which might be
10331 : * only binary-compatible with it. The declared opcintype is the right
10332 : * thing to probe pg_amop with.
10333 : */
10334 1194 : if (numfks != numpks)
10335 0 : ereport(ERROR,
10336 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10337 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10338 :
10339 : /*
10340 : * On the strength of a previous constraint, we might avoid scanning
10341 : * tables to validate this one. See below.
10342 : */
10343 1194 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10344 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10345 :
10346 2607 : for (i = 0; i < numpks; i++)
10347 : {
10348 1533 : Oid pktype = pktypoid[i];
10349 1533 : Oid fktype = fktypoid[i];
10350 : Oid fktyped;
10351 1533 : Oid pkcoll = pkcolloid[i];
10352 1533 : Oid fkcoll = fkcolloid[i];
10353 : HeapTuple cla_ht;
10354 : Form_pg_opclass cla_tup;
10355 : Oid amid;
10356 : Oid opfamily;
10357 : Oid opcintype;
10358 : bool for_overlaps;
10359 : CompareType cmptype;
10360 : Oid pfeqop;
10361 : Oid ppeqop;
10362 : Oid ffeqop;
10363 : int16 eqstrategy;
10364 : Oid pfeqop_right;
10365 :
10366 : /* We need several fields out of the pg_opclass entry */
10367 1533 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10368 1533 : if (!HeapTupleIsValid(cla_ht))
10369 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10370 1533 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10371 1533 : amid = cla_tup->opcmethod;
10372 1533 : opfamily = cla_tup->opcfamily;
10373 1533 : opcintype = cla_tup->opcintype;
10374 1533 : ReleaseSysCache(cla_ht);
10375 :
10376 : /*
10377 : * Get strategy number from index AM.
10378 : *
10379 : * For a normal foreign-key constraint, this should not fail, since we
10380 : * already checked that the index is unique and should therefore have
10381 : * appropriate equal operators. For a period foreign key, this could
10382 : * fail if we selected a non-matching exclusion constraint earlier.
10383 : * (XXX Maybe we should do these lookups earlier so we don't end up
10384 : * doing that.)
10385 : */
10386 1533 : for_overlaps = with_period && i == numpks - 1;
10387 1533 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10388 1533 : eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10389 1533 : if (eqstrategy == InvalidStrategy)
10390 0 : ereport(ERROR,
10391 : errcode(ERRCODE_UNDEFINED_OBJECT),
10392 : for_overlaps
10393 : ? errmsg("could not identify an overlaps operator for foreign key")
10394 : : errmsg("could not identify an equality operator for foreign key"),
10395 : errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10396 : cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10397 :
10398 : /*
10399 : * There had better be a primary equality operator for the index.
10400 : * We'll use it for PK = PK comparisons.
10401 : */
10402 1533 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10403 : eqstrategy);
10404 :
10405 1533 : if (!OidIsValid(ppeqop))
10406 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10407 : eqstrategy, opcintype, opcintype, opfamily);
10408 :
10409 : /*
10410 : * Are there equality operators that take exactly the FK type? Assume
10411 : * we should look through any domain here.
10412 : */
10413 1533 : fktyped = getBaseType(fktype);
10414 :
10415 1533 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10416 : eqstrategy);
10417 1533 : if (OidIsValid(pfeqop))
10418 : {
10419 1185 : pfeqop_right = fktyped;
10420 1185 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10421 : eqstrategy);
10422 : }
10423 : else
10424 : {
10425 : /* keep compiler quiet */
10426 348 : pfeqop_right = InvalidOid;
10427 348 : ffeqop = InvalidOid;
10428 : }
10429 :
10430 1533 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10431 : {
10432 : /*
10433 : * Otherwise, look for an implicit cast from the FK type to the
10434 : * opcintype, and if found, use the primary equality operator.
10435 : * This is a bit tricky because opcintype might be a polymorphic
10436 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10437 : * whether the two actual column types can be concurrently cast to
10438 : * that type. (Otherwise, we'd fail to reject combinations such
10439 : * as int[] and point[].)
10440 : */
10441 : Oid input_typeids[2];
10442 : Oid target_typeids[2];
10443 :
10444 348 : input_typeids[0] = pktype;
10445 348 : input_typeids[1] = fktype;
10446 348 : target_typeids[0] = opcintype;
10447 348 : target_typeids[1] = opcintype;
10448 348 : if (can_coerce_type(2, input_typeids, target_typeids,
10449 : COERCION_IMPLICIT))
10450 : {
10451 234 : pfeqop = ffeqop = ppeqop;
10452 234 : pfeqop_right = opcintype;
10453 : }
10454 : }
10455 :
10456 1533 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10457 114 : ereport(ERROR,
10458 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10459 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10460 : fkconstraint->conname),
10461 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10462 : "are of incompatible types: %s and %s.",
10463 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10464 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10465 : format_type_be(fktype),
10466 : format_type_be(pktype))));
10467 :
10468 : /*
10469 : * This shouldn't be possible, but better check to make sure we have a
10470 : * consistent state for the check below.
10471 : */
10472 1419 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10473 0 : elog(ERROR, "key columns are not both collatable");
10474 :
10475 1419 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10476 : {
10477 : bool pkcolldet;
10478 : bool fkcolldet;
10479 :
10480 52 : pkcolldet = get_collation_isdeterministic(pkcoll);
10481 52 : fkcolldet = get_collation_isdeterministic(fkcoll);
10482 :
10483 : /*
10484 : * SQL requires that both collations are the same. This is
10485 : * because we need a consistent notion of equality on both
10486 : * columns. We relax this by allowing different collations if
10487 : * they are both deterministic. (This is also for backward
10488 : * compatibility, because PostgreSQL has always allowed this.)
10489 : */
10490 52 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10491 6 : ereport(ERROR,
10492 : (errcode(ERRCODE_COLLATION_MISMATCH),
10493 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10494 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10495 : "have incompatible collations: \"%s\" and \"%s\". "
10496 : "If either collation is nondeterministic, then both collations have to be the same.",
10497 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10498 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10499 : get_collation_name(fkcoll),
10500 : get_collation_name(pkcoll))));
10501 : }
10502 :
10503 1413 : if (old_check_ok)
10504 : {
10505 : /*
10506 : * When a pfeqop changes, revalidate the constraint. We could
10507 : * permit intra-opfamily changes, but that adds subtle complexity
10508 : * without any concrete benefit for core types. We need not
10509 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10510 : */
10511 3 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10512 3 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10513 : old_pfeqop_item);
10514 : }
10515 1413 : if (old_check_ok)
10516 : {
10517 : Oid old_fktype;
10518 : Oid new_fktype;
10519 : CoercionPathType old_pathtype;
10520 : CoercionPathType new_pathtype;
10521 : Oid old_castfunc;
10522 : Oid new_castfunc;
10523 : Oid old_fkcoll;
10524 : Oid new_fkcoll;
10525 3 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10526 3 : fkattnum[i] - 1);
10527 :
10528 : /*
10529 : * Identify coercion pathways from each of the old and new FK-side
10530 : * column types to the right (foreign) operand type of the pfeqop.
10531 : * We may assume that pg_constraint.conkey is not changing.
10532 : */
10533 3 : old_fktype = attr->atttypid;
10534 3 : new_fktype = fktype;
10535 3 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10536 : &old_castfunc);
10537 3 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10538 : &new_castfunc);
10539 :
10540 3 : old_fkcoll = attr->attcollation;
10541 3 : new_fkcoll = fkcoll;
10542 :
10543 : /*
10544 : * Upon a change to the cast from the FK column to its pfeqop
10545 : * operand, revalidate the constraint. For this evaluation, a
10546 : * binary coercion cast is equivalent to no cast at all. While
10547 : * type implementors should design implicit casts with an eye
10548 : * toward consistency of operations like equality, we cannot
10549 : * assume here that they have done so.
10550 : *
10551 : * A function with a polymorphic argument could change behavior
10552 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10553 : * when the cast destination is polymorphic, we only avoid
10554 : * revalidation if the input type has not changed at all. Given
10555 : * just the core data types and operator classes, this requirement
10556 : * prevents no would-be optimizations.
10557 : *
10558 : * If the cast converts from a base type to a domain thereon, then
10559 : * that domain type must be the opcintype of the unique index.
10560 : * Necessarily, the primary key column must then be of the domain
10561 : * type. Since the constraint was previously valid, all values on
10562 : * the foreign side necessarily exist on the primary side and in
10563 : * turn conform to the domain. Consequently, we need not treat
10564 : * domains specially here.
10565 : *
10566 : * If the collation changes, revalidation is required, unless both
10567 : * collations are deterministic, because those share the same
10568 : * notion of equality (because texteq reduces to bitwise
10569 : * equality).
10570 : *
10571 : * We need not directly consider the PK type. It's necessarily
10572 : * binary coercible to the opcintype of the unique index column,
10573 : * and ri_triggers.c will only deal with PK datums in terms of
10574 : * that opcintype. Changing the opcintype also changes pfeqop.
10575 : */
10576 3 : old_check_ok = (new_pathtype == old_pathtype &&
10577 3 : new_castfunc == old_castfunc &&
10578 3 : (!IsPolymorphicType(pfeqop_right) ||
10579 6 : new_fktype == old_fktype) &&
10580 0 : (new_fkcoll == old_fkcoll ||
10581 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10582 : }
10583 :
10584 1413 : pfeqoperators[i] = pfeqop;
10585 1413 : ppeqoperators[i] = ppeqop;
10586 1413 : ffeqoperators[i] = ffeqop;
10587 : }
10588 :
10589 : /*
10590 : * For FKs with PERIOD we need additional operators to check whether the
10591 : * referencing row's range is contained by the aggregated ranges of the
10592 : * referenced row(s). For rangetypes and multirangetypes this is
10593 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10594 : * support for now. FKs will look these up at "runtime", but we should
10595 : * make sure the lookup works here, even if we don't use the values.
10596 : */
10597 1074 : if (with_period)
10598 : {
10599 : Oid periodoperoid;
10600 : Oid aggedperiodoperoid;
10601 : Oid intersectoperoid;
10602 :
10603 94 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10604 : &intersectoperoid);
10605 : }
10606 :
10607 : /* First, create the constraint catalog entry itself. */
10608 1074 : address = addFkConstraint(addFkBothSides,
10609 : fkconstraint->conname, fkconstraint, rel, pkrel,
10610 : indexOid,
10611 : InvalidOid, /* no parent constraint */
10612 : numfks,
10613 : pkattnum,
10614 : fkattnum,
10615 : pfeqoperators,
10616 : ppeqoperators,
10617 : ffeqoperators,
10618 : numfkdelsetcols,
10619 : fkdelsetcols,
10620 : false,
10621 : with_period);
10622 :
10623 : /* Next process the action triggers at the referenced side and recurse */
10624 1074 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10625 : indexOid,
10626 : address.objectId,
10627 : numfks,
10628 : pkattnum,
10629 : fkattnum,
10630 : pfeqoperators,
10631 : ppeqoperators,
10632 : ffeqoperators,
10633 : numfkdelsetcols,
10634 : fkdelsetcols,
10635 : old_check_ok,
10636 : InvalidOid, InvalidOid,
10637 : with_period);
10638 :
10639 : /* Lastly create the check triggers at the referencing side and recurse */
10640 1074 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10641 : indexOid,
10642 : address.objectId,
10643 : numfks,
10644 : pkattnum,
10645 : fkattnum,
10646 : pfeqoperators,
10647 : ppeqoperators,
10648 : ffeqoperators,
10649 : numfkdelsetcols,
10650 : fkdelsetcols,
10651 : old_check_ok,
10652 : lockmode,
10653 : InvalidOid, InvalidOid,
10654 : with_period);
10655 :
10656 : /*
10657 : * Done. Close pk table, but keep lock until we've committed.
10658 : */
10659 1074 : table_close(pkrel, NoLock);
10660 :
10661 1074 : return address;
10662 : }
10663 :
10664 : /*
10665 : * validateFkOnDeleteSetColumns
10666 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10667 : * column lists are valid.
10668 : *
10669 : * If there are duplicates in the fksetcolsattnums[] array, this silently
10670 : * removes the dups. The new count of numfksetcols is returned.
10671 : */
10672 : static int
10673 1311 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10674 : int numfksetcols, int16 *fksetcolsattnums,
10675 : List *fksetcols)
10676 : {
10677 1311 : int numcolsout = 0;
10678 :
10679 1326 : for (int i = 0; i < numfksetcols; i++)
10680 : {
10681 18 : int16 setcol_attnum = fksetcolsattnums[i];
10682 18 : bool seen = false;
10683 :
10684 : /* Make sure it's in fkattnums[] */
10685 33 : for (int j = 0; j < numfks; j++)
10686 : {
10687 30 : if (fkattnums[j] == setcol_attnum)
10688 : {
10689 15 : seen = true;
10690 15 : break;
10691 : }
10692 : }
10693 :
10694 18 : if (!seen)
10695 : {
10696 3 : char *col = strVal(list_nth(fksetcols, i));
10697 :
10698 3 : ereport(ERROR,
10699 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10700 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10701 : }
10702 :
10703 : /* Now check for dups */
10704 15 : seen = false;
10705 15 : for (int j = 0; j < numcolsout; j++)
10706 : {
10707 3 : if (fksetcolsattnums[j] == setcol_attnum)
10708 : {
10709 3 : seen = true;
10710 3 : break;
10711 : }
10712 : }
10713 15 : if (!seen)
10714 12 : fksetcolsattnums[numcolsout++] = setcol_attnum;
10715 : }
10716 1308 : return numcolsout;
10717 : }
10718 :
10719 : /*
10720 : * addFkConstraint
10721 : * Install pg_constraint entries to implement a foreign key constraint.
10722 : * Caller must separately invoke addFkRecurseReferenced and
10723 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10724 : * and (for partitioned tables) recurse to partitions.
10725 : *
10726 : * fkside: the side of the FK (or both) to create. Caller should
10727 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10728 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10729 : * addFkBothSides.
10730 : * constraintname: the base name for the constraint being added,
10731 : * copied to fkconstraint->conname if the latter is not set
10732 : * fkconstraint: the constraint being added
10733 : * rel: the root referencing relation
10734 : * pkrel: the referenced relation; might be a partition, if recursing
10735 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10736 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10737 : * top-level constraint
10738 : * numfks: the number of columns in the foreign key
10739 : * pkattnum: the attnum array of referenced attributes
10740 : * fkattnum: the attnum array of referencing attributes
10741 : * pf/pp/ffeqoperators: OID array of operators between columns
10742 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10743 : * (...) clause
10744 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10745 : * NULL/DEFAULT clause
10746 : * with_period: true if this is a temporal FK
10747 : */
10748 : static ObjectAddress
10749 2113 : addFkConstraint(addFkConstraintSides fkside,
10750 : char *constraintname, Constraint *fkconstraint,
10751 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10752 : int numfks, int16 *pkattnum,
10753 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10754 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10755 : bool is_internal, bool with_period)
10756 : {
10757 : ObjectAddress address;
10758 : Oid constrOid;
10759 : char *conname;
10760 : bool conislocal;
10761 : int16 coninhcount;
10762 : bool connoinherit;
10763 :
10764 : /*
10765 : * Verify relkind for each referenced partition. At the top level, this
10766 : * is redundant with a previous check, but we need it when recursing.
10767 : */
10768 2113 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10769 443 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10770 0 : ereport(ERROR,
10771 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10772 : errmsg("referenced relation \"%s\" is not a table",
10773 : RelationGetRelationName(pkrel))));
10774 :
10775 : /*
10776 : * Caller supplies us with a constraint name; however, it may be used in
10777 : * this partition, so come up with a different one in that case. Unless
10778 : * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10779 : * supplied name with an underscore and digit(s) appended.
10780 : */
10781 2113 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10782 : RelationGetRelid(rel),
10783 : constraintname))
10784 621 : conname = ChooseConstraintName(constraintname,
10785 : NULL,
10786 : "",
10787 621 : RelationGetNamespace(rel), NIL);
10788 : else
10789 1492 : conname = constraintname;
10790 :
10791 2113 : if (fkconstraint->conname == NULL)
10792 236 : fkconstraint->conname = pstrdup(conname);
10793 :
10794 2113 : if (OidIsValid(parentConstr))
10795 : {
10796 1039 : conislocal = false;
10797 1039 : coninhcount = 1;
10798 1039 : connoinherit = false;
10799 : }
10800 : else
10801 : {
10802 1074 : conislocal = true;
10803 1074 : coninhcount = 0;
10804 :
10805 : /*
10806 : * always inherit for partitioned tables, never for legacy inheritance
10807 : */
10808 1074 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10809 : }
10810 :
10811 : /*
10812 : * Record the FK constraint in pg_constraint.
10813 : */
10814 2113 : constrOid = CreateConstraintEntry(conname,
10815 2113 : RelationGetNamespace(rel),
10816 : CONSTRAINT_FOREIGN,
10817 2113 : fkconstraint->deferrable,
10818 2113 : fkconstraint->initdeferred,
10819 2113 : fkconstraint->is_enforced,
10820 2113 : fkconstraint->initially_valid,
10821 : parentConstr,
10822 : RelationGetRelid(rel),
10823 : fkattnum,
10824 : numfks,
10825 : numfks,
10826 : InvalidOid, /* not a domain constraint */
10827 : indexOid,
10828 : RelationGetRelid(pkrel),
10829 : pkattnum,
10830 : pfeqoperators,
10831 : ppeqoperators,
10832 : ffeqoperators,
10833 : numfks,
10834 2113 : fkconstraint->fk_upd_action,
10835 2113 : fkconstraint->fk_del_action,
10836 : fkdelsetcols,
10837 : numfkdelsetcols,
10838 2113 : fkconstraint->fk_matchtype,
10839 : NULL, /* no exclusion constraint */
10840 : NULL, /* no check constraint */
10841 : NULL,
10842 : conislocal, /* islocal */
10843 : coninhcount, /* inhcount */
10844 : connoinherit, /* conNoInherit */
10845 : with_period, /* conPeriod */
10846 : is_internal); /* is_internal */
10847 :
10848 2113 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10849 :
10850 : /*
10851 : * In partitioning cases, create the dependency entries for this
10852 : * constraint. (For non-partitioned cases, relevant entries were created
10853 : * by CreateConstraintEntry.)
10854 : *
10855 : * On the referenced side, we need the constraint to have an internal
10856 : * dependency on its parent constraint; this means that this constraint
10857 : * cannot be dropped on its own -- only through the parent constraint. It
10858 : * also means the containing partition cannot be dropped on its own, but
10859 : * it can be detached, at which point this dependency is removed (after
10860 : * verifying that no rows are referenced via this FK.)
10861 : *
10862 : * When processing the referencing side, we link the constraint via the
10863 : * special partitioning dependencies: the parent constraint is the primary
10864 : * dependent, and the partition on which the foreign key exists is the
10865 : * secondary dependency. That way, this constraint is dropped if either
10866 : * of these objects is.
10867 : *
10868 : * Note that this is only necessary for the subsidiary pg_constraint rows
10869 : * in partitions; the topmost row doesn't need any of this.
10870 : */
10871 2113 : if (OidIsValid(parentConstr))
10872 : {
10873 : ObjectAddress referenced;
10874 :
10875 1039 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10876 :
10877 : Assert(fkside != addFkBothSides);
10878 1039 : if (fkside == addFkReferencedSide)
10879 618 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10880 : else
10881 : {
10882 421 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10883 421 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10884 421 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10885 : }
10886 : }
10887 :
10888 : /* make new constraint visible, in case we add more */
10889 2113 : CommandCounterIncrement();
10890 :
10891 2113 : return address;
10892 : }
10893 :
10894 : /*
10895 : * addFkRecurseReferenced
10896 : * Recursive helper for the referenced side of foreign key creation,
10897 : * which creates the action triggers and recurses
10898 : *
10899 : * If the referenced relation is a plain relation, create the necessary action
10900 : * triggers that implement the constraint. If the referenced relation is a
10901 : * partitioned table, then we create a pg_constraint row referencing the parent
10902 : * of the referencing side for it and recurse on this routine for each
10903 : * partition.
10904 : *
10905 : * fkconstraint: the constraint being added
10906 : * rel: the root referencing relation
10907 : * pkrel: the referenced relation; might be a partition, if recursing
10908 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10909 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10910 : * top-level constraint
10911 : * numfks: the number of columns in the foreign key
10912 : * pkattnum: the attnum array of referenced attributes
10913 : * fkattnum: the attnum array of referencing attributes
10914 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10915 : * NULL/DEFAULT (...) clause
10916 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10917 : * NULL/DEFAULT clause
10918 : * pf/pp/ffeqoperators: OID array of operators between columns
10919 : * old_check_ok: true if this constraint replaces an existing one that
10920 : * was already validated (thus this one doesn't need validation)
10921 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10922 : * partition, the OIDs of the parent action triggers for DELETE and
10923 : * UPDATE respectively.
10924 : * with_period: true if this is a temporal FK
10925 : */
10926 : static void
10927 1746 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10928 : Relation pkrel, Oid indexOid, Oid parentConstr,
10929 : int numfks,
10930 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10931 : Oid *ppeqoperators, Oid *ffeqoperators,
10932 : int numfkdelsetcols, int16 *fkdelsetcols,
10933 : bool old_check_ok,
10934 : Oid parentDelTrigger, Oid parentUpdTrigger,
10935 : bool with_period)
10936 : {
10937 1746 : Oid deleteTriggerOid = InvalidOid,
10938 1746 : updateTriggerOid = InvalidOid;
10939 :
10940 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10941 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10942 :
10943 : /*
10944 : * Create action triggers to enforce the constraint, or skip them if the
10945 : * constraint is NOT ENFORCED.
10946 : */
10947 1746 : if (fkconstraint->is_enforced)
10948 1710 : createForeignKeyActionTriggers(RelationGetRelid(rel),
10949 : RelationGetRelid(pkrel),
10950 : fkconstraint,
10951 : parentConstr, indexOid,
10952 : parentDelTrigger, parentUpdTrigger,
10953 : &deleteTriggerOid, &updateTriggerOid);
10954 :
10955 : /*
10956 : * If the referenced table is partitioned, recurse on ourselves to handle
10957 : * each partition. We need one pg_constraint row created for each
10958 : * partition in addition to the pg_constraint row for the parent table.
10959 : */
10960 1746 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10961 : {
10962 288 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10963 :
10964 774 : for (int i = 0; i < pd->nparts; i++)
10965 : {
10966 : Relation partRel;
10967 : AttrMap *map;
10968 : AttrNumber *mapped_pkattnum;
10969 : Oid partIndexId;
10970 : ObjectAddress address;
10971 :
10972 : /* XXX would it be better to acquire these locks beforehand? */
10973 486 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10974 :
10975 : /*
10976 : * Map the attribute numbers in the referenced side of the FK
10977 : * definition to match the partition's column layout.
10978 : */
10979 486 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10980 : RelationGetDescr(pkrel),
10981 : false);
10982 486 : if (map)
10983 : {
10984 68 : mapped_pkattnum = palloc_array(AttrNumber, numfks);
10985 142 : for (int j = 0; j < numfks; j++)
10986 74 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10987 : }
10988 : else
10989 418 : mapped_pkattnum = pkattnum;
10990 :
10991 : /* Determine the index to use at this level */
10992 486 : partIndexId = index_get_partition(partRel, indexOid);
10993 486 : if (!OidIsValid(partIndexId))
10994 0 : elog(ERROR, "index for %u not found in partition %s",
10995 : indexOid, RelationGetRelationName(partRel));
10996 :
10997 : /* Create entry at this level ... */
10998 486 : address = addFkConstraint(addFkReferencedSide,
10999 : fkconstraint->conname, fkconstraint, rel,
11000 : partRel, partIndexId, parentConstr,
11001 : numfks, mapped_pkattnum,
11002 : fkattnum, pfeqoperators, ppeqoperators,
11003 : ffeqoperators, numfkdelsetcols,
11004 : fkdelsetcols, true, with_period);
11005 : /* ... and recurse to our children */
11006 486 : addFkRecurseReferenced(fkconstraint, rel, partRel,
11007 : partIndexId, address.objectId, numfks,
11008 : mapped_pkattnum, fkattnum,
11009 : pfeqoperators, ppeqoperators, ffeqoperators,
11010 : numfkdelsetcols, fkdelsetcols,
11011 : old_check_ok,
11012 : deleteTriggerOid, updateTriggerOid,
11013 : with_period);
11014 :
11015 : /* Done -- clean up (but keep the lock) */
11016 486 : table_close(partRel, NoLock);
11017 486 : if (map)
11018 : {
11019 68 : pfree(mapped_pkattnum);
11020 68 : free_attrmap(map);
11021 : }
11022 : }
11023 : }
11024 1746 : }
11025 :
11026 : /*
11027 : * addFkRecurseReferencing
11028 : * Recursive helper for the referencing side of foreign key creation,
11029 : * which creates the check triggers and recurses
11030 : *
11031 : * If the referencing relation is a plain relation, create the necessary check
11032 : * triggers that implement the constraint, and set up for Phase 3 constraint
11033 : * verification. If the referencing relation is a partitioned table, then
11034 : * we create a pg_constraint row for it and recurse on this routine for each
11035 : * partition.
11036 : *
11037 : * We assume that the referenced relation is locked against concurrent
11038 : * deletions. If it's a partitioned relation, every partition must be so
11039 : * locked.
11040 : *
11041 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
11042 : * of an ALTER TABLE sequence.
11043 : * fkconstraint: the constraint being added
11044 : * rel: the referencing relation; might be a partition, if recursing
11045 : * pkrel: the root referenced relation
11046 : * indexOid: the OID of the index (on pkrel) implementing this constraint
11047 : * parentConstr: the OID of the parent constraint (there is always one)
11048 : * numfks: the number of columns in the foreign key
11049 : * pkattnum: the attnum array of referenced attributes
11050 : * fkattnum: the attnum array of referencing attributes
11051 : * pf/pp/ffeqoperators: OID array of operators between columns
11052 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
11053 : * (...) clause
11054 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
11055 : * NULL/DEFAULT clause
11056 : * old_check_ok: true if this constraint replaces an existing one that
11057 : * was already validated (thus this one doesn't need validation)
11058 : * lockmode: the lockmode to acquire on partitions when recursing
11059 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
11060 : * a partition, the OIDs of the parent check triggers for INSERT and
11061 : * UPDATE respectively.
11062 : * with_period: true if this is a temporal FK
11063 : */
11064 : static void
11065 1495 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
11066 : Relation pkrel, Oid indexOid, Oid parentConstr,
11067 : int numfks, int16 *pkattnum, int16 *fkattnum,
11068 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11069 : int numfkdelsetcols, int16 *fkdelsetcols,
11070 : bool old_check_ok, LOCKMODE lockmode,
11071 : Oid parentInsTrigger, Oid parentUpdTrigger,
11072 : bool with_period)
11073 : {
11074 1495 : Oid insertTriggerOid = InvalidOid,
11075 1495 : updateTriggerOid = InvalidOid;
11076 :
11077 : Assert(OidIsValid(parentConstr));
11078 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
11079 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
11080 :
11081 1495 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11082 0 : ereport(ERROR,
11083 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11084 : errmsg("foreign key constraints are not supported on foreign tables")));
11085 :
11086 : /*
11087 : * Add check triggers if the constraint is ENFORCED, and if needed,
11088 : * schedule them to be checked in Phase 3.
11089 : *
11090 : * If the relation is partitioned, drill down to do it to its partitions.
11091 : */
11092 1495 : if (fkconstraint->is_enforced)
11093 1471 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
11094 : RelationGetRelid(pkrel),
11095 : fkconstraint,
11096 : parentConstr,
11097 : indexOid,
11098 : parentInsTrigger, parentUpdTrigger,
11099 : &insertTriggerOid, &updateTriggerOid);
11100 :
11101 1495 : if (rel->rd_rel->relkind == RELKIND_RELATION)
11102 : {
11103 : /*
11104 : * Tell Phase 3 to check that the constraint is satisfied by existing
11105 : * rows. We can skip this during table creation, when constraint is
11106 : * specified as NOT ENFORCED, or when requested explicitly by
11107 : * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11108 : * recreating a constraint following a SET DATA TYPE operation that
11109 : * did not impugn its validity.
11110 : */
11111 1250 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11112 386 : fkconstraint->is_enforced)
11113 : {
11114 : NewConstraint *newcon;
11115 : AlteredTableInfo *tab;
11116 :
11117 386 : tab = ATGetQueueEntry(wqueue, rel);
11118 :
11119 386 : newcon = palloc0_object(NewConstraint);
11120 386 : newcon->name = get_constraint_name(parentConstr);
11121 386 : newcon->contype = CONSTR_FOREIGN;
11122 386 : newcon->refrelid = RelationGetRelid(pkrel);
11123 386 : newcon->refindid = indexOid;
11124 386 : newcon->conid = parentConstr;
11125 386 : newcon->conwithperiod = fkconstraint->fk_with_period;
11126 386 : newcon->qual = (Node *) fkconstraint;
11127 :
11128 386 : tab->constraints = lappend(tab->constraints, newcon);
11129 : }
11130 : }
11131 245 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11132 : {
11133 245 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
11134 : Relation trigrel;
11135 :
11136 : /*
11137 : * Triggers of the foreign keys will be manipulated a bunch of times
11138 : * in the loop below. To avoid repeatedly opening/closing the trigger
11139 : * catalog relation, we open it here and pass it to the subroutines
11140 : * called below.
11141 : */
11142 245 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11143 :
11144 : /*
11145 : * Recurse to take appropriate action on each partition; either we
11146 : * find an existing constraint to reparent to ours, or we create a new
11147 : * one.
11148 : */
11149 436 : for (int i = 0; i < pd->nparts; i++)
11150 : {
11151 194 : Relation partition = table_open(pd->oids[i], lockmode);
11152 : List *partFKs;
11153 : AttrMap *attmap;
11154 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11155 : bool attached;
11156 : ObjectAddress address;
11157 :
11158 194 : CheckAlterTableIsSafe(partition);
11159 :
11160 191 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
11161 : RelationGetDescr(rel),
11162 : false);
11163 493 : for (int j = 0; j < numfks; j++)
11164 302 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11165 :
11166 : /* Check whether an existing constraint can be repurposed */
11167 191 : partFKs = copyObject(RelationGetFKeyList(partition));
11168 191 : attached = false;
11169 391 : foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11170 : {
11171 15 : if (tryAttachPartitionForeignKey(wqueue,
11172 : fk,
11173 : partition,
11174 : parentConstr,
11175 : numfks,
11176 : mapped_fkattnum,
11177 : pkattnum,
11178 : pfeqoperators,
11179 : insertTriggerOid,
11180 : updateTriggerOid,
11181 : trigrel))
11182 : {
11183 6 : attached = true;
11184 6 : break;
11185 : }
11186 : }
11187 191 : if (attached)
11188 : {
11189 6 : table_close(partition, NoLock);
11190 6 : continue;
11191 : }
11192 :
11193 : /*
11194 : * No luck finding a good constraint to reuse; create our own.
11195 : */
11196 185 : address = addFkConstraint(addFkReferencingSide,
11197 : fkconstraint->conname, fkconstraint,
11198 : partition, pkrel, indexOid, parentConstr,
11199 : numfks, pkattnum,
11200 : mapped_fkattnum, pfeqoperators,
11201 : ppeqoperators, ffeqoperators,
11202 : numfkdelsetcols, fkdelsetcols, true,
11203 : with_period);
11204 :
11205 : /* call ourselves to finalize the creation and we're done */
11206 185 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11207 : indexOid,
11208 : address.objectId,
11209 : numfks,
11210 : pkattnum,
11211 : mapped_fkattnum,
11212 : pfeqoperators,
11213 : ppeqoperators,
11214 : ffeqoperators,
11215 : numfkdelsetcols,
11216 : fkdelsetcols,
11217 : old_check_ok,
11218 : lockmode,
11219 : insertTriggerOid,
11220 : updateTriggerOid,
11221 : with_period);
11222 :
11223 185 : table_close(partition, NoLock);
11224 : }
11225 :
11226 242 : table_close(trigrel, RowExclusiveLock);
11227 : }
11228 1492 : }
11229 :
11230 : /*
11231 : * CloneForeignKeyConstraints
11232 : * Clone foreign keys from a partitioned table to a newly acquired
11233 : * partition.
11234 : *
11235 : * partitionRel is a partition of parentRel, so we can be certain that it has
11236 : * the same columns with the same datatypes. The columns may be in different
11237 : * order, though.
11238 : *
11239 : * wqueue must be passed to set up phase 3 constraint checking, unless the
11240 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
11241 : * PARTITION OF).
11242 : */
11243 : static void
11244 5785 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
11245 : Relation partitionRel)
11246 : {
11247 : /* This only works for declarative partitioning */
11248 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11249 :
11250 : /*
11251 : * First, clone constraints where the parent is on the referencing side.
11252 : */
11253 5785 : CloneFkReferencing(wqueue, parentRel, partitionRel);
11254 :
11255 : /*
11256 : * Clone constraints for which the parent is on the referenced side.
11257 : */
11258 5776 : CloneFkReferenced(parentRel, partitionRel);
11259 5776 : }
11260 :
11261 : /*
11262 : * CloneFkReferenced
11263 : * Subroutine for CloneForeignKeyConstraints
11264 : *
11265 : * Find all the FKs that have the parent relation on the referenced side;
11266 : * clone those constraints to the given partition. This is to be called
11267 : * when the partition is being created or attached.
11268 : *
11269 : * This recurses to partitions, if the relation being attached is partitioned.
11270 : * Recursion is done by calling addFkRecurseReferenced.
11271 : */
11272 : static void
11273 5776 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
11274 : {
11275 : Relation pg_constraint;
11276 : AttrMap *attmap;
11277 : ListCell *cell;
11278 : SysScanDesc scan;
11279 : ScanKeyData key[2];
11280 : HeapTuple tuple;
11281 5776 : List *clone = NIL;
11282 : Relation trigrel;
11283 :
11284 : /*
11285 : * Search for any constraints where this partition's parent is in the
11286 : * referenced side. However, we must not clone any constraint whose
11287 : * parent constraint is also going to be cloned, to avoid duplicates. So
11288 : * do it in two steps: first construct the list of constraints to clone,
11289 : * then go over that list cloning those whose parents are not in the list.
11290 : * (We must not rely on the parent being seen first, since the catalog
11291 : * scan could return children first.)
11292 : */
11293 5776 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11294 5776 : ScanKeyInit(&key[0],
11295 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11296 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11297 5776 : ScanKeyInit(&key[1],
11298 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11299 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11300 : /* This is a seqscan, as we don't have a usable index ... */
11301 5776 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11302 : NULL, 2, key);
11303 6019 : while ((tuple = systable_getnext(scan)) != NULL)
11304 : {
11305 243 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11306 :
11307 243 : clone = lappend_oid(clone, constrForm->oid);
11308 : }
11309 5776 : systable_endscan(scan);
11310 5776 : table_close(pg_constraint, RowShareLock);
11311 :
11312 : /*
11313 : * Triggers of the foreign keys will be manipulated a bunch of times in
11314 : * the loop below. To avoid repeatedly opening/closing the trigger
11315 : * catalog relation, we open it here and pass it to the subroutines called
11316 : * below.
11317 : */
11318 5776 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11319 :
11320 5776 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11321 : RelationGetDescr(parentRel),
11322 : false);
11323 6019 : foreach(cell, clone)
11324 : {
11325 243 : Oid constrOid = lfirst_oid(cell);
11326 : Form_pg_constraint constrForm;
11327 : Relation fkRel;
11328 : Oid indexOid;
11329 : Oid partIndexId;
11330 : int numfks;
11331 : AttrNumber conkey[INDEX_MAX_KEYS];
11332 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11333 : AttrNumber confkey[INDEX_MAX_KEYS];
11334 : Oid conpfeqop[INDEX_MAX_KEYS];
11335 : Oid conppeqop[INDEX_MAX_KEYS];
11336 : Oid conffeqop[INDEX_MAX_KEYS];
11337 : int numfkdelsetcols;
11338 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11339 : Constraint *fkconstraint;
11340 : ObjectAddress address;
11341 243 : Oid deleteTriggerOid = InvalidOid,
11342 243 : updateTriggerOid = InvalidOid;
11343 :
11344 243 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11345 243 : if (!HeapTupleIsValid(tuple))
11346 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11347 243 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11348 :
11349 : /*
11350 : * As explained above: don't try to clone a constraint for which we're
11351 : * going to clone the parent.
11352 : */
11353 243 : if (list_member_oid(clone, constrForm->conparentid))
11354 : {
11355 111 : ReleaseSysCache(tuple);
11356 111 : continue;
11357 : }
11358 :
11359 : /* We need the same lock level that CreateTrigger will acquire */
11360 132 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11361 :
11362 132 : indexOid = constrForm->conindid;
11363 132 : DeconstructFkConstraintRow(tuple,
11364 : &numfks,
11365 : conkey,
11366 : confkey,
11367 : conpfeqop,
11368 : conppeqop,
11369 : conffeqop,
11370 : &numfkdelsetcols,
11371 : confdelsetcols);
11372 :
11373 285 : for (int i = 0; i < numfks; i++)
11374 153 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11375 :
11376 132 : fkconstraint = makeNode(Constraint);
11377 132 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11378 132 : fkconstraint->conname = NameStr(constrForm->conname);
11379 132 : fkconstraint->deferrable = constrForm->condeferrable;
11380 132 : fkconstraint->initdeferred = constrForm->condeferred;
11381 132 : fkconstraint->location = -1;
11382 132 : fkconstraint->pktable = NULL;
11383 : /* ->fk_attrs determined below */
11384 132 : fkconstraint->pk_attrs = NIL;
11385 132 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11386 132 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11387 132 : fkconstraint->fk_del_action = constrForm->confdeltype;
11388 132 : fkconstraint->fk_del_set_cols = NIL;
11389 132 : fkconstraint->old_conpfeqop = NIL;
11390 132 : fkconstraint->old_pktable_oid = InvalidOid;
11391 132 : fkconstraint->is_enforced = constrForm->conenforced;
11392 132 : fkconstraint->skip_validation = false;
11393 132 : fkconstraint->initially_valid = constrForm->convalidated;
11394 :
11395 : /* set up colnames that are used to generate the constraint name */
11396 285 : for (int i = 0; i < numfks; i++)
11397 : {
11398 : Form_pg_attribute att;
11399 :
11400 153 : att = TupleDescAttr(RelationGetDescr(fkRel),
11401 153 : conkey[i] - 1);
11402 153 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11403 153 : makeString(NameStr(att->attname)));
11404 : }
11405 :
11406 : /*
11407 : * Add the new foreign key constraint pointing to the new partition.
11408 : * Because this new partition appears in the referenced side of the
11409 : * constraint, we don't need to set up for Phase 3 check.
11410 : */
11411 132 : partIndexId = index_get_partition(partitionRel, indexOid);
11412 132 : if (!OidIsValid(partIndexId))
11413 0 : elog(ERROR, "index for %u not found in partition %s",
11414 : indexOid, RelationGetRelationName(partitionRel));
11415 :
11416 : /*
11417 : * Get the "action" triggers belonging to the constraint to pass as
11418 : * parent OIDs for similar triggers that will be created on the
11419 : * partition in addFkRecurseReferenced().
11420 : */
11421 132 : if (constrForm->conenforced)
11422 129 : GetForeignKeyActionTriggers(trigrel, constrOid,
11423 : constrForm->confrelid, constrForm->conrelid,
11424 : &deleteTriggerOid, &updateTriggerOid);
11425 :
11426 : /* Add this constraint ... */
11427 132 : address = addFkConstraint(addFkReferencedSide,
11428 : fkconstraint->conname, fkconstraint, fkRel,
11429 : partitionRel, partIndexId, constrOid,
11430 : numfks, mapped_confkey,
11431 : conkey, conpfeqop, conppeqop, conffeqop,
11432 : numfkdelsetcols, confdelsetcols, false,
11433 132 : constrForm->conperiod);
11434 : /* ... and recurse */
11435 132 : addFkRecurseReferenced(fkconstraint,
11436 : fkRel,
11437 : partitionRel,
11438 : partIndexId,
11439 : address.objectId,
11440 : numfks,
11441 : mapped_confkey,
11442 : conkey,
11443 : conpfeqop,
11444 : conppeqop,
11445 : conffeqop,
11446 : numfkdelsetcols,
11447 : confdelsetcols,
11448 : true,
11449 : deleteTriggerOid,
11450 : updateTriggerOid,
11451 132 : constrForm->conperiod);
11452 :
11453 132 : table_close(fkRel, NoLock);
11454 132 : ReleaseSysCache(tuple);
11455 : }
11456 :
11457 5776 : table_close(trigrel, RowExclusiveLock);
11458 5776 : }
11459 :
11460 : /*
11461 : * CloneFkReferencing
11462 : * Subroutine for CloneForeignKeyConstraints
11463 : *
11464 : * For each FK constraint of the parent relation in the given list, find an
11465 : * equivalent constraint in its partition relation that can be reparented;
11466 : * if one cannot be found, create a new constraint in the partition as its
11467 : * child.
11468 : *
11469 : * If wqueue is given, it is used to set up phase-3 verification for each
11470 : * cloned constraint; omit it if such verification is not needed
11471 : * (example: the partition is being created anew).
11472 : */
11473 : static void
11474 5785 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11475 : {
11476 : AttrMap *attmap;
11477 : List *partFKs;
11478 5785 : List *clone = NIL;
11479 : ListCell *cell;
11480 : Relation trigrel;
11481 :
11482 : /* obtain a list of constraints that we need to clone */
11483 6461 : foreach(cell, RelationGetFKeyList(parentRel))
11484 : {
11485 679 : ForeignKeyCacheInfo *fk = lfirst(cell);
11486 :
11487 : /*
11488 : * Refuse to attach a table as partition that this partitioned table
11489 : * already has a foreign key to. This isn't useful schema, which is
11490 : * proven by the fact that there have been no user complaints that
11491 : * it's already impossible to achieve this in the opposite direction,
11492 : * i.e., creating a foreign key that references a partition. This
11493 : * restriction allows us to dodge some complexities around
11494 : * pg_constraint and pg_trigger row creations that would be needed
11495 : * during ATTACH/DETACH for this kind of relationship.
11496 : */
11497 679 : if (fk->confrelid == RelationGetRelid(partRel))
11498 3 : ereport(ERROR,
11499 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11500 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11501 : RelationGetRelationName(partRel),
11502 : get_constraint_name(fk->conoid))));
11503 :
11504 676 : clone = lappend_oid(clone, fk->conoid);
11505 : }
11506 :
11507 : /*
11508 : * Silently do nothing if there's nothing to do. In particular, this
11509 : * avoids throwing a spurious error for foreign tables.
11510 : */
11511 5782 : if (clone == NIL)
11512 5486 : return;
11513 :
11514 296 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11515 0 : ereport(ERROR,
11516 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11517 : errmsg("foreign key constraints are not supported on foreign tables")));
11518 :
11519 : /*
11520 : * Triggers of the foreign keys will be manipulated a bunch of times in
11521 : * the loop below. To avoid repeatedly opening/closing the trigger
11522 : * catalog relation, we open it here and pass it to the subroutines called
11523 : * below.
11524 : */
11525 296 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11526 :
11527 : /*
11528 : * The constraint key may differ, if the columns in the partition are
11529 : * different. This map is used to convert them.
11530 : */
11531 296 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11532 : RelationGetDescr(parentRel),
11533 : false);
11534 :
11535 296 : partFKs = copyObject(RelationGetFKeyList(partRel));
11536 :
11537 966 : foreach(cell, clone)
11538 : {
11539 676 : Oid parentConstrOid = lfirst_oid(cell);
11540 : Form_pg_constraint constrForm;
11541 : Relation pkrel;
11542 : HeapTuple tuple;
11543 : int numfks;
11544 : AttrNumber conkey[INDEX_MAX_KEYS];
11545 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11546 : AttrNumber confkey[INDEX_MAX_KEYS];
11547 : Oid conpfeqop[INDEX_MAX_KEYS];
11548 : Oid conppeqop[INDEX_MAX_KEYS];
11549 : Oid conffeqop[INDEX_MAX_KEYS];
11550 : int numfkdelsetcols;
11551 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11552 : Constraint *fkconstraint;
11553 : bool attached;
11554 : Oid indexOid;
11555 : ObjectAddress address;
11556 : ListCell *lc;
11557 676 : Oid insertTriggerOid = InvalidOid,
11558 676 : updateTriggerOid = InvalidOid;
11559 : bool with_period;
11560 :
11561 676 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11562 676 : if (!HeapTupleIsValid(tuple))
11563 0 : elog(ERROR, "cache lookup failed for constraint %u",
11564 : parentConstrOid);
11565 676 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11566 :
11567 : /* Don't clone constraints whose parents are being cloned */
11568 676 : if (list_member_oid(clone, constrForm->conparentid))
11569 : {
11570 362 : ReleaseSysCache(tuple);
11571 437 : continue;
11572 : }
11573 :
11574 : /*
11575 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11576 : * relation, that means to lock all partitions.
11577 : */
11578 314 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11579 314 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11580 125 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11581 : ShareRowExclusiveLock, NULL);
11582 :
11583 314 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11584 : conpfeqop, conppeqop, conffeqop,
11585 : &numfkdelsetcols, confdelsetcols);
11586 745 : for (int i = 0; i < numfks; i++)
11587 431 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11588 :
11589 : /*
11590 : * Get the "check" triggers belonging to the constraint, if it is
11591 : * ENFORCED, to pass as parent OIDs for similar triggers that will be
11592 : * created on the partition in addFkRecurseReferencing(). They are
11593 : * also passed to tryAttachPartitionForeignKey() below to simply
11594 : * assign as parents to the partition's existing "check" triggers,
11595 : * that is, if the corresponding constraints is deemed attachable to
11596 : * the parent constraint.
11597 : */
11598 314 : if (constrForm->conenforced)
11599 308 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11600 : constrForm->confrelid, constrForm->conrelid,
11601 : &insertTriggerOid, &updateTriggerOid);
11602 :
11603 : /*
11604 : * Before creating a new constraint, see whether any existing FKs are
11605 : * fit for the purpose. If one is, attach the parent constraint to
11606 : * it, and don't clone anything. This way we avoid the expensive
11607 : * verification step and don't end up with a duplicate FK, and we
11608 : * don't need to recurse to partitions for this constraint.
11609 : */
11610 314 : attached = false;
11611 359 : foreach(lc, partFKs)
11612 : {
11613 123 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11614 :
11615 123 : if (tryAttachPartitionForeignKey(wqueue,
11616 : fk,
11617 : partRel,
11618 : parentConstrOid,
11619 : numfks,
11620 : mapped_conkey,
11621 : confkey,
11622 : conpfeqop,
11623 : insertTriggerOid,
11624 : updateTriggerOid,
11625 : trigrel))
11626 : {
11627 75 : attached = true;
11628 75 : table_close(pkrel, NoLock);
11629 75 : break;
11630 : }
11631 : }
11632 311 : if (attached)
11633 : {
11634 75 : ReleaseSysCache(tuple);
11635 75 : continue;
11636 : }
11637 :
11638 : /* No dice. Set up to create our own constraint */
11639 236 : fkconstraint = makeNode(Constraint);
11640 236 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11641 : /* ->conname determined below */
11642 236 : fkconstraint->deferrable = constrForm->condeferrable;
11643 236 : fkconstraint->initdeferred = constrForm->condeferred;
11644 236 : fkconstraint->location = -1;
11645 236 : fkconstraint->pktable = NULL;
11646 : /* ->fk_attrs determined below */
11647 236 : fkconstraint->pk_attrs = NIL;
11648 236 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11649 236 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11650 236 : fkconstraint->fk_del_action = constrForm->confdeltype;
11651 236 : fkconstraint->fk_del_set_cols = NIL;
11652 236 : fkconstraint->old_conpfeqop = NIL;
11653 236 : fkconstraint->old_pktable_oid = InvalidOid;
11654 236 : fkconstraint->is_enforced = constrForm->conenforced;
11655 236 : fkconstraint->skip_validation = false;
11656 236 : fkconstraint->initially_valid = constrForm->convalidated;
11657 532 : for (int i = 0; i < numfks; i++)
11658 : {
11659 : Form_pg_attribute att;
11660 :
11661 296 : att = TupleDescAttr(RelationGetDescr(partRel),
11662 296 : mapped_conkey[i] - 1);
11663 296 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11664 296 : makeString(NameStr(att->attname)));
11665 : }
11666 :
11667 236 : indexOid = constrForm->conindid;
11668 236 : with_period = constrForm->conperiod;
11669 :
11670 : /* Create the pg_constraint entry at this level */
11671 236 : address = addFkConstraint(addFkReferencingSide,
11672 236 : NameStr(constrForm->conname), fkconstraint,
11673 : partRel, pkrel, indexOid, parentConstrOid,
11674 : numfks, confkey,
11675 : mapped_conkey, conpfeqop,
11676 : conppeqop, conffeqop,
11677 : numfkdelsetcols, confdelsetcols,
11678 : false, with_period);
11679 :
11680 : /* Done with the cloned constraint's tuple */
11681 236 : ReleaseSysCache(tuple);
11682 :
11683 : /* Create the check triggers, and recurse to partitions, if any */
11684 236 : addFkRecurseReferencing(wqueue,
11685 : fkconstraint,
11686 : partRel,
11687 : pkrel,
11688 : indexOid,
11689 : address.objectId,
11690 : numfks,
11691 : confkey,
11692 : mapped_conkey,
11693 : conpfeqop,
11694 : conppeqop,
11695 : conffeqop,
11696 : numfkdelsetcols,
11697 : confdelsetcols,
11698 : false, /* no old check exists */
11699 : AccessExclusiveLock,
11700 : insertTriggerOid,
11701 : updateTriggerOid,
11702 : with_period);
11703 233 : table_close(pkrel, NoLock);
11704 : }
11705 :
11706 290 : table_close(trigrel, RowExclusiveLock);
11707 : }
11708 :
11709 : /*
11710 : * When the parent of a partition receives [the referencing side of] a foreign
11711 : * key, we must propagate that foreign key to the partition. However, the
11712 : * partition might already have an equivalent foreign key; this routine
11713 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11714 : * by the other parameters. If they are equivalent, create the link between
11715 : * the two constraints and return true.
11716 : *
11717 : * If the given FK does not match the one defined by rest of the params,
11718 : * return false.
11719 : */
11720 : static bool
11721 138 : tryAttachPartitionForeignKey(List **wqueue,
11722 : ForeignKeyCacheInfo *fk,
11723 : Relation partition,
11724 : Oid parentConstrOid,
11725 : int numfks,
11726 : AttrNumber *mapped_conkey,
11727 : AttrNumber *confkey,
11728 : Oid *conpfeqop,
11729 : Oid parentInsTrigger,
11730 : Oid parentUpdTrigger,
11731 : Relation trigrel)
11732 : {
11733 : HeapTuple parentConstrTup;
11734 : Form_pg_constraint parentConstr;
11735 : HeapTuple partcontup;
11736 : Form_pg_constraint partConstr;
11737 :
11738 138 : parentConstrTup = SearchSysCache1(CONSTROID,
11739 : ObjectIdGetDatum(parentConstrOid));
11740 138 : if (!HeapTupleIsValid(parentConstrTup))
11741 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11742 138 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11743 :
11744 : /*
11745 : * Do some quick & easy initial checks. If any of these fail, we cannot
11746 : * use this constraint.
11747 : */
11748 138 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11749 : {
11750 0 : ReleaseSysCache(parentConstrTup);
11751 0 : return false;
11752 : }
11753 384 : for (int i = 0; i < numfks; i++)
11754 : {
11755 246 : if (fk->conkey[i] != mapped_conkey[i] ||
11756 246 : fk->confkey[i] != confkey[i] ||
11757 246 : fk->conpfeqop[i] != conpfeqop[i])
11758 : {
11759 0 : ReleaseSysCache(parentConstrTup);
11760 0 : return false;
11761 : }
11762 : }
11763 :
11764 : /* Looks good so far; perform more extensive checks. */
11765 138 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11766 138 : if (!HeapTupleIsValid(partcontup))
11767 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11768 138 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11769 :
11770 : /*
11771 : * An error should be raised if the constraint enforceability is
11772 : * different. Returning false without raising an error, as we do for other
11773 : * attributes, could lead to a duplicate constraint with the same
11774 : * enforceability as the parent. While this may be acceptable, it may not
11775 : * be ideal. Therefore, it's better to raise an error and allow the user
11776 : * to correct the enforceability before proceeding.
11777 : */
11778 138 : if (partConstr->conenforced != parentConstr->conenforced)
11779 3 : ereport(ERROR,
11780 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11781 : errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11782 : NameStr(parentConstr->conname),
11783 : NameStr(partConstr->conname),
11784 : RelationGetRelationName(partition))));
11785 :
11786 135 : if (OidIsValid(partConstr->conparentid) ||
11787 120 : partConstr->condeferrable != parentConstr->condeferrable ||
11788 106 : partConstr->condeferred != parentConstr->condeferred ||
11789 106 : partConstr->confupdtype != parentConstr->confupdtype ||
11790 88 : partConstr->confdeltype != parentConstr->confdeltype ||
11791 88 : partConstr->confmatchtype != parentConstr->confmatchtype)
11792 : {
11793 54 : ReleaseSysCache(parentConstrTup);
11794 54 : ReleaseSysCache(partcontup);
11795 54 : return false;
11796 : }
11797 :
11798 81 : ReleaseSysCache(parentConstrTup);
11799 81 : ReleaseSysCache(partcontup);
11800 :
11801 : /* Looks good! Attach this constraint. */
11802 81 : AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11803 : parentConstrOid, parentInsTrigger,
11804 : parentUpdTrigger, trigrel);
11805 :
11806 81 : return true;
11807 : }
11808 :
11809 : /*
11810 : * AttachPartitionForeignKey
11811 : *
11812 : * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11813 : * attaching the constraint, removing redundant triggers and entries from
11814 : * pg_constraint, and setting the constraint's parent.
11815 : */
11816 : static void
11817 81 : AttachPartitionForeignKey(List **wqueue,
11818 : Relation partition,
11819 : Oid partConstrOid,
11820 : Oid parentConstrOid,
11821 : Oid parentInsTrigger,
11822 : Oid parentUpdTrigger,
11823 : Relation trigrel)
11824 : {
11825 : HeapTuple parentConstrTup;
11826 : Form_pg_constraint parentConstr;
11827 : HeapTuple partcontup;
11828 : Form_pg_constraint partConstr;
11829 : bool queueValidation;
11830 : Oid partConstrFrelid;
11831 : Oid partConstrRelid;
11832 : bool parentConstrIsEnforced;
11833 :
11834 : /* Fetch the parent constraint tuple */
11835 81 : parentConstrTup = SearchSysCache1(CONSTROID,
11836 : ObjectIdGetDatum(parentConstrOid));
11837 81 : if (!HeapTupleIsValid(parentConstrTup))
11838 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11839 81 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11840 81 : parentConstrIsEnforced = parentConstr->conenforced;
11841 :
11842 : /* Fetch the child constraint tuple */
11843 81 : partcontup = SearchSysCache1(CONSTROID,
11844 : ObjectIdGetDatum(partConstrOid));
11845 81 : if (!HeapTupleIsValid(partcontup))
11846 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11847 81 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11848 81 : partConstrFrelid = partConstr->confrelid;
11849 81 : partConstrRelid = partConstr->conrelid;
11850 :
11851 : /*
11852 : * If the referenced table is partitioned, then the partition we're
11853 : * attaching now has extra pg_constraint rows and action triggers that are
11854 : * no longer needed. Remove those.
11855 : */
11856 81 : if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11857 : {
11858 18 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11859 :
11860 18 : RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11861 : partConstrRelid);
11862 :
11863 18 : table_close(pg_constraint, RowShareLock);
11864 : }
11865 :
11866 : /*
11867 : * Will we need to validate this constraint? A valid parent constraint
11868 : * implies that all child constraints have been validated, so if this one
11869 : * isn't, we must trigger phase 3 validation.
11870 : */
11871 81 : queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11872 :
11873 81 : ReleaseSysCache(partcontup);
11874 81 : ReleaseSysCache(parentConstrTup);
11875 :
11876 : /*
11877 : * The action triggers in the new partition become redundant -- the parent
11878 : * table already has equivalent ones, and those will be able to reach the
11879 : * partition. Remove the ones in the partition. We identify them because
11880 : * they have our constraint OID, as well as being on the referenced rel.
11881 : */
11882 81 : DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11883 : partConstrRelid);
11884 :
11885 81 : ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11886 : RelationGetRelid(partition));
11887 :
11888 : /*
11889 : * Like the constraint, attach partition's "check" triggers to the
11890 : * corresponding parent triggers if the constraint is ENFORCED. NOT
11891 : * ENFORCED constraints do not have these triggers.
11892 : */
11893 81 : if (parentConstrIsEnforced)
11894 : {
11895 : Oid insertTriggerOid,
11896 : updateTriggerOid;
11897 :
11898 75 : GetForeignKeyCheckTriggers(trigrel,
11899 : partConstrOid, partConstrFrelid, partConstrRelid,
11900 : &insertTriggerOid, &updateTriggerOid);
11901 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11902 75 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11903 : RelationGetRelid(partition));
11904 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11905 75 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11906 : RelationGetRelid(partition));
11907 : }
11908 :
11909 : /*
11910 : * We updated this pg_constraint row above to set its parent; validating
11911 : * it will cause its convalidated flag to change, so we need CCI here. In
11912 : * addition, we need it unconditionally for the rare case where the parent
11913 : * table has *two* identical constraints; when reaching this function for
11914 : * the second one, we must have made our changes visible, otherwise we
11915 : * would try to attach both to this one.
11916 : */
11917 81 : CommandCounterIncrement();
11918 :
11919 : /* If validation is needed, put it in the queue now. */
11920 81 : if (queueValidation)
11921 : {
11922 : Relation conrel;
11923 : Oid confrelid;
11924 :
11925 9 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11926 :
11927 9 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11928 9 : if (!HeapTupleIsValid(partcontup))
11929 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11930 :
11931 9 : confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11932 :
11933 : /* Use the same lock as for AT_ValidateConstraint */
11934 9 : QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11935 : partcontup, ShareUpdateExclusiveLock);
11936 9 : ReleaseSysCache(partcontup);
11937 9 : table_close(conrel, RowExclusiveLock);
11938 : }
11939 81 : }
11940 :
11941 : /*
11942 : * RemoveInheritedConstraint
11943 : *
11944 : * Removes the constraint and its associated trigger from the specified
11945 : * relation, which inherited the given constraint.
11946 : */
11947 : static void
11948 18 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
11949 : Oid conrelid)
11950 : {
11951 : ObjectAddresses *objs;
11952 : HeapTuple consttup;
11953 : ScanKeyData key;
11954 : SysScanDesc scan;
11955 : HeapTuple trigtup;
11956 :
11957 18 : ScanKeyInit(&key,
11958 : Anum_pg_constraint_conrelid,
11959 : BTEqualStrategyNumber, F_OIDEQ,
11960 : ObjectIdGetDatum(conrelid));
11961 :
11962 18 : scan = systable_beginscan(conrel,
11963 : ConstraintRelidTypidNameIndexId,
11964 : true, NULL, 1, &key);
11965 18 : objs = new_object_addresses();
11966 162 : while ((consttup = systable_getnext(scan)) != NULL)
11967 : {
11968 144 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11969 :
11970 144 : if (conform->conparentid != conoid)
11971 105 : continue;
11972 : else
11973 : {
11974 : ObjectAddress addr;
11975 : SysScanDesc scan2;
11976 : ScanKeyData key2;
11977 : int n PG_USED_FOR_ASSERTS_ONLY;
11978 :
11979 39 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11980 39 : add_exact_object_address(&addr, objs);
11981 :
11982 : /*
11983 : * First we must delete the dependency record that binds the
11984 : * constraint records together.
11985 : */
11986 39 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11987 : conform->oid,
11988 : DEPENDENCY_INTERNAL,
11989 : ConstraintRelationId,
11990 : conoid);
11991 : Assert(n == 1); /* actually only one is expected */
11992 :
11993 : /*
11994 : * Now search for the triggers for this constraint and set them up
11995 : * for deletion too
11996 : */
11997 39 : ScanKeyInit(&key2,
11998 : Anum_pg_trigger_tgconstraint,
11999 : BTEqualStrategyNumber, F_OIDEQ,
12000 : ObjectIdGetDatum(conform->oid));
12001 39 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
12002 : true, NULL, 1, &key2);
12003 117 : while ((trigtup = systable_getnext(scan2)) != NULL)
12004 : {
12005 78 : ObjectAddressSet(addr, TriggerRelationId,
12006 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
12007 78 : add_exact_object_address(&addr, objs);
12008 : }
12009 39 : systable_endscan(scan2);
12010 : }
12011 : }
12012 : /* make the dependency deletions visible */
12013 18 : CommandCounterIncrement();
12014 18 : performMultipleDeletions(objs, DROP_RESTRICT,
12015 : PERFORM_DELETION_INTERNAL);
12016 18 : systable_endscan(scan);
12017 18 : }
12018 :
12019 : /*
12020 : * DropForeignKeyConstraintTriggers
12021 : *
12022 : * The subroutine for tryAttachPartitionForeignKey handles the deletion of
12023 : * action triggers for the foreign key constraint.
12024 : *
12025 : * If valid confrelid and conrelid values are not provided, the respective
12026 : * trigger check will be skipped, and the trigger will be considered for
12027 : * removal.
12028 : */
12029 : static void
12030 117 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
12031 : Oid conrelid)
12032 : {
12033 : ScanKeyData key;
12034 : SysScanDesc scan;
12035 : HeapTuple trigtup;
12036 :
12037 117 : ScanKeyInit(&key,
12038 : Anum_pg_trigger_tgconstraint,
12039 : BTEqualStrategyNumber, F_OIDEQ,
12040 : ObjectIdGetDatum(conoid));
12041 117 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12042 : NULL, 1, &key);
12043 507 : while ((trigtup = systable_getnext(scan)) != NULL)
12044 : {
12045 390 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12046 : ObjectAddress trigger;
12047 :
12048 : /* Invalid if trigger is not for a referential integrity constraint */
12049 390 : if (!OidIsValid(trgform->tgconstrrelid))
12050 150 : continue;
12051 390 : if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12052 150 : continue;
12053 240 : if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12054 0 : continue;
12055 :
12056 : /* We should be dropping trigger related to foreign key constraint */
12057 : Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12058 : trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12059 : trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12060 : trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12061 : trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12062 : trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12063 : trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12064 : trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12065 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12066 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12067 : trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12068 : trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12069 :
12070 : /*
12071 : * The constraint is originally set up to contain this trigger as an
12072 : * implementation object, so there's a dependency record that links
12073 : * the two; however, since the trigger is no longer needed, we remove
12074 : * the dependency link in order to be able to drop the trigger while
12075 : * keeping the constraint intact.
12076 : */
12077 240 : deleteDependencyRecordsFor(TriggerRelationId,
12078 : trgform->oid,
12079 : false);
12080 : /* make dependency deletion visible to performDeletion */
12081 240 : CommandCounterIncrement();
12082 240 : ObjectAddressSet(trigger, TriggerRelationId,
12083 : trgform->oid);
12084 240 : performDeletion(&trigger, DROP_RESTRICT, 0);
12085 : /* make trigger drop visible, in case the loop iterates */
12086 240 : CommandCounterIncrement();
12087 : }
12088 :
12089 117 : systable_endscan(scan);
12090 117 : }
12091 :
12092 : /*
12093 : * GetForeignKeyActionTriggers
12094 : * Returns delete and update "action" triggers of the given relation
12095 : * belonging to the given constraint
12096 : */
12097 : static void
12098 129 : GetForeignKeyActionTriggers(Relation trigrel,
12099 : Oid conoid, Oid confrelid, Oid conrelid,
12100 : Oid *deleteTriggerOid,
12101 : Oid *updateTriggerOid)
12102 : {
12103 : ScanKeyData key;
12104 : SysScanDesc scan;
12105 : HeapTuple trigtup;
12106 :
12107 129 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12108 129 : ScanKeyInit(&key,
12109 : Anum_pg_trigger_tgconstraint,
12110 : BTEqualStrategyNumber, F_OIDEQ,
12111 : ObjectIdGetDatum(conoid));
12112 :
12113 129 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12114 : NULL, 1, &key);
12115 268 : while ((trigtup = systable_getnext(scan)) != NULL)
12116 : {
12117 268 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12118 :
12119 268 : if (trgform->tgconstrrelid != conrelid)
12120 5 : continue;
12121 263 : if (trgform->tgrelid != confrelid)
12122 0 : continue;
12123 : /* Only ever look at "action" triggers on the PK side. */
12124 263 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12125 5 : continue;
12126 258 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
12127 : {
12128 : Assert(*deleteTriggerOid == InvalidOid);
12129 129 : *deleteTriggerOid = trgform->oid;
12130 : }
12131 129 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12132 : {
12133 : Assert(*updateTriggerOid == InvalidOid);
12134 129 : *updateTriggerOid = trgform->oid;
12135 : }
12136 : #ifndef USE_ASSERT_CHECKING
12137 : /* In an assert-enabled build, continue looking to find duplicates */
12138 258 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12139 129 : break;
12140 : #endif
12141 : }
12142 :
12143 129 : if (!OidIsValid(*deleteTriggerOid))
12144 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12145 : conoid);
12146 129 : if (!OidIsValid(*updateTriggerOid))
12147 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12148 : conoid);
12149 :
12150 129 : systable_endscan(scan);
12151 129 : }
12152 :
12153 : /*
12154 : * GetForeignKeyCheckTriggers
12155 : * Returns insert and update "check" triggers of the given relation
12156 : * belonging to the given constraint
12157 : */
12158 : static void
12159 437 : GetForeignKeyCheckTriggers(Relation trigrel,
12160 : Oid conoid, Oid confrelid, Oid conrelid,
12161 : Oid *insertTriggerOid,
12162 : Oid *updateTriggerOid)
12163 : {
12164 : ScanKeyData key;
12165 : SysScanDesc scan;
12166 : HeapTuple trigtup;
12167 :
12168 437 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
12169 437 : ScanKeyInit(&key,
12170 : Anum_pg_trigger_tgconstraint,
12171 : BTEqualStrategyNumber, F_OIDEQ,
12172 : ObjectIdGetDatum(conoid));
12173 :
12174 437 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12175 : NULL, 1, &key);
12176 1418 : while ((trigtup = systable_getnext(scan)) != NULL)
12177 : {
12178 1418 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12179 :
12180 1418 : if (trgform->tgconstrrelid != confrelid)
12181 492 : continue;
12182 926 : if (trgform->tgrelid != conrelid)
12183 0 : continue;
12184 : /* Only ever look at "check" triggers on the FK side. */
12185 926 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12186 52 : continue;
12187 874 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
12188 : {
12189 : Assert(*insertTriggerOid == InvalidOid);
12190 437 : *insertTriggerOid = trgform->oid;
12191 : }
12192 437 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12193 : {
12194 : Assert(*updateTriggerOid == InvalidOid);
12195 437 : *updateTriggerOid = trgform->oid;
12196 : }
12197 : #ifndef USE_ASSERT_CHECKING
12198 : /* In an assert-enabled build, continue looking to find duplicates. */
12199 874 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12200 437 : break;
12201 : #endif
12202 : }
12203 :
12204 437 : if (!OidIsValid(*insertTriggerOid))
12205 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12206 : conoid);
12207 437 : if (!OidIsValid(*updateTriggerOid))
12208 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12209 : conoid);
12210 :
12211 437 : systable_endscan(scan);
12212 437 : }
12213 :
12214 : /*
12215 : * ALTER TABLE ALTER CONSTRAINT
12216 : *
12217 : * Update the attributes of a constraint.
12218 : *
12219 : * Currently only works for Foreign Key and not null constraints.
12220 : *
12221 : * If the constraint is modified, returns its address; otherwise, return
12222 : * InvalidObjectAddress.
12223 : */
12224 : static ObjectAddress
12225 147 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
12226 : bool recurse, LOCKMODE lockmode)
12227 : {
12228 : Relation conrel;
12229 : Relation tgrel;
12230 : SysScanDesc scan;
12231 : ScanKeyData skey[3];
12232 : HeapTuple contuple;
12233 : Form_pg_constraint currcon;
12234 : ObjectAddress address;
12235 :
12236 : /*
12237 : * Disallow altering ONLY a partitioned table, as it would make no sense.
12238 : * This is okay for legacy inheritance.
12239 : */
12240 147 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12241 0 : ereport(ERROR,
12242 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12243 : errmsg("constraint must be altered in child tables too"),
12244 : errhint("Do not specify the ONLY keyword."));
12245 :
12246 :
12247 147 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12248 147 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12249 :
12250 : /*
12251 : * Find and check the target constraint
12252 : */
12253 147 : ScanKeyInit(&skey[0],
12254 : Anum_pg_constraint_conrelid,
12255 : BTEqualStrategyNumber, F_OIDEQ,
12256 : ObjectIdGetDatum(RelationGetRelid(rel)));
12257 147 : ScanKeyInit(&skey[1],
12258 : Anum_pg_constraint_contypid,
12259 : BTEqualStrategyNumber, F_OIDEQ,
12260 : ObjectIdGetDatum(InvalidOid));
12261 147 : ScanKeyInit(&skey[2],
12262 : Anum_pg_constraint_conname,
12263 : BTEqualStrategyNumber, F_NAMEEQ,
12264 147 : CStringGetDatum(cmdcon->conname));
12265 147 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12266 : true, NULL, 3, skey);
12267 :
12268 : /* There can be at most one matching row */
12269 147 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12270 3 : ereport(ERROR,
12271 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12272 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12273 : cmdcon->conname, RelationGetRelationName(rel))));
12274 :
12275 144 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12276 144 : if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12277 0 : ereport(ERROR,
12278 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12279 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12280 : cmdcon->conname, RelationGetRelationName(rel))));
12281 144 : if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12282 6 : ereport(ERROR,
12283 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12284 : errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12285 : cmdcon->conname, RelationGetRelationName(rel))));
12286 138 : if (cmdcon->alterInheritability &&
12287 45 : currcon->contype != CONSTRAINT_NOTNULL)
12288 12 : ereport(ERROR,
12289 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12290 : errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12291 : cmdcon->conname, RelationGetRelationName(rel)));
12292 :
12293 : /* Refuse to modify inheritability of inherited constraints */
12294 126 : if (cmdcon->alterInheritability &&
12295 33 : cmdcon->noinherit && currcon->coninhcount > 0)
12296 3 : ereport(ERROR,
12297 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12298 : errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12299 : NameStr(currcon->conname),
12300 : RelationGetRelationName(rel)));
12301 :
12302 : /*
12303 : * If it's not the topmost constraint, raise an error.
12304 : *
12305 : * Altering a non-topmost constraint leaves some triggers untouched, since
12306 : * they are not directly connected to this constraint; also, pg_dump would
12307 : * ignore the deferrability status of the individual constraint, since it
12308 : * only dumps topmost constraints. Avoid these problems by refusing this
12309 : * operation and telling the user to alter the parent constraint instead.
12310 : */
12311 123 : if (OidIsValid(currcon->conparentid))
12312 : {
12313 : HeapTuple tp;
12314 6 : Oid parent = currcon->conparentid;
12315 6 : char *ancestorname = NULL;
12316 6 : char *ancestortable = NULL;
12317 :
12318 : /* Loop to find the topmost constraint */
12319 12 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12320 : {
12321 12 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
12322 :
12323 : /* If no parent, this is the constraint we want */
12324 12 : if (!OidIsValid(contup->conparentid))
12325 : {
12326 6 : ancestorname = pstrdup(NameStr(contup->conname));
12327 6 : ancestortable = get_rel_name(contup->conrelid);
12328 6 : ReleaseSysCache(tp);
12329 6 : break;
12330 : }
12331 :
12332 6 : parent = contup->conparentid;
12333 6 : ReleaseSysCache(tp);
12334 : }
12335 :
12336 6 : ereport(ERROR,
12337 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12338 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12339 : cmdcon->conname, RelationGetRelationName(rel)),
12340 : ancestorname && ancestortable ?
12341 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12342 : cmdcon->conname, ancestorname, ancestortable) : 0,
12343 : errhint("You may alter the constraint it derives from instead.")));
12344 : }
12345 :
12346 117 : address = InvalidObjectAddress;
12347 :
12348 : /*
12349 : * Do the actual catalog work, and recurse if necessary.
12350 : */
12351 117 : if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12352 : contuple, recurse, lockmode))
12353 111 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12354 :
12355 114 : systable_endscan(scan);
12356 :
12357 114 : table_close(tgrel, RowExclusiveLock);
12358 114 : table_close(conrel, RowExclusiveLock);
12359 :
12360 114 : return address;
12361 : }
12362 :
12363 : /*
12364 : * A subroutine of ATExecAlterConstraint that calls the respective routines for
12365 : * altering constraint's enforceability, deferrability or inheritability.
12366 : */
12367 : static bool
12368 117 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
12369 : Relation conrel, Relation tgrel, Relation rel,
12370 : HeapTuple contuple, bool recurse,
12371 : LOCKMODE lockmode)
12372 : {
12373 : Form_pg_constraint currcon;
12374 117 : bool changed = false;
12375 117 : List *otherrelids = NIL;
12376 :
12377 117 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12378 :
12379 : /*
12380 : * Do the catalog work for the enforceability or deferrability change,
12381 : * recurse if necessary.
12382 : *
12383 : * Note that even if deferrability is requested to be altered along with
12384 : * enforceability, we don't need to explicitly update multiple entries in
12385 : * pg_trigger related to deferrability.
12386 : *
12387 : * Modifying enforceability involves either creating or dropping the
12388 : * trigger, during which the deferrability setting will be adjusted
12389 : * automatically.
12390 : */
12391 156 : if (cmdcon->alterEnforceability &&
12392 39 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12393 : currcon->conrelid, currcon->confrelid,
12394 : contuple, lockmode, InvalidOid,
12395 : InvalidOid, InvalidOid, InvalidOid))
12396 36 : changed = true;
12397 :
12398 129 : else if (cmdcon->alterDeferrability &&
12399 48 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12400 : contuple, recurse, &otherrelids,
12401 : lockmode))
12402 : {
12403 : /*
12404 : * AlterConstrUpdateConstraintEntry already invalidated relcache for
12405 : * the relations having the constraint itself; here we also invalidate
12406 : * for relations that have any triggers that are part of the
12407 : * constraint.
12408 : */
12409 153 : foreach_oid(relid, otherrelids)
12410 57 : CacheInvalidateRelcacheByRelid(relid);
12411 :
12412 48 : changed = true;
12413 : }
12414 :
12415 : /*
12416 : * Do the catalog work for the inheritability change.
12417 : */
12418 144 : if (cmdcon->alterInheritability &&
12419 30 : ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12420 : lockmode))
12421 27 : changed = true;
12422 :
12423 114 : return changed;
12424 : }
12425 :
12426 : /*
12427 : * Returns true if the constraint's enforceability is altered.
12428 : *
12429 : * Depending on whether the constraint is being set to ENFORCED or NOT
12430 : * ENFORCED, it creates or drops the trigger accordingly.
12431 : *
12432 : * Note that we must recurse even when trying to change a constraint to not
12433 : * enforced if it is already not enforced, in case descendant constraints
12434 : * might be enforced and need to be changed to not enforced. Conversely, we
12435 : * should do nothing if a constraint is being set to enforced and is already
12436 : * enforced, as descendant constraints cannot be different in that case.
12437 : */
12438 : static bool
12439 90 : ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
12440 : Relation conrel, Relation tgrel,
12441 : Oid fkrelid, Oid pkrelid,
12442 : HeapTuple contuple, LOCKMODE lockmode,
12443 : Oid ReferencedParentDelTrigger,
12444 : Oid ReferencedParentUpdTrigger,
12445 : Oid ReferencingParentInsTrigger,
12446 : Oid ReferencingParentUpdTrigger)
12447 : {
12448 : Form_pg_constraint currcon;
12449 : Oid conoid;
12450 : Relation rel;
12451 90 : bool changed = false;
12452 :
12453 : /* Since this function recurses, it could be driven to stack overflow */
12454 90 : check_stack_depth();
12455 :
12456 : Assert(cmdcon->alterEnforceability);
12457 :
12458 90 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12459 90 : conoid = currcon->oid;
12460 :
12461 : /* Should be foreign key constraint */
12462 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12463 :
12464 90 : rel = table_open(currcon->conrelid, lockmode);
12465 :
12466 90 : if (currcon->conenforced != cmdcon->is_enforced)
12467 : {
12468 87 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12469 87 : changed = true;
12470 : }
12471 :
12472 : /* Drop triggers */
12473 90 : if (!cmdcon->is_enforced)
12474 : {
12475 : /*
12476 : * When setting a constraint to NOT ENFORCED, the constraint triggers
12477 : * need to be dropped. Therefore, we must process the child relations
12478 : * first, followed by the parent, to account for dependencies.
12479 : */
12480 63 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12481 27 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12482 9 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12483 : fkrelid, pkrelid, contuple,
12484 : lockmode, InvalidOid, InvalidOid,
12485 : InvalidOid, InvalidOid);
12486 :
12487 : /* Drop all the triggers */
12488 36 : DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
12489 : }
12490 54 : else if (changed) /* Create triggers */
12491 : {
12492 54 : Oid ReferencedDelTriggerOid = InvalidOid,
12493 54 : ReferencedUpdTriggerOid = InvalidOid,
12494 54 : ReferencingInsTriggerOid = InvalidOid,
12495 54 : ReferencingUpdTriggerOid = InvalidOid;
12496 :
12497 : /* Prepare the minimal information required for trigger creation. */
12498 54 : Constraint *fkconstraint = makeNode(Constraint);
12499 :
12500 54 : fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12501 54 : fkconstraint->fk_matchtype = currcon->confmatchtype;
12502 54 : fkconstraint->fk_upd_action = currcon->confupdtype;
12503 54 : fkconstraint->fk_del_action = currcon->confdeltype;
12504 :
12505 : /* Create referenced triggers */
12506 54 : if (currcon->conrelid == fkrelid)
12507 33 : createForeignKeyActionTriggers(currcon->conrelid,
12508 : currcon->confrelid,
12509 : fkconstraint,
12510 : conoid,
12511 : currcon->conindid,
12512 : ReferencedParentDelTrigger,
12513 : ReferencedParentUpdTrigger,
12514 : &ReferencedDelTriggerOid,
12515 : &ReferencedUpdTriggerOid);
12516 :
12517 : /* Create referencing triggers */
12518 54 : if (currcon->confrelid == pkrelid)
12519 45 : createForeignKeyCheckTriggers(currcon->conrelid,
12520 : pkrelid,
12521 : fkconstraint,
12522 : conoid,
12523 : currcon->conindid,
12524 : ReferencingParentInsTrigger,
12525 : ReferencingParentUpdTrigger,
12526 : &ReferencingInsTriggerOid,
12527 : &ReferencingUpdTriggerOid);
12528 :
12529 : /*
12530 : * Tell Phase 3 to check that the constraint is satisfied by existing
12531 : * rows. Only applies to leaf partitions, and (for constraints that
12532 : * reference a partitioned table) only if this is not one of the
12533 : * pg_constraint rows that exist solely to support action triggers.
12534 : */
12535 54 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
12536 45 : currcon->confrelid == pkrelid)
12537 : {
12538 : AlteredTableInfo *tab;
12539 : NewConstraint *newcon;
12540 :
12541 36 : newcon = palloc0_object(NewConstraint);
12542 36 : newcon->name = fkconstraint->conname;
12543 36 : newcon->contype = CONSTR_FOREIGN;
12544 36 : newcon->refrelid = currcon->confrelid;
12545 36 : newcon->refindid = currcon->conindid;
12546 36 : newcon->conid = currcon->oid;
12547 36 : newcon->qual = (Node *) fkconstraint;
12548 :
12549 : /* Find or create work queue entry for this table */
12550 36 : tab = ATGetQueueEntry(wqueue, rel);
12551 36 : tab->constraints = lappend(tab->constraints, newcon);
12552 : }
12553 :
12554 : /*
12555 : * If the table at either end of the constraint is partitioned, we
12556 : * need to recurse and create triggers for each constraint that is a
12557 : * child of this one.
12558 : */
12559 99 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12560 45 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12561 15 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12562 : fkrelid, pkrelid, contuple,
12563 : lockmode, ReferencedDelTriggerOid,
12564 : ReferencedUpdTriggerOid,
12565 : ReferencingInsTriggerOid,
12566 : ReferencingUpdTriggerOid);
12567 : }
12568 :
12569 90 : table_close(rel, NoLock);
12570 :
12571 90 : return changed;
12572 : }
12573 :
12574 : /*
12575 : * Returns true if the constraint's deferrability is altered.
12576 : *
12577 : * *otherrelids is appended OIDs of relations containing affected triggers.
12578 : *
12579 : * Note that we must recurse even when the values are correct, in case
12580 : * indirect descendants have had their constraints altered locally.
12581 : * (This could be avoided if we forbade altering constraints in partitions
12582 : * but existing releases don't do that.)
12583 : */
12584 : static bool
12585 81 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
12586 : Relation conrel, Relation tgrel, Relation rel,
12587 : HeapTuple contuple, bool recurse,
12588 : List **otherrelids, LOCKMODE lockmode)
12589 : {
12590 : Form_pg_constraint currcon;
12591 : Oid refrelid;
12592 81 : bool changed = false;
12593 :
12594 : /* since this function recurses, it could be driven to stack overflow */
12595 81 : check_stack_depth();
12596 :
12597 : Assert(cmdcon->alterDeferrability);
12598 :
12599 81 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12600 81 : refrelid = currcon->confrelid;
12601 :
12602 : /* Should be foreign key constraint */
12603 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12604 :
12605 : /*
12606 : * If called to modify a constraint that's already in the desired state,
12607 : * silently do nothing.
12608 : */
12609 81 : if (currcon->condeferrable != cmdcon->deferrable ||
12610 3 : currcon->condeferred != cmdcon->initdeferred)
12611 : {
12612 81 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12613 81 : changed = true;
12614 :
12615 : /*
12616 : * Now we need to update the multiple entries in pg_trigger that
12617 : * implement the constraint.
12618 : */
12619 81 : AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12620 81 : cmdcon->deferrable,
12621 81 : cmdcon->initdeferred, otherrelids);
12622 : }
12623 :
12624 : /*
12625 : * If the table at either end of the constraint is partitioned, we need to
12626 : * handle every constraint that is a child of this one.
12627 : */
12628 81 : if (recurse && changed &&
12629 150 : (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12630 69 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12631 21 : AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12632 : contuple, recurse, otherrelids,
12633 : lockmode);
12634 :
12635 81 : return changed;
12636 : }
12637 :
12638 : /*
12639 : * Returns true if the constraint's inheritability is altered.
12640 : */
12641 : static bool
12642 30 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
12643 : Relation conrel, Relation rel,
12644 : HeapTuple contuple, LOCKMODE lockmode)
12645 : {
12646 : Form_pg_constraint currcon;
12647 : AttrNumber colNum;
12648 : char *colName;
12649 : List *children;
12650 :
12651 : Assert(cmdcon->alterInheritability);
12652 :
12653 30 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12654 :
12655 : /* The current implementation only works for NOT NULL constraints */
12656 : Assert(currcon->contype == CONSTRAINT_NOTNULL);
12657 :
12658 : /*
12659 : * If called to modify a constraint that's already in the desired state,
12660 : * silently do nothing.
12661 : */
12662 30 : if (cmdcon->noinherit == currcon->connoinherit)
12663 0 : return false;
12664 :
12665 30 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12666 30 : CommandCounterIncrement();
12667 :
12668 : /* Fetch the column number and name */
12669 30 : colNum = extractNotNullColumn(contuple);
12670 30 : colName = get_attname(currcon->conrelid, colNum, false);
12671 :
12672 : /*
12673 : * Propagate the change to children. For this subcommand type we don't
12674 : * recursively affect children, just the immediate level.
12675 : */
12676 30 : children = find_inheritance_children(RelationGetRelid(rel),
12677 : lockmode);
12678 96 : foreach_oid(childoid, children)
12679 : {
12680 : ObjectAddress addr;
12681 :
12682 42 : if (cmdcon->noinherit)
12683 : {
12684 : HeapTuple childtup;
12685 : Form_pg_constraint childcon;
12686 :
12687 15 : childtup = findNotNullConstraint(childoid, colName);
12688 15 : if (!childtup)
12689 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12690 : colName, childoid);
12691 15 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12692 : Assert(childcon->coninhcount > 0);
12693 15 : childcon->coninhcount--;
12694 15 : childcon->conislocal = true;
12695 15 : CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12696 15 : heap_freetuple(childtup);
12697 : }
12698 : else
12699 : {
12700 27 : Relation childrel = table_open(childoid, NoLock);
12701 :
12702 27 : addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12703 : colName, true, true, lockmode);
12704 24 : if (OidIsValid(addr.objectId))
12705 24 : CommandCounterIncrement();
12706 24 : table_close(childrel, NoLock);
12707 : }
12708 : }
12709 :
12710 27 : return true;
12711 : }
12712 :
12713 : /*
12714 : * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12715 : * trigger's deferrability.
12716 : *
12717 : * The arguments to this function have the same meaning as the arguments to
12718 : * ATExecAlterConstrDeferrability.
12719 : */
12720 : static void
12721 81 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12722 : bool deferrable, bool initdeferred,
12723 : List **otherrelids)
12724 : {
12725 : HeapTuple tgtuple;
12726 : ScanKeyData tgkey;
12727 : SysScanDesc tgscan;
12728 :
12729 81 : ScanKeyInit(&tgkey,
12730 : Anum_pg_trigger_tgconstraint,
12731 : BTEqualStrategyNumber, F_OIDEQ,
12732 : ObjectIdGetDatum(conoid));
12733 81 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12734 : NULL, 1, &tgkey);
12735 315 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12736 : {
12737 234 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12738 : Form_pg_trigger copy_tg;
12739 : HeapTuple tgCopyTuple;
12740 :
12741 : /*
12742 : * Remember OIDs of other relation(s) involved in FK constraint.
12743 : * (Note: it's likely that we could skip forcing a relcache inval for
12744 : * other rels that don't have a trigger whose properties change, but
12745 : * let's be conservative.)
12746 : */
12747 234 : if (tgform->tgrelid != RelationGetRelid(rel))
12748 114 : *otherrelids = list_append_unique_oid(*otherrelids,
12749 : tgform->tgrelid);
12750 :
12751 : /*
12752 : * Update enable status and deferrability of RI_FKey_noaction_del,
12753 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12754 : * triggers, but not others; see createForeignKeyActionTriggers and
12755 : * CreateFKCheckTrigger.
12756 : */
12757 234 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12758 186 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12759 129 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12760 69 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12761 9 : continue;
12762 :
12763 225 : tgCopyTuple = heap_copytuple(tgtuple);
12764 225 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12765 :
12766 225 : copy_tg->tgdeferrable = deferrable;
12767 225 : copy_tg->tginitdeferred = initdeferred;
12768 225 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12769 :
12770 225 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12771 :
12772 225 : heap_freetuple(tgCopyTuple);
12773 : }
12774 :
12775 81 : systable_endscan(tgscan);
12776 81 : }
12777 :
12778 : /*
12779 : * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12780 : * the specified constraint.
12781 : *
12782 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12783 : * list of child relations and recursing; instead it uses the conparentid
12784 : * relationships. This may need to be reconsidered.
12785 : *
12786 : * The arguments to this function have the same meaning as the arguments to
12787 : * ATExecAlterConstrEnforceability.
12788 : */
12789 : static void
12790 24 : AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12791 : Relation conrel, Relation tgrel,
12792 : Oid fkrelid, Oid pkrelid,
12793 : HeapTuple contuple, LOCKMODE lockmode,
12794 : Oid ReferencedParentDelTrigger,
12795 : Oid ReferencedParentUpdTrigger,
12796 : Oid ReferencingParentInsTrigger,
12797 : Oid ReferencingParentUpdTrigger)
12798 : {
12799 : Form_pg_constraint currcon;
12800 : Oid conoid;
12801 : ScanKeyData pkey;
12802 : SysScanDesc pscan;
12803 : HeapTuple childtup;
12804 :
12805 24 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12806 24 : conoid = currcon->oid;
12807 :
12808 24 : ScanKeyInit(&pkey,
12809 : Anum_pg_constraint_conparentid,
12810 : BTEqualStrategyNumber, F_OIDEQ,
12811 : ObjectIdGetDatum(conoid));
12812 :
12813 24 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12814 : true, NULL, 1, &pkey);
12815 :
12816 75 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12817 51 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12818 : pkrelid, childtup, lockmode,
12819 : ReferencedParentDelTrigger,
12820 : ReferencedParentUpdTrigger,
12821 : ReferencingParentInsTrigger,
12822 : ReferencingParentUpdTrigger);
12823 :
12824 24 : systable_endscan(pscan);
12825 24 : }
12826 :
12827 : /*
12828 : * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12829 : * the specified constraint.
12830 : *
12831 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12832 : * list of child relations and recursing; instead it uses the conparentid
12833 : * relationships. This may need to be reconsidered.
12834 : *
12835 : * The arguments to this function have the same meaning as the arguments to
12836 : * ATExecAlterConstrDeferrability.
12837 : */
12838 : static void
12839 21 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12840 : Relation conrel, Relation tgrel, Relation rel,
12841 : HeapTuple contuple, bool recurse,
12842 : List **otherrelids, LOCKMODE lockmode)
12843 : {
12844 : Form_pg_constraint currcon;
12845 : Oid conoid;
12846 : ScanKeyData pkey;
12847 : SysScanDesc pscan;
12848 : HeapTuple childtup;
12849 :
12850 21 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12851 21 : conoid = currcon->oid;
12852 :
12853 21 : ScanKeyInit(&pkey,
12854 : Anum_pg_constraint_conparentid,
12855 : BTEqualStrategyNumber, F_OIDEQ,
12856 : ObjectIdGetDatum(conoid));
12857 :
12858 21 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12859 : true, NULL, 1, &pkey);
12860 :
12861 54 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12862 : {
12863 33 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12864 : Relation childrel;
12865 :
12866 33 : childrel = table_open(childcon->conrelid, lockmode);
12867 :
12868 33 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12869 : childtup, recurse, otherrelids, lockmode);
12870 33 : table_close(childrel, NoLock);
12871 : }
12872 :
12873 21 : systable_endscan(pscan);
12874 21 : }
12875 :
12876 : /*
12877 : * Update the constraint entry for the given ATAlterConstraint command, and
12878 : * invoke the appropriate hooks.
12879 : */
12880 : static void
12881 198 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
12882 : HeapTuple contuple)
12883 : {
12884 : HeapTuple copyTuple;
12885 : Form_pg_constraint copy_con;
12886 :
12887 : Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12888 : cmdcon->alterInheritability);
12889 :
12890 198 : copyTuple = heap_copytuple(contuple);
12891 198 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12892 :
12893 198 : if (cmdcon->alterEnforceability)
12894 : {
12895 87 : copy_con->conenforced = cmdcon->is_enforced;
12896 :
12897 : /*
12898 : * NB: The convalidated status is irrelevant when the constraint is
12899 : * set to NOT ENFORCED, but for consistency, it should still be set
12900 : * appropriately. Similarly, if the constraint is later changed to
12901 : * ENFORCED, validation will be performed during phase 3, so it makes
12902 : * sense to mark it as valid in that case.
12903 : */
12904 87 : copy_con->convalidated = cmdcon->is_enforced;
12905 : }
12906 198 : if (cmdcon->alterDeferrability)
12907 : {
12908 84 : copy_con->condeferrable = cmdcon->deferrable;
12909 84 : copy_con->condeferred = cmdcon->initdeferred;
12910 : }
12911 198 : if (cmdcon->alterInheritability)
12912 30 : copy_con->connoinherit = cmdcon->noinherit;
12913 :
12914 198 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12915 198 : InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12916 :
12917 : /* Make new constraint flags visible to others */
12918 198 : CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12919 :
12920 198 : heap_freetuple(copyTuple);
12921 198 : }
12922 :
12923 : /*
12924 : * ALTER TABLE VALIDATE CONSTRAINT
12925 : *
12926 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12927 : * there's no good way to skip recursing when handling foreign keys: there is
12928 : * no need to lock children in that case, yet we wouldn't be able to avoid
12929 : * doing so at that level.
12930 : *
12931 : * Return value is the address of the validated constraint. If the constraint
12932 : * was already validated, InvalidObjectAddress is returned.
12933 : */
12934 : static ObjectAddress
12935 295 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12936 : bool recurse, bool recursing, LOCKMODE lockmode)
12937 : {
12938 : Relation conrel;
12939 : SysScanDesc scan;
12940 : ScanKeyData skey[3];
12941 : HeapTuple tuple;
12942 : Form_pg_constraint con;
12943 : ObjectAddress address;
12944 :
12945 295 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12946 :
12947 : /*
12948 : * Find and check the target constraint
12949 : */
12950 295 : ScanKeyInit(&skey[0],
12951 : Anum_pg_constraint_conrelid,
12952 : BTEqualStrategyNumber, F_OIDEQ,
12953 : ObjectIdGetDatum(RelationGetRelid(rel)));
12954 295 : ScanKeyInit(&skey[1],
12955 : Anum_pg_constraint_contypid,
12956 : BTEqualStrategyNumber, F_OIDEQ,
12957 : ObjectIdGetDatum(InvalidOid));
12958 295 : ScanKeyInit(&skey[2],
12959 : Anum_pg_constraint_conname,
12960 : BTEqualStrategyNumber, F_NAMEEQ,
12961 : CStringGetDatum(constrName));
12962 295 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12963 : true, NULL, 3, skey);
12964 :
12965 : /* There can be at most one matching row */
12966 295 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12967 0 : ereport(ERROR,
12968 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12969 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12970 : constrName, RelationGetRelationName(rel))));
12971 :
12972 295 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12973 295 : if (con->contype != CONSTRAINT_FOREIGN &&
12974 128 : con->contype != CONSTRAINT_CHECK &&
12975 56 : con->contype != CONSTRAINT_NOTNULL)
12976 0 : ereport(ERROR,
12977 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12978 : errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12979 : constrName, RelationGetRelationName(rel)),
12980 : errdetail("This operation is not supported for this type of constraint."));
12981 :
12982 295 : if (!con->conenforced)
12983 3 : ereport(ERROR,
12984 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12985 : errmsg("cannot validate NOT ENFORCED constraint")));
12986 :
12987 292 : if (!con->convalidated)
12988 : {
12989 283 : if (con->contype == CONSTRAINT_FOREIGN)
12990 : {
12991 164 : QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12992 : tuple, lockmode);
12993 : }
12994 119 : else if (con->contype == CONSTRAINT_CHECK)
12995 : {
12996 63 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12997 : tuple, recurse, recursing, lockmode);
12998 : }
12999 56 : else if (con->contype == CONSTRAINT_NOTNULL)
13000 : {
13001 56 : QueueNNConstraintValidation(wqueue, conrel, rel,
13002 : tuple, recurse, recursing, lockmode);
13003 : }
13004 :
13005 283 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
13006 : }
13007 : else
13008 9 : address = InvalidObjectAddress; /* already validated */
13009 :
13010 292 : systable_endscan(scan);
13011 :
13012 292 : table_close(conrel, RowExclusiveLock);
13013 :
13014 292 : return address;
13015 : }
13016 :
13017 : /*
13018 : * QueueFKConstraintValidation
13019 : *
13020 : * Add an entry to the wqueue to validate the given foreign key constraint in
13021 : * Phase 3 and update the convalidated field in the pg_constraint catalog
13022 : * for the specified relation and all its children.
13023 : */
13024 : static void
13025 203 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
13026 : Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
13027 : {
13028 : Form_pg_constraint con;
13029 : AlteredTableInfo *tab;
13030 : HeapTuple copyTuple;
13031 : Form_pg_constraint copy_con;
13032 :
13033 203 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13034 : Assert(con->contype == CONSTRAINT_FOREIGN);
13035 : Assert(!con->convalidated);
13036 :
13037 : /*
13038 : * Add the validation to phase 3's queue; not needed for partitioned
13039 : * tables themselves, only for their partitions.
13040 : *
13041 : * When the referenced table (pkrelid) is partitioned, the referencing
13042 : * table (fkrel) has one pg_constraint row pointing to each partition
13043 : * thereof. These rows are there only to support action triggers and no
13044 : * table scan is needed, therefore skip this for them as well.
13045 : */
13046 203 : if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13047 179 : con->confrelid == pkrelid)
13048 : {
13049 : NewConstraint *newcon;
13050 : Constraint *fkconstraint;
13051 :
13052 : /* Queue validation for phase 3 */
13053 170 : fkconstraint = makeNode(Constraint);
13054 : /* for now this is all we need */
13055 170 : fkconstraint->conname = pstrdup(NameStr(con->conname));
13056 :
13057 170 : newcon = palloc0_object(NewConstraint);
13058 170 : newcon->name = fkconstraint->conname;
13059 170 : newcon->contype = CONSTR_FOREIGN;
13060 170 : newcon->refrelid = con->confrelid;
13061 170 : newcon->refindid = con->conindid;
13062 170 : newcon->conid = con->oid;
13063 170 : newcon->qual = (Node *) fkconstraint;
13064 :
13065 : /* Find or create work queue entry for this table */
13066 170 : tab = ATGetQueueEntry(wqueue, fkrel);
13067 170 : tab->constraints = lappend(tab->constraints, newcon);
13068 : }
13069 :
13070 : /*
13071 : * If the table at either end of the constraint is partitioned, we need to
13072 : * recurse and handle every unvalidated constraint that is a child of this
13073 : * constraint.
13074 : */
13075 382 : if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13076 179 : get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13077 : {
13078 : ScanKeyData pkey;
13079 : SysScanDesc pscan;
13080 : HeapTuple childtup;
13081 :
13082 39 : ScanKeyInit(&pkey,
13083 : Anum_pg_constraint_conparentid,
13084 : BTEqualStrategyNumber, F_OIDEQ,
13085 : ObjectIdGetDatum(con->oid));
13086 :
13087 39 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13088 : true, NULL, 1, &pkey);
13089 :
13090 78 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13091 : {
13092 : Form_pg_constraint childcon;
13093 : Relation childrel;
13094 :
13095 39 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13096 :
13097 : /*
13098 : * If the child constraint has already been validated, no further
13099 : * action is required for it or its descendants, as they are all
13100 : * valid.
13101 : */
13102 39 : if (childcon->convalidated)
13103 9 : continue;
13104 :
13105 30 : childrel = table_open(childcon->conrelid, lockmode);
13106 :
13107 : /*
13108 : * NB: Note that pkrelid should be passed as-is during recursion,
13109 : * as it is required to identify the root referenced table.
13110 : */
13111 30 : QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13112 : childtup, lockmode);
13113 30 : table_close(childrel, NoLock);
13114 : }
13115 :
13116 39 : systable_endscan(pscan);
13117 : }
13118 :
13119 : /*
13120 : * Now mark the pg_constraint row as validated (even if we didn't check,
13121 : * notably the ones for partitions on the referenced side).
13122 : *
13123 : * We rely on transaction abort to roll back this change if phase 3
13124 : * ultimately finds violating rows. This is a bit ugly.
13125 : */
13126 203 : copyTuple = heap_copytuple(contuple);
13127 203 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13128 203 : copy_con->convalidated = true;
13129 203 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13130 :
13131 203 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13132 :
13133 203 : heap_freetuple(copyTuple);
13134 203 : }
13135 :
13136 : /*
13137 : * QueueCheckConstraintValidation
13138 : *
13139 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
13140 : * and update the convalidated field in the pg_constraint catalog for the
13141 : * specified relation and all its inheriting children.
13142 : */
13143 : static void
13144 63 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13145 : char *constrName, HeapTuple contuple,
13146 : bool recurse, bool recursing, LOCKMODE lockmode)
13147 : {
13148 : Form_pg_constraint con;
13149 : AlteredTableInfo *tab;
13150 : HeapTuple copyTuple;
13151 : Form_pg_constraint copy_con;
13152 :
13153 63 : List *children = NIL;
13154 : ListCell *child;
13155 : NewConstraint *newcon;
13156 : Datum val;
13157 : char *conbin;
13158 :
13159 63 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13160 : Assert(con->contype == CONSTRAINT_CHECK);
13161 :
13162 : /*
13163 : * If we're recursing, the parent has already done this, so skip it. Also,
13164 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13165 : * for it in the children.
13166 : */
13167 63 : if (!recursing && !con->connoinherit)
13168 36 : children = find_all_inheritors(RelationGetRelid(rel),
13169 : lockmode, NULL);
13170 :
13171 : /*
13172 : * For CHECK constraints, we must ensure that we only mark the constraint
13173 : * as validated on the parent if it's already validated on the children.
13174 : *
13175 : * We recurse before validating on the parent, to reduce risk of
13176 : * deadlocks.
13177 : */
13178 123 : foreach(child, children)
13179 : {
13180 60 : Oid childoid = lfirst_oid(child);
13181 : Relation childrel;
13182 :
13183 60 : if (childoid == RelationGetRelid(rel))
13184 36 : continue;
13185 :
13186 : /*
13187 : * If we are told not to recurse, there had better not be any child
13188 : * tables, because we can't mark the constraint on the parent valid
13189 : * unless it is valid for all child tables.
13190 : */
13191 24 : if (!recurse)
13192 0 : ereport(ERROR,
13193 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13194 : errmsg("constraint must be validated on child tables too")));
13195 :
13196 : /* find_all_inheritors already got lock */
13197 24 : childrel = table_open(childoid, NoLock);
13198 :
13199 24 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
13200 : true, lockmode);
13201 24 : table_close(childrel, NoLock);
13202 : }
13203 :
13204 : /* Queue validation for phase 3 */
13205 63 : newcon = palloc0_object(NewConstraint);
13206 63 : newcon->name = constrName;
13207 63 : newcon->contype = CONSTR_CHECK;
13208 63 : newcon->refrelid = InvalidOid;
13209 63 : newcon->refindid = InvalidOid;
13210 63 : newcon->conid = con->oid;
13211 :
13212 63 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13213 : Anum_pg_constraint_conbin);
13214 63 : conbin = TextDatumGetCString(val);
13215 63 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13216 :
13217 : /* Find or create work queue entry for this table */
13218 63 : tab = ATGetQueueEntry(wqueue, rel);
13219 63 : tab->constraints = lappend(tab->constraints, newcon);
13220 :
13221 : /*
13222 : * Invalidate relcache so that others see the new validated constraint.
13223 : */
13224 63 : CacheInvalidateRelcache(rel);
13225 :
13226 : /*
13227 : * Now update the catalog, while we have the door open.
13228 : */
13229 63 : copyTuple = heap_copytuple(contuple);
13230 63 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13231 63 : copy_con->convalidated = true;
13232 63 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13233 :
13234 63 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13235 :
13236 63 : heap_freetuple(copyTuple);
13237 63 : }
13238 :
13239 : /*
13240 : * QueueNNConstraintValidation
13241 : *
13242 : * Add an entry to the wqueue to validate the given not-null constraint in
13243 : * Phase 3 and update the convalidated field in the pg_constraint catalog for
13244 : * the specified relation and all its inheriting children.
13245 : */
13246 : static void
13247 56 : QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13248 : HeapTuple contuple, bool recurse, bool recursing,
13249 : LOCKMODE lockmode)
13250 : {
13251 : Form_pg_constraint con;
13252 : AlteredTableInfo *tab;
13253 : HeapTuple copyTuple;
13254 : Form_pg_constraint copy_con;
13255 56 : List *children = NIL;
13256 : AttrNumber attnum;
13257 : char *colname;
13258 :
13259 56 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13260 : Assert(con->contype == CONSTRAINT_NOTNULL);
13261 :
13262 56 : attnum = extractNotNullColumn(contuple);
13263 :
13264 : /*
13265 : * If we're recursing, we've already done this for parent, so skip it.
13266 : * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13267 : * look for it in the children.
13268 : *
13269 : * We recurse before validating on the parent, to reduce risk of
13270 : * deadlocks.
13271 : */
13272 56 : if (!recursing && !con->connoinherit)
13273 38 : children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13274 :
13275 56 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13276 189 : foreach_oid(childoid, children)
13277 : {
13278 : Relation childrel;
13279 : HeapTuple contup;
13280 : Form_pg_constraint childcon;
13281 : char *conname;
13282 :
13283 77 : if (childoid == RelationGetRelid(rel))
13284 38 : continue;
13285 :
13286 : /*
13287 : * If we are told not to recurse, there had better not be any child
13288 : * tables, because we can't mark the constraint on the parent valid
13289 : * unless it is valid for all child tables.
13290 : */
13291 39 : if (!recurse)
13292 0 : ereport(ERROR,
13293 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13294 : errmsg("constraint must be validated on child tables too"));
13295 :
13296 : /*
13297 : * The column on child might have a different attnum, so search by
13298 : * column name.
13299 : */
13300 39 : contup = findNotNullConstraint(childoid, colname);
13301 39 : if (!contup)
13302 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13303 : colname, get_rel_name(childoid));
13304 39 : childcon = (Form_pg_constraint) GETSTRUCT(contup);
13305 39 : if (childcon->convalidated)
13306 21 : continue;
13307 :
13308 : /* find_all_inheritors already got lock */
13309 18 : childrel = table_open(childoid, NoLock);
13310 18 : conname = pstrdup(NameStr(childcon->conname));
13311 :
13312 : /* XXX improve ATExecValidateConstraint API to avoid double search */
13313 18 : ATExecValidateConstraint(wqueue, childrel, conname,
13314 : false, true, lockmode);
13315 18 : table_close(childrel, NoLock);
13316 : }
13317 :
13318 : /* Set attnotnull appropriately without queueing another validation */
13319 56 : set_attnotnull(NULL, rel, attnum, true, false);
13320 :
13321 56 : tab = ATGetQueueEntry(wqueue, rel);
13322 56 : tab->verify_new_notnull = true;
13323 :
13324 : /*
13325 : * Invalidate relcache so that others see the new validated constraint.
13326 : */
13327 56 : CacheInvalidateRelcache(rel);
13328 :
13329 : /*
13330 : * Now update the catalogs, while we have the door open.
13331 : */
13332 56 : copyTuple = heap_copytuple(contuple);
13333 56 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13334 56 : copy_con->convalidated = true;
13335 56 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13336 :
13337 56 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13338 :
13339 56 : heap_freetuple(copyTuple);
13340 56 : }
13341 :
13342 : /*
13343 : * transformColumnNameList - transform list of column names
13344 : *
13345 : * Lookup each name and return its attnum and, optionally, type and collation
13346 : * OIDs
13347 : *
13348 : * Note: the name of this function suggests that it's general-purpose,
13349 : * but actually it's only used to look up names appearing in foreign-key
13350 : * clauses. The error messages would need work to use it in other cases,
13351 : * and perhaps the validity checks as well.
13352 : */
13353 : static int
13354 3329 : transformColumnNameList(Oid relId, List *colList,
13355 : int16 *attnums, Oid *atttypids, Oid *attcollids)
13356 : {
13357 : ListCell *l;
13358 : int attnum;
13359 :
13360 3329 : attnum = 0;
13361 6071 : foreach(l, colList)
13362 : {
13363 2775 : char *attname = strVal(lfirst(l));
13364 : HeapTuple atttuple;
13365 : Form_pg_attribute attform;
13366 :
13367 2775 : atttuple = SearchSysCacheAttName(relId, attname);
13368 2775 : if (!HeapTupleIsValid(atttuple))
13369 27 : ereport(ERROR,
13370 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13371 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13372 : attname)));
13373 2748 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13374 2748 : if (attform->attnum < 0)
13375 6 : ereport(ERROR,
13376 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13377 : errmsg("system columns cannot be used in foreign keys")));
13378 2742 : if (attnum >= INDEX_MAX_KEYS)
13379 0 : ereport(ERROR,
13380 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
13381 : errmsg("cannot have more than %d keys in a foreign key",
13382 : INDEX_MAX_KEYS)));
13383 2742 : attnums[attnum] = attform->attnum;
13384 2742 : if (atttypids != NULL)
13385 2724 : atttypids[attnum] = attform->atttypid;
13386 2742 : if (attcollids != NULL)
13387 2724 : attcollids[attnum] = attform->attcollation;
13388 2742 : ReleaseSysCache(atttuple);
13389 2742 : attnum++;
13390 : }
13391 :
13392 3296 : return attnum;
13393 : }
13394 :
13395 : /*
13396 : * transformFkeyGetPrimaryKey -
13397 : *
13398 : * Look up the names, attnums, types, and collations of the primary key attributes
13399 : * for the pkrel. Also return the index OID and index opclasses of the
13400 : * index supporting the primary key. Also return whether the index has
13401 : * WITHOUT OVERLAPS.
13402 : *
13403 : * All parameters except pkrel are output parameters. Also, the function
13404 : * return value is the number of attributes in the primary key.
13405 : *
13406 : * Used when the column list in the REFERENCES specification is omitted.
13407 : */
13408 : static int
13409 634 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
13410 : List **attnamelist,
13411 : int16 *attnums, Oid *atttypids, Oid *attcollids,
13412 : Oid *opclasses, bool *pk_has_without_overlaps)
13413 : {
13414 : List *indexoidlist;
13415 : ListCell *indexoidscan;
13416 634 : HeapTuple indexTuple = NULL;
13417 634 : Form_pg_index indexStruct = NULL;
13418 : Datum indclassDatum;
13419 : oidvector *indclass;
13420 : int i;
13421 :
13422 : /*
13423 : * Get the list of index OIDs for the table from the relcache, and look up
13424 : * each one in the pg_index syscache until we find one marked primary key
13425 : * (hopefully there isn't more than one such). Insist it's valid, too.
13426 : */
13427 634 : *indexOid = InvalidOid;
13428 :
13429 634 : indexoidlist = RelationGetIndexList(pkrel);
13430 :
13431 637 : foreach(indexoidscan, indexoidlist)
13432 : {
13433 637 : Oid indexoid = lfirst_oid(indexoidscan);
13434 :
13435 637 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13436 637 : if (!HeapTupleIsValid(indexTuple))
13437 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13438 637 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13439 637 : if (indexStruct->indisprimary && indexStruct->indisvalid)
13440 : {
13441 : /*
13442 : * Refuse to use a deferrable primary key. This is per SQL spec,
13443 : * and there would be a lot of interesting semantic problems if we
13444 : * tried to allow it.
13445 : */
13446 634 : if (!indexStruct->indimmediate)
13447 0 : ereport(ERROR,
13448 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13449 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13450 : RelationGetRelationName(pkrel))));
13451 :
13452 634 : *indexOid = indexoid;
13453 634 : break;
13454 : }
13455 3 : ReleaseSysCache(indexTuple);
13456 : }
13457 :
13458 634 : list_free(indexoidlist);
13459 :
13460 : /*
13461 : * Check that we found it
13462 : */
13463 634 : if (!OidIsValid(*indexOid))
13464 0 : ereport(ERROR,
13465 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13466 : errmsg("there is no primary key for referenced table \"%s\"",
13467 : RelationGetRelationName(pkrel))));
13468 :
13469 : /* Must get indclass the hard way */
13470 634 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13471 : Anum_pg_index_indclass);
13472 634 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13473 :
13474 : /*
13475 : * Now build the list of PK attributes from the indkey definition (we
13476 : * assume a primary key cannot have expressional elements)
13477 : */
13478 634 : *attnamelist = NIL;
13479 1507 : for (i = 0; i < indexStruct->indnkeyatts; i++)
13480 : {
13481 873 : int pkattno = indexStruct->indkey.values[i];
13482 :
13483 873 : attnums[i] = pkattno;
13484 873 : atttypids[i] = attnumTypeId(pkrel, pkattno);
13485 873 : attcollids[i] = attnumCollationId(pkrel, pkattno);
13486 873 : opclasses[i] = indclass->values[i];
13487 873 : *attnamelist = lappend(*attnamelist,
13488 873 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13489 : }
13490 :
13491 634 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13492 :
13493 634 : ReleaseSysCache(indexTuple);
13494 :
13495 634 : return i;
13496 : }
13497 :
13498 : /*
13499 : * transformFkeyCheckAttrs -
13500 : *
13501 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13502 : * reference as part of a foreign key constraint.
13503 : *
13504 : * Returns the OID of the unique index supporting the constraint and
13505 : * populates the caller-provided 'opclasses' array with the opclasses
13506 : * associated with the index columns. Also sets whether the index
13507 : * uses WITHOUT OVERLAPS.
13508 : *
13509 : * Raises an ERROR on validation failure.
13510 : */
13511 : static Oid
13512 647 : transformFkeyCheckAttrs(Relation pkrel,
13513 : int numattrs, int16 *attnums,
13514 : bool with_period, Oid *opclasses,
13515 : bool *pk_has_without_overlaps)
13516 : {
13517 647 : Oid indexoid = InvalidOid;
13518 647 : bool found = false;
13519 647 : bool found_deferrable = false;
13520 : List *indexoidlist;
13521 : ListCell *indexoidscan;
13522 : int i,
13523 : j;
13524 :
13525 : /*
13526 : * Reject duplicate appearances of columns in the referenced-columns list.
13527 : * Such a case is forbidden by the SQL standard, and even if we thought it
13528 : * useful to allow it, there would be ambiguity about how to match the
13529 : * list to unique indexes (in particular, it'd be unclear which index
13530 : * opclass goes with which FK column).
13531 : */
13532 1508 : for (i = 0; i < numattrs; i++)
13533 : {
13534 1134 : for (j = i + 1; j < numattrs; j++)
13535 : {
13536 273 : if (attnums[i] == attnums[j])
13537 12 : ereport(ERROR,
13538 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13539 : errmsg("foreign key referenced-columns list must not contain duplicates")));
13540 : }
13541 : }
13542 :
13543 : /*
13544 : * Get the list of index OIDs for the table from the relcache, and look up
13545 : * each one in the pg_index syscache, and match unique indexes to the list
13546 : * of attnums we are given.
13547 : */
13548 635 : indexoidlist = RelationGetIndexList(pkrel);
13549 :
13550 725 : foreach(indexoidscan, indexoidlist)
13551 : {
13552 : HeapTuple indexTuple;
13553 : Form_pg_index indexStruct;
13554 :
13555 719 : indexoid = lfirst_oid(indexoidscan);
13556 719 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13557 719 : if (!HeapTupleIsValid(indexTuple))
13558 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13559 719 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13560 :
13561 : /*
13562 : * Must have the right number of columns; must be unique (or if
13563 : * temporal then exclusion instead) and not a partial index; forget it
13564 : * if there are any expressions, too. Invalid indexes are out as well.
13565 : */
13566 719 : if (indexStruct->indnkeyatts == numattrs &&
13567 658 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13568 1316 : indexStruct->indisvalid &&
13569 1316 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13570 658 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13571 : {
13572 : Datum indclassDatum;
13573 : oidvector *indclass;
13574 :
13575 : /* Must get indclass the hard way */
13576 658 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13577 : Anum_pg_index_indclass);
13578 658 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13579 :
13580 : /*
13581 : * The given attnum list may match the index columns in any order.
13582 : * Check for a match, and extract the appropriate opclasses while
13583 : * we're at it.
13584 : *
13585 : * We know that attnums[] is duplicate-free per the test at the
13586 : * start of this function, and we checked above that the number of
13587 : * index columns agrees, so if we find a match for each attnums[]
13588 : * entry then we must have a one-to-one match in some order.
13589 : */
13590 1513 : for (i = 0; i < numattrs; i++)
13591 : {
13592 884 : found = false;
13593 1174 : for (j = 0; j < numattrs; j++)
13594 : {
13595 1145 : if (attnums[i] == indexStruct->indkey.values[j])
13596 : {
13597 855 : opclasses[i] = indclass->values[j];
13598 855 : found = true;
13599 855 : break;
13600 : }
13601 : }
13602 884 : if (!found)
13603 29 : break;
13604 : }
13605 : /* The last attribute in the index must be the PERIOD FK part */
13606 658 : if (found && with_period)
13607 : {
13608 61 : int16 periodattnum = attnums[numattrs - 1];
13609 :
13610 61 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13611 : }
13612 :
13613 : /*
13614 : * Refuse to use a deferrable unique/primary key. This is per SQL
13615 : * spec, and there would be a lot of interesting semantic problems
13616 : * if we tried to allow it.
13617 : */
13618 658 : if (found && !indexStruct->indimmediate)
13619 : {
13620 : /*
13621 : * Remember that we found an otherwise matching index, so that
13622 : * we can generate a more appropriate error message.
13623 : */
13624 0 : found_deferrable = true;
13625 0 : found = false;
13626 : }
13627 :
13628 : /* We need to know whether the index has WITHOUT OVERLAPS */
13629 658 : if (found)
13630 629 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13631 : }
13632 719 : ReleaseSysCache(indexTuple);
13633 719 : if (found)
13634 629 : break;
13635 : }
13636 :
13637 635 : if (!found)
13638 : {
13639 6 : if (found_deferrable)
13640 0 : ereport(ERROR,
13641 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13642 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13643 : RelationGetRelationName(pkrel))));
13644 : else
13645 6 : ereport(ERROR,
13646 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13647 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13648 : RelationGetRelationName(pkrel))));
13649 : }
13650 :
13651 629 : list_free(indexoidlist);
13652 :
13653 629 : return indexoid;
13654 : }
13655 :
13656 : /*
13657 : * findFkeyCast -
13658 : *
13659 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13660 : * Caller has equal regard for binary coercibility and for an exact match.
13661 : */
13662 : static CoercionPathType
13663 6 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13664 : {
13665 : CoercionPathType ret;
13666 :
13667 6 : if (targetTypeId == sourceTypeId)
13668 : {
13669 6 : ret = COERCION_PATH_RELABELTYPE;
13670 6 : *funcid = InvalidOid;
13671 : }
13672 : else
13673 : {
13674 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13675 : COERCION_IMPLICIT, funcid);
13676 0 : if (ret == COERCION_PATH_NONE)
13677 : /* A previously-relied-upon cast is now gone. */
13678 0 : elog(ERROR, "could not find cast from %u to %u",
13679 : sourceTypeId, targetTypeId);
13680 : }
13681 :
13682 6 : return ret;
13683 : }
13684 :
13685 : /*
13686 : * Permissions checks on the referenced table for ADD FOREIGN KEY
13687 : *
13688 : * Note: we have already checked that the user owns the referencing table,
13689 : * else we'd have failed much earlier; no additional checks are needed for it.
13690 : */
13691 : static void
13692 1245 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13693 : {
13694 1245 : Oid roleid = GetUserId();
13695 : AclResult aclresult;
13696 : int i;
13697 :
13698 : /* Okay if we have relation-level REFERENCES permission */
13699 1245 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13700 : ACL_REFERENCES);
13701 1245 : if (aclresult == ACLCHECK_OK)
13702 1245 : return;
13703 : /* Else we must have REFERENCES on each column */
13704 0 : for (i = 0; i < natts; i++)
13705 : {
13706 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13707 : roleid, ACL_REFERENCES);
13708 0 : if (aclresult != ACLCHECK_OK)
13709 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13710 0 : RelationGetRelationName(rel));
13711 : }
13712 : }
13713 :
13714 : /*
13715 : * Scan the existing rows in a table to verify they meet a proposed FK
13716 : * constraint.
13717 : *
13718 : * Caller must have opened and locked both relations appropriately.
13719 : */
13720 : static void
13721 589 : validateForeignKeyConstraint(char *conname,
13722 : Relation rel,
13723 : Relation pkrel,
13724 : Oid pkindOid,
13725 : Oid constraintOid,
13726 : bool hasperiod)
13727 : {
13728 : TupleTableSlot *slot;
13729 : TableScanDesc scan;
13730 589 : Trigger trig = {0};
13731 : Snapshot snapshot;
13732 : MemoryContext oldcxt;
13733 : MemoryContext perTupCxt;
13734 :
13735 589 : ereport(DEBUG1,
13736 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13737 :
13738 : /*
13739 : * Build a trigger call structure; we'll need it either way.
13740 : */
13741 589 : trig.tgoid = InvalidOid;
13742 589 : trig.tgname = conname;
13743 589 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13744 589 : trig.tgisinternal = true;
13745 589 : trig.tgconstrrelid = RelationGetRelid(pkrel);
13746 589 : trig.tgconstrindid = pkindOid;
13747 589 : trig.tgconstraint = constraintOid;
13748 589 : trig.tgdeferrable = false;
13749 589 : trig.tginitdeferred = false;
13750 : /* we needn't fill in remaining fields */
13751 :
13752 : /*
13753 : * See if we can do it with a single LEFT JOIN query. A false result
13754 : * indicates we must proceed with the fire-the-trigger method. We can't do
13755 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13756 : * left joins.
13757 : */
13758 589 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13759 492 : return;
13760 :
13761 : /*
13762 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13763 : * if that tuple had just been inserted. If any of those fail, it should
13764 : * ereport(ERROR) and that's that.
13765 : */
13766 54 : snapshot = RegisterSnapshot(GetLatestSnapshot());
13767 54 : slot = table_slot_create(rel, NULL);
13768 54 : scan = table_beginscan(rel, snapshot, 0, NULL);
13769 :
13770 54 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
13771 : "validateForeignKeyConstraint",
13772 : ALLOCSET_SMALL_SIZES);
13773 54 : oldcxt = MemoryContextSwitchTo(perTupCxt);
13774 :
13775 96 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13776 : {
13777 51 : LOCAL_FCINFO(fcinfo, 0);
13778 51 : TriggerData trigdata = {0};
13779 :
13780 51 : CHECK_FOR_INTERRUPTS();
13781 :
13782 : /*
13783 : * Make a call to the trigger function
13784 : *
13785 : * No parameters are passed, but we do set a context
13786 : */
13787 255 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13788 :
13789 : /*
13790 : * We assume RI_FKey_check_ins won't look at flinfo...
13791 : */
13792 51 : trigdata.type = T_TriggerData;
13793 51 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
13794 51 : trigdata.tg_relation = rel;
13795 51 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13796 51 : trigdata.tg_trigslot = slot;
13797 51 : trigdata.tg_trigger = &trig;
13798 :
13799 51 : fcinfo->context = (Node *) &trigdata;
13800 :
13801 51 : RI_FKey_check_ins(fcinfo);
13802 :
13803 42 : MemoryContextReset(perTupCxt);
13804 : }
13805 :
13806 45 : MemoryContextSwitchTo(oldcxt);
13807 45 : MemoryContextDelete(perTupCxt);
13808 45 : table_endscan(scan);
13809 45 : UnregisterSnapshot(snapshot);
13810 45 : ExecDropSingleTupleTableSlot(slot);
13811 : }
13812 :
13813 : /*
13814 : * CreateFKCheckTrigger
13815 : * Creates the insert (on_insert=true) or update "check" trigger that
13816 : * implements a given foreign key
13817 : *
13818 : * Returns the OID of the so created trigger.
13819 : */
13820 : static Oid
13821 3032 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13822 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13823 : bool on_insert)
13824 : {
13825 : ObjectAddress trigAddress;
13826 : CreateTrigStmt *fk_trigger;
13827 :
13828 : /*
13829 : * Note: for a self-referential FK (referencing and referenced tables are
13830 : * the same), it is important that the ON UPDATE action fires before the
13831 : * CHECK action, since both triggers will fire on the same row during an
13832 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13833 : * state of the row. Triggers fire in name order, so we ensure this by
13834 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13835 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13836 : */
13837 3032 : fk_trigger = makeNode(CreateTrigStmt);
13838 3032 : fk_trigger->replace = false;
13839 3032 : fk_trigger->isconstraint = true;
13840 3032 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
13841 3032 : fk_trigger->relation = NULL;
13842 :
13843 : /* Either ON INSERT or ON UPDATE */
13844 3032 : if (on_insert)
13845 : {
13846 1516 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13847 1516 : fk_trigger->events = TRIGGER_TYPE_INSERT;
13848 : }
13849 : else
13850 : {
13851 1516 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13852 1516 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13853 : }
13854 :
13855 3032 : fk_trigger->args = NIL;
13856 3032 : fk_trigger->row = true;
13857 3032 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13858 3032 : fk_trigger->columns = NIL;
13859 3032 : fk_trigger->whenClause = NULL;
13860 3032 : fk_trigger->transitionRels = NIL;
13861 3032 : fk_trigger->deferrable = fkconstraint->deferrable;
13862 3032 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13863 3032 : fk_trigger->constrrel = NULL;
13864 :
13865 3032 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13866 : constraintOid, indexOid, InvalidOid,
13867 : parentTrigOid, NULL, true, false);
13868 :
13869 : /* Make changes-so-far visible */
13870 3032 : CommandCounterIncrement();
13871 :
13872 3032 : return trigAddress.objectId;
13873 : }
13874 :
13875 : /*
13876 : * createForeignKeyActionTriggers
13877 : * Create the referenced-side "action" triggers that implement a foreign
13878 : * key.
13879 : *
13880 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
13881 : * *updateTrigOid.
13882 : */
13883 : static void
13884 1743 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13885 : Oid constraintOid, Oid indexOid,
13886 : Oid parentDelTrigger, Oid parentUpdTrigger,
13887 : Oid *deleteTrigOid, Oid *updateTrigOid)
13888 : {
13889 : CreateTrigStmt *fk_trigger;
13890 : ObjectAddress trigAddress;
13891 :
13892 : /*
13893 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13894 : * DELETE action on the referenced table.
13895 : */
13896 1743 : fk_trigger = makeNode(CreateTrigStmt);
13897 1743 : fk_trigger->replace = false;
13898 1743 : fk_trigger->isconstraint = true;
13899 1743 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13900 1743 : fk_trigger->relation = NULL;
13901 1743 : fk_trigger->args = NIL;
13902 1743 : fk_trigger->row = true;
13903 1743 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13904 1743 : fk_trigger->events = TRIGGER_TYPE_DELETE;
13905 1743 : fk_trigger->columns = NIL;
13906 1743 : fk_trigger->whenClause = NULL;
13907 1743 : fk_trigger->transitionRels = NIL;
13908 1743 : fk_trigger->constrrel = NULL;
13909 :
13910 1743 : switch (fkconstraint->fk_del_action)
13911 : {
13912 1417 : case FKCONSTR_ACTION_NOACTION:
13913 1417 : fk_trigger->deferrable = fkconstraint->deferrable;
13914 1417 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13915 1417 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13916 1417 : break;
13917 15 : case FKCONSTR_ACTION_RESTRICT:
13918 15 : fk_trigger->deferrable = false;
13919 15 : fk_trigger->initdeferred = false;
13920 15 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13921 15 : break;
13922 232 : case FKCONSTR_ACTION_CASCADE:
13923 232 : fk_trigger->deferrable = false;
13924 232 : fk_trigger->initdeferred = false;
13925 232 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13926 232 : break;
13927 49 : case FKCONSTR_ACTION_SETNULL:
13928 49 : fk_trigger->deferrable = false;
13929 49 : fk_trigger->initdeferred = false;
13930 49 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13931 49 : break;
13932 30 : case FKCONSTR_ACTION_SETDEFAULT:
13933 30 : fk_trigger->deferrable = false;
13934 30 : fk_trigger->initdeferred = false;
13935 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13936 30 : break;
13937 0 : default:
13938 0 : elog(ERROR, "unrecognized FK action type: %d",
13939 : (int) fkconstraint->fk_del_action);
13940 : break;
13941 : }
13942 :
13943 1743 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13944 : constraintOid, indexOid, InvalidOid,
13945 : parentDelTrigger, NULL, true, false);
13946 1743 : if (deleteTrigOid)
13947 1743 : *deleteTrigOid = trigAddress.objectId;
13948 :
13949 : /* Make changes-so-far visible */
13950 1743 : CommandCounterIncrement();
13951 :
13952 : /*
13953 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13954 : * UPDATE action on the referenced table.
13955 : */
13956 1743 : fk_trigger = makeNode(CreateTrigStmt);
13957 1743 : fk_trigger->replace = false;
13958 1743 : fk_trigger->isconstraint = true;
13959 1743 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13960 1743 : fk_trigger->relation = NULL;
13961 1743 : fk_trigger->args = NIL;
13962 1743 : fk_trigger->row = true;
13963 1743 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13964 1743 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13965 1743 : fk_trigger->columns = NIL;
13966 1743 : fk_trigger->whenClause = NULL;
13967 1743 : fk_trigger->transitionRels = NIL;
13968 1743 : fk_trigger->constrrel = NULL;
13969 :
13970 1743 : switch (fkconstraint->fk_upd_action)
13971 : {
13972 1514 : case FKCONSTR_ACTION_NOACTION:
13973 1514 : fk_trigger->deferrable = fkconstraint->deferrable;
13974 1514 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13975 1514 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13976 1514 : break;
13977 18 : case FKCONSTR_ACTION_RESTRICT:
13978 18 : fk_trigger->deferrable = false;
13979 18 : fk_trigger->initdeferred = false;
13980 18 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13981 18 : break;
13982 159 : case FKCONSTR_ACTION_CASCADE:
13983 159 : fk_trigger->deferrable = false;
13984 159 : fk_trigger->initdeferred = false;
13985 159 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13986 159 : break;
13987 31 : case FKCONSTR_ACTION_SETNULL:
13988 31 : fk_trigger->deferrable = false;
13989 31 : fk_trigger->initdeferred = false;
13990 31 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13991 31 : break;
13992 21 : case FKCONSTR_ACTION_SETDEFAULT:
13993 21 : fk_trigger->deferrable = false;
13994 21 : fk_trigger->initdeferred = false;
13995 21 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13996 21 : break;
13997 0 : default:
13998 0 : elog(ERROR, "unrecognized FK action type: %d",
13999 : (int) fkconstraint->fk_upd_action);
14000 : break;
14001 : }
14002 :
14003 1743 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
14004 : constraintOid, indexOid, InvalidOid,
14005 : parentUpdTrigger, NULL, true, false);
14006 1743 : if (updateTrigOid)
14007 1743 : *updateTrigOid = trigAddress.objectId;
14008 1743 : }
14009 :
14010 : /*
14011 : * createForeignKeyCheckTriggers
14012 : * Create the referencing-side "check" triggers that implement a foreign
14013 : * key.
14014 : *
14015 : * Returns the OIDs of the so created triggers in *insertTrigOid and
14016 : * *updateTrigOid.
14017 : */
14018 : static void
14019 1516 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
14020 : Constraint *fkconstraint, Oid constraintOid,
14021 : Oid indexOid,
14022 : Oid parentInsTrigger, Oid parentUpdTrigger,
14023 : Oid *insertTrigOid, Oid *updateTrigOid)
14024 : {
14025 1516 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14026 : constraintOid, indexOid,
14027 : parentInsTrigger, true);
14028 1516 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14029 : constraintOid, indexOid,
14030 : parentUpdTrigger, false);
14031 1516 : }
14032 :
14033 : /*
14034 : * ALTER TABLE DROP CONSTRAINT
14035 : *
14036 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
14037 : */
14038 : static void
14039 409 : ATExecDropConstraint(Relation rel, const char *constrName,
14040 : DropBehavior behavior, bool recurse,
14041 : bool missing_ok, LOCKMODE lockmode)
14042 : {
14043 : Relation conrel;
14044 : SysScanDesc scan;
14045 : ScanKeyData skey[3];
14046 : HeapTuple tuple;
14047 409 : bool found = false;
14048 :
14049 409 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14050 :
14051 : /*
14052 : * Find and drop the target constraint
14053 : */
14054 409 : ScanKeyInit(&skey[0],
14055 : Anum_pg_constraint_conrelid,
14056 : BTEqualStrategyNumber, F_OIDEQ,
14057 : ObjectIdGetDatum(RelationGetRelid(rel)));
14058 409 : ScanKeyInit(&skey[1],
14059 : Anum_pg_constraint_contypid,
14060 : BTEqualStrategyNumber, F_OIDEQ,
14061 : ObjectIdGetDatum(InvalidOid));
14062 409 : ScanKeyInit(&skey[2],
14063 : Anum_pg_constraint_conname,
14064 : BTEqualStrategyNumber, F_NAMEEQ,
14065 : CStringGetDatum(constrName));
14066 409 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14067 : true, NULL, 3, skey);
14068 :
14069 : /* There can be at most one matching row */
14070 409 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14071 : {
14072 391 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
14073 : missing_ok, lockmode);
14074 298 : found = true;
14075 : }
14076 :
14077 316 : systable_endscan(scan);
14078 :
14079 316 : if (!found)
14080 : {
14081 18 : if (!missing_ok)
14082 12 : ereport(ERROR,
14083 : errcode(ERRCODE_UNDEFINED_OBJECT),
14084 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14085 : constrName, RelationGetRelationName(rel)));
14086 : else
14087 6 : ereport(NOTICE,
14088 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14089 : constrName, RelationGetRelationName(rel)));
14090 : }
14091 :
14092 304 : table_close(conrel, RowExclusiveLock);
14093 304 : }
14094 :
14095 : /*
14096 : * Remove a constraint, using its pg_constraint tuple
14097 : *
14098 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14099 : * DROP NOT NULL.
14100 : *
14101 : * Returns the address of the constraint being removed.
14102 : */
14103 : static ObjectAddress
14104 615 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
14105 : bool recurse, bool recursing, bool missing_ok,
14106 : LOCKMODE lockmode)
14107 : {
14108 : Relation conrel;
14109 : Form_pg_constraint con;
14110 : ObjectAddress conobj;
14111 : List *children;
14112 615 : bool is_no_inherit_constraint = false;
14113 : char *constrName;
14114 615 : char *colname = NULL;
14115 :
14116 : /* Guard against stack overflow due to overly deep inheritance tree. */
14117 615 : check_stack_depth();
14118 :
14119 : /* At top level, permission check was done in ATPrepCmd, else do it */
14120 615 : if (recursing)
14121 114 : ATSimplePermissions(AT_DropConstraint, rel,
14122 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
14123 :
14124 612 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14125 :
14126 612 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14127 612 : constrName = NameStr(con->conname);
14128 :
14129 : /* Don't allow drop of inherited constraints */
14130 612 : if (con->coninhcount > 0 && !recursing)
14131 78 : ereport(ERROR,
14132 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14133 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14134 : constrName, RelationGetRelationName(rel))));
14135 :
14136 : /*
14137 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14138 : *
14139 : * While doing that, we're in a good position to disallow dropping a not-
14140 : * null constraint underneath a primary key, a replica identity index, or
14141 : * a generated identity column.
14142 : */
14143 534 : if (con->contype == CONSTRAINT_NOTNULL)
14144 : {
14145 157 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14146 157 : AttrNumber attnum = extractNotNullColumn(constraintTup);
14147 : Bitmapset *pkattrs;
14148 : Bitmapset *irattrs;
14149 : HeapTuple atttup;
14150 : Form_pg_attribute attForm;
14151 :
14152 : /* save column name for recursion step */
14153 157 : colname = get_attname(RelationGetRelid(rel), attnum, false);
14154 :
14155 : /*
14156 : * Disallow if it's in the primary key. For partitioned tables we
14157 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14158 : * return NULL if the primary key is invalid; but we still need to
14159 : * protect not-null constraints under such a constraint, so check the
14160 : * slow way.
14161 : */
14162 157 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
14163 :
14164 157 : if (pkattrs == NULL &&
14165 139 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14166 : {
14167 9 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14168 :
14169 9 : if (OidIsValid(pkindex))
14170 : {
14171 0 : Relation pk = relation_open(pkindex, AccessShareLock);
14172 :
14173 0 : pkattrs = NULL;
14174 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14175 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14176 :
14177 0 : relation_close(pk, AccessShareLock);
14178 : }
14179 : }
14180 :
14181 175 : if (pkattrs &&
14182 18 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
14183 12 : ereport(ERROR,
14184 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14185 : errmsg("column \"%s\" is in a primary key",
14186 : get_attname(RelationGetRelid(rel), attnum, false)));
14187 :
14188 : /* Disallow if it's in the replica identity */
14189 145 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
14190 145 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
14191 6 : ereport(ERROR,
14192 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14193 : errmsg("column \"%s\" is in index used as replica identity",
14194 : get_attname(RelationGetRelid(rel), attnum, false)));
14195 :
14196 : /* Disallow if it's a GENERATED AS IDENTITY column */
14197 139 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
14198 139 : if (!HeapTupleIsValid(atttup))
14199 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14200 : attnum, RelationGetRelid(rel));
14201 139 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14202 139 : if (attForm->attidentity != '\0')
14203 0 : ereport(ERROR,
14204 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14205 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
14206 : get_attname(RelationGetRelid(rel), attnum,
14207 : false),
14208 : RelationGetRelationName(rel)));
14209 :
14210 : /* All good -- reset attnotnull if needed */
14211 139 : if (attForm->attnotnull)
14212 : {
14213 139 : attForm->attnotnull = false;
14214 139 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14215 : }
14216 :
14217 139 : table_close(attrel, RowExclusiveLock);
14218 : }
14219 :
14220 516 : is_no_inherit_constraint = con->connoinherit;
14221 :
14222 : /*
14223 : * If it's a foreign-key constraint, we'd better lock the referenced table
14224 : * and check that that's not in use, just as we've already done for the
14225 : * constrained table (else we might, eg, be dropping a trigger that has
14226 : * unfired events). But we can/must skip that in the self-referential
14227 : * case.
14228 : */
14229 516 : if (con->contype == CONSTRAINT_FOREIGN &&
14230 84 : con->confrelid != RelationGetRelid(rel))
14231 : {
14232 : Relation frel;
14233 :
14234 : /* Must match lock taken by RemoveTriggerById: */
14235 84 : frel = table_open(con->confrelid, AccessExclusiveLock);
14236 84 : CheckAlterTableIsSafe(frel);
14237 81 : table_close(frel, NoLock);
14238 : }
14239 :
14240 : /*
14241 : * Perform the actual constraint deletion
14242 : */
14243 513 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14244 513 : performDeletion(&conobj, behavior, 0);
14245 :
14246 : /*
14247 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14248 : * are dropped via the dependency mechanism, so we're done here.
14249 : */
14250 495 : if (con->contype != CONSTRAINT_CHECK &&
14251 315 : con->contype != CONSTRAINT_NOTNULL &&
14252 176 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14253 : {
14254 39 : table_close(conrel, RowExclusiveLock);
14255 39 : return conobj;
14256 : }
14257 :
14258 : /*
14259 : * Propagate to children as appropriate. Unlike most other ALTER
14260 : * routines, we have to do this one level of recursion at a time; we can't
14261 : * use find_all_inheritors to do it in one pass.
14262 : */
14263 456 : if (!is_no_inherit_constraint)
14264 313 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14265 : else
14266 143 : children = NIL;
14267 :
14268 1107 : foreach_oid(childrelid, children)
14269 : {
14270 : Relation childrel;
14271 : HeapTuple tuple;
14272 : Form_pg_constraint childcon;
14273 :
14274 : /* find_inheritance_children already got lock */
14275 201 : childrel = table_open(childrelid, NoLock);
14276 201 : CheckAlterTableIsSafe(childrel);
14277 :
14278 : /*
14279 : * We search for not-null constraints by column name, and others by
14280 : * constraint name.
14281 : */
14282 201 : if (con->contype == CONSTRAINT_NOTNULL)
14283 : {
14284 74 : tuple = findNotNullConstraint(childrelid, colname);
14285 74 : if (!HeapTupleIsValid(tuple))
14286 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14287 : colname, RelationGetRelid(childrel));
14288 : }
14289 : else
14290 : {
14291 : SysScanDesc scan;
14292 : ScanKeyData skey[3];
14293 :
14294 127 : ScanKeyInit(&skey[0],
14295 : Anum_pg_constraint_conrelid,
14296 : BTEqualStrategyNumber, F_OIDEQ,
14297 : ObjectIdGetDatum(childrelid));
14298 127 : ScanKeyInit(&skey[1],
14299 : Anum_pg_constraint_contypid,
14300 : BTEqualStrategyNumber, F_OIDEQ,
14301 : ObjectIdGetDatum(InvalidOid));
14302 127 : ScanKeyInit(&skey[2],
14303 : Anum_pg_constraint_conname,
14304 : BTEqualStrategyNumber, F_NAMEEQ,
14305 : CStringGetDatum(constrName));
14306 127 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14307 : true, NULL, 3, skey);
14308 : /* There can only be one, so no need to loop */
14309 127 : tuple = systable_getnext(scan);
14310 127 : if (!HeapTupleIsValid(tuple))
14311 0 : ereport(ERROR,
14312 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14313 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14314 : constrName,
14315 : RelationGetRelationName(childrel))));
14316 127 : tuple = heap_copytuple(tuple);
14317 127 : systable_endscan(scan);
14318 : }
14319 :
14320 201 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14321 :
14322 : /* Right now only CHECK and not-null constraints can be inherited */
14323 201 : if (childcon->contype != CONSTRAINT_CHECK &&
14324 74 : childcon->contype != CONSTRAINT_NOTNULL)
14325 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14326 :
14327 201 : if (childcon->coninhcount <= 0) /* shouldn't happen */
14328 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14329 : childrelid, NameStr(childcon->conname));
14330 :
14331 201 : if (recurse)
14332 : {
14333 : /*
14334 : * If the child constraint has other definition sources, just
14335 : * decrement its inheritance count; if not, recurse to delete it.
14336 : */
14337 150 : if (childcon->coninhcount == 1 && !childcon->conislocal)
14338 : {
14339 : /* Time to delete this child constraint, too */
14340 114 : dropconstraint_internal(childrel, tuple, behavior,
14341 : recurse, true, missing_ok,
14342 : lockmode);
14343 : }
14344 : else
14345 : {
14346 : /* Child constraint must survive my deletion */
14347 36 : childcon->coninhcount--;
14348 36 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14349 :
14350 : /* Make update visible */
14351 36 : CommandCounterIncrement();
14352 : }
14353 : }
14354 : else
14355 : {
14356 : /*
14357 : * If we were told to drop ONLY in this table (no recursion) and
14358 : * there are no further parents for this constraint, we need to
14359 : * mark the inheritors' constraints as locally defined rather than
14360 : * inherited.
14361 : */
14362 51 : childcon->coninhcount--;
14363 51 : if (childcon->coninhcount == 0)
14364 51 : childcon->conislocal = true;
14365 :
14366 51 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14367 :
14368 : /* Make update visible */
14369 51 : CommandCounterIncrement();
14370 : }
14371 :
14372 198 : heap_freetuple(tuple);
14373 :
14374 198 : table_close(childrel, NoLock);
14375 : }
14376 :
14377 453 : table_close(conrel, RowExclusiveLock);
14378 :
14379 453 : return conobj;
14380 : }
14381 :
14382 : /*
14383 : * ALTER COLUMN TYPE
14384 : *
14385 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14386 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14387 : * transformed (and must be, because we rely on some transformed fields).
14388 : *
14389 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14390 : * table will be done "in parallel" during phase 3, so all the USING
14391 : * expressions should be parsed assuming the original column types. Also,
14392 : * this allows a USING expression to refer to a field that will be dropped.
14393 : *
14394 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14395 : * the first two execution steps in phase 2; they must not see the effects
14396 : * of any other subcommand types, since the USING expressions are parsed
14397 : * against the unmodified table's state.
14398 : */
14399 : static void
14400 713 : ATPrepAlterColumnType(List **wqueue,
14401 : AlteredTableInfo *tab, Relation rel,
14402 : bool recurse, bool recursing,
14403 : AlterTableCmd *cmd, LOCKMODE lockmode,
14404 : AlterTableUtilityContext *context)
14405 : {
14406 713 : char *colName = cmd->name;
14407 713 : ColumnDef *def = (ColumnDef *) cmd->def;
14408 713 : TypeName *typeName = def->typeName;
14409 713 : Node *transform = def->cooked_default;
14410 : HeapTuple tuple;
14411 : Form_pg_attribute attTup;
14412 : AttrNumber attnum;
14413 : Oid targettype;
14414 : int32 targettypmod;
14415 : Oid targetcollid;
14416 : NewColumnValue *newval;
14417 713 : ParseState *pstate = make_parsestate(NULL);
14418 : AclResult aclresult;
14419 : bool is_expr;
14420 :
14421 713 : pstate->p_sourcetext = context->queryString;
14422 :
14423 713 : if (rel->rd_rel->reloftype && !recursing)
14424 3 : ereport(ERROR,
14425 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14426 : errmsg("cannot alter column type of typed table"),
14427 : parser_errposition(pstate, def->location)));
14428 :
14429 : /* lookup the attribute so we can check inheritance status */
14430 710 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14431 710 : if (!HeapTupleIsValid(tuple))
14432 0 : ereport(ERROR,
14433 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14434 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14435 : colName, RelationGetRelationName(rel)),
14436 : parser_errposition(pstate, def->location)));
14437 710 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14438 710 : attnum = attTup->attnum;
14439 :
14440 : /* Can't alter a system attribute */
14441 710 : if (attnum <= 0)
14442 3 : ereport(ERROR,
14443 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14444 : errmsg("cannot alter system column \"%s\"", colName),
14445 : parser_errposition(pstate, def->location)));
14446 :
14447 : /*
14448 : * Cannot specify USING when altering type of a generated column, because
14449 : * that would violate the generation expression.
14450 : */
14451 707 : if (attTup->attgenerated && def->cooked_default)
14452 6 : ereport(ERROR,
14453 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14454 : errmsg("cannot specify USING when altering type of generated column"),
14455 : errdetail("Column \"%s\" is a generated column.", colName),
14456 : parser_errposition(pstate, def->location)));
14457 :
14458 : /*
14459 : * Don't alter inherited columns. At outer level, there had better not be
14460 : * any inherited definition; when recursing, we assume this was checked at
14461 : * the parent level (see below).
14462 : */
14463 701 : if (attTup->attinhcount > 0 && !recursing)
14464 3 : ereport(ERROR,
14465 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14466 : errmsg("cannot alter inherited column \"%s\"", colName),
14467 : parser_errposition(pstate, def->location)));
14468 :
14469 : /* Don't alter columns used in the partition key */
14470 698 : if (has_partition_attrs(rel,
14471 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
14472 : &is_expr))
14473 9 : ereport(ERROR,
14474 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14475 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14476 : colName, RelationGetRelationName(rel)),
14477 : parser_errposition(pstate, def->location)));
14478 :
14479 : /* Look up the target type */
14480 689 : typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14481 :
14482 686 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14483 686 : if (aclresult != ACLCHECK_OK)
14484 6 : aclcheck_error_type(aclresult, targettype);
14485 :
14486 : /* And the collation */
14487 680 : targetcollid = GetColumnDefCollation(pstate, def, targettype);
14488 :
14489 : /* make sure datatype is legal for a column */
14490 1354 : CheckAttributeType(colName, targettype, targetcollid,
14491 677 : list_make1_oid(rel->rd_rel->reltype),
14492 677 : (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14493 :
14494 671 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14495 : {
14496 : /* do nothing */
14497 : }
14498 653 : else if (tab->relkind == RELKIND_RELATION ||
14499 101 : tab->relkind == RELKIND_PARTITIONED_TABLE)
14500 : {
14501 : /*
14502 : * Set up an expression to transform the old data value to the new
14503 : * type. If a USING option was given, use the expression as
14504 : * transformed by transformAlterTableStmt, else just take the old
14505 : * value and try to coerce it. We do this first so that type
14506 : * incompatibility can be detected before we waste effort, and because
14507 : * we need the expression to be parsed against the original table row
14508 : * type.
14509 : */
14510 585 : if (!transform)
14511 : {
14512 471 : transform = (Node *) makeVar(1, attnum,
14513 : attTup->atttypid, attTup->atttypmod,
14514 : attTup->attcollation,
14515 : 0);
14516 : }
14517 :
14518 585 : transform = coerce_to_target_type(pstate,
14519 : transform, exprType(transform),
14520 : targettype, targettypmod,
14521 : COERCION_ASSIGNMENT,
14522 : COERCE_IMPLICIT_CAST,
14523 : -1);
14524 585 : if (transform == NULL)
14525 : {
14526 : /* error text depends on whether USING was specified or not */
14527 12 : if (def->cooked_default != NULL)
14528 3 : ereport(ERROR,
14529 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14530 : errmsg("result of USING clause for column \"%s\""
14531 : " cannot be cast automatically to type %s",
14532 : colName, format_type_be(targettype)),
14533 : errhint("You might need to add an explicit cast.")));
14534 : else
14535 9 : ereport(ERROR,
14536 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14537 : errmsg("column \"%s\" cannot be cast automatically to type %s",
14538 : colName, format_type_be(targettype)),
14539 : !attTup->attgenerated ?
14540 : /* translator: USING is SQL, don't translate it */
14541 : errhint("You might need to specify \"USING %s::%s\".",
14542 : quote_identifier(colName),
14543 : format_type_with_typemod(targettype,
14544 : targettypmod)) : 0));
14545 : }
14546 :
14547 : /* Fix collations after all else */
14548 573 : assign_expr_collations(pstate, transform);
14549 :
14550 : /* Expand virtual generated columns in the expr. */
14551 573 : transform = expand_generated_columns_in_expr(transform, rel, 1);
14552 :
14553 : /* Plan the expr now so we can accurately assess the need to rewrite. */
14554 573 : transform = (Node *) expression_planner((Expr *) transform);
14555 :
14556 : /*
14557 : * Add a work queue item to make ATRewriteTable update the column
14558 : * contents.
14559 : */
14560 573 : newval = palloc0_object(NewColumnValue);
14561 573 : newval->attnum = attnum;
14562 573 : newval->expr = (Expr *) transform;
14563 573 : newval->is_generated = false;
14564 :
14565 573 : tab->newvals = lappend(tab->newvals, newval);
14566 1045 : if (ATColumnChangeRequiresRewrite(transform, attnum))
14567 472 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
14568 : }
14569 68 : else if (transform)
14570 6 : ereport(ERROR,
14571 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14572 : errmsg("\"%s\" is not a table",
14573 : RelationGetRelationName(rel))));
14574 :
14575 653 : if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14576 : {
14577 : /*
14578 : * For relations or columns without storage, do this check now.
14579 : * Regular tables will check it later when the table is being
14580 : * rewritten.
14581 : */
14582 113 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14583 : }
14584 :
14585 629 : ReleaseSysCache(tuple);
14586 :
14587 : /*
14588 : * Recurse manually by queueing a new command for each child, if
14589 : * necessary. We cannot apply ATSimpleRecursion here because we need to
14590 : * remap attribute numbers in the USING expression, if any.
14591 : *
14592 : * If we are told not to recurse, there had better not be any child
14593 : * tables; else the alter would put them out of step.
14594 : */
14595 629 : if (recurse)
14596 : {
14597 500 : Oid relid = RelationGetRelid(rel);
14598 : List *child_oids,
14599 : *child_numparents;
14600 : ListCell *lo,
14601 : *li;
14602 :
14603 500 : child_oids = find_all_inheritors(relid, lockmode,
14604 : &child_numparents);
14605 :
14606 : /*
14607 : * find_all_inheritors does the recursive search of the inheritance
14608 : * hierarchy, so all we have to do is process all of the relids in the
14609 : * list that it returns.
14610 : */
14611 1104 : forboth(lo, child_oids, li, child_numparents)
14612 : {
14613 616 : Oid childrelid = lfirst_oid(lo);
14614 616 : int numparents = lfirst_int(li);
14615 : Relation childrel;
14616 : HeapTuple childtuple;
14617 : Form_pg_attribute childattTup;
14618 :
14619 616 : if (childrelid == relid)
14620 500 : continue;
14621 :
14622 : /* find_all_inheritors already got lock */
14623 116 : childrel = relation_open(childrelid, NoLock);
14624 116 : CheckAlterTableIsSafe(childrel);
14625 :
14626 : /*
14627 : * Verify that the child doesn't have any inherited definitions of
14628 : * this column that came from outside this inheritance hierarchy.
14629 : * (renameatt makes a similar test, though in a different way
14630 : * because of its different recursion mechanism.)
14631 : */
14632 116 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14633 : colName);
14634 116 : if (!HeapTupleIsValid(childtuple))
14635 0 : ereport(ERROR,
14636 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14637 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14638 : colName, RelationGetRelationName(childrel))));
14639 116 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14640 :
14641 116 : if (childattTup->attinhcount > numparents)
14642 3 : ereport(ERROR,
14643 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14644 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14645 : colName, RelationGetRelationName(childrel))));
14646 :
14647 113 : ReleaseSysCache(childtuple);
14648 :
14649 : /*
14650 : * Remap the attribute numbers. If no USING expression was
14651 : * specified, there is no need for this step.
14652 : */
14653 113 : if (def->cooked_default)
14654 : {
14655 : AttrMap *attmap;
14656 : bool found_whole_row;
14657 :
14658 : /* create a copy to scribble on */
14659 39 : cmd = copyObject(cmd);
14660 :
14661 39 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14662 : RelationGetDescr(rel),
14663 : false);
14664 78 : ((ColumnDef *) cmd->def)->cooked_default =
14665 39 : map_variable_attnos(def->cooked_default,
14666 : 1, 0,
14667 : attmap,
14668 : InvalidOid, &found_whole_row);
14669 39 : if (found_whole_row)
14670 3 : ereport(ERROR,
14671 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14672 : errmsg("cannot convert whole-row table reference"),
14673 : errdetail("USING expression contains a whole-row table reference.")));
14674 36 : pfree(attmap);
14675 : }
14676 110 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14677 104 : relation_close(childrel, NoLock);
14678 : }
14679 : }
14680 154 : else if (!recursing &&
14681 25 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
14682 0 : ereport(ERROR,
14683 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14684 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
14685 : colName)));
14686 :
14687 617 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14688 25 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14689 614 : }
14690 :
14691 : /*
14692 : * When the data type of a column is changed, a rewrite might not be required
14693 : * if the new type is sufficiently identical to the old one, and the USING
14694 : * clause isn't trying to insert some other value. It's safe to skip the
14695 : * rewrite in these cases:
14696 : *
14697 : * - the old type is binary coercible to the new type
14698 : * - the new type is an unconstrained domain over the old type
14699 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14700 : *
14701 : * In the case of a constrained domain, we could get by with scanning the
14702 : * table and checking the constraint rather than actually rewriting it, but we
14703 : * don't currently try to do that.
14704 : */
14705 : static bool
14706 573 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
14707 : {
14708 : Assert(expr != NULL);
14709 :
14710 : for (;;)
14711 : {
14712 : /* only one varno, so no need to check that */
14713 632 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14714 101 : return false;
14715 531 : else if (IsA(expr, RelabelType))
14716 53 : expr = (Node *) ((RelabelType *) expr)->arg;
14717 478 : else if (IsA(expr, CoerceToDomain))
14718 : {
14719 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
14720 :
14721 0 : if (DomainHasConstraints(d->resulttype))
14722 0 : return true;
14723 0 : expr = (Node *) d->arg;
14724 : }
14725 478 : else if (IsA(expr, FuncExpr))
14726 : {
14727 375 : FuncExpr *f = (FuncExpr *) expr;
14728 :
14729 375 : switch (f->funcid)
14730 : {
14731 9 : case F_TIMESTAMPTZ_TIMESTAMP:
14732 : case F_TIMESTAMP_TIMESTAMPTZ:
14733 9 : if (TimestampTimestampTzRequiresRewrite())
14734 3 : return true;
14735 : else
14736 6 : expr = linitial(f->args);
14737 6 : break;
14738 366 : default:
14739 366 : return true;
14740 : }
14741 : }
14742 : else
14743 103 : return true;
14744 : }
14745 : }
14746 :
14747 : /*
14748 : * ALTER COLUMN .. SET DATA TYPE
14749 : *
14750 : * Return the address of the modified column.
14751 : */
14752 : static ObjectAddress
14753 596 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
14754 : AlterTableCmd *cmd, LOCKMODE lockmode)
14755 : {
14756 596 : char *colName = cmd->name;
14757 596 : ColumnDef *def = (ColumnDef *) cmd->def;
14758 596 : TypeName *typeName = def->typeName;
14759 : HeapTuple heapTup;
14760 : Form_pg_attribute attTup,
14761 : attOldTup;
14762 : AttrNumber attnum;
14763 : HeapTuple typeTuple;
14764 : Form_pg_type tform;
14765 : Oid targettype;
14766 : int32 targettypmod;
14767 : Oid targetcollid;
14768 : Node *defaultexpr;
14769 : Relation attrelation;
14770 : Relation depRel;
14771 : ScanKeyData key[3];
14772 : SysScanDesc scan;
14773 : HeapTuple depTup;
14774 : ObjectAddress address;
14775 :
14776 : /*
14777 : * Clear all the missing values if we're rewriting the table, since this
14778 : * renders them pointless.
14779 : */
14780 596 : if (tab->rewrite)
14781 : {
14782 : Relation newrel;
14783 :
14784 442 : newrel = table_open(RelationGetRelid(rel), NoLock);
14785 442 : RelationClearMissing(newrel);
14786 442 : relation_close(newrel, NoLock);
14787 : /* make sure we don't conflict with later attribute modifications */
14788 442 : CommandCounterIncrement();
14789 : }
14790 :
14791 596 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14792 :
14793 : /* Look up the target column */
14794 596 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14795 596 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14796 0 : ereport(ERROR,
14797 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14798 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14799 : colName, RelationGetRelationName(rel))));
14800 596 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14801 596 : attnum = attTup->attnum;
14802 596 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14803 :
14804 : /* Check for multiple ALTER TYPE on same column --- can't cope */
14805 596 : if (attTup->atttypid != attOldTup->atttypid ||
14806 596 : attTup->atttypmod != attOldTup->atttypmod)
14807 0 : ereport(ERROR,
14808 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14809 : errmsg("cannot alter type of column \"%s\" twice",
14810 : colName)));
14811 :
14812 : /* Look up the target type (should not fail, since prep found it) */
14813 596 : typeTuple = typenameType(NULL, typeName, &targettypmod);
14814 596 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
14815 596 : targettype = tform->oid;
14816 : /* And the collation */
14817 596 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
14818 :
14819 : /*
14820 : * If there is a default expression for the column, get it and ensure we
14821 : * can coerce it to the new datatype. (We must do this before changing
14822 : * the column type, because build_column_default itself will try to
14823 : * coerce, and will not issue the error message we want if it fails.)
14824 : *
14825 : * We remove any implicit coercion steps at the top level of the old
14826 : * default expression; this has been agreed to satisfy the principle of
14827 : * least surprise. (The conversion to the new column type should act like
14828 : * it started from what the user sees as the stored expression, and the
14829 : * implicit coercions aren't going to be shown.)
14830 : */
14831 596 : if (attTup->atthasdef)
14832 : {
14833 46 : defaultexpr = build_column_default(rel, attnum);
14834 : Assert(defaultexpr);
14835 46 : defaultexpr = strip_implicit_coercions(defaultexpr);
14836 46 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14837 : defaultexpr, exprType(defaultexpr),
14838 : targettype, targettypmod,
14839 : COERCION_ASSIGNMENT,
14840 : COERCE_IMPLICIT_CAST,
14841 : -1);
14842 46 : if (defaultexpr == NULL)
14843 : {
14844 3 : if (attTup->attgenerated)
14845 0 : ereport(ERROR,
14846 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14847 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14848 : colName, format_type_be(targettype))));
14849 : else
14850 3 : ereport(ERROR,
14851 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14852 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14853 : colName, format_type_be(targettype))));
14854 : }
14855 : }
14856 : else
14857 550 : defaultexpr = NULL;
14858 :
14859 : /*
14860 : * Find everything that depends on the column (constraints, indexes, etc),
14861 : * and record enough information to let us recreate the objects.
14862 : *
14863 : * The actual recreation does not happen here, but only after we have
14864 : * performed all the individual ALTER TYPE operations. We have to save
14865 : * the info before executing ALTER TYPE, though, else the deparser will
14866 : * get confused.
14867 : */
14868 593 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
14869 :
14870 : /*
14871 : * Now scan for dependencies of this column on other things. The only
14872 : * things we should find are the dependency on the column datatype and
14873 : * possibly a collation dependency. Those can be removed.
14874 : */
14875 575 : depRel = table_open(DependRelationId, RowExclusiveLock);
14876 :
14877 575 : ScanKeyInit(&key[0],
14878 : Anum_pg_depend_classid,
14879 : BTEqualStrategyNumber, F_OIDEQ,
14880 : ObjectIdGetDatum(RelationRelationId));
14881 575 : ScanKeyInit(&key[1],
14882 : Anum_pg_depend_objid,
14883 : BTEqualStrategyNumber, F_OIDEQ,
14884 : ObjectIdGetDatum(RelationGetRelid(rel)));
14885 575 : ScanKeyInit(&key[2],
14886 : Anum_pg_depend_objsubid,
14887 : BTEqualStrategyNumber, F_INT4EQ,
14888 : Int32GetDatum((int32) attnum));
14889 :
14890 575 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
14891 : NULL, 3, key);
14892 :
14893 577 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14894 : {
14895 2 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14896 : ObjectAddress foundObject;
14897 :
14898 2 : foundObject.classId = foundDep->refclassid;
14899 2 : foundObject.objectId = foundDep->refobjid;
14900 2 : foundObject.objectSubId = foundDep->refobjsubid;
14901 :
14902 2 : if (foundDep->deptype != DEPENDENCY_NORMAL)
14903 0 : elog(ERROR, "found unexpected dependency type '%c'",
14904 : foundDep->deptype);
14905 2 : if (!(foundDep->refclassid == TypeRelationId &&
14906 2 : foundDep->refobjid == attTup->atttypid) &&
14907 0 : !(foundDep->refclassid == CollationRelationId &&
14908 0 : foundDep->refobjid == attTup->attcollation))
14909 0 : elog(ERROR, "found unexpected dependency for column: %s",
14910 : getObjectDescription(&foundObject, false));
14911 :
14912 2 : CatalogTupleDelete(depRel, &depTup->t_self);
14913 : }
14914 :
14915 575 : systable_endscan(scan);
14916 :
14917 575 : table_close(depRel, RowExclusiveLock);
14918 :
14919 : /*
14920 : * Here we go --- change the recorded column type and collation. (Note
14921 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14922 : * fix up the missing value if any.
14923 : */
14924 575 : if (attTup->atthasmissing)
14925 : {
14926 : Datum missingval;
14927 : bool missingNull;
14928 :
14929 : /* if rewrite is true the missing value should already be cleared */
14930 : Assert(tab->rewrite == 0);
14931 :
14932 : /* Get the missing value datum */
14933 3 : missingval = heap_getattr(heapTup,
14934 : Anum_pg_attribute_attmissingval,
14935 : attrelation->rd_att,
14936 : &missingNull);
14937 :
14938 : /* if it's a null array there is nothing to do */
14939 :
14940 3 : if (!missingNull)
14941 : {
14942 : /*
14943 : * Get the datum out of the array and repack it in a new array
14944 : * built with the new type data. We assume that since the table
14945 : * doesn't need rewriting, the actual Datum doesn't need to be
14946 : * changed, only the array metadata.
14947 : */
14948 :
14949 3 : int one = 1;
14950 : bool isNull;
14951 3 : Datum valuesAtt[Natts_pg_attribute] = {0};
14952 3 : bool nullsAtt[Natts_pg_attribute] = {0};
14953 3 : bool replacesAtt[Natts_pg_attribute] = {0};
14954 : HeapTuple newTup;
14955 :
14956 6 : missingval = array_get_element(missingval,
14957 : 1,
14958 : &one,
14959 : 0,
14960 3 : attTup->attlen,
14961 3 : attTup->attbyval,
14962 3 : attTup->attalign,
14963 : &isNull);
14964 3 : missingval = PointerGetDatum(construct_array(&missingval,
14965 : 1,
14966 : targettype,
14967 3 : tform->typlen,
14968 3 : tform->typbyval,
14969 3 : tform->typalign));
14970 :
14971 3 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14972 3 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14973 3 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14974 :
14975 3 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14976 : valuesAtt, nullsAtt, replacesAtt);
14977 3 : heap_freetuple(heapTup);
14978 3 : heapTup = newTup;
14979 3 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14980 : }
14981 : }
14982 :
14983 575 : attTup->atttypid = targettype;
14984 575 : attTup->atttypmod = targettypmod;
14985 575 : attTup->attcollation = targetcollid;
14986 575 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14987 0 : ereport(ERROR,
14988 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14989 : errmsg("too many array dimensions"));
14990 575 : attTup->attndims = list_length(typeName->arrayBounds);
14991 575 : attTup->attlen = tform->typlen;
14992 575 : attTup->attbyval = tform->typbyval;
14993 575 : attTup->attalign = tform->typalign;
14994 575 : attTup->attstorage = tform->typstorage;
14995 575 : attTup->attcompression = InvalidCompressionMethod;
14996 :
14997 575 : ReleaseSysCache(typeTuple);
14998 :
14999 575 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
15000 :
15001 575 : table_close(attrelation, RowExclusiveLock);
15002 :
15003 : /* Install dependencies on new datatype and collation */
15004 575 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
15005 575 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
15006 :
15007 : /*
15008 : * Drop any pg_statistic entry for the column, since it's now wrong type
15009 : */
15010 575 : RemoveStatistics(RelationGetRelid(rel), attnum);
15011 :
15012 575 : InvokeObjectPostAlterHook(RelationRelationId,
15013 : RelationGetRelid(rel), attnum);
15014 :
15015 : /*
15016 : * Update the default, if present, by brute force --- remove and re-add
15017 : * the default. Probably unsafe to take shortcuts, since the new version
15018 : * may well have additional dependencies. (It's okay to do this now,
15019 : * rather than after other ALTER TYPE commands, since the default won't
15020 : * depend on other column types.)
15021 : */
15022 575 : if (defaultexpr)
15023 : {
15024 : /*
15025 : * If it's a GENERATED default, drop its dependency records, in
15026 : * particular its INTERNAL dependency on the column, which would
15027 : * otherwise cause dependency.c to refuse to perform the deletion.
15028 : */
15029 43 : if (attTup->attgenerated)
15030 : {
15031 18 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
15032 :
15033 18 : if (!OidIsValid(attrdefoid))
15034 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15035 : RelationGetRelid(rel), attnum);
15036 18 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
15037 : }
15038 :
15039 : /*
15040 : * Make updates-so-far visible, particularly the new pg_attribute row
15041 : * which will be updated again.
15042 : */
15043 43 : CommandCounterIncrement();
15044 :
15045 : /*
15046 : * We use RESTRICT here for safety, but at present we do not expect
15047 : * anything to depend on the default.
15048 : */
15049 43 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
15050 : true);
15051 :
15052 43 : (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15053 : }
15054 :
15055 575 : ObjectAddressSubSet(address, RelationRelationId,
15056 : RelationGetRelid(rel), attnum);
15057 :
15058 : /* Cleanup */
15059 575 : heap_freetuple(heapTup);
15060 :
15061 575 : return address;
15062 : }
15063 :
15064 : /*
15065 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
15066 : * that depends on the column (constraints, indexes, etc), and record enough
15067 : * information to let us recreate the objects.
15068 : */
15069 : static void
15070 699 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
15071 : Relation rel, AttrNumber attnum, const char *colName)
15072 : {
15073 : Relation depRel;
15074 : ScanKeyData key[3];
15075 : SysScanDesc scan;
15076 : HeapTuple depTup;
15077 :
15078 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15079 :
15080 699 : depRel = table_open(DependRelationId, RowExclusiveLock);
15081 :
15082 699 : ScanKeyInit(&key[0],
15083 : Anum_pg_depend_refclassid,
15084 : BTEqualStrategyNumber, F_OIDEQ,
15085 : ObjectIdGetDatum(RelationRelationId));
15086 699 : ScanKeyInit(&key[1],
15087 : Anum_pg_depend_refobjid,
15088 : BTEqualStrategyNumber, F_OIDEQ,
15089 : ObjectIdGetDatum(RelationGetRelid(rel)));
15090 699 : ScanKeyInit(&key[2],
15091 : Anum_pg_depend_refobjsubid,
15092 : BTEqualStrategyNumber, F_INT4EQ,
15093 : Int32GetDatum((int32) attnum));
15094 :
15095 699 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15096 : NULL, 3, key);
15097 :
15098 1410 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15099 : {
15100 729 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15101 : ObjectAddress foundObject;
15102 :
15103 729 : foundObject.classId = foundDep->classid;
15104 729 : foundObject.objectId = foundDep->objid;
15105 729 : foundObject.objectSubId = foundDep->objsubid;
15106 :
15107 729 : switch (foundObject.classId)
15108 : {
15109 146 : case RelationRelationId:
15110 : {
15111 146 : char relKind = get_rel_relkind(foundObject.objectId);
15112 :
15113 146 : if (relKind == RELKIND_INDEX ||
15114 : relKind == RELKIND_PARTITIONED_INDEX)
15115 : {
15116 : Assert(foundObject.objectSubId == 0);
15117 127 : RememberIndexForRebuilding(foundObject.objectId, tab);
15118 : }
15119 19 : else if (relKind == RELKIND_SEQUENCE)
15120 : {
15121 : /*
15122 : * This must be a SERIAL column's sequence. We need
15123 : * not do anything to it.
15124 : */
15125 : Assert(foundObject.objectSubId == 0);
15126 : }
15127 : else
15128 : {
15129 : /* Not expecting any other direct dependencies... */
15130 0 : elog(ERROR, "unexpected object depending on column: %s",
15131 : getObjectDescription(&foundObject, false));
15132 : }
15133 146 : break;
15134 : }
15135 :
15136 376 : case ConstraintRelationId:
15137 : Assert(foundObject.objectSubId == 0);
15138 376 : RememberConstraintForRebuilding(foundObject.objectId, tab);
15139 376 : break;
15140 :
15141 0 : case ProcedureRelationId:
15142 :
15143 : /*
15144 : * A new-style SQL function can depend on a column, if that
15145 : * column is referenced in the parsed function body. Ideally
15146 : * we'd automatically update the function by deparsing and
15147 : * reparsing it, but that's risky and might well fail anyhow.
15148 : * FIXME someday.
15149 : *
15150 : * This is only a problem for AT_AlterColumnType, not
15151 : * AT_SetExpression.
15152 : */
15153 0 : if (subtype == AT_AlterColumnType)
15154 0 : ereport(ERROR,
15155 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15156 : errmsg("cannot alter type of a column used by a function or procedure"),
15157 : errdetail("%s depends on column \"%s\"",
15158 : getObjectDescription(&foundObject, false),
15159 : colName)));
15160 0 : break;
15161 :
15162 6 : case RewriteRelationId:
15163 :
15164 : /*
15165 : * View/rule bodies have pretty much the same issues as
15166 : * function bodies. FIXME someday.
15167 : */
15168 6 : if (subtype == AT_AlterColumnType)
15169 6 : ereport(ERROR,
15170 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15171 : errmsg("cannot alter type of a column used by a view or rule"),
15172 : errdetail("%s depends on column \"%s\"",
15173 : getObjectDescription(&foundObject, false),
15174 : colName)));
15175 0 : break;
15176 :
15177 0 : case TriggerRelationId:
15178 :
15179 : /*
15180 : * A trigger can depend on a column because the column is
15181 : * specified as an update target, or because the column is
15182 : * used in the trigger's WHEN condition. The first case would
15183 : * not require any extra work, but the second case would
15184 : * require updating the WHEN expression, which has the same
15185 : * issues as above. Since we can't easily tell which case
15186 : * applies, we punt for both. FIXME someday.
15187 : */
15188 0 : if (subtype == AT_AlterColumnType)
15189 0 : ereport(ERROR,
15190 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15191 : errmsg("cannot alter type of a column used in a trigger definition"),
15192 : errdetail("%s depends on column \"%s\"",
15193 : getObjectDescription(&foundObject, false),
15194 : colName)));
15195 0 : break;
15196 :
15197 0 : case PolicyRelationId:
15198 :
15199 : /*
15200 : * A policy can depend on a column because the column is
15201 : * specified in the policy's USING or WITH CHECK qual
15202 : * expressions. It might be possible to rewrite and recheck
15203 : * the policy expression, but punt for now. It's certainly
15204 : * easy enough to remove and recreate the policy; still, FIXME
15205 : * someday.
15206 : */
15207 0 : if (subtype == AT_AlterColumnType)
15208 0 : ereport(ERROR,
15209 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15210 : errmsg("cannot alter type of a column used in a policy definition"),
15211 : errdetail("%s depends on column \"%s\"",
15212 : getObjectDescription(&foundObject, false),
15213 : colName)));
15214 0 : break;
15215 :
15216 161 : case AttrDefaultRelationId:
15217 : {
15218 161 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
15219 :
15220 310 : if (col.objectId == RelationGetRelid(rel) &&
15221 161 : col.objectSubId == attnum)
15222 : {
15223 : /*
15224 : * Ignore the column's own default expression. The
15225 : * caller deals with it.
15226 : */
15227 : }
15228 : else
15229 : {
15230 : /*
15231 : * This must be a reference from the expression of a
15232 : * generated column elsewhere in the same table.
15233 : * Changing the type/generated expression of a column
15234 : * that is used by a generated column is not allowed
15235 : * by SQL standard, so just punt for now. It might be
15236 : * doable with some thinking and effort.
15237 : */
15238 12 : if (subtype == AT_AlterColumnType)
15239 12 : ereport(ERROR,
15240 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15241 : errmsg("cannot alter type of a column used by a generated column"),
15242 : errdetail("Column \"%s\" is used by generated column \"%s\".",
15243 : colName,
15244 : get_attname(col.objectId,
15245 : col.objectSubId,
15246 : false))));
15247 : }
15248 149 : break;
15249 : }
15250 :
15251 40 : case StatisticExtRelationId:
15252 :
15253 : /*
15254 : * Give the extended-stats machinery a chance to fix anything
15255 : * that this column type change would break.
15256 : */
15257 40 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
15258 40 : break;
15259 :
15260 0 : case PublicationRelRelationId:
15261 :
15262 : /*
15263 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15264 : * clause. Same issues as above. FIXME someday.
15265 : */
15266 0 : if (subtype == AT_AlterColumnType)
15267 0 : ereport(ERROR,
15268 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15269 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
15270 : errdetail("%s depends on column \"%s\"",
15271 : getObjectDescription(&foundObject, false),
15272 : colName)));
15273 0 : break;
15274 :
15275 0 : default:
15276 :
15277 : /*
15278 : * We don't expect any other sorts of objects to depend on a
15279 : * column.
15280 : */
15281 0 : elog(ERROR, "unexpected object depending on column: %s",
15282 : getObjectDescription(&foundObject, false));
15283 : break;
15284 : }
15285 : }
15286 :
15287 681 : systable_endscan(scan);
15288 681 : table_close(depRel, NoLock);
15289 681 : }
15290 :
15291 : /*
15292 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
15293 : * needs to be reset.
15294 : */
15295 : static void
15296 231 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
15297 : {
15298 231 : if (!get_index_isreplident(indoid))
15299 222 : return;
15300 :
15301 9 : if (tab->replicaIdentityIndex)
15302 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15303 :
15304 9 : tab->replicaIdentityIndex = get_rel_name(indoid);
15305 : }
15306 :
15307 : /*
15308 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
15309 : */
15310 : static void
15311 231 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
15312 : {
15313 231 : if (!get_index_isclustered(indoid))
15314 222 : return;
15315 :
15316 9 : if (tab->clusterOnIndex)
15317 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15318 :
15319 9 : tab->clusterOnIndex = get_rel_name(indoid);
15320 : }
15321 :
15322 : /*
15323 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15324 : * to be rebuilt (which we might already know).
15325 : */
15326 : static void
15327 382 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
15328 : {
15329 : /*
15330 : * This de-duplication check is critical for two independent reasons: we
15331 : * mustn't try to recreate the same constraint twice, and if a constraint
15332 : * depends on more than one column whose type is to be altered, we must
15333 : * capture its definition string before applying any of the column type
15334 : * changes. ruleutils.c will get confused if we ask again later.
15335 : */
15336 382 : if (!list_member_oid(tab->changedConstraintOids, conoid))
15337 : {
15338 : /* OK, capture the constraint's existing definition string */
15339 328 : char *defstring = pg_get_constraintdef_command(conoid);
15340 : Oid indoid;
15341 :
15342 : /*
15343 : * It is critical to create not-null constraints ahead of primary key
15344 : * indexes; otherwise, the not-null constraint would be created by the
15345 : * primary key, and the constraint name would be wrong.
15346 : */
15347 328 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15348 : {
15349 111 : tab->changedConstraintOids = lcons_oid(conoid,
15350 : tab->changedConstraintOids);
15351 111 : tab->changedConstraintDefs = lcons(defstring,
15352 : tab->changedConstraintDefs);
15353 : }
15354 : else
15355 : {
15356 :
15357 217 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
15358 : conoid);
15359 217 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
15360 : defstring);
15361 : }
15362 :
15363 : /*
15364 : * For the index of a constraint, if any, remember if it is used for
15365 : * the table's replica identity or if it is a clustered index, so that
15366 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15367 : * those properties.
15368 : */
15369 328 : indoid = get_constraint_index(conoid);
15370 328 : if (OidIsValid(indoid))
15371 : {
15372 114 : RememberReplicaIdentityForRebuilding(indoid, tab);
15373 114 : RememberClusterOnForRebuilding(indoid, tab);
15374 : }
15375 : }
15376 382 : }
15377 :
15378 : /*
15379 : * Subroutine for ATExecAlterColumnType: remember that an index needs
15380 : * to be rebuilt (which we might already know).
15381 : */
15382 : static void
15383 127 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
15384 : {
15385 : /*
15386 : * This de-duplication check is critical for two independent reasons: we
15387 : * mustn't try to recreate the same index twice, and if an index depends
15388 : * on more than one column whose type is to be altered, we must capture
15389 : * its definition string before applying any of the column type changes.
15390 : * ruleutils.c will get confused if we ask again later.
15391 : */
15392 127 : if (!list_member_oid(tab->changedIndexOids, indoid))
15393 : {
15394 : /*
15395 : * Before adding it as an index-to-rebuild, we'd better see if it
15396 : * belongs to a constraint, and if so rebuild the constraint instead.
15397 : * Typically this check fails, because constraint indexes normally
15398 : * have only dependencies on their constraint. But it's possible for
15399 : * such an index to also have direct dependencies on table columns,
15400 : * for example with a partial exclusion constraint.
15401 : */
15402 123 : Oid conoid = get_index_constraint(indoid);
15403 :
15404 123 : if (OidIsValid(conoid))
15405 : {
15406 6 : RememberConstraintForRebuilding(conoid, tab);
15407 : }
15408 : else
15409 : {
15410 : /* OK, capture the index's existing definition string */
15411 117 : char *defstring = pg_get_indexdef_string(indoid);
15412 :
15413 117 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
15414 : indoid);
15415 117 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
15416 : defstring);
15417 :
15418 : /*
15419 : * Remember if this index is used for the table's replica identity
15420 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15421 : * can queue up commands necessary to restore those properties.
15422 : */
15423 117 : RememberReplicaIdentityForRebuilding(indoid, tab);
15424 117 : RememberClusterOnForRebuilding(indoid, tab);
15425 : }
15426 : }
15427 127 : }
15428 :
15429 : /*
15430 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
15431 : * needs to be rebuilt (which we might already know).
15432 : */
15433 : static void
15434 40 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
15435 : {
15436 : /*
15437 : * This de-duplication check is critical for two independent reasons: we
15438 : * mustn't try to recreate the same statistics object twice, and if the
15439 : * statistics object depends on more than one column whose type is to be
15440 : * altered, we must capture its definition string before applying any of
15441 : * the type changes. ruleutils.c will get confused if we ask again later.
15442 : */
15443 40 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15444 : {
15445 : /* OK, capture the statistics object's existing definition string */
15446 40 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
15447 :
15448 40 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
15449 : stxoid);
15450 40 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
15451 : defstring);
15452 : }
15453 40 : }
15454 :
15455 : /*
15456 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15457 : * operations for a particular relation. We have to drop and recreate all the
15458 : * indexes and constraints that depend on the altered columns. We do the
15459 : * actual dropping here, but re-creation is managed by adding work queue
15460 : * entries to do those steps later.
15461 : */
15462 : static void
15463 660 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
15464 : {
15465 : ObjectAddress obj;
15466 : ObjectAddresses *objects;
15467 : ListCell *def_item;
15468 : ListCell *oid_item;
15469 :
15470 : /*
15471 : * Collect all the constraints and indexes to drop so we can process them
15472 : * in a single call. That way we don't have to worry about dependencies
15473 : * among them.
15474 : */
15475 660 : objects = new_object_addresses();
15476 :
15477 : /*
15478 : * Re-parse the index and constraint definitions, and attach them to the
15479 : * appropriate work queue entries. We do this before dropping because in
15480 : * the case of a constraint on another table, we might not yet have
15481 : * exclusive lock on the table the constraint is attached to, and we need
15482 : * to get that before reparsing/dropping. (That's possible at least for
15483 : * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15484 : * requires a dependency on the target table's composite type in the other
15485 : * table's constraint expressions.)
15486 : *
15487 : * We can't rely on the output of deparsing to tell us which relation to
15488 : * operate on, because concurrent activity might have made the name
15489 : * resolve differently. Instead, we've got to use the OID of the
15490 : * constraint or index we're processing to figure out which relation to
15491 : * operate on.
15492 : */
15493 988 : forboth(oid_item, tab->changedConstraintOids,
15494 : def_item, tab->changedConstraintDefs)
15495 : {
15496 328 : Oid oldId = lfirst_oid(oid_item);
15497 : HeapTuple tup;
15498 : Form_pg_constraint con;
15499 : Oid relid;
15500 : Oid confrelid;
15501 : bool conislocal;
15502 :
15503 328 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15504 328 : if (!HeapTupleIsValid(tup)) /* should not happen */
15505 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15506 328 : con = (Form_pg_constraint) GETSTRUCT(tup);
15507 328 : if (OidIsValid(con->conrelid))
15508 321 : relid = con->conrelid;
15509 : else
15510 : {
15511 : /* must be a domain constraint */
15512 7 : relid = get_typ_typrelid(getBaseType(con->contypid));
15513 7 : if (!OidIsValid(relid))
15514 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15515 : }
15516 328 : confrelid = con->confrelid;
15517 328 : conislocal = con->conislocal;
15518 328 : ReleaseSysCache(tup);
15519 :
15520 328 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
15521 328 : add_exact_object_address(&obj, objects);
15522 :
15523 : /*
15524 : * If the constraint is inherited (only), we don't want to inject a
15525 : * new definition here; it'll get recreated when
15526 : * ATAddCheckNNConstraint recurses from adding the parent table's
15527 : * constraint. But we had to carry the info this far so that we can
15528 : * drop the constraint below.
15529 : */
15530 328 : if (!conislocal)
15531 20 : continue;
15532 :
15533 : /*
15534 : * When rebuilding another table's constraint that references the
15535 : * table we're modifying, we might not yet have any lock on the other
15536 : * table, so get one now. We'll need AccessExclusiveLock for the DROP
15537 : * CONSTRAINT step, so there's no value in asking for anything weaker.
15538 : */
15539 308 : if (relid != tab->relid)
15540 27 : LockRelationOid(relid, AccessExclusiveLock);
15541 :
15542 308 : ATPostAlterTypeParse(oldId, relid, confrelid,
15543 308 : (char *) lfirst(def_item),
15544 308 : wqueue, lockmode, tab->rewrite);
15545 : }
15546 777 : forboth(oid_item, tab->changedIndexOids,
15547 : def_item, tab->changedIndexDefs)
15548 : {
15549 117 : Oid oldId = lfirst_oid(oid_item);
15550 : Oid relid;
15551 :
15552 117 : relid = IndexGetRelation(oldId, false);
15553 :
15554 : /*
15555 : * As above, make sure we have lock on the index's table if it's not
15556 : * the same table.
15557 : */
15558 117 : if (relid != tab->relid)
15559 9 : LockRelationOid(relid, AccessExclusiveLock);
15560 :
15561 117 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15562 117 : (char *) lfirst(def_item),
15563 117 : wqueue, lockmode, tab->rewrite);
15564 :
15565 117 : ObjectAddressSet(obj, RelationRelationId, oldId);
15566 117 : add_exact_object_address(&obj, objects);
15567 : }
15568 :
15569 : /* add dependencies for new statistics */
15570 700 : forboth(oid_item, tab->changedStatisticsOids,
15571 : def_item, tab->changedStatisticsDefs)
15572 : {
15573 40 : Oid oldId = lfirst_oid(oid_item);
15574 : Oid relid;
15575 :
15576 40 : relid = StatisticsGetRelation(oldId, false);
15577 :
15578 : /*
15579 : * As above, make sure we have lock on the statistics object's table
15580 : * if it's not the same table. However, we take
15581 : * ShareUpdateExclusiveLock here, aligning with the lock level used in
15582 : * CreateStatistics and RemoveStatisticsById.
15583 : *
15584 : * CAUTION: this should be done after all cases that grab
15585 : * AccessExclusiveLock, else we risk causing deadlock due to needing
15586 : * to promote our table lock.
15587 : */
15588 40 : if (relid != tab->relid)
15589 9 : LockRelationOid(relid, ShareUpdateExclusiveLock);
15590 :
15591 40 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15592 40 : (char *) lfirst(def_item),
15593 40 : wqueue, lockmode, tab->rewrite);
15594 :
15595 40 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15596 40 : add_exact_object_address(&obj, objects);
15597 : }
15598 :
15599 : /*
15600 : * Queue up command to restore replica identity index marking
15601 : */
15602 660 : if (tab->replicaIdentityIndex)
15603 : {
15604 9 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15605 9 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
15606 :
15607 9 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15608 9 : subcmd->name = tab->replicaIdentityIndex;
15609 9 : cmd->subtype = AT_ReplicaIdentity;
15610 9 : cmd->def = (Node *) subcmd;
15611 :
15612 : /* do it after indexes and constraints */
15613 9 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15614 9 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15615 : }
15616 :
15617 : /*
15618 : * Queue up command to restore marking of index used for cluster.
15619 : */
15620 660 : if (tab->clusterOnIndex)
15621 : {
15622 9 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15623 :
15624 9 : cmd->subtype = AT_ClusterOn;
15625 9 : cmd->name = tab->clusterOnIndex;
15626 :
15627 : /* do it after indexes and constraints */
15628 9 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15629 9 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15630 : }
15631 :
15632 : /*
15633 : * It should be okay to use DROP_RESTRICT here, since nothing else should
15634 : * be depending on these objects.
15635 : */
15636 660 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
15637 :
15638 660 : free_object_addresses(objects);
15639 :
15640 : /*
15641 : * The objects will get recreated during subsequent passes over the work
15642 : * queue.
15643 : */
15644 660 : }
15645 :
15646 : /*
15647 : * Parse the previously-saved definition string for a constraint, index or
15648 : * statistics object against the newly-established column data type(s), and
15649 : * queue up the resulting command parsetrees for execution.
15650 : *
15651 : * This might fail if, for example, you have a WHERE clause that uses an
15652 : * operator that's not available for the new column type.
15653 : */
15654 : static void
15655 465 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15656 : List **wqueue, LOCKMODE lockmode, bool rewrite)
15657 : {
15658 : List *raw_parsetree_list;
15659 : List *querytree_list;
15660 : ListCell *list_item;
15661 : Relation rel;
15662 :
15663 : /*
15664 : * We expect that we will get only ALTER TABLE and CREATE INDEX
15665 : * statements. Hence, there is no need to pass them through
15666 : * parse_analyze_*() or the rewriter, but instead we need to pass them
15667 : * through parse_utilcmd.c to make them ready for execution.
15668 : */
15669 465 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15670 465 : querytree_list = NIL;
15671 930 : foreach(list_item, raw_parsetree_list)
15672 : {
15673 465 : RawStmt *rs = lfirst_node(RawStmt, list_item);
15674 465 : Node *stmt = rs->stmt;
15675 :
15676 465 : if (IsA(stmt, IndexStmt))
15677 117 : querytree_list = lappend(querytree_list,
15678 117 : transformIndexStmt(oldRelId,
15679 : (IndexStmt *) stmt,
15680 : cmd));
15681 348 : else if (IsA(stmt, AlterTableStmt))
15682 : {
15683 : List *beforeStmts;
15684 : List *afterStmts;
15685 :
15686 301 : stmt = (Node *) transformAlterTableStmt(oldRelId,
15687 : (AlterTableStmt *) stmt,
15688 : cmd,
15689 : &beforeStmts,
15690 : &afterStmts);
15691 301 : querytree_list = list_concat(querytree_list, beforeStmts);
15692 301 : querytree_list = lappend(querytree_list, stmt);
15693 301 : querytree_list = list_concat(querytree_list, afterStmts);
15694 : }
15695 47 : else if (IsA(stmt, CreateStatsStmt))
15696 40 : querytree_list = lappend(querytree_list,
15697 40 : transformStatsStmt(oldRelId,
15698 : (CreateStatsStmt *) stmt,
15699 : cmd));
15700 : else
15701 7 : querytree_list = lappend(querytree_list, stmt);
15702 : }
15703 :
15704 : /* Caller should already have acquired whatever lock we need. */
15705 465 : rel = relation_open(oldRelId, NoLock);
15706 :
15707 : /*
15708 : * Attach each generated command to the proper place in the work queue.
15709 : * Note this could result in creation of entirely new work-queue entries.
15710 : *
15711 : * Also note that we have to tweak the command subtypes, because it turns
15712 : * out that re-creation of indexes and constraints has to act a bit
15713 : * differently from initial creation.
15714 : */
15715 930 : foreach(list_item, querytree_list)
15716 : {
15717 465 : Node *stm = (Node *) lfirst(list_item);
15718 : AlteredTableInfo *tab;
15719 :
15720 465 : tab = ATGetQueueEntry(wqueue, rel);
15721 :
15722 465 : if (IsA(stm, IndexStmt))
15723 : {
15724 117 : IndexStmt *stmt = (IndexStmt *) stm;
15725 : AlterTableCmd *newcmd;
15726 :
15727 117 : if (!rewrite)
15728 31 : TryReuseIndex(oldId, stmt);
15729 117 : stmt->reset_default_tblspc = true;
15730 : /* keep the index's comment */
15731 117 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15732 :
15733 117 : newcmd = makeNode(AlterTableCmd);
15734 117 : newcmd->subtype = AT_ReAddIndex;
15735 117 : newcmd->def = (Node *) stmt;
15736 117 : tab->subcmds[AT_PASS_OLD_INDEX] =
15737 117 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15738 : }
15739 348 : else if (IsA(stm, AlterTableStmt))
15740 : {
15741 301 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
15742 : ListCell *lcmd;
15743 :
15744 602 : foreach(lcmd, stmt->cmds)
15745 : {
15746 301 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
15747 :
15748 301 : if (cmd->subtype == AT_AddIndex)
15749 : {
15750 : IndexStmt *indstmt;
15751 : Oid indoid;
15752 :
15753 114 : indstmt = castNode(IndexStmt, cmd->def);
15754 114 : indoid = get_constraint_index(oldId);
15755 :
15756 114 : if (!rewrite)
15757 24 : TryReuseIndex(indoid, indstmt);
15758 : /* keep any comment on the index */
15759 114 : indstmt->idxcomment = GetComment(indoid,
15760 : RelationRelationId, 0);
15761 114 : indstmt->reset_default_tblspc = true;
15762 :
15763 114 : cmd->subtype = AT_ReAddIndex;
15764 114 : tab->subcmds[AT_PASS_OLD_INDEX] =
15765 114 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15766 :
15767 : /* recreate any comment on the constraint */
15768 114 : RebuildConstraintComment(tab,
15769 : AT_PASS_OLD_INDEX,
15770 : oldId,
15771 : rel,
15772 : NIL,
15773 114 : indstmt->idxname);
15774 : }
15775 187 : else if (cmd->subtype == AT_AddConstraint)
15776 : {
15777 187 : Constraint *con = castNode(Constraint, cmd->def);
15778 :
15779 187 : con->old_pktable_oid = refRelId;
15780 : /* rewriting neither side of a FK */
15781 187 : if (con->contype == CONSTR_FOREIGN &&
15782 36 : !rewrite && tab->rewrite == 0)
15783 3 : TryReuseForeignKey(oldId, con);
15784 187 : con->reset_default_tblspc = true;
15785 187 : cmd->subtype = AT_ReAddConstraint;
15786 187 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15787 187 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15788 :
15789 : /*
15790 : * Recreate any comment on the constraint. If we have
15791 : * recreated a primary key, then transformTableConstraint
15792 : * has added an unnamed not-null constraint here; skip
15793 : * this in that case.
15794 : */
15795 187 : if (con->conname)
15796 187 : RebuildConstraintComment(tab,
15797 : AT_PASS_OLD_CONSTR,
15798 : oldId,
15799 : rel,
15800 : NIL,
15801 187 : con->conname);
15802 : else
15803 : Assert(con->contype == CONSTR_NOTNULL);
15804 : }
15805 : else
15806 0 : elog(ERROR, "unexpected statement subtype: %d",
15807 : (int) cmd->subtype);
15808 : }
15809 : }
15810 47 : else if (IsA(stm, AlterDomainStmt))
15811 : {
15812 7 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
15813 :
15814 7 : if (stmt->subtype == AD_AddConstraint)
15815 : {
15816 7 : Constraint *con = castNode(Constraint, stmt->def);
15817 7 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15818 :
15819 7 : cmd->subtype = AT_ReAddDomainConstraint;
15820 7 : cmd->def = (Node *) stmt;
15821 7 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15822 7 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15823 :
15824 : /* recreate any comment on the constraint */
15825 7 : RebuildConstraintComment(tab,
15826 : AT_PASS_OLD_CONSTR,
15827 : oldId,
15828 : NULL,
15829 : stmt->typeName,
15830 7 : con->conname);
15831 : }
15832 : else
15833 0 : elog(ERROR, "unexpected statement subtype: %d",
15834 : (int) stmt->subtype);
15835 : }
15836 40 : else if (IsA(stm, CreateStatsStmt))
15837 : {
15838 40 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
15839 : AlterTableCmd *newcmd;
15840 :
15841 : /* keep the statistics object's comment */
15842 40 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15843 :
15844 40 : newcmd = makeNode(AlterTableCmd);
15845 40 : newcmd->subtype = AT_ReAddStatistics;
15846 40 : newcmd->def = (Node *) stmt;
15847 40 : tab->subcmds[AT_PASS_MISC] =
15848 40 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15849 : }
15850 : else
15851 0 : elog(ERROR, "unexpected statement type: %d",
15852 : (int) nodeTag(stm));
15853 : }
15854 :
15855 465 : relation_close(rel, NoLock);
15856 465 : }
15857 :
15858 : /*
15859 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15860 : * for a table or domain constraint that is being rebuilt.
15861 : *
15862 : * objid is the OID of the constraint.
15863 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15864 : * as a string list) for a domain constraint.
15865 : * (We could dig that info, as well as the conname, out of the pg_constraint
15866 : * entry; but callers already have them so might as well pass them.)
15867 : */
15868 : static void
15869 308 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
15870 : Relation rel, List *domname,
15871 : const char *conname)
15872 : {
15873 : CommentStmt *cmd;
15874 : char *comment_str;
15875 : AlterTableCmd *newcmd;
15876 :
15877 : /* Look for comment for object wanted, and leave if none */
15878 308 : comment_str = GetComment(objid, ConstraintRelationId, 0);
15879 308 : if (comment_str == NULL)
15880 263 : return;
15881 :
15882 : /* Build CommentStmt node, copying all input data for safety */
15883 45 : cmd = makeNode(CommentStmt);
15884 45 : if (rel)
15885 : {
15886 39 : cmd->objtype = OBJECT_TABCONSTRAINT;
15887 39 : cmd->object = (Node *)
15888 39 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
15889 : makeString(pstrdup(RelationGetRelationName(rel))),
15890 : makeString(pstrdup(conname)));
15891 : }
15892 : else
15893 : {
15894 6 : cmd->objtype = OBJECT_DOMCONSTRAINT;
15895 6 : cmd->object = (Node *)
15896 6 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
15897 : makeString(pstrdup(conname)));
15898 : }
15899 45 : cmd->comment = comment_str;
15900 :
15901 : /* Append it to list of commands */
15902 45 : newcmd = makeNode(AlterTableCmd);
15903 45 : newcmd->subtype = AT_ReAddComment;
15904 45 : newcmd->def = (Node *) cmd;
15905 45 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15906 : }
15907 :
15908 : /*
15909 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15910 : * for the real analysis, then mutates the IndexStmt based on that verdict.
15911 : */
15912 : static void
15913 55 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
15914 : {
15915 55 : if (CheckIndexCompatible(oldId,
15916 55 : stmt->accessMethod,
15917 55 : stmt->indexParams,
15918 55 : stmt->excludeOpNames,
15919 55 : stmt->iswithoutoverlaps))
15920 : {
15921 52 : Relation irel = index_open(oldId, NoLock);
15922 :
15923 : /* If it's a partitioned index, there is no storage to share. */
15924 52 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15925 : {
15926 37 : stmt->oldNumber = irel->rd_locator.relNumber;
15927 37 : stmt->oldCreateSubid = irel->rd_createSubid;
15928 37 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15929 : }
15930 52 : index_close(irel, NoLock);
15931 : }
15932 55 : }
15933 :
15934 : /*
15935 : * Subroutine for ATPostAlterTypeParse().
15936 : *
15937 : * Stash the old P-F equality operator into the Constraint node, for possible
15938 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15939 : * this constraint can be skipped.
15940 : */
15941 : static void
15942 3 : TryReuseForeignKey(Oid oldId, Constraint *con)
15943 : {
15944 : HeapTuple tup;
15945 : Datum adatum;
15946 : ArrayType *arr;
15947 : Oid *rawarr;
15948 : int numkeys;
15949 : int i;
15950 :
15951 : Assert(con->contype == CONSTR_FOREIGN);
15952 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15953 :
15954 3 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15955 3 : if (!HeapTupleIsValid(tup)) /* should not happen */
15956 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15957 :
15958 3 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15959 : Anum_pg_constraint_conpfeqop);
15960 3 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15961 3 : numkeys = ARR_DIMS(arr)[0];
15962 : /* test follows the one in ri_FetchConstraintInfo() */
15963 3 : if (ARR_NDIM(arr) != 1 ||
15964 3 : ARR_HASNULL(arr) ||
15965 3 : ARR_ELEMTYPE(arr) != OIDOID)
15966 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
15967 3 : rawarr = (Oid *) ARR_DATA_PTR(arr);
15968 :
15969 : /* stash a List of the operator Oids in our Constraint node */
15970 6 : for (i = 0; i < numkeys; i++)
15971 3 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15972 :
15973 3 : ReleaseSysCache(tup);
15974 3 : }
15975 :
15976 : /*
15977 : * ALTER COLUMN .. OPTIONS ( ... )
15978 : *
15979 : * Returns the address of the modified column
15980 : */
15981 : static ObjectAddress
15982 86 : ATExecAlterColumnGenericOptions(Relation rel,
15983 : const char *colName,
15984 : List *options,
15985 : LOCKMODE lockmode)
15986 : {
15987 : Relation ftrel;
15988 : Relation attrel;
15989 : ForeignServer *server;
15990 : ForeignDataWrapper *fdw;
15991 : HeapTuple tuple;
15992 : HeapTuple newtuple;
15993 : bool isnull;
15994 : Datum repl_val[Natts_pg_attribute];
15995 : bool repl_null[Natts_pg_attribute];
15996 : bool repl_repl[Natts_pg_attribute];
15997 : Datum datum;
15998 : Form_pg_foreign_table fttableform;
15999 : Form_pg_attribute atttableform;
16000 : AttrNumber attnum;
16001 : ObjectAddress address;
16002 :
16003 86 : if (options == NIL)
16004 0 : return InvalidObjectAddress;
16005 :
16006 : /* First, determine FDW validator associated to the foreign table. */
16007 86 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
16008 86 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
16009 86 : if (!HeapTupleIsValid(tuple))
16010 0 : ereport(ERROR,
16011 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16012 : errmsg("foreign table \"%s\" does not exist",
16013 : RelationGetRelationName(rel))));
16014 86 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16015 86 : server = GetForeignServer(fttableform->ftserver);
16016 86 : fdw = GetForeignDataWrapper(server->fdwid);
16017 :
16018 86 : table_close(ftrel, AccessShareLock);
16019 86 : ReleaseSysCache(tuple);
16020 :
16021 86 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
16022 86 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
16023 86 : if (!HeapTupleIsValid(tuple))
16024 0 : ereport(ERROR,
16025 : (errcode(ERRCODE_UNDEFINED_COLUMN),
16026 : errmsg("column \"%s\" of relation \"%s\" does not exist",
16027 : colName, RelationGetRelationName(rel))));
16028 :
16029 : /* Prevent them from altering a system attribute */
16030 86 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16031 86 : attnum = atttableform->attnum;
16032 86 : if (attnum <= 0)
16033 3 : ereport(ERROR,
16034 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16035 : errmsg("cannot alter system column \"%s\"", colName)));
16036 :
16037 :
16038 : /* Initialize buffers for new tuple values */
16039 83 : memset(repl_val, 0, sizeof(repl_val));
16040 83 : memset(repl_null, false, sizeof(repl_null));
16041 83 : memset(repl_repl, false, sizeof(repl_repl));
16042 :
16043 : /* Extract the current options */
16044 83 : datum = SysCacheGetAttr(ATTNAME,
16045 : tuple,
16046 : Anum_pg_attribute_attfdwoptions,
16047 : &isnull);
16048 83 : if (isnull)
16049 78 : datum = PointerGetDatum(NULL);
16050 :
16051 : /* Transform the options */
16052 83 : datum = transformGenericOptions(AttributeRelationId,
16053 : datum,
16054 : options,
16055 : fdw->fdwvalidator);
16056 :
16057 83 : if (DatumGetPointer(datum) != NULL)
16058 83 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16059 : else
16060 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16061 :
16062 83 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16063 :
16064 : /* Everything looks good - update the tuple */
16065 :
16066 83 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16067 : repl_val, repl_null, repl_repl);
16068 :
16069 83 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16070 :
16071 83 : InvokeObjectPostAlterHook(RelationRelationId,
16072 : RelationGetRelid(rel),
16073 : atttableform->attnum);
16074 83 : ObjectAddressSubSet(address, RelationRelationId,
16075 : RelationGetRelid(rel), attnum);
16076 :
16077 83 : ReleaseSysCache(tuple);
16078 :
16079 83 : table_close(attrel, RowExclusiveLock);
16080 :
16081 83 : heap_freetuple(newtuple);
16082 :
16083 83 : return address;
16084 : }
16085 :
16086 : /*
16087 : * ALTER TABLE OWNER
16088 : *
16089 : * recursing is true if we are recursing from a table to its indexes,
16090 : * sequences, or toast table. We don't allow the ownership of those things to
16091 : * be changed separately from the parent table. Also, we can skip permission
16092 : * checks (this is necessary not just an optimization, else we'd fail to
16093 : * handle toast tables properly).
16094 : *
16095 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
16096 : * free-standing composite type.
16097 : */
16098 : void
16099 1142 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
16100 : {
16101 : Relation target_rel;
16102 : Relation class_rel;
16103 : HeapTuple tuple;
16104 : Form_pg_class tuple_class;
16105 :
16106 : /*
16107 : * Get exclusive lock till end of transaction on the target table. Use
16108 : * relation_open so that we can work on indexes and sequences.
16109 : */
16110 1142 : target_rel = relation_open(relationOid, lockmode);
16111 :
16112 : /* Get its pg_class tuple, too */
16113 1142 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
16114 :
16115 1142 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16116 1142 : if (!HeapTupleIsValid(tuple))
16117 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
16118 1142 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16119 :
16120 : /* Can we change the ownership of this tuple? */
16121 1142 : switch (tuple_class->relkind)
16122 : {
16123 1000 : case RELKIND_RELATION:
16124 : case RELKIND_VIEW:
16125 : case RELKIND_MATVIEW:
16126 : case RELKIND_FOREIGN_TABLE:
16127 : case RELKIND_PARTITIONED_TABLE:
16128 : /* ok to change owner */
16129 1000 : break;
16130 48 : case RELKIND_INDEX:
16131 48 : if (!recursing)
16132 : {
16133 : /*
16134 : * Because ALTER INDEX OWNER used to be allowed, and in fact
16135 : * is generated by old versions of pg_dump, we give a warning
16136 : * and do nothing rather than erroring out. Also, to avoid
16137 : * unnecessary chatter while restoring those old dumps, say
16138 : * nothing at all if the command would be a no-op anyway.
16139 : */
16140 0 : if (tuple_class->relowner != newOwnerId)
16141 0 : ereport(WARNING,
16142 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16143 : errmsg("cannot change owner of index \"%s\"",
16144 : NameStr(tuple_class->relname)),
16145 : errhint("Change the ownership of the index's table instead.")));
16146 : /* quick hack to exit via the no-op path */
16147 0 : newOwnerId = tuple_class->relowner;
16148 : }
16149 48 : break;
16150 10 : case RELKIND_PARTITIONED_INDEX:
16151 10 : if (recursing)
16152 10 : break;
16153 0 : ereport(ERROR,
16154 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16155 : errmsg("cannot change owner of index \"%s\"",
16156 : NameStr(tuple_class->relname)),
16157 : errhint("Change the ownership of the index's table instead.")));
16158 : break;
16159 59 : case RELKIND_SEQUENCE:
16160 59 : if (!recursing &&
16161 35 : tuple_class->relowner != newOwnerId)
16162 : {
16163 : /* if it's an owned sequence, disallow changing it by itself */
16164 : Oid tableId;
16165 : int32 colId;
16166 :
16167 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16168 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16169 0 : ereport(ERROR,
16170 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16171 : errmsg("cannot change owner of sequence \"%s\"",
16172 : NameStr(tuple_class->relname)),
16173 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
16174 : NameStr(tuple_class->relname),
16175 : get_rel_name(tableId))));
16176 : }
16177 59 : break;
16178 4 : case RELKIND_COMPOSITE_TYPE:
16179 4 : if (recursing)
16180 4 : break;
16181 0 : ereport(ERROR,
16182 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16183 : errmsg("\"%s\" is a composite type",
16184 : NameStr(tuple_class->relname)),
16185 : /* translator: %s is an SQL ALTER command */
16186 : errhint("Use %s instead.",
16187 : "ALTER TYPE")));
16188 : break;
16189 21 : case RELKIND_TOASTVALUE:
16190 21 : if (recursing)
16191 21 : break;
16192 : pg_fallthrough;
16193 : default:
16194 0 : ereport(ERROR,
16195 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16196 : errmsg("cannot change owner of relation \"%s\"",
16197 : NameStr(tuple_class->relname)),
16198 : errdetail_relkind_not_supported(tuple_class->relkind)));
16199 : }
16200 :
16201 : /*
16202 : * If the new owner is the same as the existing owner, consider the
16203 : * command to have succeeded. This is for dump restoration purposes.
16204 : */
16205 1142 : if (tuple_class->relowner != newOwnerId)
16206 : {
16207 : Datum repl_val[Natts_pg_class];
16208 : bool repl_null[Natts_pg_class];
16209 : bool repl_repl[Natts_pg_class];
16210 : Acl *newAcl;
16211 : Datum aclDatum;
16212 : bool isNull;
16213 : HeapTuple newtuple;
16214 :
16215 : /* skip permission checks when recursing to index or toast table */
16216 273 : if (!recursing)
16217 : {
16218 : /* Superusers can always do it */
16219 164 : if (!superuser())
16220 : {
16221 21 : Oid namespaceOid = tuple_class->relnamespace;
16222 : AclResult aclresult;
16223 :
16224 : /* Otherwise, must be owner of the existing object */
16225 21 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16226 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
16227 0 : RelationGetRelationName(target_rel));
16228 :
16229 : /* Must be able to become new owner */
16230 21 : check_can_set_role(GetUserId(), newOwnerId);
16231 :
16232 : /* New owner must have CREATE privilege on namespace */
16233 15 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16234 : ACL_CREATE);
16235 15 : if (aclresult != ACLCHECK_OK)
16236 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
16237 0 : get_namespace_name(namespaceOid));
16238 : }
16239 : }
16240 :
16241 267 : memset(repl_null, false, sizeof(repl_null));
16242 267 : memset(repl_repl, false, sizeof(repl_repl));
16243 :
16244 267 : repl_repl[Anum_pg_class_relowner - 1] = true;
16245 267 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16246 :
16247 : /*
16248 : * Determine the modified ACL for the new owner. This is only
16249 : * necessary when the ACL is non-null.
16250 : */
16251 267 : aclDatum = SysCacheGetAttr(RELOID, tuple,
16252 : Anum_pg_class_relacl,
16253 : &isNull);
16254 267 : if (!isNull)
16255 : {
16256 23 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16257 : tuple_class->relowner, newOwnerId);
16258 23 : repl_repl[Anum_pg_class_relacl - 1] = true;
16259 23 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16260 : }
16261 :
16262 267 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16263 :
16264 267 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16265 :
16266 267 : heap_freetuple(newtuple);
16267 :
16268 : /*
16269 : * We must similarly update any per-column ACLs to reflect the new
16270 : * owner; for neatness reasons that's split out as a subroutine.
16271 : */
16272 267 : change_owner_fix_column_acls(relationOid,
16273 : tuple_class->relowner,
16274 : newOwnerId);
16275 :
16276 : /*
16277 : * Update owner dependency reference, if any. A composite type has
16278 : * none, because it's tracked for the pg_type entry instead of here;
16279 : * indexes and TOAST tables don't have their own entries either.
16280 : */
16281 267 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16282 263 : tuple_class->relkind != RELKIND_INDEX &&
16283 215 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16284 205 : tuple_class->relkind != RELKIND_TOASTVALUE)
16285 184 : changeDependencyOnOwner(RelationRelationId, relationOid,
16286 : newOwnerId);
16287 :
16288 : /*
16289 : * Also change the ownership of the table's row type, if it has one
16290 : */
16291 267 : if (OidIsValid(tuple_class->reltype))
16292 171 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16293 :
16294 : /*
16295 : * If we are operating on a table or materialized view, also change
16296 : * the ownership of any indexes and sequences that belong to the
16297 : * relation, as well as its toast table (if it has one).
16298 : */
16299 267 : if (tuple_class->relkind == RELKIND_RELATION ||
16300 137 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16301 112 : tuple_class->relkind == RELKIND_MATVIEW ||
16302 112 : tuple_class->relkind == RELKIND_TOASTVALUE)
16303 : {
16304 : List *index_oid_list;
16305 : ListCell *i;
16306 :
16307 : /* Find all the indexes belonging to this relation */
16308 176 : index_oid_list = RelationGetIndexList(target_rel);
16309 :
16310 : /* For each index, recursively change its ownership */
16311 234 : foreach(i, index_oid_list)
16312 58 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16313 :
16314 176 : list_free(index_oid_list);
16315 : }
16316 :
16317 : /* If it has a toast table, recurse to change its ownership */
16318 267 : if (tuple_class->reltoastrelid != InvalidOid)
16319 21 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16320 : true, lockmode);
16321 :
16322 : /* If it has dependent sequences, recurse to change them too */
16323 267 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16324 : }
16325 :
16326 1136 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16327 :
16328 1136 : ReleaseSysCache(tuple);
16329 1136 : table_close(class_rel, RowExclusiveLock);
16330 1136 : relation_close(target_rel, NoLock);
16331 1136 : }
16332 :
16333 : /*
16334 : * change_owner_fix_column_acls
16335 : *
16336 : * Helper function for ATExecChangeOwner. Scan the columns of the table
16337 : * and fix any non-null column ACLs to reflect the new owner.
16338 : */
16339 : static void
16340 267 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16341 : {
16342 : Relation attRelation;
16343 : SysScanDesc scan;
16344 : ScanKeyData key[1];
16345 : HeapTuple attributeTuple;
16346 :
16347 267 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16348 267 : ScanKeyInit(&key[0],
16349 : Anum_pg_attribute_attrelid,
16350 : BTEqualStrategyNumber, F_OIDEQ,
16351 : ObjectIdGetDatum(relationOid));
16352 267 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16353 : true, NULL, 1, key);
16354 1884 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16355 : {
16356 1617 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16357 : Datum repl_val[Natts_pg_attribute];
16358 : bool repl_null[Natts_pg_attribute];
16359 : bool repl_repl[Natts_pg_attribute];
16360 : Acl *newAcl;
16361 : Datum aclDatum;
16362 : bool isNull;
16363 : HeapTuple newtuple;
16364 :
16365 : /* Ignore dropped columns */
16366 1617 : if (att->attisdropped)
16367 1616 : continue;
16368 :
16369 1617 : aclDatum = heap_getattr(attributeTuple,
16370 : Anum_pg_attribute_attacl,
16371 : RelationGetDescr(attRelation),
16372 : &isNull);
16373 : /* Null ACLs do not require changes */
16374 1617 : if (isNull)
16375 1616 : continue;
16376 :
16377 1 : memset(repl_null, false, sizeof(repl_null));
16378 1 : memset(repl_repl, false, sizeof(repl_repl));
16379 :
16380 1 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16381 : oldOwnerId, newOwnerId);
16382 1 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
16383 1 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16384 :
16385 1 : newtuple = heap_modify_tuple(attributeTuple,
16386 : RelationGetDescr(attRelation),
16387 : repl_val, repl_null, repl_repl);
16388 :
16389 1 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16390 :
16391 1 : heap_freetuple(newtuple);
16392 : }
16393 267 : systable_endscan(scan);
16394 267 : table_close(attRelation, RowExclusiveLock);
16395 267 : }
16396 :
16397 : /*
16398 : * change_owner_recurse_to_sequences
16399 : *
16400 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
16401 : * for sequences that are dependent on serial columns, and changes their
16402 : * ownership.
16403 : */
16404 : static void
16405 267 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16406 : {
16407 : Relation depRel;
16408 : SysScanDesc scan;
16409 : ScanKeyData key[2];
16410 : HeapTuple tup;
16411 :
16412 : /*
16413 : * SERIAL sequences are those having an auto dependency on one of the
16414 : * table's columns (we don't care *which* column, exactly).
16415 : */
16416 267 : depRel = table_open(DependRelationId, AccessShareLock);
16417 :
16418 267 : ScanKeyInit(&key[0],
16419 : Anum_pg_depend_refclassid,
16420 : BTEqualStrategyNumber, F_OIDEQ,
16421 : ObjectIdGetDatum(RelationRelationId));
16422 267 : ScanKeyInit(&key[1],
16423 : Anum_pg_depend_refobjid,
16424 : BTEqualStrategyNumber, F_OIDEQ,
16425 : ObjectIdGetDatum(relationOid));
16426 : /* we leave refobjsubid unspecified */
16427 :
16428 267 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16429 : NULL, 2, key);
16430 :
16431 756 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
16432 : {
16433 489 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16434 : Relation seqRel;
16435 :
16436 : /* skip dependencies other than auto dependencies on columns */
16437 489 : if (depForm->refobjsubid == 0 ||
16438 182 : depForm->classid != RelationRelationId ||
16439 71 : depForm->objsubid != 0 ||
16440 71 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16441 418 : continue;
16442 :
16443 : /* Use relation_open just in case it's an index */
16444 71 : seqRel = relation_open(depForm->objid, lockmode);
16445 :
16446 : /* skip non-sequence relations */
16447 71 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16448 : {
16449 : /* No need to keep the lock */
16450 58 : relation_close(seqRel, lockmode);
16451 58 : continue;
16452 : }
16453 :
16454 : /* We don't need to close the sequence while we alter it. */
16455 13 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16456 :
16457 : /* Now we can close it. Keep the lock till end of transaction. */
16458 13 : relation_close(seqRel, NoLock);
16459 : }
16460 :
16461 267 : systable_endscan(scan);
16462 :
16463 267 : relation_close(depRel, AccessShareLock);
16464 267 : }
16465 :
16466 : /*
16467 : * ALTER TABLE CLUSTER ON
16468 : *
16469 : * The only thing we have to do is to change the indisclustered bits.
16470 : *
16471 : * Return the address of the new clustering index.
16472 : */
16473 : static ObjectAddress
16474 32 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16475 : {
16476 : Oid indexOid;
16477 : ObjectAddress address;
16478 :
16479 32 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16480 :
16481 32 : if (!OidIsValid(indexOid))
16482 0 : ereport(ERROR,
16483 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16484 : errmsg("index \"%s\" for table \"%s\" does not exist",
16485 : indexName, RelationGetRelationName(rel))));
16486 :
16487 : /* Check index is valid to cluster on */
16488 32 : check_index_is_clusterable(rel, indexOid, lockmode);
16489 :
16490 : /* And do the work */
16491 32 : mark_index_clustered(rel, indexOid, false);
16492 :
16493 29 : ObjectAddressSet(address,
16494 : RelationRelationId, indexOid);
16495 :
16496 29 : return address;
16497 : }
16498 :
16499 : /*
16500 : * ALTER TABLE SET WITHOUT CLUSTER
16501 : *
16502 : * We have to find any indexes on the table that have indisclustered bit
16503 : * set and turn it off.
16504 : */
16505 : static void
16506 9 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
16507 : {
16508 9 : mark_index_clustered(rel, InvalidOid, false);
16509 6 : }
16510 :
16511 : /*
16512 : * Preparation phase for SET ACCESS METHOD
16513 : *
16514 : * Check that the access method exists and determine whether a change is
16515 : * actually needed.
16516 : */
16517 : static void
16518 55 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
16519 : {
16520 : Oid amoid;
16521 :
16522 : /*
16523 : * Look up the access method name and check that it differs from the
16524 : * table's current AM. If DEFAULT was specified for a partitioned table
16525 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16526 : */
16527 55 : if (amname != NULL)
16528 37 : amoid = get_table_am_oid(amname, false);
16529 18 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16530 9 : amoid = InvalidOid;
16531 : else
16532 9 : amoid = get_table_am_oid(default_table_access_method, false);
16533 :
16534 : /* if it's a match, phase 3 doesn't need to do anything */
16535 55 : if (rel->rd_rel->relam == amoid)
16536 6 : return;
16537 :
16538 : /* Save info for Phase 3 to do the real work */
16539 49 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
16540 49 : tab->newAccessMethod = amoid;
16541 49 : tab->chgAccessMethod = true;
16542 : }
16543 :
16544 : /*
16545 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16546 : * storage that have an interest in preserving AM.
16547 : *
16548 : * Since these have no storage, setting the access method is a catalog only
16549 : * operation.
16550 : */
16551 : static void
16552 22 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
16553 : {
16554 : Relation pg_class;
16555 : Oid oldAccessMethodId;
16556 : HeapTuple tuple;
16557 : Form_pg_class rd_rel;
16558 22 : Oid reloid = RelationGetRelid(rel);
16559 :
16560 : /*
16561 : * Shouldn't be called on relations having storage; these are processed in
16562 : * phase 3.
16563 : */
16564 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16565 :
16566 : /* Get a modifiable copy of the relation's pg_class row. */
16567 22 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
16568 :
16569 22 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16570 22 : if (!HeapTupleIsValid(tuple))
16571 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
16572 22 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16573 :
16574 : /* Update the pg_class row. */
16575 22 : oldAccessMethodId = rd_rel->relam;
16576 22 : rd_rel->relam = newAccessMethodId;
16577 :
16578 : /* Leave if no update required */
16579 22 : if (rd_rel->relam == oldAccessMethodId)
16580 : {
16581 0 : heap_freetuple(tuple);
16582 0 : table_close(pg_class, RowExclusiveLock);
16583 0 : return;
16584 : }
16585 :
16586 22 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16587 :
16588 : /*
16589 : * Update the dependency on the new access method. No dependency is added
16590 : * if the new access method is InvalidOid (default case). Be very careful
16591 : * that this has to compare the previous value stored in pg_class with the
16592 : * new one.
16593 : */
16594 22 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16595 10 : {
16596 : ObjectAddress relobj,
16597 : referenced;
16598 :
16599 : /*
16600 : * New access method is defined and there was no dependency
16601 : * previously, so record a new one.
16602 : */
16603 10 : ObjectAddressSet(relobj, RelationRelationId, reloid);
16604 10 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16605 10 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16606 : }
16607 12 : else if (OidIsValid(oldAccessMethodId) &&
16608 12 : !OidIsValid(rd_rel->relam))
16609 : {
16610 : /*
16611 : * There was an access method defined, and no new one, so just remove
16612 : * the existing dependency.
16613 : */
16614 6 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
16615 : AccessMethodRelationId,
16616 : DEPENDENCY_NORMAL);
16617 : }
16618 : else
16619 : {
16620 : Assert(OidIsValid(oldAccessMethodId) &&
16621 : OidIsValid(rd_rel->relam));
16622 :
16623 : /* Both are valid, so update the dependency */
16624 6 : changeDependencyFor(RelationRelationId, reloid,
16625 : AccessMethodRelationId,
16626 : oldAccessMethodId, rd_rel->relam);
16627 : }
16628 :
16629 : /* make the relam and dependency changes visible */
16630 22 : CommandCounterIncrement();
16631 :
16632 22 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16633 :
16634 22 : heap_freetuple(tuple);
16635 22 : table_close(pg_class, RowExclusiveLock);
16636 : }
16637 :
16638 : /*
16639 : * ALTER TABLE SET TABLESPACE
16640 : */
16641 : static void
16642 85 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16643 : {
16644 : Oid tablespaceId;
16645 :
16646 : /* Check that the tablespace exists */
16647 85 : tablespaceId = get_tablespace_oid(tablespacename, false);
16648 :
16649 : /* Check permissions except when moving to database's default */
16650 85 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16651 : {
16652 : AclResult aclresult;
16653 :
16654 36 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16655 36 : if (aclresult != ACLCHECK_OK)
16656 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16657 : }
16658 :
16659 : /* Save info for Phase 3 to do the real work */
16660 85 : if (OidIsValid(tab->newTableSpace))
16661 0 : ereport(ERROR,
16662 : (errcode(ERRCODE_SYNTAX_ERROR),
16663 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
16664 :
16665 85 : tab->newTableSpace = tablespaceId;
16666 85 : }
16667 :
16668 : /*
16669 : * Set, reset, or replace reloptions.
16670 : */
16671 : static void
16672 495 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
16673 : LOCKMODE lockmode)
16674 : {
16675 : Oid relid;
16676 : Relation pgclass;
16677 : HeapTuple tuple;
16678 : HeapTuple newtuple;
16679 : Datum datum;
16680 : Datum newOptions;
16681 : Datum repl_val[Natts_pg_class];
16682 : bool repl_null[Natts_pg_class];
16683 : bool repl_repl[Natts_pg_class];
16684 495 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16685 :
16686 495 : if (defList == NIL && operation != AT_ReplaceRelOptions)
16687 0 : return; /* nothing to do */
16688 :
16689 495 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
16690 :
16691 : /* Fetch heap tuple */
16692 495 : relid = RelationGetRelid(rel);
16693 495 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16694 495 : if (!HeapTupleIsValid(tuple))
16695 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16696 :
16697 495 : if (operation == AT_ReplaceRelOptions)
16698 : {
16699 : /*
16700 : * If we're supposed to replace the reloptions list, we just pretend
16701 : * there were none before.
16702 : */
16703 97 : datum = (Datum) 0;
16704 : }
16705 : else
16706 : {
16707 : bool isnull;
16708 :
16709 : /* Get the old reloptions */
16710 398 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16711 : &isnull);
16712 398 : if (isnull)
16713 249 : datum = (Datum) 0;
16714 : }
16715 :
16716 : /* Generate new proposed reloptions (text array) */
16717 495 : newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16718 : operation == AT_ResetRelOptions);
16719 :
16720 : /* Validate */
16721 492 : switch (rel->rd_rel->relkind)
16722 : {
16723 276 : case RELKIND_RELATION:
16724 : case RELKIND_MATVIEW:
16725 276 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16726 276 : break;
16727 3 : case RELKIND_PARTITIONED_TABLE:
16728 3 : (void) partitioned_table_reloptions(newOptions, true);
16729 0 : break;
16730 148 : case RELKIND_VIEW:
16731 148 : (void) view_reloptions(newOptions, true);
16732 139 : break;
16733 65 : case RELKIND_INDEX:
16734 : case RELKIND_PARTITIONED_INDEX:
16735 65 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16736 51 : break;
16737 0 : case RELKIND_TOASTVALUE:
16738 : /* fall through to error -- shouldn't ever get here */
16739 : default:
16740 0 : ereport(ERROR,
16741 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16742 : errmsg("cannot set options for relation \"%s\"",
16743 : RelationGetRelationName(rel)),
16744 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16745 : break;
16746 : }
16747 :
16748 : /* Special-case validation of view options */
16749 466 : if (rel->rd_rel->relkind == RELKIND_VIEW)
16750 : {
16751 139 : Query *view_query = get_view_query(rel);
16752 139 : List *view_options = untransformRelOptions(newOptions);
16753 : ListCell *cell;
16754 139 : bool check_option = false;
16755 :
16756 190 : foreach(cell, view_options)
16757 : {
16758 51 : DefElem *defel = (DefElem *) lfirst(cell);
16759 :
16760 51 : if (strcmp(defel->defname, "check_option") == 0)
16761 12 : check_option = true;
16762 : }
16763 :
16764 : /*
16765 : * If the check option is specified, look to see if the view is
16766 : * actually auto-updatable or not.
16767 : */
16768 139 : if (check_option)
16769 : {
16770 : const char *view_updatable_error =
16771 12 : view_query_is_auto_updatable(view_query, true);
16772 :
16773 12 : if (view_updatable_error)
16774 0 : ereport(ERROR,
16775 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16776 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16777 : errhint("%s", _(view_updatable_error))));
16778 : }
16779 : }
16780 :
16781 : /*
16782 : * All we need do here is update the pg_class row; the new options will be
16783 : * propagated into relcaches during post-commit cache inval.
16784 : */
16785 466 : memset(repl_val, 0, sizeof(repl_val));
16786 466 : memset(repl_null, false, sizeof(repl_null));
16787 466 : memset(repl_repl, false, sizeof(repl_repl));
16788 :
16789 466 : if (newOptions != (Datum) 0)
16790 318 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16791 : else
16792 148 : repl_null[Anum_pg_class_reloptions - 1] = true;
16793 :
16794 466 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16795 :
16796 466 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16797 : repl_val, repl_null, repl_repl);
16798 :
16799 466 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16800 466 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16801 :
16802 466 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16803 :
16804 466 : heap_freetuple(newtuple);
16805 :
16806 466 : ReleaseSysCache(tuple);
16807 :
16808 : /* repeat the whole exercise for the toast table, if there's one */
16809 466 : if (OidIsValid(rel->rd_rel->reltoastrelid))
16810 : {
16811 : Relation toastrel;
16812 140 : Oid toastid = rel->rd_rel->reltoastrelid;
16813 :
16814 140 : toastrel = table_open(toastid, lockmode);
16815 :
16816 : /* Fetch heap tuple */
16817 140 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16818 140 : if (!HeapTupleIsValid(tuple))
16819 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
16820 :
16821 140 : if (operation == AT_ReplaceRelOptions)
16822 : {
16823 : /*
16824 : * If we're supposed to replace the reloptions list, we just
16825 : * pretend there were none before.
16826 : */
16827 0 : datum = (Datum) 0;
16828 : }
16829 : else
16830 : {
16831 : bool isnull;
16832 :
16833 : /* Get the old reloptions */
16834 140 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16835 : &isnull);
16836 140 : if (isnull)
16837 122 : datum = (Datum) 0;
16838 : }
16839 :
16840 140 : newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16841 : false, operation == AT_ResetRelOptions);
16842 :
16843 140 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16844 :
16845 140 : memset(repl_val, 0, sizeof(repl_val));
16846 140 : memset(repl_null, false, sizeof(repl_null));
16847 140 : memset(repl_repl, false, sizeof(repl_repl));
16848 :
16849 140 : if (newOptions != (Datum) 0)
16850 21 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16851 : else
16852 119 : repl_null[Anum_pg_class_reloptions - 1] = true;
16853 :
16854 140 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16855 :
16856 140 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16857 : repl_val, repl_null, repl_repl);
16858 :
16859 140 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16860 :
16861 140 : InvokeObjectPostAlterHookArg(RelationRelationId,
16862 : RelationGetRelid(toastrel), 0,
16863 : InvalidOid, true);
16864 :
16865 140 : heap_freetuple(newtuple);
16866 :
16867 140 : ReleaseSysCache(tuple);
16868 :
16869 140 : table_close(toastrel, NoLock);
16870 : }
16871 :
16872 466 : table_close(pgclass, RowExclusiveLock);
16873 : }
16874 :
16875 : /*
16876 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16877 : * rewriting to be done, so we just want to copy the data as fast as possible.
16878 : */
16879 : static void
16880 87 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16881 : {
16882 : Relation rel;
16883 : Oid reltoastrelid;
16884 : RelFileNumber newrelfilenumber;
16885 : RelFileLocator newrlocator;
16886 87 : List *reltoastidxids = NIL;
16887 : ListCell *lc;
16888 :
16889 : /*
16890 : * Need lock here in case we are recursing to toast table or index
16891 : */
16892 87 : rel = relation_open(tableOid, lockmode);
16893 :
16894 : /* Check first if relation can be moved to new tablespace */
16895 87 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16896 : {
16897 4 : InvokeObjectPostAlterHook(RelationRelationId,
16898 : RelationGetRelid(rel), 0);
16899 4 : relation_close(rel, NoLock);
16900 4 : return;
16901 : }
16902 :
16903 83 : reltoastrelid = rel->rd_rel->reltoastrelid;
16904 : /* Fetch the list of indexes on toast relation if necessary */
16905 83 : if (OidIsValid(reltoastrelid))
16906 : {
16907 10 : Relation toastRel = relation_open(reltoastrelid, lockmode);
16908 :
16909 10 : reltoastidxids = RelationGetIndexList(toastRel);
16910 10 : relation_close(toastRel, lockmode);
16911 : }
16912 :
16913 : /*
16914 : * Relfilenumbers are not unique in databases across tablespaces, so we
16915 : * need to allocate a new one in the new tablespace.
16916 : */
16917 83 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16918 83 : rel->rd_rel->relpersistence);
16919 :
16920 : /* Open old and new relation */
16921 83 : newrlocator = rel->rd_locator;
16922 83 : newrlocator.relNumber = newrelfilenumber;
16923 83 : newrlocator.spcOid = newTableSpace;
16924 :
16925 : /* hand off to AM to actually create new rel storage and copy the data */
16926 83 : if (rel->rd_rel->relkind == RELKIND_INDEX)
16927 : {
16928 31 : index_copy_data(rel, newrlocator);
16929 : }
16930 : else
16931 : {
16932 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16933 52 : table_relation_copy_data(rel, &newrlocator);
16934 : }
16935 :
16936 : /*
16937 : * Update the pg_class row.
16938 : *
16939 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16940 : * executed on pg_class or its indexes (the above copy wouldn't contain
16941 : * the updated pg_class entry), but that's forbidden with
16942 : * CheckRelationTableSpaceMove().
16943 : */
16944 83 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16945 :
16946 83 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16947 :
16948 83 : RelationAssumeNewRelfilelocator(rel);
16949 :
16950 83 : relation_close(rel, NoLock);
16951 :
16952 : /* Make sure the reltablespace change is visible */
16953 83 : CommandCounterIncrement();
16954 :
16955 : /* Move associated toast relation and/or indexes, too */
16956 83 : if (OidIsValid(reltoastrelid))
16957 10 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16958 93 : foreach(lc, reltoastidxids)
16959 10 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16960 :
16961 : /* Clean up */
16962 83 : list_free(reltoastidxids);
16963 : }
16964 :
16965 : /*
16966 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16967 : * storage that have an interest in preserving tablespace.
16968 : *
16969 : * Since these have no storage the tablespace can be updated with a simple
16970 : * metadata only operation to update the tablespace.
16971 : */
16972 : static void
16973 18 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
16974 : {
16975 : /*
16976 : * Shouldn't be called on relations having storage; these are processed in
16977 : * phase 3.
16978 : */
16979 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16980 :
16981 : /* check if relation can be moved to its new tablespace */
16982 18 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16983 : {
16984 0 : InvokeObjectPostAlterHook(RelationRelationId,
16985 : RelationGetRelid(rel),
16986 : 0);
16987 0 : return;
16988 : }
16989 :
16990 : /* Update can be done, so change reltablespace */
16991 15 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16992 :
16993 15 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16994 :
16995 : /* Make sure the reltablespace change is visible */
16996 15 : CommandCounterIncrement();
16997 : }
16998 :
16999 : /*
17000 : * Alter Table ALL ... SET TABLESPACE
17001 : *
17002 : * Allows a user to move all objects of some type in a given tablespace in the
17003 : * current database to another tablespace. Objects can be chosen based on the
17004 : * owner of the object also, to allow users to move only their objects.
17005 : * The user must have CREATE rights on the new tablespace, as usual. The main
17006 : * permissions handling is done by the lower-level table move function.
17007 : *
17008 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
17009 : * lock can't be acquired then we ereport(ERROR).
17010 : */
17011 : Oid
17012 15 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
17013 : {
17014 15 : List *relations = NIL;
17015 : ListCell *l;
17016 : ScanKeyData key[1];
17017 : Relation rel;
17018 : TableScanDesc scan;
17019 : HeapTuple tuple;
17020 : Oid orig_tablespaceoid;
17021 : Oid new_tablespaceoid;
17022 15 : List *role_oids = roleSpecsToIds(stmt->roles);
17023 :
17024 : /* Ensure we were not asked to move something we can't */
17025 15 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17026 6 : stmt->objtype != OBJECT_MATVIEW)
17027 0 : ereport(ERROR,
17028 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17029 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17030 :
17031 : /* Get the orig and new tablespace OIDs */
17032 15 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17033 15 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17034 :
17035 : /* Can't move shared relations in to or out of pg_global */
17036 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17037 15 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
17038 : new_tablespaceoid == GLOBALTABLESPACE_OID)
17039 0 : ereport(ERROR,
17040 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17041 : errmsg("cannot move relations in to or out of pg_global tablespace")));
17042 :
17043 : /*
17044 : * Must have CREATE rights on the new tablespace, unless it is the
17045 : * database default tablespace (which all users implicitly have CREATE
17046 : * rights on).
17047 : */
17048 15 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
17049 : {
17050 : AclResult aclresult;
17051 :
17052 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
17053 : ACL_CREATE);
17054 0 : if (aclresult != ACLCHECK_OK)
17055 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
17056 0 : get_tablespace_name(new_tablespaceoid));
17057 : }
17058 :
17059 : /*
17060 : * Now that the checks are done, check if we should set either to
17061 : * InvalidOid because it is our database's default tablespace.
17062 : */
17063 15 : if (orig_tablespaceoid == MyDatabaseTableSpace)
17064 0 : orig_tablespaceoid = InvalidOid;
17065 :
17066 15 : if (new_tablespaceoid == MyDatabaseTableSpace)
17067 15 : new_tablespaceoid = InvalidOid;
17068 :
17069 : /* no-op */
17070 15 : if (orig_tablespaceoid == new_tablespaceoid)
17071 0 : return new_tablespaceoid;
17072 :
17073 : /*
17074 : * Walk the list of objects in the tablespace and move them. This will
17075 : * only find objects in our database, of course.
17076 : */
17077 15 : ScanKeyInit(&key[0],
17078 : Anum_pg_class_reltablespace,
17079 : BTEqualStrategyNumber, F_OIDEQ,
17080 : ObjectIdGetDatum(orig_tablespaceoid));
17081 :
17082 15 : rel = table_open(RelationRelationId, AccessShareLock);
17083 15 : scan = table_beginscan_catalog(rel, 1, key);
17084 66 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17085 : {
17086 51 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17087 51 : Oid relOid = relForm->oid;
17088 :
17089 : /*
17090 : * Do not move objects in pg_catalog as part of this, if an admin
17091 : * really wishes to do so, they can issue the individual ALTER
17092 : * commands directly.
17093 : *
17094 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
17095 : * (TOAST will be moved with the main table).
17096 : */
17097 51 : if (IsCatalogNamespace(relForm->relnamespace) ||
17098 102 : relForm->relisshared ||
17099 102 : isAnyTempNamespace(relForm->relnamespace) ||
17100 51 : IsToastNamespace(relForm->relnamespace))
17101 0 : continue;
17102 :
17103 : /* Only move the object type requested */
17104 51 : if ((stmt->objtype == OBJECT_TABLE &&
17105 30 : relForm->relkind != RELKIND_RELATION &&
17106 18 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17107 33 : (stmt->objtype == OBJECT_INDEX &&
17108 18 : relForm->relkind != RELKIND_INDEX &&
17109 3 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17110 30 : (stmt->objtype == OBJECT_MATVIEW &&
17111 3 : relForm->relkind != RELKIND_MATVIEW))
17112 21 : continue;
17113 :
17114 : /* Check if we are only moving objects owned by certain roles */
17115 30 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17116 0 : continue;
17117 :
17118 : /*
17119 : * Handle permissions-checking here since we are locking the tables
17120 : * and also to avoid doing a bunch of work only to fail part-way. Note
17121 : * that permissions will also be checked by AlterTableInternal().
17122 : *
17123 : * Caller must be considered an owner on the table to move it.
17124 : */
17125 30 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17126 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
17127 0 : NameStr(relForm->relname));
17128 :
17129 30 : if (stmt->nowait &&
17130 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
17131 0 : ereport(ERROR,
17132 : (errcode(ERRCODE_OBJECT_IN_USE),
17133 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
17134 : get_namespace_name(relForm->relnamespace),
17135 : NameStr(relForm->relname))));
17136 : else
17137 30 : LockRelationOid(relOid, AccessExclusiveLock);
17138 :
17139 : /* Add to our list of objects to move */
17140 30 : relations = lappend_oid(relations, relOid);
17141 : }
17142 :
17143 15 : table_endscan(scan);
17144 15 : table_close(rel, AccessShareLock);
17145 :
17146 15 : if (relations == NIL)
17147 6 : ereport(NOTICE,
17148 : (errcode(ERRCODE_NO_DATA_FOUND),
17149 : errmsg("no matching relations in tablespace \"%s\" found",
17150 : orig_tablespaceoid == InvalidOid ? "(database default)" :
17151 : get_tablespace_name(orig_tablespaceoid))));
17152 :
17153 : /* Everything is locked, loop through and move all of the relations. */
17154 45 : foreach(l, relations)
17155 : {
17156 30 : List *cmds = NIL;
17157 30 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
17158 :
17159 30 : cmd->subtype = AT_SetTableSpace;
17160 30 : cmd->name = stmt->new_tablespacename;
17161 :
17162 30 : cmds = lappend(cmds, cmd);
17163 :
17164 30 : EventTriggerAlterTableStart((Node *) stmt);
17165 : /* OID is set by AlterTableInternal */
17166 30 : AlterTableInternal(lfirst_oid(l), cmds, false);
17167 30 : EventTriggerAlterTableEnd();
17168 : }
17169 :
17170 15 : return new_tablespaceoid;
17171 : }
17172 :
17173 : static void
17174 31 : index_copy_data(Relation rel, RelFileLocator newrlocator)
17175 : {
17176 : SMgrRelation dstrel;
17177 :
17178 : /*
17179 : * Since we copy the file directly without looking at the shared buffers,
17180 : * we'd better first flush out any pages of the source relation that are
17181 : * in shared buffers. We assume no new changes will be made while we are
17182 : * holding exclusive lock on the rel.
17183 : */
17184 31 : FlushRelationBuffers(rel);
17185 :
17186 : /*
17187 : * Create and copy all forks of the relation, and schedule unlinking of
17188 : * old physical files.
17189 : *
17190 : * NOTE: any conflict in relfilenumber value will be caught in
17191 : * RelationCreateStorage().
17192 : */
17193 31 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17194 :
17195 : /* copy main fork */
17196 31 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
17197 31 : rel->rd_rel->relpersistence);
17198 :
17199 : /* copy those extra forks that exist */
17200 31 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17201 124 : forkNum <= MAX_FORKNUM; forkNum++)
17202 : {
17203 93 : if (smgrexists(RelationGetSmgr(rel), forkNum))
17204 : {
17205 0 : smgrcreate(dstrel, forkNum, false);
17206 :
17207 : /*
17208 : * WAL log creation if the relation is persistent, or this is the
17209 : * init fork of an unlogged relation.
17210 : */
17211 0 : if (RelationIsPermanent(rel) ||
17212 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17213 : forkNum == INIT_FORKNUM))
17214 0 : log_smgrcreate(&newrlocator, forkNum);
17215 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17216 0 : rel->rd_rel->relpersistence);
17217 : }
17218 : }
17219 :
17220 : /* drop old relation, and close new one */
17221 31 : RelationDropStorage(rel);
17222 31 : smgrclose(dstrel);
17223 31 : }
17224 :
17225 : /*
17226 : * ALTER TABLE ENABLE/DISABLE TRIGGER
17227 : *
17228 : * We just pass this off to trigger.c.
17229 : */
17230 : static void
17231 171 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17232 : char fires_when, bool skip_system, bool recurse,
17233 : LOCKMODE lockmode)
17234 : {
17235 171 : EnableDisableTrigger(rel, trigname, InvalidOid,
17236 : fires_when, skip_system, recurse,
17237 : lockmode);
17238 :
17239 171 : InvokeObjectPostAlterHook(RelationRelationId,
17240 : RelationGetRelid(rel), 0);
17241 171 : }
17242 :
17243 : /*
17244 : * ALTER TABLE ENABLE/DISABLE RULE
17245 : *
17246 : * We just pass this off to rewriteDefine.c.
17247 : */
17248 : static void
17249 23 : ATExecEnableDisableRule(Relation rel, const char *rulename,
17250 : char fires_when, LOCKMODE lockmode)
17251 : {
17252 23 : EnableDisableRule(rel, rulename, fires_when);
17253 :
17254 23 : InvokeObjectPostAlterHook(RelationRelationId,
17255 : RelationGetRelid(rel), 0);
17256 23 : }
17257 :
17258 : /*
17259 : * ALTER TABLE INHERIT
17260 : *
17261 : * Add a parent to the child's parents. This verifies that all the columns and
17262 : * check constraints of the parent appear in the child and that they have the
17263 : * same data types and expressions.
17264 : */
17265 : static void
17266 232 : ATPrepAddInherit(Relation child_rel)
17267 : {
17268 232 : if (child_rel->rd_rel->reloftype)
17269 3 : ereport(ERROR,
17270 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17271 : errmsg("cannot change inheritance of typed table")));
17272 :
17273 229 : if (child_rel->rd_rel->relispartition)
17274 3 : ereport(ERROR,
17275 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17276 : errmsg("cannot change inheritance of a partition")));
17277 :
17278 226 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17279 3 : ereport(ERROR,
17280 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17281 : errmsg("cannot change inheritance of partitioned table")));
17282 223 : }
17283 :
17284 : /*
17285 : * Return the address of the new parent relation.
17286 : */
17287 : static ObjectAddress
17288 223 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17289 : {
17290 : Relation parent_rel;
17291 : List *children;
17292 : ObjectAddress address;
17293 : const char *trigger_name;
17294 :
17295 : /*
17296 : * A self-exclusive lock is needed here. See the similar case in
17297 : * MergeAttributes() for a full explanation.
17298 : */
17299 223 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17300 :
17301 : /*
17302 : * Must be owner of both parent and child -- child was checked by
17303 : * ATSimplePermissions call in ATPrepCmd
17304 : */
17305 223 : ATSimplePermissions(AT_AddInherit, parent_rel,
17306 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
17307 :
17308 : /* Permanent rels cannot inherit from temporary ones */
17309 223 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17310 3 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17311 0 : ereport(ERROR,
17312 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17313 : errmsg("cannot inherit from temporary relation \"%s\"",
17314 : RelationGetRelationName(parent_rel))));
17315 :
17316 : /* If parent rel is temp, it must belong to this session */
17317 223 : if (RELATION_IS_OTHER_TEMP(parent_rel))
17318 0 : ereport(ERROR,
17319 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17320 : errmsg("cannot inherit from temporary relation of another session")));
17321 :
17322 : /* Ditto for the child */
17323 223 : if (RELATION_IS_OTHER_TEMP(child_rel))
17324 0 : ereport(ERROR,
17325 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17326 : errmsg("cannot inherit to temporary relation of another session")));
17327 :
17328 : /* Prevent partitioned tables from becoming inheritance parents */
17329 223 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17330 3 : ereport(ERROR,
17331 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17332 : errmsg("cannot inherit from partitioned table \"%s\"",
17333 : parent->relname)));
17334 :
17335 : /* Likewise for partitions */
17336 220 : if (parent_rel->rd_rel->relispartition)
17337 3 : ereport(ERROR,
17338 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17339 : errmsg("cannot inherit from a partition")));
17340 :
17341 : /*
17342 : * Prevent circularity by seeing if proposed parent inherits from child.
17343 : * (In particular, this disallows making a rel inherit from itself.)
17344 : *
17345 : * This is not completely bulletproof because of race conditions: in
17346 : * multi-level inheritance trees, someone else could concurrently be
17347 : * making another inheritance link that closes the loop but does not join
17348 : * either of the rels we have locked. Preventing that seems to require
17349 : * exclusive locks on the entire inheritance tree, which is a cure worse
17350 : * than the disease. find_all_inheritors() will cope with circularity
17351 : * anyway, so don't sweat it too much.
17352 : *
17353 : * We use weakest lock we can on child's children, namely AccessShareLock.
17354 : */
17355 217 : children = find_all_inheritors(RelationGetRelid(child_rel),
17356 : AccessShareLock, NULL);
17357 :
17358 217 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
17359 6 : ereport(ERROR,
17360 : (errcode(ERRCODE_DUPLICATE_TABLE),
17361 : errmsg("circular inheritance not allowed"),
17362 : errdetail("\"%s\" is already a child of \"%s\".",
17363 : parent->relname,
17364 : RelationGetRelationName(child_rel))));
17365 :
17366 : /*
17367 : * If child_rel has row-level triggers with transition tables, we
17368 : * currently don't allow it to become an inheritance child. See also
17369 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
17370 : */
17371 211 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17372 211 : if (trigger_name != NULL)
17373 3 : ereport(ERROR,
17374 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17375 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17376 : trigger_name, RelationGetRelationName(child_rel)),
17377 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17378 :
17379 : /* OK to create inheritance */
17380 208 : CreateInheritance(child_rel, parent_rel, false);
17381 :
17382 163 : ObjectAddressSet(address, RelationRelationId,
17383 : RelationGetRelid(parent_rel));
17384 :
17385 : /* keep our lock on the parent relation until commit */
17386 163 : table_close(parent_rel, NoLock);
17387 :
17388 163 : return address;
17389 : }
17390 :
17391 : /*
17392 : * CreateInheritance
17393 : * Catalog manipulation portion of creating inheritance between a child
17394 : * table and a parent table.
17395 : *
17396 : * Common to ATExecAddInherit() and ATExecAttachPartition().
17397 : */
17398 : static void
17399 1716 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17400 : {
17401 : Relation catalogRelation;
17402 : SysScanDesc scan;
17403 : ScanKeyData key;
17404 : HeapTuple inheritsTuple;
17405 : int32 inhseqno;
17406 :
17407 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17408 1716 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17409 :
17410 : /*
17411 : * Check for duplicates in the list of parents, and determine the highest
17412 : * inhseqno already present; we'll use the next one for the new parent.
17413 : * Also, if proposed child is a partition, it cannot already be
17414 : * inheriting.
17415 : *
17416 : * Note: we do not reject the case where the child already inherits from
17417 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17418 : */
17419 1716 : ScanKeyInit(&key,
17420 : Anum_pg_inherits_inhrelid,
17421 : BTEqualStrategyNumber, F_OIDEQ,
17422 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17423 1716 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17424 : true, NULL, 1, &key);
17425 :
17426 : /* inhseqno sequences start at 1 */
17427 1716 : inhseqno = 0;
17428 1751 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17429 : {
17430 38 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17431 :
17432 38 : if (inh->inhparent == RelationGetRelid(parent_rel))
17433 3 : ereport(ERROR,
17434 : (errcode(ERRCODE_DUPLICATE_TABLE),
17435 : errmsg("relation \"%s\" would be inherited from more than once",
17436 : RelationGetRelationName(parent_rel))));
17437 :
17438 35 : if (inh->inhseqno > inhseqno)
17439 35 : inhseqno = inh->inhseqno;
17440 : }
17441 1713 : systable_endscan(scan);
17442 :
17443 : /* Match up the columns and bump attinhcount as needed */
17444 1713 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17445 :
17446 : /* Match up the constraints and bump coninhcount as needed */
17447 1647 : MergeConstraintsIntoExisting(child_rel, parent_rel);
17448 :
17449 : /*
17450 : * OK, it looks valid. Make the catalog entries that show inheritance.
17451 : */
17452 1617 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
17453 : RelationGetRelid(parent_rel),
17454 : inhseqno + 1,
17455 : catalogRelation,
17456 1617 : parent_rel->rd_rel->relkind ==
17457 : RELKIND_PARTITIONED_TABLE);
17458 :
17459 : /* Now we're done with pg_inherits */
17460 1617 : table_close(catalogRelation, RowExclusiveLock);
17461 1617 : }
17462 :
17463 : /*
17464 : * Obtain the source-text form of the constraint expression for a check
17465 : * constraint, given its pg_constraint tuple
17466 : */
17467 : static char *
17468 194 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
17469 : {
17470 : Form_pg_constraint con;
17471 : bool isnull;
17472 : Datum attr;
17473 : Datum expr;
17474 :
17475 194 : con = (Form_pg_constraint) GETSTRUCT(contup);
17476 194 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17477 194 : if (isnull)
17478 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
17479 :
17480 194 : expr = DirectFunctionCall2(pg_get_expr, attr,
17481 : ObjectIdGetDatum(con->conrelid));
17482 194 : return TextDatumGetCString(expr);
17483 : }
17484 :
17485 : /*
17486 : * Determine whether two check constraints are functionally equivalent
17487 : *
17488 : * The test we apply is to see whether they reverse-compile to the same
17489 : * source string. This insulates us from issues like whether attributes
17490 : * have the same physical column numbers in parent and child relations.
17491 : *
17492 : * Note that we ignore enforceability as there are cases where constraints
17493 : * with differing enforceability are allowed.
17494 : */
17495 : static bool
17496 97 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
17497 : {
17498 97 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
17499 97 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
17500 :
17501 97 : if (acon->condeferrable != bcon->condeferrable ||
17502 97 : acon->condeferred != bcon->condeferred ||
17503 97 : strcmp(decompile_conbin(a, tupleDesc),
17504 97 : decompile_conbin(b, tupleDesc)) != 0)
17505 3 : return false;
17506 : else
17507 94 : return true;
17508 : }
17509 :
17510 : /*
17511 : * Check columns in child table match up with columns in parent, and increment
17512 : * their attinhcount.
17513 : *
17514 : * Called by CreateInheritance
17515 : *
17516 : * Currently all parent columns must be found in child. Missing columns are an
17517 : * error. One day we might consider creating new columns like CREATE TABLE
17518 : * does. However, that is widely unpopular --- in the common use case of
17519 : * partitioned tables it's a foot-gun.
17520 : *
17521 : * The data type must match exactly. If the parent column is NOT NULL then
17522 : * the child must be as well. Defaults are not compared, however.
17523 : */
17524 : static void
17525 1713 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17526 : {
17527 : Relation attrrel;
17528 : TupleDesc parent_desc;
17529 :
17530 1713 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17531 1713 : parent_desc = RelationGetDescr(parent_rel);
17532 :
17533 5627 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17534 : {
17535 3980 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17536 3980 : char *parent_attname = NameStr(parent_att->attname);
17537 : HeapTuple tuple;
17538 :
17539 : /* Ignore dropped columns in the parent. */
17540 3980 : if (parent_att->attisdropped)
17541 148 : continue;
17542 :
17543 : /* Find same column in child (matching on column name). */
17544 3832 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17545 3832 : if (HeapTupleIsValid(tuple))
17546 : {
17547 3826 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17548 :
17549 3826 : if (parent_att->atttypid != child_att->atttypid ||
17550 3823 : parent_att->atttypmod != child_att->atttypmod)
17551 6 : ereport(ERROR,
17552 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17553 : errmsg("child table \"%s\" has different type for column \"%s\"",
17554 : RelationGetRelationName(child_rel), parent_attname)));
17555 :
17556 3820 : if (parent_att->attcollation != child_att->attcollation)
17557 3 : ereport(ERROR,
17558 : (errcode(ERRCODE_COLLATION_MISMATCH),
17559 : errmsg("child table \"%s\" has different collation for column \"%s\"",
17560 : RelationGetRelationName(child_rel), parent_attname)));
17561 :
17562 : /*
17563 : * If the parent has a not-null constraint that's not NO INHERIT,
17564 : * make sure the child has one too.
17565 : *
17566 : * Other constraints are checked elsewhere.
17567 : */
17568 3817 : if (parent_att->attnotnull && !child_att->attnotnull)
17569 : {
17570 : HeapTuple contup;
17571 :
17572 24 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17573 24 : parent_att->attnum);
17574 24 : if (HeapTupleIsValid(contup) &&
17575 24 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17576 15 : ereport(ERROR,
17577 : errcode(ERRCODE_DATATYPE_MISMATCH),
17578 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17579 : parent_attname, RelationGetRelationName(child_rel)));
17580 : }
17581 :
17582 : /*
17583 : * Child column must be generated if and only if parent column is.
17584 : */
17585 3802 : if (parent_att->attgenerated && !child_att->attgenerated)
17586 18 : ereport(ERROR,
17587 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17588 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17589 3784 : if (child_att->attgenerated && !parent_att->attgenerated)
17590 12 : ereport(ERROR,
17591 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17592 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17593 :
17594 3772 : if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17595 6 : ereport(ERROR,
17596 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17597 : errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17598 : errdetail("Parent column is %s, child column is %s.",
17599 : parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17600 : child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17601 :
17602 : /*
17603 : * Regular inheritance children are independent enough not to
17604 : * inherit identity columns. But partitions are integral part of
17605 : * a partitioned table and inherit identity column.
17606 : */
17607 3766 : if (ispartition)
17608 3402 : child_att->attidentity = parent_att->attidentity;
17609 :
17610 : /*
17611 : * OK, bump the child column's inheritance count. (If we fail
17612 : * later on, this change will just roll back.)
17613 : */
17614 3766 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
17615 : &child_att->attinhcount))
17616 0 : ereport(ERROR,
17617 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17618 : errmsg("too many inheritance parents"));
17619 :
17620 : /*
17621 : * In case of partitions, we must enforce that value of attislocal
17622 : * is same in all partitions. (Note: there are only inherited
17623 : * attributes in partitions)
17624 : */
17625 3766 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17626 : {
17627 : Assert(child_att->attinhcount == 1);
17628 3402 : child_att->attislocal = false;
17629 : }
17630 :
17631 3766 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17632 3766 : heap_freetuple(tuple);
17633 : }
17634 : else
17635 : {
17636 6 : ereport(ERROR,
17637 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17638 : errmsg("child table is missing column \"%s\"", parent_attname)));
17639 : }
17640 : }
17641 :
17642 1647 : table_close(attrrel, RowExclusiveLock);
17643 1647 : }
17644 :
17645 : /*
17646 : * Check constraints in child table match up with constraints in parent,
17647 : * and increment their coninhcount.
17648 : *
17649 : * Constraints that are marked ONLY in the parent are ignored.
17650 : *
17651 : * Called by CreateInheritance
17652 : *
17653 : * Currently all constraints in parent must be present in the child. One day we
17654 : * may consider adding new constraints like CREATE TABLE does.
17655 : *
17656 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
17657 : * constraints. As long as tables have more like 10 constraints it shouldn't be
17658 : * a problem though. Even 100 constraints ought not be the end of the world.
17659 : *
17660 : * XXX See MergeWithExistingConstraint too if you change this code.
17661 : */
17662 : static void
17663 1647 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
17664 : {
17665 : Relation constraintrel;
17666 : SysScanDesc parent_scan;
17667 : ScanKeyData parent_key;
17668 : HeapTuple parent_tuple;
17669 1647 : Oid parent_relid = RelationGetRelid(parent_rel);
17670 : AttrMap *attmap;
17671 :
17672 1647 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17673 :
17674 : /* Outer loop scans through the parent's constraint definitions */
17675 1647 : ScanKeyInit(&parent_key,
17676 : Anum_pg_constraint_conrelid,
17677 : BTEqualStrategyNumber, F_OIDEQ,
17678 : ObjectIdGetDatum(parent_relid));
17679 1647 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17680 : true, NULL, 1, &parent_key);
17681 :
17682 1647 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17683 : RelationGetDescr(child_rel),
17684 : true);
17685 :
17686 2873 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17687 : {
17688 1256 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17689 : SysScanDesc child_scan;
17690 : ScanKeyData child_key;
17691 : HeapTuple child_tuple;
17692 : AttrNumber parent_attno;
17693 1256 : bool found = false;
17694 :
17695 1256 : if (parent_con->contype != CONSTRAINT_CHECK &&
17696 1137 : parent_con->contype != CONSTRAINT_NOTNULL)
17697 581 : continue;
17698 :
17699 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17700 697 : if (parent_con->connoinherit)
17701 22 : continue;
17702 :
17703 675 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17704 566 : parent_attno = extractNotNullColumn(parent_tuple);
17705 : else
17706 109 : parent_attno = InvalidAttrNumber;
17707 :
17708 : /* Search for a child constraint matching this one */
17709 675 : ScanKeyInit(&child_key,
17710 : Anum_pg_constraint_conrelid,
17711 : BTEqualStrategyNumber, F_OIDEQ,
17712 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17713 675 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17714 : true, NULL, 1, &child_key);
17715 :
17716 1069 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17717 : {
17718 1057 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17719 : HeapTuple child_copy;
17720 :
17721 1057 : if (child_con->contype != parent_con->contype)
17722 206 : continue;
17723 :
17724 : /*
17725 : * CHECK constraint are matched by constraint name, NOT NULL ones
17726 : * by attribute number.
17727 : */
17728 851 : if (child_con->contype == CONSTRAINT_CHECK)
17729 : {
17730 187 : if (strcmp(NameStr(parent_con->conname),
17731 142 : NameStr(child_con->conname)) != 0)
17732 45 : continue;
17733 : }
17734 709 : else if (child_con->contype == CONSTRAINT_NOTNULL)
17735 : {
17736 : Form_pg_attribute parent_attr;
17737 : Form_pg_attribute child_attr;
17738 : AttrNumber child_attno;
17739 :
17740 709 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17741 709 : child_attno = extractNotNullColumn(child_tuple);
17742 709 : if (parent_attno != attmap->attnums[child_attno - 1])
17743 143 : continue;
17744 :
17745 566 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17746 : /* there shouldn't be constraints on dropped columns */
17747 566 : if (parent_attr->attisdropped || child_attr->attisdropped)
17748 0 : elog(ERROR, "found not-null constraint on dropped columns");
17749 : }
17750 :
17751 663 : if (child_con->contype == CONSTRAINT_CHECK &&
17752 97 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17753 3 : ereport(ERROR,
17754 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17755 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17756 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17757 :
17758 : /*
17759 : * If the child constraint is "no inherit" then cannot merge
17760 : */
17761 660 : if (child_con->connoinherit)
17762 6 : ereport(ERROR,
17763 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17764 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17765 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17766 :
17767 : /*
17768 : * If the child constraint is "not valid" then cannot merge with a
17769 : * valid parent constraint
17770 : */
17771 654 : if (parent_con->convalidated && child_con->conenforced &&
17772 600 : !child_con->convalidated)
17773 6 : ereport(ERROR,
17774 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17775 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17776 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17777 :
17778 : /*
17779 : * A NOT ENFORCED child constraint cannot be merged with an
17780 : * ENFORCED parent constraint. However, the reverse is allowed,
17781 : * where the child constraint is ENFORCED.
17782 : */
17783 648 : if (parent_con->conenforced && !child_con->conenforced)
17784 3 : ereport(ERROR,
17785 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17786 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17787 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17788 :
17789 : /*
17790 : * OK, bump the child constraint's inheritance count. (If we fail
17791 : * later on, this change will just roll back.)
17792 : */
17793 645 : child_copy = heap_copytuple(child_tuple);
17794 645 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17795 :
17796 645 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
17797 : &child_con->coninhcount))
17798 0 : ereport(ERROR,
17799 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17800 : errmsg("too many inheritance parents"));
17801 :
17802 : /*
17803 : * In case of partitions, an inherited constraint must be
17804 : * inherited only once since it cannot have multiple parents and
17805 : * it is never considered local.
17806 : */
17807 645 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17808 : {
17809 : Assert(child_con->coninhcount == 1);
17810 571 : child_con->conislocal = false;
17811 : }
17812 :
17813 645 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17814 645 : heap_freetuple(child_copy);
17815 :
17816 645 : found = true;
17817 645 : break;
17818 : }
17819 :
17820 657 : systable_endscan(child_scan);
17821 :
17822 657 : if (!found)
17823 : {
17824 12 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17825 0 : ereport(ERROR,
17826 : errcode(ERRCODE_DATATYPE_MISMATCH),
17827 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17828 : get_attname(parent_relid,
17829 : extractNotNullColumn(parent_tuple),
17830 : false),
17831 : RelationGetRelationName(child_rel)));
17832 :
17833 12 : ereport(ERROR,
17834 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17835 : errmsg("child table is missing constraint \"%s\"",
17836 : NameStr(parent_con->conname))));
17837 : }
17838 : }
17839 :
17840 1617 : systable_endscan(parent_scan);
17841 1617 : table_close(constraintrel, RowExclusiveLock);
17842 1617 : }
17843 :
17844 : /*
17845 : * ALTER TABLE NO INHERIT
17846 : *
17847 : * Return value is the address of the relation that is no longer parent.
17848 : */
17849 : static ObjectAddress
17850 47 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
17851 : {
17852 : ObjectAddress address;
17853 : Relation parent_rel;
17854 :
17855 47 : if (rel->rd_rel->relispartition)
17856 0 : ereport(ERROR,
17857 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17858 : errmsg("cannot change inheritance of a partition")));
17859 :
17860 : /*
17861 : * AccessShareLock on the parent is probably enough, seeing that DROP
17862 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
17863 : * be inspecting the parent's schema.
17864 : */
17865 47 : parent_rel = table_openrv(parent, AccessShareLock);
17866 :
17867 : /*
17868 : * We don't bother to check ownership of the parent table --- ownership of
17869 : * the child is presumed enough rights.
17870 : */
17871 :
17872 : /* Off to RemoveInheritance() where most of the work happens */
17873 47 : RemoveInheritance(rel, parent_rel, false);
17874 :
17875 44 : ObjectAddressSet(address, RelationRelationId,
17876 : RelationGetRelid(parent_rel));
17877 :
17878 : /* keep our lock on the parent relation until commit */
17879 44 : table_close(parent_rel, NoLock);
17880 :
17881 44 : return address;
17882 : }
17883 :
17884 : /*
17885 : * MarkInheritDetached
17886 : *
17887 : * Set inhdetachpending for a partition, for ATExecDetachPartition
17888 : * in concurrent mode. While at it, verify that no other partition is
17889 : * already pending detach.
17890 : */
17891 : static void
17892 73 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
17893 : {
17894 : Relation catalogRelation;
17895 : SysScanDesc scan;
17896 : ScanKeyData key;
17897 : HeapTuple inheritsTuple;
17898 73 : bool found = false;
17899 :
17900 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17901 :
17902 : /*
17903 : * Find pg_inherits entries by inhparent. (We need to scan them all in
17904 : * order to verify that no other partition is pending detach.)
17905 : */
17906 73 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17907 73 : ScanKeyInit(&key,
17908 : Anum_pg_inherits_inhparent,
17909 : BTEqualStrategyNumber, F_OIDEQ,
17910 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17911 73 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17912 : true, NULL, 1, &key);
17913 :
17914 288 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17915 : {
17916 : Form_pg_inherits inhForm;
17917 :
17918 143 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17919 143 : if (inhForm->inhdetachpending)
17920 1 : ereport(ERROR,
17921 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17922 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17923 : get_rel_name(inhForm->inhrelid),
17924 : get_namespace_name(parent_rel->rd_rel->relnamespace),
17925 : RelationGetRelationName(parent_rel)),
17926 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17927 :
17928 142 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
17929 : {
17930 : HeapTuple newtup;
17931 :
17932 72 : newtup = heap_copytuple(inheritsTuple);
17933 72 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17934 :
17935 72 : CatalogTupleUpdate(catalogRelation,
17936 72 : &inheritsTuple->t_self,
17937 : newtup);
17938 72 : found = true;
17939 72 : heap_freetuple(newtup);
17940 : /* keep looking, to ensure we catch others pending detach */
17941 : }
17942 : }
17943 :
17944 : /* Done */
17945 72 : systable_endscan(scan);
17946 72 : table_close(catalogRelation, RowExclusiveLock);
17947 :
17948 72 : if (!found)
17949 0 : ereport(ERROR,
17950 : (errcode(ERRCODE_UNDEFINED_TABLE),
17951 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17952 : RelationGetRelationName(child_rel),
17953 : RelationGetRelationName(parent_rel))));
17954 72 : }
17955 :
17956 : /*
17957 : * RemoveInheritance
17958 : *
17959 : * Drop a parent from the child's parents. This just adjusts the attinhcount
17960 : * and attislocal of the columns and removes the pg_inherit and pg_depend
17961 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17962 : *
17963 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17964 : * up attislocal stays true, which means if a child is ever removed from a
17965 : * parent then its columns will never be automatically dropped which may
17966 : * surprise. But at least we'll never surprise by dropping columns someone
17967 : * isn't expecting to be dropped which would actually mean data loss.
17968 : *
17969 : * coninhcount and conislocal for inherited constraints are adjusted in
17970 : * exactly the same way.
17971 : *
17972 : * Common to ATExecDropInherit() and ATExecDetachPartition().
17973 : */
17974 : static void
17975 591 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17976 : {
17977 : Relation catalogRelation;
17978 : SysScanDesc scan;
17979 : ScanKeyData key[3];
17980 : HeapTuple attributeTuple,
17981 : constraintTuple;
17982 : AttrMap *attmap;
17983 : List *connames;
17984 : List *nncolumns;
17985 : bool found;
17986 : bool is_partitioning;
17987 :
17988 591 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17989 :
17990 591 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17991 : RelationGetRelid(parent_rel),
17992 : expect_detached,
17993 591 : RelationGetRelationName(child_rel));
17994 591 : if (!found)
17995 : {
17996 12 : if (is_partitioning)
17997 9 : ereport(ERROR,
17998 : (errcode(ERRCODE_UNDEFINED_TABLE),
17999 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18000 : RelationGetRelationName(child_rel),
18001 : RelationGetRelationName(parent_rel))));
18002 : else
18003 3 : ereport(ERROR,
18004 : (errcode(ERRCODE_UNDEFINED_TABLE),
18005 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18006 : RelationGetRelationName(parent_rel),
18007 : RelationGetRelationName(child_rel))));
18008 : }
18009 :
18010 : /*
18011 : * Search through child columns looking for ones matching parent rel
18012 : */
18013 579 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
18014 579 : ScanKeyInit(&key[0],
18015 : Anum_pg_attribute_attrelid,
18016 : BTEqualStrategyNumber, F_OIDEQ,
18017 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
18018 579 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
18019 : true, NULL, 1, key);
18020 5332 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
18021 : {
18022 4753 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
18023 :
18024 : /* Ignore if dropped or not inherited */
18025 4753 : if (att->attisdropped)
18026 21 : continue;
18027 4732 : if (att->attinhcount <= 0)
18028 3489 : continue;
18029 :
18030 1243 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
18031 1243 : NameStr(att->attname)))
18032 : {
18033 : /* Decrement inhcount and possibly set islocal to true */
18034 1216 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
18035 1216 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
18036 :
18037 1216 : copy_att->attinhcount--;
18038 1216 : if (copy_att->attinhcount == 0)
18039 1201 : copy_att->attislocal = true;
18040 :
18041 1216 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18042 1216 : heap_freetuple(copyTuple);
18043 : }
18044 : }
18045 579 : systable_endscan(scan);
18046 579 : table_close(catalogRelation, RowExclusiveLock);
18047 :
18048 : /*
18049 : * Likewise, find inherited check and not-null constraints and disinherit
18050 : * them. To do this, we first need a list of the names of the parent's
18051 : * check constraints. (We cheat a bit by only checking for name matches,
18052 : * assuming that the expressions will match.)
18053 : *
18054 : * For NOT NULL columns, we store column numbers to match, mapping them in
18055 : * to the child rel's attribute numbers.
18056 : */
18057 579 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
18058 : RelationGetDescr(parent_rel),
18059 : false);
18060 :
18061 579 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
18062 579 : ScanKeyInit(&key[0],
18063 : Anum_pg_constraint_conrelid,
18064 : BTEqualStrategyNumber, F_OIDEQ,
18065 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18066 579 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18067 : true, NULL, 1, key);
18068 :
18069 579 : connames = NIL;
18070 579 : nncolumns = NIL;
18071 :
18072 1104 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18073 : {
18074 525 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18075 :
18076 525 : if (con->connoinherit)
18077 112 : continue;
18078 :
18079 413 : if (con->contype == CONSTRAINT_CHECK)
18080 57 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
18081 413 : if (con->contype == CONSTRAINT_NOTNULL)
18082 : {
18083 191 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
18084 :
18085 191 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18086 : }
18087 : }
18088 :
18089 579 : systable_endscan(scan);
18090 :
18091 : /* Now scan the child's constraints to find matches */
18092 579 : ScanKeyInit(&key[0],
18093 : Anum_pg_constraint_conrelid,
18094 : BTEqualStrategyNumber, F_OIDEQ,
18095 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
18096 579 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18097 : true, NULL, 1, key);
18098 :
18099 1102 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18100 : {
18101 523 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18102 523 : bool match = false;
18103 :
18104 : /*
18105 : * Match CHECK constraints by name, not-null constraints by column
18106 : * number, and ignore all others.
18107 : */
18108 523 : if (con->contype == CONSTRAINT_CHECK)
18109 : {
18110 173 : foreach_ptr(char, chkname, connames)
18111 : {
18112 60 : if (con->contype == CONSTRAINT_CHECK &&
18113 60 : strcmp(NameStr(con->conname), chkname) == 0)
18114 : {
18115 57 : match = true;
18116 57 : connames = foreach_delete_current(connames, chkname);
18117 57 : break;
18118 : }
18119 : }
18120 : }
18121 438 : else if (con->contype == CONSTRAINT_NOTNULL)
18122 : {
18123 221 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18124 :
18125 445 : foreach_int(prevattno, nncolumns)
18126 : {
18127 194 : if (prevattno == child_attno)
18128 : {
18129 191 : match = true;
18130 191 : nncolumns = foreach_delete_current(nncolumns, prevattno);
18131 191 : break;
18132 : }
18133 : }
18134 : }
18135 : else
18136 217 : continue;
18137 :
18138 306 : if (match)
18139 : {
18140 : /* Decrement inhcount and possibly set islocal to true */
18141 248 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
18142 248 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18143 :
18144 248 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
18145 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18146 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
18147 :
18148 248 : copy_con->coninhcount--;
18149 248 : if (copy_con->coninhcount == 0)
18150 239 : copy_con->conislocal = true;
18151 :
18152 248 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18153 248 : heap_freetuple(copyTuple);
18154 : }
18155 : }
18156 :
18157 : /* We should have matched all constraints */
18158 579 : if (connames != NIL || nncolumns != NIL)
18159 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18160 : list_length(connames) + list_length(nncolumns),
18161 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18162 :
18163 579 : systable_endscan(scan);
18164 579 : table_close(catalogRelation, RowExclusiveLock);
18165 :
18166 579 : drop_parent_dependency(RelationGetRelid(child_rel),
18167 : RelationRelationId,
18168 : RelationGetRelid(parent_rel),
18169 : child_dependency_type(is_partitioning));
18170 :
18171 : /*
18172 : * Post alter hook of this inherits. Since object_access_hook doesn't take
18173 : * multiple object identifiers, we relay oid of parent relation using
18174 : * auxiliary_id argument.
18175 : */
18176 579 : InvokeObjectPostAlterHookArg(InheritsRelationId,
18177 : RelationGetRelid(child_rel), 0,
18178 : RelationGetRelid(parent_rel), false);
18179 579 : }
18180 :
18181 : /*
18182 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18183 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18184 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18185 : * be TypeRelationId). There's no convenient way to do this, so go trawling
18186 : * through pg_depend.
18187 : */
18188 : static void
18189 585 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18190 : DependencyType deptype)
18191 : {
18192 : Relation catalogRelation;
18193 : SysScanDesc scan;
18194 : ScanKeyData key[3];
18195 : HeapTuple depTuple;
18196 :
18197 585 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18198 :
18199 585 : ScanKeyInit(&key[0],
18200 : Anum_pg_depend_classid,
18201 : BTEqualStrategyNumber, F_OIDEQ,
18202 : ObjectIdGetDatum(RelationRelationId));
18203 585 : ScanKeyInit(&key[1],
18204 : Anum_pg_depend_objid,
18205 : BTEqualStrategyNumber, F_OIDEQ,
18206 : ObjectIdGetDatum(relid));
18207 585 : ScanKeyInit(&key[2],
18208 : Anum_pg_depend_objsubid,
18209 : BTEqualStrategyNumber, F_INT4EQ,
18210 : Int32GetDatum(0));
18211 :
18212 585 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18213 : NULL, 3, key);
18214 :
18215 1796 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18216 : {
18217 1211 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18218 :
18219 1211 : if (dep->refclassid == refclassid &&
18220 606 : dep->refobjid == refobjid &&
18221 585 : dep->refobjsubid == 0 &&
18222 585 : dep->deptype == deptype)
18223 585 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18224 : }
18225 :
18226 585 : systable_endscan(scan);
18227 585 : table_close(catalogRelation, RowExclusiveLock);
18228 585 : }
18229 :
18230 : /*
18231 : * ALTER TABLE OF
18232 : *
18233 : * Attach a table to a composite type, as though it had been created with CREATE
18234 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18235 : * subject table must not have inheritance parents. These restrictions ensure
18236 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18237 : *
18238 : * The address of the type is returned.
18239 : */
18240 : static ObjectAddress
18241 33 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18242 : {
18243 33 : Oid relid = RelationGetRelid(rel);
18244 : Type typetuple;
18245 : Form_pg_type typeform;
18246 : Oid typeid;
18247 : Relation inheritsRelation,
18248 : relationRelation;
18249 : SysScanDesc scan;
18250 : ScanKeyData key;
18251 : AttrNumber table_attno,
18252 : type_attno;
18253 : TupleDesc typeTupleDesc,
18254 : tableTupleDesc;
18255 : ObjectAddress tableobj,
18256 : typeobj;
18257 : HeapTuple classtuple;
18258 :
18259 : /* Validate the type. */
18260 33 : typetuple = typenameType(NULL, ofTypename, NULL);
18261 33 : check_of_type(typetuple);
18262 33 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
18263 33 : typeid = typeform->oid;
18264 :
18265 : /* Fail if the table has any inheritance parents. */
18266 33 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18267 33 : ScanKeyInit(&key,
18268 : Anum_pg_inherits_inhrelid,
18269 : BTEqualStrategyNumber, F_OIDEQ,
18270 : ObjectIdGetDatum(relid));
18271 33 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18272 : true, NULL, 1, &key);
18273 33 : if (HeapTupleIsValid(systable_getnext(scan)))
18274 3 : ereport(ERROR,
18275 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18276 : errmsg("typed tables cannot inherit")));
18277 30 : systable_endscan(scan);
18278 30 : table_close(inheritsRelation, AccessShareLock);
18279 :
18280 : /*
18281 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
18282 : * require that the order also match. However, attnotnull need not match.
18283 : */
18284 30 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18285 30 : tableTupleDesc = RelationGetDescr(rel);
18286 30 : table_attno = 1;
18287 95 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18288 : {
18289 : Form_pg_attribute type_attr,
18290 : table_attr;
18291 : const char *type_attname,
18292 : *table_attname;
18293 :
18294 : /* Get the next non-dropped type attribute. */
18295 77 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18296 77 : if (type_attr->attisdropped)
18297 22 : continue;
18298 55 : type_attname = NameStr(type_attr->attname);
18299 :
18300 : /* Get the next non-dropped table attribute. */
18301 : do
18302 : {
18303 61 : if (table_attno > tableTupleDesc->natts)
18304 3 : ereport(ERROR,
18305 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18306 : errmsg("table is missing column \"%s\"",
18307 : type_attname)));
18308 58 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18309 58 : table_attno++;
18310 58 : } while (table_attr->attisdropped);
18311 52 : table_attname = NameStr(table_attr->attname);
18312 :
18313 : /* Compare name. */
18314 52 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18315 3 : ereport(ERROR,
18316 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18317 : errmsg("table has column \"%s\" where type requires \"%s\"",
18318 : table_attname, type_attname)));
18319 :
18320 : /* Compare type. */
18321 49 : if (table_attr->atttypid != type_attr->atttypid ||
18322 46 : table_attr->atttypmod != type_attr->atttypmod ||
18323 43 : table_attr->attcollation != type_attr->attcollation)
18324 6 : ereport(ERROR,
18325 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18326 : errmsg("table \"%s\" has different type for column \"%s\"",
18327 : RelationGetRelationName(rel), type_attname)));
18328 : }
18329 18 : ReleaseTupleDesc(typeTupleDesc);
18330 :
18331 : /* Any remaining columns at the end of the table had better be dropped. */
18332 18 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
18333 : {
18334 3 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18335 : table_attno - 1);
18336 :
18337 3 : if (!table_attr->attisdropped)
18338 3 : ereport(ERROR,
18339 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18340 : errmsg("table has extra column \"%s\"",
18341 : NameStr(table_attr->attname))));
18342 : }
18343 :
18344 : /* If the table was already typed, drop the existing dependency. */
18345 15 : if (rel->rd_rel->reloftype)
18346 3 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18347 : DEPENDENCY_NORMAL);
18348 :
18349 : /* Record a dependency on the new type. */
18350 15 : tableobj.classId = RelationRelationId;
18351 15 : tableobj.objectId = relid;
18352 15 : tableobj.objectSubId = 0;
18353 15 : typeobj.classId = TypeRelationId;
18354 15 : typeobj.objectId = typeid;
18355 15 : typeobj.objectSubId = 0;
18356 15 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18357 :
18358 : /* Update pg_class.reloftype */
18359 15 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18360 15 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18361 15 : if (!HeapTupleIsValid(classtuple))
18362 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18363 15 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18364 15 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18365 :
18366 15 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18367 :
18368 15 : heap_freetuple(classtuple);
18369 15 : table_close(relationRelation, RowExclusiveLock);
18370 :
18371 15 : ReleaseSysCache(typetuple);
18372 :
18373 15 : return typeobj;
18374 : }
18375 :
18376 : /*
18377 : * ALTER TABLE NOT OF
18378 : *
18379 : * Detach a typed table from its originating type. Just clear reloftype and
18380 : * remove the dependency.
18381 : */
18382 : static void
18383 3 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
18384 : {
18385 3 : Oid relid = RelationGetRelid(rel);
18386 : Relation relationRelation;
18387 : HeapTuple tuple;
18388 :
18389 3 : if (!OidIsValid(rel->rd_rel->reloftype))
18390 0 : ereport(ERROR,
18391 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18392 : errmsg("\"%s\" is not a typed table",
18393 : RelationGetRelationName(rel))));
18394 :
18395 : /*
18396 : * We don't bother to check ownership of the type --- ownership of the
18397 : * table is presumed enough rights. No lock required on the type, either.
18398 : */
18399 :
18400 3 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18401 : DEPENDENCY_NORMAL);
18402 :
18403 : /* Clear pg_class.reloftype */
18404 3 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18405 3 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18406 3 : if (!HeapTupleIsValid(tuple))
18407 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18408 3 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18409 3 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18410 :
18411 3 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18412 :
18413 3 : heap_freetuple(tuple);
18414 3 : table_close(relationRelation, RowExclusiveLock);
18415 3 : }
18416 :
18417 : /*
18418 : * relation_mark_replica_identity: Update a table's replica identity
18419 : *
18420 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18421 : * index. Otherwise, it must be InvalidOid.
18422 : *
18423 : * Caller had better hold an exclusive lock on the relation, as the results
18424 : * of running two of these concurrently wouldn't be pretty.
18425 : */
18426 : static void
18427 232 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18428 : bool is_internal)
18429 : {
18430 : Relation pg_index;
18431 : Relation pg_class;
18432 : HeapTuple pg_class_tuple;
18433 : HeapTuple pg_index_tuple;
18434 : Form_pg_class pg_class_form;
18435 : Form_pg_index pg_index_form;
18436 : ListCell *index;
18437 :
18438 : /*
18439 : * Check whether relreplident has changed, and update it if so.
18440 : */
18441 232 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18442 232 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
18443 : ObjectIdGetDatum(RelationGetRelid(rel)));
18444 232 : if (!HeapTupleIsValid(pg_class_tuple))
18445 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
18446 : RelationGetRelationName(rel));
18447 232 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18448 232 : if (pg_class_form->relreplident != ri_type)
18449 : {
18450 207 : pg_class_form->relreplident = ri_type;
18451 207 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18452 : }
18453 232 : table_close(pg_class, RowExclusiveLock);
18454 232 : heap_freetuple(pg_class_tuple);
18455 :
18456 : /*
18457 : * Update the per-index indisreplident flags correctly.
18458 : */
18459 232 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
18460 594 : foreach(index, RelationGetIndexList(rel))
18461 : {
18462 362 : Oid thisIndexOid = lfirst_oid(index);
18463 362 : bool dirty = false;
18464 :
18465 362 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18466 : ObjectIdGetDatum(thisIndexOid));
18467 362 : if (!HeapTupleIsValid(pg_index_tuple))
18468 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18469 362 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18470 :
18471 362 : if (thisIndexOid == indexOid)
18472 : {
18473 : /* Set the bit if not already set. */
18474 120 : if (!pg_index_form->indisreplident)
18475 : {
18476 111 : dirty = true;
18477 111 : pg_index_form->indisreplident = true;
18478 : }
18479 : }
18480 : else
18481 : {
18482 : /* Unset the bit if set. */
18483 242 : if (pg_index_form->indisreplident)
18484 : {
18485 26 : dirty = true;
18486 26 : pg_index_form->indisreplident = false;
18487 : }
18488 : }
18489 :
18490 362 : if (dirty)
18491 : {
18492 137 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18493 137 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18494 : InvalidOid, is_internal);
18495 :
18496 : /*
18497 : * Invalidate the relcache for the table, so that after we commit
18498 : * all sessions will refresh the table's replica identity index
18499 : * before attempting any UPDATE or DELETE on the table. (If we
18500 : * changed the table's pg_class row above, then a relcache inval
18501 : * is already queued due to that; but we might not have.)
18502 : */
18503 137 : CacheInvalidateRelcache(rel);
18504 : }
18505 362 : heap_freetuple(pg_index_tuple);
18506 : }
18507 :
18508 232 : table_close(pg_index, RowExclusiveLock);
18509 232 : }
18510 :
18511 : /*
18512 : * ALTER TABLE <name> REPLICA IDENTITY ...
18513 : */
18514 : static void
18515 256 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
18516 : {
18517 : Oid indexOid;
18518 : Relation indexRel;
18519 : int key;
18520 :
18521 256 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18522 : {
18523 3 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18524 3 : return;
18525 : }
18526 253 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18527 : {
18528 85 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18529 85 : return;
18530 : }
18531 168 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18532 : {
18533 24 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18534 24 : return;
18535 : }
18536 144 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18537 : {
18538 : /* fallthrough */ ;
18539 : }
18540 : else
18541 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18542 :
18543 : /* Check that the index exists */
18544 144 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18545 144 : if (!OidIsValid(indexOid))
18546 0 : ereport(ERROR,
18547 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18548 : errmsg("index \"%s\" for table \"%s\" does not exist",
18549 : stmt->name, RelationGetRelationName(rel))));
18550 :
18551 144 : indexRel = index_open(indexOid, ShareLock);
18552 :
18553 : /* Check that the index is on the relation we're altering. */
18554 144 : if (indexRel->rd_index == NULL ||
18555 144 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
18556 3 : ereport(ERROR,
18557 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18558 : errmsg("\"%s\" is not an index for table \"%s\"",
18559 : RelationGetRelationName(indexRel),
18560 : RelationGetRelationName(rel))));
18561 :
18562 : /*
18563 : * The AM must support uniqueness, and the index must in fact be unique.
18564 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18565 : * exclusion), we can use that too.
18566 : */
18567 141 : if ((!indexRel->rd_indam->amcanunique ||
18568 131 : !indexRel->rd_index->indisunique) &&
18569 13 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18570 6 : ereport(ERROR,
18571 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18572 : errmsg("cannot use non-unique index \"%s\" as replica identity",
18573 : RelationGetRelationName(indexRel))));
18574 : /* Deferred indexes are not guaranteed to be always unique. */
18575 135 : if (!indexRel->rd_index->indimmediate)
18576 6 : ereport(ERROR,
18577 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18578 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
18579 : RelationGetRelationName(indexRel))));
18580 : /* Expression indexes aren't supported. */
18581 129 : if (RelationGetIndexExpressions(indexRel) != NIL)
18582 3 : ereport(ERROR,
18583 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18584 : errmsg("cannot use expression index \"%s\" as replica identity",
18585 : RelationGetRelationName(indexRel))));
18586 : /* Predicate indexes aren't supported. */
18587 126 : if (RelationGetIndexPredicate(indexRel) != NIL)
18588 3 : ereport(ERROR,
18589 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18590 : errmsg("cannot use partial index \"%s\" as replica identity",
18591 : RelationGetRelationName(indexRel))));
18592 :
18593 : /* Check index for nullable columns. */
18594 276 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18595 : {
18596 156 : int16 attno = indexRel->rd_index->indkey.values[key];
18597 : Form_pg_attribute attr;
18598 :
18599 : /*
18600 : * Reject any other system columns. (Going forward, we'll disallow
18601 : * indexes containing such columns in the first place, but they might
18602 : * exist in older branches.)
18603 : */
18604 156 : if (attno <= 0)
18605 0 : ereport(ERROR,
18606 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18607 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18608 : RelationGetRelationName(indexRel), attno)));
18609 :
18610 156 : attr = TupleDescAttr(rel->rd_att, attno - 1);
18611 156 : if (!attr->attnotnull)
18612 3 : ereport(ERROR,
18613 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18614 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18615 : RelationGetRelationName(indexRel),
18616 : NameStr(attr->attname))));
18617 : }
18618 :
18619 : /* This index is suitable for use as a replica identity. Mark it. */
18620 120 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18621 :
18622 120 : index_close(indexRel, NoLock);
18623 : }
18624 :
18625 : /*
18626 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18627 : */
18628 : static void
18629 174 : ATExecSetRowSecurity(Relation rel, bool rls)
18630 : {
18631 : Relation pg_class;
18632 : Oid relid;
18633 : HeapTuple tuple;
18634 :
18635 174 : relid = RelationGetRelid(rel);
18636 :
18637 : /* Pull the record for this relation and update it */
18638 174 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18639 :
18640 174 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18641 :
18642 174 : if (!HeapTupleIsValid(tuple))
18643 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18644 :
18645 174 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18646 174 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18647 :
18648 174 : InvokeObjectPostAlterHook(RelationRelationId,
18649 : RelationGetRelid(rel), 0);
18650 :
18651 174 : table_close(pg_class, RowExclusiveLock);
18652 174 : heap_freetuple(tuple);
18653 174 : }
18654 :
18655 : /*
18656 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18657 : */
18658 : static void
18659 66 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
18660 : {
18661 : Relation pg_class;
18662 : Oid relid;
18663 : HeapTuple tuple;
18664 :
18665 66 : relid = RelationGetRelid(rel);
18666 :
18667 66 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18668 :
18669 66 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18670 :
18671 66 : if (!HeapTupleIsValid(tuple))
18672 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18673 :
18674 66 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18675 66 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18676 :
18677 66 : InvokeObjectPostAlterHook(RelationRelationId,
18678 : RelationGetRelid(rel), 0);
18679 :
18680 66 : table_close(pg_class, RowExclusiveLock);
18681 66 : heap_freetuple(tuple);
18682 66 : }
18683 :
18684 : /*
18685 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
18686 : */
18687 : static void
18688 29 : ATExecGenericOptions(Relation rel, List *options)
18689 : {
18690 : Relation ftrel;
18691 : ForeignServer *server;
18692 : ForeignDataWrapper *fdw;
18693 : HeapTuple tuple;
18694 : bool isnull;
18695 : Datum repl_val[Natts_pg_foreign_table];
18696 : bool repl_null[Natts_pg_foreign_table];
18697 : bool repl_repl[Natts_pg_foreign_table];
18698 : Datum datum;
18699 : Form_pg_foreign_table tableform;
18700 :
18701 29 : if (options == NIL)
18702 0 : return;
18703 :
18704 29 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18705 :
18706 29 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18707 : ObjectIdGetDatum(rel->rd_id));
18708 29 : if (!HeapTupleIsValid(tuple))
18709 0 : ereport(ERROR,
18710 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18711 : errmsg("foreign table \"%s\" does not exist",
18712 : RelationGetRelationName(rel))));
18713 29 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18714 29 : server = GetForeignServer(tableform->ftserver);
18715 29 : fdw = GetForeignDataWrapper(server->fdwid);
18716 :
18717 29 : memset(repl_val, 0, sizeof(repl_val));
18718 29 : memset(repl_null, false, sizeof(repl_null));
18719 29 : memset(repl_repl, false, sizeof(repl_repl));
18720 :
18721 : /* Extract the current options */
18722 29 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
18723 : tuple,
18724 : Anum_pg_foreign_table_ftoptions,
18725 : &isnull);
18726 29 : if (isnull)
18727 2 : datum = PointerGetDatum(NULL);
18728 :
18729 : /* Transform the options */
18730 29 : datum = transformGenericOptions(ForeignTableRelationId,
18731 : datum,
18732 : options,
18733 : fdw->fdwvalidator);
18734 :
18735 28 : if (DatumGetPointer(datum) != NULL)
18736 28 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18737 : else
18738 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18739 :
18740 28 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18741 :
18742 : /* Everything looks good - update the tuple */
18743 :
18744 28 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18745 : repl_val, repl_null, repl_repl);
18746 :
18747 28 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18748 :
18749 : /*
18750 : * Invalidate relcache so that all sessions will refresh any cached plans
18751 : * that might depend on the old options.
18752 : */
18753 28 : CacheInvalidateRelcache(rel);
18754 :
18755 28 : InvokeObjectPostAlterHook(ForeignTableRelationId,
18756 : RelationGetRelid(rel), 0);
18757 :
18758 28 : table_close(ftrel, RowExclusiveLock);
18759 :
18760 28 : heap_freetuple(tuple);
18761 : }
18762 :
18763 : /*
18764 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
18765 : *
18766 : * Return value is the address of the modified column
18767 : */
18768 : static ObjectAddress
18769 39 : ATExecSetCompression(Relation rel,
18770 : const char *column,
18771 : Node *newValue,
18772 : LOCKMODE lockmode)
18773 : {
18774 : Relation attrel;
18775 : HeapTuple tuple;
18776 : Form_pg_attribute atttableform;
18777 : AttrNumber attnum;
18778 : char *compression;
18779 : char cmethod;
18780 : ObjectAddress address;
18781 :
18782 39 : compression = strVal(newValue);
18783 :
18784 39 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
18785 :
18786 : /* copy the cache entry so we can scribble on it below */
18787 39 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18788 39 : if (!HeapTupleIsValid(tuple))
18789 0 : ereport(ERROR,
18790 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18791 : errmsg("column \"%s\" of relation \"%s\" does not exist",
18792 : column, RelationGetRelationName(rel))));
18793 :
18794 : /* prevent them from altering a system attribute */
18795 39 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18796 39 : attnum = atttableform->attnum;
18797 39 : if (attnum <= 0)
18798 0 : ereport(ERROR,
18799 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18800 : errmsg("cannot alter system column \"%s\"", column)));
18801 :
18802 : /*
18803 : * Check that column type is compressible, then get the attribute
18804 : * compression method code
18805 : */
18806 39 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18807 :
18808 : /* update pg_attribute entry */
18809 36 : atttableform->attcompression = cmethod;
18810 36 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18811 :
18812 36 : InvokeObjectPostAlterHook(RelationRelationId,
18813 : RelationGetRelid(rel),
18814 : attnum);
18815 :
18816 : /*
18817 : * Apply the change to indexes as well (only for simple index columns,
18818 : * matching behavior of index.c ConstructTupleDescriptor()).
18819 : */
18820 36 : SetIndexStorageProperties(rel, attrel, attnum,
18821 : false, 0,
18822 : true, cmethod,
18823 : lockmode);
18824 :
18825 36 : heap_freetuple(tuple);
18826 :
18827 36 : table_close(attrel, RowExclusiveLock);
18828 :
18829 : /* make changes visible */
18830 36 : CommandCounterIncrement();
18831 :
18832 36 : ObjectAddressSubSet(address, RelationRelationId,
18833 : RelationGetRelid(rel), attnum);
18834 36 : return address;
18835 : }
18836 :
18837 :
18838 : /*
18839 : * Preparation phase for SET LOGGED/UNLOGGED
18840 : *
18841 : * This verifies that we're not trying to change a temp table. Also,
18842 : * existing foreign key constraints are checked to avoid ending up with
18843 : * permanent tables referencing unlogged tables.
18844 : */
18845 : static void
18846 50 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
18847 : {
18848 : Relation pg_constraint;
18849 : HeapTuple tuple;
18850 : SysScanDesc scan;
18851 : ScanKeyData skey[1];
18852 :
18853 : /*
18854 : * Disallow changing status for a temp table. Also verify whether we can
18855 : * get away with doing nothing; in such cases we don't need to run the
18856 : * checks below, either.
18857 : */
18858 50 : switch (rel->rd_rel->relpersistence)
18859 : {
18860 0 : case RELPERSISTENCE_TEMP:
18861 0 : ereport(ERROR,
18862 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18863 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
18864 : RelationGetRelationName(rel)),
18865 : errtable(rel)));
18866 : break;
18867 28 : case RELPERSISTENCE_PERMANENT:
18868 28 : if (toLogged)
18869 : /* nothing to do */
18870 6 : return;
18871 25 : break;
18872 22 : case RELPERSISTENCE_UNLOGGED:
18873 22 : if (!toLogged)
18874 : /* nothing to do */
18875 3 : return;
18876 19 : break;
18877 : }
18878 :
18879 : /*
18880 : * Check that the table is not part of any publication when changing to
18881 : * UNLOGGED, as UNLOGGED tables can't be published.
18882 : */
18883 69 : if (!toLogged &&
18884 25 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
18885 0 : ereport(ERROR,
18886 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18887 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18888 : RelationGetRelationName(rel)),
18889 : errdetail("Unlogged relations cannot be replicated.")));
18890 :
18891 : /*
18892 : * Check existing foreign key constraints to preserve the invariant that
18893 : * permanent tables cannot reference unlogged ones. Self-referencing
18894 : * foreign keys can safely be ignored.
18895 : */
18896 44 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18897 :
18898 : /*
18899 : * Scan conrelid if changing to permanent, else confrelid. This also
18900 : * determines whether a useful index exists.
18901 : */
18902 44 : ScanKeyInit(&skey[0],
18903 : toLogged ? Anum_pg_constraint_conrelid :
18904 : Anum_pg_constraint_confrelid,
18905 : BTEqualStrategyNumber, F_OIDEQ,
18906 : ObjectIdGetDatum(RelationGetRelid(rel)));
18907 44 : scan = systable_beginscan(pg_constraint,
18908 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18909 : true, NULL, 1, skey);
18910 :
18911 71 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18912 : {
18913 33 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
18914 :
18915 33 : if (con->contype == CONSTRAINT_FOREIGN)
18916 : {
18917 : Oid foreignrelid;
18918 : Relation foreignrel;
18919 :
18920 : /* the opposite end of what we used as scankey */
18921 15 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
18922 :
18923 : /* ignore if self-referencing */
18924 15 : if (RelationGetRelid(rel) == foreignrelid)
18925 6 : continue;
18926 :
18927 9 : foreignrel = relation_open(foreignrelid, AccessShareLock);
18928 :
18929 9 : if (toLogged)
18930 : {
18931 3 : if (!RelationIsPermanent(foreignrel))
18932 3 : ereport(ERROR,
18933 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18934 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18935 : RelationGetRelationName(rel),
18936 : RelationGetRelationName(foreignrel)),
18937 : errtableconstraint(rel, NameStr(con->conname))));
18938 : }
18939 : else
18940 : {
18941 6 : if (RelationIsPermanent(foreignrel))
18942 3 : ereport(ERROR,
18943 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18944 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18945 : RelationGetRelationName(rel),
18946 : RelationGetRelationName(foreignrel)),
18947 : errtableconstraint(rel, NameStr(con->conname))));
18948 : }
18949 :
18950 3 : relation_close(foreignrel, AccessShareLock);
18951 : }
18952 : }
18953 :
18954 38 : systable_endscan(scan);
18955 :
18956 38 : table_close(pg_constraint, AccessShareLock);
18957 :
18958 : /* force rewrite if necessary; see comment in ATRewriteTables */
18959 38 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
18960 38 : if (toLogged)
18961 16 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18962 : else
18963 22 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18964 38 : tab->chgPersistence = true;
18965 : }
18966 :
18967 : /*
18968 : * Execute ALTER TABLE SET SCHEMA
18969 : */
18970 : ObjectAddress
18971 52 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
18972 : {
18973 : Relation rel;
18974 : Oid relid;
18975 : Oid oldNspOid;
18976 : Oid nspOid;
18977 : RangeVar *newrv;
18978 : ObjectAddresses *objsMoved;
18979 : ObjectAddress myself;
18980 :
18981 52 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
18982 52 : stmt->missing_ok ? RVR_MISSING_OK : 0,
18983 : RangeVarCallbackForAlterRelation,
18984 : stmt);
18985 :
18986 51 : if (!OidIsValid(relid))
18987 : {
18988 6 : ereport(NOTICE,
18989 : (errmsg("relation \"%s\" does not exist, skipping",
18990 : stmt->relation->relname)));
18991 6 : return InvalidObjectAddress;
18992 : }
18993 :
18994 45 : rel = relation_open(relid, NoLock);
18995 :
18996 45 : oldNspOid = RelationGetNamespace(rel);
18997 :
18998 : /* If it's an owned sequence, disallow moving it by itself. */
18999 45 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19000 : {
19001 : Oid tableId;
19002 : int32 colId;
19003 :
19004 5 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19005 1 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
19006 3 : ereport(ERROR,
19007 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19008 : errmsg("cannot move an owned sequence into another schema"),
19009 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
19010 : RelationGetRelationName(rel),
19011 : get_rel_name(tableId))));
19012 : }
19013 :
19014 : /* Get and lock schema OID and check its permissions. */
19015 42 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19016 42 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
19017 :
19018 : /* common checks on switching namespaces */
19019 42 : CheckSetNamespace(oldNspOid, nspOid);
19020 :
19021 42 : objsMoved = new_object_addresses();
19022 42 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
19023 42 : free_object_addresses(objsMoved);
19024 :
19025 42 : ObjectAddressSet(myself, RelationRelationId, relid);
19026 :
19027 42 : if (oldschema)
19028 42 : *oldschema = oldNspOid;
19029 :
19030 : /* close rel, but keep lock until commit */
19031 42 : relation_close(rel, NoLock);
19032 :
19033 42 : return myself;
19034 : }
19035 :
19036 : /*
19037 : * The guts of relocating a table or materialized view to another namespace:
19038 : * besides moving the relation itself, its dependent objects are relocated to
19039 : * the new schema.
19040 : */
19041 : void
19042 43 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
19043 : ObjectAddresses *objsMoved)
19044 : {
19045 : Relation classRel;
19046 :
19047 : Assert(objsMoved != NULL);
19048 :
19049 : /* OK, modify the pg_class row and pg_depend entry */
19050 43 : classRel = table_open(RelationRelationId, RowExclusiveLock);
19051 :
19052 43 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
19053 : nspOid, true, objsMoved);
19054 :
19055 : /* Fix the table's row type too, if it has one */
19056 43 : if (OidIsValid(rel->rd_rel->reltype))
19057 42 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
19058 : false, /* isImplicitArray */
19059 : false, /* ignoreDependent */
19060 : false, /* errorOnTableType */
19061 : objsMoved);
19062 :
19063 : /* Fix other dependent stuff */
19064 43 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19065 43 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19066 : objsMoved, AccessExclusiveLock);
19067 43 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19068 : false, objsMoved);
19069 :
19070 43 : table_close(classRel, RowExclusiveLock);
19071 43 : }
19072 :
19073 : /*
19074 : * The guts of relocating a relation to another namespace: fix the pg_class
19075 : * entry, and the pg_depend entry if any. Caller must already have
19076 : * opened and write-locked pg_class.
19077 : */
19078 : void
19079 94 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
19080 : Oid oldNspOid, Oid newNspOid,
19081 : bool hasDependEntry,
19082 : ObjectAddresses *objsMoved)
19083 : {
19084 : HeapTuple classTup;
19085 : Form_pg_class classForm;
19086 : ObjectAddress thisobj;
19087 94 : bool already_done = false;
19088 :
19089 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19090 94 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19091 94 : if (!HeapTupleIsValid(classTup))
19092 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
19093 94 : classForm = (Form_pg_class) GETSTRUCT(classTup);
19094 :
19095 : Assert(classForm->relnamespace == oldNspOid);
19096 :
19097 94 : thisobj.classId = RelationRelationId;
19098 94 : thisobj.objectId = relOid;
19099 94 : thisobj.objectSubId = 0;
19100 :
19101 : /*
19102 : * If the object has already been moved, don't move it again. If it's
19103 : * already in the right place, don't move it, but still fire the object
19104 : * access hook.
19105 : */
19106 94 : already_done = object_address_present(&thisobj, objsMoved);
19107 94 : if (!already_done && oldNspOid != newNspOid)
19108 73 : {
19109 73 : ItemPointerData otid = classTup->t_self;
19110 :
19111 : /* check for duplicate name (more friendly than unique-index failure) */
19112 73 : if (get_relname_relid(NameStr(classForm->relname),
19113 : newNspOid) != InvalidOid)
19114 0 : ereport(ERROR,
19115 : (errcode(ERRCODE_DUPLICATE_TABLE),
19116 : errmsg("relation \"%s\" already exists in schema \"%s\"",
19117 : NameStr(classForm->relname),
19118 : get_namespace_name(newNspOid))));
19119 :
19120 : /* classTup is a copy, so OK to scribble on */
19121 73 : classForm->relnamespace = newNspOid;
19122 :
19123 73 : CatalogTupleUpdate(classRel, &otid, classTup);
19124 73 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19125 :
19126 :
19127 : /* Update dependency on schema if caller said so */
19128 125 : if (hasDependEntry &&
19129 52 : changeDependencyFor(RelationRelationId,
19130 : relOid,
19131 : NamespaceRelationId,
19132 : oldNspOid,
19133 : newNspOid) != 1)
19134 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
19135 : NameStr(classForm->relname));
19136 : }
19137 : else
19138 21 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19139 94 : if (!already_done)
19140 : {
19141 94 : add_exact_object_address(&thisobj, objsMoved);
19142 :
19143 94 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19144 : }
19145 :
19146 94 : heap_freetuple(classTup);
19147 94 : }
19148 :
19149 : /*
19150 : * Move all indexes for the specified relation to another namespace.
19151 : *
19152 : * Note: we assume adequate permission checking was done by the caller,
19153 : * and that the caller has a suitable lock on the owning relation.
19154 : */
19155 : static void
19156 43 : AlterIndexNamespaces(Relation classRel, Relation rel,
19157 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19158 : {
19159 : List *indexList;
19160 : ListCell *l;
19161 :
19162 43 : indexList = RelationGetIndexList(rel);
19163 :
19164 66 : foreach(l, indexList)
19165 : {
19166 23 : Oid indexOid = lfirst_oid(l);
19167 : ObjectAddress thisobj;
19168 :
19169 23 : thisobj.classId = RelationRelationId;
19170 23 : thisobj.objectId = indexOid;
19171 23 : thisobj.objectSubId = 0;
19172 :
19173 : /*
19174 : * Note: currently, the index will not have its own dependency on the
19175 : * namespace, so we don't need to do changeDependencyFor(). There's no
19176 : * row type in pg_type, either.
19177 : *
19178 : * XXX this objsMoved test may be pointless -- surely we have a single
19179 : * dependency link from a relation to each index?
19180 : */
19181 23 : if (!object_address_present(&thisobj, objsMoved))
19182 : {
19183 23 : AlterRelationNamespaceInternal(classRel, indexOid,
19184 : oldNspOid, newNspOid,
19185 : false, objsMoved);
19186 23 : add_exact_object_address(&thisobj, objsMoved);
19187 : }
19188 : }
19189 :
19190 43 : list_free(indexList);
19191 43 : }
19192 :
19193 : /*
19194 : * Move all identity and SERIAL-column sequences of the specified relation to another
19195 : * namespace.
19196 : *
19197 : * Note: we assume adequate permission checking was done by the caller,
19198 : * and that the caller has a suitable lock on the owning relation.
19199 : */
19200 : static void
19201 43 : AlterSeqNamespaces(Relation classRel, Relation rel,
19202 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19203 : LOCKMODE lockmode)
19204 : {
19205 : Relation depRel;
19206 : SysScanDesc scan;
19207 : ScanKeyData key[2];
19208 : HeapTuple tup;
19209 :
19210 : /*
19211 : * SERIAL sequences are those having an auto dependency on one of the
19212 : * table's columns (we don't care *which* column, exactly).
19213 : */
19214 43 : depRel = table_open(DependRelationId, AccessShareLock);
19215 :
19216 43 : ScanKeyInit(&key[0],
19217 : Anum_pg_depend_refclassid,
19218 : BTEqualStrategyNumber, F_OIDEQ,
19219 : ObjectIdGetDatum(RelationRelationId));
19220 43 : ScanKeyInit(&key[1],
19221 : Anum_pg_depend_refobjid,
19222 : BTEqualStrategyNumber, F_OIDEQ,
19223 : ObjectIdGetDatum(RelationGetRelid(rel)));
19224 : /* we leave refobjsubid unspecified */
19225 :
19226 43 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19227 : NULL, 2, key);
19228 :
19229 308 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
19230 : {
19231 265 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19232 : Relation seqRel;
19233 :
19234 : /* skip dependencies other than auto dependencies on columns */
19235 265 : if (depForm->refobjsubid == 0 ||
19236 191 : depForm->classid != RelationRelationId ||
19237 21 : depForm->objsubid != 0 ||
19238 21 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19239 244 : continue;
19240 :
19241 : /* Use relation_open just in case it's an index */
19242 21 : seqRel = relation_open(depForm->objid, lockmode);
19243 :
19244 : /* skip non-sequence relations */
19245 21 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19246 : {
19247 : /* No need to keep the lock */
19248 0 : relation_close(seqRel, lockmode);
19249 0 : continue;
19250 : }
19251 :
19252 : /* Fix the pg_class and pg_depend entries */
19253 21 : AlterRelationNamespaceInternal(classRel, depForm->objid,
19254 : oldNspOid, newNspOid,
19255 : true, objsMoved);
19256 :
19257 : /*
19258 : * Sequences used to have entries in pg_type, but no longer do. If we
19259 : * ever re-instate that, we'll need to move the pg_type entry to the
19260 : * new namespace, too (using AlterTypeNamespaceInternal).
19261 : */
19262 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19263 :
19264 : /* Now we can close it. Keep the lock till end of transaction. */
19265 21 : relation_close(seqRel, NoLock);
19266 : }
19267 :
19268 43 : systable_endscan(scan);
19269 :
19270 43 : relation_close(depRel, AccessShareLock);
19271 43 : }
19272 :
19273 :
19274 : /*
19275 : * This code supports
19276 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19277 : *
19278 : * Because we only support this for TEMP tables, it's sufficient to remember
19279 : * the state in a backend-local data structure.
19280 : */
19281 :
19282 : /*
19283 : * Register a newly-created relation's ON COMMIT action.
19284 : */
19285 : void
19286 91 : register_on_commit_action(Oid relid, OnCommitAction action)
19287 : {
19288 : OnCommitItem *oc;
19289 : MemoryContext oldcxt;
19290 :
19291 : /*
19292 : * We needn't bother registering the relation unless there is an ON COMMIT
19293 : * action we need to take.
19294 : */
19295 91 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19296 12 : return;
19297 :
19298 79 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
19299 :
19300 79 : oc = palloc_object(OnCommitItem);
19301 79 : oc->relid = relid;
19302 79 : oc->oncommit = action;
19303 79 : oc->creating_subid = GetCurrentSubTransactionId();
19304 79 : oc->deleting_subid = InvalidSubTransactionId;
19305 :
19306 : /*
19307 : * We use lcons() here so that ON COMMIT actions are processed in reverse
19308 : * order of registration. That might not be essential but it seems
19309 : * reasonable.
19310 : */
19311 79 : on_commits = lcons(oc, on_commits);
19312 :
19313 79 : MemoryContextSwitchTo(oldcxt);
19314 : }
19315 :
19316 : /*
19317 : * Unregister any ON COMMIT action when a relation is deleted.
19318 : *
19319 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19320 : */
19321 : void
19322 25969 : remove_on_commit_action(Oid relid)
19323 : {
19324 : ListCell *l;
19325 :
19326 26054 : foreach(l, on_commits)
19327 : {
19328 155 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19329 :
19330 155 : if (oc->relid == relid)
19331 : {
19332 70 : oc->deleting_subid = GetCurrentSubTransactionId();
19333 70 : break;
19334 : }
19335 : }
19336 25969 : }
19337 :
19338 : /*
19339 : * Perform ON COMMIT actions.
19340 : *
19341 : * This is invoked just before actually committing, since it's possible
19342 : * to encounter errors.
19343 : */
19344 : void
19345 501689 : PreCommit_on_commit_actions(void)
19346 : {
19347 : ListCell *l;
19348 501689 : List *oids_to_truncate = NIL;
19349 501689 : List *oids_to_drop = NIL;
19350 :
19351 502100 : foreach(l, on_commits)
19352 : {
19353 411 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19354 :
19355 : /* Ignore entry if already dropped in this xact */
19356 411 : if (oc->deleting_subid != InvalidSubTransactionId)
19357 37 : continue;
19358 :
19359 374 : switch (oc->oncommit)
19360 : {
19361 0 : case ONCOMMIT_NOOP:
19362 : case ONCOMMIT_PRESERVE_ROWS:
19363 : /* Do nothing (there shouldn't be such entries, actually) */
19364 0 : break;
19365 347 : case ONCOMMIT_DELETE_ROWS:
19366 :
19367 : /*
19368 : * If this transaction hasn't accessed any temporary
19369 : * relations, we can skip truncating ON COMMIT DELETE ROWS
19370 : * tables, as they must still be empty.
19371 : */
19372 347 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
19373 224 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19374 347 : break;
19375 27 : case ONCOMMIT_DROP:
19376 27 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19377 27 : break;
19378 : }
19379 : }
19380 :
19381 : /*
19382 : * Truncate relations before dropping so that all dependencies between
19383 : * relations are removed after they are worked on. Doing it like this
19384 : * might be a waste as it is possible that a relation being truncated will
19385 : * be dropped anyway due to its parent being dropped, but this makes the
19386 : * code more robust because of not having to re-check that the relation
19387 : * exists at truncation time.
19388 : */
19389 501689 : if (oids_to_truncate != NIL)
19390 191 : heap_truncate(oids_to_truncate);
19391 :
19392 501686 : if (oids_to_drop != NIL)
19393 : {
19394 24 : ObjectAddresses *targetObjects = new_object_addresses();
19395 :
19396 51 : foreach(l, oids_to_drop)
19397 : {
19398 : ObjectAddress object;
19399 :
19400 27 : object.classId = RelationRelationId;
19401 27 : object.objectId = lfirst_oid(l);
19402 27 : object.objectSubId = 0;
19403 :
19404 : Assert(!object_address_present(&object, targetObjects));
19405 :
19406 27 : add_exact_object_address(&object, targetObjects);
19407 : }
19408 :
19409 : /*
19410 : * Object deletion might involve toast table access (to clean up
19411 : * toasted catalog entries), so ensure we have a valid snapshot.
19412 : */
19413 24 : PushActiveSnapshot(GetTransactionSnapshot());
19414 :
19415 : /*
19416 : * Since this is an automatic drop, rather than one directly initiated
19417 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19418 : */
19419 24 : performMultipleDeletions(targetObjects, DROP_CASCADE,
19420 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
19421 :
19422 24 : PopActiveSnapshot();
19423 :
19424 : #ifdef USE_ASSERT_CHECKING
19425 :
19426 : /*
19427 : * Note that table deletion will call remove_on_commit_action, so the
19428 : * entry should get marked as deleted.
19429 : */
19430 : foreach(l, on_commits)
19431 : {
19432 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19433 :
19434 : if (oc->oncommit != ONCOMMIT_DROP)
19435 : continue;
19436 :
19437 : Assert(oc->deleting_subid != InvalidSubTransactionId);
19438 : }
19439 : #endif
19440 : }
19441 501686 : }
19442 :
19443 : /*
19444 : * Post-commit or post-abort cleanup for ON COMMIT management.
19445 : *
19446 : * All we do here is remove no-longer-needed OnCommitItem entries.
19447 : *
19448 : * During commit, remove entries that were deleted during this transaction;
19449 : * during abort, remove those created during this transaction.
19450 : */
19451 : void
19452 528153 : AtEOXact_on_commit_actions(bool isCommit)
19453 : {
19454 : ListCell *cur_item;
19455 :
19456 528582 : foreach(cur_item, on_commits)
19457 : {
19458 429 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19459 :
19460 483 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19461 54 : oc->creating_subid != InvalidSubTransactionId)
19462 : {
19463 : /* cur_item must be removed */
19464 79 : on_commits = foreach_delete_current(on_commits, cur_item);
19465 79 : pfree(oc);
19466 : }
19467 : else
19468 : {
19469 : /* cur_item must be preserved */
19470 350 : oc->creating_subid = InvalidSubTransactionId;
19471 350 : oc->deleting_subid = InvalidSubTransactionId;
19472 : }
19473 : }
19474 528153 : }
19475 :
19476 : /*
19477 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19478 : *
19479 : * During subabort, we can immediately remove entries created during this
19480 : * subtransaction. During subcommit, just relabel entries marked during
19481 : * this subtransaction as being the parent's responsibility.
19482 : */
19483 : void
19484 10086 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
19485 : SubTransactionId parentSubid)
19486 : {
19487 : ListCell *cur_item;
19488 :
19489 10086 : foreach(cur_item, on_commits)
19490 : {
19491 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19492 :
19493 0 : if (!isCommit && oc->creating_subid == mySubid)
19494 : {
19495 : /* cur_item must be removed */
19496 0 : on_commits = foreach_delete_current(on_commits, cur_item);
19497 0 : pfree(oc);
19498 : }
19499 : else
19500 : {
19501 : /* cur_item must be preserved */
19502 0 : if (oc->creating_subid == mySubid)
19503 0 : oc->creating_subid = parentSubid;
19504 0 : if (oc->deleting_subid == mySubid)
19505 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19506 : }
19507 : }
19508 10086 : }
19509 :
19510 : /*
19511 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19512 : * the relation to be locked only if (1) it's a plain or partitioned table,
19513 : * materialized view, or TOAST table and (2) the current user is the owner (or
19514 : * the superuser) or has been granted MAINTAIN. This meets the
19515 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19516 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19517 : */
19518 : void
19519 509 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
19520 : Oid relId, Oid oldRelId, void *arg)
19521 : {
19522 : char relkind;
19523 : AclResult aclresult;
19524 :
19525 : /* Nothing to do if the relation was not found. */
19526 509 : if (!OidIsValid(relId))
19527 3 : return;
19528 :
19529 : /*
19530 : * If the relation does exist, check whether it's an index. But note that
19531 : * the relation might have been dropped between the time we did the name
19532 : * lookup and now. In that case, there's nothing to do.
19533 : */
19534 506 : relkind = get_rel_relkind(relId);
19535 506 : if (!relkind)
19536 0 : return;
19537 506 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19538 70 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19539 14 : ereport(ERROR,
19540 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19541 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19542 :
19543 : /* Check permissions */
19544 492 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19545 492 : if (aclresult != ACLCHECK_OK)
19546 15 : aclcheck_error(aclresult,
19547 15 : get_relkind_objtype(get_rel_relkind(relId)),
19548 15 : relation->relname);
19549 : }
19550 :
19551 : /*
19552 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19553 : */
19554 : static void
19555 1118 : RangeVarCallbackForTruncate(const RangeVar *relation,
19556 : Oid relId, Oid oldRelId, void *arg)
19557 : {
19558 : HeapTuple tuple;
19559 :
19560 : /* Nothing to do if the relation was not found. */
19561 1118 : if (!OidIsValid(relId))
19562 0 : return;
19563 :
19564 1118 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19565 1118 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19566 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19567 :
19568 1118 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
19569 1115 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
19570 :
19571 1099 : ReleaseSysCache(tuple);
19572 : }
19573 :
19574 : /*
19575 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19576 : * the owner of the relation, or superuser.
19577 : */
19578 : void
19579 8630 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
19580 : Oid relId, Oid oldRelId, void *arg)
19581 : {
19582 : HeapTuple tuple;
19583 :
19584 : /* Nothing to do if the relation was not found. */
19585 8630 : if (!OidIsValid(relId))
19586 7 : return;
19587 :
19588 8623 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19589 8623 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19590 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19591 :
19592 8623 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19593 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
19594 12 : relation->relname);
19595 :
19596 17162 : if (!allowSystemTableMods &&
19597 8551 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19598 1 : ereport(ERROR,
19599 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19600 : errmsg("permission denied: \"%s\" is a system catalog",
19601 : relation->relname)));
19602 :
19603 8610 : ReleaseSysCache(tuple);
19604 : }
19605 :
19606 : /*
19607 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
19608 : * processing.
19609 : */
19610 : static void
19611 17601 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
19612 : void *arg)
19613 : {
19614 17601 : Node *stmt = (Node *) arg;
19615 : ObjectType reltype;
19616 : HeapTuple tuple;
19617 : Form_pg_class classform;
19618 : AclResult aclresult;
19619 : char relkind;
19620 :
19621 17601 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19622 17601 : if (!HeapTupleIsValid(tuple))
19623 110 : return; /* concurrently dropped */
19624 17491 : classform = (Form_pg_class) GETSTRUCT(tuple);
19625 17491 : relkind = classform->relkind;
19626 :
19627 : /* Must own relation. */
19628 17491 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19629 36 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
19630 :
19631 : /* No system table modifications unless explicitly allowed. */
19632 17455 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
19633 15 : ereport(ERROR,
19634 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19635 : errmsg("permission denied: \"%s\" is a system catalog",
19636 : rv->relname)));
19637 :
19638 : /*
19639 : * Extract the specified relation type from the statement parse tree.
19640 : *
19641 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
19642 : * have CREATE rights on the containing namespace.
19643 : */
19644 17440 : if (IsA(stmt, RenameStmt))
19645 : {
19646 244 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19647 : GetUserId(), ACL_CREATE);
19648 244 : if (aclresult != ACLCHECK_OK)
19649 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
19650 0 : get_namespace_name(classform->relnamespace));
19651 244 : reltype = ((RenameStmt *) stmt)->renameType;
19652 : }
19653 17196 : else if (IsA(stmt, AlterObjectSchemaStmt))
19654 45 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19655 :
19656 17151 : else if (IsA(stmt, AlterTableStmt))
19657 17151 : reltype = ((AlterTableStmt *) stmt)->objtype;
19658 : else
19659 : {
19660 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19661 : reltype = OBJECT_TABLE; /* placate compiler */
19662 : }
19663 :
19664 : /*
19665 : * For compatibility with prior releases, we allow ALTER TABLE to be used
19666 : * with most other types of relations (but not composite types). We allow
19667 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
19668 : * otherwise. Otherwise, the user must select the correct form of the
19669 : * command for the relation at issue.
19670 : */
19671 17440 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19672 0 : ereport(ERROR,
19673 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19674 : errmsg("\"%s\" is not a sequence", rv->relname)));
19675 :
19676 17440 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19677 0 : ereport(ERROR,
19678 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19679 : errmsg("\"%s\" is not a view", rv->relname)));
19680 :
19681 17440 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19682 0 : ereport(ERROR,
19683 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19684 : errmsg("\"%s\" is not a materialized view", rv->relname)));
19685 :
19686 17440 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19687 0 : ereport(ERROR,
19688 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19689 : errmsg("\"%s\" is not a foreign table", rv->relname)));
19690 :
19691 17440 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19692 0 : ereport(ERROR,
19693 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19694 : errmsg("\"%s\" is not a composite type", rv->relname)));
19695 :
19696 17440 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19697 : relkind != RELKIND_PARTITIONED_INDEX
19698 15 : && !IsA(stmt, RenameStmt))
19699 3 : ereport(ERROR,
19700 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19701 : errmsg("\"%s\" is not an index", rv->relname)));
19702 :
19703 : /*
19704 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19705 : * TYPE for that.
19706 : */
19707 17437 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19708 0 : ereport(ERROR,
19709 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19710 : errmsg("\"%s\" is a composite type", rv->relname),
19711 : /* translator: %s is an SQL ALTER command */
19712 : errhint("Use %s instead.",
19713 : "ALTER TYPE")));
19714 :
19715 : /*
19716 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19717 : * to a different schema, such as indexes and TOAST tables.
19718 : */
19719 17437 : if (IsA(stmt, AlterObjectSchemaStmt))
19720 : {
19721 45 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19722 0 : ereport(ERROR,
19723 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19724 : errmsg("cannot change schema of index \"%s\"",
19725 : rv->relname),
19726 : errhint("Change the schema of the table instead.")));
19727 45 : else if (relkind == RELKIND_COMPOSITE_TYPE)
19728 0 : ereport(ERROR,
19729 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19730 : errmsg("cannot change schema of composite type \"%s\"",
19731 : rv->relname),
19732 : /* translator: %s is an SQL ALTER command */
19733 : errhint("Use %s instead.",
19734 : "ALTER TYPE")));
19735 45 : else if (relkind == RELKIND_TOASTVALUE)
19736 0 : ereport(ERROR,
19737 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19738 : errmsg("cannot change schema of TOAST table \"%s\"",
19739 : rv->relname),
19740 : errhint("Change the schema of the table instead.")));
19741 : }
19742 :
19743 17437 : ReleaseSysCache(tuple);
19744 : }
19745 :
19746 : /*
19747 : * Transform any expressions present in the partition key
19748 : *
19749 : * Returns a transformed PartitionSpec.
19750 : */
19751 : static PartitionSpec *
19752 2778 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
19753 : {
19754 : PartitionSpec *newspec;
19755 : ParseState *pstate;
19756 : ParseNamespaceItem *nsitem;
19757 : ListCell *l;
19758 :
19759 2778 : newspec = makeNode(PartitionSpec);
19760 :
19761 2778 : newspec->strategy = partspec->strategy;
19762 2778 : newspec->partParams = NIL;
19763 2778 : newspec->location = partspec->location;
19764 :
19765 : /* Check valid number of columns for strategy */
19766 4071 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19767 1293 : list_length(partspec->partParams) != 1)
19768 3 : ereport(ERROR,
19769 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19770 : errmsg("cannot use \"list\" partition strategy with more than one column")));
19771 :
19772 : /*
19773 : * Create a dummy ParseState and insert the target relation as its sole
19774 : * rangetable entry. We need a ParseState for transformExpr.
19775 : */
19776 2775 : pstate = make_parsestate(NULL);
19777 2775 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19778 : NULL, false, true);
19779 2775 : addNSItemToQuery(pstate, nsitem, true, true, true);
19780 :
19781 : /* take care of any partition expressions */
19782 5778 : foreach(l, partspec->partParams)
19783 : {
19784 3015 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
19785 :
19786 3015 : if (pelem->expr)
19787 : {
19788 : /* Copy, to avoid scribbling on the input */
19789 176 : pelem = copyObject(pelem);
19790 :
19791 : /* Now do parse transformation of the expression */
19792 176 : pelem->expr = transformExpr(pstate, pelem->expr,
19793 : EXPR_KIND_PARTITION_EXPRESSION);
19794 :
19795 : /* we have to fix its collations too */
19796 164 : assign_expr_collations(pstate, pelem->expr);
19797 : }
19798 :
19799 3003 : newspec->partParams = lappend(newspec->partParams, pelem);
19800 : }
19801 :
19802 2763 : return newspec;
19803 : }
19804 :
19805 : /*
19806 : * Compute per-partition-column information from a list of PartitionElems.
19807 : * Expressions in the PartitionElems must be parse-analyzed already.
19808 : */
19809 : static void
19810 2763 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19811 : List **partexprs, Oid *partopclass, Oid *partcollation,
19812 : PartitionStrategy strategy)
19813 : {
19814 : int attn;
19815 : ListCell *lc;
19816 : Oid am_oid;
19817 :
19818 2763 : attn = 0;
19819 5700 : foreach(lc, partParams)
19820 : {
19821 3003 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
19822 : Oid atttype;
19823 : Oid attcollation;
19824 :
19825 3003 : if (pelem->name != NULL)
19826 : {
19827 : /* Simple attribute reference */
19828 : HeapTuple atttuple;
19829 : Form_pg_attribute attform;
19830 :
19831 2839 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
19832 2839 : pelem->name);
19833 2839 : if (!HeapTupleIsValid(atttuple))
19834 6 : ereport(ERROR,
19835 : (errcode(ERRCODE_UNDEFINED_COLUMN),
19836 : errmsg("column \"%s\" named in partition key does not exist",
19837 : pelem->name),
19838 : parser_errposition(pstate, pelem->location)));
19839 2833 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19840 :
19841 2833 : if (attform->attnum <= 0)
19842 3 : ereport(ERROR,
19843 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19844 : errmsg("cannot use system column \"%s\" in partition key",
19845 : pelem->name),
19846 : parser_errposition(pstate, pelem->location)));
19847 :
19848 : /*
19849 : * Stored generated columns cannot work: They are computed after
19850 : * BEFORE triggers, but partition routing is done before all
19851 : * triggers. Maybe virtual generated columns could be made to
19852 : * work, but then they would need to be handled as an expression
19853 : * below.
19854 : */
19855 2830 : if (attform->attgenerated)
19856 6 : ereport(ERROR,
19857 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19858 : errmsg("cannot use generated column in partition key"),
19859 : errdetail("Column \"%s\" is a generated column.",
19860 : pelem->name),
19861 : parser_errposition(pstate, pelem->location)));
19862 :
19863 2824 : partattrs[attn] = attform->attnum;
19864 2824 : atttype = attform->atttypid;
19865 2824 : attcollation = attform->attcollation;
19866 2824 : ReleaseSysCache(atttuple);
19867 : }
19868 : else
19869 : {
19870 : /* Expression */
19871 164 : Node *expr = pelem->expr;
19872 : char partattname[16];
19873 164 : Bitmapset *expr_attrs = NULL;
19874 : int i;
19875 :
19876 : Assert(expr != NULL);
19877 164 : atttype = exprType(expr);
19878 164 : attcollation = exprCollation(expr);
19879 :
19880 : /*
19881 : * The expression must be of a storable type (e.g., not RECORD).
19882 : * The test is the same as for whether a table column is of a safe
19883 : * type (which is why we needn't check for the non-expression
19884 : * case).
19885 : */
19886 164 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19887 164 : CheckAttributeType(partattname,
19888 : atttype, attcollation,
19889 : NIL, CHKATYPE_IS_PARTKEY);
19890 :
19891 : /*
19892 : * Strip any top-level COLLATE clause. This ensures that we treat
19893 : * "x COLLATE y" and "(x COLLATE y)" alike.
19894 : */
19895 158 : while (IsA(expr, CollateExpr))
19896 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
19897 :
19898 : /*
19899 : * Examine all the columns in the partition key expression. When
19900 : * the whole-row reference is present, examine all the columns of
19901 : * the partitioned table.
19902 : */
19903 158 : pull_varattnos(expr, 1, &expr_attrs);
19904 158 : if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
19905 : {
19906 30 : expr_attrs = bms_add_range(expr_attrs,
19907 : 1 - FirstLowInvalidHeapAttributeNumber,
19908 15 : RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
19909 15 : expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
19910 : }
19911 :
19912 158 : i = -1;
19913 348 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
19914 : {
19915 214 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
19916 :
19917 : Assert(attno != 0);
19918 :
19919 : /*
19920 : * Cannot allow system column references, since that would
19921 : * make partition routing impossible: their values won't be
19922 : * known yet when we need to do that.
19923 : */
19924 214 : if (attno < 0)
19925 0 : ereport(ERROR,
19926 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19927 : errmsg("partition key expressions cannot contain system column references")));
19928 :
19929 : /*
19930 : * Stored generated columns cannot work: They are computed
19931 : * after BEFORE triggers, but partition routing is done before
19932 : * all triggers. Virtual generated columns could probably
19933 : * work, but it would require more work elsewhere (for example
19934 : * SET EXPRESSION would need to check whether the column is
19935 : * used in partition keys). Seems safer to prohibit for now.
19936 : */
19937 214 : if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19938 24 : ereport(ERROR,
19939 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19940 : errmsg("cannot use generated column in partition key"),
19941 : errdetail("Column \"%s\" is a generated column.",
19942 : get_attname(RelationGetRelid(rel), attno, false)),
19943 : parser_errposition(pstate, pelem->location)));
19944 : }
19945 :
19946 134 : if (IsA(expr, Var) &&
19947 6 : ((Var *) expr)->varattno > 0)
19948 : {
19949 :
19950 : /*
19951 : * User wrote "(column)" or "(column COLLATE something)".
19952 : * Treat it like simple attribute anyway.
19953 : */
19954 3 : partattrs[attn] = ((Var *) expr)->varattno;
19955 : }
19956 : else
19957 : {
19958 131 : partattrs[attn] = 0; /* marks the column as expression */
19959 131 : *partexprs = lappend(*partexprs, expr);
19960 :
19961 : /*
19962 : * transformPartitionSpec() should have already rejected
19963 : * subqueries, aggregates, window functions, and SRFs, based
19964 : * on the EXPR_KIND_ for partition expressions.
19965 : */
19966 :
19967 : /*
19968 : * Preprocess the expression before checking for mutability.
19969 : * This is essential for the reasons described in
19970 : * contain_mutable_functions_after_planning. However, we call
19971 : * expression_planner for ourselves rather than using that
19972 : * function, because if constant-folding reduces the
19973 : * expression to a constant, we'd like to know that so we can
19974 : * complain below.
19975 : *
19976 : * Like contain_mutable_functions_after_planning, assume that
19977 : * expression_planner won't scribble on its input, so this
19978 : * won't affect the partexprs entry we saved above.
19979 : */
19980 131 : expr = (Node *) expression_planner((Expr *) expr);
19981 :
19982 : /*
19983 : * Partition expressions cannot contain mutable functions,
19984 : * because a given row must always map to the same partition
19985 : * as long as there is no change in the partition boundary
19986 : * structure.
19987 : */
19988 131 : if (contain_mutable_functions(expr))
19989 3 : ereport(ERROR,
19990 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19991 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
19992 :
19993 : /*
19994 : * While it is not exactly *wrong* for a partition expression
19995 : * to be a constant, it seems better to reject such keys.
19996 : */
19997 128 : if (IsA(expr, Const))
19998 6 : ereport(ERROR,
19999 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20000 : errmsg("cannot use constant expression as partition key")));
20001 : }
20002 : }
20003 :
20004 : /*
20005 : * Apply collation override if any
20006 : */
20007 2949 : if (pelem->collation)
20008 27 : attcollation = get_collation_oid(pelem->collation, false);
20009 :
20010 : /*
20011 : * Check we have a collation iff it's a collatable type. The only
20012 : * expected failures here are (1) COLLATE applied to a noncollatable
20013 : * type, or (2) partition expression had an unresolved collation. But
20014 : * we might as well code this to be a complete consistency check.
20015 : */
20016 2949 : if (type_is_collatable(atttype))
20017 : {
20018 330 : if (!OidIsValid(attcollation))
20019 0 : ereport(ERROR,
20020 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
20021 : errmsg("could not determine which collation to use for partition expression"),
20022 : errhint("Use the COLLATE clause to set the collation explicitly.")));
20023 : }
20024 : else
20025 : {
20026 2619 : if (OidIsValid(attcollation))
20027 0 : ereport(ERROR,
20028 : (errcode(ERRCODE_DATATYPE_MISMATCH),
20029 : errmsg("collations are not supported by type %s",
20030 : format_type_be(atttype))));
20031 : }
20032 :
20033 2949 : partcollation[attn] = attcollation;
20034 :
20035 : /*
20036 : * Identify the appropriate operator class. For list and range
20037 : * partitioning, we use a btree operator class; hash partitioning uses
20038 : * a hash operator class.
20039 : */
20040 2949 : if (strategy == PARTITION_STRATEGY_HASH)
20041 166 : am_oid = HASH_AM_OID;
20042 : else
20043 2783 : am_oid = BTREE_AM_OID;
20044 :
20045 2949 : if (!pelem->opclass)
20046 : {
20047 2880 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20048 :
20049 2880 : if (!OidIsValid(partopclass[attn]))
20050 : {
20051 6 : if (strategy == PARTITION_STRATEGY_HASH)
20052 0 : ereport(ERROR,
20053 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20054 : errmsg("data type %s has no default operator class for access method \"%s\"",
20055 : format_type_be(atttype), "hash"),
20056 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20057 : else
20058 6 : ereport(ERROR,
20059 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20060 : errmsg("data type %s has no default operator class for access method \"%s\"",
20061 : format_type_be(atttype), "btree"),
20062 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20063 : }
20064 : }
20065 : else
20066 69 : partopclass[attn] = ResolveOpClass(pelem->opclass,
20067 : atttype,
20068 : am_oid == HASH_AM_OID ? "hash" : "btree",
20069 : am_oid);
20070 :
20071 2937 : attn++;
20072 : }
20073 2697 : }
20074 :
20075 : /*
20076 : * PartConstraintImpliedByRelConstraint
20077 : * Do scanrel's existing constraints imply the partition constraint?
20078 : *
20079 : * "Existing constraints" include its check constraints and column-level
20080 : * not-null constraints. partConstraint describes the partition constraint,
20081 : * in implicit-AND form.
20082 : */
20083 : bool
20084 1610 : PartConstraintImpliedByRelConstraint(Relation scanrel,
20085 : List *partConstraint)
20086 : {
20087 1610 : List *existConstraint = NIL;
20088 1610 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20089 : int i;
20090 :
20091 1610 : if (constr && constr->has_not_null)
20092 : {
20093 421 : int natts = scanrel->rd_att->natts;
20094 :
20095 1419 : for (i = 1; i <= natts; i++)
20096 : {
20097 998 : CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20098 :
20099 : /* invalid not-null constraint must be ignored here */
20100 998 : if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20101 : {
20102 569 : Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20103 569 : NullTest *ntest = makeNode(NullTest);
20104 :
20105 569 : ntest->arg = (Expr *) makeVar(1,
20106 : i,
20107 : wholeatt->atttypid,
20108 : wholeatt->atttypmod,
20109 : wholeatt->attcollation,
20110 : 0);
20111 569 : ntest->nulltesttype = IS_NOT_NULL;
20112 :
20113 : /*
20114 : * argisrow=false is correct even for a composite column,
20115 : * because attnotnull does not represent a SQL-spec IS NOT
20116 : * NULL test in such a case, just IS DISTINCT FROM NULL.
20117 : */
20118 569 : ntest->argisrow = false;
20119 569 : ntest->location = -1;
20120 569 : existConstraint = lappend(existConstraint, ntest);
20121 : }
20122 : }
20123 : }
20124 :
20125 1610 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20126 : }
20127 :
20128 : /*
20129 : * ConstraintImpliedByRelConstraint
20130 : * Do scanrel's existing constraints imply the given constraint?
20131 : *
20132 : * testConstraint is the constraint to validate. provenConstraint is a
20133 : * caller-provided list of conditions which this function may assume
20134 : * to be true. Both provenConstraint and testConstraint must be in
20135 : * implicit-AND form, must only contain immutable clauses, and must
20136 : * contain only Vars with varno = 1.
20137 : */
20138 : bool
20139 2233 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20140 : {
20141 2233 : List *existConstraint = list_copy(provenConstraint);
20142 2233 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20143 : int num_check,
20144 : i;
20145 :
20146 2233 : num_check = (constr != NULL) ? constr->num_check : 0;
20147 2487 : for (i = 0; i < num_check; i++)
20148 : {
20149 : Node *cexpr;
20150 :
20151 : /*
20152 : * If this constraint hasn't been fully validated yet, we must ignore
20153 : * it here.
20154 : */
20155 254 : if (!constr->check[i].ccvalid)
20156 3 : continue;
20157 :
20158 : /*
20159 : * NOT ENFORCED constraints are always marked as invalid, which should
20160 : * have been ignored.
20161 : */
20162 : Assert(constr->check[i].ccenforced);
20163 :
20164 251 : cexpr = stringToNode(constr->check[i].ccbin);
20165 :
20166 : /*
20167 : * Run each expression through const-simplification and
20168 : * canonicalization. It is necessary, because we will be comparing it
20169 : * to similarly-processed partition constraint expressions, and may
20170 : * fail to detect valid matches without this.
20171 : */
20172 251 : cexpr = eval_const_expressions(NULL, cexpr);
20173 251 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20174 :
20175 251 : existConstraint = list_concat(existConstraint,
20176 251 : make_ands_implicit((Expr *) cexpr));
20177 : }
20178 :
20179 : /*
20180 : * Try to make the proof. Since we are comparing CHECK constraints, we
20181 : * need to use weak implication, i.e., we assume existConstraint is
20182 : * not-false and try to prove the same for testConstraint.
20183 : *
20184 : * Note that predicate_implied_by assumes its first argument is known
20185 : * immutable. That should always be true for both NOT NULL and partition
20186 : * constraints, so we don't test it here.
20187 : */
20188 2233 : return predicate_implied_by(testConstraint, existConstraint, true);
20189 : }
20190 :
20191 : /*
20192 : * QueuePartitionConstraintValidation
20193 : *
20194 : * Add an entry to wqueue to have the given partition constraint validated by
20195 : * Phase 3, for the given relation, and all its children.
20196 : *
20197 : * We first verify whether the given constraint is implied by pre-existing
20198 : * relation constraints; if it is, there's no need to scan the table to
20199 : * validate, so don't queue in that case.
20200 : */
20201 : static void
20202 1365 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
20203 : List *partConstraint,
20204 : bool validate_default)
20205 : {
20206 : /*
20207 : * Based on the table's existing constraints, determine whether or not we
20208 : * may skip scanning the table.
20209 : */
20210 1365 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20211 : {
20212 44 : if (!validate_default)
20213 33 : ereport(DEBUG1,
20214 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20215 : RelationGetRelationName(scanrel))));
20216 : else
20217 11 : ereport(DEBUG1,
20218 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20219 : RelationGetRelationName(scanrel))));
20220 44 : return;
20221 : }
20222 :
20223 : /*
20224 : * Constraints proved insufficient. For plain relations, queue a
20225 : * validation item now; for partitioned tables, recurse to process each
20226 : * partition.
20227 : */
20228 1321 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20229 : {
20230 : AlteredTableInfo *tab;
20231 :
20232 : /* Grab a work queue entry. */
20233 1102 : tab = ATGetQueueEntry(wqueue, scanrel);
20234 : Assert(tab->partition_constraint == NULL);
20235 1102 : tab->partition_constraint = (Expr *) linitial(partConstraint);
20236 1102 : tab->validate_default = validate_default;
20237 : }
20238 219 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20239 : {
20240 193 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20241 : int i;
20242 :
20243 406 : for (i = 0; i < partdesc->nparts; i++)
20244 : {
20245 : Relation part_rel;
20246 : List *thisPartConstraint;
20247 :
20248 : /*
20249 : * This is the minimum lock we need to prevent deadlocks.
20250 : */
20251 213 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20252 :
20253 : /*
20254 : * Adjust the constraint for scanrel so that it matches this
20255 : * partition's attribute numbers.
20256 : */
20257 : thisPartConstraint =
20258 213 : map_partition_varattnos(partConstraint, 1,
20259 : part_rel, scanrel);
20260 :
20261 213 : QueuePartitionConstraintValidation(wqueue, part_rel,
20262 : thisPartConstraint,
20263 : validate_default);
20264 213 : table_close(part_rel, NoLock); /* keep lock till commit */
20265 : }
20266 : }
20267 : }
20268 :
20269 : /*
20270 : * attachPartitionTable: attach a new partition to the partitioned table
20271 : *
20272 : * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
20273 : * of an ALTER TABLE sequence.
20274 : * rel: partitioned relation;
20275 : * attachrel: relation of attached partition;
20276 : * bound: bounds of attached relation.
20277 : */
20278 : static void
20279 1508 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
20280 : {
20281 : /*
20282 : * Create an inheritance; the relevant checks are performed inside the
20283 : * function.
20284 : */
20285 1508 : CreateInheritance(attachrel, rel, true);
20286 :
20287 : /* Update the pg_class entry. */
20288 1454 : StorePartitionBound(attachrel, rel, bound);
20289 :
20290 : /* Ensure there exists a correct set of indexes in the partition. */
20291 1454 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20292 :
20293 : /* and triggers */
20294 1439 : CloneRowTriggersToPartition(rel, attachrel);
20295 :
20296 : /*
20297 : * Clone foreign key constraints. Callee is responsible for setting up
20298 : * for phase 3 constraint verification.
20299 : */
20300 1436 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
20301 1427 : }
20302 :
20303 : /*
20304 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20305 : *
20306 : * Return the address of the newly attached partition.
20307 : */
20308 : static ObjectAddress
20309 1262 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
20310 : AlterTableUtilityContext *context)
20311 : {
20312 : Relation attachrel,
20313 : catalog;
20314 : List *attachrel_children;
20315 : List *partConstraint;
20316 : SysScanDesc scan;
20317 : ScanKeyData skey;
20318 : AttrNumber attno;
20319 : int natts;
20320 : TupleDesc tupleDesc;
20321 : ObjectAddress address;
20322 : const char *trigger_name;
20323 : Oid defaultPartOid;
20324 : List *partBoundConstraint;
20325 1262 : ParseState *pstate = make_parsestate(NULL);
20326 :
20327 1262 : pstate->p_sourcetext = context->queryString;
20328 :
20329 : /*
20330 : * We must lock the default partition if one exists, because attaching a
20331 : * new partition will change its partition constraint.
20332 : */
20333 : defaultPartOid =
20334 1262 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20335 1262 : if (OidIsValid(defaultPartOid))
20336 91 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20337 :
20338 1262 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20339 :
20340 : /*
20341 : * XXX I think it'd be a good idea to grab locks on all tables referenced
20342 : * by FKs at this point also.
20343 : */
20344 :
20345 : /*
20346 : * Must be owner of both parent and source table -- parent was checked by
20347 : * ATSimplePermissions call in ATPrepCmd
20348 : */
20349 1259 : ATSimplePermissions(AT_AttachPartition, attachrel,
20350 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
20351 :
20352 : /* A partition can only have one parent */
20353 1256 : if (attachrel->rd_rel->relispartition)
20354 3 : ereport(ERROR,
20355 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20356 : errmsg("\"%s\" is already a partition",
20357 : RelationGetRelationName(attachrel))));
20358 :
20359 1253 : if (OidIsValid(attachrel->rd_rel->reloftype))
20360 3 : ereport(ERROR,
20361 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20362 : errmsg("cannot attach a typed table as partition")));
20363 :
20364 : /*
20365 : * Table being attached should not already be part of inheritance; either
20366 : * as a child table...
20367 : */
20368 1250 : catalog = table_open(InheritsRelationId, AccessShareLock);
20369 1250 : ScanKeyInit(&skey,
20370 : Anum_pg_inherits_inhrelid,
20371 : BTEqualStrategyNumber, F_OIDEQ,
20372 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20373 1250 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20374 : NULL, 1, &skey);
20375 1250 : if (HeapTupleIsValid(systable_getnext(scan)))
20376 3 : ereport(ERROR,
20377 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20378 : errmsg("cannot attach inheritance child as partition")));
20379 1247 : systable_endscan(scan);
20380 :
20381 : /* ...or as a parent table (except the case when it is partitioned) */
20382 1247 : ScanKeyInit(&skey,
20383 : Anum_pg_inherits_inhparent,
20384 : BTEqualStrategyNumber, F_OIDEQ,
20385 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20386 1247 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20387 : 1, &skey);
20388 1247 : if (HeapTupleIsValid(systable_getnext(scan)) &&
20389 140 : attachrel->rd_rel->relkind == RELKIND_RELATION)
20390 3 : ereport(ERROR,
20391 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20392 : errmsg("cannot attach inheritance parent as partition")));
20393 1244 : systable_endscan(scan);
20394 1244 : table_close(catalog, AccessShareLock);
20395 :
20396 : /*
20397 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
20398 : * particular, this disallows making a rel a partition of itself.)
20399 : *
20400 : * We do that by checking if rel is a member of the list of attachrel's
20401 : * partitions provided the latter is partitioned at all. We want to avoid
20402 : * having to construct this list again, so we request the strongest lock
20403 : * on all partitions. We need the strongest lock, because we may decide
20404 : * to scan them if we find out that the table being attached (or its leaf
20405 : * partitions) may contain rows that violate the partition constraint. If
20406 : * the table has a constraint that would prevent such rows, which by
20407 : * definition is present in all the partitions, we need not scan the
20408 : * table, nor its partitions. But we cannot risk a deadlock by taking a
20409 : * weaker lock now and the stronger one only when needed.
20410 : */
20411 1244 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20412 : AccessExclusiveLock, NULL);
20413 1244 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20414 6 : ereport(ERROR,
20415 : (errcode(ERRCODE_DUPLICATE_TABLE),
20416 : errmsg("circular inheritance not allowed"),
20417 : errdetail("\"%s\" is already a child of \"%s\".",
20418 : RelationGetRelationName(rel),
20419 : RelationGetRelationName(attachrel))));
20420 :
20421 : /* If the parent is permanent, so must be all of its partitions. */
20422 1238 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20423 1217 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20424 3 : ereport(ERROR,
20425 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20426 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20427 : RelationGetRelationName(rel))));
20428 :
20429 : /* Temp parent cannot have a partition that is itself not a temp */
20430 1235 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20431 21 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20432 9 : ereport(ERROR,
20433 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20434 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20435 : RelationGetRelationName(rel))));
20436 :
20437 : /* If the parent is temp, it must belong to this session */
20438 1226 : if (RELATION_IS_OTHER_TEMP(rel))
20439 0 : ereport(ERROR,
20440 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20441 : errmsg("cannot attach as partition of temporary relation of another session")));
20442 :
20443 : /* Ditto for the partition */
20444 1226 : if (RELATION_IS_OTHER_TEMP(attachrel))
20445 0 : ereport(ERROR,
20446 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20447 : errmsg("cannot attach temporary relation of another session as partition")));
20448 :
20449 : /*
20450 : * Check if attachrel has any identity columns or any columns that aren't
20451 : * in the parent.
20452 : */
20453 1226 : tupleDesc = RelationGetDescr(attachrel);
20454 1226 : natts = tupleDesc->natts;
20455 4201 : for (attno = 1; attno <= natts; attno++)
20456 : {
20457 2996 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20458 2996 : char *attributeName = NameStr(attribute->attname);
20459 :
20460 : /* Ignore dropped */
20461 2996 : if (attribute->attisdropped)
20462 308 : continue;
20463 :
20464 2688 : if (attribute->attidentity)
20465 12 : ereport(ERROR,
20466 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20467 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20468 : RelationGetRelationName(attachrel), attributeName),
20469 : errdetail("The new partition may not contain an identity column."));
20470 :
20471 : /* Try to find the column in parent (matching on column name) */
20472 2676 : if (!SearchSysCacheExists2(ATTNAME,
20473 : ObjectIdGetDatum(RelationGetRelid(rel)),
20474 : CStringGetDatum(attributeName)))
20475 9 : ereport(ERROR,
20476 : (errcode(ERRCODE_DATATYPE_MISMATCH),
20477 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20478 : RelationGetRelationName(attachrel), attributeName,
20479 : RelationGetRelationName(rel)),
20480 : errdetail("The new partition may contain only the columns present in parent.")));
20481 : }
20482 :
20483 : /*
20484 : * If child_rel has row-level triggers with transition tables, we
20485 : * currently don't allow it to become a partition. See also prohibitions
20486 : * in ATExecAddInherit() and CreateTrigger().
20487 : */
20488 1205 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20489 1205 : if (trigger_name != NULL)
20490 3 : ereport(ERROR,
20491 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20492 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20493 : trigger_name, RelationGetRelationName(attachrel)),
20494 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
20495 :
20496 : /*
20497 : * Check that the new partition's bound is valid and does not overlap any
20498 : * of existing partitions of the parent - note that it does not return on
20499 : * error.
20500 : */
20501 1202 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
20502 : cmd->bound, pstate);
20503 :
20504 1184 : attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
20505 :
20506 : /*
20507 : * Generate a partition constraint from the partition bound specification.
20508 : * If the parent itself is a partition, make sure to include its
20509 : * constraint as well.
20510 : */
20511 1103 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20512 :
20513 : /*
20514 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20515 : * since it's needed later to construct the constraint expression for
20516 : * validating against the default partition, if any.
20517 : */
20518 1103 : partConstraint = list_concat_copy(partBoundConstraint,
20519 1103 : RelationGetPartitionQual(rel));
20520 :
20521 : /* Skip validation if there are no constraints to validate. */
20522 1103 : if (partConstraint)
20523 : {
20524 : /*
20525 : * Run the partition quals through const-simplification similar to
20526 : * check constraints. We skip canonicalize_qual, though, because
20527 : * partition quals should be in canonical form already.
20528 : */
20529 : partConstraint =
20530 1079 : (List *) eval_const_expressions(NULL,
20531 : (Node *) partConstraint);
20532 :
20533 : /* XXX this sure looks wrong */
20534 1079 : partConstraint = list_make1(make_ands_explicit(partConstraint));
20535 :
20536 : /*
20537 : * Adjust the generated constraint to match this partition's attribute
20538 : * numbers.
20539 : */
20540 1079 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20541 : rel);
20542 :
20543 : /* Validate partition constraints against the table being attached. */
20544 1079 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20545 : false);
20546 : }
20547 :
20548 : /*
20549 : * If we're attaching a partition other than the default partition and a
20550 : * default one exists, then that partition's partition constraint changes,
20551 : * so add an entry to the work queue to validate it, too. (We must not do
20552 : * this when the partition being attached is the default one; we already
20553 : * did it above!)
20554 : */
20555 1103 : if (OidIsValid(defaultPartOid))
20556 : {
20557 : Relation defaultrel;
20558 : List *defPartConstraint;
20559 :
20560 : Assert(!cmd->bound->is_default);
20561 :
20562 : /* we already hold a lock on the default partition */
20563 73 : defaultrel = table_open(defaultPartOid, NoLock);
20564 : defPartConstraint =
20565 73 : get_proposed_default_constraint(partBoundConstraint);
20566 :
20567 : /*
20568 : * Map the Vars in the constraint expression from rel's attnos to
20569 : * defaultrel's.
20570 : */
20571 : defPartConstraint =
20572 73 : map_partition_varattnos(defPartConstraint,
20573 : 1, defaultrel, rel);
20574 73 : QueuePartitionConstraintValidation(wqueue, defaultrel,
20575 : defPartConstraint, true);
20576 :
20577 : /* keep our lock until commit. */
20578 73 : table_close(defaultrel, NoLock);
20579 : }
20580 :
20581 1103 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20582 :
20583 : /*
20584 : * If the partition we just attached is partitioned itself, invalidate
20585 : * relcache for all descendent partitions too to ensure that their
20586 : * rd_partcheck expression trees are rebuilt; partitions already locked at
20587 : * the beginning of this function.
20588 : */
20589 1103 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20590 : {
20591 : ListCell *l;
20592 :
20593 554 : foreach(l, attachrel_children)
20594 : {
20595 373 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
20596 : }
20597 : }
20598 :
20599 : /* keep our lock until commit */
20600 1103 : table_close(attachrel, NoLock);
20601 :
20602 1103 : return address;
20603 : }
20604 :
20605 : /*
20606 : * AttachPartitionEnsureIndexes
20607 : * subroutine for ATExecAttachPartition to create/match indexes
20608 : *
20609 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20610 : * PARTITION: every partition must have an index attached to each index on the
20611 : * partitioned table.
20612 : */
20613 : static void
20614 1454 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
20615 : {
20616 : List *idxes;
20617 : List *attachRelIdxs;
20618 : Relation *attachrelIdxRels;
20619 : IndexInfo **attachInfos;
20620 : ListCell *cell;
20621 : MemoryContext cxt;
20622 : MemoryContext oldcxt;
20623 :
20624 1454 : cxt = AllocSetContextCreate(CurrentMemoryContext,
20625 : "AttachPartitionEnsureIndexes",
20626 : ALLOCSET_DEFAULT_SIZES);
20627 1454 : oldcxt = MemoryContextSwitchTo(cxt);
20628 :
20629 1454 : idxes = RelationGetIndexList(rel);
20630 1454 : attachRelIdxs = RelationGetIndexList(attachrel);
20631 1454 : attachrelIdxRels = palloc_array(Relation, list_length(attachRelIdxs));
20632 1454 : attachInfos = palloc_array(IndexInfo *, list_length(attachRelIdxs));
20633 :
20634 : /* Build arrays of all existing indexes and their IndexInfos */
20635 3111 : foreach_oid(cldIdxId, attachRelIdxs)
20636 : {
20637 203 : int i = foreach_current_index(cldIdxId);
20638 :
20639 203 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20640 203 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20641 : }
20642 :
20643 : /*
20644 : * If we're attaching a foreign table, we must fail if any of the indexes
20645 : * is a constraint index; otherwise, there's nothing to do here. Do this
20646 : * before starting work, to avoid wasting the effort of building a few
20647 : * non-unique indexes before coming across a unique one.
20648 : */
20649 1454 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20650 : {
20651 44 : foreach(cell, idxes)
20652 : {
20653 18 : Oid idx = lfirst_oid(cell);
20654 18 : Relation idxRel = index_open(idx, AccessShareLock);
20655 :
20656 18 : if (idxRel->rd_index->indisunique ||
20657 12 : idxRel->rd_index->indisprimary)
20658 6 : ereport(ERROR,
20659 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20660 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20661 : RelationGetRelationName(attachrel),
20662 : RelationGetRelationName(rel)),
20663 : errdetail("Partitioned table \"%s\" contains unique indexes.",
20664 : RelationGetRelationName(rel))));
20665 12 : index_close(idxRel, AccessShareLock);
20666 : }
20667 :
20668 26 : goto out;
20669 : }
20670 :
20671 : /*
20672 : * For each index on the partitioned table, find a matching one in the
20673 : * partition-to-be; if one is not found, create one.
20674 : */
20675 1776 : foreach(cell, idxes)
20676 : {
20677 363 : Oid idx = lfirst_oid(cell);
20678 363 : Relation idxRel = index_open(idx, AccessShareLock);
20679 : IndexInfo *info;
20680 : AttrMap *attmap;
20681 363 : bool found = false;
20682 : Oid constraintOid;
20683 :
20684 : /*
20685 : * Ignore indexes in the partitioned table other than partitioned
20686 : * indexes.
20687 : */
20688 363 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20689 : {
20690 0 : index_close(idxRel, AccessShareLock);
20691 0 : continue;
20692 : }
20693 :
20694 : /* construct an indexinfo to compare existing indexes against */
20695 363 : info = BuildIndexInfo(idxRel);
20696 363 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20697 : RelationGetDescr(rel),
20698 : false);
20699 363 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
20700 :
20701 : /*
20702 : * Scan the list of existing indexes in the partition-to-be, and mark
20703 : * the first matching, valid, unattached one we find, if any, as
20704 : * partition of the parent index. If we find one, we're done.
20705 : */
20706 393 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20707 : {
20708 149 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20709 149 : Oid cldConstrOid = InvalidOid;
20710 :
20711 : /* does this index have a parent? if so, can't use it */
20712 149 : if (attachrelIdxRels[i]->rd_rel->relispartition)
20713 6 : continue;
20714 :
20715 : /* If this index is invalid, can't use it */
20716 143 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
20717 3 : continue;
20718 :
20719 140 : if (CompareIndexInfo(attachInfos[i], info,
20720 140 : attachrelIdxRels[i]->rd_indcollation,
20721 140 : idxRel->rd_indcollation,
20722 140 : attachrelIdxRels[i]->rd_opfamily,
20723 140 : idxRel->rd_opfamily,
20724 : attmap))
20725 : {
20726 : /*
20727 : * If this index is being created in the parent because of a
20728 : * constraint, then the child needs to have a constraint also,
20729 : * so look for one. If there is no such constraint, this
20730 : * index is no good, so keep looking.
20731 : */
20732 122 : if (OidIsValid(constraintOid))
20733 : {
20734 : cldConstrOid =
20735 73 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
20736 : cldIdxId);
20737 : /* no dice */
20738 73 : if (!OidIsValid(cldConstrOid))
20739 3 : continue;
20740 :
20741 : /* Ensure they're both the same type of constraint */
20742 140 : if (get_constraint_type(constraintOid) !=
20743 70 : get_constraint_type(cldConstrOid))
20744 0 : continue;
20745 : }
20746 :
20747 : /* bingo. */
20748 119 : IndexSetParentIndex(attachrelIdxRels[i], idx);
20749 119 : if (OidIsValid(constraintOid))
20750 70 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20751 : RelationGetRelid(attachrel));
20752 119 : found = true;
20753 :
20754 119 : CommandCounterIncrement();
20755 119 : break;
20756 : }
20757 : }
20758 :
20759 : /*
20760 : * If no suitable index was found in the partition-to-be, create one
20761 : * now. Note that if this is a PK, not-null constraints must already
20762 : * exist.
20763 : */
20764 363 : if (!found)
20765 : {
20766 : IndexStmt *stmt;
20767 : Oid conOid;
20768 :
20769 244 : stmt = generateClonedIndexStmt(NULL,
20770 : idxRel, attmap,
20771 : &conOid);
20772 244 : DefineIndex(NULL,
20773 : RelationGetRelid(attachrel), stmt, InvalidOid,
20774 : RelationGetRelid(idxRel),
20775 : conOid,
20776 : -1,
20777 : true, false, false, false, false);
20778 : }
20779 :
20780 354 : index_close(idxRel, AccessShareLock);
20781 : }
20782 :
20783 1439 : out:
20784 : /* Clean up. */
20785 1636 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20786 197 : index_close(attachrelIdxRels[i], AccessShareLock);
20787 1439 : MemoryContextSwitchTo(oldcxt);
20788 1439 : MemoryContextDelete(cxt);
20789 1439 : }
20790 :
20791 : /*
20792 : * CloneRowTriggersToPartition
20793 : * subroutine for ATExecAttachPartition/DefineRelation to create row
20794 : * triggers on partitions
20795 : */
20796 : static void
20797 1676 : CloneRowTriggersToPartition(Relation parent, Relation partition)
20798 : {
20799 : Relation pg_trigger;
20800 : ScanKeyData key;
20801 : SysScanDesc scan;
20802 : HeapTuple tuple;
20803 : MemoryContext perTupCxt;
20804 :
20805 1676 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20806 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20807 1676 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20808 1676 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20809 : true, NULL, 1, &key);
20810 :
20811 1676 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
20812 : "clone trig", ALLOCSET_SMALL_SIZES);
20813 :
20814 2712 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20815 : {
20816 1039 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20817 : CreateTrigStmt *trigStmt;
20818 1039 : Node *qual = NULL;
20819 : Datum value;
20820 : bool isnull;
20821 1039 : List *cols = NIL;
20822 1039 : List *trigargs = NIL;
20823 : MemoryContext oldcxt;
20824 :
20825 : /*
20826 : * Ignore statement-level triggers; those are not cloned.
20827 : */
20828 1039 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20829 940 : continue;
20830 :
20831 : /*
20832 : * Don't clone internal triggers, because the constraint cloning code
20833 : * will.
20834 : */
20835 1018 : if (trigForm->tgisinternal)
20836 919 : continue;
20837 :
20838 : /*
20839 : * Complain if we find an unexpected trigger type.
20840 : */
20841 99 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20842 81 : !TRIGGER_FOR_AFTER(trigForm->tgtype))
20843 0 : elog(ERROR, "unexpected trigger \"%s\" found",
20844 : NameStr(trigForm->tgname));
20845 :
20846 : /* Use short-lived context for CREATE TRIGGER */
20847 99 : oldcxt = MemoryContextSwitchTo(perTupCxt);
20848 :
20849 : /*
20850 : * If there is a WHEN clause, generate a 'cooked' version of it that's
20851 : * appropriate for the partition.
20852 : */
20853 99 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20854 : RelationGetDescr(pg_trigger), &isnull);
20855 99 : if (!isnull)
20856 : {
20857 3 : qual = stringToNode(TextDatumGetCString(value));
20858 3 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20859 : partition, parent);
20860 3 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20861 : partition, parent);
20862 : }
20863 :
20864 : /*
20865 : * If there is a column list, transform it to a list of column names.
20866 : * Note we don't need to map this list in any way ...
20867 : */
20868 99 : if (trigForm->tgattr.dim1 > 0)
20869 : {
20870 : int i;
20871 :
20872 6 : for (i = 0; i < trigForm->tgattr.dim1; i++)
20873 : {
20874 : Form_pg_attribute col;
20875 :
20876 3 : col = TupleDescAttr(parent->rd_att,
20877 3 : trigForm->tgattr.values[i] - 1);
20878 3 : cols = lappend(cols,
20879 3 : makeString(pstrdup(NameStr(col->attname))));
20880 : }
20881 : }
20882 :
20883 : /* Reconstruct trigger arguments list. */
20884 99 : if (trigForm->tgnargs > 0)
20885 : {
20886 : char *p;
20887 :
20888 27 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20889 : RelationGetDescr(pg_trigger), &isnull);
20890 27 : if (isnull)
20891 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20892 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
20893 :
20894 27 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20895 :
20896 60 : for (int i = 0; i < trigForm->tgnargs; i++)
20897 : {
20898 33 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
20899 33 : p += strlen(p) + 1;
20900 : }
20901 : }
20902 :
20903 99 : trigStmt = makeNode(CreateTrigStmt);
20904 99 : trigStmt->replace = false;
20905 99 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20906 99 : trigStmt->trigname = NameStr(trigForm->tgname);
20907 99 : trigStmt->relation = NULL;
20908 99 : trigStmt->funcname = NULL; /* passed separately */
20909 99 : trigStmt->args = trigargs;
20910 99 : trigStmt->row = true;
20911 99 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20912 99 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20913 99 : trigStmt->columns = cols;
20914 99 : trigStmt->whenClause = NULL; /* passed separately */
20915 99 : trigStmt->transitionRels = NIL; /* not supported at present */
20916 99 : trigStmt->deferrable = trigForm->tgdeferrable;
20917 99 : trigStmt->initdeferred = trigForm->tginitdeferred;
20918 99 : trigStmt->constrrel = NULL; /* passed separately */
20919 :
20920 99 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20921 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20922 : trigForm->tgfoid, trigForm->oid, qual,
20923 99 : false, true, trigForm->tgenabled);
20924 :
20925 96 : MemoryContextSwitchTo(oldcxt);
20926 96 : MemoryContextReset(perTupCxt);
20927 : }
20928 :
20929 1673 : MemoryContextDelete(perTupCxt);
20930 :
20931 1673 : systable_endscan(scan);
20932 1673 : table_close(pg_trigger, RowExclusiveLock);
20933 1673 : }
20934 :
20935 : /*
20936 : * ALTER TABLE DETACH PARTITION
20937 : *
20938 : * Return the address of the relation that is no longer a partition of rel.
20939 : *
20940 : * If concurrent mode is requested, we run in two transactions. A side-
20941 : * effect is that this command cannot run in a multi-part ALTER TABLE.
20942 : * Currently, that's enforced by the grammar.
20943 : *
20944 : * The strategy for concurrency is to first modify the partition's
20945 : * pg_inherit catalog row to make it visible to everyone that the
20946 : * partition is detached, lock the partition against writes, and commit
20947 : * the transaction; anyone who requests the partition descriptor from
20948 : * that point onwards has to ignore such a partition. In a second
20949 : * transaction, we wait until all transactions that could have seen the
20950 : * partition as attached are gone, then we remove the rest of partition
20951 : * metadata (pg_inherits and pg_class.relpartbounds).
20952 : */
20953 : static ObjectAddress
20954 292 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
20955 : RangeVar *name, bool concurrent)
20956 : {
20957 : Relation partRel;
20958 : ObjectAddress address;
20959 : Oid defaultPartOid;
20960 :
20961 : /*
20962 : * We must lock the default partition, because detaching this partition
20963 : * will change its partition constraint.
20964 : */
20965 : defaultPartOid =
20966 292 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20967 292 : if (OidIsValid(defaultPartOid))
20968 : {
20969 : /*
20970 : * Concurrent detaching when a default partition exists is not
20971 : * supported. The main problem is that the default partition
20972 : * constraint would change. And there's a definitional problem: what
20973 : * should happen to the tuples that are being inserted that belong to
20974 : * the partition being detached? Putting them on the partition being
20975 : * detached would be wrong, since they'd become "lost" after the
20976 : * detaching completes but we cannot put them in the default partition
20977 : * either until we alter its partition constraint.
20978 : *
20979 : * I think we could solve this problem if we effected the constraint
20980 : * change before committing the first transaction. But the lock would
20981 : * have to remain AEL and it would cause concurrent query planning to
20982 : * be blocked, so changing it that way would be even worse.
20983 : */
20984 56 : if (concurrent)
20985 6 : ereport(ERROR,
20986 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20987 : errmsg("cannot detach partitions concurrently when a default partition exists")));
20988 50 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20989 : }
20990 :
20991 : /*
20992 : * In concurrent mode, the partition is locked with share-update-exclusive
20993 : * in the first transaction. This allows concurrent transactions to be
20994 : * doing DML to the partition.
20995 : */
20996 286 : partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20997 : AccessExclusiveLock);
20998 :
20999 : /*
21000 : * Check inheritance conditions and either delete the pg_inherits row (in
21001 : * non-concurrent mode) or just set the inhdetachpending flag.
21002 : */
21003 280 : if (!concurrent)
21004 207 : RemoveInheritance(partRel, rel, false);
21005 : else
21006 73 : MarkInheritDetached(partRel, rel);
21007 :
21008 : /*
21009 : * Ensure that foreign keys still hold after this detach. This keeps
21010 : * locks on the referencing tables, which prevents concurrent transactions
21011 : * from adding rows that we wouldn't see. For this to work in concurrent
21012 : * mode, it is critical that the partition appears as no longer attached
21013 : * for the RI queries as soon as the first transaction commits.
21014 : */
21015 270 : ATDetachCheckNoForeignKeyRefs(partRel);
21016 :
21017 : /*
21018 : * Concurrent mode has to work harder; first we add a new constraint to
21019 : * the partition that matches the partition constraint. Then we close our
21020 : * existing transaction, and in a new one wait for all processes to catch
21021 : * up on the catalog updates we've done so far; at that point we can
21022 : * complete the operation.
21023 : */
21024 253 : if (concurrent)
21025 : {
21026 : Oid partrelid,
21027 : parentrelid;
21028 : LOCKTAG tag;
21029 : char *parentrelname;
21030 : char *partrelname;
21031 :
21032 : /*
21033 : * We're almost done now; the only traces that remain are the
21034 : * pg_inherits tuple and the partition's relpartbounds. Before we can
21035 : * remove those, we need to wait until all transactions that know that
21036 : * this is a partition are gone.
21037 : */
21038 :
21039 : /*
21040 : * Remember relation OIDs to re-acquire them later; and relation names
21041 : * too, for error messages if something is dropped in between.
21042 : */
21043 70 : partrelid = RelationGetRelid(partRel);
21044 70 : parentrelid = RelationGetRelid(rel);
21045 70 : parentrelname = MemoryContextStrdup(PortalContext,
21046 70 : RelationGetRelationName(rel));
21047 70 : partrelname = MemoryContextStrdup(PortalContext,
21048 70 : RelationGetRelationName(partRel));
21049 :
21050 : /* Invalidate relcache entries for the parent -- must be before close */
21051 70 : CacheInvalidateRelcache(rel);
21052 :
21053 70 : table_close(partRel, NoLock);
21054 70 : table_close(rel, NoLock);
21055 70 : tab->rel = NULL;
21056 :
21057 : /* Make updated catalog entry visible */
21058 70 : PopActiveSnapshot();
21059 70 : CommitTransactionCommand();
21060 :
21061 70 : StartTransactionCommand();
21062 :
21063 : /*
21064 : * Now wait. This ensures that all queries that were planned
21065 : * including the partition are finished before we remove the rest of
21066 : * catalog entries. We don't need or indeed want to acquire this
21067 : * lock, though -- that would block later queries.
21068 : *
21069 : * We don't need to concern ourselves with waiting for a lock on the
21070 : * partition itself, since we will acquire AccessExclusiveLock below.
21071 : */
21072 70 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
21073 70 : WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
21074 :
21075 : /*
21076 : * Now acquire locks in both relations again. Note they may have been
21077 : * removed in the meantime, so care is required.
21078 : */
21079 45 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
21080 45 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
21081 :
21082 : /* If the relations aren't there, something bad happened; bail out */
21083 45 : if (rel == NULL)
21084 : {
21085 0 : if (partRel != NULL) /* shouldn't happen */
21086 0 : elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21087 : partrelname);
21088 0 : ereport(ERROR,
21089 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21090 : errmsg("partitioned table \"%s\" was removed concurrently",
21091 : parentrelname)));
21092 : }
21093 45 : if (partRel == NULL)
21094 0 : ereport(ERROR,
21095 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21096 : errmsg("partition \"%s\" was removed concurrently", partrelname)));
21097 :
21098 45 : tab->rel = rel;
21099 : }
21100 :
21101 : /*
21102 : * Detaching the partition might involve TOAST table access, so ensure we
21103 : * have a valid snapshot.
21104 : */
21105 228 : PushActiveSnapshot(GetTransactionSnapshot());
21106 :
21107 : /* Do the final part of detaching */
21108 228 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21109 :
21110 227 : PopActiveSnapshot();
21111 :
21112 227 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21113 :
21114 : /* keep our lock until commit */
21115 227 : table_close(partRel, NoLock);
21116 :
21117 227 : return address;
21118 : }
21119 :
21120 : /*
21121 : * Second part of ALTER TABLE .. DETACH.
21122 : *
21123 : * This is separate so that it can be run independently when the second
21124 : * transaction of the concurrent algorithm fails (crash or abort).
21125 : */
21126 : static void
21127 520 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
21128 : Oid defaultPartOid)
21129 : {
21130 : Relation classRel;
21131 : List *fks;
21132 : ListCell *cell;
21133 : List *indexes;
21134 : Datum new_val[Natts_pg_class];
21135 : bool new_null[Natts_pg_class],
21136 : new_repl[Natts_pg_class];
21137 : HeapTuple tuple,
21138 : newtuple;
21139 520 : Relation trigrel = NULL;
21140 520 : List *fkoids = NIL;
21141 :
21142 520 : if (concurrent)
21143 : {
21144 : /*
21145 : * We can remove the pg_inherits row now. (In the non-concurrent case,
21146 : * this was already done).
21147 : */
21148 52 : RemoveInheritance(partRel, rel, true);
21149 : }
21150 :
21151 : /* Drop any triggers that were cloned on creation/attach. */
21152 520 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
21153 :
21154 : /*
21155 : * Detach any foreign keys that are inherited. This includes creating
21156 : * additional action triggers.
21157 : */
21158 520 : fks = copyObject(RelationGetFKeyList(partRel));
21159 520 : if (fks != NIL)
21160 45 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21161 :
21162 : /*
21163 : * It's possible that the partition being detached has a foreign key that
21164 : * references a partitioned table. When that happens, there are multiple
21165 : * pg_constraint rows for the partition: one points to the partitioned
21166 : * table itself, while the others point to each of its partitions. Only
21167 : * the topmost one is to be considered here; the child constraints must be
21168 : * left alone, because conceptually those aren't coming from our parent
21169 : * partitioned table, but from this partition itself.
21170 : *
21171 : * We implement this by collecting all the constraint OIDs in a first scan
21172 : * of the FK array, and skipping in the loop below those constraints whose
21173 : * parents are listed here.
21174 : */
21175 1127 : foreach_node(ForeignKeyCacheInfo, fk, fks)
21176 87 : fkoids = lappend_oid(fkoids, fk->conoid);
21177 :
21178 607 : foreach(cell, fks)
21179 : {
21180 87 : ForeignKeyCacheInfo *fk = lfirst(cell);
21181 : HeapTuple contup;
21182 : Form_pg_constraint conform;
21183 :
21184 87 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21185 87 : if (!HeapTupleIsValid(contup))
21186 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21187 87 : conform = (Form_pg_constraint) GETSTRUCT(contup);
21188 :
21189 : /*
21190 : * Consider only inherited foreign keys, and only if their parents
21191 : * aren't in the list.
21192 : */
21193 87 : if (conform->contype != CONSTRAINT_FOREIGN ||
21194 162 : !OidIsValid(conform->conparentid) ||
21195 75 : list_member_oid(fkoids, conform->conparentid))
21196 : {
21197 33 : ReleaseSysCache(contup);
21198 33 : continue;
21199 : }
21200 :
21201 : /*
21202 : * The constraint on this table must be marked no longer a child of
21203 : * the parent's constraint, as do its check triggers.
21204 : */
21205 54 : ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
21206 :
21207 : /*
21208 : * Also, look up the partition's "check" triggers corresponding to the
21209 : * ENFORCED constraint being detached and detach them from the parent
21210 : * triggers. NOT ENFORCED constraints do not have these triggers;
21211 : * therefore, this step is not needed.
21212 : */
21213 54 : if (fk->conenforced)
21214 : {
21215 : Oid insertTriggerOid,
21216 : updateTriggerOid;
21217 :
21218 54 : GetForeignKeyCheckTriggers(trigrel,
21219 : fk->conoid, fk->confrelid, fk->conrelid,
21220 : &insertTriggerOid, &updateTriggerOid);
21221 : Assert(OidIsValid(insertTriggerOid));
21222 54 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21223 : RelationGetRelid(partRel));
21224 : Assert(OidIsValid(updateTriggerOid));
21225 54 : TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21226 : RelationGetRelid(partRel));
21227 : }
21228 :
21229 : /*
21230 : * Lastly, create the action triggers on the referenced table, using
21231 : * addFkRecurseReferenced, which requires some elaborate setup (so put
21232 : * it in a separate block). While at it, if the table is partitioned,
21233 : * that function will recurse to create the pg_constraint rows and
21234 : * action triggers for each partition.
21235 : *
21236 : * Note there's no need to do addFkConstraint() here, because the
21237 : * pg_constraint row already exists.
21238 : */
21239 : {
21240 : Constraint *fkconstraint;
21241 : int numfks;
21242 : AttrNumber conkey[INDEX_MAX_KEYS];
21243 : AttrNumber confkey[INDEX_MAX_KEYS];
21244 : Oid conpfeqop[INDEX_MAX_KEYS];
21245 : Oid conppeqop[INDEX_MAX_KEYS];
21246 : Oid conffeqop[INDEX_MAX_KEYS];
21247 : int numfkdelsetcols;
21248 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21249 : Relation refdRel;
21250 :
21251 54 : DeconstructFkConstraintRow(contup,
21252 : &numfks,
21253 : conkey,
21254 : confkey,
21255 : conpfeqop,
21256 : conppeqop,
21257 : conffeqop,
21258 : &numfkdelsetcols,
21259 : confdelsetcols);
21260 :
21261 : /* Create a synthetic node we'll use throughout */
21262 54 : fkconstraint = makeNode(Constraint);
21263 54 : fkconstraint->contype = CONSTRAINT_FOREIGN;
21264 54 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
21265 54 : fkconstraint->deferrable = conform->condeferrable;
21266 54 : fkconstraint->initdeferred = conform->condeferred;
21267 54 : fkconstraint->is_enforced = conform->conenforced;
21268 54 : fkconstraint->skip_validation = true;
21269 54 : fkconstraint->initially_valid = conform->convalidated;
21270 : /* a few irrelevant fields omitted here */
21271 54 : fkconstraint->pktable = NULL;
21272 54 : fkconstraint->fk_attrs = NIL;
21273 54 : fkconstraint->pk_attrs = NIL;
21274 54 : fkconstraint->fk_matchtype = conform->confmatchtype;
21275 54 : fkconstraint->fk_upd_action = conform->confupdtype;
21276 54 : fkconstraint->fk_del_action = conform->confdeltype;
21277 54 : fkconstraint->fk_del_set_cols = NIL;
21278 54 : fkconstraint->old_conpfeqop = NIL;
21279 54 : fkconstraint->old_pktable_oid = InvalidOid;
21280 54 : fkconstraint->location = -1;
21281 :
21282 : /* set up colnames, used to generate the constraint name */
21283 132 : for (int i = 0; i < numfks; i++)
21284 : {
21285 : Form_pg_attribute att;
21286 :
21287 78 : att = TupleDescAttr(RelationGetDescr(partRel),
21288 78 : conkey[i] - 1);
21289 :
21290 78 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21291 78 : makeString(NameStr(att->attname)));
21292 : }
21293 :
21294 54 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
21295 :
21296 54 : addFkRecurseReferenced(fkconstraint, partRel,
21297 : refdRel,
21298 : conform->conindid,
21299 : fk->conoid,
21300 : numfks,
21301 : confkey,
21302 : conkey,
21303 : conpfeqop,
21304 : conppeqop,
21305 : conffeqop,
21306 : numfkdelsetcols,
21307 : confdelsetcols,
21308 : true,
21309 : InvalidOid, InvalidOid,
21310 54 : conform->conperiod);
21311 54 : table_close(refdRel, NoLock); /* keep lock till end of xact */
21312 : }
21313 :
21314 54 : ReleaseSysCache(contup);
21315 : }
21316 520 : list_free_deep(fks);
21317 520 : if (trigrel)
21318 45 : table_close(trigrel, RowExclusiveLock);
21319 :
21320 : /*
21321 : * Any sub-constraints that are in the referenced-side of a larger
21322 : * constraint have to be removed. This partition is no longer part of the
21323 : * key space of the constraint.
21324 : */
21325 565 : foreach(cell, GetParentedForeignKeyRefs(partRel))
21326 : {
21327 46 : Oid constrOid = lfirst_oid(cell);
21328 : ObjectAddress constraint;
21329 :
21330 46 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21331 46 : deleteDependencyRecordsForClass(ConstraintRelationId,
21332 : constrOid,
21333 : ConstraintRelationId,
21334 : DEPENDENCY_INTERNAL);
21335 46 : CommandCounterIncrement();
21336 :
21337 46 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21338 46 : performDeletion(&constraint, DROP_RESTRICT, 0);
21339 : }
21340 :
21341 : /* Now we can detach indexes */
21342 519 : indexes = RelationGetIndexList(partRel);
21343 739 : foreach(cell, indexes)
21344 : {
21345 220 : Oid idxid = lfirst_oid(cell);
21346 : Oid parentidx;
21347 : Relation idx;
21348 : Oid constrOid;
21349 : Oid parentConstrOid;
21350 :
21351 220 : if (!has_superclass(idxid))
21352 6 : continue;
21353 :
21354 214 : parentidx = get_partition_parent(idxid, false);
21355 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21356 :
21357 214 : idx = index_open(idxid, AccessExclusiveLock);
21358 214 : IndexSetParentIndex(idx, InvalidOid);
21359 :
21360 : /*
21361 : * If there's a constraint associated with the index, detach it too.
21362 : * Careful: it is possible for a constraint index in a partition to be
21363 : * the child of a non-constraint index, so verify whether the parent
21364 : * index does actually have a constraint.
21365 : */
21366 214 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
21367 : idxid);
21368 214 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
21369 : parentidx);
21370 214 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21371 99 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21372 :
21373 214 : index_close(idx, NoLock);
21374 : }
21375 :
21376 : /* Update pg_class tuple */
21377 519 : classRel = table_open(RelationRelationId, RowExclusiveLock);
21378 519 : tuple = SearchSysCacheCopy1(RELOID,
21379 : ObjectIdGetDatum(RelationGetRelid(partRel)));
21380 519 : if (!HeapTupleIsValid(tuple))
21381 0 : elog(ERROR, "cache lookup failed for relation %u",
21382 : RelationGetRelid(partRel));
21383 : Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21384 :
21385 : /* Clear relpartbound and reset relispartition */
21386 519 : memset(new_val, 0, sizeof(new_val));
21387 519 : memset(new_null, false, sizeof(new_null));
21388 519 : memset(new_repl, false, sizeof(new_repl));
21389 519 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21390 519 : new_null[Anum_pg_class_relpartbound - 1] = true;
21391 519 : new_repl[Anum_pg_class_relpartbound - 1] = true;
21392 519 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21393 : new_val, new_null, new_repl);
21394 :
21395 519 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21396 519 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21397 519 : heap_freetuple(newtuple);
21398 519 : table_close(classRel, RowExclusiveLock);
21399 :
21400 : /*
21401 : * Drop identity property from all identity columns of partition.
21402 : */
21403 1664 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21404 : {
21405 1145 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21406 :
21407 1145 : if (!attr->attisdropped && attr->attidentity)
21408 15 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21409 : AccessExclusiveLock, true, true);
21410 : }
21411 :
21412 519 : if (OidIsValid(defaultPartOid))
21413 : {
21414 : /*
21415 : * If the relation being detached is the default partition itself,
21416 : * remove it from the parent's pg_partitioned_table entry.
21417 : *
21418 : * If not, we must invalidate default partition's relcache entry, as
21419 : * in StorePartitionBound: its partition constraint depends on every
21420 : * other partition's partition constraint.
21421 : */
21422 128 : if (RelationGetRelid(partRel) == defaultPartOid)
21423 22 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
21424 : else
21425 106 : CacheInvalidateRelcacheByRelid(defaultPartOid);
21426 : }
21427 :
21428 : /*
21429 : * Invalidate the parent's relcache so that the partition is no longer
21430 : * included in its partition descriptor.
21431 : */
21432 519 : CacheInvalidateRelcache(rel);
21433 :
21434 : /*
21435 : * If the partition we just detached is partitioned itself, invalidate
21436 : * relcache for all descendent partitions too to ensure that their
21437 : * rd_partcheck expression trees are rebuilt; must lock partitions before
21438 : * doing so, using the same lockmode as what partRel has been locked with
21439 : * by the caller.
21440 : */
21441 519 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21442 : {
21443 : List *children;
21444 :
21445 31 : children = find_all_inheritors(RelationGetRelid(partRel),
21446 : AccessExclusiveLock, NULL);
21447 102 : foreach(cell, children)
21448 : {
21449 71 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
21450 : }
21451 : }
21452 519 : }
21453 :
21454 : /*
21455 : * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21456 : *
21457 : * To use when a DETACH PARTITION command previously did not run to
21458 : * completion; this completes the detaching process.
21459 : */
21460 : static ObjectAddress
21461 7 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
21462 : {
21463 : Relation partRel;
21464 : ObjectAddress address;
21465 7 : Snapshot snap = GetActiveSnapshot();
21466 :
21467 7 : partRel = table_openrv(name, AccessExclusiveLock);
21468 :
21469 : /*
21470 : * Wait until existing snapshots are gone. This is important if the
21471 : * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21472 : * user could immediately run DETACH FINALIZE without actually waiting for
21473 : * existing transactions. We must not complete the detach action until
21474 : * all such queries are complete (otherwise we would present them with an
21475 : * inconsistent view of catalogs).
21476 : */
21477 7 : WaitForOlderSnapshots(snap->xmin, false);
21478 :
21479 7 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21480 :
21481 7 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21482 :
21483 7 : table_close(partRel, NoLock);
21484 :
21485 7 : return address;
21486 : }
21487 :
21488 : /*
21489 : * DropClonedTriggersFromPartition
21490 : * subroutine for ATExecDetachPartition to remove any triggers that were
21491 : * cloned to the partition when it was created-as-partition or attached.
21492 : * This undoes what CloneRowTriggersToPartition did.
21493 : */
21494 : static void
21495 520 : DropClonedTriggersFromPartition(Oid partitionId)
21496 : {
21497 : ScanKeyData skey;
21498 : SysScanDesc scan;
21499 : HeapTuple trigtup;
21500 : Relation tgrel;
21501 : ObjectAddresses *objects;
21502 :
21503 520 : objects = new_object_addresses();
21504 :
21505 : /*
21506 : * Scan pg_trigger to search for all triggers on this rel.
21507 : */
21508 520 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21509 : F_OIDEQ, ObjectIdGetDatum(partitionId));
21510 520 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21511 520 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21512 : true, NULL, 1, &skey);
21513 780 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21514 : {
21515 260 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21516 : ObjectAddress trig;
21517 :
21518 : /* Ignore triggers that weren't cloned */
21519 260 : if (!OidIsValid(pg_trigger->tgparentid))
21520 230 : continue;
21521 :
21522 : /*
21523 : * Ignore internal triggers that are implementation objects of foreign
21524 : * keys, because these will be detached when the foreign keys
21525 : * themselves are.
21526 : */
21527 218 : if (OidIsValid(pg_trigger->tgconstrrelid))
21528 188 : continue;
21529 :
21530 : /*
21531 : * This is ugly, but necessary: remove the dependency markings on the
21532 : * trigger so that it can be removed.
21533 : */
21534 30 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21535 : TriggerRelationId,
21536 : DEPENDENCY_PARTITION_PRI);
21537 30 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21538 : RelationRelationId,
21539 : DEPENDENCY_PARTITION_SEC);
21540 :
21541 : /* remember this trigger to remove it below */
21542 30 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21543 30 : add_exact_object_address(&trig, objects);
21544 : }
21545 :
21546 : /* make the dependency removal visible to the deletion below */
21547 520 : CommandCounterIncrement();
21548 520 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
21549 :
21550 : /* done */
21551 520 : free_object_addresses(objects);
21552 520 : systable_endscan(scan);
21553 520 : table_close(tgrel, RowExclusiveLock);
21554 520 : }
21555 :
21556 : /*
21557 : * Before acquiring lock on an index, acquire the same lock on the owning
21558 : * table.
21559 : */
21560 : struct AttachIndexCallbackState
21561 : {
21562 : Oid partitionOid;
21563 : Oid parentTblOid;
21564 : bool lockedParentTbl;
21565 : };
21566 :
21567 : static void
21568 208 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21569 : void *arg)
21570 : {
21571 : struct AttachIndexCallbackState *state;
21572 : Form_pg_class classform;
21573 : HeapTuple tuple;
21574 :
21575 208 : state = (struct AttachIndexCallbackState *) arg;
21576 :
21577 208 : if (!state->lockedParentTbl)
21578 : {
21579 198 : LockRelationOid(state->parentTblOid, AccessShareLock);
21580 198 : state->lockedParentTbl = true;
21581 : }
21582 :
21583 : /*
21584 : * If we previously locked some other heap, and the name we're looking up
21585 : * no longer refers to an index on that relation, release the now-useless
21586 : * lock. XXX maybe we should do *after* we verify whether the index does
21587 : * not actually belong to the same relation ...
21588 : */
21589 208 : if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21590 : {
21591 0 : UnlockRelationOid(state->partitionOid, AccessShareLock);
21592 0 : state->partitionOid = InvalidOid;
21593 : }
21594 :
21595 : /* Didn't find a relation, so no need for locking or permission checks. */
21596 208 : if (!OidIsValid(relOid))
21597 3 : return;
21598 :
21599 205 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21600 205 : if (!HeapTupleIsValid(tuple))
21601 0 : return; /* concurrently dropped, so nothing to do */
21602 205 : classform = (Form_pg_class) GETSTRUCT(tuple);
21603 205 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21604 159 : classform->relkind != RELKIND_INDEX)
21605 3 : ereport(ERROR,
21606 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21607 : errmsg("\"%s\" is not an index", rv->relname)));
21608 202 : ReleaseSysCache(tuple);
21609 :
21610 : /*
21611 : * Since we need only examine the heap's tupledesc, an access share lock
21612 : * on it (preventing any DDL) is sufficient.
21613 : */
21614 202 : state->partitionOid = IndexGetRelation(relOid, false);
21615 202 : LockRelationOid(state->partitionOid, AccessShareLock);
21616 : }
21617 :
21618 : /*
21619 : * ALTER INDEX i1 ATTACH PARTITION i2
21620 : */
21621 : static ObjectAddress
21622 198 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
21623 : {
21624 : Relation partIdx;
21625 : Relation partTbl;
21626 : Relation parentTbl;
21627 : ObjectAddress address;
21628 : Oid partIdxId;
21629 : Oid currParent;
21630 : struct AttachIndexCallbackState state;
21631 :
21632 : /*
21633 : * We need to obtain lock on the index 'name' to modify it, but we also
21634 : * need to read its owning table's tuple descriptor -- so we need to lock
21635 : * both. To avoid deadlocks, obtain lock on the table before doing so on
21636 : * the index. Furthermore, we need to examine the parent table of the
21637 : * partition, so lock that one too.
21638 : */
21639 198 : state.partitionOid = InvalidOid;
21640 198 : state.parentTblOid = parentIdx->rd_index->indrelid;
21641 198 : state.lockedParentTbl = false;
21642 : partIdxId =
21643 198 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
21644 : RangeVarCallbackForAttachIndex,
21645 : &state);
21646 : /* Not there? */
21647 192 : if (!OidIsValid(partIdxId))
21648 0 : ereport(ERROR,
21649 : (errcode(ERRCODE_UNDEFINED_OBJECT),
21650 : errmsg("index \"%s\" does not exist", name->relname)));
21651 :
21652 : /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21653 192 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
21654 :
21655 : /* we already hold locks on both tables, so this is safe: */
21656 192 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21657 192 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21658 :
21659 192 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21660 :
21661 : /* Silently do nothing if already in the right state */
21662 384 : currParent = partIdx->rd_rel->relispartition ?
21663 192 : get_partition_parent(partIdxId, false) : InvalidOid;
21664 192 : if (currParent != RelationGetRelid(parentIdx))
21665 : {
21666 : IndexInfo *childInfo;
21667 : IndexInfo *parentInfo;
21668 : AttrMap *attmap;
21669 : bool found;
21670 : int i;
21671 : PartitionDesc partDesc;
21672 : Oid constraintOid,
21673 180 : cldConstrId = InvalidOid;
21674 :
21675 : /*
21676 : * If this partition already has an index attached, refuse the
21677 : * operation.
21678 : */
21679 180 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21680 :
21681 177 : if (OidIsValid(currParent))
21682 0 : ereport(ERROR,
21683 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21684 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21685 : RelationGetRelationName(partIdx),
21686 : RelationGetRelationName(parentIdx)),
21687 : errdetail("Index \"%s\" is already attached to another index.",
21688 : RelationGetRelationName(partIdx))));
21689 :
21690 : /* Make sure it indexes a partition of the other index's table */
21691 177 : partDesc = RelationGetPartitionDesc(parentTbl, true);
21692 177 : found = false;
21693 278 : for (i = 0; i < partDesc->nparts; i++)
21694 : {
21695 275 : if (partDesc->oids[i] == state.partitionOid)
21696 : {
21697 174 : found = true;
21698 174 : break;
21699 : }
21700 : }
21701 177 : if (!found)
21702 3 : ereport(ERROR,
21703 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21704 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21705 : RelationGetRelationName(partIdx),
21706 : RelationGetRelationName(parentIdx)),
21707 : errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21708 : RelationGetRelationName(partIdx),
21709 : RelationGetRelationName(parentTbl))));
21710 :
21711 : /* Ensure the indexes are compatible */
21712 174 : childInfo = BuildIndexInfo(partIdx);
21713 174 : parentInfo = BuildIndexInfo(parentIdx);
21714 174 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21715 : RelationGetDescr(parentTbl),
21716 : false);
21717 174 : if (!CompareIndexInfo(childInfo, parentInfo,
21718 174 : partIdx->rd_indcollation,
21719 174 : parentIdx->rd_indcollation,
21720 174 : partIdx->rd_opfamily,
21721 174 : parentIdx->rd_opfamily,
21722 : attmap))
21723 21 : ereport(ERROR,
21724 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21725 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21726 : RelationGetRelationName(partIdx),
21727 : RelationGetRelationName(parentIdx)),
21728 : errdetail("The index definitions do not match.")));
21729 :
21730 : /*
21731 : * If there is a constraint in the parent, make sure there is one in
21732 : * the child too.
21733 : */
21734 153 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21735 : RelationGetRelid(parentIdx));
21736 :
21737 153 : if (OidIsValid(constraintOid))
21738 : {
21739 63 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
21740 : partIdxId);
21741 63 : if (!OidIsValid(cldConstrId))
21742 3 : ereport(ERROR,
21743 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21744 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21745 : RelationGetRelationName(partIdx),
21746 : RelationGetRelationName(parentIdx)),
21747 : errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21748 : RelationGetRelationName(parentIdx),
21749 : RelationGetRelationName(parentTbl),
21750 : RelationGetRelationName(partIdx))));
21751 : }
21752 :
21753 : /*
21754 : * If it's a primary key, make sure the columns in the partition are
21755 : * NOT NULL.
21756 : */
21757 150 : if (parentIdx->rd_index->indisprimary)
21758 48 : verifyPartitionIndexNotNull(childInfo, partTbl);
21759 :
21760 : /* All good -- do it */
21761 150 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21762 150 : if (OidIsValid(constraintOid))
21763 60 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
21764 : RelationGetRelid(partTbl));
21765 :
21766 150 : free_attrmap(attmap);
21767 :
21768 150 : validatePartitionedIndex(parentIdx, parentTbl);
21769 : }
21770 :
21771 162 : relation_close(parentTbl, AccessShareLock);
21772 : /* keep these locks till commit */
21773 162 : relation_close(partTbl, NoLock);
21774 162 : relation_close(partIdx, NoLock);
21775 :
21776 162 : return address;
21777 : }
21778 :
21779 : /*
21780 : * Verify whether the given partition already contains an index attached
21781 : * to the given partitioned index. If so, raise an error.
21782 : */
21783 : static void
21784 180 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21785 : {
21786 : Oid existingIdx;
21787 :
21788 180 : existingIdx = index_get_partition(partitionTbl,
21789 : RelationGetRelid(parentIdx));
21790 180 : if (OidIsValid(existingIdx))
21791 3 : ereport(ERROR,
21792 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21793 : errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21794 : RelationGetRelationName(partIdx),
21795 : RelationGetRelationName(parentIdx)),
21796 : errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21797 : get_rel_name(existingIdx),
21798 : RelationGetRelationName(partitionTbl))));
21799 177 : }
21800 :
21801 : /*
21802 : * Verify whether the set of attached partition indexes to a parent index on
21803 : * a partitioned table is complete. If it is, mark the parent index valid.
21804 : *
21805 : * This should be called each time a partition index is attached.
21806 : */
21807 : static void
21808 171 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
21809 : {
21810 : Relation inheritsRel;
21811 : SysScanDesc scan;
21812 : ScanKeyData key;
21813 171 : int tuples = 0;
21814 : HeapTuple inhTup;
21815 171 : bool updated = false;
21816 :
21817 : Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21818 :
21819 : /*
21820 : * Scan pg_inherits for this parent index. Count each valid index we find
21821 : * (verifying the pg_index entry for each), and if we reach the total
21822 : * amount we expect, we can mark this parent index as valid.
21823 : */
21824 171 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21825 171 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21826 : BTEqualStrategyNumber, F_OIDEQ,
21827 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21828 171 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21829 : NULL, 1, &key);
21830 444 : while ((inhTup = systable_getnext(scan)) != NULL)
21831 : {
21832 273 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21833 : HeapTuple indTup;
21834 : Form_pg_index indexForm;
21835 :
21836 273 : indTup = SearchSysCache1(INDEXRELID,
21837 : ObjectIdGetDatum(inhForm->inhrelid));
21838 273 : if (!HeapTupleIsValid(indTup))
21839 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21840 273 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21841 273 : if (indexForm->indisvalid)
21842 244 : tuples += 1;
21843 273 : ReleaseSysCache(indTup);
21844 : }
21845 :
21846 : /* Done with pg_inherits */
21847 171 : systable_endscan(scan);
21848 171 : table_close(inheritsRel, AccessShareLock);
21849 :
21850 : /*
21851 : * If we found as many inherited indexes as the partitioned table has
21852 : * partitions, we're good; update pg_index to set indisvalid.
21853 : */
21854 171 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21855 : {
21856 : Relation idxRel;
21857 : HeapTuple indTup;
21858 : Form_pg_index indexForm;
21859 :
21860 86 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
21861 86 : indTup = SearchSysCacheCopy1(INDEXRELID,
21862 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21863 86 : if (!HeapTupleIsValid(indTup))
21864 0 : elog(ERROR, "cache lookup failed for index %u",
21865 : RelationGetRelid(partedIdx));
21866 86 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21867 :
21868 86 : indexForm->indisvalid = true;
21869 86 : updated = true;
21870 :
21871 86 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21872 :
21873 86 : table_close(idxRel, RowExclusiveLock);
21874 86 : heap_freetuple(indTup);
21875 : }
21876 :
21877 : /*
21878 : * If this index is in turn a partition of a larger index, validating it
21879 : * might cause the parent to become valid also. Try that.
21880 : */
21881 171 : if (updated && partedIdx->rd_rel->relispartition)
21882 : {
21883 : Oid parentIdxId,
21884 : parentTblId;
21885 : Relation parentIdx,
21886 : parentTbl;
21887 :
21888 : /* make sure we see the validation we just did */
21889 21 : CommandCounterIncrement();
21890 :
21891 21 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21892 21 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21893 21 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21894 21 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21895 : Assert(!parentIdx->rd_index->indisvalid);
21896 :
21897 21 : validatePartitionedIndex(parentIdx, parentTbl);
21898 :
21899 21 : relation_close(parentIdx, AccessExclusiveLock);
21900 21 : relation_close(parentTbl, AccessExclusiveLock);
21901 : }
21902 171 : }
21903 :
21904 : /*
21905 : * When attaching an index as a partition of a partitioned index which is a
21906 : * primary key, verify that all the columns in the partition are marked NOT
21907 : * NULL.
21908 : */
21909 : static void
21910 48 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
21911 : {
21912 97 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21913 : {
21914 49 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
21915 49 : iinfo->ii_IndexAttrNumbers[i] - 1);
21916 :
21917 49 : if (!att->attnotnull)
21918 0 : ereport(ERROR,
21919 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21920 : errmsg("invalid primary key definition"),
21921 : errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21922 : NameStr(att->attname),
21923 : RelationGetRelationName(partition)));
21924 : }
21925 48 : }
21926 :
21927 : /*
21928 : * Return an OID list of constraints that reference the given relation
21929 : * that are marked as having a parent constraints.
21930 : */
21931 : static List *
21932 790 : GetParentedForeignKeyRefs(Relation partition)
21933 : {
21934 : Relation pg_constraint;
21935 : HeapTuple tuple;
21936 : SysScanDesc scan;
21937 : ScanKeyData key[2];
21938 790 : List *constraints = NIL;
21939 :
21940 : /*
21941 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
21942 : * scan.
21943 : */
21944 1109 : if (RelationGetIndexList(partition) == NIL ||
21945 319 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
21946 : INDEX_ATTR_BITMAP_KEY)))
21947 597 : return NIL;
21948 :
21949 : /* Search for constraints referencing this table */
21950 193 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21951 193 : ScanKeyInit(&key[0],
21952 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21953 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21954 193 : ScanKeyInit(&key[1],
21955 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
21956 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21957 :
21958 : /* XXX This is a seqscan, as we don't have a usable index */
21959 193 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21960 294 : while ((tuple = systable_getnext(scan)) != NULL)
21961 : {
21962 101 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21963 :
21964 : /*
21965 : * We only need to process constraints that are part of larger ones.
21966 : */
21967 101 : if (!OidIsValid(constrForm->conparentid))
21968 0 : continue;
21969 :
21970 101 : constraints = lappend_oid(constraints, constrForm->oid);
21971 : }
21972 :
21973 193 : systable_endscan(scan);
21974 193 : table_close(pg_constraint, AccessShareLock);
21975 :
21976 193 : return constraints;
21977 : }
21978 :
21979 : /*
21980 : * During DETACH PARTITION, verify that any foreign keys pointing to the
21981 : * partitioned table would not become invalid. An error is raised if any
21982 : * referenced values exist.
21983 : */
21984 : static void
21985 270 : ATDetachCheckNoForeignKeyRefs(Relation partition)
21986 : {
21987 : List *constraints;
21988 : ListCell *cell;
21989 :
21990 270 : constraints = GetParentedForeignKeyRefs(partition);
21991 :
21992 308 : foreach(cell, constraints)
21993 : {
21994 55 : Oid constrOid = lfirst_oid(cell);
21995 : HeapTuple tuple;
21996 : Form_pg_constraint constrForm;
21997 : Relation rel;
21998 55 : Trigger trig = {0};
21999 :
22000 55 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
22001 55 : if (!HeapTupleIsValid(tuple))
22002 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22003 55 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
22004 :
22005 : Assert(OidIsValid(constrForm->conparentid));
22006 : Assert(constrForm->confrelid == RelationGetRelid(partition));
22007 :
22008 : /* prevent data changes into the referencing table until commit */
22009 55 : rel = table_open(constrForm->conrelid, ShareLock);
22010 :
22011 55 : trig.tgoid = InvalidOid;
22012 55 : trig.tgname = NameStr(constrForm->conname);
22013 55 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
22014 55 : trig.tgisinternal = true;
22015 55 : trig.tgconstrrelid = RelationGetRelid(partition);
22016 55 : trig.tgconstrindid = constrForm->conindid;
22017 55 : trig.tgconstraint = constrForm->oid;
22018 55 : trig.tgdeferrable = false;
22019 55 : trig.tginitdeferred = false;
22020 : /* we needn't fill in remaining fields */
22021 :
22022 55 : RI_PartitionRemove_Check(&trig, rel, partition);
22023 :
22024 38 : ReleaseSysCache(tuple);
22025 :
22026 38 : table_close(rel, NoLock);
22027 : }
22028 253 : }
22029 :
22030 : /*
22031 : * resolve column compression specification to compression method.
22032 : */
22033 : static char
22034 135167 : GetAttributeCompression(Oid atttypid, const char *compression)
22035 : {
22036 : char cmethod;
22037 :
22038 135167 : if (compression == NULL || strcmp(compression, "default") == 0)
22039 135060 : return InvalidCompressionMethod;
22040 :
22041 : /*
22042 : * To specify a nondefault method, the column data type must be toastable.
22043 : * Note this says nothing about whether the column's attstorage setting
22044 : * permits compression; we intentionally allow attstorage and
22045 : * attcompression to be independent. But with a non-toastable type,
22046 : * attstorage could not be set to a value that would permit compression.
22047 : *
22048 : * We don't actually need to enforce this, since nothing bad would happen
22049 : * if attcompression were non-default; it would never be consulted. But
22050 : * it seems more user-friendly to complain about a certainly-useless
22051 : * attempt to set the property.
22052 : */
22053 107 : if (!TypeIsToastable(atttypid))
22054 3 : ereport(ERROR,
22055 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22056 : errmsg("column data type %s does not support compression",
22057 : format_type_be(atttypid))));
22058 :
22059 104 : cmethod = CompressionNameToMethod(compression);
22060 104 : if (!CompressionMethodIsValid(cmethod))
22061 6 : ereport(ERROR,
22062 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22063 : errmsg("invalid compression method \"%s\"", compression)));
22064 :
22065 98 : return cmethod;
22066 : }
22067 :
22068 : /*
22069 : * resolve column storage specification
22070 : */
22071 : static char
22072 165 : GetAttributeStorage(Oid atttypid, const char *storagemode)
22073 : {
22074 165 : char cstorage = 0;
22075 :
22076 165 : if (pg_strcasecmp(storagemode, "plain") == 0)
22077 29 : cstorage = TYPSTORAGE_PLAIN;
22078 136 : else if (pg_strcasecmp(storagemode, "external") == 0)
22079 88 : cstorage = TYPSTORAGE_EXTERNAL;
22080 48 : else if (pg_strcasecmp(storagemode, "extended") == 0)
22081 20 : cstorage = TYPSTORAGE_EXTENDED;
22082 28 : else if (pg_strcasecmp(storagemode, "main") == 0)
22083 25 : cstorage = TYPSTORAGE_MAIN;
22084 3 : else if (pg_strcasecmp(storagemode, "default") == 0)
22085 3 : cstorage = get_typstorage(atttypid);
22086 : else
22087 0 : ereport(ERROR,
22088 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22089 : errmsg("invalid storage type \"%s\"",
22090 : storagemode)));
22091 :
22092 : /*
22093 : * safety check: do not allow toasted storage modes unless column datatype
22094 : * is TOAST-aware.
22095 : */
22096 165 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22097 3 : ereport(ERROR,
22098 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22099 : errmsg("column data type %s can only have storage PLAIN",
22100 : format_type_be(atttypid))));
22101 :
22102 162 : return cstorage;
22103 : }
22104 :
22105 : /*
22106 : * buildExpressionExecutionStates: build the needed expression execution states
22107 : * for new partition (newPartRel) checks and initialize expressions for
22108 : * generated columns. All expressions should be created in "tab"
22109 : * (AlteredTableInfo structure).
22110 : */
22111 : static void
22112 324 : buildExpressionExecutionStates(AlteredTableInfo *tab, Relation newPartRel, EState *estate)
22113 : {
22114 : /*
22115 : * Build the needed expression execution states. Here, we expect only NOT
22116 : * NULL and CHECK constraint.
22117 : */
22118 660 : foreach_ptr(NewConstraint, con, tab->constraints)
22119 : {
22120 12 : switch (con->contype)
22121 : {
22122 12 : case CONSTR_CHECK:
22123 :
22124 : /*
22125 : * We already expanded virtual expression in
22126 : * createTableConstraints.
22127 : */
22128 12 : con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22129 12 : break;
22130 0 : case CONSTR_NOTNULL:
22131 : /* Nothing to do here. */
22132 0 : break;
22133 0 : default:
22134 0 : elog(ERROR, "unrecognized constraint type: %d",
22135 : (int) con->contype);
22136 : }
22137 : }
22138 :
22139 : /* Expression already planned in createTableConstraints */
22140 681 : foreach_ptr(NewColumnValue, ex, tab->newvals)
22141 33 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22142 324 : }
22143 :
22144 : /*
22145 : * evaluateGeneratedExpressionsAndCheckConstraints: evaluate any generated
22146 : * expressions for "tab" (AlteredTableInfo structure) whose inputs come from
22147 : * the new tuple (insertslot) of the new partition (newPartRel).
22148 : */
22149 : static void
22150 509 : evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab,
22151 : Relation newPartRel,
22152 : TupleTableSlot *insertslot,
22153 : ExprContext *econtext)
22154 : {
22155 509 : econtext->ecxt_scantuple = insertslot;
22156 :
22157 1066 : foreach_ptr(NewColumnValue, ex, tab->newvals)
22158 : {
22159 48 : if (!ex->is_generated)
22160 0 : continue;
22161 :
22162 48 : insertslot->tts_values[ex->attnum - 1]
22163 48 : = ExecEvalExpr(ex->exprstate,
22164 : econtext,
22165 48 : &insertslot->tts_isnull[ex->attnum - 1]);
22166 : }
22167 :
22168 1036 : foreach_ptr(NewConstraint, con, tab->constraints)
22169 : {
22170 18 : switch (con->contype)
22171 : {
22172 18 : case CONSTR_CHECK:
22173 18 : if (!ExecCheck(con->qualstate, econtext))
22174 0 : ereport(ERROR,
22175 : errcode(ERRCODE_CHECK_VIOLATION),
22176 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22177 : con->name, RelationGetRelationName(newPartRel)),
22178 : errtableconstraint(newPartRel, con->name));
22179 18 : break;
22180 0 : case CONSTR_NOTNULL:
22181 : case CONSTR_FOREIGN:
22182 : /* Nothing to do here */
22183 0 : break;
22184 0 : default:
22185 0 : elog(ERROR, "unrecognized constraint type: %d",
22186 : (int) con->contype);
22187 : }
22188 : }
22189 509 : }
22190 :
22191 : /*
22192 : * getAttributesList: build a list of columns (ColumnDef) based on parent_rel
22193 : */
22194 : static List *
22195 339 : getAttributesList(Relation parent_rel)
22196 : {
22197 : AttrNumber parent_attno;
22198 : TupleDesc modelDesc;
22199 339 : List *colList = NIL;
22200 :
22201 339 : modelDesc = RelationGetDescr(parent_rel);
22202 :
22203 1212 : for (parent_attno = 1; parent_attno <= modelDesc->natts;
22204 873 : parent_attno++)
22205 : {
22206 873 : Form_pg_attribute attribute = TupleDescAttr(modelDesc,
22207 : parent_attno - 1);
22208 : ColumnDef *def;
22209 :
22210 : /* Ignore dropped columns in the parent. */
22211 873 : if (attribute->attisdropped)
22212 0 : continue;
22213 :
22214 873 : def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22215 : attribute->atttypmod, attribute->attcollation);
22216 :
22217 873 : def->is_not_null = attribute->attnotnull;
22218 :
22219 : /* Copy identity. */
22220 873 : def->identity = attribute->attidentity;
22221 :
22222 : /* Copy attgenerated. */
22223 873 : def->generated = attribute->attgenerated;
22224 :
22225 873 : def->storage = attribute->attstorage;
22226 :
22227 : /* Likewise, copy compression. */
22228 873 : if (CompressionMethodIsValid(attribute->attcompression))
22229 9 : def->compression =
22230 9 : pstrdup(GetCompressionMethodName(attribute->attcompression));
22231 : else
22232 864 : def->compression = NULL;
22233 :
22234 : /* Add to column list. */
22235 873 : colList = lappend(colList, def);
22236 : }
22237 :
22238 339 : return colList;
22239 : }
22240 :
22241 : /*
22242 : * createTableConstraints:
22243 : * create check constraints, default values, and generated values for newRel
22244 : * based on parent_rel. tab is pending-work queue for newRel, we may need it in
22245 : * MergePartitionsMoveRows.
22246 : */
22247 : static void
22248 324 : createTableConstraints(List **wqueue, AlteredTableInfo *tab,
22249 : Relation parent_rel, Relation newRel)
22250 : {
22251 : TupleDesc tupleDesc;
22252 : TupleConstr *constr;
22253 : AttrMap *attmap;
22254 : AttrNumber parent_attno;
22255 : int ccnum;
22256 324 : List *constraints = NIL;
22257 324 : List *cookedConstraints = NIL;
22258 :
22259 324 : tupleDesc = RelationGetDescr(parent_rel);
22260 324 : constr = tupleDesc->constr;
22261 :
22262 324 : if (!constr)
22263 207 : return;
22264 :
22265 : /*
22266 : * Construct a map from the parent relation's attnos to the child rel's.
22267 : * This re-checks type match, etc, although it shouldn't be possible to
22268 : * have a failure since both tables are locked.
22269 : */
22270 117 : attmap = build_attrmap_by_name(RelationGetDescr(newRel),
22271 : tupleDesc,
22272 : false);
22273 :
22274 : /* Cycle for default values. */
22275 444 : for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
22276 : {
22277 327 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
22278 : parent_attno - 1);
22279 :
22280 : /* Ignore dropped columns in the parent. */
22281 327 : if (attribute->attisdropped)
22282 0 : continue;
22283 :
22284 : /* Copy the default, if present, and it should be copied. */
22285 327 : if (attribute->atthasdef)
22286 : {
22287 75 : Node *this_default = NULL;
22288 : bool found_whole_row;
22289 : AttrNumber num;
22290 : Node *def;
22291 : NewColumnValue *newval;
22292 :
22293 75 : if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
22294 3 : this_default = build_generation_expression(parent_rel, attribute->attnum);
22295 : else
22296 : {
22297 72 : this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
22298 72 : if (this_default == NULL)
22299 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
22300 : attribute->attnum, RelationGetRelationName(parent_rel));
22301 : }
22302 :
22303 75 : num = attmap->attnums[parent_attno - 1];
22304 75 : def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
22305 :
22306 75 : if (found_whole_row && attribute->attgenerated != '\0')
22307 0 : elog(ERROR, "cannot convert whole-row table reference");
22308 :
22309 : /* Add a pre-cooked default expression. */
22310 75 : StoreAttrDefault(newRel, num, def, true);
22311 :
22312 : /*
22313 : * Stored generated column expressions in parent_rel might
22314 : * reference the tableoid. newRel, parent_rel tableoid clear is
22315 : * not the same. If so, these stored generated columns require
22316 : * recomputation for newRel within MergePartitionsMoveRows.
22317 : */
22318 75 : if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
22319 : {
22320 33 : newval = palloc0_object(NewColumnValue);
22321 33 : newval->attnum = num;
22322 33 : newval->expr = expression_planner((Expr *) def);
22323 33 : newval->is_generated = (attribute->attgenerated != '\0');
22324 33 : tab->newvals = lappend(tab->newvals, newval);
22325 : }
22326 : }
22327 : }
22328 :
22329 : /* Cycle for CHECK constraints. */
22330 168 : for (ccnum = 0; ccnum < constr->num_check; ccnum++)
22331 : {
22332 51 : char *ccname = constr->check[ccnum].ccname;
22333 51 : char *ccbin = constr->check[ccnum].ccbin;
22334 51 : bool ccenforced = constr->check[ccnum].ccenforced;
22335 51 : bool ccnoinherit = constr->check[ccnum].ccnoinherit;
22336 51 : bool ccvalid = constr->check[ccnum].ccvalid;
22337 : Node *ccbin_node;
22338 : bool found_whole_row;
22339 : Constraint *constr;
22340 :
22341 : /*
22342 : * The partitioned table can not have a NO INHERIT check constraint
22343 : * (see StoreRelCheck function for details).
22344 : */
22345 : Assert(!ccnoinherit);
22346 :
22347 51 : ccbin_node = map_variable_attnos(stringToNode(ccbin),
22348 : 1, 0,
22349 : attmap,
22350 : InvalidOid, &found_whole_row);
22351 :
22352 : /*
22353 : * For the moment we have to reject whole-row variables (as for CREATE
22354 : * TABLE LIKE and inheritances).
22355 : */
22356 51 : if (found_whole_row)
22357 0 : elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
22358 : ccname,
22359 : RelationGetRelationName(parent_rel));
22360 :
22361 51 : constr = makeNode(Constraint);
22362 51 : constr->contype = CONSTR_CHECK;
22363 51 : constr->conname = pstrdup(ccname);
22364 51 : constr->deferrable = false;
22365 51 : constr->initdeferred = false;
22366 51 : constr->is_enforced = ccenforced;
22367 51 : constr->skip_validation = !ccvalid;
22368 51 : constr->initially_valid = ccvalid;
22369 51 : constr->is_no_inherit = ccnoinherit;
22370 51 : constr->raw_expr = NULL;
22371 51 : constr->cooked_expr = nodeToString(ccbin_node);
22372 51 : constr->location = -1;
22373 51 : constraints = lappend(constraints, constr);
22374 : }
22375 :
22376 : /* Install all CHECK constraints. */
22377 117 : cookedConstraints = AddRelationNewConstraints(newRel, NIL, constraints,
22378 : false, true, true, NULL);
22379 :
22380 : /* Make the additional catalog changes visible. */
22381 117 : CommandCounterIncrement();
22382 :
22383 : /*
22384 : * parent_rel check constraint expression may reference tableoid, so later
22385 : * in MergePartitionsMoveRows, we need to evaluate the check constraint
22386 : * again for the newRel. We can check whether the check constraint
22387 : * contains a tableoid reference via pull_varattnos.
22388 : */
22389 285 : foreach_ptr(CookedConstraint, ccon, cookedConstraints)
22390 : {
22391 51 : if (!ccon->skip_validation)
22392 : {
22393 : Node *qual;
22394 33 : Bitmapset *attnums = NULL;
22395 :
22396 : Assert(ccon->contype == CONSTR_CHECK);
22397 33 : qual = expand_generated_columns_in_expr(ccon->expr, newRel, 1);
22398 33 : pull_varattnos(qual, 1, &attnums);
22399 :
22400 : /*
22401 : * Add a check only if it contains a tableoid
22402 : * (TableOidAttributeNumber).
22403 : */
22404 33 : if (bms_is_member(TableOidAttributeNumber - FirstLowInvalidHeapAttributeNumber,
22405 : attnums))
22406 : {
22407 : NewConstraint *newcon;
22408 :
22409 12 : newcon = palloc0_object(NewConstraint);
22410 12 : newcon->name = ccon->name;
22411 12 : newcon->contype = CONSTR_CHECK;
22412 12 : newcon->qual = qual;
22413 :
22414 12 : tab->constraints = lappend(tab->constraints, newcon);
22415 : }
22416 : }
22417 : }
22418 :
22419 : /* Don't need the cookedConstraints anymore. */
22420 117 : list_free_deep(cookedConstraints);
22421 :
22422 : /* Reproduce not-null constraints. */
22423 117 : if (constr->has_not_null)
22424 : {
22425 : List *nnconstraints;
22426 :
22427 : /*
22428 : * The "include_noinh" argument is false because a partitioned table
22429 : * can't have NO INHERIT constraint.
22430 : */
22431 81 : nnconstraints = RelationGetNotNullConstraints(RelationGetRelid(parent_rel),
22432 : false, false);
22433 :
22434 : Assert(list_length(nnconstraints) > 0);
22435 :
22436 : /*
22437 : * We already set pg_attribute.attnotnull in createPartitionTable. No
22438 : * need call set_attnotnull again.
22439 : */
22440 81 : AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, true, NULL);
22441 : }
22442 : }
22443 :
22444 : /*
22445 : * createPartitionTable:
22446 : *
22447 : * Create a new partition (newPartName) for the partitioned table (parent_rel).
22448 : * ownerId is determined by the partition on which the operation is performed,
22449 : * so it is passed separately. The new partition will inherit the access method
22450 : * and persistence type from the parent table.
22451 : *
22452 : * Returns the created relation (locked in AccessExclusiveLock mode).
22453 : */
22454 : static Relation
22455 339 : createPartitionTable(List **wqueue, RangeVar *newPartName,
22456 : Relation parent_rel, Oid ownerId)
22457 : {
22458 : Relation newRel;
22459 : Oid newRelId;
22460 : Oid existingRelid;
22461 : TupleDesc descriptor;
22462 339 : List *colList = NIL;
22463 : Oid relamId;
22464 : Oid namespaceId;
22465 : AlteredTableInfo *new_partrel_tab;
22466 339 : Form_pg_class parent_relform = parent_rel->rd_rel;
22467 :
22468 : /* If the existing rel is temp, it must belong to this session. */
22469 339 : if (RELATION_IS_OTHER_TEMP(parent_rel))
22470 0 : ereport(ERROR,
22471 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
22472 : errmsg("cannot create as partition of temporary relation of another session"));
22473 :
22474 : /* Look up inheritance ancestors and generate the relation schema. */
22475 339 : colList = getAttributesList(parent_rel);
22476 :
22477 : /* Create a tuple descriptor from the relation schema. */
22478 339 : descriptor = BuildDescForRelation(colList);
22479 :
22480 : /* Look up the access method for the new relation. */
22481 339 : relamId = (parent_relform->relam != InvalidOid) ? parent_relform->relam : HEAP_TABLE_AM_OID;
22482 :
22483 : /* Look up the namespace in which we are supposed to create the relation. */
22484 : namespaceId =
22485 339 : RangeVarGetAndCheckCreationNamespace(newPartName, NoLock, &existingRelid);
22486 339 : if (OidIsValid(existingRelid))
22487 0 : ereport(ERROR,
22488 : errcode(ERRCODE_DUPLICATE_TABLE),
22489 : errmsg("relation \"%s\" already exists", newPartName->relname));
22490 :
22491 : /*
22492 : * We intended to create the partition with the same persistence as the
22493 : * parent table, but we still need to recheck because that might be
22494 : * affected by the search_path. If the parent is permanent, so must be
22495 : * all of its partitions.
22496 : */
22497 339 : if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22498 312 : newPartName->relpersistence == RELPERSISTENCE_TEMP)
22499 6 : ereport(ERROR,
22500 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
22501 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22502 : RelationGetRelationName(parent_rel)));
22503 :
22504 : /* Permanent rels cannot be partitions belonging to a temporary parent. */
22505 333 : if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22506 315 : parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22507 9 : ereport(ERROR,
22508 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
22509 : errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
22510 : RelationGetRelationName(parent_rel)));
22511 :
22512 : /* Create the relation. */
22513 324 : newRelId = heap_create_with_catalog(newPartName->relname,
22514 : namespaceId,
22515 : parent_relform->reltablespace,
22516 : InvalidOid,
22517 : InvalidOid,
22518 : InvalidOid,
22519 : ownerId,
22520 : relamId,
22521 : descriptor,
22522 : NIL,
22523 : RELKIND_RELATION,
22524 324 : newPartName->relpersistence,
22525 : false,
22526 : false,
22527 : ONCOMMIT_NOOP,
22528 : (Datum) 0,
22529 : true,
22530 : allowSystemTableMods,
22531 : true,
22532 : InvalidOid,
22533 : NULL);
22534 :
22535 : /*
22536 : * We must bump the command counter to make the newly-created relation
22537 : * tuple visible for opening.
22538 : */
22539 324 : CommandCounterIncrement();
22540 :
22541 : /*
22542 : * Open the new partition with no lock, because we already have an
22543 : * AccessExclusiveLock placed there after creation.
22544 : */
22545 324 : newRel = table_open(newRelId, NoLock);
22546 :
22547 : /* Find or create a work queue entry for the newly created table. */
22548 324 : new_partrel_tab = ATGetQueueEntry(wqueue, newRel);
22549 :
22550 : /* Create constraints, default values, and generated values. */
22551 324 : createTableConstraints(wqueue, new_partrel_tab, parent_rel, newRel);
22552 :
22553 : /*
22554 : * Need to call CommandCounterIncrement, so a fresh relcache entry has
22555 : * newly installed constraint info.
22556 : */
22557 324 : CommandCounterIncrement();
22558 :
22559 324 : return newRel;
22560 : }
22561 :
22562 : /*
22563 : * MergePartitionsMoveRows: scan partitions to be merged (mergingPartitions)
22564 : * of the partitioned table and move rows into the new partition
22565 : * (newPartRel). We also verify check constraints against these rows.
22566 : */
22567 : static void
22568 69 : MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
22569 : {
22570 : CommandId mycid;
22571 : EState *estate;
22572 : AlteredTableInfo *tab;
22573 : ListCell *ltab;
22574 :
22575 : /* The FSM is empty, so don't bother using it. */
22576 69 : int ti_options = TABLE_INSERT_SKIP_FSM;
22577 : BulkInsertState bistate; /* state of bulk inserts for partition */
22578 : TupleTableSlot *dstslot;
22579 :
22580 : /* Find the work queue entry for the new partition table: newPartRel. */
22581 69 : tab = ATGetQueueEntry(wqueue, newPartRel);
22582 :
22583 : /* Generate the constraint and default execution states. */
22584 69 : estate = CreateExecutorState();
22585 :
22586 69 : buildExpressionExecutionStates(tab, newPartRel, estate);
22587 :
22588 69 : mycid = GetCurrentCommandId(true);
22589 :
22590 : /* Prepare a BulkInsertState for table_tuple_insert. */
22591 69 : bistate = GetBulkInsertState();
22592 :
22593 : /* Create the necessary tuple slot. */
22594 69 : dstslot = table_slot_create(newPartRel, NULL);
22595 :
22596 297 : foreach_oid(merging_oid, mergingPartitions)
22597 : {
22598 : ExprContext *econtext;
22599 : TupleTableSlot *srcslot;
22600 : TupleConversionMap *tuple_map;
22601 : TableScanDesc scan;
22602 : MemoryContext oldCxt;
22603 : Snapshot snapshot;
22604 : Relation mergingPartition;
22605 :
22606 159 : econtext = GetPerTupleExprContext(estate);
22607 :
22608 : /*
22609 : * Partition is already locked in the transformPartitionCmdForMerge
22610 : * function.
22611 : */
22612 159 : mergingPartition = table_open(merging_oid, NoLock);
22613 :
22614 : /* Create a source tuple slot for the partition being merged. */
22615 159 : srcslot = table_slot_create(mergingPartition, NULL);
22616 :
22617 : /*
22618 : * Map computing for moving attributes of the merged partition to the
22619 : * new partition.
22620 : */
22621 159 : tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
22622 : RelationGetDescr(newPartRel));
22623 :
22624 : /* Scan through the rows. */
22625 159 : snapshot = RegisterSnapshot(GetLatestSnapshot());
22626 159 : scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
22627 :
22628 : /*
22629 : * Switch to per-tuple memory context and reset it for each tuple
22630 : * produced, so we don't leak memory.
22631 : */
22632 159 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
22633 :
22634 348 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
22635 : {
22636 : TupleTableSlot *insertslot;
22637 :
22638 189 : CHECK_FOR_INTERRUPTS();
22639 :
22640 189 : if (tuple_map)
22641 : {
22642 : /* Need to use a map to copy attributes. */
22643 21 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
22644 : }
22645 : else
22646 : {
22647 168 : slot_getallattrs(srcslot);
22648 :
22649 : /* Copy attributes directly. */
22650 168 : insertslot = dstslot;
22651 :
22652 168 : ExecClearTuple(insertslot);
22653 :
22654 168 : memcpy(insertslot->tts_values, srcslot->tts_values,
22655 168 : sizeof(Datum) * srcslot->tts_nvalid);
22656 168 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
22657 168 : sizeof(bool) * srcslot->tts_nvalid);
22658 :
22659 168 : ExecStoreVirtualTuple(insertslot);
22660 : }
22661 :
22662 : /*
22663 : * Constraints and GENERATED expressions might reference the
22664 : * tableoid column, so fill tts_tableOid with the desired value.
22665 : * (We must do this each time, because it gets overwritten with
22666 : * newrel's OID during storing.)
22667 : */
22668 189 : insertslot->tts_tableOid = RelationGetRelid(newPartRel);
22669 :
22670 : /*
22671 : * Now, evaluate any generated expressions whose inputs come from
22672 : * the new tuple. We assume these columns won't reference each
22673 : * other, so that there's no ordering dependency.
22674 : */
22675 189 : evaluateGeneratedExpressionsAndCheckConstraints(tab, newPartRel,
22676 : insertslot, econtext);
22677 :
22678 : /* Write the tuple out to the new relation. */
22679 189 : table_tuple_insert(newPartRel, insertslot, mycid,
22680 : ti_options, bistate);
22681 :
22682 189 : ResetExprContext(econtext);
22683 : }
22684 :
22685 159 : MemoryContextSwitchTo(oldCxt);
22686 159 : table_endscan(scan);
22687 159 : UnregisterSnapshot(snapshot);
22688 :
22689 159 : if (tuple_map)
22690 15 : free_conversion_map(tuple_map);
22691 :
22692 159 : ExecDropSingleTupleTableSlot(srcslot);
22693 159 : table_close(mergingPartition, NoLock);
22694 : }
22695 :
22696 69 : FreeExecutorState(estate);
22697 69 : ExecDropSingleTupleTableSlot(dstslot);
22698 69 : FreeBulkInsertState(bistate);
22699 :
22700 69 : table_finish_bulk_insert(newPartRel, ti_options);
22701 :
22702 : /*
22703 : * We don't need to process this newPartRel since we already processed it
22704 : * here, so delete the ALTER TABLE queue for it.
22705 : */
22706 138 : foreach(ltab, *wqueue)
22707 : {
22708 138 : tab = (AlteredTableInfo *) lfirst(ltab);
22709 138 : if (tab->relid == RelationGetRelid(newPartRel))
22710 : {
22711 69 : *wqueue = list_delete_cell(*wqueue, ltab);
22712 69 : break;
22713 : }
22714 : }
22715 69 : }
22716 :
22717 : /*
22718 : * detachPartitionTable: detach partition "child_rel" from partitioned table
22719 : * "parent_rel" with default partition identifier "defaultPartOid"
22720 : */
22721 : static void
22722 285 : detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
22723 : {
22724 : /* Remove the pg_inherits row first. */
22725 285 : RemoveInheritance(child_rel, parent_rel, false);
22726 :
22727 : /*
22728 : * Detaching the partition might involve TOAST table access, so ensure we
22729 : * have a valid snapshot.
22730 : */
22731 285 : PushActiveSnapshot(GetTransactionSnapshot());
22732 :
22733 : /* Do the final part of detaching. */
22734 285 : DetachPartitionFinalize(parent_rel, child_rel, false, defaultPartOid);
22735 :
22736 285 : PopActiveSnapshot();
22737 285 : }
22738 :
22739 : /*
22740 : * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
22741 : */
22742 : static void
22743 90 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
22744 : PartitionCmd *cmd, AlterTableUtilityContext *context)
22745 : {
22746 : Relation newPartRel;
22747 90 : List *mergingPartitions = NIL;
22748 : Oid defaultPartOid;
22749 : Oid existingRelid;
22750 90 : Oid ownerId = InvalidOid;
22751 : Oid save_userid;
22752 : int save_sec_context;
22753 : int save_nestlevel;
22754 :
22755 : /*
22756 : * Check ownership of merged partitions - partitions with different owners
22757 : * cannot be merged. Also, collect the OIDs of these partitions during the
22758 : * check.
22759 : */
22760 378 : foreach_node(RangeVar, name, cmd->partlist)
22761 : {
22762 : Relation mergingPartition;
22763 :
22764 : /*
22765 : * We are going to detach and remove this partition. We already took
22766 : * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
22767 : * NoLock is fine.
22768 : */
22769 204 : mergingPartition = table_openrv_extended(name, NoLock, false);
22770 : Assert(CheckRelationLockedByMe(mergingPartition, AccessExclusiveLock, false));
22771 :
22772 204 : if (OidIsValid(ownerId))
22773 : {
22774 : /* Do the partitions being merged have different owners? */
22775 114 : if (ownerId != mergingPartition->rd_rel->relowner)
22776 3 : ereport(ERROR,
22777 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22778 : errmsg("partitions being merged have different owners"));
22779 : }
22780 : else
22781 90 : ownerId = mergingPartition->rd_rel->relowner;
22782 :
22783 : /* Store the next merging partition into the list. */
22784 201 : mergingPartitions = lappend_oid(mergingPartitions,
22785 : RelationGetRelid(mergingPartition));
22786 :
22787 201 : table_close(mergingPartition, NoLock);
22788 : }
22789 :
22790 : /* Look up the existing relation by the new partition name. */
22791 87 : RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, &existingRelid);
22792 :
22793 : /*
22794 : * Check if this name is already taken. This helps us to detect the
22795 : * situation when one of the merging partitions has the same name as the
22796 : * new partition. Otherwise, this would fail later on anyway, but
22797 : * catching this here allows us to emit a nicer error message.
22798 : */
22799 87 : if (OidIsValid(existingRelid))
22800 : {
22801 13 : if (list_member_oid(mergingPartitions, existingRelid))
22802 : {
22803 : /*
22804 : * The new partition has the same name as one of the merging
22805 : * partitions.
22806 : */
22807 : char tmpRelName[NAMEDATALEN];
22808 :
22809 : /* Generate a temporary name. */
22810 10 : sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
22811 :
22812 : /*
22813 : * Rename the existing partition with a temporary name, leaving it
22814 : * free for the new partition. We don't need to care about this
22815 : * in the future because we're going to eventually drop the
22816 : * existing partition anyway.
22817 : */
22818 10 : RenameRelationInternal(existingRelid, tmpRelName, true, false);
22819 :
22820 : /*
22821 : * We must bump the command counter to make the new partition
22822 : * tuple visible for rename.
22823 : */
22824 10 : CommandCounterIncrement();
22825 : }
22826 : else
22827 : {
22828 3 : ereport(ERROR,
22829 : errcode(ERRCODE_DUPLICATE_TABLE),
22830 : errmsg("relation \"%s\" already exists", cmd->name->relname));
22831 : }
22832 : }
22833 :
22834 : defaultPartOid =
22835 84 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
22836 :
22837 : /* Detach all merging partitions. */
22838 357 : foreach_oid(mergingPartitionOid, mergingPartitions)
22839 : {
22840 : Relation child_rel;
22841 :
22842 189 : child_rel = table_open(mergingPartitionOid, NoLock);
22843 :
22844 189 : detachPartitionTable(rel, child_rel, defaultPartOid);
22845 :
22846 189 : table_close(child_rel, NoLock);
22847 : }
22848 :
22849 : /*
22850 : * Perform a preliminary check to determine whether it's safe to drop all
22851 : * merging partitions before we actually do so later. After merging rows
22852 : * into the new partitions via MergePartitionsMoveRows, all old partitions
22853 : * need to be dropped. However, since the drop behavior is DROP_RESTRICT
22854 : * and the merge process (MergePartitionsMoveRows) can be time-consuming,
22855 : * performing an early check on the drop eligibility of old partitions is
22856 : * preferable.
22857 : */
22858 348 : foreach_oid(mergingPartitionOid, mergingPartitions)
22859 : {
22860 : ObjectAddress object;
22861 :
22862 : /* Get oid of the later to be dropped relation. */
22863 186 : object.objectId = mergingPartitionOid;
22864 186 : object.classId = RelationRelationId;
22865 186 : object.objectSubId = 0;
22866 :
22867 186 : performDeletionCheck(&object, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
22868 : }
22869 :
22870 : /*
22871 : * Create a table for the new partition, using the partitioned table as a
22872 : * model.
22873 : */
22874 : Assert(OidIsValid(ownerId));
22875 81 : newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
22876 :
22877 : /*
22878 : * Switch to the table owner's userid, so that any index functions are run
22879 : * as that user. Also, lockdown security-restricted operations and
22880 : * arrange to make GUC variable changes local to this command.
22881 : *
22882 : * Need to do it after determining the namespace in the
22883 : * createPartitionTable() call.
22884 : */
22885 69 : GetUserIdAndSecContext(&save_userid, &save_sec_context);
22886 69 : SetUserIdAndSecContext(ownerId,
22887 : save_sec_context | SECURITY_RESTRICTED_OPERATION);
22888 69 : save_nestlevel = NewGUCNestLevel();
22889 69 : RestrictSearchPath();
22890 :
22891 : /* Copy data from merged partitions to the new partition. */
22892 69 : MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel);
22893 :
22894 : /* Drop the current partitions before attaching the new one. */
22895 297 : foreach_oid(mergingPartitionOid, mergingPartitions)
22896 : {
22897 : ObjectAddress object;
22898 :
22899 159 : object.objectId = mergingPartitionOid;
22900 159 : object.classId = RelationRelationId;
22901 159 : object.objectSubId = 0;
22902 :
22903 159 : performDeletion(&object, DROP_RESTRICT, 0);
22904 : }
22905 :
22906 69 : list_free(mergingPartitions);
22907 :
22908 : /*
22909 : * Attach a new partition to the partitioned table. wqueue = NULL:
22910 : * verification for each cloned constraint is not needed.
22911 : */
22912 69 : attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
22913 :
22914 : /* Keep the lock until commit. */
22915 69 : table_close(newPartRel, NoLock);
22916 :
22917 : /* Roll back any GUC changes executed by index functions. */
22918 69 : AtEOXact_GUC(false, save_nestlevel);
22919 :
22920 : /* Restore the userid and security context. */
22921 69 : SetUserIdAndSecContext(save_userid, save_sec_context);
22922 69 : }
22923 :
22924 : /*
22925 : * Struct with the context of the new partition for inserting rows from the
22926 : * split partition.
22927 : */
22928 : typedef struct SplitPartitionContext
22929 : {
22930 : ExprState *partqualstate; /* expression for checking a slot for a
22931 : * partition (NULL for DEFAULT partition) */
22932 : BulkInsertState bistate; /* state of bulk inserts for partition */
22933 : TupleTableSlot *dstslot; /* slot for inserting row into partition */
22934 : AlteredTableInfo *tab; /* structure with generated column expressions
22935 : * and check constraint expressions. */
22936 : Relation partRel; /* relation for partition */
22937 : } SplitPartitionContext;
22938 :
22939 : /*
22940 : * createSplitPartitionContext: create context for partition and fill it
22941 : */
22942 : static SplitPartitionContext *
22943 255 : createSplitPartitionContext(Relation partRel)
22944 : {
22945 : SplitPartitionContext *pc;
22946 :
22947 255 : pc = palloc0_object(SplitPartitionContext);
22948 255 : pc->partRel = partRel;
22949 :
22950 : /*
22951 : * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
22952 : * don't bother using it.
22953 : */
22954 255 : pc->bistate = GetBulkInsertState();
22955 :
22956 : /* Create a destination tuple slot for the new partition. */
22957 255 : pc->dstslot = table_slot_create(pc->partRel, NULL);
22958 :
22959 255 : return pc;
22960 : }
22961 :
22962 : /*
22963 : * deleteSplitPartitionContext: delete context for partition
22964 : */
22965 : static void
22966 255 : deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, int ti_options)
22967 : {
22968 : ListCell *ltab;
22969 :
22970 255 : ExecDropSingleTupleTableSlot(pc->dstslot);
22971 255 : FreeBulkInsertState(pc->bistate);
22972 :
22973 255 : table_finish_bulk_insert(pc->partRel, ti_options);
22974 :
22975 : /*
22976 : * We don't need to process this pc->partRel so delete the ALTER TABLE
22977 : * queue of it.
22978 : */
22979 510 : foreach(ltab, *wqueue)
22980 : {
22981 510 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
22982 :
22983 510 : if (tab->relid == RelationGetRelid(pc->partRel))
22984 : {
22985 255 : *wqueue = list_delete_cell(*wqueue, ltab);
22986 255 : break;
22987 : }
22988 : }
22989 :
22990 255 : pfree(pc);
22991 255 : }
22992 :
22993 : /*
22994 : * SplitPartitionMoveRows: scan split partition (splitRel) of partitioned table
22995 : * (rel) and move rows into new partitions.
22996 : *
22997 : * New partitions description:
22998 : * partlist: list of pointers to SinglePartitionSpec structures. It contains
22999 : * the partition specification details for all new partitions.
23000 : * newPartRels: list of Relations, new partitions created in
23001 : * ATExecSplitPartition.
23002 : */
23003 : static void
23004 93 : SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel,
23005 : List *partlist, List *newPartRels)
23006 : {
23007 : /* The FSM is empty, so don't bother using it. */
23008 93 : int ti_options = TABLE_INSERT_SKIP_FSM;
23009 : CommandId mycid;
23010 : EState *estate;
23011 : ListCell *listptr,
23012 : *listptr2;
23013 : TupleTableSlot *srcslot;
23014 : ExprContext *econtext;
23015 : TableScanDesc scan;
23016 : Snapshot snapshot;
23017 : MemoryContext oldCxt;
23018 93 : List *partContexts = NIL;
23019 : TupleConversionMap *tuple_map;
23020 93 : SplitPartitionContext *defaultPartCtx = NULL,
23021 : *pc;
23022 :
23023 93 : mycid = GetCurrentCommandId(true);
23024 :
23025 93 : estate = CreateExecutorState();
23026 :
23027 348 : forboth(listptr, partlist, listptr2, newPartRels)
23028 : {
23029 255 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
23030 :
23031 255 : pc = createSplitPartitionContext((Relation) lfirst(listptr2));
23032 :
23033 : /* Find the work queue entry for the new partition table: newPartRel. */
23034 255 : pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
23035 :
23036 255 : buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
23037 :
23038 255 : if (sps->bound->is_default)
23039 : {
23040 : /*
23041 : * We should not create a structure to check the partition
23042 : * constraint for the new DEFAULT partition.
23043 : */
23044 21 : defaultPartCtx = pc;
23045 : }
23046 : else
23047 : {
23048 : List *partConstraint;
23049 :
23050 : /* Build expression execution states for partition check quals. */
23051 234 : partConstraint = get_qual_from_partbound(rel, sps->bound);
23052 : partConstraint =
23053 234 : (List *) eval_const_expressions(NULL,
23054 : (Node *) partConstraint);
23055 : /* Make a boolean expression for ExecCheck(). */
23056 234 : partConstraint = list_make1(make_ands_explicit(partConstraint));
23057 :
23058 : /*
23059 : * Map the vars in the constraint expression from rel's attnos to
23060 : * splitRel's.
23061 : */
23062 234 : partConstraint = map_partition_varattnos(partConstraint,
23063 : 1, splitRel, rel);
23064 :
23065 234 : pc->partqualstate =
23066 234 : ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
23067 : Assert(pc->partqualstate != NULL);
23068 : }
23069 :
23070 : /* Store partition context into a list. */
23071 255 : partContexts = lappend(partContexts, pc);
23072 : }
23073 :
23074 93 : econtext = GetPerTupleExprContext(estate);
23075 :
23076 : /* Create the necessary tuple slot. */
23077 93 : srcslot = table_slot_create(splitRel, NULL);
23078 :
23079 : /*
23080 : * Map computing for moving attributes of the split partition to the new
23081 : * partition (for the first new partition, but other new partitions can
23082 : * use the same map).
23083 : */
23084 93 : pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
23085 93 : tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
23086 93 : RelationGetDescr(pc->partRel));
23087 :
23088 : /* Scan through the rows. */
23089 93 : snapshot = RegisterSnapshot(GetLatestSnapshot());
23090 93 : scan = table_beginscan(splitRel, snapshot, 0, NULL);
23091 :
23092 : /*
23093 : * Switch to per-tuple memory context and reset it for each tuple
23094 : * produced, so we don't leak memory.
23095 : */
23096 93 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
23097 :
23098 413 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
23099 : {
23100 320 : bool found = false;
23101 : TupleTableSlot *insertslot;
23102 :
23103 320 : CHECK_FOR_INTERRUPTS();
23104 :
23105 320 : econtext->ecxt_scantuple = srcslot;
23106 :
23107 : /* Search partition for the current slot, srcslot. */
23108 859 : foreach(listptr, partContexts)
23109 : {
23110 802 : pc = (SplitPartitionContext *) lfirst(listptr);
23111 :
23112 : /* skip DEFAULT partition */
23113 802 : if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
23114 : {
23115 263 : found = true;
23116 263 : break;
23117 : }
23118 : }
23119 320 : if (!found)
23120 : {
23121 : /* Use the DEFAULT partition if it exists. */
23122 57 : if (defaultPartCtx)
23123 57 : pc = defaultPartCtx;
23124 : else
23125 0 : ereport(ERROR,
23126 : errcode(ERRCODE_CHECK_VIOLATION),
23127 : errmsg("can not find partition for split partition row"),
23128 : errtable(splitRel));
23129 : }
23130 :
23131 320 : if (tuple_map)
23132 : {
23133 : /* Need to use a map to copy attributes. */
23134 12 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
23135 : }
23136 : else
23137 : {
23138 : /* Extract data from the old tuple. */
23139 308 : slot_getallattrs(srcslot);
23140 :
23141 : /* Copy attributes directly. */
23142 308 : insertslot = pc->dstslot;
23143 :
23144 308 : ExecClearTuple(insertslot);
23145 :
23146 308 : memcpy(insertslot->tts_values, srcslot->tts_values,
23147 308 : sizeof(Datum) * srcslot->tts_nvalid);
23148 308 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23149 308 : sizeof(bool) * srcslot->tts_nvalid);
23150 :
23151 308 : ExecStoreVirtualTuple(insertslot);
23152 : }
23153 :
23154 : /*
23155 : * Constraints and GENERATED expressions might reference the tableoid
23156 : * column, so fill tts_tableOid with the desired value. (We must do
23157 : * this each time, because it gets overwritten with newrel's OID
23158 : * during storing.)
23159 : */
23160 320 : insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
23161 :
23162 : /*
23163 : * Now, evaluate any generated expressions whose inputs come from the
23164 : * new tuple. We assume these columns won't reference each other, so
23165 : * that there's no ordering dependency.
23166 : */
23167 320 : evaluateGeneratedExpressionsAndCheckConstraints(pc->tab, pc->partRel,
23168 : insertslot, econtext);
23169 :
23170 : /* Write the tuple out to the new relation. */
23171 320 : table_tuple_insert(pc->partRel, insertslot, mycid,
23172 320 : ti_options, pc->bistate);
23173 :
23174 320 : ResetExprContext(econtext);
23175 : }
23176 :
23177 93 : MemoryContextSwitchTo(oldCxt);
23178 :
23179 93 : table_endscan(scan);
23180 93 : UnregisterSnapshot(snapshot);
23181 :
23182 93 : if (tuple_map)
23183 3 : free_conversion_map(tuple_map);
23184 :
23185 93 : ExecDropSingleTupleTableSlot(srcslot);
23186 :
23187 93 : FreeExecutorState(estate);
23188 :
23189 441 : foreach_ptr(SplitPartitionContext, spc, partContexts)
23190 255 : deleteSplitPartitionContext(spc, wqueue, ti_options);
23191 93 : }
23192 :
23193 : /*
23194 : * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
23195 : */
23196 : static void
23197 99 : ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
23198 : PartitionCmd *cmd, AlterTableUtilityContext *context)
23199 : {
23200 : Relation splitRel;
23201 : Oid splitRelOid;
23202 : ListCell *listptr,
23203 : *listptr2;
23204 99 : bool isSameName = false;
23205 : char tmpRelName[NAMEDATALEN];
23206 99 : List *newPartRels = NIL;
23207 : ObjectAddress object;
23208 : Oid defaultPartOid;
23209 : Oid save_userid;
23210 : int save_sec_context;
23211 : int save_nestlevel;
23212 :
23213 99 : defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
23214 :
23215 : /*
23216 : * Partition is already locked in the transformPartitionCmdForSplit
23217 : * function.
23218 : */
23219 99 : splitRel = table_openrv(cmd->name, NoLock);
23220 :
23221 99 : splitRelOid = RelationGetRelid(splitRel);
23222 :
23223 : /* Check descriptions of new partitions. */
23224 456 : foreach_node(SinglePartitionSpec, sps, cmd->partlist)
23225 : {
23226 : Oid existingRelid;
23227 :
23228 : /* Look up the existing relation by the new partition name. */
23229 264 : RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, &existingRelid);
23230 :
23231 : /*
23232 : * This would fail later on anyway if the relation already exists. But
23233 : * by catching it here, we can emit a nicer error message.
23234 : */
23235 264 : if (existingRelid == splitRelOid && !isSameName)
23236 : /* One new partition can have the same name as a split partition. */
23237 22 : isSameName = true;
23238 242 : else if (OidIsValid(existingRelid))
23239 3 : ereport(ERROR,
23240 : errcode(ERRCODE_DUPLICATE_TABLE),
23241 : errmsg("relation \"%s\" already exists", sps->name->relname));
23242 : }
23243 :
23244 : /* Detach the split partition. */
23245 96 : detachPartitionTable(rel, splitRel, defaultPartOid);
23246 :
23247 : /*
23248 : * Perform a preliminary check to determine whether it's safe to drop the
23249 : * split partition before we actually do so later. After merging rows into
23250 : * the new partitions via SplitPartitionMoveRows, all old partitions need
23251 : * to be dropped. However, since the drop behavior is DROP_RESTRICT and
23252 : * the merge process (SplitPartitionMoveRows) can be time-consuming,
23253 : * performing an early check on the drop eligibility of old partitions is
23254 : * preferable.
23255 : */
23256 96 : object.objectId = splitRelOid;
23257 96 : object.classId = RelationRelationId;
23258 96 : object.objectSubId = 0;
23259 96 : performDeletionCheck(&object, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
23260 :
23261 : /*
23262 : * If a new partition has the same name as the split partition, then we
23263 : * should rename the split partition to reuse its name.
23264 : */
23265 96 : if (isSameName)
23266 : {
23267 : /*
23268 : * We must bump the command counter to make the split partition tuple
23269 : * visible for renaming.
23270 : */
23271 22 : CommandCounterIncrement();
23272 : /* Rename partition. */
23273 22 : sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23274 22 : RenameRelationInternal(splitRelOid, tmpRelName, true, false);
23275 :
23276 : /*
23277 : * We must bump the command counter to make the split partition tuple
23278 : * visible after renaming.
23279 : */
23280 22 : CommandCounterIncrement();
23281 : }
23282 :
23283 : /* Create new partitions (like a split partition), without indexes. */
23284 444 : foreach_node(SinglePartitionSpec, sps, cmd->partlist)
23285 : {
23286 : Relation newPartRel;
23287 :
23288 258 : newPartRel = createPartitionTable(wqueue, sps->name, rel,
23289 258 : splitRel->rd_rel->relowner);
23290 255 : newPartRels = lappend(newPartRels, newPartRel);
23291 : }
23292 :
23293 : /*
23294 : * Switch to the table owner's userid, so that any index functions are run
23295 : * as that user. Also, lockdown security-restricted operations and
23296 : * arrange to make GUC variable changes local to this command.
23297 : *
23298 : * Need to do it after determining the namespace in the
23299 : * createPartitionTable() call.
23300 : */
23301 93 : GetUserIdAndSecContext(&save_userid, &save_sec_context);
23302 93 : SetUserIdAndSecContext(splitRel->rd_rel->relowner,
23303 : save_sec_context | SECURITY_RESTRICTED_OPERATION);
23304 93 : save_nestlevel = NewGUCNestLevel();
23305 93 : RestrictSearchPath();
23306 :
23307 : /* Copy data from the split partition to the new partitions. */
23308 93 : SplitPartitionMoveRows(wqueue, rel, splitRel, cmd->partlist, newPartRels);
23309 : /* Keep the lock until commit. */
23310 93 : table_close(splitRel, NoLock);
23311 :
23312 : /* Attach new partitions to the partitioned table. */
23313 348 : forboth(listptr, cmd->partlist, listptr2, newPartRels)
23314 : {
23315 255 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
23316 255 : Relation newPartRel = (Relation) lfirst(listptr2);
23317 :
23318 : /*
23319 : * wqueue = NULL: verification for each cloned constraint is not
23320 : * needed.
23321 : */
23322 255 : attachPartitionTable(NULL, rel, newPartRel, sps->bound);
23323 : /* Keep the lock until commit. */
23324 255 : table_close(newPartRel, NoLock);
23325 : }
23326 :
23327 : /* Drop the split partition. */
23328 93 : object.classId = RelationRelationId;
23329 93 : object.objectId = splitRelOid;
23330 93 : object.objectSubId = 0;
23331 : /* Probably DROP_CASCADE is not needed. */
23332 93 : performDeletion(&object, DROP_RESTRICT, 0);
23333 :
23334 : /* Roll back any GUC changes executed by index functions. */
23335 93 : AtEOXact_GUC(false, save_nestlevel);
23336 :
23337 : /* Restore the userid and security context. */
23338 93 : SetUserIdAndSecContext(save_userid, save_sec_context);
23339 93 : }
|