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 65428 : 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 : Datum reloptions;
785 : ListCell *listptr;
786 : AttrNumber attnum;
787 : bool partitioned;
788 65428 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
789 : Oid ofTypeId;
790 : ObjectAddress address;
791 : LOCKMODE parentLockmode;
792 65428 : Oid accessMethodId = InvalidOid;
793 :
794 : /*
795 : * Truncate relname to appropriate length (probably a waste of time, as
796 : * parser should have done this already).
797 : */
798 65428 : strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
799 :
800 : /*
801 : * Check consistency of arguments
802 : */
803 65428 : if (stmt->oncommit != ONCOMMIT_NOOP
804 194 : && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
805 12 : ereport(ERROR,
806 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
807 : errmsg("ON COMMIT can only be used on temporary tables")));
808 :
809 65416 : if (stmt->partspec != NULL)
810 : {
811 5574 : if (relkind != RELKIND_RELATION)
812 0 : elog(ERROR, "unexpected relkind: %d", (int) relkind);
813 :
814 5574 : relkind = RELKIND_PARTITIONED_TABLE;
815 5574 : partitioned = true;
816 : }
817 : else
818 59842 : partitioned = false;
819 :
820 65416 : if (relkind == RELKIND_PARTITIONED_TABLE &&
821 5574 : stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
822 6 : ereport(ERROR,
823 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
824 : errmsg("partitioned tables cannot be unlogged")));
825 :
826 : /*
827 : * Look up the namespace in which we are supposed to create the relation,
828 : * check we have permission to create there, lock it against concurrent
829 : * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
830 : * namespace is selected.
831 : */
832 : namespaceId =
833 65410 : RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
834 :
835 : /*
836 : * Security check: disallow creating temp tables from security-restricted
837 : * code. This is needed because calling code might not expect untrusted
838 : * tables to appear in pg_temp at the front of its search path.
839 : */
840 65410 : if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
841 3370 : && InSecurityRestrictedOperation())
842 0 : ereport(ERROR,
843 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
844 : errmsg("cannot create temporary table within security-restricted operation")));
845 :
846 : /*
847 : * Determine the lockmode to use when scanning parents. A self-exclusive
848 : * lock is needed here.
849 : *
850 : * For regular inheritance, if two backends attempt to add children to the
851 : * same parent simultaneously, and that parent has no pre-existing
852 : * children, then both will attempt to update the parent's relhassubclass
853 : * field, leading to a "tuple concurrently updated" error. Also, this
854 : * interlocks against a concurrent ANALYZE on the parent table, which
855 : * might otherwise be attempting to clear the parent's relhassubclass
856 : * field, if its previous children were recently dropped.
857 : *
858 : * If the child table is a partition, then we instead grab an exclusive
859 : * lock on the parent because its partition descriptor will be changed by
860 : * addition of the new partition.
861 : */
862 65410 : parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
863 : ShareUpdateExclusiveLock);
864 :
865 : /* Determine the list of OIDs of the parents. */
866 65410 : inheritOids = NIL;
867 77068 : foreach(listptr, stmt->inhRelations)
868 : {
869 11658 : RangeVar *rv = (RangeVar *) lfirst(listptr);
870 : Oid parentOid;
871 :
872 11658 : parentOid = RangeVarGetRelid(rv, parentLockmode, false);
873 :
874 : /*
875 : * Reject duplications in the list of parents.
876 : */
877 11658 : if (list_member_oid(inheritOids, parentOid))
878 0 : ereport(ERROR,
879 : (errcode(ERRCODE_DUPLICATE_TABLE),
880 : errmsg("relation \"%s\" would be inherited from more than once",
881 : get_rel_name(parentOid))));
882 :
883 11658 : inheritOids = lappend_oid(inheritOids, parentOid);
884 : }
885 :
886 : /*
887 : * Select tablespace to use: an explicitly indicated one, or (in the case
888 : * of a partitioned table) the parent's, if it has one.
889 : */
890 65410 : if (stmt->tablespacename)
891 : {
892 140 : tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
893 :
894 134 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
895 6 : ereport(ERROR,
896 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
897 : errmsg("cannot specify default tablespace for partitioned relations")));
898 : }
899 65270 : else if (stmt->partbound)
900 : {
901 : Assert(list_length(inheritOids) == 1);
902 9132 : tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
903 : }
904 : else
905 56138 : tablespaceId = InvalidOid;
906 :
907 : /* still nothing? use the default */
908 65398 : if (!OidIsValid(tablespaceId))
909 65230 : tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
910 : partitioned);
911 :
912 : /* Check permissions except when using database's default */
913 65392 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
914 : {
915 : AclResult aclresult;
916 :
917 194 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
918 : ACL_CREATE);
919 194 : if (aclresult != ACLCHECK_OK)
920 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
921 6 : get_tablespace_name(tablespaceId));
922 : }
923 :
924 : /* In all cases disallow placing user relations in pg_global */
925 65386 : if (tablespaceId == GLOBALTABLESPACE_OID)
926 18 : ereport(ERROR,
927 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
928 : errmsg("only shared relations can be placed in pg_global tablespace")));
929 :
930 : /* Identify user ID that will own the table */
931 65368 : if (!OidIsValid(ownerId))
932 65122 : ownerId = GetUserId();
933 :
934 : /*
935 : * Parse and validate reloptions, if any.
936 : */
937 65368 : reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
938 : true, false);
939 :
940 65350 : switch (relkind)
941 : {
942 17102 : case RELKIND_VIEW:
943 17102 : (void) view_reloptions(reloptions, true);
944 17084 : break;
945 5550 : case RELKIND_PARTITIONED_TABLE:
946 5550 : (void) partitioned_table_reloptions(reloptions, true);
947 5544 : break;
948 42698 : default:
949 42698 : (void) heap_reloptions(relkind, reloptions, true);
950 : }
951 :
952 65230 : if (stmt->ofTypename)
953 : {
954 : AclResult aclresult;
955 :
956 86 : ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
957 :
958 86 : aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
959 86 : if (aclresult != ACLCHECK_OK)
960 6 : aclcheck_error_type(aclresult, ofTypeId);
961 : }
962 : else
963 65144 : ofTypeId = InvalidOid;
964 :
965 : /*
966 : * Look up inheritance ancestors and generate relation schema, including
967 : * inherited attributes. (Note that stmt->tableElts is destructively
968 : * modified by MergeAttributes.)
969 : */
970 64984 : stmt->tableElts =
971 65224 : MergeAttributes(stmt->tableElts, inheritOids,
972 65224 : stmt->relation->relpersistence,
973 65224 : stmt->partbound != NULL,
974 : &old_constraints, &old_notnulls);
975 :
976 : /*
977 : * Create a tuple descriptor from the relation schema. Note that this
978 : * deals with column names, types, and in-descriptor NOT NULL flags, but
979 : * not default values, NOT NULL or CHECK constraints; we handle those
980 : * below.
981 : */
982 64984 : descriptor = BuildDescForRelation(stmt->tableElts);
983 :
984 : /*
985 : * Find columns with default values and prepare for insertion of the
986 : * defaults. Pre-cooked (that is, inherited) defaults go into a list of
987 : * CookedConstraint structs that we'll pass to heap_create_with_catalog,
988 : * while raw defaults go into a list of RawColumnDefault structs that will
989 : * be processed by AddRelationNewConstraints. (We can't deal with raw
990 : * expressions until we can do transformExpr.)
991 : */
992 64936 : rawDefaults = NIL;
993 64936 : cookedDefaults = NIL;
994 64936 : attnum = 0;
995 :
996 329724 : foreach(listptr, stmt->tableElts)
997 : {
998 264788 : ColumnDef *colDef = lfirst(listptr);
999 :
1000 264788 : attnum++;
1001 264788 : if (colDef->raw_default != NULL)
1002 : {
1003 : RawColumnDefault *rawEnt;
1004 :
1005 : Assert(colDef->cooked_default == NULL);
1006 :
1007 3400 : rawEnt = palloc_object(RawColumnDefault);
1008 3400 : rawEnt->attnum = attnum;
1009 3400 : rawEnt->raw_default = colDef->raw_default;
1010 3400 : rawEnt->generated = colDef->generated;
1011 3400 : rawDefaults = lappend(rawDefaults, rawEnt);
1012 : }
1013 261388 : else if (colDef->cooked_default != NULL)
1014 : {
1015 : CookedConstraint *cooked;
1016 :
1017 540 : cooked = palloc_object(CookedConstraint);
1018 540 : cooked->contype = CONSTR_DEFAULT;
1019 540 : cooked->conoid = InvalidOid; /* until created */
1020 540 : cooked->name = NULL;
1021 540 : cooked->attnum = attnum;
1022 540 : cooked->expr = colDef->cooked_default;
1023 540 : cooked->is_enforced = true;
1024 540 : cooked->skip_validation = false;
1025 540 : cooked->is_local = true; /* not used for defaults */
1026 540 : cooked->inhcount = 0; /* ditto */
1027 540 : cooked->is_no_inherit = false;
1028 540 : cookedDefaults = lappend(cookedDefaults, cooked);
1029 : }
1030 : }
1031 :
1032 : /*
1033 : * For relations with table AM and partitioned tables, select access
1034 : * method to use: an explicitly indicated one, or (in the case of a
1035 : * partitioned table) the parent's, if it has one.
1036 : */
1037 64936 : if (stmt->accessMethod != NULL)
1038 : {
1039 : Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1040 134 : accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1041 : }
1042 64802 : else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1043 : {
1044 40822 : if (stmt->partbound)
1045 : {
1046 : Assert(list_length(inheritOids) == 1);
1047 8950 : accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1048 : }
1049 :
1050 40822 : if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1051 35258 : accessMethodId = get_table_am_oid(default_table_access_method, false);
1052 : }
1053 :
1054 : /*
1055 : * Create the relation. Inherited defaults and CHECK constraints are
1056 : * passed in for immediate handling --- since they don't need parsing,
1057 : * they can be stored immediately.
1058 : */
1059 64918 : relationId = heap_create_with_catalog(relname,
1060 : namespaceId,
1061 : tablespaceId,
1062 : InvalidOid,
1063 : InvalidOid,
1064 : ofTypeId,
1065 : ownerId,
1066 : accessMethodId,
1067 : descriptor,
1068 : list_concat(cookedDefaults,
1069 : old_constraints),
1070 : relkind,
1071 64918 : stmt->relation->relpersistence,
1072 : false,
1073 : false,
1074 : stmt->oncommit,
1075 : reloptions,
1076 : true,
1077 : allowSystemTableMods,
1078 : false,
1079 : InvalidOid,
1080 : typaddress);
1081 :
1082 : /*
1083 : * We must bump the command counter to make the newly-created relation
1084 : * tuple visible for opening.
1085 : */
1086 64870 : CommandCounterIncrement();
1087 :
1088 : /*
1089 : * Open the new relation and acquire exclusive lock on it. This isn't
1090 : * really necessary for locking out other backends (since they can't see
1091 : * the new rel anyway until we commit), but it keeps the lock manager from
1092 : * complaining about deadlock risks.
1093 : */
1094 64870 : rel = relation_open(relationId, AccessExclusiveLock);
1095 :
1096 : /*
1097 : * Now add any newly specified column default and generation expressions
1098 : * to the new relation. These are passed to us in the form of raw
1099 : * parsetrees; we need to transform them to executable expression trees
1100 : * before they can be added. The most convenient way to do that is to
1101 : * apply the parser's transformExpr routine, but transformExpr doesn't
1102 : * work unless we have a pre-existing relation. So, the transformation has
1103 : * to be postponed to this final step of CREATE TABLE.
1104 : *
1105 : * This needs to be before processing the partitioning clauses because
1106 : * those could refer to generated columns.
1107 : */
1108 64870 : if (rawDefaults)
1109 2880 : AddRelationNewConstraints(rel, rawDefaults, NIL,
1110 : true, true, false, queryString);
1111 :
1112 : /*
1113 : * Make column generation expressions visible for use by partitioning.
1114 : */
1115 64678 : CommandCounterIncrement();
1116 :
1117 : /* Process and store partition bound, if any. */
1118 64678 : if (stmt->partbound)
1119 : {
1120 : PartitionBoundSpec *bound;
1121 : ParseState *pstate;
1122 9054 : Oid parentId = linitial_oid(inheritOids),
1123 : defaultPartOid;
1124 : Relation parent,
1125 9054 : defaultRel = NULL;
1126 : ParseNamespaceItem *nsitem;
1127 :
1128 : /* Already have strong enough lock on the parent */
1129 9054 : parent = table_open(parentId, NoLock);
1130 :
1131 : /*
1132 : * We are going to try to validate the partition bound specification
1133 : * against the partition key of parentRel, so it better have one.
1134 : */
1135 9054 : if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1136 18 : ereport(ERROR,
1137 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1138 : errmsg("\"%s\" is not partitioned",
1139 : RelationGetRelationName(parent))));
1140 :
1141 : /*
1142 : * The partition constraint of the default partition depends on the
1143 : * partition bounds of every other partition. It is possible that
1144 : * another backend might be about to execute a query on the default
1145 : * partition table, and that the query relies on previously cached
1146 : * default partition constraints. We must therefore take a table lock
1147 : * strong enough to prevent all queries on the default partition from
1148 : * proceeding until we commit and send out a shared-cache-inval notice
1149 : * that will make them update their index lists.
1150 : *
1151 : * Order of locking: The relation being added won't be visible to
1152 : * other backends until it is committed, hence here in
1153 : * DefineRelation() the order of locking the default partition and the
1154 : * relation being added does not matter. But at all other places we
1155 : * need to lock the default relation before we lock the relation being
1156 : * added or removed i.e. we should take the lock in same order at all
1157 : * the places such that lock parent, lock default partition and then
1158 : * lock the partition so as to avoid a deadlock.
1159 : */
1160 : defaultPartOid =
1161 9036 : get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1162 : true));
1163 9036 : if (OidIsValid(defaultPartOid))
1164 378 : defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1165 :
1166 : /* Transform the bound values */
1167 9036 : pstate = make_parsestate(NULL);
1168 9036 : pstate->p_sourcetext = queryString;
1169 :
1170 : /*
1171 : * Add an nsitem containing this relation, so that transformExpr
1172 : * called on partition bound expressions is able to report errors
1173 : * using a proper context.
1174 : */
1175 9036 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1176 : NULL, false, false);
1177 9036 : addNSItemToQuery(pstate, nsitem, false, true, true);
1178 :
1179 9036 : bound = transformPartitionBound(pstate, parent, stmt->partbound);
1180 :
1181 : /*
1182 : * Check first that the new partition's bound is valid and does not
1183 : * overlap with any of existing partitions of the parent.
1184 : */
1185 8832 : check_new_partition_bound(relname, parent, bound, pstate);
1186 :
1187 : /*
1188 : * If the default partition exists, its partition constraints will
1189 : * change after the addition of this new partition such that it won't
1190 : * allow any row that qualifies for this new partition. So, check that
1191 : * the existing data in the default partition satisfies the constraint
1192 : * as it will exist after adding this partition.
1193 : */
1194 8718 : if (OidIsValid(defaultPartOid))
1195 : {
1196 348 : check_default_partition_contents(parent, defaultRel, bound);
1197 : /* Keep the lock until commit. */
1198 330 : table_close(defaultRel, NoLock);
1199 : }
1200 :
1201 : /* Update the pg_class entry. */
1202 8700 : StorePartitionBound(rel, parent, bound);
1203 :
1204 8700 : table_close(parent, NoLock);
1205 : }
1206 :
1207 : /* Store inheritance information for new rel. */
1208 64324 : StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1209 :
1210 : /*
1211 : * Process the partitioning specification (if any) and store the partition
1212 : * key information into the catalog.
1213 : */
1214 64324 : if (partitioned)
1215 : {
1216 : ParseState *pstate;
1217 : int partnatts;
1218 : AttrNumber partattrs[PARTITION_MAX_KEYS];
1219 : Oid partopclass[PARTITION_MAX_KEYS];
1220 : Oid partcollation[PARTITION_MAX_KEYS];
1221 5544 : List *partexprs = NIL;
1222 :
1223 5544 : pstate = make_parsestate(NULL);
1224 5544 : pstate->p_sourcetext = queryString;
1225 :
1226 5544 : partnatts = list_length(stmt->partspec->partParams);
1227 :
1228 : /* Protect fixed-size arrays here and in executor */
1229 5544 : if (partnatts > PARTITION_MAX_KEYS)
1230 0 : ereport(ERROR,
1231 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1232 : errmsg("cannot partition using more than %d columns",
1233 : PARTITION_MAX_KEYS)));
1234 :
1235 : /*
1236 : * We need to transform the raw parsetrees corresponding to partition
1237 : * expressions into executable expression trees. Like column defaults
1238 : * and CHECK constraints, we could not have done the transformation
1239 : * earlier.
1240 : */
1241 5544 : stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1242 :
1243 5514 : ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1244 : partattrs, &partexprs, partopclass,
1245 5514 : partcollation, stmt->partspec->strategy);
1246 :
1247 5382 : StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1248 : partexprs,
1249 : partopclass, partcollation);
1250 :
1251 : /* make it all visible */
1252 5382 : CommandCounterIncrement();
1253 : }
1254 :
1255 : /*
1256 : * If we're creating a partition, create now all the indexes, triggers,
1257 : * FKs defined in the parent.
1258 : *
1259 : * We can't do it earlier, because DefineIndex wants to know the partition
1260 : * key which we just stored.
1261 : */
1262 64162 : if (stmt->partbound)
1263 : {
1264 8694 : Oid parentId = linitial_oid(inheritOids);
1265 : Relation parent;
1266 : List *idxlist;
1267 : ListCell *cell;
1268 :
1269 : /* Already have strong enough lock on the parent */
1270 8694 : parent = table_open(parentId, NoLock);
1271 8694 : idxlist = RelationGetIndexList(parent);
1272 :
1273 : /*
1274 : * For each index in the parent table, create one in the partition
1275 : */
1276 10342 : foreach(cell, idxlist)
1277 : {
1278 1666 : Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1279 : AttrMap *attmap;
1280 : IndexStmt *idxstmt;
1281 : Oid constraintOid;
1282 :
1283 1666 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1284 : {
1285 36 : if (idxRel->rd_index->indisunique)
1286 12 : ereport(ERROR,
1287 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1288 : errmsg("cannot create foreign partition of partitioned table \"%s\"",
1289 : RelationGetRelationName(parent)),
1290 : errdetail("Table \"%s\" contains indexes that are unique.",
1291 : RelationGetRelationName(parent))));
1292 : else
1293 : {
1294 24 : index_close(idxRel, AccessShareLock);
1295 24 : continue;
1296 : }
1297 : }
1298 :
1299 1630 : attmap = build_attrmap_by_name(RelationGetDescr(rel),
1300 : RelationGetDescr(parent),
1301 : false);
1302 : idxstmt =
1303 1630 : generateClonedIndexStmt(NULL, idxRel,
1304 : attmap, &constraintOid);
1305 1630 : DefineIndex(RelationGetRelid(rel),
1306 : idxstmt,
1307 : InvalidOid,
1308 : RelationGetRelid(idxRel),
1309 : constraintOid,
1310 : -1,
1311 : false, false, false, false, false);
1312 :
1313 1624 : index_close(idxRel, AccessShareLock);
1314 : }
1315 :
1316 8676 : list_free(idxlist);
1317 :
1318 : /*
1319 : * If there are any row-level triggers, clone them to the new
1320 : * partition.
1321 : */
1322 8676 : if (parent->trigdesc != NULL)
1323 474 : CloneRowTriggersToPartition(parent, rel);
1324 :
1325 : /*
1326 : * And foreign keys too. Note that because we're freshly creating the
1327 : * table, there is no need to verify these new constraints.
1328 : */
1329 8676 : CloneForeignKeyConstraints(NULL, parent, rel);
1330 :
1331 8676 : table_close(parent, NoLock);
1332 : }
1333 :
1334 : /*
1335 : * Now add any newly specified CHECK constraints to the new relation. Same
1336 : * as for defaults above, but these need to come after partitioning is set
1337 : * up.
1338 : */
1339 64144 : if (stmt->constraints)
1340 742 : AddRelationNewConstraints(rel, NIL, stmt->constraints,
1341 : true, true, false, queryString);
1342 :
1343 : /*
1344 : * Finally, merge the not-null constraints that are declared directly with
1345 : * those that come from parent relations (making sure to count inheritance
1346 : * appropriately for each), create them, and set the attnotnull flag on
1347 : * columns that don't yet have it.
1348 : */
1349 64114 : nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1350 : old_notnulls);
1351 143900 : foreach_int(attrnum, nncols)
1352 15828 : set_attnotnull(NULL, rel, attrnum, true, false);
1353 :
1354 64036 : ObjectAddressSet(address, RelationRelationId, relationId);
1355 :
1356 : /*
1357 : * Clean up. We keep lock on new relation (although it shouldn't be
1358 : * visible to anyone else anyway, until commit).
1359 : */
1360 64036 : relation_close(rel, NoLock);
1361 :
1362 64036 : return address;
1363 : }
1364 :
1365 : /*
1366 : * BuildDescForRelation
1367 : *
1368 : * Given a list of ColumnDef nodes, build a TupleDesc.
1369 : *
1370 : * Note: This is only for the limited purpose of table and view creation. Not
1371 : * everything is filled in. A real tuple descriptor should be obtained from
1372 : * the relcache.
1373 : */
1374 : TupleDesc
1375 68652 : BuildDescForRelation(const List *columns)
1376 : {
1377 : int natts;
1378 : AttrNumber attnum;
1379 : ListCell *l;
1380 : TupleDesc desc;
1381 : char *attname;
1382 : Oid atttypid;
1383 : int32 atttypmod;
1384 : Oid attcollation;
1385 : int attdim;
1386 :
1387 : /*
1388 : * allocate a new tuple descriptor
1389 : */
1390 68652 : natts = list_length(columns);
1391 68652 : desc = CreateTemplateTupleDesc(natts);
1392 :
1393 68652 : attnum = 0;
1394 :
1395 338440 : foreach(l, columns)
1396 : {
1397 269848 : ColumnDef *entry = lfirst(l);
1398 : AclResult aclresult;
1399 : Form_pg_attribute att;
1400 :
1401 : /*
1402 : * for each entry in the list, get the name and type information from
1403 : * the list and have TupleDescInitEntry fill in the attribute
1404 : * information we need.
1405 : */
1406 269848 : attnum++;
1407 :
1408 269848 : attname = entry->colname;
1409 269848 : typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1410 :
1411 269848 : aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1412 269848 : if (aclresult != ACLCHECK_OK)
1413 42 : aclcheck_error_type(aclresult, atttypid);
1414 :
1415 269806 : attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1416 269806 : attdim = list_length(entry->typeName->arrayBounds);
1417 269806 : if (attdim > PG_INT16_MAX)
1418 0 : ereport(ERROR,
1419 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1420 : errmsg("too many array dimensions"));
1421 :
1422 269806 : if (entry->typeName->setof)
1423 0 : ereport(ERROR,
1424 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1425 : errmsg("column \"%s\" cannot be declared SETOF",
1426 : attname)));
1427 :
1428 269806 : TupleDescInitEntry(desc, attnum, attname,
1429 : atttypid, atttypmod, attdim);
1430 269806 : att = TupleDescAttr(desc, attnum - 1);
1431 :
1432 : /* Override TupleDescInitEntry's settings as requested */
1433 269806 : TupleDescInitEntryCollation(desc, attnum, attcollation);
1434 :
1435 : /* Fill in additional stuff not handled by TupleDescInitEntry */
1436 269806 : att->attnotnull = entry->is_not_null;
1437 269806 : att->attislocal = entry->is_local;
1438 269806 : att->attinhcount = entry->inhcount;
1439 269806 : att->attidentity = entry->identity;
1440 269806 : att->attgenerated = entry->generated;
1441 269806 : att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1442 269794 : if (entry->storage)
1443 24998 : att->attstorage = entry->storage;
1444 244796 : else if (entry->storage_name)
1445 56 : att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1446 :
1447 269788 : populate_compact_attribute(desc, attnum - 1);
1448 : }
1449 :
1450 68592 : return desc;
1451 : }
1452 :
1453 : /*
1454 : * Emit the right error or warning message for a "DROP" command issued on a
1455 : * non-existent relation
1456 : */
1457 : static void
1458 1132 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1459 : {
1460 : const struct dropmsgstrings *rentry;
1461 :
1462 1252 : if (rel->schemaname != NULL &&
1463 120 : !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1464 : {
1465 42 : if (!missing_ok)
1466 : {
1467 0 : ereport(ERROR,
1468 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1469 : errmsg("schema \"%s\" does not exist", rel->schemaname)));
1470 : }
1471 : else
1472 : {
1473 42 : ereport(NOTICE,
1474 : (errmsg("schema \"%s\" does not exist, skipping",
1475 : rel->schemaname)));
1476 : }
1477 42 : return;
1478 : }
1479 :
1480 1410 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1481 : {
1482 1410 : if (rentry->kind == rightkind)
1483 : {
1484 1090 : if (!missing_ok)
1485 : {
1486 138 : ereport(ERROR,
1487 : (errcode(rentry->nonexistent_code),
1488 : errmsg(rentry->nonexistent_msg, rel->relname)));
1489 : }
1490 : else
1491 : {
1492 952 : ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1493 952 : break;
1494 : }
1495 : }
1496 : }
1497 :
1498 : Assert(rentry->kind != '\0'); /* Should be impossible */
1499 : }
1500 :
1501 : /*
1502 : * Emit the right error message for a "DROP" command issued on a
1503 : * relation of the wrong type
1504 : */
1505 : static void
1506 0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1507 : {
1508 : const struct dropmsgstrings *rentry;
1509 : const struct dropmsgstrings *wentry;
1510 :
1511 0 : for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1512 0 : if (rentry->kind == rightkind)
1513 0 : break;
1514 : Assert(rentry->kind != '\0');
1515 :
1516 0 : for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1517 0 : if (wentry->kind == wrongkind)
1518 0 : break;
1519 : /* wrongkind could be something we don't have in our table... */
1520 :
1521 0 : ereport(ERROR,
1522 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1523 : errmsg(rentry->nota_msg, relname),
1524 : (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1525 : }
1526 :
1527 : /*
1528 : * RemoveRelations
1529 : * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1530 : * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1531 : */
1532 : void
1533 18126 : RemoveRelations(DropStmt *drop)
1534 : {
1535 : ObjectAddresses *objects;
1536 : char relkind;
1537 : ListCell *cell;
1538 18126 : int flags = 0;
1539 18126 : LOCKMODE lockmode = AccessExclusiveLock;
1540 :
1541 : /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1542 18126 : if (drop->concurrent)
1543 : {
1544 : /*
1545 : * Note that for temporary relations this lock may get upgraded later
1546 : * on, but as no other session can access a temporary relation, this
1547 : * is actually fine.
1548 : */
1549 138 : lockmode = ShareUpdateExclusiveLock;
1550 : Assert(drop->removeType == OBJECT_INDEX);
1551 138 : if (list_length(drop->objects) != 1)
1552 6 : ereport(ERROR,
1553 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1554 : errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1555 132 : if (drop->behavior == DROP_CASCADE)
1556 0 : ereport(ERROR,
1557 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1558 : errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1559 : }
1560 :
1561 : /*
1562 : * First we identify all the relations, then we delete them in a single
1563 : * performMultipleDeletions() call. This is to avoid unwanted DROP
1564 : * RESTRICT errors if one of the relations depends on another.
1565 : */
1566 :
1567 : /* Determine required relkind */
1568 18120 : switch (drop->removeType)
1569 : {
1570 15864 : case OBJECT_TABLE:
1571 15864 : relkind = RELKIND_RELATION;
1572 15864 : break;
1573 :
1574 820 : case OBJECT_INDEX:
1575 820 : relkind = RELKIND_INDEX;
1576 820 : break;
1577 :
1578 178 : case OBJECT_SEQUENCE:
1579 178 : relkind = RELKIND_SEQUENCE;
1580 178 : break;
1581 :
1582 968 : case OBJECT_VIEW:
1583 968 : relkind = RELKIND_VIEW;
1584 968 : break;
1585 :
1586 126 : case OBJECT_MATVIEW:
1587 126 : relkind = RELKIND_MATVIEW;
1588 126 : break;
1589 :
1590 164 : case OBJECT_FOREIGN_TABLE:
1591 164 : relkind = RELKIND_FOREIGN_TABLE;
1592 164 : break;
1593 :
1594 0 : default:
1595 0 : elog(ERROR, "unrecognized drop object type: %d",
1596 : (int) drop->removeType);
1597 : relkind = 0; /* keep compiler quiet */
1598 : break;
1599 : }
1600 :
1601 : /* Lock and validate each relation; build a list of object addresses */
1602 18120 : objects = new_object_addresses();
1603 :
1604 40250 : foreach(cell, drop->objects)
1605 : {
1606 22294 : RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1607 : Oid relOid;
1608 : ObjectAddress obj;
1609 : struct DropRelationCallbackState state;
1610 :
1611 : /*
1612 : * These next few steps are a great deal like relation_openrv, but we
1613 : * don't bother building a relcache entry since we don't need it.
1614 : *
1615 : * Check for shared-cache-inval messages before trying to access the
1616 : * relation. This is needed to cover the case where the name
1617 : * identifies a rel that has been dropped and recreated since the
1618 : * start of our transaction: if we don't flush the old syscache entry,
1619 : * then we'll latch onto that entry and suffer an error later.
1620 : */
1621 22294 : AcceptInvalidationMessages();
1622 :
1623 : /* Look up the appropriate relation using namespace search. */
1624 22294 : state.expected_relkind = relkind;
1625 44588 : state.heap_lockmode = drop->concurrent ?
1626 22294 : ShareUpdateExclusiveLock : AccessExclusiveLock;
1627 : /* We must initialize these fields to show that no locks are held: */
1628 22294 : state.heapOid = InvalidOid;
1629 22294 : state.partParentOid = InvalidOid;
1630 :
1631 22294 : relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1632 : RangeVarCallbackForDropRelation,
1633 : &state);
1634 :
1635 : /* Not there? */
1636 22274 : if (!OidIsValid(relOid))
1637 : {
1638 1132 : DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1639 994 : continue;
1640 : }
1641 :
1642 : /*
1643 : * Decide if concurrent mode needs to be used here or not. The
1644 : * callback retrieved the rel's persistence for us.
1645 : */
1646 21142 : if (drop->concurrent &&
1647 126 : state.actual_relpersistence != RELPERSISTENCE_TEMP)
1648 : {
1649 : Assert(list_length(drop->objects) == 1 &&
1650 : drop->removeType == OBJECT_INDEX);
1651 108 : flags |= PERFORM_DELETION_CONCURRENTLY;
1652 : }
1653 :
1654 : /*
1655 : * Concurrent index drop cannot be used with partitioned indexes,
1656 : * either.
1657 : */
1658 21142 : if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1659 108 : state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1660 6 : ereport(ERROR,
1661 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1662 : errmsg("cannot drop partitioned index \"%s\" concurrently",
1663 : rel->relname)));
1664 :
1665 : /*
1666 : * If we're told to drop a partitioned index, we must acquire lock on
1667 : * all the children of its parent partitioned table before proceeding.
1668 : * Otherwise we'd try to lock the child index partitions before their
1669 : * tables, leading to potential deadlock against other sessions that
1670 : * will lock those objects in the other order.
1671 : */
1672 21136 : if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1673 76 : (void) find_all_inheritors(state.heapOid,
1674 : state.heap_lockmode,
1675 : NULL);
1676 :
1677 : /* OK, we're ready to delete this one */
1678 21136 : obj.classId = RelationRelationId;
1679 21136 : obj.objectId = relOid;
1680 21136 : obj.objectSubId = 0;
1681 :
1682 21136 : add_exact_object_address(&obj, objects);
1683 : }
1684 :
1685 17956 : performMultipleDeletions(objects, drop->behavior, flags);
1686 :
1687 17814 : free_object_addresses(objects);
1688 17814 : }
1689 :
1690 : /*
1691 : * Before acquiring a table lock, check whether we have sufficient rights.
1692 : * In the case of DROP INDEX, also try to lock the table before the index.
1693 : * Also, if the table to be dropped is a partition, we try to lock the parent
1694 : * first.
1695 : */
1696 : static void
1697 22754 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1698 : void *arg)
1699 : {
1700 : HeapTuple tuple;
1701 : struct DropRelationCallbackState *state;
1702 : char expected_relkind;
1703 : bool is_partition;
1704 : Form_pg_class classform;
1705 : LOCKMODE heap_lockmode;
1706 22754 : bool invalid_system_index = false;
1707 :
1708 22754 : state = (struct DropRelationCallbackState *) arg;
1709 22754 : heap_lockmode = state->heap_lockmode;
1710 :
1711 : /*
1712 : * If we previously locked some other index's heap, and the name we're
1713 : * looking up no longer refers to that relation, release the now-useless
1714 : * lock.
1715 : */
1716 22754 : if (relOid != oldRelOid && OidIsValid(state->heapOid))
1717 : {
1718 0 : UnlockRelationOid(state->heapOid, heap_lockmode);
1719 0 : state->heapOid = InvalidOid;
1720 : }
1721 :
1722 : /*
1723 : * Similarly, if we previously locked some other partition's heap, and the
1724 : * name we're looking up no longer refers to that relation, release the
1725 : * now-useless lock.
1726 : */
1727 22754 : if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1728 : {
1729 0 : UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1730 0 : state->partParentOid = InvalidOid;
1731 : }
1732 :
1733 : /* Didn't find a relation, so no need for locking or permission checks. */
1734 22754 : if (!OidIsValid(relOid))
1735 1140 : return;
1736 :
1737 21614 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1738 21614 : if (!HeapTupleIsValid(tuple))
1739 0 : return; /* concurrently dropped, so nothing to do */
1740 21614 : classform = (Form_pg_class) GETSTRUCT(tuple);
1741 21614 : is_partition = classform->relispartition;
1742 :
1743 : /* Pass back some data to save lookups in RemoveRelations */
1744 21614 : state->actual_relkind = classform->relkind;
1745 21614 : state->actual_relpersistence = classform->relpersistence;
1746 :
1747 : /*
1748 : * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1749 : * but RemoveRelations() can only pass one relkind for a given relation.
1750 : * It chooses RELKIND_RELATION for both regular and partitioned tables.
1751 : * That means we must be careful before giving the wrong type error when
1752 : * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1753 : * exists with indexes.
1754 : */
1755 21614 : if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1756 3440 : expected_relkind = RELKIND_RELATION;
1757 18174 : else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1758 86 : expected_relkind = RELKIND_INDEX;
1759 : else
1760 18088 : expected_relkind = classform->relkind;
1761 :
1762 21614 : if (state->expected_relkind != expected_relkind)
1763 0 : DropErrorMsgWrongType(rel->relname, classform->relkind,
1764 0 : state->expected_relkind);
1765 :
1766 : /* Allow DROP to either table owner or schema owner */
1767 21614 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1768 18 : !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1769 18 : aclcheck_error(ACLCHECK_NOT_OWNER,
1770 18 : get_relkind_objtype(classform->relkind),
1771 18 : rel->relname);
1772 :
1773 : /*
1774 : * Check the case of a system index that might have been invalidated by a
1775 : * failed concurrent process and allow its drop. For the time being, this
1776 : * only concerns indexes of toast relations that became invalid during a
1777 : * REINDEX CONCURRENTLY process.
1778 : */
1779 21596 : if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1780 : {
1781 : HeapTuple locTuple;
1782 : Form_pg_index indexform;
1783 : bool indisvalid;
1784 :
1785 0 : locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1786 0 : if (!HeapTupleIsValid(locTuple))
1787 : {
1788 0 : ReleaseSysCache(tuple);
1789 0 : return;
1790 : }
1791 :
1792 0 : indexform = (Form_pg_index) GETSTRUCT(locTuple);
1793 0 : indisvalid = indexform->indisvalid;
1794 0 : ReleaseSysCache(locTuple);
1795 :
1796 : /* Mark object as being an invalid index of system catalogs */
1797 0 : if (!indisvalid)
1798 0 : invalid_system_index = true;
1799 : }
1800 :
1801 : /* In the case of an invalid index, it is fine to bypass this check */
1802 21596 : if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1803 2 : ereport(ERROR,
1804 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1805 : errmsg("permission denied: \"%s\" is a system catalog",
1806 : rel->relname)));
1807 :
1808 21594 : ReleaseSysCache(tuple);
1809 :
1810 : /*
1811 : * In DROP INDEX, attempt to acquire lock on the parent table before
1812 : * locking the index. index_drop() will need this anyway, and since
1813 : * regular queries lock tables before their indexes, we risk deadlock if
1814 : * we do it the other way around. No error if we don't find a pg_index
1815 : * entry, though --- the relation may have been dropped. Note that this
1816 : * code will execute for either plain or partitioned indexes.
1817 : */
1818 21594 : if (expected_relkind == RELKIND_INDEX &&
1819 : relOid != oldRelOid)
1820 : {
1821 808 : state->heapOid = IndexGetRelation(relOid, true);
1822 808 : if (OidIsValid(state->heapOid))
1823 808 : LockRelationOid(state->heapOid, heap_lockmode);
1824 : }
1825 :
1826 : /*
1827 : * Similarly, if the relation is a partition, we must acquire lock on its
1828 : * parent before locking the partition. That's because queries lock the
1829 : * parent before its partitions, so we risk deadlock if we do it the other
1830 : * way around.
1831 : */
1832 21594 : if (is_partition && relOid != oldRelOid)
1833 : {
1834 618 : state->partParentOid = get_partition_parent(relOid, true);
1835 618 : if (OidIsValid(state->partParentOid))
1836 618 : LockRelationOid(state->partParentOid, AccessExclusiveLock);
1837 : }
1838 : }
1839 :
1840 : /*
1841 : * ExecuteTruncate
1842 : * Executes a TRUNCATE command.
1843 : *
1844 : * This is a multi-relation truncate. We first open and grab exclusive
1845 : * lock on all relations involved, checking permissions and otherwise
1846 : * verifying that the relation is OK for truncation. Note that if relations
1847 : * are foreign tables, at this stage, we have not yet checked that their
1848 : * foreign data in external data sources are OK for truncation. These are
1849 : * checked when foreign data are actually truncated later. In CASCADE mode,
1850 : * relations having FK references to the targeted relations are automatically
1851 : * added to the group; in RESTRICT mode, we check that all FK references are
1852 : * internal to the group that's being truncated. Finally all the relations
1853 : * are truncated and reindexed.
1854 : */
1855 : void
1856 1778 : ExecuteTruncate(TruncateStmt *stmt)
1857 : {
1858 1778 : List *rels = NIL;
1859 1778 : List *relids = NIL;
1860 1778 : List *relids_logged = NIL;
1861 : ListCell *cell;
1862 :
1863 : /*
1864 : * Open, exclusive-lock, and check all the explicitly-specified relations
1865 : */
1866 3768 : foreach(cell, stmt->relations)
1867 : {
1868 2046 : RangeVar *rv = lfirst(cell);
1869 : Relation rel;
1870 2046 : bool recurse = rv->inh;
1871 : Oid myrelid;
1872 2046 : LOCKMODE lockmode = AccessExclusiveLock;
1873 :
1874 2046 : myrelid = RangeVarGetRelidExtended(rv, lockmode,
1875 : 0, RangeVarCallbackForTruncate,
1876 : NULL);
1877 :
1878 : /* don't throw error for "TRUNCATE foo, foo" */
1879 2008 : if (list_member_oid(relids, myrelid))
1880 2 : continue;
1881 :
1882 : /* open the relation, we already hold a lock on it */
1883 2006 : rel = table_open(myrelid, NoLock);
1884 :
1885 : /*
1886 : * RangeVarGetRelidExtended() has done most checks with its callback,
1887 : * but other checks with the now-opened Relation remain.
1888 : */
1889 2006 : truncate_check_activity(rel);
1890 :
1891 2000 : rels = lappend(rels, rel);
1892 2000 : relids = lappend_oid(relids, myrelid);
1893 :
1894 : /* Log this relation only if needed for logical decoding */
1895 2000 : if (RelationIsLogicallyLogged(rel))
1896 74 : relids_logged = lappend_oid(relids_logged, myrelid);
1897 :
1898 2000 : if (recurse)
1899 : {
1900 : ListCell *child;
1901 : List *children;
1902 :
1903 1942 : children = find_all_inheritors(myrelid, lockmode, NULL);
1904 :
1905 5688 : foreach(child, children)
1906 : {
1907 3746 : Oid childrelid = lfirst_oid(child);
1908 :
1909 3746 : if (list_member_oid(relids, childrelid))
1910 1942 : continue;
1911 :
1912 : /* find_all_inheritors already got lock */
1913 1804 : rel = table_open(childrelid, NoLock);
1914 :
1915 : /*
1916 : * It is possible that the parent table has children that are
1917 : * temp tables of other backends. We cannot safely access
1918 : * such tables (because of buffering issues), and the best
1919 : * thing to do is to silently ignore them. Note that this
1920 : * check is the same as one of the checks done in
1921 : * truncate_check_activity() called below, still it is kept
1922 : * here for simplicity.
1923 : */
1924 1804 : if (RELATION_IS_OTHER_TEMP(rel))
1925 : {
1926 8 : table_close(rel, lockmode);
1927 8 : continue;
1928 : }
1929 :
1930 : /*
1931 : * Inherited TRUNCATE commands perform access permission
1932 : * checks on the parent table only. So we skip checking the
1933 : * children's permissions and don't call
1934 : * truncate_check_perms() here.
1935 : */
1936 1796 : truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1937 1796 : truncate_check_activity(rel);
1938 :
1939 1796 : rels = lappend(rels, rel);
1940 1796 : relids = lappend_oid(relids, childrelid);
1941 :
1942 : /* Log this relation only if needed for logical decoding */
1943 1796 : if (RelationIsLogicallyLogged(rel))
1944 22 : relids_logged = lappend_oid(relids_logged, childrelid);
1945 : }
1946 : }
1947 58 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1948 12 : ereport(ERROR,
1949 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1950 : errmsg("cannot truncate only a partitioned table"),
1951 : errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1952 : }
1953 :
1954 1722 : ExecuteTruncateGuts(rels, relids, relids_logged,
1955 1722 : stmt->behavior, stmt->restart_seqs, false);
1956 :
1957 : /* And close the rels */
1958 5270 : foreach(cell, rels)
1959 : {
1960 3630 : Relation rel = (Relation) lfirst(cell);
1961 :
1962 3630 : table_close(rel, NoLock);
1963 : }
1964 1640 : }
1965 :
1966 : /*
1967 : * ExecuteTruncateGuts
1968 : *
1969 : * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1970 : * command (see above) as well as replication subscribers that execute a
1971 : * replicated TRUNCATE action.
1972 : *
1973 : * explicit_rels is the list of Relations to truncate that the command
1974 : * specified. relids is the list of Oids corresponding to explicit_rels.
1975 : * relids_logged is the list of Oids (a subset of relids) that require
1976 : * WAL-logging. This is all a bit redundant, but the existing callers have
1977 : * this information handy in this form.
1978 : */
1979 : void
1980 1752 : ExecuteTruncateGuts(List *explicit_rels,
1981 : List *relids,
1982 : List *relids_logged,
1983 : DropBehavior behavior, bool restart_seqs,
1984 : bool run_as_table_owner)
1985 : {
1986 : List *rels;
1987 1752 : List *seq_relids = NIL;
1988 1752 : HTAB *ft_htab = NULL;
1989 : EState *estate;
1990 : ResultRelInfo *resultRelInfos;
1991 : ResultRelInfo *resultRelInfo;
1992 : SubTransactionId mySubid;
1993 : ListCell *cell;
1994 : Oid *logrelids;
1995 :
1996 : /*
1997 : * Check the explicitly-specified relations.
1998 : *
1999 : * In CASCADE mode, suck in all referencing relations as well. This
2000 : * requires multiple iterations to find indirectly-dependent relations. At
2001 : * each phase, we need to exclusive-lock new rels before looking for their
2002 : * dependencies, else we might miss something. Also, we check each rel as
2003 : * soon as we open it, to avoid a faux pas such as holding lock for a long
2004 : * time on a rel we have no permissions for.
2005 : */
2006 1752 : rels = list_copy(explicit_rels);
2007 1752 : if (behavior == DROP_CASCADE)
2008 : {
2009 : for (;;)
2010 40 : {
2011 : List *newrelids;
2012 :
2013 80 : newrelids = heap_truncate_find_FKs(relids);
2014 80 : if (newrelids == NIL)
2015 40 : break; /* nothing else to add */
2016 :
2017 134 : foreach(cell, newrelids)
2018 : {
2019 94 : Oid relid = lfirst_oid(cell);
2020 : Relation rel;
2021 :
2022 94 : rel = table_open(relid, AccessExclusiveLock);
2023 94 : ereport(NOTICE,
2024 : (errmsg("truncate cascades to table \"%s\"",
2025 : RelationGetRelationName(rel))));
2026 94 : truncate_check_rel(relid, rel->rd_rel);
2027 94 : truncate_check_perms(relid, rel->rd_rel);
2028 94 : truncate_check_activity(rel);
2029 94 : rels = lappend(rels, rel);
2030 94 : relids = lappend_oid(relids, relid);
2031 :
2032 : /* Log this relation only if needed for logical decoding */
2033 94 : if (RelationIsLogicallyLogged(rel))
2034 0 : relids_logged = lappend_oid(relids_logged, relid);
2035 : }
2036 : }
2037 : }
2038 :
2039 : /*
2040 : * Check foreign key references. In CASCADE mode, this should be
2041 : * unnecessary since we just pulled in all the references; but as a
2042 : * cross-check, do it anyway if in an Assert-enabled build.
2043 : */
2044 : #ifdef USE_ASSERT_CHECKING
2045 : heap_truncate_check_FKs(rels, false);
2046 : #else
2047 1752 : if (behavior == DROP_RESTRICT)
2048 1712 : heap_truncate_check_FKs(rels, false);
2049 : #endif
2050 :
2051 : /*
2052 : * If we are asked to restart sequences, find all the sequences, lock them
2053 : * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2054 : * We want to do this early since it's pointless to do all the truncation
2055 : * work only to fail on sequence permissions.
2056 : */
2057 1678 : if (restart_seqs)
2058 : {
2059 44 : foreach(cell, rels)
2060 : {
2061 22 : Relation rel = (Relation) lfirst(cell);
2062 22 : List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2063 : ListCell *seqcell;
2064 :
2065 54 : foreach(seqcell, seqlist)
2066 : {
2067 32 : Oid seq_relid = lfirst_oid(seqcell);
2068 : Relation seq_rel;
2069 :
2070 32 : seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2071 :
2072 : /* This check must match AlterSequence! */
2073 32 : if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2074 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2075 0 : RelationGetRelationName(seq_rel));
2076 :
2077 32 : seq_relids = lappend_oid(seq_relids, seq_relid);
2078 :
2079 32 : relation_close(seq_rel, NoLock);
2080 : }
2081 : }
2082 : }
2083 :
2084 : /* Prepare to catch AFTER triggers. */
2085 1678 : AfterTriggerBeginQuery();
2086 :
2087 : /*
2088 : * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2089 : * each relation. We don't need to call ExecOpenIndices, though.
2090 : *
2091 : * We put the ResultRelInfos in the es_opened_result_relations list, even
2092 : * though we don't have a range table and don't populate the
2093 : * es_result_relations array. That's a bit bogus, but it's enough to make
2094 : * ExecGetTriggerResultRel() find them.
2095 : */
2096 1678 : estate = CreateExecutorState();
2097 : resultRelInfos = (ResultRelInfo *)
2098 1678 : palloc(list_length(rels) * sizeof(ResultRelInfo));
2099 1678 : resultRelInfo = resultRelInfos;
2100 5470 : foreach(cell, rels)
2101 : {
2102 3792 : Relation rel = (Relation) lfirst(cell);
2103 :
2104 3792 : InitResultRelInfo(resultRelInfo,
2105 : rel,
2106 : 0, /* dummy rangetable index */
2107 : NULL,
2108 : 0);
2109 3792 : estate->es_opened_result_relations =
2110 3792 : lappend(estate->es_opened_result_relations, resultRelInfo);
2111 3792 : resultRelInfo++;
2112 : }
2113 :
2114 : /*
2115 : * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2116 : * truncating (this is because one of them might throw an error). Also, if
2117 : * we were to allow them to prevent statement execution, that would need
2118 : * to be handled here.
2119 : */
2120 1678 : resultRelInfo = resultRelInfos;
2121 5470 : foreach(cell, rels)
2122 : {
2123 : UserContext ucxt;
2124 :
2125 3792 : if (run_as_table_owner)
2126 60 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2127 : &ucxt);
2128 3792 : ExecBSTruncateTriggers(estate, resultRelInfo);
2129 3792 : if (run_as_table_owner)
2130 60 : RestoreUserContext(&ucxt);
2131 3792 : resultRelInfo++;
2132 : }
2133 :
2134 : /*
2135 : * OK, truncate each table.
2136 : */
2137 1678 : mySubid = GetCurrentSubTransactionId();
2138 :
2139 5470 : foreach(cell, rels)
2140 : {
2141 3792 : Relation rel = (Relation) lfirst(cell);
2142 :
2143 : /* Skip partitioned tables as there is nothing to do */
2144 3792 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2145 704 : continue;
2146 :
2147 : /*
2148 : * Build the lists of foreign tables belonging to each foreign server
2149 : * and pass each list to the foreign data wrapper's callback function,
2150 : * so that each server can truncate its all foreign tables in bulk.
2151 : * Each list is saved as a single entry in a hash table that uses the
2152 : * server OID as lookup key.
2153 : */
2154 3088 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2155 34 : {
2156 34 : Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2157 : bool found;
2158 : ForeignTruncateInfo *ft_info;
2159 :
2160 : /* First time through, initialize hashtable for foreign tables */
2161 34 : if (!ft_htab)
2162 : {
2163 : HASHCTL hctl;
2164 :
2165 30 : memset(&hctl, 0, sizeof(HASHCTL));
2166 30 : hctl.keysize = sizeof(Oid);
2167 30 : hctl.entrysize = sizeof(ForeignTruncateInfo);
2168 30 : hctl.hcxt = CurrentMemoryContext;
2169 :
2170 30 : ft_htab = hash_create("TRUNCATE for Foreign Tables",
2171 : 32, /* start small and extend */
2172 : &hctl,
2173 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2174 : }
2175 :
2176 : /* Find or create cached entry for the foreign table */
2177 34 : ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2178 34 : if (!found)
2179 30 : ft_info->rels = NIL;
2180 :
2181 : /*
2182 : * Save the foreign table in the entry of the server that the
2183 : * foreign table belongs to.
2184 : */
2185 34 : ft_info->rels = lappend(ft_info->rels, rel);
2186 34 : continue;
2187 : }
2188 :
2189 : /*
2190 : * Normally, we need a transaction-safe truncation here. However, if
2191 : * the table was either created in the current (sub)transaction or has
2192 : * a new relfilenumber in the current (sub)transaction, then we can
2193 : * just truncate it in-place, because a rollback would cause the whole
2194 : * table or the current physical file to be thrown away anyway.
2195 : */
2196 3054 : if (rel->rd_createSubid == mySubid ||
2197 3028 : rel->rd_newRelfilelocatorSubid == mySubid)
2198 : {
2199 : /* Immediate, non-rollbackable truncation is OK */
2200 90 : heap_truncate_one_rel(rel);
2201 : }
2202 : else
2203 : {
2204 : Oid heap_relid;
2205 : Oid toast_relid;
2206 2964 : ReindexParams reindex_params = {0};
2207 :
2208 : /*
2209 : * This effectively deletes all rows in the table, and may be done
2210 : * in a serializable transaction. In that case we must record a
2211 : * rw-conflict in to this transaction from each transaction
2212 : * holding a predicate lock on the table.
2213 : */
2214 2964 : CheckTableForSerializableConflictIn(rel);
2215 :
2216 : /*
2217 : * Need the full transaction-safe pushups.
2218 : *
2219 : * Create a new empty storage file for the relation, and assign it
2220 : * as the relfilenumber value. The old storage file is scheduled
2221 : * for deletion at commit.
2222 : */
2223 2964 : RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2224 :
2225 2964 : heap_relid = RelationGetRelid(rel);
2226 :
2227 : /*
2228 : * The same for the toast table, if any.
2229 : */
2230 2964 : toast_relid = rel->rd_rel->reltoastrelid;
2231 2964 : if (OidIsValid(toast_relid))
2232 : {
2233 1784 : Relation toastrel = relation_open(toast_relid,
2234 : AccessExclusiveLock);
2235 :
2236 1784 : RelationSetNewRelfilenumber(toastrel,
2237 1784 : toastrel->rd_rel->relpersistence);
2238 1784 : table_close(toastrel, NoLock);
2239 : }
2240 :
2241 : /*
2242 : * Reconstruct the indexes to match, and we're done.
2243 : */
2244 2964 : reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2245 : &reindex_params);
2246 : }
2247 :
2248 3054 : pgstat_count_truncate(rel);
2249 : }
2250 :
2251 : /* Now go through the hash table, and truncate foreign tables */
2252 1678 : if (ft_htab)
2253 : {
2254 : ForeignTruncateInfo *ft_info;
2255 : HASH_SEQ_STATUS seq;
2256 :
2257 30 : hash_seq_init(&seq, ft_htab);
2258 :
2259 30 : PG_TRY();
2260 : {
2261 52 : while ((ft_info = hash_seq_search(&seq)) != NULL)
2262 : {
2263 30 : FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2264 :
2265 : /* truncate_check_rel() has checked that already */
2266 : Assert(routine->ExecForeignTruncate != NULL);
2267 :
2268 30 : routine->ExecForeignTruncate(ft_info->rels,
2269 : behavior,
2270 : restart_seqs);
2271 : }
2272 : }
2273 8 : PG_FINALLY();
2274 : {
2275 30 : hash_destroy(ft_htab);
2276 : }
2277 30 : PG_END_TRY();
2278 : }
2279 :
2280 : /*
2281 : * Restart owned sequences if we were asked to.
2282 : */
2283 1702 : foreach(cell, seq_relids)
2284 : {
2285 32 : Oid seq_relid = lfirst_oid(cell);
2286 :
2287 32 : ResetSequence(seq_relid);
2288 : }
2289 :
2290 : /*
2291 : * Write a WAL record to allow this set of actions to be logically
2292 : * decoded.
2293 : *
2294 : * Assemble an array of relids so we can write a single WAL record for the
2295 : * whole action.
2296 : */
2297 1670 : if (relids_logged != NIL)
2298 : {
2299 : xl_heap_truncate xlrec;
2300 62 : int i = 0;
2301 :
2302 : /* should only get here if effective_wal_level is 'logical' */
2303 : Assert(XLogLogicalInfoActive());
2304 :
2305 62 : logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2306 160 : foreach(cell, relids_logged)
2307 98 : logrelids[i++] = lfirst_oid(cell);
2308 :
2309 62 : xlrec.dbId = MyDatabaseId;
2310 62 : xlrec.nrelids = list_length(relids_logged);
2311 62 : xlrec.flags = 0;
2312 62 : if (behavior == DROP_CASCADE)
2313 2 : xlrec.flags |= XLH_TRUNCATE_CASCADE;
2314 62 : if (restart_seqs)
2315 4 : xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2316 :
2317 62 : XLogBeginInsert();
2318 62 : XLogRegisterData(&xlrec, SizeOfHeapTruncate);
2319 62 : XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2320 :
2321 62 : XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2322 :
2323 62 : (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2324 : }
2325 :
2326 : /*
2327 : * Process all AFTER STATEMENT TRUNCATE triggers.
2328 : */
2329 1670 : resultRelInfo = resultRelInfos;
2330 5454 : foreach(cell, rels)
2331 : {
2332 : UserContext ucxt;
2333 :
2334 3784 : if (run_as_table_owner)
2335 60 : SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2336 : &ucxt);
2337 3784 : ExecASTruncateTriggers(estate, resultRelInfo);
2338 3784 : if (run_as_table_owner)
2339 60 : RestoreUserContext(&ucxt);
2340 3784 : resultRelInfo++;
2341 : }
2342 :
2343 : /* Handle queued AFTER triggers */
2344 1670 : AfterTriggerEndQuery(estate);
2345 :
2346 : /* We can clean up the EState now */
2347 1670 : FreeExecutorState(estate);
2348 :
2349 : /*
2350 : * Close any rels opened by CASCADE (can't do this while EState still
2351 : * holds refs)
2352 : */
2353 1670 : rels = list_difference_ptr(rels, explicit_rels);
2354 1764 : foreach(cell, rels)
2355 : {
2356 94 : Relation rel = (Relation) lfirst(cell);
2357 :
2358 94 : table_close(rel, NoLock);
2359 : }
2360 1670 : }
2361 :
2362 : /*
2363 : * Check that a given relation is safe to truncate. Subroutine for
2364 : * ExecuteTruncate() and RangeVarCallbackForTruncate().
2365 : */
2366 : static void
2367 4068 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
2368 : {
2369 4068 : char *relname = NameStr(reltuple->relname);
2370 :
2371 : /*
2372 : * Only allow truncate on regular tables, foreign tables using foreign
2373 : * data wrappers supporting TRUNCATE and partitioned tables (although, the
2374 : * latter are only being included here for the following checks; no
2375 : * physical truncation will occur in their case.).
2376 : */
2377 4068 : if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2378 : {
2379 38 : Oid serverid = GetForeignServerIdByRelId(relid);
2380 38 : FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2381 :
2382 36 : if (!fdwroutine->ExecForeignTruncate)
2383 2 : ereport(ERROR,
2384 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2385 : errmsg("cannot truncate foreign table \"%s\"",
2386 : relname)));
2387 : }
2388 4030 : else if (reltuple->relkind != RELKIND_RELATION &&
2389 716 : reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2390 0 : ereport(ERROR,
2391 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2392 : errmsg("\"%s\" is not a table", relname)));
2393 :
2394 : /*
2395 : * Most system catalogs can't be truncated at all, or at least not unless
2396 : * allow_system_table_mods=on. As an exception, however, we allow
2397 : * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2398 : * pg_upgrade, because we need to change its relfilenode to match the old
2399 : * cluster, and allowing a TRUNCATE command to be executed is the easiest
2400 : * way of doing that.
2401 : */
2402 4064 : if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2403 122 : && (!IsBinaryUpgrade ||
2404 60 : (relid != LargeObjectRelationId &&
2405 : relid != LargeObjectMetadataRelationId)))
2406 2 : ereport(ERROR,
2407 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2408 : errmsg("permission denied: \"%s\" is a system catalog",
2409 : relname)));
2410 :
2411 4062 : InvokeObjectTruncateHook(relid);
2412 4062 : }
2413 :
2414 : /*
2415 : * Check that current user has the permission to truncate given relation.
2416 : */
2417 : static void
2418 2266 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
2419 : {
2420 2266 : char *relname = NameStr(reltuple->relname);
2421 : AclResult aclresult;
2422 :
2423 : /* Permissions checks */
2424 2266 : aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2425 2266 : if (aclresult != ACLCHECK_OK)
2426 32 : aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2427 : relname);
2428 2234 : }
2429 :
2430 : /*
2431 : * Set of extra sanity checks to check if a given relation is safe to
2432 : * truncate. This is split with truncate_check_rel() as
2433 : * RangeVarCallbackForTruncate() cannot open a Relation yet.
2434 : */
2435 : static void
2436 3896 : truncate_check_activity(Relation rel)
2437 : {
2438 : /*
2439 : * Don't allow truncate on temp tables of other backends ... their local
2440 : * buffer manager is not going to cope.
2441 : */
2442 3896 : if (RELATION_IS_OTHER_TEMP(rel))
2443 0 : ereport(ERROR,
2444 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2445 : errmsg("cannot truncate temporary tables of other sessions")));
2446 :
2447 : /*
2448 : * Also check for active uses of the relation in the current transaction,
2449 : * including open scans and pending AFTER trigger events.
2450 : */
2451 3896 : CheckTableNotInUse(rel, "TRUNCATE");
2452 3890 : }
2453 :
2454 : /*
2455 : * storage_name
2456 : * returns the name corresponding to a typstorage/attstorage enum value
2457 : */
2458 : static const char *
2459 24 : storage_name(char c)
2460 : {
2461 24 : switch (c)
2462 : {
2463 0 : case TYPSTORAGE_PLAIN:
2464 0 : return "PLAIN";
2465 0 : case TYPSTORAGE_EXTERNAL:
2466 0 : return "EXTERNAL";
2467 12 : case TYPSTORAGE_EXTENDED:
2468 12 : return "EXTENDED";
2469 12 : case TYPSTORAGE_MAIN:
2470 12 : return "MAIN";
2471 0 : default:
2472 0 : return "???";
2473 : }
2474 : }
2475 :
2476 : /*----------
2477 : * MergeAttributes
2478 : * Returns new schema given initial schema and superclasses.
2479 : *
2480 : * Input arguments:
2481 : * 'columns' is the column/attribute definition for the table. (It's a list
2482 : * of ColumnDef's.) It is destructively changed.
2483 : * 'supers' is a list of OIDs of parent relations, already locked by caller.
2484 : * 'relpersistence' is the persistence type of the table.
2485 : * 'is_partition' tells if the table is a partition.
2486 : *
2487 : * Output arguments:
2488 : * 'supconstr' receives a list of CookedConstraint representing
2489 : * CHECK constraints belonging to parent relations, updated as
2490 : * necessary to be valid for the child.
2491 : * 'supnotnulls' receives a list of CookedConstraint representing
2492 : * not-null constraints based on those from parent relations.
2493 : *
2494 : * Return value:
2495 : * Completed schema list.
2496 : *
2497 : * Notes:
2498 : * The order in which the attributes are inherited is very important.
2499 : * Intuitively, the inherited attributes should come first. If a table
2500 : * inherits from multiple parents, the order of those attributes are
2501 : * according to the order of the parents specified in CREATE TABLE.
2502 : *
2503 : * Here's an example:
2504 : *
2505 : * create table person (name text, age int4, location point);
2506 : * create table emp (salary int4, manager text) inherits(person);
2507 : * create table student (gpa float8) inherits (person);
2508 : * create table stud_emp (percent int4) inherits (emp, student);
2509 : *
2510 : * The order of the attributes of stud_emp is:
2511 : *
2512 : * person {1:name, 2:age, 3:location}
2513 : * / \
2514 : * {6:gpa} student emp {4:salary, 5:manager}
2515 : * \ /
2516 : * stud_emp {7:percent}
2517 : *
2518 : * If the same attribute name appears multiple times, then it appears
2519 : * in the result table in the proper location for its first appearance.
2520 : *
2521 : * Constraints (including not-null constraints) for the child table
2522 : * are the union of all relevant constraints, from both the child schema
2523 : * and parent tables. In addition, in legacy inheritance, each column that
2524 : * appears in a primary key in any of the parents also gets a NOT NULL
2525 : * constraint (partitioning doesn't need this, because the PK itself gets
2526 : * inherited.)
2527 : *
2528 : * The default value for a child column is defined as:
2529 : * (1) If the child schema specifies a default, that value is used.
2530 : * (2) If neither the child nor any parent specifies a default, then
2531 : * the column will not have a default.
2532 : * (3) If conflicting defaults are inherited from different parents
2533 : * (and not overridden by the child), an error is raised.
2534 : * (4) Otherwise the inherited default is used.
2535 : *
2536 : * Note that the default-value infrastructure is used for generated
2537 : * columns' expressions too, so most of the preceding paragraph applies
2538 : * to generation expressions too. We insist that a child column be
2539 : * generated if and only if its parent(s) are, but it need not have
2540 : * the same generation expression.
2541 : *----------
2542 : */
2543 : static List *
2544 65224 : MergeAttributes(List *columns, const List *supers, char relpersistence,
2545 : bool is_partition, List **supconstr, List **supnotnulls)
2546 : {
2547 65224 : List *inh_columns = NIL;
2548 65224 : List *constraints = NIL;
2549 65224 : List *nnconstraints = NIL;
2550 65224 : bool have_bogus_defaults = false;
2551 : int child_attno;
2552 : static Node bogus_marker = {0}; /* marks conflicting defaults */
2553 65224 : List *saved_columns = NIL;
2554 : ListCell *lc;
2555 :
2556 : /*
2557 : * Check for and reject tables with too many columns. We perform this
2558 : * check relatively early for two reasons: (a) we don't run the risk of
2559 : * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2560 : * okay if we're processing <= 1600 columns, but could take minutes to
2561 : * execute if the user attempts to create a table with hundreds of
2562 : * thousands of columns.
2563 : *
2564 : * Note that we also need to check that we do not exceed this figure after
2565 : * including columns from inherited relations.
2566 : */
2567 65224 : if (list_length(columns) > MaxHeapAttributeNumber)
2568 0 : ereport(ERROR,
2569 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
2570 : errmsg("tables can have at most %d columns",
2571 : MaxHeapAttributeNumber)));
2572 :
2573 : /*
2574 : * Check for duplicate names in the explicit list of attributes.
2575 : *
2576 : * Although we might consider merging such entries in the same way that we
2577 : * handle name conflicts for inherited attributes, it seems to make more
2578 : * sense to assume such conflicts are errors.
2579 : *
2580 : * We don't use foreach() here because we have two nested loops over the
2581 : * columns list, with possible element deletions in the inner one. If we
2582 : * used foreach_delete_current() it could only fix up the state of one of
2583 : * the loops, so it seems cleaner to use looping over list indexes for
2584 : * both loops. Note that any deletion will happen beyond where the outer
2585 : * loop is, so its index never needs adjustment.
2586 : */
2587 307676 : for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2588 : {
2589 242476 : ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2590 :
2591 242476 : if (!is_partition && coldef->typeName == NULL)
2592 : {
2593 : /*
2594 : * Typed table column option that does not belong to a column from
2595 : * the type. This works because the columns from the type come
2596 : * first in the list. (We omit this check for partition column
2597 : * lists; those are processed separately below.)
2598 : */
2599 6 : ereport(ERROR,
2600 : (errcode(ERRCODE_UNDEFINED_COLUMN),
2601 : errmsg("column \"%s\" does not exist",
2602 : coldef->colname)));
2603 : }
2604 :
2605 : /* restpos scans all entries beyond coldef; incr is in loop body */
2606 6632380 : for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2607 : {
2608 6389928 : ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2609 :
2610 6389928 : if (strcmp(coldef->colname, restdef->colname) == 0)
2611 : {
2612 50 : if (coldef->is_from_type)
2613 : {
2614 : /*
2615 : * merge the column options into the column from the type
2616 : */
2617 32 : coldef->is_not_null = restdef->is_not_null;
2618 32 : coldef->raw_default = restdef->raw_default;
2619 32 : coldef->cooked_default = restdef->cooked_default;
2620 32 : coldef->constraints = restdef->constraints;
2621 32 : coldef->is_from_type = false;
2622 32 : columns = list_delete_nth_cell(columns, restpos);
2623 : }
2624 : else
2625 18 : ereport(ERROR,
2626 : (errcode(ERRCODE_DUPLICATE_COLUMN),
2627 : errmsg("column \"%s\" specified more than once",
2628 : coldef->colname)));
2629 : }
2630 : else
2631 6389878 : restpos++;
2632 : }
2633 : }
2634 :
2635 : /*
2636 : * In case of a partition, there are no new column definitions, only dummy
2637 : * ColumnDefs created for column constraints. Set them aside for now and
2638 : * process them at the end.
2639 : */
2640 65200 : if (is_partition)
2641 : {
2642 9120 : saved_columns = columns;
2643 9120 : columns = NIL;
2644 : }
2645 :
2646 : /*
2647 : * Scan the parents left-to-right, and merge their attributes to form a
2648 : * list of inherited columns (inh_columns).
2649 : */
2650 65200 : child_attno = 0;
2651 76750 : foreach(lc, supers)
2652 : {
2653 11634 : Oid parent = lfirst_oid(lc);
2654 : Relation relation;
2655 : TupleDesc tupleDesc;
2656 : TupleConstr *constr;
2657 : AttrMap *newattmap;
2658 : List *inherited_defaults;
2659 : List *cols_with_defaults;
2660 : List *nnconstrs;
2661 : ListCell *lc1;
2662 : ListCell *lc2;
2663 11634 : Bitmapset *nncols = NULL;
2664 :
2665 : /* caller already got lock */
2666 11634 : relation = table_open(parent, NoLock);
2667 :
2668 : /*
2669 : * Check for active uses of the parent partitioned table in the
2670 : * current transaction, such as being used in some manner by an
2671 : * enclosing command.
2672 : */
2673 11634 : if (is_partition)
2674 9120 : CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2675 :
2676 : /*
2677 : * We do not allow partitioned tables and partitions to participate in
2678 : * regular inheritance.
2679 : */
2680 11628 : if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2681 6 : ereport(ERROR,
2682 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2683 : errmsg("cannot inherit from partitioned table \"%s\"",
2684 : RelationGetRelationName(relation))));
2685 11622 : if (relation->rd_rel->relispartition && !is_partition)
2686 6 : ereport(ERROR,
2687 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2688 : errmsg("cannot inherit from partition \"%s\"",
2689 : RelationGetRelationName(relation))));
2690 :
2691 11616 : if (relation->rd_rel->relkind != RELKIND_RELATION &&
2692 9116 : relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2693 9096 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2694 0 : ereport(ERROR,
2695 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2696 : errmsg("inherited relation \"%s\" is not a table or foreign table",
2697 : RelationGetRelationName(relation))));
2698 :
2699 : /*
2700 : * If the parent is permanent, so must be all of its partitions. Note
2701 : * that inheritance allows that case.
2702 : */
2703 11616 : if (is_partition &&
2704 9114 : relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2705 : relpersistence == RELPERSISTENCE_TEMP)
2706 6 : ereport(ERROR,
2707 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2708 : errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2709 : RelationGetRelationName(relation))));
2710 :
2711 : /* Permanent rels cannot inherit from temporary ones */
2712 11610 : if (relpersistence != RELPERSISTENCE_TEMP &&
2713 11196 : relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2714 24 : ereport(ERROR,
2715 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2716 : errmsg(!is_partition
2717 : ? "cannot inherit from temporary relation \"%s\""
2718 : : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2719 : RelationGetRelationName(relation))));
2720 :
2721 : /* If existing rel is temp, it must belong to this session */
2722 11586 : if (RELATION_IS_OTHER_TEMP(relation))
2723 0 : ereport(ERROR,
2724 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2725 : errmsg(!is_partition
2726 : ? "cannot inherit from temporary relation of another session"
2727 : : "cannot create as partition of temporary relation of another session")));
2728 :
2729 : /*
2730 : * We should have an UNDER permission flag for this, but for now,
2731 : * demand that creator of a child table own the parent.
2732 : */
2733 11586 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2734 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2735 0 : RelationGetRelationName(relation));
2736 :
2737 11586 : tupleDesc = RelationGetDescr(relation);
2738 11586 : constr = tupleDesc->constr;
2739 :
2740 : /*
2741 : * newattmap->attnums[] will contain the child-table attribute numbers
2742 : * for the attributes of this parent table. (They are not the same
2743 : * for parents after the first one, nor if we have dropped columns.)
2744 : */
2745 11586 : newattmap = make_attrmap(tupleDesc->natts);
2746 :
2747 : /* We can't process inherited defaults until newattmap is complete. */
2748 11586 : inherited_defaults = cols_with_defaults = NIL;
2749 :
2750 : /*
2751 : * Request attnotnull on columns that have a not-null constraint
2752 : * that's not marked NO INHERIT (even if not valid).
2753 : */
2754 11586 : nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2755 : true, false);
2756 25788 : foreach_ptr(CookedConstraint, cc, nnconstrs)
2757 2616 : nncols = bms_add_member(nncols, cc->attnum);
2758 :
2759 35628 : for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2760 24042 : parent_attno++)
2761 : {
2762 24078 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2763 : parent_attno - 1);
2764 24078 : char *attributeName = NameStr(attribute->attname);
2765 : int exist_attno;
2766 : ColumnDef *newdef;
2767 : ColumnDef *mergeddef;
2768 :
2769 : /*
2770 : * Ignore dropped columns in the parent.
2771 : */
2772 24078 : if (attribute->attisdropped)
2773 198 : continue; /* leave newattmap->attnums entry as zero */
2774 :
2775 : /*
2776 : * Create new column definition
2777 : */
2778 23880 : newdef = makeColumnDef(attributeName, attribute->atttypid,
2779 : attribute->atttypmod, attribute->attcollation);
2780 23880 : newdef->storage = attribute->attstorage;
2781 23880 : newdef->generated = attribute->attgenerated;
2782 23880 : if (CompressionMethodIsValid(attribute->attcompression))
2783 36 : newdef->compression =
2784 36 : pstrdup(GetCompressionMethodName(attribute->attcompression));
2785 :
2786 : /*
2787 : * Regular inheritance children are independent enough not to
2788 : * inherit identity columns. But partitions are integral part of
2789 : * a partitioned table and inherit identity column.
2790 : */
2791 23880 : if (is_partition)
2792 19270 : newdef->identity = attribute->attidentity;
2793 :
2794 : /*
2795 : * Does it match some previously considered column from another
2796 : * parent?
2797 : */
2798 23880 : exist_attno = findAttrByName(attributeName, inh_columns);
2799 23880 : if (exist_attno > 0)
2800 : {
2801 : /*
2802 : * Yes, try to merge the two column definitions.
2803 : */
2804 370 : mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2805 :
2806 334 : newattmap->attnums[parent_attno - 1] = exist_attno;
2807 :
2808 : /*
2809 : * Partitions have only one parent, so conflict should never
2810 : * occur.
2811 : */
2812 : Assert(!is_partition);
2813 : }
2814 : else
2815 : {
2816 : /*
2817 : * No, create a new inherited column
2818 : */
2819 23510 : newdef->inhcount = 1;
2820 23510 : newdef->is_local = false;
2821 23510 : inh_columns = lappend(inh_columns, newdef);
2822 :
2823 23510 : newattmap->attnums[parent_attno - 1] = ++child_attno;
2824 23510 : mergeddef = newdef;
2825 : }
2826 :
2827 : /*
2828 : * mark attnotnull if parent has it
2829 : */
2830 23844 : if (bms_is_member(parent_attno, nncols))
2831 2616 : mergeddef->is_not_null = true;
2832 :
2833 : /*
2834 : * Locate default/generation expression if any
2835 : */
2836 23844 : if (attribute->atthasdef)
2837 : {
2838 : Node *this_default;
2839 :
2840 854 : this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2841 854 : if (this_default == NULL)
2842 0 : elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2843 : parent_attno, RelationGetRelationName(relation));
2844 :
2845 : /*
2846 : * If it's a GENERATED default, it might contain Vars that
2847 : * need to be mapped to the inherited column(s)' new numbers.
2848 : * We can't do that till newattmap is ready, so just remember
2849 : * all the inherited default expressions for the moment.
2850 : */
2851 854 : inherited_defaults = lappend(inherited_defaults, this_default);
2852 854 : cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2853 : }
2854 : }
2855 :
2856 : /*
2857 : * Now process any inherited default expressions, adjusting attnos
2858 : * using the completed newattmap map.
2859 : */
2860 12404 : forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2861 : {
2862 854 : Node *this_default = (Node *) lfirst(lc1);
2863 854 : ColumnDef *def = (ColumnDef *) lfirst(lc2);
2864 : bool found_whole_row;
2865 :
2866 : /* Adjust Vars to match new table's column numbering */
2867 854 : this_default = map_variable_attnos(this_default,
2868 : 1, 0,
2869 : newattmap,
2870 : InvalidOid, &found_whole_row);
2871 :
2872 : /*
2873 : * For the moment we have to reject whole-row variables. We could
2874 : * convert them, if we knew the new table's rowtype OID, but that
2875 : * hasn't been assigned yet. (A variable could only appear in a
2876 : * generation expression, so the error message is correct.)
2877 : */
2878 854 : if (found_whole_row)
2879 0 : ereport(ERROR,
2880 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2881 : errmsg("cannot convert whole-row table reference"),
2882 : errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2883 : def->colname,
2884 : RelationGetRelationName(relation))));
2885 :
2886 : /*
2887 : * If we already had a default from some prior parent, check to
2888 : * see if they are the same. If so, no problem; if not, mark the
2889 : * column as having a bogus default. Below, we will complain if
2890 : * the bogus default isn't overridden by the child columns.
2891 : */
2892 : Assert(def->raw_default == NULL);
2893 854 : if (def->cooked_default == NULL)
2894 812 : def->cooked_default = this_default;
2895 42 : else if (!equal(def->cooked_default, this_default))
2896 : {
2897 36 : def->cooked_default = &bogus_marker;
2898 36 : have_bogus_defaults = true;
2899 : }
2900 : }
2901 :
2902 : /*
2903 : * Now copy the CHECK constraints of this parent, adjusting attnos
2904 : * using the completed newattmap map. Identically named constraints
2905 : * are merged if possible, else we throw error.
2906 : */
2907 11550 : if (constr && constr->num_check > 0)
2908 : {
2909 352 : ConstrCheck *check = constr->check;
2910 :
2911 1106 : for (int i = 0; i < constr->num_check; i++)
2912 : {
2913 754 : char *name = check[i].ccname;
2914 : Node *expr;
2915 : bool found_whole_row;
2916 :
2917 : /* ignore if the constraint is non-inheritable */
2918 754 : if (check[i].ccnoinherit)
2919 48 : continue;
2920 :
2921 : /* Adjust Vars to match new table's column numbering */
2922 706 : expr = map_variable_attnos(stringToNode(check[i].ccbin),
2923 : 1, 0,
2924 : newattmap,
2925 : InvalidOid, &found_whole_row);
2926 :
2927 : /*
2928 : * For the moment we have to reject whole-row variables. We
2929 : * could convert them, if we knew the new table's rowtype OID,
2930 : * but that hasn't been assigned yet.
2931 : */
2932 706 : if (found_whole_row)
2933 0 : ereport(ERROR,
2934 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2935 : errmsg("cannot convert whole-row table reference"),
2936 : errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2937 : name,
2938 : RelationGetRelationName(relation))));
2939 :
2940 706 : constraints = MergeCheckConstraint(constraints, name, expr,
2941 706 : check[i].ccenforced);
2942 : }
2943 : }
2944 :
2945 : /*
2946 : * Also copy the not-null constraints from this parent. The
2947 : * attnotnull markings were already installed above.
2948 : */
2949 25716 : foreach_ptr(CookedConstraint, nn, nnconstrs)
2950 : {
2951 : Assert(nn->contype == CONSTR_NOTNULL);
2952 :
2953 2616 : nn->attnum = newattmap->attnums[nn->attnum - 1];
2954 :
2955 2616 : nnconstraints = lappend(nnconstraints, nn);
2956 : }
2957 :
2958 11550 : free_attrmap(newattmap);
2959 :
2960 : /*
2961 : * Close the parent rel, but keep our lock on it until xact commit.
2962 : * That will prevent someone else from deleting or ALTERing the parent
2963 : * before the child is committed.
2964 : */
2965 11550 : table_close(relation, NoLock);
2966 : }
2967 :
2968 : /*
2969 : * If we had no inherited attributes, the result columns are just the
2970 : * explicitly declared columns. Otherwise, we need to merge the declared
2971 : * columns into the inherited column list. Although, we never have any
2972 : * explicitly declared columns if the table is a partition.
2973 : */
2974 65116 : if (inh_columns != NIL)
2975 : {
2976 11114 : int newcol_attno = 0;
2977 :
2978 12088 : foreach(lc, columns)
2979 : {
2980 1052 : ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2981 1052 : char *attributeName = newdef->colname;
2982 : int exist_attno;
2983 :
2984 : /*
2985 : * Partitions have only one parent and have no column definitions
2986 : * of their own, so conflict should never occur.
2987 : */
2988 : Assert(!is_partition);
2989 :
2990 1052 : newcol_attno++;
2991 :
2992 : /*
2993 : * Does it match some inherited column?
2994 : */
2995 1052 : exist_attno = findAttrByName(attributeName, inh_columns);
2996 1052 : if (exist_attno > 0)
2997 : {
2998 : /*
2999 : * Yes, try to merge the two column definitions.
3000 : */
3001 380 : MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
3002 : }
3003 : else
3004 : {
3005 : /*
3006 : * No, attach new column unchanged to result columns.
3007 : */
3008 672 : inh_columns = lappend(inh_columns, newdef);
3009 : }
3010 : }
3011 :
3012 11036 : columns = inh_columns;
3013 :
3014 : /*
3015 : * Check that we haven't exceeded the legal # of columns after merging
3016 : * in inherited columns.
3017 : */
3018 11036 : if (list_length(columns) > MaxHeapAttributeNumber)
3019 0 : ereport(ERROR,
3020 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
3021 : errmsg("tables can have at most %d columns",
3022 : MaxHeapAttributeNumber)));
3023 : }
3024 :
3025 : /*
3026 : * Now that we have the column definition list for a partition, we can
3027 : * check whether the columns referenced in the column constraint specs
3028 : * actually exist. Also, merge column defaults.
3029 : */
3030 65038 : if (is_partition)
3031 : {
3032 9308 : foreach(lc, saved_columns)
3033 : {
3034 254 : ColumnDef *restdef = lfirst(lc);
3035 254 : bool found = false;
3036 : ListCell *l;
3037 :
3038 960 : foreach(l, columns)
3039 : {
3040 742 : ColumnDef *coldef = lfirst(l);
3041 :
3042 742 : if (strcmp(coldef->colname, restdef->colname) == 0)
3043 : {
3044 254 : found = true;
3045 :
3046 : /*
3047 : * Check for conflicts related to generated columns.
3048 : *
3049 : * Same rules as above: generated-ness has to match the
3050 : * parent, but the contents of the generation expression
3051 : * can be different.
3052 : */
3053 254 : if (coldef->generated)
3054 : {
3055 146 : if (restdef->raw_default && !restdef->generated)
3056 12 : ereport(ERROR,
3057 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3058 : errmsg("column \"%s\" inherits from generated column but specifies default",
3059 : restdef->colname)));
3060 134 : if (restdef->identity)
3061 0 : ereport(ERROR,
3062 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3063 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3064 : restdef->colname)));
3065 : }
3066 : else
3067 : {
3068 108 : if (restdef->generated)
3069 12 : ereport(ERROR,
3070 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3071 : errmsg("child column \"%s\" specifies generation expression",
3072 : restdef->colname),
3073 : errhint("A child table column cannot be generated unless its parent column is.")));
3074 : }
3075 :
3076 230 : if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3077 12 : ereport(ERROR,
3078 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3079 : errmsg("column \"%s\" inherits from generated column of different kind",
3080 : restdef->colname),
3081 : errdetail("Parent column is %s, child column is %s.",
3082 : coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3083 : restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3084 :
3085 : /*
3086 : * Override the parent's default value for this column
3087 : * (coldef->cooked_default) with the partition's local
3088 : * definition (restdef->raw_default), if there's one. It
3089 : * should be physically impossible to get a cooked default
3090 : * in the local definition or a raw default in the
3091 : * inherited definition, but make sure they're nulls, for
3092 : * future-proofing.
3093 : */
3094 : Assert(restdef->cooked_default == NULL);
3095 : Assert(coldef->raw_default == NULL);
3096 218 : if (restdef->raw_default)
3097 : {
3098 146 : coldef->raw_default = restdef->raw_default;
3099 146 : coldef->cooked_default = NULL;
3100 : }
3101 : }
3102 : }
3103 :
3104 : /* complain for constraints on columns not in parent */
3105 218 : if (!found)
3106 0 : ereport(ERROR,
3107 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3108 : errmsg("column \"%s\" does not exist",
3109 : restdef->colname)));
3110 : }
3111 : }
3112 :
3113 : /*
3114 : * If we found any conflicting parent default values, check to make sure
3115 : * they were overridden by the child.
3116 : */
3117 65002 : if (have_bogus_defaults)
3118 : {
3119 90 : foreach(lc, columns)
3120 : {
3121 72 : ColumnDef *def = lfirst(lc);
3122 :
3123 72 : if (def->cooked_default == &bogus_marker)
3124 : {
3125 18 : if (def->generated)
3126 12 : ereport(ERROR,
3127 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3128 : errmsg("column \"%s\" inherits conflicting generation expressions",
3129 : def->colname),
3130 : errhint("To resolve the conflict, specify a generation expression explicitly.")));
3131 : else
3132 6 : ereport(ERROR,
3133 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3134 : errmsg("column \"%s\" inherits conflicting default values",
3135 : def->colname),
3136 : errhint("To resolve the conflict, specify a default explicitly.")));
3137 : }
3138 : }
3139 : }
3140 :
3141 64984 : *supconstr = constraints;
3142 64984 : *supnotnulls = nnconstraints;
3143 :
3144 64984 : return columns;
3145 : }
3146 :
3147 :
3148 : /*
3149 : * MergeCheckConstraint
3150 : * Try to merge an inherited CHECK constraint with previous ones
3151 : *
3152 : * If we inherit identically-named constraints from multiple parents, we must
3153 : * merge them, or throw an error if they don't have identical definitions.
3154 : *
3155 : * constraints is a list of CookedConstraint structs for previous constraints.
3156 : *
3157 : * If the new constraint matches an existing one, then the existing
3158 : * constraint's inheritance count is updated. If there is a conflict (same
3159 : * name but different expression), throw an error. If the constraint neither
3160 : * matches nor conflicts with an existing one, a new constraint is appended to
3161 : * the list.
3162 : */
3163 : static List *
3164 706 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3165 : {
3166 : ListCell *lc;
3167 : CookedConstraint *newcon;
3168 :
3169 2230 : foreach(lc, constraints)
3170 : {
3171 1674 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3172 :
3173 : Assert(ccon->contype == CONSTR_CHECK);
3174 :
3175 : /* Non-matching names never conflict */
3176 1674 : if (strcmp(ccon->name, name) != 0)
3177 1524 : continue;
3178 :
3179 150 : if (equal(expr, ccon->expr))
3180 : {
3181 : /* OK to merge constraint with existing */
3182 150 : if (pg_add_s16_overflow(ccon->inhcount, 1,
3183 : &ccon->inhcount))
3184 0 : ereport(ERROR,
3185 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3186 : errmsg("too many inheritance parents"));
3187 :
3188 : /*
3189 : * When enforceability differs, the merged constraint should be
3190 : * marked as ENFORCED because one of the parents is ENFORCED.
3191 : */
3192 150 : if (!ccon->is_enforced && is_enforced)
3193 : {
3194 48 : ccon->is_enforced = true;
3195 48 : ccon->skip_validation = false;
3196 : }
3197 :
3198 150 : return constraints;
3199 : }
3200 :
3201 0 : ereport(ERROR,
3202 : (errcode(ERRCODE_DUPLICATE_OBJECT),
3203 : errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3204 : name)));
3205 : }
3206 :
3207 : /*
3208 : * Constraint couldn't be merged with an existing one and also didn't
3209 : * conflict with an existing one, so add it as a new one to the list.
3210 : */
3211 556 : newcon = palloc0_object(CookedConstraint);
3212 556 : newcon->contype = CONSTR_CHECK;
3213 556 : newcon->name = pstrdup(name);
3214 556 : newcon->expr = expr;
3215 556 : newcon->inhcount = 1;
3216 556 : newcon->is_enforced = is_enforced;
3217 556 : newcon->skip_validation = !is_enforced;
3218 556 : return lappend(constraints, newcon);
3219 : }
3220 :
3221 : /*
3222 : * MergeChildAttribute
3223 : * Merge given child attribute definition into given inherited attribute.
3224 : *
3225 : * Input arguments:
3226 : * 'inh_columns' is the list of inherited ColumnDefs.
3227 : * 'exist_attno' is the number of the inherited attribute in inh_columns
3228 : * 'newcol_attno' is the attribute number in child table's schema definition
3229 : * 'newdef' is the column/attribute definition from the child table.
3230 : *
3231 : * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3232 : * ColumnDef remains unchanged.
3233 : *
3234 : * Notes:
3235 : * - The attribute is merged according to the rules laid out in the prologue
3236 : * of MergeAttributes().
3237 : * - If matching inherited attribute exists but the child attribute can not be
3238 : * merged into it, the function throws respective errors.
3239 : * - A partition can not have its own column definitions. Hence this function
3240 : * is applicable only to a regular inheritance child.
3241 : */
3242 : static void
3243 380 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3244 : {
3245 380 : char *attributeName = newdef->colname;
3246 : ColumnDef *inhdef;
3247 : Oid inhtypeid,
3248 : newtypeid;
3249 : int32 inhtypmod,
3250 : newtypmod;
3251 : Oid inhcollid,
3252 : newcollid;
3253 :
3254 380 : if (exist_attno == newcol_attno)
3255 346 : ereport(NOTICE,
3256 : (errmsg("merging column \"%s\" with inherited definition",
3257 : attributeName)));
3258 : else
3259 34 : ereport(NOTICE,
3260 : (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3261 : errdetail("User-specified column moved to the position of the inherited column.")));
3262 :
3263 380 : inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3264 :
3265 : /*
3266 : * Must have the same type and typmod
3267 : */
3268 380 : typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3269 380 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3270 380 : if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3271 12 : ereport(ERROR,
3272 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3273 : errmsg("column \"%s\" has a type conflict",
3274 : attributeName),
3275 : errdetail("%s versus %s",
3276 : format_type_with_typemod(inhtypeid, inhtypmod),
3277 : format_type_with_typemod(newtypeid, newtypmod))));
3278 :
3279 : /*
3280 : * Must have the same collation
3281 : */
3282 368 : inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3283 368 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3284 368 : if (inhcollid != newcollid)
3285 6 : ereport(ERROR,
3286 : (errcode(ERRCODE_COLLATION_MISMATCH),
3287 : errmsg("column \"%s\" has a collation conflict",
3288 : attributeName),
3289 : errdetail("\"%s\" versus \"%s\"",
3290 : get_collation_name(inhcollid),
3291 : get_collation_name(newcollid))));
3292 :
3293 : /*
3294 : * Identity is never inherited by a regular inheritance child. Pick
3295 : * child's identity definition if there's one.
3296 : */
3297 362 : inhdef->identity = newdef->identity;
3298 :
3299 : /*
3300 : * Copy storage parameter
3301 : */
3302 362 : if (inhdef->storage == 0)
3303 0 : inhdef->storage = newdef->storage;
3304 362 : else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3305 6 : ereport(ERROR,
3306 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3307 : errmsg("column \"%s\" has a storage parameter conflict",
3308 : attributeName),
3309 : errdetail("%s versus %s",
3310 : storage_name(inhdef->storage),
3311 : storage_name(newdef->storage))));
3312 :
3313 : /*
3314 : * Copy compression parameter
3315 : */
3316 356 : if (inhdef->compression == NULL)
3317 350 : inhdef->compression = newdef->compression;
3318 6 : else if (newdef->compression != NULL)
3319 : {
3320 6 : if (strcmp(inhdef->compression, newdef->compression) != 0)
3321 6 : ereport(ERROR,
3322 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3323 : errmsg("column \"%s\" has a compression method conflict",
3324 : attributeName),
3325 : errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3326 : }
3327 :
3328 : /*
3329 : * Merge of not-null constraints = OR 'em together
3330 : */
3331 350 : inhdef->is_not_null |= newdef->is_not_null;
3332 :
3333 : /*
3334 : * Check for conflicts related to generated columns.
3335 : *
3336 : * If the parent column is generated, the child column will be made a
3337 : * generated column if it isn't already. If it is a generated column,
3338 : * we'll take its generation expression in preference to the parent's. We
3339 : * must check that the child column doesn't specify a default value or
3340 : * identity, which matches the rules for a single column in
3341 : * parse_utilcmd.c.
3342 : *
3343 : * Conversely, if the parent column is not generated, the child column
3344 : * can't be either. (We used to allow that, but it results in being able
3345 : * to override the generation expression via UPDATEs through the parent.)
3346 : */
3347 350 : if (inhdef->generated)
3348 : {
3349 62 : if (newdef->raw_default && !newdef->generated)
3350 12 : ereport(ERROR,
3351 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3352 : errmsg("column \"%s\" inherits from generated column but specifies default",
3353 : inhdef->colname)));
3354 50 : if (newdef->identity)
3355 12 : ereport(ERROR,
3356 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3357 : errmsg("column \"%s\" inherits from generated column but specifies identity",
3358 : inhdef->colname)));
3359 : }
3360 : else
3361 : {
3362 288 : if (newdef->generated)
3363 12 : ereport(ERROR,
3364 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3365 : errmsg("child column \"%s\" specifies generation expression",
3366 : inhdef->colname),
3367 : errhint("A child table column cannot be generated unless its parent column is.")));
3368 : }
3369 :
3370 314 : if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3371 12 : ereport(ERROR,
3372 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3373 : errmsg("column \"%s\" inherits from generated column of different kind",
3374 : inhdef->colname),
3375 : errdetail("Parent column is %s, child column is %s.",
3376 : inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3377 : newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3378 :
3379 : /*
3380 : * If new def has a default, override previous default
3381 : */
3382 302 : if (newdef->raw_default != NULL)
3383 : {
3384 30 : inhdef->raw_default = newdef->raw_default;
3385 30 : inhdef->cooked_default = newdef->cooked_default;
3386 : }
3387 :
3388 : /* Mark the column as locally defined */
3389 302 : inhdef->is_local = true;
3390 302 : }
3391 :
3392 : /*
3393 : * MergeInheritedAttribute
3394 : * Merge given parent attribute definition into specified attribute
3395 : * inherited from the previous parents.
3396 : *
3397 : * Input arguments:
3398 : * 'inh_columns' is the list of previously inherited ColumnDefs.
3399 : * 'exist_attno' is the number the existing matching attribute in inh_columns.
3400 : * 'newdef' is the new parent column/attribute definition to be merged.
3401 : *
3402 : * The matching ColumnDef in 'inh_columns' list is modified and returned.
3403 : *
3404 : * Notes:
3405 : * - The attribute is merged according to the rules laid out in the prologue
3406 : * of MergeAttributes().
3407 : * - If matching inherited attribute exists but the new attribute can not be
3408 : * merged into it, the function throws respective errors.
3409 : * - A partition inherits from only a single parent. Hence this function is
3410 : * applicable only to a regular inheritance.
3411 : */
3412 : static ColumnDef *
3413 370 : MergeInheritedAttribute(List *inh_columns,
3414 : int exist_attno,
3415 : const ColumnDef *newdef)
3416 : {
3417 370 : char *attributeName = newdef->colname;
3418 : ColumnDef *prevdef;
3419 : Oid prevtypeid,
3420 : newtypeid;
3421 : int32 prevtypmod,
3422 : newtypmod;
3423 : Oid prevcollid,
3424 : newcollid;
3425 :
3426 370 : ereport(NOTICE,
3427 : (errmsg("merging multiple inherited definitions of column \"%s\"",
3428 : attributeName)));
3429 370 : prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3430 :
3431 : /*
3432 : * Must have the same type and typmod
3433 : */
3434 370 : typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3435 370 : typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3436 370 : if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3437 0 : ereport(ERROR,
3438 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3439 : errmsg("inherited column \"%s\" has a type conflict",
3440 : attributeName),
3441 : errdetail("%s versus %s",
3442 : format_type_with_typemod(prevtypeid, prevtypmod),
3443 : format_type_with_typemod(newtypeid, newtypmod))));
3444 :
3445 : /*
3446 : * Must have the same collation
3447 : */
3448 370 : prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3449 370 : newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3450 370 : if (prevcollid != newcollid)
3451 0 : ereport(ERROR,
3452 : (errcode(ERRCODE_COLLATION_MISMATCH),
3453 : errmsg("inherited column \"%s\" has a collation conflict",
3454 : attributeName),
3455 : errdetail("\"%s\" versus \"%s\"",
3456 : get_collation_name(prevcollid),
3457 : get_collation_name(newcollid))));
3458 :
3459 : /*
3460 : * Copy/check storage parameter
3461 : */
3462 370 : if (prevdef->storage == 0)
3463 0 : prevdef->storage = newdef->storage;
3464 370 : else if (prevdef->storage != newdef->storage)
3465 6 : ereport(ERROR,
3466 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3467 : errmsg("inherited column \"%s\" has a storage parameter conflict",
3468 : attributeName),
3469 : errdetail("%s versus %s",
3470 : storage_name(prevdef->storage),
3471 : storage_name(newdef->storage))));
3472 :
3473 : /*
3474 : * Copy/check compression parameter
3475 : */
3476 364 : if (prevdef->compression == NULL)
3477 346 : prevdef->compression = newdef->compression;
3478 18 : else if (newdef->compression != NULL)
3479 : {
3480 6 : if (strcmp(prevdef->compression, newdef->compression) != 0)
3481 6 : ereport(ERROR,
3482 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3483 : errmsg("column \"%s\" has a compression method conflict",
3484 : attributeName),
3485 : errdetail("%s versus %s",
3486 : prevdef->compression, newdef->compression)));
3487 : }
3488 :
3489 : /*
3490 : * Check for GENERATED conflicts
3491 : */
3492 358 : if (prevdef->generated != newdef->generated)
3493 24 : ereport(ERROR,
3494 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3495 : errmsg("inherited column \"%s\" has a generation conflict",
3496 : attributeName)));
3497 :
3498 : /*
3499 : * Default and other constraints are handled by the caller.
3500 : */
3501 :
3502 334 : if (pg_add_s16_overflow(prevdef->inhcount, 1,
3503 : &prevdef->inhcount))
3504 0 : ereport(ERROR,
3505 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3506 : errmsg("too many inheritance parents"));
3507 :
3508 334 : return prevdef;
3509 : }
3510 :
3511 : /*
3512 : * StoreCatalogInheritance
3513 : * Updates the system catalogs with proper inheritance information.
3514 : *
3515 : * supers is a list of the OIDs of the new relation's direct ancestors.
3516 : */
3517 : static void
3518 64324 : StoreCatalogInheritance(Oid relationId, List *supers,
3519 : bool child_is_partition)
3520 : {
3521 : Relation relation;
3522 : int32 seqNumber;
3523 : ListCell *entry;
3524 :
3525 : /*
3526 : * sanity checks
3527 : */
3528 : Assert(OidIsValid(relationId));
3529 :
3530 64324 : if (supers == NIL)
3531 53648 : return;
3532 :
3533 : /*
3534 : * Store INHERITS information in pg_inherits using direct ancestors only.
3535 : * Also enter dependencies on the direct ancestors, and make sure they are
3536 : * marked with relhassubclass = true.
3537 : *
3538 : * (Once upon a time, both direct and indirect ancestors were found here
3539 : * and then entered into pg_ipl. Since that catalog doesn't exist
3540 : * anymore, there's no need to look for indirect ancestors.)
3541 : */
3542 10676 : relation = table_open(InheritsRelationId, RowExclusiveLock);
3543 :
3544 10676 : seqNumber = 1;
3545 21686 : foreach(entry, supers)
3546 : {
3547 11010 : Oid parentOid = lfirst_oid(entry);
3548 :
3549 11010 : StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3550 : child_is_partition);
3551 11010 : seqNumber++;
3552 : }
3553 :
3554 10676 : table_close(relation, RowExclusiveLock);
3555 : }
3556 :
3557 : /*
3558 : * Make catalog entries showing relationId as being an inheritance child
3559 : * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3560 : */
3561 : static void
3562 14244 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3563 : int32 seqNumber, Relation inhRelation,
3564 : bool child_is_partition)
3565 : {
3566 : ObjectAddress childobject,
3567 : parentobject;
3568 :
3569 : /* store the pg_inherits row */
3570 14244 : StoreSingleInheritance(relationId, parentOid, seqNumber);
3571 :
3572 : /*
3573 : * Store a dependency too
3574 : */
3575 14244 : parentobject.classId = RelationRelationId;
3576 14244 : parentobject.objectId = parentOid;
3577 14244 : parentobject.objectSubId = 0;
3578 14244 : childobject.classId = RelationRelationId;
3579 14244 : childobject.objectId = relationId;
3580 14244 : childobject.objectSubId = 0;
3581 :
3582 14244 : recordDependencyOn(&childobject, &parentobject,
3583 : child_dependency_type(child_is_partition));
3584 :
3585 : /*
3586 : * Post creation hook of this inheritance. Since object_access_hook
3587 : * doesn't take multiple object identifiers, we relay oid of parent
3588 : * relation using auxiliary_id argument.
3589 : */
3590 14244 : InvokeObjectPostAlterHookArg(InheritsRelationId,
3591 : relationId, 0,
3592 : parentOid, false);
3593 :
3594 : /*
3595 : * Mark the parent as having subclasses.
3596 : */
3597 14244 : SetRelationHasSubclass(parentOid, true);
3598 14244 : }
3599 :
3600 : /*
3601 : * Look for an existing column entry with the given name.
3602 : *
3603 : * Returns the index (starting with 1) if attribute already exists in columns,
3604 : * 0 if it doesn't.
3605 : */
3606 : static int
3607 24932 : findAttrByName(const char *attributeName, const List *columns)
3608 : {
3609 : ListCell *lc;
3610 24932 : int i = 1;
3611 :
3612 45936 : foreach(lc, columns)
3613 : {
3614 21754 : if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3615 750 : return i;
3616 :
3617 21004 : i++;
3618 : }
3619 24182 : return 0;
3620 : }
3621 :
3622 :
3623 : /*
3624 : * SetRelationHasSubclass
3625 : * Set the value of the relation's relhassubclass field in pg_class.
3626 : *
3627 : * It's always safe to set this field to true, because all SQL commands are
3628 : * ready to see true and then find no children. On the other hand, commands
3629 : * generally assume zero children if this is false.
3630 : *
3631 : * Caller must hold any self-exclusive lock until end of transaction. If the
3632 : * new value is false, caller must have acquired that lock before reading the
3633 : * evidence that justified the false value. That way, it properly waits if
3634 : * another backend is simultaneously concluding no need to change the tuple
3635 : * (new and old values are true).
3636 : *
3637 : * NOTE: an important side-effect of this operation is that an SI invalidation
3638 : * message is sent out to all backends --- including me --- causing plans
3639 : * referencing the relation to be rebuilt with the new list of children.
3640 : * This must happen even if we find that no change is needed in the pg_class
3641 : * row.
3642 : */
3643 : void
3644 17984 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3645 : {
3646 : Relation relationRelation;
3647 : HeapTuple tuple;
3648 : Form_pg_class classtuple;
3649 :
3650 : Assert(CheckRelationOidLockedByMe(relationId,
3651 : ShareUpdateExclusiveLock, false) ||
3652 : CheckRelationOidLockedByMe(relationId,
3653 : ShareRowExclusiveLock, true));
3654 :
3655 : /*
3656 : * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3657 : */
3658 17984 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3659 17984 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3660 17984 : if (!HeapTupleIsValid(tuple))
3661 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
3662 17984 : classtuple = (Form_pg_class) GETSTRUCT(tuple);
3663 :
3664 17984 : if (classtuple->relhassubclass != relhassubclass)
3665 : {
3666 8492 : classtuple->relhassubclass = relhassubclass;
3667 8492 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3668 : }
3669 : else
3670 : {
3671 : /* no need to change tuple, but force relcache rebuild anyway */
3672 9492 : CacheInvalidateRelcacheByTuple(tuple);
3673 : }
3674 :
3675 17984 : heap_freetuple(tuple);
3676 17984 : table_close(relationRelation, RowExclusiveLock);
3677 17984 : }
3678 :
3679 : /*
3680 : * CheckRelationTableSpaceMove
3681 : * Check if relation can be moved to new tablespace.
3682 : *
3683 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3684 : *
3685 : * Returns true if the relation can be moved to the new tablespace; raises
3686 : * an error if it is not possible to do the move; returns false if the move
3687 : * would have no effect.
3688 : */
3689 : bool
3690 232 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3691 : {
3692 : Oid oldTableSpaceId;
3693 :
3694 : /*
3695 : * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3696 : * stored as 0.
3697 : */
3698 232 : oldTableSpaceId = rel->rd_rel->reltablespace;
3699 232 : if (newTableSpaceId == oldTableSpaceId ||
3700 224 : (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3701 16 : return false;
3702 :
3703 : /*
3704 : * We cannot support moving mapped relations into different tablespaces.
3705 : * (In particular this eliminates all shared catalogs.)
3706 : */
3707 216 : if (RelationIsMapped(rel))
3708 0 : ereport(ERROR,
3709 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3710 : errmsg("cannot move system relation \"%s\"",
3711 : RelationGetRelationName(rel))));
3712 :
3713 : /* Cannot move a non-shared relation into pg_global */
3714 216 : if (newTableSpaceId == GLOBALTABLESPACE_OID)
3715 12 : ereport(ERROR,
3716 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3717 : errmsg("only shared relations can be placed in pg_global tablespace")));
3718 :
3719 : /*
3720 : * Do not allow moving temp tables of other backends ... their local
3721 : * buffer manager is not going to cope.
3722 : */
3723 204 : if (RELATION_IS_OTHER_TEMP(rel))
3724 0 : ereport(ERROR,
3725 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3726 : errmsg("cannot move temporary tables of other sessions")));
3727 :
3728 204 : return true;
3729 : }
3730 :
3731 : /*
3732 : * SetRelationTableSpace
3733 : * Set new reltablespace and relfilenumber in pg_class entry.
3734 : *
3735 : * newTableSpaceId is the new tablespace for the relation, and
3736 : * newRelFilenumber its new filenumber. If newRelFilenumber is
3737 : * InvalidRelFileNumber, this field is not updated.
3738 : *
3739 : * NOTE: The caller must hold AccessExclusiveLock on the relation.
3740 : *
3741 : * The caller of this routine had better check if a relation can be
3742 : * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3743 : * first, and is responsible for making the change visible with
3744 : * CommandCounterIncrement().
3745 : */
3746 : void
3747 204 : SetRelationTableSpace(Relation rel,
3748 : Oid newTableSpaceId,
3749 : RelFileNumber newRelFilenumber)
3750 : {
3751 : Relation pg_class;
3752 : HeapTuple tuple;
3753 : ItemPointerData otid;
3754 : Form_pg_class rd_rel;
3755 204 : Oid reloid = RelationGetRelid(rel);
3756 :
3757 : Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3758 :
3759 : /* Get a modifiable copy of the relation's pg_class row. */
3760 204 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3761 :
3762 204 : tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3763 204 : if (!HeapTupleIsValid(tuple))
3764 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
3765 204 : otid = tuple->t_self;
3766 204 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3767 :
3768 : /* Update the pg_class row. */
3769 408 : rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3770 204 : InvalidOid : newTableSpaceId;
3771 204 : if (RelFileNumberIsValid(newRelFilenumber))
3772 160 : rd_rel->relfilenode = newRelFilenumber;
3773 204 : CatalogTupleUpdate(pg_class, &otid, tuple);
3774 204 : UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3775 :
3776 : /*
3777 : * Record dependency on tablespace. This is only required for relations
3778 : * that have no physical storage.
3779 : */
3780 204 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3781 30 : changeDependencyOnTablespace(RelationRelationId, reloid,
3782 : rd_rel->reltablespace);
3783 :
3784 204 : heap_freetuple(tuple);
3785 204 : table_close(pg_class, RowExclusiveLock);
3786 204 : }
3787 :
3788 : /*
3789 : * renameatt_check - basic sanity checks before attribute rename
3790 : */
3791 : static void
3792 1028 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3793 : {
3794 1028 : char relkind = classform->relkind;
3795 :
3796 1028 : if (classform->reloftype && !recursing)
3797 6 : ereport(ERROR,
3798 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3799 : errmsg("cannot rename column of typed table")));
3800 :
3801 : /*
3802 : * Renaming the columns of sequences or toast tables doesn't actually
3803 : * break anything from the system's point of view, since internal
3804 : * references are by attnum. But it doesn't seem right to allow users to
3805 : * change names that are hardcoded into the system, hence the following
3806 : * restriction.
3807 : */
3808 1022 : if (relkind != RELKIND_RELATION &&
3809 84 : relkind != RELKIND_VIEW &&
3810 84 : relkind != RELKIND_MATVIEW &&
3811 36 : relkind != RELKIND_COMPOSITE_TYPE &&
3812 36 : relkind != RELKIND_INDEX &&
3813 36 : relkind != RELKIND_PARTITIONED_INDEX &&
3814 0 : relkind != RELKIND_FOREIGN_TABLE &&
3815 : relkind != RELKIND_PARTITIONED_TABLE)
3816 0 : ereport(ERROR,
3817 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3818 : errmsg("cannot rename columns of relation \"%s\"",
3819 : NameStr(classform->relname)),
3820 : errdetail_relkind_not_supported(relkind)));
3821 :
3822 : /*
3823 : * permissions checking. only the owner of a class can change its schema.
3824 : */
3825 1022 : if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3826 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3827 0 : NameStr(classform->relname));
3828 1022 : if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3829 2 : ereport(ERROR,
3830 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3831 : errmsg("permission denied: \"%s\" is a system catalog",
3832 : NameStr(classform->relname))));
3833 1020 : }
3834 :
3835 : /*
3836 : * renameatt_internal - workhorse for renameatt
3837 : *
3838 : * Return value is the attribute number in the 'myrelid' relation.
3839 : */
3840 : static AttrNumber
3841 552 : renameatt_internal(Oid myrelid,
3842 : const char *oldattname,
3843 : const char *newattname,
3844 : bool recurse,
3845 : bool recursing,
3846 : int expected_parents,
3847 : DropBehavior behavior)
3848 : {
3849 : Relation targetrelation;
3850 : Relation attrelation;
3851 : HeapTuple atttup;
3852 : Form_pg_attribute attform;
3853 : AttrNumber attnum;
3854 :
3855 : /*
3856 : * Grab an exclusive lock on the target table, which we will NOT release
3857 : * until end of transaction.
3858 : */
3859 552 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
3860 552 : renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3861 :
3862 : /*
3863 : * if the 'recurse' flag is set then we are supposed to rename this
3864 : * attribute in all classes that inherit from 'relname' (as well as in
3865 : * 'relname').
3866 : *
3867 : * any permissions or problems with duplicate attributes will cause the
3868 : * whole transaction to abort, which is what we want -- all or nothing.
3869 : */
3870 552 : if (recurse)
3871 : {
3872 : List *child_oids,
3873 : *child_numparents;
3874 : ListCell *lo,
3875 : *li;
3876 :
3877 : /*
3878 : * we need the number of parents for each child so that the recursive
3879 : * calls to renameatt() can determine whether there are any parents
3880 : * outside the inheritance hierarchy being processed.
3881 : */
3882 248 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3883 : &child_numparents);
3884 :
3885 : /*
3886 : * find_all_inheritors does the recursive search of the inheritance
3887 : * hierarchy, so all we have to do is process all of the relids in the
3888 : * list that it returns.
3889 : */
3890 734 : forboth(lo, child_oids, li, child_numparents)
3891 : {
3892 516 : Oid childrelid = lfirst_oid(lo);
3893 516 : int numparents = lfirst_int(li);
3894 :
3895 516 : if (childrelid == myrelid)
3896 248 : continue;
3897 : /* note we need not recurse again */
3898 268 : renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3899 : }
3900 : }
3901 : else
3902 : {
3903 : /*
3904 : * If we are told not to recurse, there had better not be any child
3905 : * tables; else the rename would put them out of step.
3906 : *
3907 : * expected_parents will only be 0 if we are not already recursing.
3908 : */
3909 340 : if (expected_parents == 0 &&
3910 36 : find_inheritance_children(myrelid, NoLock) != NIL)
3911 12 : ereport(ERROR,
3912 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3913 : errmsg("inherited column \"%s\" must be renamed in child tables too",
3914 : oldattname)));
3915 : }
3916 :
3917 : /* rename attributes in typed tables of composite type */
3918 510 : if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3919 : {
3920 : List *child_oids;
3921 : ListCell *lo;
3922 :
3923 24 : child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3924 24 : RelationGetRelationName(targetrelation),
3925 : behavior);
3926 :
3927 24 : foreach(lo, child_oids)
3928 6 : renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3929 : }
3930 :
3931 504 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3932 :
3933 504 : atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3934 504 : if (!HeapTupleIsValid(atttup))
3935 24 : ereport(ERROR,
3936 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3937 : errmsg("column \"%s\" does not exist",
3938 : oldattname)));
3939 480 : attform = (Form_pg_attribute) GETSTRUCT(atttup);
3940 :
3941 480 : attnum = attform->attnum;
3942 480 : if (attnum <= 0)
3943 0 : ereport(ERROR,
3944 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3945 : errmsg("cannot rename system column \"%s\"",
3946 : oldattname)));
3947 :
3948 : /*
3949 : * if the attribute is inherited, forbid the renaming. if this is a
3950 : * top-level call to renameatt(), then expected_parents will be 0, so the
3951 : * effect of this code will be to prohibit the renaming if the attribute
3952 : * is inherited at all. if this is a recursive call to renameatt(),
3953 : * expected_parents will be the number of parents the current relation has
3954 : * within the inheritance hierarchy being processed, so we'll prohibit the
3955 : * renaming only if there are additional parents from elsewhere.
3956 : */
3957 480 : if (attform->attinhcount > expected_parents)
3958 30 : ereport(ERROR,
3959 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3960 : errmsg("cannot rename inherited column \"%s\"",
3961 : oldattname)));
3962 :
3963 : /* new name should not already exist */
3964 450 : (void) check_for_column_name_collision(targetrelation, newattname, false);
3965 :
3966 : /* apply the update */
3967 438 : namestrcpy(&(attform->attname), newattname);
3968 :
3969 438 : CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3970 :
3971 438 : InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3972 :
3973 438 : heap_freetuple(atttup);
3974 :
3975 438 : table_close(attrelation, RowExclusiveLock);
3976 :
3977 438 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
3978 :
3979 438 : return attnum;
3980 : }
3981 :
3982 : /*
3983 : * Perform permissions and integrity checks before acquiring a relation lock.
3984 : */
3985 : static void
3986 432 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3987 : void *arg)
3988 : {
3989 : HeapTuple tuple;
3990 : Form_pg_class form;
3991 :
3992 432 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3993 432 : if (!HeapTupleIsValid(tuple))
3994 40 : return; /* concurrently dropped */
3995 392 : form = (Form_pg_class) GETSTRUCT(tuple);
3996 392 : renameatt_check(relid, form, false);
3997 384 : ReleaseSysCache(tuple);
3998 : }
3999 :
4000 : /*
4001 : * renameatt - changes the name of an attribute in a relation
4002 : *
4003 : * The returned ObjectAddress is that of the renamed column.
4004 : */
4005 : ObjectAddress
4006 316 : renameatt(RenameStmt *stmt)
4007 : {
4008 : Oid relid;
4009 : AttrNumber attnum;
4010 : ObjectAddress address;
4011 :
4012 : /* lock level taken here should match renameatt_internal */
4013 316 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4014 316 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4015 : RangeVarCallbackForRenameAttribute,
4016 : NULL);
4017 :
4018 302 : if (!OidIsValid(relid))
4019 : {
4020 24 : ereport(NOTICE,
4021 : (errmsg("relation \"%s\" does not exist, skipping",
4022 : stmt->relation->relname)));
4023 24 : return InvalidObjectAddress;
4024 : }
4025 :
4026 : attnum =
4027 278 : renameatt_internal(relid,
4028 278 : stmt->subname, /* old att name */
4029 278 : stmt->newname, /* new att name */
4030 278 : stmt->relation->inh, /* recursive? */
4031 : false, /* recursing? */
4032 : 0, /* expected inhcount */
4033 : stmt->behavior);
4034 :
4035 194 : ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4036 :
4037 194 : return address;
4038 : }
4039 :
4040 : /*
4041 : * same logic as renameatt_internal
4042 : */
4043 : static ObjectAddress
4044 90 : rename_constraint_internal(Oid myrelid,
4045 : Oid mytypid,
4046 : const char *oldconname,
4047 : const char *newconname,
4048 : bool recurse,
4049 : bool recursing,
4050 : int expected_parents)
4051 : {
4052 90 : Relation targetrelation = NULL;
4053 : Oid constraintOid;
4054 : HeapTuple tuple;
4055 : Form_pg_constraint con;
4056 : ObjectAddress address;
4057 :
4058 : Assert(!myrelid || !mytypid);
4059 :
4060 90 : if (mytypid)
4061 : {
4062 6 : constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4063 : }
4064 : else
4065 : {
4066 84 : targetrelation = relation_open(myrelid, AccessExclusiveLock);
4067 :
4068 : /*
4069 : * don't tell it whether we're recursing; we allow changing typed
4070 : * tables here
4071 : */
4072 84 : renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4073 :
4074 84 : constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4075 : }
4076 :
4077 90 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4078 90 : if (!HeapTupleIsValid(tuple))
4079 0 : elog(ERROR, "cache lookup failed for constraint %u",
4080 : constraintOid);
4081 90 : con = (Form_pg_constraint) GETSTRUCT(tuple);
4082 :
4083 90 : if (myrelid &&
4084 84 : (con->contype == CONSTRAINT_CHECK ||
4085 24 : con->contype == CONSTRAINT_NOTNULL) &&
4086 66 : !con->connoinherit)
4087 : {
4088 54 : if (recurse)
4089 : {
4090 : List *child_oids,
4091 : *child_numparents;
4092 : ListCell *lo,
4093 : *li;
4094 :
4095 36 : child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4096 : &child_numparents);
4097 :
4098 84 : forboth(lo, child_oids, li, child_numparents)
4099 : {
4100 48 : Oid childrelid = lfirst_oid(lo);
4101 48 : int numparents = lfirst_int(li);
4102 :
4103 48 : if (childrelid == myrelid)
4104 36 : continue;
4105 :
4106 12 : rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4107 : }
4108 : }
4109 : else
4110 : {
4111 24 : if (expected_parents == 0 &&
4112 6 : find_inheritance_children(myrelid, NoLock) != NIL)
4113 6 : ereport(ERROR,
4114 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4115 : errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4116 : oldconname)));
4117 : }
4118 :
4119 48 : if (con->coninhcount > expected_parents)
4120 6 : ereport(ERROR,
4121 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4122 : errmsg("cannot rename inherited constraint \"%s\"",
4123 : oldconname)));
4124 : }
4125 :
4126 78 : if (con->conindid
4127 18 : && (con->contype == CONSTRAINT_PRIMARY
4128 6 : || con->contype == CONSTRAINT_UNIQUE
4129 0 : || con->contype == CONSTRAINT_EXCLUSION))
4130 : /* rename the index; this renames the constraint as well */
4131 18 : RenameRelationInternal(con->conindid, newconname, false, true);
4132 : else
4133 60 : RenameConstraintById(constraintOid, newconname);
4134 :
4135 78 : ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4136 :
4137 78 : ReleaseSysCache(tuple);
4138 :
4139 78 : if (targetrelation)
4140 : {
4141 : /*
4142 : * Invalidate relcache so as others can see the new constraint name.
4143 : */
4144 72 : CacheInvalidateRelcache(targetrelation);
4145 :
4146 72 : relation_close(targetrelation, NoLock); /* close rel but keep lock */
4147 : }
4148 :
4149 78 : return address;
4150 : }
4151 :
4152 : ObjectAddress
4153 84 : RenameConstraint(RenameStmt *stmt)
4154 : {
4155 84 : Oid relid = InvalidOid;
4156 84 : Oid typid = InvalidOid;
4157 :
4158 84 : if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4159 : {
4160 : Relation rel;
4161 : HeapTuple tup;
4162 :
4163 6 : typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4164 6 : rel = table_open(TypeRelationId, RowExclusiveLock);
4165 6 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4166 6 : if (!HeapTupleIsValid(tup))
4167 0 : elog(ERROR, "cache lookup failed for type %u", typid);
4168 6 : checkDomainOwner(tup);
4169 6 : ReleaseSysCache(tup);
4170 6 : table_close(rel, NoLock);
4171 : }
4172 : else
4173 : {
4174 : /* lock level taken here should match rename_constraint_internal */
4175 78 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4176 78 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4177 : RangeVarCallbackForRenameAttribute,
4178 : NULL);
4179 78 : if (!OidIsValid(relid))
4180 : {
4181 6 : ereport(NOTICE,
4182 : (errmsg("relation \"%s\" does not exist, skipping",
4183 : stmt->relation->relname)));
4184 6 : return InvalidObjectAddress;
4185 : }
4186 : }
4187 :
4188 : return
4189 78 : rename_constraint_internal(relid, typid,
4190 78 : stmt->subname,
4191 78 : stmt->newname,
4192 150 : (stmt->relation &&
4193 72 : stmt->relation->inh), /* recursive? */
4194 : false, /* recursing? */
4195 : 0 /* expected inhcount */ );
4196 : }
4197 :
4198 : /*
4199 : * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4200 : * RENAME
4201 : */
4202 : ObjectAddress
4203 512 : RenameRelation(RenameStmt *stmt)
4204 : {
4205 512 : bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4206 : Oid relid;
4207 : ObjectAddress address;
4208 :
4209 : /*
4210 : * Grab an exclusive lock on the target table, index, sequence, view,
4211 : * materialized view, or foreign table, which we will NOT release until
4212 : * end of transaction.
4213 : *
4214 : * Lock level used here should match RenameRelationInternal, to avoid lock
4215 : * escalation. However, because ALTER INDEX can be used with any relation
4216 : * type, we mustn't believe without verification.
4217 : */
4218 : for (;;)
4219 12 : {
4220 : LOCKMODE lockmode;
4221 : char relkind;
4222 : bool obj_is_index;
4223 :
4224 524 : lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4225 :
4226 524 : relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4227 524 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4228 : RangeVarCallbackForAlterRelation,
4229 : stmt);
4230 :
4231 474 : if (!OidIsValid(relid))
4232 : {
4233 18 : ereport(NOTICE,
4234 : (errmsg("relation \"%s\" does not exist, skipping",
4235 : stmt->relation->relname)));
4236 18 : return InvalidObjectAddress;
4237 : }
4238 :
4239 : /*
4240 : * We allow mismatched statement and object types (e.g., ALTER INDEX
4241 : * to rename a table), but we might've used the wrong lock level. If
4242 : * that happens, retry with the correct lock level. We don't bother
4243 : * if we already acquired AccessExclusiveLock with an index, however.
4244 : */
4245 456 : relkind = get_rel_relkind(relid);
4246 456 : obj_is_index = (relkind == RELKIND_INDEX ||
4247 : relkind == RELKIND_PARTITIONED_INDEX);
4248 456 : if (obj_is_index || is_index_stmt == obj_is_index)
4249 : break;
4250 :
4251 12 : UnlockRelationOid(relid, lockmode);
4252 12 : is_index_stmt = obj_is_index;
4253 : }
4254 :
4255 : /* Do the work */
4256 444 : RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4257 :
4258 432 : ObjectAddressSet(address, RelationRelationId, relid);
4259 :
4260 432 : return address;
4261 : }
4262 :
4263 : /*
4264 : * RenameRelationInternal - change the name of a relation
4265 : */
4266 : void
4267 1744 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4268 : {
4269 : Relation targetrelation;
4270 : Relation relrelation; /* for RELATION relation */
4271 : ItemPointerData otid;
4272 : HeapTuple reltup;
4273 : Form_pg_class relform;
4274 : Oid namespaceId;
4275 :
4276 : /*
4277 : * Grab a lock on the target relation, which we will NOT release until end
4278 : * of transaction. We need at least a self-exclusive lock so that
4279 : * concurrent DDL doesn't overwrite the rename if they start updating
4280 : * while still seeing the old version. The lock also guards against
4281 : * triggering relcache reloads in concurrent sessions, which might not
4282 : * handle this information changing under them. For indexes, we can use a
4283 : * reduced lock level because RelationReloadIndexInfo() handles indexes
4284 : * specially.
4285 : */
4286 1744 : targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4287 1744 : namespaceId = RelationGetNamespace(targetrelation);
4288 :
4289 : /*
4290 : * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4291 : */
4292 1744 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4293 :
4294 1744 : reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4295 1744 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4296 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4297 1744 : otid = reltup->t_self;
4298 1744 : relform = (Form_pg_class) GETSTRUCT(reltup);
4299 :
4300 1744 : if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4301 12 : ereport(ERROR,
4302 : (errcode(ERRCODE_DUPLICATE_TABLE),
4303 : errmsg("relation \"%s\" already exists",
4304 : newrelname)));
4305 :
4306 : /*
4307 : * RenameRelation is careful not to believe the caller's idea of the
4308 : * relation kind being handled. We don't have to worry about this, but
4309 : * let's not be totally oblivious to it. We can process an index as
4310 : * not-an-index, but not the other way around.
4311 : */
4312 : Assert(!is_index ||
4313 : is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4314 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4315 :
4316 : /*
4317 : * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4318 : * because it's a copy...)
4319 : */
4320 1732 : namestrcpy(&(relform->relname), newrelname);
4321 :
4322 1732 : CatalogTupleUpdate(relrelation, &otid, reltup);
4323 1732 : UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4324 :
4325 1732 : InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4326 : InvalidOid, is_internal);
4327 :
4328 1732 : heap_freetuple(reltup);
4329 1732 : table_close(relrelation, RowExclusiveLock);
4330 :
4331 : /*
4332 : * Also rename the associated type, if any.
4333 : */
4334 1732 : if (OidIsValid(targetrelation->rd_rel->reltype))
4335 190 : RenameTypeInternal(targetrelation->rd_rel->reltype,
4336 : newrelname, namespaceId);
4337 :
4338 : /*
4339 : * Also rename the associated constraint, if any.
4340 : */
4341 1732 : if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4342 940 : targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4343 : {
4344 810 : Oid constraintId = get_index_constraint(myrelid);
4345 :
4346 810 : if (OidIsValid(constraintId))
4347 36 : RenameConstraintById(constraintId, newrelname);
4348 : }
4349 :
4350 : /*
4351 : * Close rel, but keep lock!
4352 : */
4353 1732 : relation_close(targetrelation, NoLock);
4354 1732 : }
4355 :
4356 : /*
4357 : * ResetRelRewrite - reset relrewrite
4358 : */
4359 : void
4360 598 : ResetRelRewrite(Oid myrelid)
4361 : {
4362 : Relation relrelation; /* for RELATION relation */
4363 : HeapTuple reltup;
4364 : Form_pg_class relform;
4365 :
4366 : /*
4367 : * Find relation's pg_class tuple.
4368 : */
4369 598 : relrelation = table_open(RelationRelationId, RowExclusiveLock);
4370 :
4371 598 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4372 598 : if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4373 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
4374 598 : relform = (Form_pg_class) GETSTRUCT(reltup);
4375 :
4376 : /*
4377 : * Update pg_class tuple.
4378 : */
4379 598 : relform->relrewrite = InvalidOid;
4380 :
4381 598 : CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4382 :
4383 598 : heap_freetuple(reltup);
4384 598 : table_close(relrelation, RowExclusiveLock);
4385 598 : }
4386 :
4387 : /*
4388 : * Disallow ALTER TABLE (and similar commands) when the current backend has
4389 : * any open reference to the target table besides the one just acquired by
4390 : * the calling command; this implies there's an open cursor or active plan.
4391 : * We need this check because our lock doesn't protect us against stomping
4392 : * on our own foot, only other people's feet!
4393 : *
4394 : * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4395 : * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4396 : * possibly be relaxed to only error out for certain types of alterations.
4397 : * But the use-case for allowing any of these things is not obvious, so we
4398 : * won't work hard at it for now.
4399 : *
4400 : * We also reject these commands if there are any pending AFTER trigger events
4401 : * for the rel. This is certainly necessary for the rewriting variants of
4402 : * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4403 : * events would try to fetch the wrong tuples. It might be overly cautious
4404 : * in other cases, but again it seems better to err on the side of paranoia.
4405 : *
4406 : * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4407 : * we are worried about active indexscans on the index. The trigger-event
4408 : * check can be skipped, since we are doing no damage to the parent table.
4409 : *
4410 : * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4411 : */
4412 : void
4413 177552 : CheckTableNotInUse(Relation rel, const char *stmt)
4414 : {
4415 : int expected_refcnt;
4416 :
4417 177552 : expected_refcnt = rel->rd_isnailed ? 2 : 1;
4418 177552 : if (rel->rd_refcnt != expected_refcnt)
4419 42 : ereport(ERROR,
4420 : (errcode(ERRCODE_OBJECT_IN_USE),
4421 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4422 : errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4423 : stmt, RelationGetRelationName(rel))));
4424 :
4425 177510 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
4426 290020 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4427 143892 : AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4428 18 : ereport(ERROR,
4429 : (errcode(ERRCODE_OBJECT_IN_USE),
4430 : /* translator: first %s is a SQL command, eg ALTER TABLE */
4431 : errmsg("cannot %s \"%s\" because it has pending trigger events",
4432 : stmt, RelationGetRelationName(rel))));
4433 177492 : }
4434 :
4435 : /*
4436 : * CheckAlterTableIsSafe
4437 : * Verify that it's safe to allow ALTER TABLE on this relation.
4438 : *
4439 : * This consists of CheckTableNotInUse() plus a check that the relation
4440 : * isn't another session's temp table. We must split out the temp-table
4441 : * check because there are callers of CheckTableNotInUse() that don't want
4442 : * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4443 : * an orphaned temp schema.) Compare truncate_check_activity().
4444 : */
4445 : static void
4446 63464 : CheckAlterTableIsSafe(Relation rel)
4447 : {
4448 : /*
4449 : * Don't allow ALTER on temp tables of other backends. Their local buffer
4450 : * manager is not going to cope if we need to change the table's contents.
4451 : * Even if we don't, there may be optimizations that assume temp tables
4452 : * aren't subject to such interference.
4453 : */
4454 63464 : if (RELATION_IS_OTHER_TEMP(rel))
4455 0 : ereport(ERROR,
4456 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4457 : errmsg("cannot alter temporary tables of other sessions")));
4458 :
4459 : /*
4460 : * Also check for active uses of the relation in the current transaction,
4461 : * including open scans and pending AFTER trigger events.
4462 : */
4463 63464 : CheckTableNotInUse(rel, "ALTER TABLE");
4464 63428 : }
4465 :
4466 : /*
4467 : * AlterTableLookupRelation
4468 : * Look up, and lock, the OID for the relation named by an alter table
4469 : * statement.
4470 : */
4471 : Oid
4472 33676 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4473 : {
4474 67252 : return RangeVarGetRelidExtended(stmt->relation, lockmode,
4475 33676 : stmt->missing_ok ? RVR_MISSING_OK : 0,
4476 : RangeVarCallbackForAlterRelation,
4477 : stmt);
4478 : }
4479 :
4480 : /*
4481 : * AlterTable
4482 : * Execute ALTER TABLE, which can be a list of subcommands
4483 : *
4484 : * ALTER TABLE is performed in three phases:
4485 : * 1. Examine subcommands and perform pre-transformation checking.
4486 : * 2. Validate and transform subcommands, and update system catalogs.
4487 : * 3. Scan table(s) to check new constraints, and optionally recopy
4488 : * the data into new table(s).
4489 : * Phase 3 is not performed unless one or more of the subcommands requires
4490 : * it. The intention of this design is to allow multiple independent
4491 : * updates of the table schema to be performed with only one pass over the
4492 : * data.
4493 : *
4494 : * ATPrepCmd performs phase 1. A "work queue" entry is created for
4495 : * each table to be affected (there may be multiple affected tables if the
4496 : * commands traverse a table inheritance hierarchy). Also we do preliminary
4497 : * validation of the subcommands. Because earlier subcommands may change
4498 : * the catalog state seen by later commands, there are limits to what can
4499 : * be done in this phase. Generally, this phase acquires table locks,
4500 : * checks permissions and relkind, and recurses to find child tables.
4501 : *
4502 : * ATRewriteCatalogs performs phase 2 for each affected table.
4503 : * Certain subcommands need to be performed before others to avoid
4504 : * unnecessary conflicts; for example, DROP COLUMN should come before
4505 : * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4506 : * lists, one for each logical "pass" of phase 2.
4507 : *
4508 : * ATRewriteTables performs phase 3 for those tables that need it.
4509 : *
4510 : * For most subcommand types, phases 2 and 3 do no explicit recursion,
4511 : * since phase 1 already does it. However, for certain subcommand types
4512 : * it is only possible to determine how to recurse at phase 2 time; for
4513 : * those cases, phase 1 sets the cmd->recurse flag.
4514 : *
4515 : * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4516 : * the whole operation; we don't have to do anything special to clean up.
4517 : *
4518 : * The caller must lock the relation, with an appropriate lock level
4519 : * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4520 : * or higher. We pass the lock level down
4521 : * so that we can apply it recursively to inherited tables. Note that the
4522 : * lock level we want as we recurse might well be higher than required for
4523 : * that specific subcommand. So we pass down the overall lock requirement,
4524 : * rather than reassess it at lower levels.
4525 : *
4526 : * The caller also provides a "context" which is to be passed back to
4527 : * utility.c when we need to execute a subcommand such as CREATE INDEX.
4528 : * Some of the fields therein, such as the relid, are used here as well.
4529 : */
4530 : void
4531 33438 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4532 : AlterTableUtilityContext *context)
4533 : {
4534 : Relation rel;
4535 :
4536 : /* Caller is required to provide an adequate lock. */
4537 33438 : rel = relation_open(context->relid, NoLock);
4538 :
4539 33438 : CheckAlterTableIsSafe(rel);
4540 :
4541 33420 : ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4542 29376 : }
4543 :
4544 : /*
4545 : * AlterTableInternal
4546 : *
4547 : * ALTER TABLE with target specified by OID
4548 : *
4549 : * We do not reject if the relation is already open, because it's quite
4550 : * likely that one or more layers of caller have it open. That means it
4551 : * is unsafe to use this entry point for alterations that could break
4552 : * existing query plans. On the assumption it's not used for such, we
4553 : * don't have to reject pending AFTER triggers, either.
4554 : *
4555 : * Also, since we don't have an AlterTableUtilityContext, this cannot be
4556 : * used for any subcommand types that require parse transformation or
4557 : * could generate subcommands that have to be passed to ProcessUtility.
4558 : */
4559 : void
4560 278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
4561 : {
4562 : Relation rel;
4563 278 : LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4564 :
4565 278 : rel = relation_open(relid, lockmode);
4566 :
4567 278 : EventTriggerAlterTableRelid(relid);
4568 :
4569 278 : ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4570 278 : }
4571 :
4572 : /*
4573 : * AlterTableGetLockLevel
4574 : *
4575 : * Sets the overall lock level required for the supplied list of subcommands.
4576 : * Policy for doing this set according to needs of AlterTable(), see
4577 : * comments there for overall explanation.
4578 : *
4579 : * Function is called before and after parsing, so it must give same
4580 : * answer each time it is called. Some subcommands are transformed
4581 : * into other subcommand types, so the transform must never be made to a
4582 : * lower lock level than previously assigned. All transforms are noted below.
4583 : *
4584 : * Since this is called before we lock the table we cannot use table metadata
4585 : * to influence the type of lock we acquire.
4586 : *
4587 : * There should be no lockmodes hardcoded into the subcommand functions. All
4588 : * lockmode decisions for ALTER TABLE are made here only. The one exception is
4589 : * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4590 : * and does not travel through this section of code and cannot be combined with
4591 : * any of the subcommands given here.
4592 : *
4593 : * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4594 : * so any changes that might affect SELECTs running on standbys need to use
4595 : * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4596 : * have a solution for that also.
4597 : *
4598 : * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4599 : * that takes a lock less than AccessExclusiveLock can change object definitions
4600 : * while pg_dump is running. Be careful to check that the appropriate data is
4601 : * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4602 : * otherwise we might end up with an inconsistent dump that can't restore.
4603 : */
4604 : LOCKMODE
4605 33954 : AlterTableGetLockLevel(List *cmds)
4606 : {
4607 : /*
4608 : * This only works if we read catalog tables using MVCC snapshots.
4609 : */
4610 : ListCell *lcmd;
4611 33954 : LOCKMODE lockmode = ShareUpdateExclusiveLock;
4612 :
4613 69104 : foreach(lcmd, cmds)
4614 : {
4615 35150 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4616 35150 : LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4617 :
4618 35150 : switch (cmd->subtype)
4619 : {
4620 : /*
4621 : * These subcommands rewrite the heap, so require full locks.
4622 : */
4623 3742 : case AT_AddColumn: /* may rewrite heap, in some cases and visible
4624 : * to SELECT */
4625 : case AT_SetAccessMethod: /* must rewrite heap */
4626 : case AT_SetTableSpace: /* must rewrite heap */
4627 : case AT_AlterColumnType: /* must rewrite heap */
4628 3742 : cmd_lockmode = AccessExclusiveLock;
4629 3742 : break;
4630 :
4631 : /*
4632 : * These subcommands may require addition of toast tables. If
4633 : * we add a toast table to a table currently being scanned, we
4634 : * might miss data added to the new toast table by concurrent
4635 : * insert transactions.
4636 : */
4637 238 : case AT_SetStorage: /* may add toast tables, see
4638 : * ATRewriteCatalogs() */
4639 238 : cmd_lockmode = AccessExclusiveLock;
4640 238 : break;
4641 :
4642 : /*
4643 : * Removing constraints can affect SELECTs that have been
4644 : * optimized assuming the constraint holds true. See also
4645 : * CloneFkReferenced.
4646 : */
4647 1130 : case AT_DropConstraint: /* as DROP INDEX */
4648 : case AT_DropNotNull: /* may change some SQL plans */
4649 1130 : cmd_lockmode = AccessExclusiveLock;
4650 1130 : break;
4651 :
4652 : /*
4653 : * Subcommands that may be visible to concurrent SELECTs
4654 : */
4655 1794 : case AT_DropColumn: /* change visible to SELECT */
4656 : case AT_AddColumnToView: /* CREATE VIEW */
4657 : case AT_DropOids: /* used to equiv to DropColumn */
4658 : case AT_EnableAlwaysRule: /* may change SELECT rules */
4659 : case AT_EnableReplicaRule: /* may change SELECT rules */
4660 : case AT_EnableRule: /* may change SELECT rules */
4661 : case AT_DisableRule: /* may change SELECT rules */
4662 1794 : cmd_lockmode = AccessExclusiveLock;
4663 1794 : break;
4664 :
4665 : /*
4666 : * Changing owner may remove implicit SELECT privileges
4667 : */
4668 2082 : case AT_ChangeOwner: /* change visible to SELECT */
4669 2082 : cmd_lockmode = AccessExclusiveLock;
4670 2082 : break;
4671 :
4672 : /*
4673 : * Changing foreign table options may affect optimization.
4674 : */
4675 254 : case AT_GenericOptions:
4676 : case AT_AlterColumnGenericOptions:
4677 254 : cmd_lockmode = AccessExclusiveLock;
4678 254 : break;
4679 :
4680 : /*
4681 : * These subcommands affect write operations only.
4682 : */
4683 342 : case AT_EnableTrig:
4684 : case AT_EnableAlwaysTrig:
4685 : case AT_EnableReplicaTrig:
4686 : case AT_EnableTrigAll:
4687 : case AT_EnableTrigUser:
4688 : case AT_DisableTrig:
4689 : case AT_DisableTrigAll:
4690 : case AT_DisableTrigUser:
4691 342 : cmd_lockmode = ShareRowExclusiveLock;
4692 342 : break;
4693 :
4694 : /*
4695 : * These subcommands affect write operations only. XXX
4696 : * Theoretically, these could be ShareRowExclusiveLock.
4697 : */
4698 2962 : case AT_ColumnDefault:
4699 : case AT_CookedColumnDefault:
4700 : case AT_AlterConstraint:
4701 : case AT_AddIndex: /* from ADD CONSTRAINT */
4702 : case AT_AddIndexConstraint:
4703 : case AT_ReplicaIdentity:
4704 : case AT_SetNotNull:
4705 : case AT_EnableRowSecurity:
4706 : case AT_DisableRowSecurity:
4707 : case AT_ForceRowSecurity:
4708 : case AT_NoForceRowSecurity:
4709 : case AT_AddIdentity:
4710 : case AT_DropIdentity:
4711 : case AT_SetIdentity:
4712 : case AT_SetExpression:
4713 : case AT_DropExpression:
4714 : case AT_SetCompression:
4715 2962 : cmd_lockmode = AccessExclusiveLock;
4716 2962 : break;
4717 :
4718 15946 : case AT_AddConstraint:
4719 : case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4720 : case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4721 15946 : if (IsA(cmd->def, Constraint))
4722 : {
4723 15946 : Constraint *con = (Constraint *) cmd->def;
4724 :
4725 15946 : switch (con->contype)
4726 : {
4727 12136 : case CONSTR_EXCLUSION:
4728 : case CONSTR_PRIMARY:
4729 : case CONSTR_UNIQUE:
4730 :
4731 : /*
4732 : * Cases essentially the same as CREATE INDEX. We
4733 : * could reduce the lock strength to ShareLock if
4734 : * we can work out how to allow concurrent catalog
4735 : * updates. XXX Might be set down to
4736 : * ShareRowExclusiveLock but requires further
4737 : * analysis.
4738 : */
4739 12136 : cmd_lockmode = AccessExclusiveLock;
4740 12136 : break;
4741 2618 : case CONSTR_FOREIGN:
4742 :
4743 : /*
4744 : * We add triggers to both tables when we add a
4745 : * Foreign Key, so the lock level must be at least
4746 : * as strong as CREATE TRIGGER.
4747 : */
4748 2618 : cmd_lockmode = ShareRowExclusiveLock;
4749 2618 : break;
4750 :
4751 1192 : default:
4752 1192 : cmd_lockmode = AccessExclusiveLock;
4753 : }
4754 : }
4755 15946 : break;
4756 :
4757 : /*
4758 : * These subcommands affect inheritance behaviour. Queries
4759 : * started before us will continue to see the old inheritance
4760 : * behaviour, while queries started after we commit will see
4761 : * new behaviour. No need to prevent reads or writes to the
4762 : * subtable while we hook it up though. Changing the TupDesc
4763 : * may be a problem, so keep highest lock.
4764 : */
4765 558 : case AT_AddInherit:
4766 : case AT_DropInherit:
4767 558 : cmd_lockmode = AccessExclusiveLock;
4768 558 : break;
4769 :
4770 : /*
4771 : * These subcommands affect implicit row type conversion. They
4772 : * have affects similar to CREATE/DROP CAST on queries. don't
4773 : * provide for invalidating parse trees as a result of such
4774 : * changes, so we keep these at AccessExclusiveLock.
4775 : */
4776 72 : case AT_AddOf:
4777 : case AT_DropOf:
4778 72 : cmd_lockmode = AccessExclusiveLock;
4779 72 : break;
4780 :
4781 : /*
4782 : * Only used by CREATE OR REPLACE VIEW which must conflict
4783 : * with an SELECTs currently using the view.
4784 : */
4785 194 : case AT_ReplaceRelOptions:
4786 194 : cmd_lockmode = AccessExclusiveLock;
4787 194 : break;
4788 :
4789 : /*
4790 : * These subcommands affect general strategies for performance
4791 : * and maintenance, though don't change the semantic results
4792 : * from normal data reads and writes. Delaying an ALTER TABLE
4793 : * behind currently active writes only delays the point where
4794 : * the new strategy begins to take effect, so there is no
4795 : * benefit in waiting. In this case the minimum restriction
4796 : * applies: we don't currently allow concurrent catalog
4797 : * updates.
4798 : */
4799 234 : case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4800 : case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4801 : case AT_DropCluster: /* Uses MVCC in getIndexes() */
4802 : case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4803 : case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4804 234 : cmd_lockmode = ShareUpdateExclusiveLock;
4805 234 : break;
4806 :
4807 112 : case AT_SetLogged:
4808 : case AT_SetUnLogged:
4809 112 : cmd_lockmode = AccessExclusiveLock;
4810 112 : break;
4811 :
4812 482 : case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4813 482 : cmd_lockmode = ShareUpdateExclusiveLock;
4814 482 : break;
4815 :
4816 : /*
4817 : * Rel options are more complex than first appears. Options
4818 : * are set here for tables, views and indexes; for historical
4819 : * reasons these can all be used with ALTER TABLE, so we can't
4820 : * decide between them using the basic grammar.
4821 : */
4822 770 : case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4823 : * getTables() */
4824 : case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4825 : * getTables() */
4826 770 : cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4827 770 : break;
4828 :
4829 2956 : case AT_AttachPartition:
4830 2956 : cmd_lockmode = ShareUpdateExclusiveLock;
4831 2956 : break;
4832 :
4833 602 : case AT_DetachPartition:
4834 602 : if (((PartitionCmd *) cmd->def)->concurrent)
4835 164 : cmd_lockmode = ShareUpdateExclusiveLock;
4836 : else
4837 438 : cmd_lockmode = AccessExclusiveLock;
4838 602 : break;
4839 :
4840 20 : case AT_DetachPartitionFinalize:
4841 20 : cmd_lockmode = ShareUpdateExclusiveLock;
4842 20 : break;
4843 :
4844 660 : case AT_MergePartitions:
4845 : case AT_SplitPartition:
4846 660 : cmd_lockmode = AccessExclusiveLock;
4847 660 : break;
4848 :
4849 0 : default: /* oops */
4850 0 : elog(ERROR, "unrecognized alter table type: %d",
4851 : (int) cmd->subtype);
4852 : break;
4853 : }
4854 :
4855 : /*
4856 : * Take the greatest lockmode from any subcommand
4857 : */
4858 35150 : if (cmd_lockmode > lockmode)
4859 29516 : lockmode = cmd_lockmode;
4860 : }
4861 :
4862 33954 : return lockmode;
4863 : }
4864 :
4865 : /*
4866 : * ATController provides top level control over the phases.
4867 : *
4868 : * parsetree is passed in to allow it to be passed to event triggers
4869 : * when requested.
4870 : */
4871 : static void
4872 33698 : ATController(AlterTableStmt *parsetree,
4873 : Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4874 : AlterTableUtilityContext *context)
4875 : {
4876 33698 : List *wqueue = NIL;
4877 : ListCell *lcmd;
4878 :
4879 : /* Phase 1: preliminary examination of commands, create work queue */
4880 68174 : foreach(lcmd, cmds)
4881 : {
4882 34888 : AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4883 :
4884 34888 : ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4885 : }
4886 :
4887 : /* Close the relation, but keep lock until commit */
4888 33286 : relation_close(rel, NoLock);
4889 :
4890 : /* Phase 2: update system catalogs */
4891 33286 : ATRewriteCatalogs(&wqueue, lockmode, context);
4892 :
4893 : /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4894 30140 : ATRewriteTables(parsetree, &wqueue, lockmode, context);
4895 29654 : }
4896 :
4897 : /*
4898 : * ATPrepCmd
4899 : *
4900 : * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4901 : * recursion and permission checks.
4902 : *
4903 : * Caller must have acquired appropriate lock type on relation already.
4904 : * This lock should be held until commit.
4905 : */
4906 : static void
4907 35828 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4908 : bool recurse, bool recursing, LOCKMODE lockmode,
4909 : AlterTableUtilityContext *context)
4910 : {
4911 : AlteredTableInfo *tab;
4912 35828 : AlterTablePass pass = AT_PASS_UNSET;
4913 :
4914 : /* Find or create work queue entry for this table */
4915 35828 : tab = ATGetQueueEntry(wqueue, rel);
4916 :
4917 : /*
4918 : * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4919 : * partitions that are pending detach.
4920 : */
4921 35828 : if (rel->rd_rel->relispartition &&
4922 2788 : cmd->subtype != AT_DetachPartitionFinalize &&
4923 1394 : PartitionHasPendingDetach(RelationGetRelid(rel)))
4924 2 : ereport(ERROR,
4925 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4926 : errmsg("cannot alter partition \"%s\" with an incomplete detach",
4927 : RelationGetRelationName(rel)),
4928 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4929 :
4930 : /*
4931 : * Copy the original subcommand for each table, so we can scribble on it.
4932 : * This avoids conflicts when different child tables need to make
4933 : * different parse transformations (for example, the same column may have
4934 : * different column numbers in different children).
4935 : */
4936 35826 : cmd = copyObject(cmd);
4937 :
4938 : /*
4939 : * Do permissions and relkind checking, recursion to child tables if
4940 : * needed, and any additional phase-1 processing needed. (But beware of
4941 : * adding any processing that looks at table details that another
4942 : * subcommand could change. In some cases we reject multiple subcommands
4943 : * that could try to change the same state in contrary ways.)
4944 : */
4945 35826 : switch (cmd->subtype)
4946 : {
4947 2190 : case AT_AddColumn: /* ADD COLUMN */
4948 2190 : ATSimplePermissions(cmd->subtype, rel,
4949 : ATT_TABLE | ATT_PARTITIONED_TABLE |
4950 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4951 2190 : ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4952 : lockmode, context);
4953 : /* Recursion occurs during execution phase */
4954 2178 : pass = AT_PASS_ADD_COL;
4955 2178 : break;
4956 24 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4957 24 : ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4958 24 : ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4959 : lockmode, context);
4960 : /* Recursion occurs during execution phase */
4961 24 : pass = AT_PASS_ADD_COL;
4962 24 : break;
4963 620 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4964 :
4965 : /*
4966 : * We allow defaults on views so that INSERT into a view can have
4967 : * default-ish behavior. This works because the rewriter
4968 : * substitutes default values into INSERTs before it expands
4969 : * rules.
4970 : */
4971 620 : ATSimplePermissions(cmd->subtype, rel,
4972 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4973 : ATT_FOREIGN_TABLE);
4974 620 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4975 : /* No command-specific prep needed */
4976 620 : pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4977 620 : break;
4978 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
4979 : /* This is currently used only in CREATE TABLE */
4980 : /* (so the permission check really isn't necessary) */
4981 80 : ATSimplePermissions(cmd->subtype, rel,
4982 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4983 : /* This command never recurses */
4984 80 : pass = AT_PASS_ADD_OTHERCONSTR;
4985 80 : break;
4986 172 : case AT_AddIdentity:
4987 172 : ATSimplePermissions(cmd->subtype, rel,
4988 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4989 : ATT_FOREIGN_TABLE);
4990 : /* Set up recursion for phase 2; no other prep needed */
4991 172 : if (recurse)
4992 166 : cmd->recurse = true;
4993 172 : pass = AT_PASS_ADD_OTHERCONSTR;
4994 172 : break;
4995 62 : case AT_SetIdentity:
4996 62 : ATSimplePermissions(cmd->subtype, rel,
4997 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4998 : ATT_FOREIGN_TABLE);
4999 : /* Set up recursion for phase 2; no other prep needed */
5000 62 : if (recurse)
5001 56 : cmd->recurse = true;
5002 : /* This should run after AddIdentity, so do it in MISC pass */
5003 62 : pass = AT_PASS_MISC;
5004 62 : break;
5005 56 : case AT_DropIdentity:
5006 56 : ATSimplePermissions(cmd->subtype, rel,
5007 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5008 : ATT_FOREIGN_TABLE);
5009 : /* Set up recursion for phase 2; no other prep needed */
5010 56 : if (recurse)
5011 50 : cmd->recurse = true;
5012 56 : pass = AT_PASS_DROP;
5013 56 : break;
5014 274 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5015 274 : ATSimplePermissions(cmd->subtype, rel,
5016 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5017 : /* Set up recursion for phase 2; no other prep needed */
5018 268 : if (recurse)
5019 250 : cmd->recurse = true;
5020 268 : pass = AT_PASS_DROP;
5021 268 : break;
5022 420 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5023 420 : ATSimplePermissions(cmd->subtype, rel,
5024 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5025 : /* Set up recursion for phase 2; no other prep needed */
5026 414 : if (recurse)
5027 384 : cmd->recurse = true;
5028 414 : pass = AT_PASS_COL_ATTRS;
5029 414 : break;
5030 224 : case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5031 224 : ATSimplePermissions(cmd->subtype, rel,
5032 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5033 224 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5034 224 : pass = AT_PASS_SET_EXPRESSION;
5035 224 : break;
5036 86 : case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5037 86 : ATSimplePermissions(cmd->subtype, rel,
5038 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5039 86 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5040 86 : ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5041 62 : pass = AT_PASS_DROP;
5042 62 : break;
5043 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5044 164 : ATSimplePermissions(cmd->subtype, rel,
5045 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
5046 : ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
5047 164 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5048 : /* No command-specific prep needed */
5049 164 : pass = AT_PASS_MISC;
5050 164 : break;
5051 44 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5052 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5053 44 : ATSimplePermissions(cmd->subtype, rel,
5054 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5055 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5056 : /* This command never recurses */
5057 32 : pass = AT_PASS_MISC;
5058 32 : break;
5059 260 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5060 260 : ATSimplePermissions(cmd->subtype, rel,
5061 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5062 : ATT_MATVIEW | ATT_FOREIGN_TABLE);
5063 260 : ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5064 : /* No command-specific prep needed */
5065 260 : pass = AT_PASS_MISC;
5066 260 : break;
5067 78 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5068 78 : ATSimplePermissions(cmd->subtype, rel,
5069 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5070 : /* This command never recurses */
5071 : /* No command-specific prep needed */
5072 78 : pass = AT_PASS_MISC;
5073 78 : break;
5074 1700 : case AT_DropColumn: /* DROP COLUMN */
5075 1700 : ATSimplePermissions(cmd->subtype, rel,
5076 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5077 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5078 1694 : ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5079 : lockmode, context);
5080 : /* Recursion occurs during execution phase */
5081 1682 : pass = AT_PASS_DROP;
5082 1682 : break;
5083 0 : case AT_AddIndex: /* ADD INDEX */
5084 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5085 : /* This command never recurses */
5086 : /* No command-specific prep needed */
5087 0 : pass = AT_PASS_ADD_INDEX;
5088 0 : break;
5089 16410 : case AT_AddConstraint: /* ADD CONSTRAINT */
5090 16410 : ATSimplePermissions(cmd->subtype, rel,
5091 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5092 16410 : ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5093 16380 : if (recurse)
5094 : {
5095 : /* recurses at exec time; lock descendants and set flag */
5096 15996 : (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5097 15996 : cmd->recurse = true;
5098 : }
5099 16380 : pass = AT_PASS_ADD_CONSTR;
5100 16380 : break;
5101 0 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5102 0 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5103 : /* This command never recurses */
5104 : /* No command-specific prep needed */
5105 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5106 0 : break;
5107 818 : case AT_DropConstraint: /* DROP CONSTRAINT */
5108 818 : ATSimplePermissions(cmd->subtype, rel,
5109 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5110 818 : ATCheckPartitionsNotInUse(rel, lockmode);
5111 : /* Other recursion occurs during execution phase */
5112 : /* No command-specific prep needed except saving recurse flag */
5113 812 : if (recurse)
5114 776 : cmd->recurse = true;
5115 812 : pass = AT_PASS_DROP;
5116 812 : break;
5117 1432 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5118 1432 : ATSimplePermissions(cmd->subtype, rel,
5119 : ATT_TABLE | ATT_PARTITIONED_TABLE |
5120 : ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5121 : /* See comments for ATPrepAlterColumnType */
5122 1432 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5123 : AT_PASS_UNSET, context);
5124 : Assert(cmd != NULL);
5125 : /* Performs own recursion */
5126 1426 : ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5127 : lockmode, context);
5128 1228 : pass = AT_PASS_ALTER_TYPE;
5129 1228 : break;
5130 172 : case AT_AlterColumnGenericOptions:
5131 172 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5132 : /* This command never recurses */
5133 : /* No command-specific prep needed */
5134 172 : pass = AT_PASS_MISC;
5135 172 : break;
5136 2058 : case AT_ChangeOwner: /* ALTER OWNER */
5137 : /* This command never recurses */
5138 : /* No command-specific prep needed */
5139 2058 : pass = AT_PASS_MISC;
5140 2058 : break;
5141 64 : case AT_ClusterOn: /* CLUSTER ON */
5142 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5143 64 : ATSimplePermissions(cmd->subtype, rel,
5144 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5145 : /* These commands never recurse */
5146 : /* No command-specific prep needed */
5147 64 : pass = AT_PASS_MISC;
5148 64 : break;
5149 112 : case AT_SetLogged: /* SET LOGGED */
5150 : case AT_SetUnLogged: /* SET UNLOGGED */
5151 112 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5152 100 : if (tab->chgPersistence)
5153 0 : ereport(ERROR,
5154 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5155 : errmsg("cannot change persistence setting twice")));
5156 100 : ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5157 88 : pass = AT_PASS_MISC;
5158 88 : break;
5159 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5160 6 : ATSimplePermissions(cmd->subtype, rel,
5161 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5162 6 : pass = AT_PASS_DROP;
5163 6 : break;
5164 128 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5165 128 : ATSimplePermissions(cmd->subtype, rel,
5166 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5167 :
5168 : /* check if another access method change was already requested */
5169 128 : if (tab->chgAccessMethod)
5170 18 : ereport(ERROR,
5171 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5172 : errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5173 :
5174 110 : ATPrepSetAccessMethod(tab, rel, cmd->name);
5175 110 : pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5176 110 : break;
5177 164 : case AT_SetTableSpace: /* SET TABLESPACE */
5178 164 : ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5179 : ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5180 : /* This command never recurses */
5181 164 : ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5182 164 : pass = AT_PASS_MISC; /* doesn't actually matter */
5183 164 : break;
5184 962 : case AT_SetRelOptions: /* SET (...) */
5185 : case AT_ResetRelOptions: /* RESET (...) */
5186 : case AT_ReplaceRelOptions: /* reset them all, then set just these */
5187 962 : ATSimplePermissions(cmd->subtype, rel,
5188 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5189 : ATT_MATVIEW | ATT_INDEX);
5190 : /* This command never recurses */
5191 : /* No command-specific prep needed */
5192 960 : pass = AT_PASS_MISC;
5193 960 : break;
5194 464 : case AT_AddInherit: /* INHERIT */
5195 464 : ATSimplePermissions(cmd->subtype, rel,
5196 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5197 : /* This command never recurses */
5198 464 : ATPrepAddInherit(rel);
5199 446 : pass = AT_PASS_MISC;
5200 446 : break;
5201 94 : case AT_DropInherit: /* NO INHERIT */
5202 94 : ATSimplePermissions(cmd->subtype, rel,
5203 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5204 : /* This command never recurses */
5205 : /* No command-specific prep needed */
5206 94 : pass = AT_PASS_MISC;
5207 94 : break;
5208 300 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5209 300 : ATSimplePermissions(cmd->subtype, rel,
5210 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5211 : /* Recursion occurs during execution phase */
5212 294 : if (recurse)
5213 294 : cmd->recurse = true;
5214 294 : pass = AT_PASS_MISC;
5215 294 : break;
5216 482 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5217 482 : ATSimplePermissions(cmd->subtype, rel,
5218 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5219 : /* Recursion occurs during execution phase */
5220 : /* No command-specific prep needed except saving recurse flag */
5221 482 : if (recurse)
5222 482 : cmd->recurse = true;
5223 482 : pass = AT_PASS_MISC;
5224 482 : break;
5225 494 : case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5226 494 : ATSimplePermissions(cmd->subtype, rel,
5227 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5228 494 : pass = AT_PASS_MISC;
5229 : /* This command never recurses */
5230 : /* No command-specific prep needed */
5231 494 : break;
5232 342 : case AT_EnableTrig: /* ENABLE TRIGGER variants */
5233 : case AT_EnableAlwaysTrig:
5234 : case AT_EnableReplicaTrig:
5235 : case AT_EnableTrigAll:
5236 : case AT_EnableTrigUser:
5237 : case AT_DisableTrig: /* DISABLE TRIGGER variants */
5238 : case AT_DisableTrigAll:
5239 : case AT_DisableTrigUser:
5240 342 : ATSimplePermissions(cmd->subtype, rel,
5241 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5242 : /* Set up recursion for phase 2; no other prep needed */
5243 342 : if (recurse)
5244 314 : cmd->recurse = true;
5245 342 : pass = AT_PASS_MISC;
5246 342 : break;
5247 598 : case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5248 : case AT_EnableAlwaysRule:
5249 : case AT_EnableReplicaRule:
5250 : case AT_DisableRule:
5251 : case AT_AddOf: /* OF */
5252 : case AT_DropOf: /* NOT OF */
5253 : case AT_EnableRowSecurity:
5254 : case AT_DisableRowSecurity:
5255 : case AT_ForceRowSecurity:
5256 : case AT_NoForceRowSecurity:
5257 598 : ATSimplePermissions(cmd->subtype, rel,
5258 : ATT_TABLE | ATT_PARTITIONED_TABLE);
5259 : /* These commands never recurse */
5260 : /* No command-specific prep needed */
5261 598 : pass = AT_PASS_MISC;
5262 598 : break;
5263 58 : case AT_GenericOptions:
5264 58 : ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5265 : /* No command-specific prep needed */
5266 58 : pass = AT_PASS_MISC;
5267 58 : break;
5268 2944 : case AT_AttachPartition:
5269 2944 : ATSimplePermissions(cmd->subtype, rel,
5270 : ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5271 : /* No command-specific prep needed */
5272 2938 : pass = AT_PASS_MISC;
5273 2938 : break;
5274 602 : case AT_DetachPartition:
5275 602 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5276 : /* No command-specific prep needed */
5277 584 : pass = AT_PASS_MISC;
5278 584 : break;
5279 20 : case AT_DetachPartitionFinalize:
5280 20 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5281 : /* No command-specific prep needed */
5282 14 : pass = AT_PASS_MISC;
5283 14 : break;
5284 648 : case AT_MergePartitions:
5285 : case AT_SplitPartition:
5286 648 : ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5287 : /* No command-specific prep needed */
5288 642 : pass = AT_PASS_MISC;
5289 642 : break;
5290 0 : default: /* oops */
5291 0 : elog(ERROR, "unrecognized alter table type: %d",
5292 : (int) cmd->subtype);
5293 : pass = AT_PASS_UNSET; /* keep compiler quiet */
5294 : break;
5295 : }
5296 : Assert(pass > AT_PASS_UNSET);
5297 :
5298 : /* Add the subcommand to the appropriate list for phase 2 */
5299 35404 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5300 35404 : }
5301 :
5302 : /*
5303 : * ATRewriteCatalogs
5304 : *
5305 : * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5306 : * dispatched in a "safe" execution order (designed to avoid unnecessary
5307 : * conflicts).
5308 : */
5309 : static void
5310 33286 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5311 : AlterTableUtilityContext *context)
5312 : {
5313 : ListCell *ltab;
5314 :
5315 : /*
5316 : * We process all the tables "in parallel", one pass at a time. This is
5317 : * needed because we may have to propagate work from one table to another
5318 : * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5319 : * re-adding of the foreign key constraint to the other table). Work can
5320 : * only be propagated into later passes, however.
5321 : */
5322 419792 : for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5323 : {
5324 : /* Go through each table that needs to be processed */
5325 792566 : foreach(ltab, *wqueue)
5326 : {
5327 406060 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5328 406060 : List *subcmds = tab->subcmds[pass];
5329 : ListCell *lcmd;
5330 :
5331 406060 : if (subcmds == NIL)
5332 348720 : continue;
5333 :
5334 : /*
5335 : * Open the relation and store it in tab. This allows subroutines
5336 : * close and reopen, if necessary. Appropriate lock was obtained
5337 : * by phase 1, needn't get it again.
5338 : */
5339 57340 : tab->rel = relation_open(tab->relid, NoLock);
5340 :
5341 115316 : foreach(lcmd, subcmds)
5342 61122 : ATExecCmd(wqueue, tab,
5343 61122 : lfirst_node(AlterTableCmd, lcmd),
5344 : lockmode, pass, context);
5345 :
5346 : /*
5347 : * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5348 : * (this is not done in ATExecAlterColumnType since it should be
5349 : * done only once if multiple columns of a table are altered).
5350 : */
5351 54194 : if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5352 1302 : ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5353 :
5354 54194 : if (tab->rel)
5355 : {
5356 54194 : relation_close(tab->rel, NoLock);
5357 54194 : tab->rel = NULL;
5358 : }
5359 : }
5360 : }
5361 :
5362 : /* Check to see if a toast table must be added. */
5363 64674 : foreach(ltab, *wqueue)
5364 : {
5365 34534 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5366 :
5367 : /*
5368 : * If the table is source table of ATTACH PARTITION command, we did
5369 : * not modify anything about it that will change its toasting
5370 : * requirement, so no need to check.
5371 : */
5372 34534 : if (((tab->relkind == RELKIND_RELATION ||
5373 6682 : tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5374 32622 : tab->partition_constraint == NULL) ||
5375 4116 : tab->relkind == RELKIND_MATVIEW)
5376 30468 : AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5377 : }
5378 30140 : }
5379 :
5380 : /*
5381 : * ATExecCmd: dispatch a subcommand to appropriate execution routine
5382 : */
5383 : static void
5384 61122 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5385 : AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5386 : AlterTableUtilityContext *context)
5387 : {
5388 61122 : ObjectAddress address = InvalidObjectAddress;
5389 61122 : Relation rel = tab->rel;
5390 :
5391 61122 : switch (cmd->subtype)
5392 : {
5393 2196 : case AT_AddColumn: /* ADD COLUMN */
5394 : case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5395 2196 : address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5396 2196 : cmd->recurse, false,
5397 : lockmode, cur_pass, context);
5398 2058 : break;
5399 584 : case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5400 584 : address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5401 518 : break;
5402 80 : case AT_CookedColumnDefault: /* add a pre-cooked default */
5403 80 : address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5404 80 : break;
5405 172 : case AT_AddIdentity:
5406 172 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5407 : cur_pass, context);
5408 : Assert(cmd != NULL);
5409 160 : address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5410 106 : break;
5411 62 : case AT_SetIdentity:
5412 62 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5413 : cur_pass, context);
5414 : Assert(cmd != NULL);
5415 62 : address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5416 38 : break;
5417 56 : case AT_DropIdentity:
5418 56 : address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5419 38 : break;
5420 268 : case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5421 268 : address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5422 166 : break;
5423 414 : case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5424 414 : address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5425 414 : cmd->recurse, false, lockmode);
5426 384 : break;
5427 224 : case AT_SetExpression:
5428 224 : address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5429 194 : break;
5430 56 : case AT_DropExpression:
5431 56 : address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5432 32 : break;
5433 164 : case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5434 164 : address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5435 116 : break;
5436 26 : case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5437 26 : address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5438 26 : break;
5439 6 : case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5440 6 : address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5441 6 : break;
5442 260 : case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5443 260 : address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5444 248 : break;
5445 78 : case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5446 78 : address = ATExecSetCompression(rel, cmd->name, cmd->def,
5447 : lockmode);
5448 72 : break;
5449 1682 : case AT_DropColumn: /* DROP COLUMN */
5450 1682 : address = ATExecDropColumn(wqueue, rel, cmd->name,
5451 1682 : cmd->behavior, cmd->recurse, false,
5452 1682 : cmd->missing_ok, lockmode,
5453 : NULL);
5454 1502 : break;
5455 1194 : case AT_AddIndex: /* ADD INDEX */
5456 1194 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5457 : lockmode);
5458 1024 : break;
5459 456 : case AT_ReAddIndex: /* ADD INDEX */
5460 456 : address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5461 : lockmode);
5462 456 : break;
5463 74 : case AT_ReAddStatistics: /* ADD STATISTICS */
5464 74 : address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5465 : true, lockmode);
5466 74 : break;
5467 29150 : case AT_AddConstraint: /* ADD CONSTRAINT */
5468 : /* Transform the command only during initial examination */
5469 29150 : if (cur_pass == AT_PASS_ADD_CONSTR)
5470 16350 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5471 16380 : cmd->recurse, lockmode,
5472 : cur_pass, context);
5473 : /* Depending on constraint type, might be no more work to do now */
5474 29120 : if (cmd != NULL)
5475 : address =
5476 12770 : ATExecAddConstraint(wqueue, tab, rel,
5477 12770 : (Constraint *) cmd->def,
5478 12770 : cmd->recurse, false, lockmode);
5479 28440 : break;
5480 338 : case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5481 : address =
5482 338 : ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5483 : true, true, lockmode);
5484 326 : break;
5485 14 : case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5486 : * constraint */
5487 : address =
5488 14 : AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5489 14 : ((AlterDomainStmt *) cmd->def)->def,
5490 : NULL);
5491 8 : break;
5492 78 : case AT_ReAddComment: /* Re-add existing comment */
5493 78 : address = CommentObject((CommentStmt *) cmd->def);
5494 78 : break;
5495 10860 : case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5496 10860 : address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5497 : lockmode);
5498 10848 : break;
5499 294 : case AT_AlterConstraint: /* ALTER CONSTRAINT */
5500 294 : address = ATExecAlterConstraint(wqueue, rel,
5501 294 : castNode(ATAlterConstraint, cmd->def),
5502 294 : cmd->recurse, lockmode);
5503 228 : break;
5504 482 : case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5505 482 : address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5506 : false, lockmode);
5507 476 : break;
5508 812 : case AT_DropConstraint: /* DROP CONSTRAINT */
5509 812 : ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5510 812 : cmd->recurse,
5511 812 : cmd->missing_ok, lockmode);
5512 602 : break;
5513 1192 : case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5514 : /* parse transformation was done earlier */
5515 1192 : address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5516 1150 : break;
5517 172 : case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5518 : address =
5519 172 : ATExecAlterColumnGenericOptions(rel, cmd->name,
5520 172 : (List *) cmd->def, lockmode);
5521 166 : break;
5522 2058 : case AT_ChangeOwner: /* ALTER OWNER */
5523 2052 : ATExecChangeOwner(RelationGetRelid(rel),
5524 2058 : get_rolespec_oid(cmd->newowner, false),
5525 : false, lockmode);
5526 2040 : break;
5527 64 : case AT_ClusterOn: /* CLUSTER ON */
5528 64 : address = ATExecClusterOn(rel, cmd->name, lockmode);
5529 58 : break;
5530 18 : case AT_DropCluster: /* SET WITHOUT CLUSTER */
5531 18 : ATExecDropCluster(rel, lockmode);
5532 12 : break;
5533 88 : case AT_SetLogged: /* SET LOGGED */
5534 : case AT_SetUnLogged: /* SET UNLOGGED */
5535 88 : break;
5536 6 : case AT_DropOids: /* SET WITHOUT OIDS */
5537 : /* nothing to do here, oid columns don't exist anymore */
5538 6 : break;
5539 92 : case AT_SetAccessMethod: /* SET ACCESS METHOD */
5540 :
5541 : /*
5542 : * Only do this for partitioned tables, for which this is just a
5543 : * catalog change. Tables with storage are handled by Phase 3.
5544 : */
5545 92 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5546 50 : tab->chgAccessMethod)
5547 44 : ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5548 92 : break;
5549 164 : case AT_SetTableSpace: /* SET TABLESPACE */
5550 :
5551 : /*
5552 : * Only do this for partitioned tables and indexes, for which this
5553 : * is just a catalog change. Other relation types which have
5554 : * storage are handled by Phase 3.
5555 : */
5556 164 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5557 152 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5558 36 : ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5559 :
5560 158 : break;
5561 960 : case AT_SetRelOptions: /* SET (...) */
5562 : case AT_ResetRelOptions: /* RESET (...) */
5563 : case AT_ReplaceRelOptions: /* replace entire option list */
5564 960 : ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5565 908 : break;
5566 122 : case AT_EnableTrig: /* ENABLE TRIGGER name */
5567 122 : ATExecEnableDisableTrigger(rel, cmd->name,
5568 : TRIGGER_FIRES_ON_ORIGIN, false,
5569 122 : cmd->recurse,
5570 : lockmode);
5571 122 : break;
5572 42 : case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5573 42 : ATExecEnableDisableTrigger(rel, cmd->name,
5574 : TRIGGER_FIRES_ALWAYS, false,
5575 42 : cmd->recurse,
5576 : lockmode);
5577 42 : break;
5578 16 : case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5579 16 : ATExecEnableDisableTrigger(rel, cmd->name,
5580 : TRIGGER_FIRES_ON_REPLICA, false,
5581 16 : cmd->recurse,
5582 : lockmode);
5583 16 : break;
5584 138 : case AT_DisableTrig: /* DISABLE TRIGGER name */
5585 138 : ATExecEnableDisableTrigger(rel, cmd->name,
5586 : TRIGGER_DISABLED, false,
5587 138 : cmd->recurse,
5588 : lockmode);
5589 138 : break;
5590 0 : case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5591 0 : ATExecEnableDisableTrigger(rel, NULL,
5592 : TRIGGER_FIRES_ON_ORIGIN, false,
5593 0 : cmd->recurse,
5594 : lockmode);
5595 0 : break;
5596 12 : case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5597 12 : ATExecEnableDisableTrigger(rel, NULL,
5598 : TRIGGER_DISABLED, false,
5599 12 : cmd->recurse,
5600 : lockmode);
5601 12 : break;
5602 0 : case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5603 0 : ATExecEnableDisableTrigger(rel, NULL,
5604 : TRIGGER_FIRES_ON_ORIGIN, true,
5605 0 : cmd->recurse,
5606 : lockmode);
5607 0 : break;
5608 12 : case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5609 12 : ATExecEnableDisableTrigger(rel, NULL,
5610 : TRIGGER_DISABLED, true,
5611 12 : cmd->recurse,
5612 : lockmode);
5613 12 : break;
5614 :
5615 8 : case AT_EnableRule: /* ENABLE RULE name */
5616 8 : ATExecEnableDisableRule(rel, cmd->name,
5617 : RULE_FIRES_ON_ORIGIN, lockmode);
5618 8 : break;
5619 0 : case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5620 0 : ATExecEnableDisableRule(rel, cmd->name,
5621 : RULE_FIRES_ALWAYS, lockmode);
5622 0 : break;
5623 6 : case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5624 6 : ATExecEnableDisableRule(rel, cmd->name,
5625 : RULE_FIRES_ON_REPLICA, lockmode);
5626 6 : break;
5627 32 : case AT_DisableRule: /* DISABLE RULE name */
5628 32 : ATExecEnableDisableRule(rel, cmd->name,
5629 : RULE_DISABLED, lockmode);
5630 32 : break;
5631 :
5632 446 : case AT_AddInherit:
5633 446 : address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5634 326 : break;
5635 94 : case AT_DropInherit:
5636 94 : address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5637 88 : break;
5638 66 : case AT_AddOf:
5639 66 : address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5640 30 : break;
5641 6 : case AT_DropOf:
5642 6 : ATExecDropOf(rel, lockmode);
5643 6 : break;
5644 512 : case AT_ReplicaIdentity:
5645 512 : ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5646 464 : break;
5647 338 : case AT_EnableRowSecurity:
5648 338 : ATExecSetRowSecurity(rel, true);
5649 338 : break;
5650 10 : case AT_DisableRowSecurity:
5651 10 : ATExecSetRowSecurity(rel, false);
5652 10 : break;
5653 100 : case AT_ForceRowSecurity:
5654 100 : ATExecForceNoForceRowSecurity(rel, true);
5655 100 : break;
5656 32 : case AT_NoForceRowSecurity:
5657 32 : ATExecForceNoForceRowSecurity(rel, false);
5658 32 : break;
5659 58 : case AT_GenericOptions:
5660 58 : ATExecGenericOptions(rel, (List *) cmd->def);
5661 56 : break;
5662 2938 : case AT_AttachPartition:
5663 2938 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5664 : cur_pass, context);
5665 : Assert(cmd != NULL);
5666 2914 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5667 2524 : address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5668 : context);
5669 : else
5670 390 : address = ATExecAttachPartitionIdx(wqueue, rel,
5671 390 : ((PartitionCmd *) cmd->def)->name);
5672 2524 : break;
5673 584 : case AT_DetachPartition:
5674 584 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5675 : cur_pass, context);
5676 : Assert(cmd != NULL);
5677 : /* ATPrepCmd ensures it must be a table */
5678 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5679 584 : address = ATExecDetachPartition(wqueue, tab, rel,
5680 584 : ((PartitionCmd *) cmd->def)->name,
5681 584 : ((PartitionCmd *) cmd->def)->concurrent);
5682 454 : break;
5683 14 : case AT_DetachPartitionFinalize:
5684 14 : address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5685 14 : break;
5686 264 : case AT_MergePartitions:
5687 264 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5688 : cur_pass, context);
5689 : Assert(cmd != NULL);
5690 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5691 180 : ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5692 : context);
5693 138 : break;
5694 378 : case AT_SplitPartition:
5695 378 : cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5696 : cur_pass, context);
5697 : Assert(cmd != NULL);
5698 : Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5699 198 : ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5700 : context);
5701 186 : break;
5702 0 : default: /* oops */
5703 0 : elog(ERROR, "unrecognized alter table type: %d",
5704 : (int) cmd->subtype);
5705 : break;
5706 : }
5707 :
5708 : /*
5709 : * Report the subcommand to interested event triggers.
5710 : */
5711 57976 : if (cmd)
5712 41626 : EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5713 :
5714 : /*
5715 : * Bump the command counter to ensure the next subcommand in the sequence
5716 : * can see the changes so far
5717 : */
5718 57976 : CommandCounterIncrement();
5719 57976 : }
5720 :
5721 : /*
5722 : * ATParseTransformCmd: perform parse transformation for one subcommand
5723 : *
5724 : * Returns the transformed subcommand tree, if there is one, else NULL.
5725 : *
5726 : * The parser may hand back additional AlterTableCmd(s) and/or other
5727 : * utility statements, either before or after the original subcommand.
5728 : * Other AlterTableCmds are scheduled into the appropriate slot of the
5729 : * AlteredTableInfo (they had better be for later passes than the current one).
5730 : * Utility statements that are supposed to happen before the AlterTableCmd
5731 : * are executed immediately. Those that are supposed to happen afterwards
5732 : * are added to the tab->afterStmts list to be done at the very end.
5733 : */
5734 : static AlterTableCmd *
5735 24286 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5736 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5737 : AlterTablePass cur_pass, AlterTableUtilityContext *context)
5738 : {
5739 24286 : AlterTableCmd *newcmd = NULL;
5740 24286 : AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5741 : List *beforeStmts;
5742 : List *afterStmts;
5743 : ListCell *lc;
5744 :
5745 : /* Gin up an AlterTableStmt with just this subcommand and this table */
5746 24286 : atstmt->relation =
5747 24286 : makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5748 24286 : pstrdup(RelationGetRelationName(rel)),
5749 : -1);
5750 24286 : atstmt->relation->inh = recurse;
5751 24286 : atstmt->cmds = list_make1(cmd);
5752 24286 : atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5753 24286 : atstmt->missing_ok = false;
5754 :
5755 : /* Transform the AlterTableStmt */
5756 24286 : atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5757 : atstmt,
5758 : context->queryString,
5759 : &beforeStmts,
5760 : &afterStmts);
5761 :
5762 : /* Execute any statements that should happen before these subcommand(s) */
5763 24442 : foreach(lc, beforeStmts)
5764 : {
5765 498 : Node *stmt = (Node *) lfirst(lc);
5766 :
5767 498 : ProcessUtilityForAlterTable(stmt, context);
5768 486 : CommandCounterIncrement();
5769 : }
5770 :
5771 : /* Examine the transformed subcommands and schedule them appropriately */
5772 56398 : foreach(lc, atstmt->cmds)
5773 : {
5774 32454 : AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5775 : AlterTablePass pass;
5776 :
5777 : /*
5778 : * This switch need only cover the subcommand types that can be added
5779 : * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5780 : * executing the subcommand immediately, as a substitute for the
5781 : * original subcommand. (Note, however, that this does cause
5782 : * AT_AddConstraint subcommands to be rescheduled into later passes,
5783 : * which is important for index and foreign key constraints.)
5784 : *
5785 : * We assume we needn't do any phase-1 checks for added subcommands.
5786 : */
5787 32454 : switch (cmd2->subtype)
5788 : {
5789 1218 : case AT_AddIndex:
5790 1218 : pass = AT_PASS_ADD_INDEX;
5791 1218 : break;
5792 10860 : case AT_AddIndexConstraint:
5793 10860 : pass = AT_PASS_ADD_INDEXCONSTR;
5794 10860 : break;
5795 12782 : case AT_AddConstraint:
5796 : /* Recursion occurs during execution phase */
5797 12782 : if (recurse)
5798 12734 : cmd2->recurse = true;
5799 12782 : switch (castNode(Constraint, cmd2->def)->contype)
5800 : {
5801 9160 : case CONSTR_NOTNULL:
5802 9160 : pass = AT_PASS_COL_ATTRS;
5803 9160 : break;
5804 0 : case CONSTR_PRIMARY:
5805 : case CONSTR_UNIQUE:
5806 : case CONSTR_EXCLUSION:
5807 0 : pass = AT_PASS_ADD_INDEXCONSTR;
5808 0 : break;
5809 3622 : default:
5810 3622 : pass = AT_PASS_ADD_OTHERCONSTR;
5811 3622 : break;
5812 : }
5813 12782 : break;
5814 0 : case AT_AlterColumnGenericOptions:
5815 : /* This command never recurses */
5816 : /* No command-specific prep needed */
5817 0 : pass = AT_PASS_MISC;
5818 0 : break;
5819 7594 : default:
5820 7594 : pass = cur_pass;
5821 7594 : break;
5822 : }
5823 :
5824 32454 : if (pass < cur_pass)
5825 : {
5826 : /* Cannot schedule into a pass we already finished */
5827 0 : elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5828 : pass);
5829 : }
5830 32454 : else if (pass > cur_pass)
5831 : {
5832 : /* OK, queue it up for later */
5833 24860 : tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5834 : }
5835 : else
5836 : {
5837 : /*
5838 : * We should see at most one subcommand for the current pass,
5839 : * which is the transformed version of the original subcommand.
5840 : */
5841 7594 : if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5842 : {
5843 : /* Found the transformed version of our subcommand */
5844 7594 : newcmd = cmd2;
5845 : }
5846 : else
5847 0 : elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5848 : pass);
5849 : }
5850 : }
5851 :
5852 : /* Queue up any after-statements to happen at the end */
5853 23944 : tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5854 :
5855 23944 : return newcmd;
5856 : }
5857 :
5858 : /*
5859 : * ATRewriteTables: ALTER TABLE phase 3
5860 : */
5861 : static void
5862 30140 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5863 : AlterTableUtilityContext *context)
5864 : {
5865 : ListCell *ltab;
5866 :
5867 : /* Go through each table that needs to be checked or rewritten */
5868 64226 : foreach(ltab, *wqueue)
5869 : {
5870 34468 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5871 :
5872 : /* Relations without storage may be ignored here */
5873 34468 : if (!RELKIND_HAS_STORAGE(tab->relkind))
5874 6374 : continue;
5875 :
5876 : /*
5877 : * If we change column data types, the operation has to be propagated
5878 : * to tables that use this table's rowtype as a column type.
5879 : * tab->newvals will also be non-NULL in the case where we're adding a
5880 : * column with a default. We choose to forbid that case as well,
5881 : * since composite types might eventually support defaults.
5882 : *
5883 : * (Eventually we'll probably need to check for composite type
5884 : * dependencies even when we're just scanning the table without a
5885 : * rewrite, but at the moment a composite type does not enforce any
5886 : * constraints, so it's not necessary/appropriate to enforce them just
5887 : * during ALTER.)
5888 : */
5889 28094 : if (tab->newvals != NIL || tab->rewrite > 0)
5890 : {
5891 : Relation rel;
5892 :
5893 1870 : rel = table_open(tab->relid, NoLock);
5894 1870 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5895 1816 : table_close(rel, NoLock);
5896 : }
5897 :
5898 : /*
5899 : * We only need to rewrite the table if at least one column needs to
5900 : * be recomputed, or we are changing its persistence or access method.
5901 : *
5902 : * There are two reasons for requiring a rewrite when changing
5903 : * persistence: on one hand, we need to ensure that the buffers
5904 : * belonging to each of the two relations are marked with or without
5905 : * BM_PERMANENT properly. On the other hand, since rewriting creates
5906 : * and assigns a new relfilenumber, we automatically create or drop an
5907 : * init fork for the relation as appropriate.
5908 : */
5909 28040 : if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5910 1050 : {
5911 : /* Build a temporary relation and copy data */
5912 : Relation OldHeap;
5913 : Oid OIDNewHeap;
5914 : Oid NewAccessMethod;
5915 : Oid NewTableSpace;
5916 : char persistence;
5917 :
5918 1106 : OldHeap = table_open(tab->relid, NoLock);
5919 :
5920 : /*
5921 : * We don't support rewriting of system catalogs; there are too
5922 : * many corner cases and too little benefit. In particular this
5923 : * is certainly not going to work for mapped catalogs.
5924 : */
5925 1106 : if (IsSystemRelation(OldHeap))
5926 0 : ereport(ERROR,
5927 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5928 : errmsg("cannot rewrite system relation \"%s\"",
5929 : RelationGetRelationName(OldHeap))));
5930 :
5931 1106 : if (RelationIsUsedAsCatalogTable(OldHeap))
5932 2 : ereport(ERROR,
5933 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5934 : errmsg("cannot rewrite table \"%s\" used as a catalog table",
5935 : RelationGetRelationName(OldHeap))));
5936 :
5937 : /*
5938 : * Don't allow rewrite on temp tables of other backends ... their
5939 : * local buffer manager is not going to cope. (This is redundant
5940 : * with the check in CheckAlterTableIsSafe, but for safety we'll
5941 : * check here too.)
5942 : */
5943 1104 : if (RELATION_IS_OTHER_TEMP(OldHeap))
5944 0 : ereport(ERROR,
5945 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5946 : errmsg("cannot rewrite temporary tables of other sessions")));
5947 :
5948 : /*
5949 : * Select destination tablespace (same as original unless user
5950 : * requested a change)
5951 : */
5952 1104 : if (tab->newTableSpace)
5953 0 : NewTableSpace = tab->newTableSpace;
5954 : else
5955 1104 : NewTableSpace = OldHeap->rd_rel->reltablespace;
5956 :
5957 : /*
5958 : * Select destination access method (same as original unless user
5959 : * requested a change)
5960 : */
5961 1104 : if (tab->chgAccessMethod)
5962 36 : NewAccessMethod = tab->newAccessMethod;
5963 : else
5964 1068 : NewAccessMethod = OldHeap->rd_rel->relam;
5965 :
5966 : /*
5967 : * Select persistence of transient table (same as original unless
5968 : * user requested a change)
5969 : */
5970 1104 : persistence = tab->chgPersistence ?
5971 1052 : tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5972 :
5973 1104 : table_close(OldHeap, NoLock);
5974 :
5975 : /*
5976 : * Fire off an Event Trigger now, before actually rewriting the
5977 : * table.
5978 : *
5979 : * We don't support Event Trigger for nested commands anywhere,
5980 : * here included, and parsetree is given NULL when coming from
5981 : * AlterTableInternal.
5982 : *
5983 : * And fire it only once.
5984 : */
5985 1104 : if (parsetree)
5986 1104 : EventTriggerTableRewrite((Node *) parsetree,
5987 : tab->relid,
5988 : tab->rewrite);
5989 :
5990 : /*
5991 : * Create transient table that will receive the modified data.
5992 : *
5993 : * Ensure it is marked correctly as logged or unlogged. We have
5994 : * to do this here so that buffers for the new relfilenumber will
5995 : * have the right persistence set, and at the same time ensure
5996 : * that the original filenumbers's buffers will get read in with
5997 : * the correct setting (i.e. the original one). Otherwise a
5998 : * rollback after the rewrite would possibly result with buffers
5999 : * for the original filenumbers having the wrong persistence
6000 : * setting.
6001 : *
6002 : * NB: This relies on swap_relation_files() also swapping the
6003 : * persistence. That wouldn't work for pg_class, but that can't be
6004 : * unlogged anyway.
6005 : */
6006 1098 : OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
6007 : persistence, lockmode);
6008 :
6009 : /*
6010 : * Copy the heap data into the new table with the desired
6011 : * modifications, and test the current data within the table
6012 : * against new constraints generated by ALTER TABLE commands.
6013 : */
6014 1098 : ATRewriteTable(tab, OIDNewHeap);
6015 :
6016 : /*
6017 : * Swap the physical files of the old and new heaps, then rebuild
6018 : * indexes and discard the old heap. We can use RecentXmin for
6019 : * the table's new relfrozenxid because we rewrote all the tuples
6020 : * in ATRewriteTable, so no older Xid remains in the table. Also,
6021 : * we never try to swap toast tables by content, since we have no
6022 : * interest in letting this code work on system catalogs.
6023 : */
6024 1056 : finish_heap_swap(tab->relid, OIDNewHeap,
6025 : false, false, true,
6026 1056 : !OidIsValid(tab->newTableSpace),
6027 : RecentXmin,
6028 : ReadNextMultiXactId(),
6029 : persistence);
6030 :
6031 1050 : InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
6032 : }
6033 26934 : else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6034 : {
6035 24 : if (tab->chgPersistence)
6036 24 : SequenceChangePersistence(tab->relid, tab->newrelpersistence);
6037 : }
6038 : else
6039 : {
6040 : /*
6041 : * If required, test the current data within the table against new
6042 : * constraints generated by ALTER TABLE commands, but don't
6043 : * rebuild data.
6044 : */
6045 26910 : if (tab->constraints != NIL || tab->verify_new_notnull ||
6046 23952 : tab->partition_constraint != NULL)
6047 5008 : ATRewriteTable(tab, InvalidOid);
6048 :
6049 : /*
6050 : * If we had SET TABLESPACE but no reason to reconstruct tuples,
6051 : * just do a block-by-block copy.
6052 : */
6053 26638 : if (tab->newTableSpace)
6054 128 : ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6055 : }
6056 :
6057 : /*
6058 : * Also change persistence of owned sequences, so that it matches the
6059 : * table persistence.
6060 : */
6061 27712 : if (tab->chgPersistence)
6062 : {
6063 76 : List *seqlist = getOwnedSequences(tab->relid);
6064 : ListCell *lc;
6065 :
6066 124 : foreach(lc, seqlist)
6067 : {
6068 48 : Oid seq_relid = lfirst_oid(lc);
6069 :
6070 48 : SequenceChangePersistence(seq_relid, tab->newrelpersistence);
6071 : }
6072 : }
6073 : }
6074 :
6075 : /*
6076 : * Foreign key constraints are checked in a final pass, since (a) it's
6077 : * generally best to examine each one separately, and (b) it's at least
6078 : * theoretically possible that we have changed both relations of the
6079 : * foreign key, and we'd better have finished both rewrites before we try
6080 : * to read the tables.
6081 : */
6082 63574 : foreach(ltab, *wqueue)
6083 : {
6084 33920 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6085 33920 : Relation rel = NULL;
6086 : ListCell *lcon;
6087 :
6088 : /* Relations without storage may be ignored here too */
6089 33920 : if (!RELKIND_HAS_STORAGE(tab->relkind))
6090 6270 : continue;
6091 :
6092 29514 : foreach(lcon, tab->constraints)
6093 : {
6094 1968 : NewConstraint *con = lfirst(lcon);
6095 :
6096 1968 : if (con->contype == CONSTR_FOREIGN)
6097 : {
6098 1178 : Constraint *fkconstraint = (Constraint *) con->qual;
6099 : Relation refrel;
6100 :
6101 1178 : if (rel == NULL)
6102 : {
6103 : /* Long since locked, no need for another */
6104 1166 : rel = table_open(tab->relid, NoLock);
6105 : }
6106 :
6107 1178 : refrel = table_open(con->refrelid, RowShareLock);
6108 :
6109 1178 : validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6110 : con->refindid,
6111 : con->conid,
6112 1178 : con->conwithperiod);
6113 :
6114 : /*
6115 : * No need to mark the constraint row as validated, we did
6116 : * that when we inserted the row earlier.
6117 : */
6118 :
6119 1074 : table_close(refrel, NoLock);
6120 : }
6121 : }
6122 :
6123 27546 : if (rel)
6124 1062 : table_close(rel, NoLock);
6125 : }
6126 :
6127 : /* Finally, run any afterStmts that were queued up */
6128 63426 : foreach(ltab, *wqueue)
6129 : {
6130 33772 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6131 : ListCell *lc;
6132 :
6133 33858 : foreach(lc, tab->afterStmts)
6134 : {
6135 86 : Node *stmt = (Node *) lfirst(lc);
6136 :
6137 86 : ProcessUtilityForAlterTable(stmt, context);
6138 86 : CommandCounterIncrement();
6139 : }
6140 : }
6141 29654 : }
6142 :
6143 : /*
6144 : * ATRewriteTable: scan or rewrite one table
6145 : *
6146 : * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6147 : * must already hold AccessExclusiveLock on it.
6148 : */
6149 : static void
6150 6106 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6151 : {
6152 : Relation oldrel;
6153 : Relation newrel;
6154 : TupleDesc oldTupDesc;
6155 : TupleDesc newTupDesc;
6156 6106 : bool needscan = false;
6157 : List *notnull_attrs;
6158 : List *notnull_virtual_attrs;
6159 : int i;
6160 : ListCell *l;
6161 : EState *estate;
6162 : CommandId mycid;
6163 : BulkInsertState bistate;
6164 : int ti_options;
6165 6106 : ExprState *partqualstate = NULL;
6166 :
6167 : /*
6168 : * Open the relation(s). We have surely already locked the existing
6169 : * table.
6170 : */
6171 6106 : oldrel = table_open(tab->relid, NoLock);
6172 6106 : oldTupDesc = tab->oldDesc;
6173 6106 : newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6174 :
6175 6106 : if (OidIsValid(OIDNewHeap))
6176 : {
6177 : Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6178 : false));
6179 1098 : newrel = table_open(OIDNewHeap, NoLock);
6180 : }
6181 : else
6182 5008 : newrel = NULL;
6183 :
6184 : /*
6185 : * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6186 : * is empty, so don't bother using it.
6187 : */
6188 6106 : if (newrel)
6189 : {
6190 1098 : mycid = GetCurrentCommandId(true);
6191 1098 : bistate = GetBulkInsertState();
6192 1098 : ti_options = TABLE_INSERT_SKIP_FSM;
6193 : }
6194 : else
6195 : {
6196 : /* keep compiler quiet about using these uninitialized */
6197 5008 : mycid = 0;
6198 5008 : bistate = NULL;
6199 5008 : ti_options = 0;
6200 : }
6201 :
6202 : /*
6203 : * Generate the constraint and default execution states
6204 : */
6205 :
6206 6106 : estate = CreateExecutorState();
6207 :
6208 : /* Build the needed expression execution states */
6209 8194 : foreach(l, tab->constraints)
6210 : {
6211 2088 : NewConstraint *con = lfirst(l);
6212 :
6213 2088 : switch (con->contype)
6214 : {
6215 904 : case CONSTR_CHECK:
6216 904 : needscan = true;
6217 904 : con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6218 904 : break;
6219 1184 : case CONSTR_FOREIGN:
6220 : /* Nothing to do here */
6221 1184 : break;
6222 0 : default:
6223 0 : elog(ERROR, "unrecognized constraint type: %d",
6224 : (int) con->contype);
6225 : }
6226 : }
6227 :
6228 : /* Build expression execution states for partition check quals */
6229 6106 : if (tab->partition_constraint)
6230 : {
6231 2198 : needscan = true;
6232 2198 : partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6233 : }
6234 :
6235 7266 : foreach(l, tab->newvals)
6236 : {
6237 1160 : NewColumnValue *ex = lfirst(l);
6238 :
6239 : /* expr already planned */
6240 1160 : ex->exprstate = ExecInitExpr(ex->expr, NULL);
6241 : }
6242 :
6243 6106 : notnull_attrs = notnull_virtual_attrs = NIL;
6244 6106 : if (newrel || tab->verify_new_notnull)
6245 : {
6246 : /*
6247 : * If we are rebuilding the tuples OR if we added any new but not
6248 : * verified not-null constraints, check all *valid* not-null
6249 : * constraints. This is a bit of overkill but it minimizes risk of
6250 : * bugs.
6251 : *
6252 : * notnull_attrs does *not* collect attribute numbers for valid
6253 : * not-null constraints over virtual generated columns; instead, they
6254 : * are collected in notnull_virtual_attrs for verification elsewhere.
6255 : */
6256 7782 : for (i = 0; i < newTupDesc->natts; i++)
6257 : {
6258 5644 : CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6259 :
6260 5644 : if (attr->attnullability == ATTNULLABLE_VALID &&
6261 2120 : !attr->attisdropped)
6262 : {
6263 2120 : Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6264 :
6265 2120 : if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6266 2030 : notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6267 : else
6268 90 : notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6269 90 : wholeatt->attnum);
6270 : }
6271 : }
6272 2138 : if (notnull_attrs || notnull_virtual_attrs)
6273 1558 : needscan = true;
6274 : }
6275 :
6276 6106 : if (newrel || needscan)
6277 : {
6278 : ExprContext *econtext;
6279 : TupleTableSlot *oldslot;
6280 : TupleTableSlot *newslot;
6281 : TableScanDesc scan;
6282 : MemoryContext oldCxt;
6283 5124 : List *dropped_attrs = NIL;
6284 : ListCell *lc;
6285 : Snapshot snapshot;
6286 5124 : ResultRelInfo *rInfo = NULL;
6287 :
6288 : /*
6289 : * When adding or changing a virtual generated column with a not-null
6290 : * constraint, we need to evaluate whether the generation expression
6291 : * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6292 : * prepare a dummy ResultRelInfo.
6293 : */
6294 5124 : if (notnull_virtual_attrs != NIL)
6295 : {
6296 : MemoryContext oldcontext;
6297 :
6298 : Assert(newTupDesc->constr->has_generated_virtual);
6299 : Assert(newTupDesc->constr->has_not_null);
6300 60 : oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6301 60 : rInfo = makeNode(ResultRelInfo);
6302 60 : InitResultRelInfo(rInfo,
6303 : oldrel,
6304 : 0, /* dummy rangetable index */
6305 : NULL,
6306 : estate->es_instrument);
6307 60 : MemoryContextSwitchTo(oldcontext);
6308 : }
6309 :
6310 5124 : if (newrel)
6311 1098 : ereport(DEBUG1,
6312 : (errmsg_internal("rewriting table \"%s\"",
6313 : RelationGetRelationName(oldrel))));
6314 : else
6315 4026 : ereport(DEBUG1,
6316 : (errmsg_internal("verifying table \"%s\"",
6317 : RelationGetRelationName(oldrel))));
6318 :
6319 5124 : if (newrel)
6320 : {
6321 : /*
6322 : * All predicate locks on the tuples or pages are about to be made
6323 : * invalid, because we move tuples around. Promote them to
6324 : * relation locks.
6325 : */
6326 1098 : TransferPredicateLocksToHeapRelation(oldrel);
6327 : }
6328 :
6329 5124 : econtext = GetPerTupleExprContext(estate);
6330 :
6331 : /*
6332 : * Create necessary tuple slots. When rewriting, two slots are needed,
6333 : * otherwise one suffices. In the case where one slot suffices, we
6334 : * need to use the new tuple descriptor, otherwise some constraints
6335 : * can't be evaluated. Note that even when the tuple layout is the
6336 : * same and no rewrite is required, the tupDescs might not be
6337 : * (consider ADD COLUMN without a default).
6338 : */
6339 5124 : if (tab->rewrite)
6340 : {
6341 : Assert(newrel != NULL);
6342 1098 : oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6343 : table_slot_callbacks(oldrel));
6344 1098 : newslot = MakeSingleTupleTableSlot(newTupDesc,
6345 : table_slot_callbacks(newrel));
6346 :
6347 : /*
6348 : * Set all columns in the new slot to NULL initially, to ensure
6349 : * columns added as part of the rewrite are initialized to NULL.
6350 : * That is necessary as tab->newvals will not contain an
6351 : * expression for columns with a NULL default, e.g. when adding a
6352 : * column without a default together with a column with a default
6353 : * requiring an actual rewrite.
6354 : */
6355 1098 : ExecStoreAllNullTuple(newslot);
6356 : }
6357 : else
6358 : {
6359 4026 : oldslot = MakeSingleTupleTableSlot(newTupDesc,
6360 : table_slot_callbacks(oldrel));
6361 4026 : newslot = NULL;
6362 : }
6363 :
6364 : /*
6365 : * Any attributes that are dropped according to the new tuple
6366 : * descriptor can be set to NULL. We precompute the list of dropped
6367 : * attributes to avoid needing to do so in the per-tuple loop.
6368 : */
6369 18010 : for (i = 0; i < newTupDesc->natts; i++)
6370 : {
6371 12886 : if (TupleDescAttr(newTupDesc, i)->attisdropped)
6372 826 : dropped_attrs = lappend_int(dropped_attrs, i);
6373 : }
6374 :
6375 : /*
6376 : * Scan through the rows, generating a new row if needed and then
6377 : * checking all the constraints.
6378 : */
6379 5124 : snapshot = RegisterSnapshot(GetLatestSnapshot());
6380 5124 : scan = table_beginscan(oldrel, snapshot, 0, NULL);
6381 :
6382 : /*
6383 : * Switch to per-tuple memory context and reset it for each tuple
6384 : * produced, so we don't leak memory.
6385 : */
6386 5124 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6387 :
6388 775480 : while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6389 : {
6390 : TupleTableSlot *insertslot;
6391 :
6392 765546 : if (tab->rewrite > 0)
6393 : {
6394 : /* Extract data from old tuple */
6395 100122 : slot_getallattrs(oldslot);
6396 100122 : ExecClearTuple(newslot);
6397 :
6398 : /* copy attributes */
6399 100122 : memcpy(newslot->tts_values, oldslot->tts_values,
6400 100122 : sizeof(Datum) * oldslot->tts_nvalid);
6401 100122 : memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6402 100122 : sizeof(bool) * oldslot->tts_nvalid);
6403 :
6404 : /* Set dropped attributes to null in new tuple */
6405 100238 : foreach(lc, dropped_attrs)
6406 116 : newslot->tts_isnull[lfirst_int(lc)] = true;
6407 :
6408 : /*
6409 : * Constraints and GENERATED expressions might reference the
6410 : * tableoid column, so fill tts_tableOid with the desired
6411 : * value. (We must do this each time, because it gets
6412 : * overwritten with newrel's OID during storing.)
6413 : */
6414 100122 : newslot->tts_tableOid = RelationGetRelid(oldrel);
6415 :
6416 : /*
6417 : * Process supplied expressions to replace selected columns.
6418 : *
6419 : * First, evaluate expressions whose inputs come from the old
6420 : * tuple.
6421 : */
6422 100122 : econtext->ecxt_scantuple = oldslot;
6423 :
6424 206196 : foreach(l, tab->newvals)
6425 : {
6426 106086 : NewColumnValue *ex = lfirst(l);
6427 :
6428 106086 : if (ex->is_generated)
6429 312 : continue;
6430 :
6431 105774 : newslot->tts_values[ex->attnum - 1]
6432 105762 : = ExecEvalExpr(ex->exprstate,
6433 : econtext,
6434 105774 : &newslot->tts_isnull[ex->attnum - 1]);
6435 : }
6436 :
6437 100110 : ExecStoreVirtualTuple(newslot);
6438 :
6439 : /*
6440 : * Now, evaluate any expressions whose inputs come from the
6441 : * new tuple. We assume these columns won't reference each
6442 : * other, so that there's no ordering dependency.
6443 : */
6444 100110 : econtext->ecxt_scantuple = newslot;
6445 :
6446 206184 : foreach(l, tab->newvals)
6447 : {
6448 106074 : NewColumnValue *ex = lfirst(l);
6449 :
6450 106074 : if (!ex->is_generated)
6451 105762 : continue;
6452 :
6453 312 : newslot->tts_values[ex->attnum - 1]
6454 312 : = ExecEvalExpr(ex->exprstate,
6455 : econtext,
6456 312 : &newslot->tts_isnull[ex->attnum - 1]);
6457 : }
6458 :
6459 100110 : insertslot = newslot;
6460 : }
6461 : else
6462 : {
6463 : /*
6464 : * If there's no rewrite, old and new table are guaranteed to
6465 : * have the same AM, so we can just use the old slot to verify
6466 : * new constraints etc.
6467 : */
6468 665424 : insertslot = oldslot;
6469 : }
6470 :
6471 : /* Now check any constraints on the possibly-changed tuple */
6472 765534 : econtext->ecxt_scantuple = insertslot;
6473 :
6474 4107338 : foreach_int(attn, notnull_attrs)
6475 : {
6476 2576474 : if (slot_attisnull(insertslot, attn))
6477 : {
6478 102 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6479 :
6480 102 : ereport(ERROR,
6481 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
6482 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6483 : NameStr(attr->attname),
6484 : RelationGetRelationName(oldrel)),
6485 : errtablecol(oldrel, attn)));
6486 : }
6487 : }
6488 :
6489 765432 : if (notnull_virtual_attrs != NIL)
6490 : {
6491 : AttrNumber attnum;
6492 :
6493 84 : attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6494 : estate,
6495 : notnull_virtual_attrs);
6496 84 : if (attnum != InvalidAttrNumber)
6497 : {
6498 30 : Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6499 :
6500 30 : ereport(ERROR,
6501 : errcode(ERRCODE_NOT_NULL_VIOLATION),
6502 : errmsg("column \"%s\" of relation \"%s\" contains null values",
6503 : NameStr(attr->attname),
6504 : RelationGetRelationName(oldrel)),
6505 : errtablecol(oldrel, attnum));
6506 : }
6507 : }
6508 :
6509 773562 : foreach(l, tab->constraints)
6510 : {
6511 8256 : NewConstraint *con = lfirst(l);
6512 :
6513 8256 : switch (con->contype)
6514 : {
6515 8150 : case CONSTR_CHECK:
6516 8150 : if (!ExecCheck(con->qualstate, econtext))
6517 96 : ereport(ERROR,
6518 : (errcode(ERRCODE_CHECK_VIOLATION),
6519 : errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6520 : con->name,
6521 : RelationGetRelationName(oldrel)),
6522 : errtableconstraint(oldrel, con->name)));
6523 8054 : break;
6524 106 : case CONSTR_NOTNULL:
6525 : case CONSTR_FOREIGN:
6526 : /* Nothing to do here */
6527 106 : break;
6528 0 : default:
6529 0 : elog(ERROR, "unrecognized constraint type: %d",
6530 : (int) con->contype);
6531 : }
6532 : }
6533 :
6534 765306 : if (partqualstate && !ExecCheck(partqualstate, econtext))
6535 : {
6536 74 : if (tab->validate_default)
6537 26 : ereport(ERROR,
6538 : (errcode(ERRCODE_CHECK_VIOLATION),
6539 : errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6540 : RelationGetRelationName(oldrel)),
6541 : errtable(oldrel)));
6542 : else
6543 48 : ereport(ERROR,
6544 : (errcode(ERRCODE_CHECK_VIOLATION),
6545 : errmsg("partition constraint of relation \"%s\" is violated by some row",
6546 : RelationGetRelationName(oldrel)),
6547 : errtable(oldrel)));
6548 : }
6549 :
6550 : /* Write the tuple out to the new relation */
6551 765232 : if (newrel)
6552 100080 : table_tuple_insert(newrel, insertslot, mycid,
6553 : ti_options, bistate);
6554 :
6555 765232 : ResetExprContext(econtext);
6556 :
6557 765232 : CHECK_FOR_INTERRUPTS();
6558 : }
6559 :
6560 4810 : MemoryContextSwitchTo(oldCxt);
6561 4810 : table_endscan(scan);
6562 4810 : UnregisterSnapshot(snapshot);
6563 :
6564 4810 : ExecDropSingleTupleTableSlot(oldslot);
6565 4810 : if (newslot)
6566 1056 : ExecDropSingleTupleTableSlot(newslot);
6567 : }
6568 :
6569 5792 : FreeExecutorState(estate);
6570 :
6571 5792 : table_close(oldrel, NoLock);
6572 5792 : if (newrel)
6573 : {
6574 1056 : FreeBulkInsertState(bistate);
6575 :
6576 1056 : table_finish_bulk_insert(newrel, ti_options);
6577 :
6578 1056 : table_close(newrel, NoLock);
6579 : }
6580 5792 : }
6581 :
6582 : /*
6583 : * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6584 : */
6585 : static AlteredTableInfo *
6586 44472 : ATGetQueueEntry(List **wqueue, Relation rel)
6587 : {
6588 44472 : Oid relid = RelationGetRelid(rel);
6589 : AlteredTableInfo *tab;
6590 : ListCell *ltab;
6591 :
6592 56716 : foreach(ltab, *wqueue)
6593 : {
6594 17832 : tab = (AlteredTableInfo *) lfirst(ltab);
6595 17832 : if (tab->relid == relid)
6596 5588 : return tab;
6597 : }
6598 :
6599 : /*
6600 : * Not there, so add it. Note that we make a copy of the relation's
6601 : * existing descriptor before anything interesting can happen to it.
6602 : */
6603 38884 : tab = palloc0_object(AlteredTableInfo);
6604 38884 : tab->relid = relid;
6605 38884 : tab->rel = NULL; /* set later */
6606 38884 : tab->relkind = rel->rd_rel->relkind;
6607 38884 : tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6608 38884 : tab->newAccessMethod = InvalidOid;
6609 38884 : tab->chgAccessMethod = false;
6610 38884 : tab->newTableSpace = InvalidOid;
6611 38884 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6612 38884 : tab->chgPersistence = false;
6613 :
6614 38884 : *wqueue = lappend(*wqueue, tab);
6615 :
6616 38884 : return tab;
6617 : }
6618 :
6619 : static const char *
6620 86 : alter_table_type_to_string(AlterTableType cmdtype)
6621 : {
6622 86 : switch (cmdtype)
6623 : {
6624 0 : case AT_AddColumn:
6625 : case AT_AddColumnToView:
6626 0 : return "ADD COLUMN";
6627 0 : case AT_ColumnDefault:
6628 : case AT_CookedColumnDefault:
6629 0 : return "ALTER COLUMN ... SET DEFAULT";
6630 6 : case AT_DropNotNull:
6631 6 : return "ALTER COLUMN ... DROP NOT NULL";
6632 6 : case AT_SetNotNull:
6633 6 : return "ALTER COLUMN ... SET NOT NULL";
6634 0 : case AT_SetExpression:
6635 0 : return "ALTER COLUMN ... SET EXPRESSION";
6636 0 : case AT_DropExpression:
6637 0 : return "ALTER COLUMN ... DROP EXPRESSION";
6638 0 : case AT_SetStatistics:
6639 0 : return "ALTER COLUMN ... SET STATISTICS";
6640 12 : case AT_SetOptions:
6641 12 : return "ALTER COLUMN ... SET";
6642 0 : case AT_ResetOptions:
6643 0 : return "ALTER COLUMN ... RESET";
6644 0 : case AT_SetStorage:
6645 0 : return "ALTER COLUMN ... SET STORAGE";
6646 0 : case AT_SetCompression:
6647 0 : return "ALTER COLUMN ... SET COMPRESSION";
6648 6 : case AT_DropColumn:
6649 6 : return "DROP COLUMN";
6650 0 : case AT_AddIndex:
6651 : case AT_ReAddIndex:
6652 0 : return NULL; /* not real grammar */
6653 0 : case AT_AddConstraint:
6654 : case AT_ReAddConstraint:
6655 : case AT_ReAddDomainConstraint:
6656 : case AT_AddIndexConstraint:
6657 0 : return "ADD CONSTRAINT";
6658 6 : case AT_AlterConstraint:
6659 6 : return "ALTER CONSTRAINT";
6660 0 : case AT_ValidateConstraint:
6661 0 : return "VALIDATE CONSTRAINT";
6662 0 : case AT_DropConstraint:
6663 0 : return "DROP CONSTRAINT";
6664 0 : case AT_ReAddComment:
6665 0 : return NULL; /* not real grammar */
6666 0 : case AT_AlterColumnType:
6667 0 : return "ALTER COLUMN ... SET DATA TYPE";
6668 0 : case AT_AlterColumnGenericOptions:
6669 0 : return "ALTER COLUMN ... OPTIONS";
6670 0 : case AT_ChangeOwner:
6671 0 : return "OWNER TO";
6672 0 : case AT_ClusterOn:
6673 0 : return "CLUSTER ON";
6674 0 : case AT_DropCluster:
6675 0 : return "SET WITHOUT CLUSTER";
6676 0 : case AT_SetAccessMethod:
6677 0 : return "SET ACCESS METHOD";
6678 6 : case AT_SetLogged:
6679 6 : return "SET LOGGED";
6680 6 : case AT_SetUnLogged:
6681 6 : return "SET UNLOGGED";
6682 0 : case AT_DropOids:
6683 0 : return "SET WITHOUT OIDS";
6684 0 : case AT_SetTableSpace:
6685 0 : return "SET TABLESPACE";
6686 2 : case AT_SetRelOptions:
6687 2 : return "SET";
6688 0 : case AT_ResetRelOptions:
6689 0 : return "RESET";
6690 0 : case AT_ReplaceRelOptions:
6691 0 : return NULL; /* not real grammar */
6692 0 : case AT_EnableTrig:
6693 0 : return "ENABLE TRIGGER";
6694 0 : case AT_EnableAlwaysTrig:
6695 0 : return "ENABLE ALWAYS TRIGGER";
6696 0 : case AT_EnableReplicaTrig:
6697 0 : return "ENABLE REPLICA TRIGGER";
6698 0 : case AT_DisableTrig:
6699 0 : return "DISABLE TRIGGER";
6700 0 : case AT_EnableTrigAll:
6701 0 : return "ENABLE TRIGGER ALL";
6702 0 : case AT_DisableTrigAll:
6703 0 : return "DISABLE TRIGGER ALL";
6704 0 : case AT_EnableTrigUser:
6705 0 : return "ENABLE TRIGGER USER";
6706 0 : case AT_DisableTrigUser:
6707 0 : return "DISABLE TRIGGER USER";
6708 0 : case AT_EnableRule:
6709 0 : return "ENABLE RULE";
6710 0 : case AT_EnableAlwaysRule:
6711 0 : return "ENABLE ALWAYS RULE";
6712 0 : case AT_EnableReplicaRule:
6713 0 : return "ENABLE REPLICA RULE";
6714 0 : case AT_DisableRule:
6715 0 : return "DISABLE RULE";
6716 0 : case AT_AddInherit:
6717 0 : return "INHERIT";
6718 0 : case AT_DropInherit:
6719 0 : return "NO INHERIT";
6720 0 : case AT_AddOf:
6721 0 : return "OF";
6722 0 : case AT_DropOf:
6723 0 : return "NOT OF";
6724 0 : case AT_ReplicaIdentity:
6725 0 : return "REPLICA IDENTITY";
6726 0 : case AT_EnableRowSecurity:
6727 0 : return "ENABLE ROW SECURITY";
6728 0 : case AT_DisableRowSecurity:
6729 0 : return "DISABLE ROW SECURITY";
6730 0 : case AT_ForceRowSecurity:
6731 0 : return "FORCE ROW SECURITY";
6732 0 : case AT_NoForceRowSecurity:
6733 0 : return "NO FORCE ROW SECURITY";
6734 0 : case AT_GenericOptions:
6735 0 : return "OPTIONS";
6736 6 : case AT_AttachPartition:
6737 6 : return "ATTACH PARTITION";
6738 18 : case AT_DetachPartition:
6739 18 : return "DETACH PARTITION";
6740 6 : case AT_DetachPartitionFinalize:
6741 6 : return "DETACH PARTITION ... FINALIZE";
6742 0 : case AT_MergePartitions:
6743 0 : return "MERGE PARTITIONS";
6744 6 : case AT_SplitPartition:
6745 6 : return "SPLIT PARTITION";
6746 0 : case AT_AddIdentity:
6747 0 : return "ALTER COLUMN ... ADD IDENTITY";
6748 0 : case AT_SetIdentity:
6749 0 : return "ALTER COLUMN ... SET";
6750 0 : case AT_DropIdentity:
6751 0 : return "ALTER COLUMN ... DROP IDENTITY";
6752 0 : case AT_ReAddStatistics:
6753 0 : return NULL; /* not real grammar */
6754 : }
6755 :
6756 0 : return NULL;
6757 : }
6758 :
6759 : /*
6760 : * ATSimplePermissions
6761 : *
6762 : * - Ensure that it is a relation (or possibly a view)
6763 : * - Ensure this user is the owner
6764 : * - Ensure that it is not a system table
6765 : */
6766 : static void
6767 39446 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6768 : {
6769 : int actual_target;
6770 :
6771 39446 : switch (rel->rd_rel->relkind)
6772 : {
6773 30642 : case RELKIND_RELATION:
6774 30642 : actual_target = ATT_TABLE;
6775 30642 : break;
6776 6522 : case RELKIND_PARTITIONED_TABLE:
6777 6522 : actual_target = ATT_PARTITIONED_TABLE;
6778 6522 : break;
6779 402 : case RELKIND_VIEW:
6780 402 : actual_target = ATT_VIEW;
6781 402 : break;
6782 46 : case RELKIND_MATVIEW:
6783 46 : actual_target = ATT_MATVIEW;
6784 46 : break;
6785 228 : case RELKIND_INDEX:
6786 228 : actual_target = ATT_INDEX;
6787 228 : break;
6788 432 : case RELKIND_PARTITIONED_INDEX:
6789 432 : actual_target = ATT_PARTITIONED_INDEX;
6790 432 : break;
6791 216 : case RELKIND_COMPOSITE_TYPE:
6792 216 : actual_target = ATT_COMPOSITE_TYPE;
6793 216 : break;
6794 932 : case RELKIND_FOREIGN_TABLE:
6795 932 : actual_target = ATT_FOREIGN_TABLE;
6796 932 : break;
6797 24 : case RELKIND_SEQUENCE:
6798 24 : actual_target = ATT_SEQUENCE;
6799 24 : break;
6800 2 : default:
6801 2 : actual_target = 0;
6802 2 : break;
6803 : }
6804 :
6805 : /* Wrong target type? */
6806 39446 : if ((actual_target & allowed_targets) == 0)
6807 : {
6808 86 : const char *action_str = alter_table_type_to_string(cmdtype);
6809 :
6810 86 : if (action_str)
6811 86 : ereport(ERROR,
6812 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6813 : /* translator: %s is a group of some SQL keywords */
6814 : errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6815 : action_str, RelationGetRelationName(rel)),
6816 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6817 : else
6818 : /* internal error? */
6819 0 : elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6820 : RelationGetRelationName(rel));
6821 : }
6822 :
6823 : /* Permissions checks */
6824 39360 : if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6825 12 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6826 12 : RelationGetRelationName(rel));
6827 :
6828 39348 : if (!allowSystemTableMods && IsSystemRelation(rel))
6829 0 : ereport(ERROR,
6830 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6831 : errmsg("permission denied: \"%s\" is a system catalog",
6832 : RelationGetRelationName(rel))));
6833 39348 : }
6834 :
6835 : /*
6836 : * ATSimpleRecursion
6837 : *
6838 : * Simple table recursion sufficient for most ALTER TABLE operations.
6839 : * All direct and indirect children are processed in an unspecified order.
6840 : * Note that if a child inherits from the original table via multiple
6841 : * inheritance paths, it will be visited just once.
6842 : */
6843 : static void
6844 1354 : ATSimpleRecursion(List **wqueue, Relation rel,
6845 : AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6846 : AlterTableUtilityContext *context)
6847 : {
6848 : /*
6849 : * Propagate to children, if desired and if there are (or might be) any
6850 : * children.
6851 : */
6852 1354 : if (recurse && rel->rd_rel->relhassubclass)
6853 : {
6854 84 : Oid relid = RelationGetRelid(rel);
6855 : ListCell *child;
6856 : List *children;
6857 :
6858 84 : children = find_all_inheritors(relid, lockmode, NULL);
6859 :
6860 : /*
6861 : * find_all_inheritors does the recursive search of the inheritance
6862 : * hierarchy, so all we have to do is process all of the relids in the
6863 : * list that it returns.
6864 : */
6865 366 : foreach(child, children)
6866 : {
6867 282 : Oid childrelid = lfirst_oid(child);
6868 : Relation childrel;
6869 :
6870 282 : if (childrelid == relid)
6871 84 : continue;
6872 : /* find_all_inheritors already got lock */
6873 198 : childrel = relation_open(childrelid, NoLock);
6874 198 : CheckAlterTableIsSafe(childrel);
6875 198 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6876 198 : relation_close(childrel, NoLock);
6877 : }
6878 : }
6879 1354 : }
6880 :
6881 : /*
6882 : * Obtain list of partitions of the given table, locking them all at the given
6883 : * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6884 : *
6885 : * This function is a no-op if the given relation is not a partitioned table;
6886 : * in particular, nothing is done if it's a legacy inheritance parent.
6887 : */
6888 : static void
6889 818 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6890 : {
6891 818 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6892 : {
6893 : List *inh;
6894 : ListCell *cell;
6895 :
6896 176 : inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6897 : /* first element is the parent rel; must ignore it */
6898 574 : for_each_from(cell, inh, 1)
6899 : {
6900 : Relation childrel;
6901 :
6902 : /* find_all_inheritors already got lock */
6903 404 : childrel = table_open(lfirst_oid(cell), NoLock);
6904 404 : CheckAlterTableIsSafe(childrel);
6905 398 : table_close(childrel, NoLock);
6906 : }
6907 170 : list_free(inh);
6908 : }
6909 812 : }
6910 :
6911 : /*
6912 : * ATTypedTableRecursion
6913 : *
6914 : * Propagate ALTER TYPE operations to the typed tables of that type.
6915 : * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6916 : * recursion to inheritance children of the typed tables.
6917 : */
6918 : static void
6919 192 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6920 : LOCKMODE lockmode, AlterTableUtilityContext *context)
6921 : {
6922 : ListCell *child;
6923 : List *children;
6924 :
6925 : Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6926 :
6927 192 : children = find_typed_table_dependencies(rel->rd_rel->reltype,
6928 192 : RelationGetRelationName(rel),
6929 : cmd->behavior);
6930 :
6931 204 : foreach(child, children)
6932 : {
6933 30 : Oid childrelid = lfirst_oid(child);
6934 : Relation childrel;
6935 :
6936 30 : childrel = relation_open(childrelid, lockmode);
6937 30 : CheckAlterTableIsSafe(childrel);
6938 30 : ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6939 30 : relation_close(childrel, NoLock);
6940 : }
6941 174 : }
6942 :
6943 :
6944 : /*
6945 : * find_composite_type_dependencies
6946 : *
6947 : * Check to see if the type "typeOid" is being used as a column in some table
6948 : * (possibly nested several levels deep in composite types, arrays, etc!).
6949 : * Eventually, we'd like to propagate the check or rewrite operation
6950 : * into such tables, but for now, just error out if we find any.
6951 : *
6952 : * Caller should provide either the associated relation of a rowtype,
6953 : * or a type name (not both) for use in the error message, if any.
6954 : *
6955 : * Note that "typeOid" is not necessarily a composite type; it could also be
6956 : * another container type such as an array or range, or a domain over one of
6957 : * these things. The name of this function is therefore somewhat historical,
6958 : * but it's not worth changing.
6959 : *
6960 : * We assume that functions and views depending on the type are not reasons
6961 : * to reject the ALTER. (How safe is this really?)
6962 : */
6963 : void
6964 4796 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6965 : const char *origTypeName)
6966 : {
6967 : Relation depRel;
6968 : ScanKeyData key[2];
6969 : SysScanDesc depScan;
6970 : HeapTuple depTup;
6971 :
6972 : /* since this function recurses, it could be driven to stack overflow */
6973 4796 : check_stack_depth();
6974 :
6975 : /*
6976 : * We scan pg_depend to find those things that depend on the given type.
6977 : * (We assume we can ignore refobjsubid for a type.)
6978 : */
6979 4796 : depRel = table_open(DependRelationId, AccessShareLock);
6980 :
6981 4796 : ScanKeyInit(&key[0],
6982 : Anum_pg_depend_refclassid,
6983 : BTEqualStrategyNumber, F_OIDEQ,
6984 : ObjectIdGetDatum(TypeRelationId));
6985 4796 : ScanKeyInit(&key[1],
6986 : Anum_pg_depend_refobjid,
6987 : BTEqualStrategyNumber, F_OIDEQ,
6988 : ObjectIdGetDatum(typeOid));
6989 :
6990 4796 : depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6991 : NULL, 2, key);
6992 :
6993 7362 : while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6994 : {
6995 2722 : Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6996 : Relation rel;
6997 : TupleDesc tupleDesc;
6998 : Form_pg_attribute att;
6999 :
7000 : /* Check for directly dependent types */
7001 2722 : if (pg_depend->classid == TypeRelationId)
7002 : {
7003 : /*
7004 : * This must be an array, domain, or range containing the given
7005 : * type, so recursively check for uses of this type. Note that
7006 : * any error message will mention the original type not the
7007 : * container; this is intentional.
7008 : */
7009 2316 : find_composite_type_dependencies(pg_depend->objid,
7010 : origRelation, origTypeName);
7011 2292 : continue;
7012 : }
7013 :
7014 : /* Else, ignore dependees that aren't relations */
7015 406 : if (pg_depend->classid != RelationRelationId)
7016 122 : continue;
7017 :
7018 284 : rel = relation_open(pg_depend->objid, AccessShareLock);
7019 284 : tupleDesc = RelationGetDescr(rel);
7020 :
7021 : /*
7022 : * If objsubid identifies a specific column, refer to that in error
7023 : * messages. Otherwise, search to see if there's a user column of the
7024 : * type. (We assume system columns are never of interesting types.)
7025 : * The search is needed because an index containing an expression
7026 : * column of the target type will just be recorded as a whole-relation
7027 : * dependency. If we do not find a column of the type, the dependency
7028 : * must indicate that the type is transiently referenced in an index
7029 : * expression but not stored on disk, which we assume is OK, just as
7030 : * we do for references in views. (It could also be that the target
7031 : * type is embedded in some container type that is stored in an index
7032 : * column, but the previous recursion should catch such cases.)
7033 : */
7034 284 : if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
7035 126 : att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7036 : else
7037 : {
7038 158 : att = NULL;
7039 406 : for (int attno = 1; attno <= tupleDesc->natts; attno++)
7040 : {
7041 254 : att = TupleDescAttr(tupleDesc, attno - 1);
7042 254 : if (att->atttypid == typeOid && !att->attisdropped)
7043 6 : break;
7044 248 : att = NULL;
7045 : }
7046 158 : if (att == NULL)
7047 : {
7048 : /* No such column, so assume OK */
7049 152 : relation_close(rel, AccessShareLock);
7050 152 : continue;
7051 : }
7052 : }
7053 :
7054 : /*
7055 : * We definitely should reject if the relation has storage. If it's
7056 : * partitioned, then perhaps we don't have to reject: if there are
7057 : * partitions then we'll fail when we find one, else there is no
7058 : * stored data to worry about. However, it's possible that the type
7059 : * change would affect conclusions about whether the type is sortable
7060 : * or hashable and thus (if it's a partitioning column) break the
7061 : * partitioning rule. For now, reject for partitioned rels too.
7062 : */
7063 132 : if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7064 0 : RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7065 : {
7066 132 : if (origTypeName)
7067 30 : ereport(ERROR,
7068 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7069 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7070 : origTypeName,
7071 : RelationGetRelationName(rel),
7072 : NameStr(att->attname))));
7073 102 : else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7074 18 : ereport(ERROR,
7075 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7076 : errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7077 : RelationGetRelationName(origRelation),
7078 : RelationGetRelationName(rel),
7079 : NameStr(att->attname))));
7080 84 : else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7081 6 : ereport(ERROR,
7082 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7083 : errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7084 : RelationGetRelationName(origRelation),
7085 : RelationGetRelationName(rel),
7086 : NameStr(att->attname))));
7087 : else
7088 78 : ereport(ERROR,
7089 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7090 : errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7091 : RelationGetRelationName(origRelation),
7092 : RelationGetRelationName(rel),
7093 : NameStr(att->attname))));
7094 : }
7095 0 : else if (OidIsValid(rel->rd_rel->reltype))
7096 : {
7097 : /*
7098 : * A view or composite type itself isn't a problem, but we must
7099 : * recursively check for indirect dependencies via its rowtype.
7100 : */
7101 0 : find_composite_type_dependencies(rel->rd_rel->reltype,
7102 : origRelation, origTypeName);
7103 : }
7104 :
7105 0 : relation_close(rel, AccessShareLock);
7106 : }
7107 :
7108 4640 : systable_endscan(depScan);
7109 :
7110 4640 : relation_close(depRel, AccessShareLock);
7111 4640 : }
7112 :
7113 :
7114 : /*
7115 : * find_typed_table_dependencies
7116 : *
7117 : * Check to see if a composite type is being used as the type of a
7118 : * typed table. Abort if any are found and behavior is RESTRICT.
7119 : * Else return the list of tables.
7120 : */
7121 : static List *
7122 216 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7123 : {
7124 : Relation classRel;
7125 : ScanKeyData key[1];
7126 : TableScanDesc scan;
7127 : HeapTuple tuple;
7128 216 : List *result = NIL;
7129 :
7130 216 : classRel = table_open(RelationRelationId, AccessShareLock);
7131 :
7132 216 : ScanKeyInit(&key[0],
7133 : Anum_pg_class_reloftype,
7134 : BTEqualStrategyNumber, F_OIDEQ,
7135 : ObjectIdGetDatum(typeOid));
7136 :
7137 216 : scan = table_beginscan_catalog(classRel, 1, key);
7138 :
7139 252 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7140 : {
7141 60 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7142 :
7143 60 : if (behavior == DROP_RESTRICT)
7144 24 : ereport(ERROR,
7145 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7146 : errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7147 : typeName),
7148 : errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7149 : else
7150 36 : result = lappend_oid(result, classform->oid);
7151 : }
7152 :
7153 192 : table_endscan(scan);
7154 192 : table_close(classRel, AccessShareLock);
7155 :
7156 192 : return result;
7157 : }
7158 :
7159 :
7160 : /*
7161 : * check_of_type
7162 : *
7163 : * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7164 : * isn't suitable, throw an error. Currently, we require that the type
7165 : * originated with CREATE TYPE AS. We could support any row type, but doing so
7166 : * would require handling a number of extra corner cases in the DDL commands.
7167 : * (Also, allowing domain-over-composite would open up a can of worms about
7168 : * whether and how the domain's constraints should apply to derived tables.)
7169 : */
7170 : void
7171 182 : check_of_type(HeapTuple typetuple)
7172 : {
7173 182 : Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7174 182 : bool typeOk = false;
7175 :
7176 182 : if (typ->typtype == TYPTYPE_COMPOSITE)
7177 : {
7178 : Relation typeRelation;
7179 :
7180 : Assert(OidIsValid(typ->typrelid));
7181 176 : typeRelation = relation_open(typ->typrelid, AccessShareLock);
7182 176 : typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7183 :
7184 : /*
7185 : * Close the parent rel, but keep our AccessShareLock on it until xact
7186 : * commit. That will prevent someone else from deleting or ALTERing
7187 : * the type before the typed table creation/conversion commits.
7188 : */
7189 176 : relation_close(typeRelation, NoLock);
7190 :
7191 176 : if (!typeOk)
7192 6 : ereport(ERROR,
7193 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7194 : errmsg("type %s is the row type of another table",
7195 : format_type_be(typ->oid)),
7196 : errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7197 : }
7198 : else
7199 6 : ereport(ERROR,
7200 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7201 : errmsg("type %s is not a composite type",
7202 : format_type_be(typ->oid))));
7203 170 : }
7204 :
7205 :
7206 : /*
7207 : * ALTER TABLE ADD COLUMN
7208 : *
7209 : * Adds an additional attribute to a relation making the assumption that
7210 : * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7211 : * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7212 : * AlterTableCmd's.
7213 : *
7214 : * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7215 : * have to decide at runtime whether to recurse or not depending on whether we
7216 : * actually add a column or merely merge with an existing column. (We can't
7217 : * check this in a static pre-pass because it won't handle multiple inheritance
7218 : * situations correctly.)
7219 : */
7220 : static void
7221 2214 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7222 : bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7223 : AlterTableUtilityContext *context)
7224 : {
7225 2214 : if (rel->rd_rel->reloftype && !recursing)
7226 6 : ereport(ERROR,
7227 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7228 : errmsg("cannot add column to typed table")));
7229 :
7230 2208 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7231 58 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7232 :
7233 2202 : if (recurse && !is_view)
7234 2102 : cmd->recurse = true;
7235 2202 : }
7236 :
7237 : /*
7238 : * Add a column to a table. The return value is the address of the
7239 : * new column in the parent relation.
7240 : *
7241 : * cmd is pass-by-ref so that we can replace it with the parse-transformed
7242 : * copy (but that happens only after we check for IF NOT EXISTS).
7243 : */
7244 : static ObjectAddress
7245 2934 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7246 : AlterTableCmd **cmd, bool recurse, bool recursing,
7247 : LOCKMODE lockmode, AlterTablePass cur_pass,
7248 : AlterTableUtilityContext *context)
7249 : {
7250 2934 : Oid myrelid = RelationGetRelid(rel);
7251 2934 : ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7252 2934 : bool if_not_exists = (*cmd)->missing_ok;
7253 : Relation pgclass,
7254 : attrdesc;
7255 : HeapTuple reltup;
7256 : Form_pg_class relform;
7257 : Form_pg_attribute attribute;
7258 : int newattnum;
7259 : char relkind;
7260 : Expr *defval;
7261 : List *children;
7262 : ListCell *child;
7263 : AlterTableCmd *childcmd;
7264 : ObjectAddress address;
7265 : TupleDesc tupdesc;
7266 :
7267 : /* since this function recurses, it could be driven to stack overflow */
7268 2934 : check_stack_depth();
7269 :
7270 : /* At top level, permission check was done in ATPrepCmd, else do it */
7271 2934 : if (recursing)
7272 738 : ATSimplePermissions((*cmd)->subtype, rel,
7273 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7274 :
7275 2934 : if (rel->rd_rel->relispartition && !recursing)
7276 12 : ereport(ERROR,
7277 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7278 : errmsg("cannot add column to a partition")));
7279 :
7280 2922 : attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7281 :
7282 : /*
7283 : * Are we adding the column to a recursion child? If so, check whether to
7284 : * merge with an existing definition for the column. If we do merge, we
7285 : * must not recurse. Children will already have the column, and recursing
7286 : * into them would mess up attinhcount.
7287 : */
7288 2922 : if (colDef->inhcount > 0)
7289 : {
7290 : HeapTuple tuple;
7291 :
7292 : /* Does child already have a column by this name? */
7293 738 : tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7294 738 : if (HeapTupleIsValid(tuple))
7295 : {
7296 60 : Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7297 : Oid ctypeId;
7298 : int32 ctypmod;
7299 : Oid ccollid;
7300 :
7301 : /* Child column must match on type, typmod, and collation */
7302 60 : typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7303 60 : if (ctypeId != childatt->atttypid ||
7304 60 : ctypmod != childatt->atttypmod)
7305 0 : ereport(ERROR,
7306 : (errcode(ERRCODE_DATATYPE_MISMATCH),
7307 : errmsg("child table \"%s\" has different type for column \"%s\"",
7308 : RelationGetRelationName(rel), colDef->colname)));
7309 60 : ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7310 60 : if (ccollid != childatt->attcollation)
7311 0 : ereport(ERROR,
7312 : (errcode(ERRCODE_COLLATION_MISMATCH),
7313 : errmsg("child table \"%s\" has different collation for column \"%s\"",
7314 : RelationGetRelationName(rel), colDef->colname),
7315 : errdetail("\"%s\" versus \"%s\"",
7316 : get_collation_name(ccollid),
7317 : get_collation_name(childatt->attcollation))));
7318 :
7319 : /* Bump the existing child att's inhcount */
7320 60 : if (pg_add_s16_overflow(childatt->attinhcount, 1,
7321 : &childatt->attinhcount))
7322 0 : ereport(ERROR,
7323 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7324 : errmsg("too many inheritance parents"));
7325 60 : CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7326 :
7327 60 : heap_freetuple(tuple);
7328 :
7329 : /* Inform the user about the merge */
7330 60 : ereport(NOTICE,
7331 : (errmsg("merging definition of column \"%s\" for child \"%s\"",
7332 : colDef->colname, RelationGetRelationName(rel))));
7333 :
7334 60 : table_close(attrdesc, RowExclusiveLock);
7335 :
7336 : /* Make the child column change visible */
7337 60 : CommandCounterIncrement();
7338 :
7339 60 : return InvalidObjectAddress;
7340 : }
7341 : }
7342 :
7343 : /* skip if the name already exists and if_not_exists is true */
7344 2862 : if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7345 : {
7346 54 : table_close(attrdesc, RowExclusiveLock);
7347 54 : return InvalidObjectAddress;
7348 : }
7349 :
7350 : /*
7351 : * Okay, we need to add the column, so go ahead and do parse
7352 : * transformation. This can result in queueing up, or even immediately
7353 : * executing, subsidiary operations (such as creation of unique indexes);
7354 : * so we mustn't do it until we have made the if_not_exists check.
7355 : *
7356 : * When recursing, the command was already transformed and we needn't do
7357 : * so again. Also, if context isn't given we can't transform. (That
7358 : * currently happens only for AT_AddColumnToView; we expect that view.c
7359 : * passed us a ColumnDef that doesn't need work.)
7360 : */
7361 2778 : if (context != NULL && !recursing)
7362 : {
7363 2076 : *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7364 : cur_pass, context);
7365 : Assert(*cmd != NULL);
7366 2070 : colDef = castNode(ColumnDef, (*cmd)->def);
7367 : }
7368 :
7369 : /*
7370 : * Regular inheritance children are independent enough not to inherit the
7371 : * identity column from parent hence cannot recursively add identity
7372 : * column if the table has inheritance children.
7373 : *
7374 : * Partitions, on the other hand, are integral part of a partitioned table
7375 : * and inherit identity column. Hence propagate identity column down the
7376 : * partition hierarchy.
7377 : */
7378 2772 : if (colDef->identity &&
7379 54 : recurse &&
7380 102 : rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7381 48 : find_inheritance_children(myrelid, NoLock) != NIL)
7382 6 : ereport(ERROR,
7383 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7384 : errmsg("cannot recursively add identity column to table that has child tables")));
7385 :
7386 2766 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
7387 :
7388 2766 : reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7389 2766 : if (!HeapTupleIsValid(reltup))
7390 0 : elog(ERROR, "cache lookup failed for relation %u", myrelid);
7391 2766 : relform = (Form_pg_class) GETSTRUCT(reltup);
7392 2766 : relkind = relform->relkind;
7393 :
7394 : /* Determine the new attribute's number */
7395 2766 : newattnum = relform->relnatts + 1;
7396 2766 : if (newattnum > MaxHeapAttributeNumber)
7397 0 : ereport(ERROR,
7398 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
7399 : errmsg("tables can have at most %d columns",
7400 : MaxHeapAttributeNumber)));
7401 :
7402 : /*
7403 : * Construct new attribute's pg_attribute entry.
7404 : */
7405 2766 : tupdesc = BuildDescForRelation(list_make1(colDef));
7406 :
7407 2754 : attribute = TupleDescAttr(tupdesc, 0);
7408 :
7409 : /* Fix up attribute number */
7410 2754 : attribute->attnum = newattnum;
7411 :
7412 : /* make sure datatype is legal for a column */
7413 5508 : CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7414 2754 : list_make1_oid(rel->rd_rel->reltype),
7415 2754 : (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7416 :
7417 2718 : InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7418 :
7419 2718 : table_close(attrdesc, RowExclusiveLock);
7420 :
7421 : /*
7422 : * Update pg_class tuple as appropriate
7423 : */
7424 2718 : relform->relnatts = newattnum;
7425 :
7426 2718 : CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7427 :
7428 2718 : heap_freetuple(reltup);
7429 :
7430 : /* Post creation hook for new attribute */
7431 2718 : InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7432 :
7433 2718 : table_close(pgclass, RowExclusiveLock);
7434 :
7435 : /* Make the attribute's catalog entry visible */
7436 2718 : CommandCounterIncrement();
7437 :
7438 : /*
7439 : * Store the DEFAULT, if any, in the catalogs
7440 : */
7441 2718 : if (colDef->raw_default)
7442 : {
7443 : RawColumnDefault *rawEnt;
7444 :
7445 950 : rawEnt = palloc_object(RawColumnDefault);
7446 950 : rawEnt->attnum = attribute->attnum;
7447 950 : rawEnt->raw_default = copyObject(colDef->raw_default);
7448 950 : rawEnt->generated = colDef->generated;
7449 :
7450 : /*
7451 : * This function is intended for CREATE TABLE, so it processes a
7452 : * _list_ of defaults, but we just do one.
7453 : */
7454 950 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7455 : false, true, false, NULL);
7456 :
7457 : /* Make the additional catalog changes visible */
7458 926 : CommandCounterIncrement();
7459 : }
7460 :
7461 : /*
7462 : * Tell Phase 3 to fill in the default expression, if there is one.
7463 : *
7464 : * If there is no default, Phase 3 doesn't have to do anything, because
7465 : * that effectively means that the default is NULL. The heap tuple access
7466 : * routines always check for attnum > # of attributes in tuple, and return
7467 : * NULL if so, so without any modification of the tuple data we will get
7468 : * the effect of NULL values in the new column.
7469 : *
7470 : * An exception occurs when the new column is of a domain type: the domain
7471 : * might have a not-null constraint, or a check constraint that indirectly
7472 : * rejects nulls. If there are any domain constraints then we construct
7473 : * an explicit NULL default value that will be passed through
7474 : * CoerceToDomain processing. (This is a tad inefficient, since it causes
7475 : * rewriting the table which we really wouldn't have to do; but we do it
7476 : * to preserve the historical behavior that such a failure will be raised
7477 : * only if the table currently contains some rows.)
7478 : *
7479 : * Note: we use build_column_default, and not just the cooked default
7480 : * returned by AddRelationNewConstraints, so that the right thing happens
7481 : * when a datatype's default applies.
7482 : *
7483 : * Note: it might seem that this should happen at the end of Phase 2, so
7484 : * that the effects of subsequent subcommands can be taken into account.
7485 : * It's intentional that we do it now, though. The new column should be
7486 : * filled according to what is said in the ADD COLUMN subcommand, so that
7487 : * the effects are the same as if this subcommand had been run by itself
7488 : * and the later subcommands had been issued in new ALTER TABLE commands.
7489 : *
7490 : * We can skip this entirely for relations without storage, since Phase 3
7491 : * is certainly not going to touch them.
7492 : */
7493 2694 : if (RELKIND_HAS_STORAGE(relkind))
7494 : {
7495 : bool has_domain_constraints;
7496 2318 : bool has_missing = false;
7497 :
7498 : /*
7499 : * For an identity column, we can't use build_column_default(),
7500 : * because the sequence ownership isn't set yet. So do it manually.
7501 : */
7502 2318 : if (colDef->identity)
7503 : {
7504 42 : NextValueExpr *nve = makeNode(NextValueExpr);
7505 :
7506 42 : nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7507 42 : nve->typeId = attribute->atttypid;
7508 :
7509 42 : defval = (Expr *) nve;
7510 : }
7511 : else
7512 2276 : defval = (Expr *) build_column_default(rel, attribute->attnum);
7513 :
7514 : /* Build CoerceToDomain(NULL) expression if needed */
7515 2318 : has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7516 2318 : if (!defval && has_domain_constraints)
7517 : {
7518 : Oid baseTypeId;
7519 : int32 baseTypeMod;
7520 : Oid baseTypeColl;
7521 :
7522 6 : baseTypeMod = attribute->atttypmod;
7523 6 : baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7524 6 : baseTypeColl = get_typcollation(baseTypeId);
7525 6 : defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7526 6 : defval = (Expr *) coerce_to_target_type(NULL,
7527 : (Node *) defval,
7528 : baseTypeId,
7529 : attribute->atttypid,
7530 : attribute->atttypmod,
7531 : COERCION_ASSIGNMENT,
7532 : COERCE_IMPLICIT_CAST,
7533 : -1);
7534 6 : if (defval == NULL) /* should not happen */
7535 0 : elog(ERROR, "failed to coerce base type to domain");
7536 : }
7537 :
7538 2318 : if (defval)
7539 : {
7540 : NewColumnValue *newval;
7541 :
7542 : /* Prepare defval for execution, either here or in Phase 3 */
7543 822 : defval = expression_planner(defval);
7544 :
7545 : /* Add the new default to the newvals list */
7546 822 : newval = palloc0_object(NewColumnValue);
7547 822 : newval->attnum = attribute->attnum;
7548 822 : newval->expr = defval;
7549 822 : newval->is_generated = (colDef->generated != '\0');
7550 :
7551 822 : tab->newvals = lappend(tab->newvals, newval);
7552 :
7553 : /*
7554 : * Attempt to skip a complete table rewrite by storing the
7555 : * specified DEFAULT value outside of the heap. This is only
7556 : * allowed for plain relations and non-generated columns, and the
7557 : * default expression can't be volatile (stable is OK). Note that
7558 : * contain_volatile_functions deems CoerceToDomain immutable, but
7559 : * here we consider that coercion to a domain with constraints is
7560 : * volatile; else it might fail even when the table is empty.
7561 : */
7562 822 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
7563 822 : !colDef->generated &&
7564 694 : !has_domain_constraints &&
7565 682 : !contain_volatile_functions((Node *) defval))
7566 514 : {
7567 : EState *estate;
7568 : ExprState *exprState;
7569 : Datum missingval;
7570 : bool missingIsNull;
7571 :
7572 : /* Evaluate the default expression */
7573 514 : estate = CreateExecutorState();
7574 514 : exprState = ExecPrepareExpr(defval, estate);
7575 514 : missingval = ExecEvalExpr(exprState,
7576 514 : GetPerTupleExprContext(estate),
7577 : &missingIsNull);
7578 : /* If it turns out NULL, nothing to do; else store it */
7579 514 : if (!missingIsNull)
7580 : {
7581 514 : StoreAttrMissingVal(rel, attribute->attnum, missingval);
7582 : /* Make the additional catalog change visible */
7583 514 : CommandCounterIncrement();
7584 514 : has_missing = true;
7585 : }
7586 514 : FreeExecutorState(estate);
7587 : }
7588 : else
7589 : {
7590 : /*
7591 : * Failed to use missing mode. We have to do a table rewrite
7592 : * to install the value --- unless it's a virtual generated
7593 : * column.
7594 : */
7595 308 : if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7596 216 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7597 : }
7598 : }
7599 :
7600 2318 : if (!has_missing)
7601 : {
7602 : /*
7603 : * If the new column is NOT NULL, and there is no missing value,
7604 : * tell Phase 3 it needs to check for NULLs.
7605 : */
7606 1804 : tab->verify_new_notnull |= colDef->is_not_null;
7607 : }
7608 : }
7609 :
7610 : /*
7611 : * Add needed dependency entries for the new column.
7612 : */
7613 2694 : add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7614 2694 : add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7615 :
7616 : /*
7617 : * Propagate to children as appropriate. Unlike most other ALTER
7618 : * routines, we have to do this one level of recursion at a time; we can't
7619 : * use find_all_inheritors to do it in one pass.
7620 : */
7621 : children =
7622 2694 : find_inheritance_children(RelationGetRelid(rel), lockmode);
7623 :
7624 : /*
7625 : * If we are told not to recurse, there had better not be any child
7626 : * tables; else the addition would put them out of step.
7627 : */
7628 2694 : if (children && !recurse)
7629 12 : ereport(ERROR,
7630 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7631 : errmsg("column must be added to child tables too")));
7632 :
7633 : /* Children should see column as singly inherited */
7634 2682 : if (!recursing)
7635 : {
7636 2004 : childcmd = copyObject(*cmd);
7637 2004 : colDef = castNode(ColumnDef, childcmd->def);
7638 2004 : colDef->inhcount = 1;
7639 2004 : colDef->is_local = false;
7640 : }
7641 : else
7642 678 : childcmd = *cmd; /* no need to copy again */
7643 :
7644 3420 : foreach(child, children)
7645 : {
7646 738 : Oid childrelid = lfirst_oid(child);
7647 : Relation childrel;
7648 : AlteredTableInfo *childtab;
7649 :
7650 : /* find_inheritance_children already got lock */
7651 738 : childrel = table_open(childrelid, NoLock);
7652 738 : CheckAlterTableIsSafe(childrel);
7653 :
7654 : /* Find or create work queue entry for this table */
7655 738 : childtab = ATGetQueueEntry(wqueue, childrel);
7656 :
7657 : /* Recurse to child; return value is ignored */
7658 738 : ATExecAddColumn(wqueue, childtab, childrel,
7659 : &childcmd, recurse, true,
7660 : lockmode, cur_pass, context);
7661 :
7662 738 : table_close(childrel, NoLock);
7663 : }
7664 :
7665 2682 : ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7666 2682 : return address;
7667 : }
7668 :
7669 : /*
7670 : * If a new or renamed column will collide with the name of an existing
7671 : * column and if_not_exists is false then error out, else do nothing.
7672 : */
7673 : static bool
7674 3312 : check_for_column_name_collision(Relation rel, const char *colname,
7675 : bool if_not_exists)
7676 : {
7677 : HeapTuple attTuple;
7678 : int attnum;
7679 :
7680 : /*
7681 : * this test is deliberately not attisdropped-aware, since if one tries to
7682 : * add a column matching a dropped column name, it's gonna fail anyway.
7683 : */
7684 3312 : attTuple = SearchSysCache2(ATTNAME,
7685 : ObjectIdGetDatum(RelationGetRelid(rel)),
7686 : PointerGetDatum(colname));
7687 3312 : if (!HeapTupleIsValid(attTuple))
7688 3216 : return true;
7689 :
7690 96 : attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7691 96 : ReleaseSysCache(attTuple);
7692 :
7693 : /*
7694 : * We throw a different error message for conflicts with system column
7695 : * names, since they are normally not shown and the user might otherwise
7696 : * be confused about the reason for the conflict.
7697 : */
7698 96 : if (attnum <= 0)
7699 12 : ereport(ERROR,
7700 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7701 : errmsg("column name \"%s\" conflicts with a system column name",
7702 : colname)));
7703 : else
7704 : {
7705 84 : if (if_not_exists)
7706 : {
7707 54 : ereport(NOTICE,
7708 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7709 : errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7710 : colname, RelationGetRelationName(rel))));
7711 54 : return false;
7712 : }
7713 :
7714 30 : ereport(ERROR,
7715 : (errcode(ERRCODE_DUPLICATE_COLUMN),
7716 : errmsg("column \"%s\" of relation \"%s\" already exists",
7717 : colname, RelationGetRelationName(rel))));
7718 : }
7719 :
7720 : return true;
7721 : }
7722 :
7723 : /*
7724 : * Install a column's dependency on its datatype.
7725 : */
7726 : static void
7727 3844 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7728 : {
7729 : ObjectAddress myself,
7730 : referenced;
7731 :
7732 3844 : myself.classId = RelationRelationId;
7733 3844 : myself.objectId = relid;
7734 3844 : myself.objectSubId = attnum;
7735 3844 : referenced.classId = TypeRelationId;
7736 3844 : referenced.objectId = typid;
7737 3844 : referenced.objectSubId = 0;
7738 3844 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7739 3844 : }
7740 :
7741 : /*
7742 : * Install a column's dependency on its collation.
7743 : */
7744 : static void
7745 3844 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7746 : {
7747 : ObjectAddress myself,
7748 : referenced;
7749 :
7750 : /* We know the default collation is pinned, so don't bother recording it */
7751 3844 : if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7752 : {
7753 18 : myself.classId = RelationRelationId;
7754 18 : myself.objectId = relid;
7755 18 : myself.objectSubId = attnum;
7756 18 : referenced.classId = CollationRelationId;
7757 18 : referenced.objectId = collid;
7758 18 : referenced.objectSubId = 0;
7759 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7760 : }
7761 3844 : }
7762 :
7763 : /*
7764 : * ALTER TABLE ALTER COLUMN DROP NOT NULL
7765 : *
7766 : * Return the address of the modified column. If the column was already
7767 : * nullable, InvalidObjectAddress is returned.
7768 : */
7769 : static ObjectAddress
7770 268 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7771 : LOCKMODE lockmode)
7772 : {
7773 : HeapTuple tuple;
7774 : HeapTuple conTup;
7775 : Form_pg_attribute attTup;
7776 : AttrNumber attnum;
7777 : Relation attr_rel;
7778 : ObjectAddress address;
7779 :
7780 : /*
7781 : * lookup the attribute
7782 : */
7783 268 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7784 :
7785 268 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7786 268 : if (!HeapTupleIsValid(tuple))
7787 18 : ereport(ERROR,
7788 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7789 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7790 : colName, RelationGetRelationName(rel))));
7791 250 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7792 250 : attnum = attTup->attnum;
7793 250 : ObjectAddressSubSet(address, RelationRelationId,
7794 : RelationGetRelid(rel), attnum);
7795 :
7796 : /* If the column is already nullable there's nothing to do. */
7797 250 : if (!attTup->attnotnull)
7798 : {
7799 0 : table_close(attr_rel, RowExclusiveLock);
7800 0 : return InvalidObjectAddress;
7801 : }
7802 :
7803 : /* Prevent them from altering a system attribute */
7804 250 : if (attnum <= 0)
7805 0 : ereport(ERROR,
7806 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7807 : errmsg("cannot alter system column \"%s\"",
7808 : colName)));
7809 :
7810 250 : if (attTup->attidentity)
7811 18 : ereport(ERROR,
7812 : (errcode(ERRCODE_SYNTAX_ERROR),
7813 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
7814 : colName, RelationGetRelationName(rel))));
7815 :
7816 : /*
7817 : * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7818 : */
7819 232 : if (rel->rd_rel->relispartition)
7820 : {
7821 12 : Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7822 12 : Relation parent = table_open(parentId, AccessShareLock);
7823 12 : TupleDesc tupDesc = RelationGetDescr(parent);
7824 : AttrNumber parent_attnum;
7825 :
7826 12 : parent_attnum = get_attnum(parentId, colName);
7827 12 : if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7828 12 : ereport(ERROR,
7829 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7830 : errmsg("column \"%s\" is marked NOT NULL in parent table",
7831 : colName)));
7832 0 : table_close(parent, AccessShareLock);
7833 : }
7834 :
7835 : /*
7836 : * Find the constraint that makes this column NOT NULL, and drop it.
7837 : * dropconstraint_internal() resets attnotnull.
7838 : */
7839 220 : conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7840 220 : if (conTup == NULL)
7841 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7842 : colName, RelationGetRelationName(rel));
7843 :
7844 : /* The normal case: we have a pg_constraint row, remove it */
7845 220 : dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7846 : false, lockmode);
7847 166 : heap_freetuple(conTup);
7848 :
7849 166 : InvokeObjectPostAlterHook(RelationRelationId,
7850 : RelationGetRelid(rel), attnum);
7851 :
7852 166 : table_close(attr_rel, RowExclusiveLock);
7853 :
7854 166 : return address;
7855 : }
7856 :
7857 : /*
7858 : * set_attnotnull
7859 : * Helper to update/validate the pg_attribute status of a not-null
7860 : * constraint
7861 : *
7862 : * pg_attribute.attnotnull is set true, if it isn't already.
7863 : * If queue_validation is true, also set up wqueue to validate the constraint.
7864 : * wqueue may be given as NULL when validation is not needed (e.g., on table
7865 : * creation).
7866 : */
7867 : static void
7868 25980 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7869 : bool is_valid, bool queue_validation)
7870 : {
7871 : Form_pg_attribute attr;
7872 : CompactAttribute *thisatt;
7873 :
7874 : Assert(!queue_validation || wqueue);
7875 :
7876 25980 : CheckAlterTableIsSafe(rel);
7877 :
7878 : /*
7879 : * Exit quickly by testing attnotnull from the tupledesc's copy of the
7880 : * attribute.
7881 : */
7882 25980 : attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7883 25980 : if (attr->attisdropped)
7884 0 : return;
7885 :
7886 25980 : if (!attr->attnotnull)
7887 : {
7888 : Relation attr_rel;
7889 : HeapTuple tuple;
7890 :
7891 1494 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7892 :
7893 1494 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7894 1494 : if (!HeapTupleIsValid(tuple))
7895 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7896 : attnum, RelationGetRelid(rel));
7897 :
7898 1494 : thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7899 1494 : thisatt->attnullability = ATTNULLABLE_VALID;
7900 :
7901 1494 : attr = (Form_pg_attribute) GETSTRUCT(tuple);
7902 :
7903 1494 : attr->attnotnull = true;
7904 1494 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7905 :
7906 : /*
7907 : * If the nullness isn't already proven by validated constraints, have
7908 : * ALTER TABLE phase 3 test for it.
7909 : */
7910 1494 : if (queue_validation && wqueue &&
7911 1246 : !NotNullImpliedByRelConstraints(rel, attr))
7912 : {
7913 : AlteredTableInfo *tab;
7914 :
7915 1196 : tab = ATGetQueueEntry(wqueue, rel);
7916 1196 : tab->verify_new_notnull = true;
7917 : }
7918 :
7919 1494 : CommandCounterIncrement();
7920 :
7921 1494 : table_close(attr_rel, RowExclusiveLock);
7922 1494 : heap_freetuple(tuple);
7923 : }
7924 : else
7925 : {
7926 24486 : CacheInvalidateRelcache(rel);
7927 : }
7928 : }
7929 :
7930 : /*
7931 : * ALTER TABLE ALTER COLUMN SET NOT NULL
7932 : *
7933 : * Add a not-null constraint to a single table and its children. Returns
7934 : * the address of the constraint added to the parent relation, if one gets
7935 : * added, or InvalidObjectAddress otherwise.
7936 : *
7937 : * We must recurse to child tables during execution, rather than using
7938 : * ALTER TABLE's normal prep-time recursion.
7939 : */
7940 : static ObjectAddress
7941 712 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7942 : bool recurse, bool recursing, LOCKMODE lockmode)
7943 : {
7944 : HeapTuple tuple;
7945 : AttrNumber attnum;
7946 : ObjectAddress address;
7947 : Constraint *constraint;
7948 : CookedConstraint *ccon;
7949 : List *cooked;
7950 712 : bool is_no_inherit = false;
7951 :
7952 : /* Guard against stack overflow due to overly deep inheritance tree. */
7953 712 : check_stack_depth();
7954 :
7955 : /* At top level, permission check was done in ATPrepCmd, else do it */
7956 712 : if (recursing)
7957 : {
7958 298 : ATSimplePermissions(AT_AddConstraint, rel,
7959 : ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7960 : Assert(conName != NULL);
7961 : }
7962 :
7963 712 : attnum = get_attnum(RelationGetRelid(rel), colName);
7964 712 : if (attnum == InvalidAttrNumber)
7965 18 : ereport(ERROR,
7966 : (errcode(ERRCODE_UNDEFINED_COLUMN),
7967 : errmsg("column \"%s\" of relation \"%s\" does not exist",
7968 : colName, RelationGetRelationName(rel))));
7969 :
7970 : /* Prevent them from altering a system attribute */
7971 694 : if (attnum <= 0)
7972 0 : ereport(ERROR,
7973 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7974 : errmsg("cannot alter system column \"%s\"",
7975 : colName)));
7976 :
7977 : /* See if there's already a constraint */
7978 694 : tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7979 694 : if (HeapTupleIsValid(tuple))
7980 : {
7981 158 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7982 158 : bool changed = false;
7983 :
7984 : /*
7985 : * Don't let a NO INHERIT constraint be changed into inherit.
7986 : */
7987 158 : if (conForm->connoinherit && recurse)
7988 12 : ereport(ERROR,
7989 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7990 : errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7991 : NameStr(conForm->conname),
7992 : RelationGetRelationName(rel)));
7993 :
7994 : /*
7995 : * If we find an appropriate constraint, we're almost done, but just
7996 : * need to change some properties on it: if we're recursing, increment
7997 : * coninhcount; if not, set conislocal if not already set.
7998 : */
7999 146 : if (recursing)
8000 : {
8001 102 : if (pg_add_s16_overflow(conForm->coninhcount, 1,
8002 : &conForm->coninhcount))
8003 0 : ereport(ERROR,
8004 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
8005 : errmsg("too many inheritance parents"));
8006 102 : changed = true;
8007 : }
8008 44 : else if (!conForm->conislocal)
8009 : {
8010 0 : conForm->conislocal = true;
8011 0 : changed = true;
8012 : }
8013 44 : else if (!conForm->convalidated)
8014 : {
8015 : /*
8016 : * Flip attnotnull and convalidated, and also validate the
8017 : * constraint.
8018 : */
8019 24 : return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
8020 : recurse, recursing, lockmode);
8021 : }
8022 :
8023 122 : if (changed)
8024 : {
8025 : Relation constr_rel;
8026 :
8027 102 : constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
8028 :
8029 102 : CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
8030 102 : ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
8031 102 : table_close(constr_rel, RowExclusiveLock);
8032 : }
8033 :
8034 122 : if (changed)
8035 102 : return address;
8036 : else
8037 20 : return InvalidObjectAddress;
8038 : }
8039 :
8040 : /*
8041 : * If we're asked not to recurse, and children exist, raise an error for
8042 : * partitioned tables. For inheritance, we act as if NO INHERIT had been
8043 : * specified.
8044 : */
8045 566 : if (!recurse &&
8046 30 : find_inheritance_children(RelationGetRelid(rel),
8047 : NoLock) != NIL)
8048 : {
8049 18 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8050 6 : ereport(ERROR,
8051 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8052 : errmsg("constraint must be added to child tables too"),
8053 : errhint("Do not specify the ONLY keyword."));
8054 : else
8055 12 : is_no_inherit = true;
8056 : }
8057 :
8058 : /*
8059 : * No constraint exists; we must add one. First determine a name to use,
8060 : * if we haven't already.
8061 : */
8062 530 : if (!recursing)
8063 : {
8064 : Assert(conName == NULL);
8065 340 : conName = ChooseConstraintName(RelationGetRelationName(rel),
8066 : colName, "not_null",
8067 340 : RelationGetNamespace(rel),
8068 : NIL);
8069 : }
8070 :
8071 530 : constraint = makeNotNullConstraint(makeString(colName));
8072 530 : constraint->is_no_inherit = is_no_inherit;
8073 530 : constraint->conname = conName;
8074 :
8075 : /* and do it */
8076 530 : cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8077 530 : false, !recursing, false, NULL);
8078 530 : ccon = linitial(cooked);
8079 530 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8080 :
8081 530 : InvokeObjectPostAlterHook(RelationRelationId,
8082 : RelationGetRelid(rel), attnum);
8083 :
8084 : /* Mark pg_attribute.attnotnull for the column and queue validation */
8085 530 : set_attnotnull(wqueue, rel, attnum, true, true);
8086 :
8087 : /*
8088 : * Recurse to propagate the constraint to children that don't have one.
8089 : */
8090 530 : if (recurse)
8091 : {
8092 : List *children;
8093 :
8094 506 : children = find_inheritance_children(RelationGetRelid(rel),
8095 : lockmode);
8096 :
8097 1244 : foreach_oid(childoid, children)
8098 : {
8099 244 : Relation childrel = table_open(childoid, NoLock);
8100 :
8101 244 : CommandCounterIncrement();
8102 :
8103 244 : ATExecSetNotNull(wqueue, childrel, conName, colName,
8104 : recurse, true, lockmode);
8105 238 : table_close(childrel, NoLock);
8106 : }
8107 : }
8108 :
8109 524 : return address;
8110 : }
8111 :
8112 : /*
8113 : * NotNullImpliedByRelConstraints
8114 : * Does rel's existing constraints imply NOT NULL for the given attribute?
8115 : */
8116 : static bool
8117 1246 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8118 : {
8119 1246 : NullTest *nnulltest = makeNode(NullTest);
8120 :
8121 2492 : nnulltest->arg = (Expr *) makeVar(1,
8122 1246 : attr->attnum,
8123 : attr->atttypid,
8124 : attr->atttypmod,
8125 : attr->attcollation,
8126 : 0);
8127 1246 : nnulltest->nulltesttype = IS_NOT_NULL;
8128 :
8129 : /*
8130 : * argisrow = false is correct even for a composite column, because
8131 : * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8132 : * case, just IS DISTINCT FROM NULL.
8133 : */
8134 1246 : nnulltest->argisrow = false;
8135 1246 : nnulltest->location = -1;
8136 :
8137 1246 : if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8138 : {
8139 50 : ereport(DEBUG1,
8140 : (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8141 : RelationGetRelationName(rel), NameStr(attr->attname))));
8142 50 : return true;
8143 : }
8144 :
8145 1196 : return false;
8146 : }
8147 :
8148 : /*
8149 : * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8150 : *
8151 : * Return the address of the affected column.
8152 : */
8153 : static ObjectAddress
8154 584 : ATExecColumnDefault(Relation rel, const char *colName,
8155 : Node *newDefault, LOCKMODE lockmode)
8156 : {
8157 584 : TupleDesc tupdesc = RelationGetDescr(rel);
8158 : AttrNumber attnum;
8159 : ObjectAddress address;
8160 :
8161 : /*
8162 : * get the number of the attribute
8163 : */
8164 584 : attnum = get_attnum(RelationGetRelid(rel), colName);
8165 584 : if (attnum == InvalidAttrNumber)
8166 30 : ereport(ERROR,
8167 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8168 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8169 : colName, RelationGetRelationName(rel))));
8170 :
8171 : /* Prevent them from altering a system attribute */
8172 554 : if (attnum <= 0)
8173 0 : ereport(ERROR,
8174 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8175 : errmsg("cannot alter system column \"%s\"",
8176 : colName)));
8177 :
8178 554 : if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8179 18 : ereport(ERROR,
8180 : (errcode(ERRCODE_SYNTAX_ERROR),
8181 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
8182 : colName, RelationGetRelationName(rel)),
8183 : /* translator: %s is an SQL ALTER command */
8184 : newDefault ? 0 : errhint("Use %s instead.",
8185 : "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8186 :
8187 536 : if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8188 12 : ereport(ERROR,
8189 : (errcode(ERRCODE_SYNTAX_ERROR),
8190 : errmsg("column \"%s\" of relation \"%s\" is a generated column",
8191 : colName, RelationGetRelationName(rel)),
8192 : newDefault ?
8193 : /* translator: %s is an SQL ALTER command */
8194 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8195 : (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8196 : errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8197 :
8198 : /*
8199 : * Remove any old default for the column. We use RESTRICT here for
8200 : * safety, but at present we do not expect anything to depend on the
8201 : * default.
8202 : *
8203 : * We treat removing the existing default as an internal operation when it
8204 : * is preparatory to adding a new default, but as a user-initiated
8205 : * operation when the user asked for a drop.
8206 : */
8207 524 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8208 : newDefault != NULL);
8209 :
8210 524 : if (newDefault)
8211 : {
8212 : /* SET DEFAULT */
8213 : RawColumnDefault *rawEnt;
8214 :
8215 350 : rawEnt = palloc_object(RawColumnDefault);
8216 350 : rawEnt->attnum = attnum;
8217 350 : rawEnt->raw_default = newDefault;
8218 350 : rawEnt->generated = '\0';
8219 :
8220 : /*
8221 : * This function is intended for CREATE TABLE, so it processes a
8222 : * _list_ of defaults, but we just do one.
8223 : */
8224 350 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8225 : false, true, false, NULL);
8226 : }
8227 :
8228 518 : ObjectAddressSubSet(address, RelationRelationId,
8229 : RelationGetRelid(rel), attnum);
8230 518 : return address;
8231 : }
8232 :
8233 : /*
8234 : * Add a pre-cooked default expression.
8235 : *
8236 : * Return the address of the affected column.
8237 : */
8238 : static ObjectAddress
8239 80 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8240 : Node *newDefault)
8241 : {
8242 : ObjectAddress address;
8243 :
8244 : /* We assume no checking is required */
8245 :
8246 : /*
8247 : * Remove any old default for the column. We use RESTRICT here for
8248 : * safety, but at present we do not expect anything to depend on the
8249 : * default. (In ordinary cases, there could not be a default in place
8250 : * anyway, but it's possible when combining LIKE with inheritance.)
8251 : */
8252 80 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8253 : true);
8254 :
8255 80 : (void) StoreAttrDefault(rel, attnum, newDefault, true);
8256 :
8257 80 : ObjectAddressSubSet(address, RelationRelationId,
8258 : RelationGetRelid(rel), attnum);
8259 80 : return address;
8260 : }
8261 :
8262 : /*
8263 : * ALTER TABLE ALTER COLUMN ADD IDENTITY
8264 : *
8265 : * Return the address of the affected column.
8266 : */
8267 : static ObjectAddress
8268 166 : ATExecAddIdentity(Relation rel, const char *colName,
8269 : Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8270 : {
8271 : Relation attrelation;
8272 : HeapTuple tuple;
8273 : Form_pg_attribute attTup;
8274 : AttrNumber attnum;
8275 : ObjectAddress address;
8276 166 : ColumnDef *cdef = castNode(ColumnDef, def);
8277 : bool ispartitioned;
8278 :
8279 166 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8280 166 : if (ispartitioned && !recurse)
8281 6 : ereport(ERROR,
8282 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8283 : errmsg("cannot add identity to a column of only the partitioned table"),
8284 : errhint("Do not specify the ONLY keyword.")));
8285 :
8286 160 : if (rel->rd_rel->relispartition && !recursing)
8287 12 : ereport(ERROR,
8288 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8289 : errmsg("cannot add identity to a column of a partition"));
8290 :
8291 148 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8292 :
8293 148 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8294 148 : if (!HeapTupleIsValid(tuple))
8295 0 : ereport(ERROR,
8296 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8297 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8298 : colName, RelationGetRelationName(rel))));
8299 148 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8300 148 : attnum = attTup->attnum;
8301 :
8302 : /* Can't alter a system attribute */
8303 148 : if (attnum <= 0)
8304 0 : ereport(ERROR,
8305 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8306 : errmsg("cannot alter system column \"%s\"",
8307 : colName)));
8308 :
8309 : /*
8310 : * Creating a column as identity implies NOT NULL, so adding the identity
8311 : * to an existing column that is not NOT NULL would create a state that
8312 : * cannot be reproduced without contortions.
8313 : */
8314 148 : if (!attTup->attnotnull)
8315 6 : ereport(ERROR,
8316 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8317 : errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8318 : colName, RelationGetRelationName(rel))));
8319 :
8320 : /*
8321 : * On the other hand, if a not-null constraint exists, then verify that
8322 : * it's compatible.
8323 : */
8324 142 : if (attTup->attnotnull)
8325 : {
8326 : HeapTuple contup;
8327 : Form_pg_constraint conForm;
8328 :
8329 142 : contup = findNotNullConstraintAttnum(RelationGetRelid(rel),
8330 : attnum);
8331 142 : if (!HeapTupleIsValid(contup))
8332 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8333 : colName, RelationGetRelationName(rel));
8334 :
8335 142 : conForm = (Form_pg_constraint) GETSTRUCT(contup);
8336 142 : if (!conForm->convalidated)
8337 6 : ereport(ERROR,
8338 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8339 : errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8340 : NameStr(conForm->conname), RelationGetRelationName(rel)),
8341 : errhint("You might need to validate it using %s.",
8342 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
8343 : }
8344 :
8345 136 : if (attTup->attidentity)
8346 18 : ereport(ERROR,
8347 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8348 : errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8349 : colName, RelationGetRelationName(rel))));
8350 :
8351 118 : if (attTup->atthasdef)
8352 6 : ereport(ERROR,
8353 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8354 : errmsg("column \"%s\" of relation \"%s\" already has a default value",
8355 : colName, RelationGetRelationName(rel))));
8356 :
8357 112 : attTup->attidentity = cdef->identity;
8358 112 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8359 :
8360 112 : InvokeObjectPostAlterHook(RelationRelationId,
8361 : RelationGetRelid(rel),
8362 : attTup->attnum);
8363 112 : ObjectAddressSubSet(address, RelationRelationId,
8364 : RelationGetRelid(rel), attnum);
8365 112 : heap_freetuple(tuple);
8366 :
8367 112 : table_close(attrelation, RowExclusiveLock);
8368 :
8369 : /*
8370 : * Recurse to propagate the identity column to partitions. Identity is
8371 : * not inherited in regular inheritance children.
8372 : */
8373 112 : if (recurse && ispartitioned)
8374 : {
8375 : List *children;
8376 : ListCell *lc;
8377 :
8378 10 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8379 :
8380 16 : foreach(lc, children)
8381 : {
8382 : Relation childrel;
8383 :
8384 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8385 6 : ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8386 6 : table_close(childrel, NoLock);
8387 : }
8388 : }
8389 :
8390 112 : return address;
8391 : }
8392 :
8393 : /*
8394 : * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8395 : *
8396 : * Return the address of the affected column.
8397 : */
8398 : static ObjectAddress
8399 74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8400 : LOCKMODE lockmode, bool recurse, bool recursing)
8401 : {
8402 : ListCell *option;
8403 74 : DefElem *generatedEl = NULL;
8404 : HeapTuple tuple;
8405 : Form_pg_attribute attTup;
8406 : AttrNumber attnum;
8407 : Relation attrelation;
8408 : ObjectAddress address;
8409 : bool ispartitioned;
8410 :
8411 74 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8412 74 : if (ispartitioned && !recurse)
8413 6 : ereport(ERROR,
8414 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8415 : errmsg("cannot change identity column of only the partitioned table"),
8416 : errhint("Do not specify the ONLY keyword.")));
8417 :
8418 68 : if (rel->rd_rel->relispartition && !recursing)
8419 12 : ereport(ERROR,
8420 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8421 : errmsg("cannot change identity column of a partition"));
8422 :
8423 100 : foreach(option, castNode(List, def))
8424 : {
8425 44 : DefElem *defel = lfirst_node(DefElem, option);
8426 :
8427 44 : if (strcmp(defel->defname, "generated") == 0)
8428 : {
8429 44 : if (generatedEl)
8430 0 : ereport(ERROR,
8431 : (errcode(ERRCODE_SYNTAX_ERROR),
8432 : errmsg("conflicting or redundant options")));
8433 44 : generatedEl = defel;
8434 : }
8435 : else
8436 0 : elog(ERROR, "option \"%s\" not recognized",
8437 : defel->defname);
8438 : }
8439 :
8440 : /*
8441 : * Even if there is nothing to change here, we run all the checks. There
8442 : * will be a subsequent ALTER SEQUENCE that relies on everything being
8443 : * there.
8444 : */
8445 :
8446 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8447 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8448 56 : if (!HeapTupleIsValid(tuple))
8449 0 : ereport(ERROR,
8450 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8451 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8452 : colName, RelationGetRelationName(rel))));
8453 :
8454 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8455 56 : attnum = attTup->attnum;
8456 :
8457 56 : if (attnum <= 0)
8458 0 : ereport(ERROR,
8459 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8460 : errmsg("cannot alter system column \"%s\"",
8461 : colName)));
8462 :
8463 56 : if (!attTup->attidentity)
8464 6 : ereport(ERROR,
8465 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8466 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8467 : colName, RelationGetRelationName(rel))));
8468 :
8469 50 : if (generatedEl)
8470 : {
8471 44 : attTup->attidentity = defGetInt32(generatedEl);
8472 44 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8473 :
8474 44 : InvokeObjectPostAlterHook(RelationRelationId,
8475 : RelationGetRelid(rel),
8476 : attTup->attnum);
8477 44 : ObjectAddressSubSet(address, RelationRelationId,
8478 : RelationGetRelid(rel), attnum);
8479 : }
8480 : else
8481 6 : address = InvalidObjectAddress;
8482 :
8483 50 : heap_freetuple(tuple);
8484 50 : table_close(attrelation, RowExclusiveLock);
8485 :
8486 : /*
8487 : * Recurse to propagate the identity change to partitions. Identity is not
8488 : * inherited in regular inheritance children.
8489 : */
8490 50 : if (generatedEl && recurse && ispartitioned)
8491 : {
8492 : List *children;
8493 : ListCell *lc;
8494 :
8495 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8496 :
8497 18 : foreach(lc, children)
8498 : {
8499 : Relation childrel;
8500 :
8501 12 : childrel = table_open(lfirst_oid(lc), NoLock);
8502 12 : ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8503 12 : table_close(childrel, NoLock);
8504 : }
8505 : }
8506 :
8507 50 : return address;
8508 : }
8509 :
8510 : /*
8511 : * ALTER TABLE ALTER COLUMN DROP IDENTITY
8512 : *
8513 : * Return the address of the affected column.
8514 : */
8515 : static ObjectAddress
8516 92 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8517 : bool recurse, bool recursing)
8518 : {
8519 : HeapTuple tuple;
8520 : Form_pg_attribute attTup;
8521 : AttrNumber attnum;
8522 : Relation attrelation;
8523 : ObjectAddress address;
8524 : Oid seqid;
8525 : ObjectAddress seqaddress;
8526 : bool ispartitioned;
8527 :
8528 92 : ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8529 92 : if (ispartitioned && !recurse)
8530 6 : ereport(ERROR,
8531 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8532 : errmsg("cannot drop identity from a column of only the partitioned table"),
8533 : errhint("Do not specify the ONLY keyword.")));
8534 :
8535 86 : if (rel->rd_rel->relispartition && !recursing)
8536 6 : ereport(ERROR,
8537 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8538 : errmsg("cannot drop identity from a column of a partition"));
8539 :
8540 80 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8541 80 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8542 80 : if (!HeapTupleIsValid(tuple))
8543 0 : ereport(ERROR,
8544 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8545 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8546 : colName, RelationGetRelationName(rel))));
8547 :
8548 80 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8549 80 : attnum = attTup->attnum;
8550 :
8551 80 : if (attnum <= 0)
8552 0 : ereport(ERROR,
8553 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8554 : errmsg("cannot alter system column \"%s\"",
8555 : colName)));
8556 :
8557 80 : if (!attTup->attidentity)
8558 : {
8559 12 : if (!missing_ok)
8560 6 : ereport(ERROR,
8561 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8562 : errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8563 : colName, RelationGetRelationName(rel))));
8564 : else
8565 : {
8566 6 : ereport(NOTICE,
8567 : (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8568 : colName, RelationGetRelationName(rel))));
8569 6 : heap_freetuple(tuple);
8570 6 : table_close(attrelation, RowExclusiveLock);
8571 6 : return InvalidObjectAddress;
8572 : }
8573 : }
8574 :
8575 68 : attTup->attidentity = '\0';
8576 68 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8577 :
8578 68 : InvokeObjectPostAlterHook(RelationRelationId,
8579 : RelationGetRelid(rel),
8580 : attTup->attnum);
8581 68 : ObjectAddressSubSet(address, RelationRelationId,
8582 : RelationGetRelid(rel), attnum);
8583 68 : heap_freetuple(tuple);
8584 :
8585 68 : table_close(attrelation, RowExclusiveLock);
8586 :
8587 : /*
8588 : * Recurse to drop the identity from column in partitions. Identity is
8589 : * not inherited in regular inheritance children so ignore them.
8590 : */
8591 68 : if (recurse && ispartitioned)
8592 : {
8593 : List *children;
8594 : ListCell *lc;
8595 :
8596 6 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8597 :
8598 12 : foreach(lc, children)
8599 : {
8600 : Relation childrel;
8601 :
8602 6 : childrel = table_open(lfirst_oid(lc), NoLock);
8603 6 : ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8604 6 : table_close(childrel, NoLock);
8605 : }
8606 : }
8607 :
8608 68 : if (!recursing)
8609 : {
8610 : /* drop the internal sequence */
8611 32 : seqid = getIdentitySequence(rel, attnum, false);
8612 32 : deleteDependencyRecordsForClass(RelationRelationId, seqid,
8613 : RelationRelationId, DEPENDENCY_INTERNAL);
8614 32 : CommandCounterIncrement();
8615 32 : seqaddress.classId = RelationRelationId;
8616 32 : seqaddress.objectId = seqid;
8617 32 : seqaddress.objectSubId = 0;
8618 32 : performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8619 : }
8620 :
8621 68 : return address;
8622 : }
8623 :
8624 : /*
8625 : * ALTER TABLE ALTER COLUMN SET EXPRESSION
8626 : *
8627 : * Return the address of the affected column.
8628 : */
8629 : static ObjectAddress
8630 224 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8631 : Node *newExpr, LOCKMODE lockmode)
8632 : {
8633 : HeapTuple tuple;
8634 : Form_pg_attribute attTup;
8635 : AttrNumber attnum;
8636 : char attgenerated;
8637 : bool rewrite;
8638 : Oid attrdefoid;
8639 : ObjectAddress address;
8640 : Expr *defval;
8641 : NewColumnValue *newval;
8642 : RawColumnDefault *rawEnt;
8643 :
8644 224 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8645 224 : if (!HeapTupleIsValid(tuple))
8646 0 : ereport(ERROR,
8647 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8648 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8649 : colName, RelationGetRelationName(rel))));
8650 :
8651 224 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8652 :
8653 224 : attnum = attTup->attnum;
8654 224 : if (attnum <= 0)
8655 0 : ereport(ERROR,
8656 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8657 : errmsg("cannot alter system column \"%s\"",
8658 : colName)));
8659 :
8660 224 : attgenerated = attTup->attgenerated;
8661 224 : if (!attgenerated)
8662 12 : ereport(ERROR,
8663 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8664 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8665 : colName, RelationGetRelationName(rel))));
8666 :
8667 : /*
8668 : * TODO: This could be done, just need to recheck any constraints
8669 : * afterwards.
8670 : */
8671 212 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8672 108 : rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8673 12 : ereport(ERROR,
8674 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8675 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
8676 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8677 : colName, RelationGetRelationName(rel))));
8678 :
8679 200 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8680 24 : tab->verify_new_notnull = true;
8681 :
8682 : /*
8683 : * We need to prevent this because a change of expression could affect a
8684 : * row filter and inject expressions that are not permitted in a row
8685 : * filter. XXX We could try to have a more precise check to catch only
8686 : * publications with row filters, or even re-verify the row filter
8687 : * expressions.
8688 : */
8689 296 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8690 96 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
8691 6 : ereport(ERROR,
8692 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8693 : errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8694 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8695 : colName, RelationGetRelationName(rel))));
8696 :
8697 194 : rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8698 :
8699 194 : ReleaseSysCache(tuple);
8700 :
8701 194 : if (rewrite)
8702 : {
8703 : /*
8704 : * Clear all the missing values if we're rewriting the table, since
8705 : * this renders them pointless.
8706 : */
8707 104 : RelationClearMissing(rel);
8708 :
8709 : /* make sure we don't conflict with later attribute modifications */
8710 104 : CommandCounterIncrement();
8711 :
8712 : /*
8713 : * Find everything that depends on the column (constraints, indexes,
8714 : * etc), and record enough information to let us recreate the objects
8715 : * after rewrite.
8716 : */
8717 104 : RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8718 : }
8719 :
8720 : /*
8721 : * Drop the dependency records of the GENERATED expression, in particular
8722 : * its INTERNAL dependency on the column, which would otherwise cause
8723 : * dependency.c to refuse to perform the deletion.
8724 : */
8725 194 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8726 194 : if (!OidIsValid(attrdefoid))
8727 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8728 : RelationGetRelid(rel), attnum);
8729 194 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8730 :
8731 : /* Make above changes visible */
8732 194 : CommandCounterIncrement();
8733 :
8734 : /*
8735 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8736 : * safety, but at present we do not expect anything to depend on the
8737 : * expression.
8738 : */
8739 194 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8740 : false, false);
8741 :
8742 : /* Prepare to store the new expression, in the catalogs */
8743 194 : rawEnt = palloc_object(RawColumnDefault);
8744 194 : rawEnt->attnum = attnum;
8745 194 : rawEnt->raw_default = newExpr;
8746 194 : rawEnt->generated = attgenerated;
8747 :
8748 : /* Store the generated expression */
8749 194 : AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8750 : false, true, false, NULL);
8751 :
8752 : /* Make above new expression visible */
8753 194 : CommandCounterIncrement();
8754 :
8755 194 : if (rewrite)
8756 : {
8757 : /* Prepare for table rewrite */
8758 104 : defval = (Expr *) build_column_default(rel, attnum);
8759 :
8760 104 : newval = palloc0_object(NewColumnValue);
8761 104 : newval->attnum = attnum;
8762 104 : newval->expr = expression_planner(defval);
8763 104 : newval->is_generated = true;
8764 :
8765 104 : tab->newvals = lappend(tab->newvals, newval);
8766 104 : tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8767 : }
8768 :
8769 : /* Drop any pg_statistic entry for the column */
8770 194 : RemoveStatistics(RelationGetRelid(rel), attnum);
8771 :
8772 194 : InvokeObjectPostAlterHook(RelationRelationId,
8773 : RelationGetRelid(rel), attnum);
8774 :
8775 194 : ObjectAddressSubSet(address, RelationRelationId,
8776 : RelationGetRelid(rel), attnum);
8777 194 : return address;
8778 : }
8779 :
8780 : /*
8781 : * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8782 : */
8783 : static void
8784 86 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8785 : {
8786 : /*
8787 : * Reject ONLY if there are child tables. We could implement this, but it
8788 : * is a bit complicated. GENERATED clauses must be attached to the column
8789 : * definition and cannot be added later like DEFAULT, so if a child table
8790 : * has a generation expression that the parent does not have, the child
8791 : * column will necessarily be an attislocal column. So to implement ONLY
8792 : * here, we'd need extra code to update attislocal of the direct child
8793 : * tables, somewhat similar to how DROP COLUMN does it, so that the
8794 : * resulting state can be properly dumped and restored.
8795 : */
8796 110 : if (!recurse &&
8797 24 : find_inheritance_children(RelationGetRelid(rel), lockmode))
8798 12 : ereport(ERROR,
8799 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8800 : errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8801 :
8802 : /*
8803 : * Cannot drop generation expression from inherited columns.
8804 : */
8805 74 : if (!recursing)
8806 : {
8807 : HeapTuple tuple;
8808 : Form_pg_attribute attTup;
8809 :
8810 62 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8811 62 : if (!HeapTupleIsValid(tuple))
8812 0 : ereport(ERROR,
8813 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8814 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8815 : cmd->name, RelationGetRelationName(rel))));
8816 :
8817 62 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8818 :
8819 62 : if (attTup->attinhcount > 0)
8820 12 : ereport(ERROR,
8821 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8822 : errmsg("cannot drop generation expression from inherited column")));
8823 : }
8824 62 : }
8825 :
8826 : /*
8827 : * Return the address of the affected column.
8828 : */
8829 : static ObjectAddress
8830 56 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8831 : {
8832 : HeapTuple tuple;
8833 : Form_pg_attribute attTup;
8834 : AttrNumber attnum;
8835 : Relation attrelation;
8836 : Oid attrdefoid;
8837 : ObjectAddress address;
8838 :
8839 56 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8840 56 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8841 56 : if (!HeapTupleIsValid(tuple))
8842 0 : ereport(ERROR,
8843 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8844 : errmsg("column \"%s\" of relation \"%s\" does not exist",
8845 : colName, RelationGetRelationName(rel))));
8846 :
8847 56 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8848 56 : attnum = attTup->attnum;
8849 :
8850 56 : if (attnum <= 0)
8851 0 : ereport(ERROR,
8852 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8853 : errmsg("cannot alter system column \"%s\"",
8854 : colName)));
8855 :
8856 : /*
8857 : * TODO: This could be done, but it would need a table rewrite to
8858 : * materialize the generated values. Note that for the time being, we
8859 : * still error with missing_ok, so that we don't silently leave the column
8860 : * as generated.
8861 : */
8862 56 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8863 12 : ereport(ERROR,
8864 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8865 : errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8866 : errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8867 : colName, RelationGetRelationName(rel))));
8868 :
8869 44 : if (!attTup->attgenerated)
8870 : {
8871 24 : if (!missing_ok)
8872 12 : ereport(ERROR,
8873 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8874 : errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8875 : colName, RelationGetRelationName(rel))));
8876 : else
8877 : {
8878 12 : ereport(NOTICE,
8879 : (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8880 : colName, RelationGetRelationName(rel))));
8881 12 : heap_freetuple(tuple);
8882 12 : table_close(attrelation, RowExclusiveLock);
8883 12 : return InvalidObjectAddress;
8884 : }
8885 : }
8886 :
8887 : /*
8888 : * Mark the column as no longer generated. (The atthasdef flag needs to
8889 : * get cleared too, but RemoveAttrDefault will handle that.)
8890 : */
8891 20 : attTup->attgenerated = '\0';
8892 20 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8893 :
8894 20 : InvokeObjectPostAlterHook(RelationRelationId,
8895 : RelationGetRelid(rel),
8896 : attnum);
8897 20 : heap_freetuple(tuple);
8898 :
8899 20 : table_close(attrelation, RowExclusiveLock);
8900 :
8901 : /*
8902 : * Drop the dependency records of the GENERATED expression, in particular
8903 : * its INTERNAL dependency on the column, which would otherwise cause
8904 : * dependency.c to refuse to perform the deletion.
8905 : */
8906 20 : attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8907 20 : if (!OidIsValid(attrdefoid))
8908 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8909 : RelationGetRelid(rel), attnum);
8910 20 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8911 :
8912 : /* Make above changes visible */
8913 20 : CommandCounterIncrement();
8914 :
8915 : /*
8916 : * Get rid of the GENERATED expression itself. We use RESTRICT here for
8917 : * safety, but at present we do not expect anything to depend on the
8918 : * default.
8919 : */
8920 20 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8921 : false, false);
8922 :
8923 20 : ObjectAddressSubSet(address, RelationRelationId,
8924 : RelationGetRelid(rel), attnum);
8925 20 : return address;
8926 : }
8927 :
8928 : /*
8929 : * ALTER TABLE ALTER COLUMN SET STATISTICS
8930 : *
8931 : * Return value is the address of the modified column
8932 : */
8933 : static ObjectAddress
8934 164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8935 : {
8936 164 : int newtarget = 0;
8937 : bool newtarget_default;
8938 : Relation attrelation;
8939 : HeapTuple tuple,
8940 : newtuple;
8941 : Form_pg_attribute attrtuple;
8942 : AttrNumber attnum;
8943 : ObjectAddress address;
8944 : Datum repl_val[Natts_pg_attribute];
8945 : bool repl_null[Natts_pg_attribute];
8946 : bool repl_repl[Natts_pg_attribute];
8947 :
8948 : /*
8949 : * We allow referencing columns by numbers only for indexes, since table
8950 : * column numbers could contain gaps if columns are later dropped.
8951 : */
8952 164 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
8953 100 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8954 : !colName)
8955 0 : ereport(ERROR,
8956 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8957 : errmsg("cannot refer to non-index column by number")));
8958 :
8959 : /* -1 was used in previous versions for the default setting */
8960 164 : if (newValue && intVal(newValue) != -1)
8961 : {
8962 120 : newtarget = intVal(newValue);
8963 120 : newtarget_default = false;
8964 : }
8965 : else
8966 44 : newtarget_default = true;
8967 :
8968 164 : if (!newtarget_default)
8969 : {
8970 : /*
8971 : * Limit target to a sane range
8972 : */
8973 120 : if (newtarget < 0)
8974 : {
8975 0 : ereport(ERROR,
8976 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8977 : errmsg("statistics target %d is too low",
8978 : newtarget)));
8979 : }
8980 120 : else if (newtarget > MAX_STATISTICS_TARGET)
8981 : {
8982 0 : newtarget = MAX_STATISTICS_TARGET;
8983 0 : ereport(WARNING,
8984 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8985 : errmsg("lowering statistics target to %d",
8986 : newtarget)));
8987 : }
8988 : }
8989 :
8990 164 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8991 :
8992 164 : if (colName)
8993 : {
8994 100 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8995 :
8996 100 : if (!HeapTupleIsValid(tuple))
8997 12 : ereport(ERROR,
8998 : (errcode(ERRCODE_UNDEFINED_COLUMN),
8999 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9000 : colName, RelationGetRelationName(rel))));
9001 : }
9002 : else
9003 : {
9004 64 : tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
9005 :
9006 64 : if (!HeapTupleIsValid(tuple))
9007 12 : ereport(ERROR,
9008 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9009 : errmsg("column number %d of relation \"%s\" does not exist",
9010 : colNum, RelationGetRelationName(rel))));
9011 : }
9012 :
9013 140 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9014 :
9015 140 : attnum = attrtuple->attnum;
9016 140 : if (attnum <= 0)
9017 0 : ereport(ERROR,
9018 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9019 : errmsg("cannot alter system column \"%s\"",
9020 : colName)));
9021 :
9022 : /*
9023 : * Prevent this as long as the ANALYZE code skips virtual generated
9024 : * columns.
9025 : */
9026 140 : if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9027 0 : ereport(ERROR,
9028 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9029 : errmsg("cannot alter statistics on virtual generated column \"%s\"",
9030 : colName)));
9031 :
9032 140 : if (rel->rd_rel->relkind == RELKIND_INDEX ||
9033 88 : rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9034 : {
9035 52 : if (attnum > rel->rd_index->indnkeyatts)
9036 6 : ereport(ERROR,
9037 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9038 : errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9039 : NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9040 46 : else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9041 18 : ereport(ERROR,
9042 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9043 : errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9044 : NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9045 : errhint("Alter statistics on table column instead.")));
9046 : }
9047 :
9048 : /* Build new tuple. */
9049 116 : memset(repl_null, false, sizeof(repl_null));
9050 116 : memset(repl_repl, false, sizeof(repl_repl));
9051 116 : if (!newtarget_default)
9052 72 : repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
9053 : else
9054 44 : repl_null[Anum_pg_attribute_attstattarget - 1] = true;
9055 116 : repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
9056 116 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9057 : repl_val, repl_null, repl_repl);
9058 116 : CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9059 :
9060 116 : InvokeObjectPostAlterHook(RelationRelationId,
9061 : RelationGetRelid(rel),
9062 : attrtuple->attnum);
9063 116 : ObjectAddressSubSet(address, RelationRelationId,
9064 : RelationGetRelid(rel), attnum);
9065 :
9066 116 : heap_freetuple(newtuple);
9067 :
9068 116 : ReleaseSysCache(tuple);
9069 :
9070 116 : table_close(attrelation, RowExclusiveLock);
9071 :
9072 116 : return address;
9073 : }
9074 :
9075 : /*
9076 : * Return value is the address of the modified column
9077 : */
9078 : static ObjectAddress
9079 32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
9080 : bool isReset, LOCKMODE lockmode)
9081 : {
9082 : Relation attrelation;
9083 : HeapTuple tuple,
9084 : newtuple;
9085 : Form_pg_attribute attrtuple;
9086 : AttrNumber attnum;
9087 : Datum datum,
9088 : newOptions;
9089 : bool isnull;
9090 : ObjectAddress address;
9091 : Datum repl_val[Natts_pg_attribute];
9092 : bool repl_null[Natts_pg_attribute];
9093 : bool repl_repl[Natts_pg_attribute];
9094 :
9095 32 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9096 :
9097 32 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9098 :
9099 32 : if (!HeapTupleIsValid(tuple))
9100 0 : ereport(ERROR,
9101 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9102 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9103 : colName, RelationGetRelationName(rel))));
9104 32 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9105 :
9106 32 : attnum = attrtuple->attnum;
9107 32 : if (attnum <= 0)
9108 0 : ereport(ERROR,
9109 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9110 : errmsg("cannot alter system column \"%s\"",
9111 : colName)));
9112 :
9113 : /* Generate new proposed attoptions (text array) */
9114 32 : datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9115 : &isnull);
9116 32 : newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9117 : castNode(List, options), NULL, NULL,
9118 : false, isReset);
9119 : /* Validate new options */
9120 32 : (void) attribute_reloptions(newOptions, true);
9121 :
9122 : /* Build new tuple. */
9123 32 : memset(repl_null, false, sizeof(repl_null));
9124 32 : memset(repl_repl, false, sizeof(repl_repl));
9125 32 : if (newOptions != (Datum) 0)
9126 32 : repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9127 : else
9128 0 : repl_null[Anum_pg_attribute_attoptions - 1] = true;
9129 32 : repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9130 32 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9131 : repl_val, repl_null, repl_repl);
9132 :
9133 : /* Update system catalog. */
9134 32 : CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9135 :
9136 32 : InvokeObjectPostAlterHook(RelationRelationId,
9137 : RelationGetRelid(rel),
9138 : attrtuple->attnum);
9139 32 : ObjectAddressSubSet(address, RelationRelationId,
9140 : RelationGetRelid(rel), attnum);
9141 :
9142 32 : heap_freetuple(newtuple);
9143 :
9144 32 : ReleaseSysCache(tuple);
9145 :
9146 32 : table_close(attrelation, RowExclusiveLock);
9147 :
9148 32 : return address;
9149 : }
9150 :
9151 : /*
9152 : * Helper function for ATExecSetStorage and ATExecSetCompression
9153 : *
9154 : * Set the attstorage and/or attcompression fields for index columns
9155 : * associated with the specified table column.
9156 : */
9157 : static void
9158 320 : SetIndexStorageProperties(Relation rel, Relation attrelation,
9159 : AttrNumber attnum,
9160 : bool setstorage, char newstorage,
9161 : bool setcompression, char newcompression,
9162 : LOCKMODE lockmode)
9163 : {
9164 : ListCell *lc;
9165 :
9166 412 : foreach(lc, RelationGetIndexList(rel))
9167 : {
9168 92 : Oid indexoid = lfirst_oid(lc);
9169 : Relation indrel;
9170 92 : AttrNumber indattnum = 0;
9171 : HeapTuple tuple;
9172 :
9173 92 : indrel = index_open(indexoid, lockmode);
9174 :
9175 154 : for (int i = 0; i < indrel->rd_index->indnatts; i++)
9176 : {
9177 98 : if (indrel->rd_index->indkey.values[i] == attnum)
9178 : {
9179 36 : indattnum = i + 1;
9180 36 : break;
9181 : }
9182 : }
9183 :
9184 92 : if (indattnum == 0)
9185 : {
9186 56 : index_close(indrel, lockmode);
9187 56 : continue;
9188 : }
9189 :
9190 36 : tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9191 :
9192 36 : if (HeapTupleIsValid(tuple))
9193 : {
9194 36 : Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9195 :
9196 36 : if (setstorage)
9197 24 : attrtuple->attstorage = newstorage;
9198 :
9199 36 : if (setcompression)
9200 12 : attrtuple->attcompression = newcompression;
9201 :
9202 36 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9203 :
9204 36 : InvokeObjectPostAlterHook(RelationRelationId,
9205 : RelationGetRelid(rel),
9206 : attrtuple->attnum);
9207 :
9208 36 : heap_freetuple(tuple);
9209 : }
9210 :
9211 36 : index_close(indrel, lockmode);
9212 : }
9213 320 : }
9214 :
9215 : /*
9216 : * ALTER TABLE ALTER COLUMN SET STORAGE
9217 : *
9218 : * Return value is the address of the modified column
9219 : */
9220 : static ObjectAddress
9221 260 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9222 : {
9223 : Relation attrelation;
9224 : HeapTuple tuple;
9225 : Form_pg_attribute attrtuple;
9226 : AttrNumber attnum;
9227 : ObjectAddress address;
9228 :
9229 260 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9230 :
9231 260 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9232 :
9233 260 : if (!HeapTupleIsValid(tuple))
9234 12 : ereport(ERROR,
9235 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9236 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9237 : colName, RelationGetRelationName(rel))));
9238 248 : attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9239 :
9240 248 : attnum = attrtuple->attnum;
9241 248 : if (attnum <= 0)
9242 0 : ereport(ERROR,
9243 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9244 : errmsg("cannot alter system column \"%s\"",
9245 : colName)));
9246 :
9247 248 : attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9248 :
9249 248 : CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9250 :
9251 248 : InvokeObjectPostAlterHook(RelationRelationId,
9252 : RelationGetRelid(rel),
9253 : attrtuple->attnum);
9254 :
9255 : /*
9256 : * Apply the change to indexes as well (only for simple index columns,
9257 : * matching behavior of index.c ConstructTupleDescriptor()).
9258 : */
9259 248 : SetIndexStorageProperties(rel, attrelation, attnum,
9260 248 : true, attrtuple->attstorage,
9261 : false, 0,
9262 : lockmode);
9263 :
9264 248 : heap_freetuple(tuple);
9265 :
9266 248 : table_close(attrelation, RowExclusiveLock);
9267 :
9268 248 : ObjectAddressSubSet(address, RelationRelationId,
9269 : RelationGetRelid(rel), attnum);
9270 248 : return address;
9271 : }
9272 :
9273 :
9274 : /*
9275 : * ALTER TABLE DROP COLUMN
9276 : *
9277 : * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9278 : * because we have to decide at runtime whether to recurse or not depending
9279 : * on whether attinhcount goes to zero or not. (We can't check this in a
9280 : * static pre-pass because it won't handle multiple inheritance situations
9281 : * correctly.)
9282 : */
9283 : static void
9284 1694 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9285 : AlterTableCmd *cmd, LOCKMODE lockmode,
9286 : AlterTableUtilityContext *context)
9287 : {
9288 1694 : if (rel->rd_rel->reloftype && !recursing)
9289 6 : ereport(ERROR,
9290 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9291 : errmsg("cannot drop column from typed table")));
9292 :
9293 1688 : if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9294 84 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9295 :
9296 1682 : if (recurse)
9297 1402 : cmd->recurse = true;
9298 1682 : }
9299 :
9300 : /*
9301 : * Drops column 'colName' from relation 'rel' and returns the address of the
9302 : * dropped column. The column is also dropped (or marked as no longer
9303 : * inherited from relation) from the relation's inheritance children, if any.
9304 : *
9305 : * In the recursive invocations for inheritance child relations, instead of
9306 : * dropping the column directly (if to be dropped at all), its object address
9307 : * is added to 'addrs', which must be non-NULL in such invocations. All
9308 : * columns are dropped at the same time after all the children have been
9309 : * checked recursively.
9310 : */
9311 : static ObjectAddress
9312 2244 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9313 : DropBehavior behavior,
9314 : bool recurse, bool recursing,
9315 : bool missing_ok, LOCKMODE lockmode,
9316 : ObjectAddresses *addrs)
9317 : {
9318 : HeapTuple tuple;
9319 : Form_pg_attribute targetatt;
9320 : AttrNumber attnum;
9321 : List *children;
9322 : ObjectAddress object;
9323 : bool is_expr;
9324 :
9325 : /* At top level, permission check was done in ATPrepCmd, else do it */
9326 2244 : if (recursing)
9327 562 : ATSimplePermissions(AT_DropColumn, rel,
9328 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9329 :
9330 : /* Initialize addrs on the first invocation */
9331 : Assert(!recursing || addrs != NULL);
9332 :
9333 : /* since this function recurses, it could be driven to stack overflow */
9334 2244 : check_stack_depth();
9335 :
9336 2244 : if (!recursing)
9337 1682 : addrs = new_object_addresses();
9338 :
9339 : /*
9340 : * get the number of the attribute
9341 : */
9342 2244 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9343 2244 : if (!HeapTupleIsValid(tuple))
9344 : {
9345 54 : if (!missing_ok)
9346 : {
9347 36 : ereport(ERROR,
9348 : (errcode(ERRCODE_UNDEFINED_COLUMN),
9349 : errmsg("column \"%s\" of relation \"%s\" does not exist",
9350 : colName, RelationGetRelationName(rel))));
9351 : }
9352 : else
9353 : {
9354 18 : ereport(NOTICE,
9355 : (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9356 : colName, RelationGetRelationName(rel))));
9357 18 : return InvalidObjectAddress;
9358 : }
9359 : }
9360 2190 : targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9361 :
9362 2190 : attnum = targetatt->attnum;
9363 :
9364 : /* Can't drop a system attribute */
9365 2190 : if (attnum <= 0)
9366 6 : ereport(ERROR,
9367 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9368 : errmsg("cannot drop system column \"%s\"",
9369 : colName)));
9370 :
9371 : /*
9372 : * Don't drop inherited columns, unless recursing (presumably from a drop
9373 : * of the parent column)
9374 : */
9375 2184 : if (targetatt->attinhcount > 0 && !recursing)
9376 48 : ereport(ERROR,
9377 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9378 : errmsg("cannot drop inherited column \"%s\"",
9379 : colName)));
9380 :
9381 : /*
9382 : * Don't drop columns used in the partition key, either. (If we let this
9383 : * go through, the key column's dependencies would cause a cascaded drop
9384 : * of the whole table, which is surely not what the user expected.)
9385 : */
9386 2136 : if (has_partition_attrs(rel,
9387 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9388 : &is_expr))
9389 30 : ereport(ERROR,
9390 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9391 : errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9392 : colName, RelationGetRelationName(rel))));
9393 :
9394 2106 : ReleaseSysCache(tuple);
9395 :
9396 : /*
9397 : * Propagate to children as appropriate. Unlike most other ALTER
9398 : * routines, we have to do this one level of recursion at a time; we can't
9399 : * use find_all_inheritors to do it in one pass.
9400 : */
9401 : children =
9402 2106 : find_inheritance_children(RelationGetRelid(rel), lockmode);
9403 :
9404 2106 : if (children)
9405 : {
9406 : Relation attr_rel;
9407 : ListCell *child;
9408 :
9409 : /*
9410 : * In case of a partitioned table, the column must be dropped from the
9411 : * partitions as well.
9412 : */
9413 308 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9414 6 : ereport(ERROR,
9415 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9416 : errmsg("cannot drop column from only the partitioned table when partitions exist"),
9417 : errhint("Do not specify the ONLY keyword.")));
9418 :
9419 302 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9420 894 : foreach(child, children)
9421 : {
9422 598 : Oid childrelid = lfirst_oid(child);
9423 : Relation childrel;
9424 : Form_pg_attribute childatt;
9425 :
9426 : /* find_inheritance_children already got lock */
9427 598 : childrel = table_open(childrelid, NoLock);
9428 598 : CheckAlterTableIsSafe(childrel);
9429 :
9430 598 : tuple = SearchSysCacheCopyAttName(childrelid, colName);
9431 598 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9432 0 : elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9433 : colName, childrelid);
9434 598 : childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9435 :
9436 598 : if (childatt->attinhcount <= 0) /* shouldn't happen */
9437 0 : elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9438 : childrelid, colName);
9439 :
9440 598 : if (recurse)
9441 : {
9442 : /*
9443 : * If the child column has other definition sources, just
9444 : * decrement its inheritance count; if not, recurse to delete
9445 : * it.
9446 : */
9447 574 : if (childatt->attinhcount == 1 && !childatt->attislocal)
9448 : {
9449 : /* Time to delete this child column, too */
9450 562 : ATExecDropColumn(wqueue, childrel, colName,
9451 : behavior, true, true,
9452 : false, lockmode, addrs);
9453 : }
9454 : else
9455 : {
9456 : /* Child column must survive my deletion */
9457 12 : childatt->attinhcount--;
9458 :
9459 12 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9460 :
9461 : /* Make update visible */
9462 12 : CommandCounterIncrement();
9463 : }
9464 : }
9465 : else
9466 : {
9467 : /*
9468 : * If we were told to drop ONLY in this table (no recursion),
9469 : * we need to mark the inheritors' attributes as locally
9470 : * defined rather than inherited.
9471 : */
9472 24 : childatt->attinhcount--;
9473 24 : childatt->attislocal = true;
9474 :
9475 24 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9476 :
9477 : /* Make update visible */
9478 24 : CommandCounterIncrement();
9479 : }
9480 :
9481 592 : heap_freetuple(tuple);
9482 :
9483 592 : table_close(childrel, NoLock);
9484 : }
9485 296 : table_close(attr_rel, RowExclusiveLock);
9486 : }
9487 :
9488 : /* Add object to delete */
9489 2094 : object.classId = RelationRelationId;
9490 2094 : object.objectId = RelationGetRelid(rel);
9491 2094 : object.objectSubId = attnum;
9492 2094 : add_exact_object_address(&object, addrs);
9493 :
9494 2094 : if (!recursing)
9495 : {
9496 : /* Recursion has ended, drop everything that was collected */
9497 1538 : performMultipleDeletions(addrs, behavior, 0);
9498 1484 : free_object_addresses(addrs);
9499 : }
9500 :
9501 2040 : return object;
9502 : }
9503 :
9504 : /*
9505 : * Prepare to add a primary key on a table, by adding not-null constraints
9506 : * on all columns.
9507 : *
9508 : * The not-null constraints for a primary key must cover the whole inheritance
9509 : * hierarchy (failing to ensure that leads to funny corner cases). For the
9510 : * normal case where we're asked to recurse, this routine checks if the
9511 : * not-null constraints exist already, and if not queues a requirement for
9512 : * them to be created by phase 2.
9513 : *
9514 : * For the case where we're asked not to recurse, we verify that a not-null
9515 : * constraint exists on each column of each (direct) child table, throwing an
9516 : * error if not. Not throwing an error would also work, because a not-null
9517 : * constraint would be created anyway, but it'd cause a silent scan of the
9518 : * child table to verify absence of nulls. We prefer to let the user know so
9519 : * that they can add the constraint manually without having to hold
9520 : * AccessExclusiveLock while at it.
9521 : *
9522 : * However, it's also important that we do not acquire locks on children if
9523 : * the not-null constraints already exist on the parent, to avoid risking
9524 : * deadlocks during parallel pg_restore of PKs on partitioned tables.
9525 : */
9526 : static void
9527 16410 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9528 : bool recurse, LOCKMODE lockmode,
9529 : AlterTableUtilityContext *context)
9530 : {
9531 : Constraint *pkconstr;
9532 16410 : List *children = NIL;
9533 16410 : bool got_children = false;
9534 :
9535 16410 : pkconstr = castNode(Constraint, cmd->def);
9536 16410 : if (pkconstr->contype != CONSTR_PRIMARY)
9537 9424 : return;
9538 :
9539 : /* Verify that columns are not-null, or request that they be made so */
9540 14948 : foreach_node(String, column, pkconstr->keys)
9541 : {
9542 : AlterTableCmd *newcmd;
9543 : Constraint *nnconstr;
9544 : HeapTuple tuple;
9545 :
9546 : /*
9547 : * First check if a suitable constraint exists. If it does, we don't
9548 : * need to request another one. We do need to bail out if it's not
9549 : * valid, though.
9550 : */
9551 1036 : tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9552 1036 : if (tuple != NULL)
9553 : {
9554 526 : verifyNotNullPKCompatible(tuple, strVal(column));
9555 :
9556 : /* All good with this one; don't request another */
9557 514 : heap_freetuple(tuple);
9558 514 : continue;
9559 : }
9560 510 : else if (!recurse)
9561 : {
9562 : /*
9563 : * No constraint on this column. Asked not to recurse, we won't
9564 : * create one here, but verify that all children have one.
9565 : */
9566 36 : if (!got_children)
9567 : {
9568 36 : children = find_inheritance_children(RelationGetRelid(rel),
9569 : lockmode);
9570 : /* only search for children on the first time through */
9571 36 : got_children = true;
9572 : }
9573 :
9574 72 : foreach_oid(childrelid, children)
9575 : {
9576 : HeapTuple tup;
9577 :
9578 36 : tup = findNotNullConstraint(childrelid, strVal(column));
9579 36 : if (!tup)
9580 6 : ereport(ERROR,
9581 : errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9582 : strVal(column), get_rel_name(childrelid)));
9583 : /* verify it's good enough */
9584 30 : verifyNotNullPKCompatible(tup, strVal(column));
9585 : }
9586 : }
9587 :
9588 : /* This column is not already not-null, so add it to the queue */
9589 492 : nnconstr = makeNotNullConstraint(column);
9590 :
9591 492 : newcmd = makeNode(AlterTableCmd);
9592 492 : newcmd->subtype = AT_AddConstraint;
9593 : /* note we force recurse=true here; see above */
9594 492 : newcmd->recurse = true;
9595 492 : newcmd->def = (Node *) nnconstr;
9596 :
9597 492 : ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9598 : }
9599 : }
9600 :
9601 : /*
9602 : * Verify whether the given not-null constraint is compatible with a
9603 : * primary key. If not, an error is thrown.
9604 : */
9605 : static void
9606 556 : verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9607 : {
9608 556 : Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
9609 :
9610 556 : if (conForm->contype != CONSTRAINT_NOTNULL)
9611 0 : elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9612 :
9613 : /* a NO INHERIT constraint is no good */
9614 556 : if (conForm->connoinherit)
9615 12 : ereport(ERROR,
9616 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9617 : errmsg("cannot create primary key on column \"%s\"", colname),
9618 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9619 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9620 : NameStr(conForm->conname), colname,
9621 : get_rel_name(conForm->conrelid), "NO INHERIT"),
9622 : errhint("You might need to make the existing constraint inheritable using %s.",
9623 : "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9624 :
9625 : /* an unvalidated constraint is no good */
9626 544 : if (!conForm->convalidated)
9627 12 : ereport(ERROR,
9628 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9629 : errmsg("cannot create primary key on column \"%s\"", colname),
9630 : /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9631 : errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9632 : NameStr(conForm->conname), colname,
9633 : get_rel_name(conForm->conrelid), "NOT VALID"),
9634 : errhint("You might need to validate it using %s.",
9635 : "ALTER TABLE ... VALIDATE CONSTRAINT"));
9636 532 : }
9637 :
9638 : /*
9639 : * ALTER TABLE ADD INDEX
9640 : *
9641 : * There is no such command in the grammar, but parse_utilcmd.c converts
9642 : * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9643 : * us schedule creation of the index at the appropriate time during ALTER.
9644 : *
9645 : * Return value is the address of the new index.
9646 : */
9647 : static ObjectAddress
9648 1650 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9649 : IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9650 : {
9651 : bool check_rights;
9652 : bool skip_build;
9653 : bool quiet;
9654 : ObjectAddress address;
9655 :
9656 : Assert(IsA(stmt, IndexStmt));
9657 : Assert(!stmt->concurrent);
9658 :
9659 : /* The IndexStmt has already been through transformIndexStmt */
9660 : Assert(stmt->transformed);
9661 :
9662 : /* suppress schema rights check when rebuilding existing index */
9663 1650 : check_rights = !is_rebuild;
9664 : /* skip index build if phase 3 will do it or we're reusing an old one */
9665 1650 : skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9666 : /* suppress notices when rebuilding existing index */
9667 1650 : quiet = is_rebuild;
9668 :
9669 1650 : address = DefineIndex(RelationGetRelid(rel),
9670 : stmt,
9671 : InvalidOid, /* no predefined OID */
9672 : InvalidOid, /* no parent index */
9673 : InvalidOid, /* no parent constraint */
9674 : -1, /* total_parts unknown */
9675 : true, /* is_alter_table */
9676 : check_rights,
9677 : false, /* check_not_in_use - we did it already */
9678 : skip_build,
9679 : quiet);
9680 :
9681 : /*
9682 : * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9683 : * new index instead of building from scratch. Restore associated fields.
9684 : * This may store InvalidSubTransactionId in both fields, in which case
9685 : * relcache.c will assume it can rebuild the relcache entry. Hence, do
9686 : * this after the CCI that made catalog rows visible to any rebuild. The
9687 : * DROP of the old edition of this index will have scheduled the storage
9688 : * for deletion at commit, so cancel that pending deletion.
9689 : */
9690 1480 : if (RelFileNumberIsValid(stmt->oldNumber))
9691 : {
9692 74 : Relation irel = index_open(address.objectId, NoLock);
9693 :
9694 74 : irel->rd_createSubid = stmt->oldCreateSubid;
9695 74 : irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9696 74 : RelationPreserveStorage(irel->rd_locator, true);
9697 74 : index_close(irel, NoLock);
9698 : }
9699 :
9700 1480 : return address;
9701 : }
9702 :
9703 : /*
9704 : * ALTER TABLE ADD STATISTICS
9705 : *
9706 : * This is no such command in the grammar, but we use this internally to add
9707 : * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9708 : * column type change.
9709 : */
9710 : static ObjectAddress
9711 74 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9712 : CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9713 : {
9714 : ObjectAddress address;
9715 :
9716 : Assert(IsA(stmt, CreateStatsStmt));
9717 :
9718 : /* The CreateStatsStmt has already been through transformStatsStmt */
9719 : Assert(stmt->transformed);
9720 :
9721 74 : address = CreateStatistics(stmt, !is_rebuild);
9722 :
9723 74 : return address;
9724 : }
9725 :
9726 : /*
9727 : * ALTER TABLE ADD CONSTRAINT USING INDEX
9728 : *
9729 : * Returns the address of the new constraint.
9730 : */
9731 : static ObjectAddress
9732 10860 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9733 : IndexStmt *stmt, LOCKMODE lockmode)
9734 : {
9735 10860 : Oid index_oid = stmt->indexOid;
9736 : Relation indexRel;
9737 : char *indexName;
9738 : IndexInfo *indexInfo;
9739 : char *constraintName;
9740 : char constraintType;
9741 : ObjectAddress address;
9742 : bits16 flags;
9743 :
9744 : Assert(IsA(stmt, IndexStmt));
9745 : Assert(OidIsValid(index_oid));
9746 : Assert(stmt->isconstraint);
9747 :
9748 : /*
9749 : * Doing this on partitioned tables is not a simple feature to implement,
9750 : * so let's punt for now.
9751 : */
9752 10860 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9753 6 : ereport(ERROR,
9754 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9755 : errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9756 :
9757 10854 : indexRel = index_open(index_oid, AccessShareLock);
9758 :
9759 10854 : indexName = pstrdup(RelationGetRelationName(indexRel));
9760 :
9761 10854 : indexInfo = BuildIndexInfo(indexRel);
9762 :
9763 : /* this should have been checked at parse time */
9764 10854 : if (!indexInfo->ii_Unique)
9765 0 : elog(ERROR, "index \"%s\" is not unique", indexName);
9766 :
9767 : /*
9768 : * Determine name to assign to constraint. We require a constraint to
9769 : * have the same name as the underlying index; therefore, use the index's
9770 : * existing name as the default constraint name, and if the user
9771 : * explicitly gives some other name for the constraint, rename the index
9772 : * to match.
9773 : */
9774 10854 : constraintName = stmt->idxname;
9775 10854 : if (constraintName == NULL)
9776 10828 : constraintName = indexName;
9777 26 : else if (strcmp(constraintName, indexName) != 0)
9778 : {
9779 20 : ereport(NOTICE,
9780 : (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9781 : indexName, constraintName)));
9782 20 : RenameRelationInternal(index_oid, constraintName, false, true);
9783 : }
9784 :
9785 : /* Extra checks needed if making primary key */
9786 10854 : if (stmt->primary)
9787 6130 : index_check_primary_key(rel, indexInfo, true, stmt);
9788 :
9789 : /* Note we currently don't support EXCLUSION constraints here */
9790 10848 : if (stmt->primary)
9791 6124 : constraintType = CONSTRAINT_PRIMARY;
9792 : else
9793 4724 : constraintType = CONSTRAINT_UNIQUE;
9794 :
9795 : /* Create the catalog entries for the constraint */
9796 10848 : flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9797 : INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9798 21696 : (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9799 10848 : (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9800 10848 : (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9801 :
9802 10848 : address = index_constraint_create(rel,
9803 : index_oid,
9804 : InvalidOid,
9805 : indexInfo,
9806 : constraintName,
9807 : constraintType,
9808 : flags,
9809 : allowSystemTableMods,
9810 : false); /* is_internal */
9811 :
9812 10848 : index_close(indexRel, NoLock);
9813 :
9814 10848 : return address;
9815 : }
9816 :
9817 : /*
9818 : * ALTER TABLE ADD CONSTRAINT
9819 : *
9820 : * Return value is the address of the new constraint; if no constraint was
9821 : * added, InvalidObjectAddress is returned.
9822 : */
9823 : static ObjectAddress
9824 13108 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9825 : Constraint *newConstraint, bool recurse, bool is_readd,
9826 : LOCKMODE lockmode)
9827 : {
9828 13108 : ObjectAddress address = InvalidObjectAddress;
9829 :
9830 : Assert(IsA(newConstraint, Constraint));
9831 :
9832 : /*
9833 : * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9834 : * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9835 : * parse_utilcmd.c).
9836 : */
9837 13108 : switch (newConstraint->contype)
9838 : {
9839 10412 : case CONSTR_CHECK:
9840 : case CONSTR_NOTNULL:
9841 : address =
9842 10412 : ATAddCheckNNConstraint(wqueue, tab, rel,
9843 : newConstraint, recurse, false, is_readd,
9844 : lockmode);
9845 10268 : break;
9846 :
9847 2696 : case CONSTR_FOREIGN:
9848 :
9849 : /*
9850 : * Assign or validate constraint name
9851 : */
9852 2696 : if (newConstraint->conname)
9853 : {
9854 1212 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9855 : RelationGetRelid(rel),
9856 1212 : newConstraint->conname))
9857 0 : ereport(ERROR,
9858 : (errcode(ERRCODE_DUPLICATE_OBJECT),
9859 : errmsg("constraint \"%s\" for relation \"%s\" already exists",
9860 : newConstraint->conname,
9861 : RelationGetRelationName(rel))));
9862 : }
9863 : else
9864 1484 : newConstraint->conname =
9865 1484 : ChooseConstraintName(RelationGetRelationName(rel),
9866 1484 : ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9867 : "fkey",
9868 1484 : RelationGetNamespace(rel),
9869 : NIL);
9870 :
9871 2696 : address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9872 : newConstraint,
9873 : recurse, false,
9874 : lockmode);
9875 2148 : break;
9876 :
9877 0 : default:
9878 0 : elog(ERROR, "unrecognized constraint type: %d",
9879 : (int) newConstraint->contype);
9880 : }
9881 :
9882 12416 : return address;
9883 : }
9884 :
9885 : /*
9886 : * Generate the column-name portion of the constraint name for a new foreign
9887 : * key given the list of column names that reference the referenced
9888 : * table. This will be passed to ChooseConstraintName along with the parent
9889 : * table name and the "fkey" suffix.
9890 : *
9891 : * We know that less than NAMEDATALEN characters will actually be used, so we
9892 : * can truncate the result once we've generated that many.
9893 : *
9894 : * XXX see also ChooseExtendedStatisticNameAddition and
9895 : * ChooseIndexNameAddition.
9896 : */
9897 : static char *
9898 1484 : ChooseForeignKeyConstraintNameAddition(List *colnames)
9899 : {
9900 : char buf[NAMEDATALEN * 2];
9901 1484 : int buflen = 0;
9902 : ListCell *lc;
9903 :
9904 1484 : buf[0] = '\0';
9905 3396 : foreach(lc, colnames)
9906 : {
9907 1912 : const char *name = strVal(lfirst(lc));
9908 :
9909 1912 : if (buflen > 0)
9910 428 : buf[buflen++] = '_'; /* insert _ between names */
9911 :
9912 : /*
9913 : * At this point we have buflen <= NAMEDATALEN. name should be less
9914 : * than NAMEDATALEN already, but use strlcpy for paranoia.
9915 : */
9916 1912 : strlcpy(buf + buflen, name, NAMEDATALEN);
9917 1912 : buflen += strlen(buf + buflen);
9918 1912 : if (buflen >= NAMEDATALEN)
9919 0 : break;
9920 : }
9921 1484 : return pstrdup(buf);
9922 : }
9923 :
9924 : /*
9925 : * Add a check or not-null constraint to a single table and its children.
9926 : * Returns the address of the constraint added to the parent relation,
9927 : * if one gets added, or InvalidObjectAddress otherwise.
9928 : *
9929 : * Subroutine for ATExecAddConstraint.
9930 : *
9931 : * We must recurse to child tables during execution, rather than using
9932 : * ALTER TABLE's normal prep-time recursion. The reason is that all the
9933 : * constraints *must* be given the same name, else they won't be seen as
9934 : * related later. If the user didn't explicitly specify a name, then
9935 : * AddRelationNewConstraints would normally assign different names to the
9936 : * child constraints. To fix that, we must capture the name assigned at
9937 : * the parent table and pass that down.
9938 : */
9939 : static ObjectAddress
9940 11318 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9941 : Constraint *constr, bool recurse, bool recursing,
9942 : bool is_readd, LOCKMODE lockmode)
9943 : {
9944 : List *newcons;
9945 : ListCell *lcon;
9946 : List *children;
9947 : ListCell *child;
9948 11318 : ObjectAddress address = InvalidObjectAddress;
9949 :
9950 : /* Guard against stack overflow due to overly deep inheritance tree. */
9951 11318 : check_stack_depth();
9952 :
9953 : /* At top level, permission check was done in ATPrepCmd, else do it */
9954 11318 : if (recursing)
9955 906 : ATSimplePermissions(AT_AddConstraint, rel,
9956 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9957 :
9958 : /*
9959 : * Call AddRelationNewConstraints to do the work, making sure it works on
9960 : * a copy of the Constraint so transformExpr can't modify the original. It
9961 : * returns a list of cooked constraints.
9962 : *
9963 : * If the constraint ends up getting merged with a pre-existing one, it's
9964 : * omitted from the returned list, which is what we want: we do not need
9965 : * to do any validation work. That can only happen at child tables,
9966 : * though, since we disallow merging at the top level.
9967 : */
9968 11318 : newcons = AddRelationNewConstraints(rel, NIL,
9969 11318 : list_make1(copyObject(constr)),
9970 11318 : recursing || is_readd, /* allow_merge */
9971 11318 : !recursing, /* is_local */
9972 : is_readd, /* is_internal */
9973 11318 : NULL); /* queryString not available
9974 : * here */
9975 :
9976 : /* we don't expect more than one constraint here */
9977 : Assert(list_length(newcons) <= 1);
9978 :
9979 : /* Add each to-be-validated constraint to Phase 3's queue */
9980 22160 : foreach(lcon, newcons)
9981 : {
9982 10980 : CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9983 :
9984 10980 : if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9985 : {
9986 : NewConstraint *newcon;
9987 :
9988 1006 : newcon = palloc0_object(NewConstraint);
9989 1006 : newcon->name = ccon->name;
9990 1006 : newcon->contype = ccon->contype;
9991 1006 : newcon->qual = ccon->expr;
9992 :
9993 1006 : tab->constraints = lappend(tab->constraints, newcon);
9994 : }
9995 :
9996 : /* Save the actually assigned name if it was defaulted */
9997 10980 : if (constr->conname == NULL)
9998 9074 : constr->conname = ccon->name;
9999 :
10000 : /*
10001 : * If adding a valid not-null constraint, set the pg_attribute flag
10002 : * and tell phase 3 to verify existing rows, if needed. For an
10003 : * invalid constraint, just set attnotnull, without queueing
10004 : * verification.
10005 : */
10006 10980 : if (constr->contype == CONSTR_NOTNULL)
10007 9510 : set_attnotnull(wqueue, rel, ccon->attnum,
10008 9510 : !constr->skip_validation,
10009 9510 : !constr->skip_validation);
10010 :
10011 10980 : ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
10012 : }
10013 :
10014 : /* At this point we must have a locked-down name to use */
10015 : Assert(newcons == NIL || constr->conname != NULL);
10016 :
10017 : /* Advance command counter in case same table is visited multiple times */
10018 11180 : CommandCounterIncrement();
10019 :
10020 : /*
10021 : * If the constraint got merged with an existing constraint, we're done.
10022 : * We mustn't recurse to child tables in this case, because they've
10023 : * already got the constraint, and visiting them again would lead to an
10024 : * incorrect value for coninhcount.
10025 : */
10026 11180 : if (newcons == NIL)
10027 200 : return address;
10028 :
10029 : /*
10030 : * If adding a NO INHERIT constraint, no need to find our children.
10031 : */
10032 10980 : if (constr->is_no_inherit)
10033 84 : return address;
10034 :
10035 : /*
10036 : * Propagate to children as appropriate. Unlike most other ALTER
10037 : * routines, we have to do this one level of recursion at a time; we can't
10038 : * use find_all_inheritors to do it in one pass.
10039 : */
10040 : children =
10041 10896 : find_inheritance_children(RelationGetRelid(rel), lockmode);
10042 :
10043 : /*
10044 : * Check if ONLY was specified with ALTER TABLE. If so, allow the
10045 : * constraint creation only if there are no children currently. Error out
10046 : * otherwise.
10047 : */
10048 10896 : if (!recurse && children != NIL)
10049 6 : ereport(ERROR,
10050 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10051 : errmsg("constraint must be added to child tables too")));
10052 :
10053 : /*
10054 : * Recurse to create the constraint on each child.
10055 : */
10056 11766 : foreach(child, children)
10057 : {
10058 906 : Oid childrelid = lfirst_oid(child);
10059 : Relation childrel;
10060 : AlteredTableInfo *childtab;
10061 :
10062 : /* find_inheritance_children already got lock */
10063 906 : childrel = table_open(childrelid, NoLock);
10064 906 : CheckAlterTableIsSafe(childrel);
10065 :
10066 : /* Find or create work queue entry for this table */
10067 906 : childtab = ATGetQueueEntry(wqueue, childrel);
10068 :
10069 : /* Recurse to this child */
10070 906 : ATAddCheckNNConstraint(wqueue, childtab, childrel,
10071 : constr, recurse, true, is_readd, lockmode);
10072 :
10073 876 : table_close(childrel, NoLock);
10074 : }
10075 :
10076 10860 : return address;
10077 : }
10078 :
10079 : /*
10080 : * Add a foreign-key constraint to a single table; return the new constraint's
10081 : * address.
10082 : *
10083 : * Subroutine for ATExecAddConstraint. Must already hold exclusive
10084 : * lock on the rel, and have done appropriate validity checks for it.
10085 : * We do permissions checks here, however.
10086 : *
10087 : * When the referenced or referencing tables (or both) are partitioned,
10088 : * multiple pg_constraint rows are required -- one for each partitioned table
10089 : * and each partition on each side (fortunately, not one for every combination
10090 : * thereof). We also need action triggers on each leaf partition on the
10091 : * referenced side, and check triggers on each leaf partition on the
10092 : * referencing side.
10093 : */
10094 : static ObjectAddress
10095 2696 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
10096 : Constraint *fkconstraint,
10097 : bool recurse, bool recursing, LOCKMODE lockmode)
10098 : {
10099 : Relation pkrel;
10100 2696 : int16 pkattnum[INDEX_MAX_KEYS] = {0};
10101 2696 : int16 fkattnum[INDEX_MAX_KEYS] = {0};
10102 2696 : Oid pktypoid[INDEX_MAX_KEYS] = {0};
10103 2696 : Oid fktypoid[INDEX_MAX_KEYS] = {0};
10104 2696 : Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10105 2696 : Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10106 2696 : Oid opclasses[INDEX_MAX_KEYS] = {0};
10107 2696 : Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10108 2696 : Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10109 2696 : Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10110 2696 : int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10111 : bool with_period;
10112 : bool pk_has_without_overlaps;
10113 : int i;
10114 : int numfks,
10115 : numpks,
10116 : numfkdelsetcols;
10117 : Oid indexOid;
10118 : bool old_check_ok;
10119 : ObjectAddress address;
10120 2696 : ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10121 :
10122 : /*
10123 : * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10124 : * delete rows out from under us.
10125 : */
10126 2696 : if (OidIsValid(fkconstraint->old_pktable_oid))
10127 72 : pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10128 : else
10129 2624 : pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10130 :
10131 : /*
10132 : * Validity checks (permission checks wait till we have the column
10133 : * numbers)
10134 : */
10135 2690 : if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10136 6 : ereport(ERROR,
10137 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
10138 : errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10139 : RelationGetRelationName(rel),
10140 : RelationGetRelationName(pkrel)));
10141 :
10142 2684 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10143 368 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10144 0 : ereport(ERROR,
10145 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10146 : errmsg("referenced relation \"%s\" is not a table",
10147 : RelationGetRelationName(pkrel))));
10148 :
10149 2684 : if (!allowSystemTableMods && IsSystemRelation(pkrel))
10150 2 : ereport(ERROR,
10151 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10152 : errmsg("permission denied: \"%s\" is a system catalog",
10153 : RelationGetRelationName(pkrel))));
10154 :
10155 : /*
10156 : * References from permanent or unlogged tables to temp tables, and from
10157 : * permanent tables to unlogged tables, are disallowed because the
10158 : * referenced data can vanish out from under us. References from temp
10159 : * tables to any other table type are also disallowed, because other
10160 : * backends might need to run the RI triggers on the perm table, but they
10161 : * can't reliably see tuples in the local buffers of other backends.
10162 : */
10163 2682 : switch (rel->rd_rel->relpersistence)
10164 : {
10165 2392 : case RELPERSISTENCE_PERMANENT:
10166 2392 : if (!RelationIsPermanent(pkrel))
10167 0 : ereport(ERROR,
10168 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10169 : errmsg("constraints on permanent tables may reference only permanent tables")));
10170 2392 : break;
10171 12 : case RELPERSISTENCE_UNLOGGED:
10172 12 : if (!RelationIsPermanent(pkrel)
10173 12 : && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10174 0 : ereport(ERROR,
10175 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10176 : errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10177 12 : break;
10178 278 : case RELPERSISTENCE_TEMP:
10179 278 : if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10180 0 : ereport(ERROR,
10181 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10182 : errmsg("constraints on temporary tables may reference only temporary tables")));
10183 278 : if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10184 0 : ereport(ERROR,
10185 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10186 : errmsg("constraints on temporary tables must involve temporary tables of this session")));
10187 278 : break;
10188 : }
10189 :
10190 : /*
10191 : * Look up the referencing attributes to make sure they exist, and record
10192 : * their attnums and type and collation OIDs.
10193 : */
10194 2682 : numfks = transformColumnNameList(RelationGetRelid(rel),
10195 : fkconstraint->fk_attrs,
10196 : fkattnum, fktypoid, fkcolloid);
10197 2652 : with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10198 2652 : if (with_period && !fkconstraint->fk_with_period)
10199 24 : ereport(ERROR,
10200 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10201 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10202 :
10203 2628 : numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10204 : fkconstraint->fk_del_set_cols,
10205 : fkdelsetcols, NULL, NULL);
10206 2622 : numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10207 : numfkdelsetcols,
10208 : fkdelsetcols,
10209 : fkconstraint->fk_del_set_cols);
10210 :
10211 : /*
10212 : * If the attribute list for the referenced table was omitted, lookup the
10213 : * definition of the primary key and use it. Otherwise, validate the
10214 : * supplied attribute list. In either case, discover the index OID and
10215 : * index opclasses, and the attnums and type and collation OIDs of the
10216 : * attributes.
10217 : */
10218 2616 : if (fkconstraint->pk_attrs == NIL)
10219 : {
10220 1268 : numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10221 : &fkconstraint->pk_attrs,
10222 : pkattnum, pktypoid, pkcolloid,
10223 : opclasses, &pk_has_without_overlaps);
10224 :
10225 : /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10226 1268 : if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10227 24 : ereport(ERROR,
10228 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10229 : errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10230 : }
10231 : else
10232 : {
10233 1348 : numpks = transformColumnNameList(RelationGetRelid(pkrel),
10234 : fkconstraint->pk_attrs,
10235 : pkattnum, pktypoid, pkcolloid);
10236 :
10237 : /* Since we got pk_attrs, one should be a period. */
10238 1318 : if (with_period && !fkconstraint->pk_with_period)
10239 24 : ereport(ERROR,
10240 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10241 : errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10242 :
10243 : /* Look for an index matching the column list */
10244 1294 : indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10245 : with_period, opclasses, &pk_has_without_overlaps);
10246 : }
10247 :
10248 : /*
10249 : * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10250 : * must use PERIOD.
10251 : */
10252 2502 : if (pk_has_without_overlaps && !with_period)
10253 12 : ereport(ERROR,
10254 : errcode(ERRCODE_INVALID_FOREIGN_KEY),
10255 : errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10256 :
10257 : /*
10258 : * Now we can check permissions.
10259 : */
10260 2490 : checkFkeyPermissions(pkrel, pkattnum, numpks);
10261 :
10262 : /*
10263 : * Check some things for generated columns.
10264 : */
10265 5844 : for (i = 0; i < numfks; i++)
10266 : {
10267 3384 : char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10268 :
10269 3384 : if (attgenerated)
10270 : {
10271 : /*
10272 : * Check restrictions on UPDATE/DELETE actions, per SQL standard
10273 : */
10274 48 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10275 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10276 48 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10277 12 : ereport(ERROR,
10278 : (errcode(ERRCODE_SYNTAX_ERROR),
10279 : errmsg("invalid %s action for foreign key constraint containing generated column",
10280 : "ON UPDATE")));
10281 36 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10282 24 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10283 12 : ereport(ERROR,
10284 : (errcode(ERRCODE_SYNTAX_ERROR),
10285 : errmsg("invalid %s action for foreign key constraint containing generated column",
10286 : "ON DELETE")));
10287 : }
10288 :
10289 : /*
10290 : * FKs on virtual columns are not supported. This would require
10291 : * various additional support in ri_triggers.c, including special
10292 : * handling in ri_NullCheck(), ri_KeysEqual(),
10293 : * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10294 : * as NULL there). Also not really practical as long as you can't
10295 : * index virtual columns.
10296 : */
10297 3360 : if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10298 6 : ereport(ERROR,
10299 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10300 : errmsg("foreign key constraints on virtual generated columns are not supported")));
10301 : }
10302 :
10303 : /*
10304 : * Some actions are currently unsupported for foreign keys using PERIOD.
10305 : */
10306 2460 : if (fkconstraint->fk_with_period)
10307 : {
10308 278 : if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10309 266 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10310 248 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10311 230 : fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10312 66 : ereport(ERROR,
10313 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10314 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10315 : "ON UPDATE"));
10316 :
10317 212 : if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10318 206 : fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10319 206 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10320 206 : fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10321 6 : ereport(ERROR,
10322 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10323 : errmsg("unsupported %s action for foreign key constraint using PERIOD",
10324 : "ON DELETE"));
10325 : }
10326 :
10327 : /*
10328 : * Look up the equality operators to use in the constraint.
10329 : *
10330 : * Note that we have to be careful about the difference between the actual
10331 : * PK column type and the opclass' declared input type, which might be
10332 : * only binary-compatible with it. The declared opcintype is the right
10333 : * thing to probe pg_amop with.
10334 : */
10335 2388 : if (numfks != numpks)
10336 0 : ereport(ERROR,
10337 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10338 : errmsg("number of referencing and referenced columns for foreign key disagree")));
10339 :
10340 : /*
10341 : * On the strength of a previous constraint, we might avoid scanning
10342 : * tables to validate this one. See below.
10343 : */
10344 2388 : old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10345 : Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10346 :
10347 5214 : for (i = 0; i < numpks; i++)
10348 : {
10349 3066 : Oid pktype = pktypoid[i];
10350 3066 : Oid fktype = fktypoid[i];
10351 : Oid fktyped;
10352 3066 : Oid pkcoll = pkcolloid[i];
10353 3066 : Oid fkcoll = fkcolloid[i];
10354 : HeapTuple cla_ht;
10355 : Form_pg_opclass cla_tup;
10356 : Oid amid;
10357 : Oid opfamily;
10358 : Oid opcintype;
10359 : bool for_overlaps;
10360 : CompareType cmptype;
10361 : Oid pfeqop;
10362 : Oid ppeqop;
10363 : Oid ffeqop;
10364 : int16 eqstrategy;
10365 : Oid pfeqop_right;
10366 :
10367 : /* We need several fields out of the pg_opclass entry */
10368 3066 : cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10369 3066 : if (!HeapTupleIsValid(cla_ht))
10370 0 : elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10371 3066 : cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10372 3066 : amid = cla_tup->opcmethod;
10373 3066 : opfamily = cla_tup->opcfamily;
10374 3066 : opcintype = cla_tup->opcintype;
10375 3066 : ReleaseSysCache(cla_ht);
10376 :
10377 : /*
10378 : * Get strategy number from index AM.
10379 : *
10380 : * For a normal foreign-key constraint, this should not fail, since we
10381 : * already checked that the index is unique and should therefore have
10382 : * appropriate equal operators. For a period foreign key, this could
10383 : * fail if we selected a non-matching exclusion constraint earlier.
10384 : * (XXX Maybe we should do these lookups earlier so we don't end up
10385 : * doing that.)
10386 : */
10387 3066 : for_overlaps = with_period && i == numpks - 1;
10388 3066 : cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10389 3066 : eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10390 3066 : if (eqstrategy == InvalidStrategy)
10391 0 : ereport(ERROR,
10392 : errcode(ERRCODE_UNDEFINED_OBJECT),
10393 : for_overlaps
10394 : ? errmsg("could not identify an overlaps operator for foreign key")
10395 : : errmsg("could not identify an equality operator for foreign key"),
10396 : errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10397 : cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10398 :
10399 : /*
10400 : * There had better be a primary equality operator for the index.
10401 : * We'll use it for PK = PK comparisons.
10402 : */
10403 3066 : ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10404 : eqstrategy);
10405 :
10406 3066 : if (!OidIsValid(ppeqop))
10407 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10408 : eqstrategy, opcintype, opcintype, opfamily);
10409 :
10410 : /*
10411 : * Are there equality operators that take exactly the FK type? Assume
10412 : * we should look through any domain here.
10413 : */
10414 3066 : fktyped = getBaseType(fktype);
10415 :
10416 3066 : pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10417 : eqstrategy);
10418 3066 : if (OidIsValid(pfeqop))
10419 : {
10420 2370 : pfeqop_right = fktyped;
10421 2370 : ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10422 : eqstrategy);
10423 : }
10424 : else
10425 : {
10426 : /* keep compiler quiet */
10427 696 : pfeqop_right = InvalidOid;
10428 696 : ffeqop = InvalidOid;
10429 : }
10430 :
10431 3066 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10432 : {
10433 : /*
10434 : * Otherwise, look for an implicit cast from the FK type to the
10435 : * opcintype, and if found, use the primary equality operator.
10436 : * This is a bit tricky because opcintype might be a polymorphic
10437 : * type such as ANYARRAY or ANYENUM; so what we have to test is
10438 : * whether the two actual column types can be concurrently cast to
10439 : * that type. (Otherwise, we'd fail to reject combinations such
10440 : * as int[] and point[].)
10441 : */
10442 : Oid input_typeids[2];
10443 : Oid target_typeids[2];
10444 :
10445 696 : input_typeids[0] = pktype;
10446 696 : input_typeids[1] = fktype;
10447 696 : target_typeids[0] = opcintype;
10448 696 : target_typeids[1] = opcintype;
10449 696 : if (can_coerce_type(2, input_typeids, target_typeids,
10450 : COERCION_IMPLICIT))
10451 : {
10452 468 : pfeqop = ffeqop = ppeqop;
10453 468 : pfeqop_right = opcintype;
10454 : }
10455 : }
10456 :
10457 3066 : if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10458 228 : ereport(ERROR,
10459 : (errcode(ERRCODE_DATATYPE_MISMATCH),
10460 : errmsg("foreign key constraint \"%s\" cannot be implemented",
10461 : fkconstraint->conname),
10462 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10463 : "are of incompatible types: %s and %s.",
10464 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10465 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10466 : format_type_be(fktype),
10467 : format_type_be(pktype))));
10468 :
10469 : /*
10470 : * This shouldn't be possible, but better check to make sure we have a
10471 : * consistent state for the check below.
10472 : */
10473 2838 : if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10474 0 : elog(ERROR, "key columns are not both collatable");
10475 :
10476 2838 : if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10477 : {
10478 : bool pkcolldet;
10479 : bool fkcolldet;
10480 :
10481 104 : pkcolldet = get_collation_isdeterministic(pkcoll);
10482 104 : fkcolldet = get_collation_isdeterministic(fkcoll);
10483 :
10484 : /*
10485 : * SQL requires that both collations are the same. This is
10486 : * because we need a consistent notion of equality on both
10487 : * columns. We relax this by allowing different collations if
10488 : * they are both deterministic. (This is also for backward
10489 : * compatibility, because PostgreSQL has always allowed this.)
10490 : */
10491 104 : if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10492 12 : ereport(ERROR,
10493 : (errcode(ERRCODE_COLLATION_MISMATCH),
10494 : errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10495 : errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10496 : "have incompatible collations: \"%s\" and \"%s\". "
10497 : "If either collation is nondeterministic, then both collations have to be the same.",
10498 : strVal(list_nth(fkconstraint->fk_attrs, i)),
10499 : strVal(list_nth(fkconstraint->pk_attrs, i)),
10500 : get_collation_name(fkcoll),
10501 : get_collation_name(pkcoll))));
10502 : }
10503 :
10504 2826 : if (old_check_ok)
10505 : {
10506 : /*
10507 : * When a pfeqop changes, revalidate the constraint. We could
10508 : * permit intra-opfamily changes, but that adds subtle complexity
10509 : * without any concrete benefit for core types. We need not
10510 : * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10511 : */
10512 6 : old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10513 6 : old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10514 : old_pfeqop_item);
10515 : }
10516 2826 : if (old_check_ok)
10517 : {
10518 : Oid old_fktype;
10519 : Oid new_fktype;
10520 : CoercionPathType old_pathtype;
10521 : CoercionPathType new_pathtype;
10522 : Oid old_castfunc;
10523 : Oid new_castfunc;
10524 : Oid old_fkcoll;
10525 : Oid new_fkcoll;
10526 6 : Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10527 6 : fkattnum[i] - 1);
10528 :
10529 : /*
10530 : * Identify coercion pathways from each of the old and new FK-side
10531 : * column types to the right (foreign) operand type of the pfeqop.
10532 : * We may assume that pg_constraint.conkey is not changing.
10533 : */
10534 6 : old_fktype = attr->atttypid;
10535 6 : new_fktype = fktype;
10536 6 : old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10537 : &old_castfunc);
10538 6 : new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10539 : &new_castfunc);
10540 :
10541 6 : old_fkcoll = attr->attcollation;
10542 6 : new_fkcoll = fkcoll;
10543 :
10544 : /*
10545 : * Upon a change to the cast from the FK column to its pfeqop
10546 : * operand, revalidate the constraint. For this evaluation, a
10547 : * binary coercion cast is equivalent to no cast at all. While
10548 : * type implementors should design implicit casts with an eye
10549 : * toward consistency of operations like equality, we cannot
10550 : * assume here that they have done so.
10551 : *
10552 : * A function with a polymorphic argument could change behavior
10553 : * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10554 : * when the cast destination is polymorphic, we only avoid
10555 : * revalidation if the input type has not changed at all. Given
10556 : * just the core data types and operator classes, this requirement
10557 : * prevents no would-be optimizations.
10558 : *
10559 : * If the cast converts from a base type to a domain thereon, then
10560 : * that domain type must be the opcintype of the unique index.
10561 : * Necessarily, the primary key column must then be of the domain
10562 : * type. Since the constraint was previously valid, all values on
10563 : * the foreign side necessarily exist on the primary side and in
10564 : * turn conform to the domain. Consequently, we need not treat
10565 : * domains specially here.
10566 : *
10567 : * If the collation changes, revalidation is required, unless both
10568 : * collations are deterministic, because those share the same
10569 : * notion of equality (because texteq reduces to bitwise
10570 : * equality).
10571 : *
10572 : * We need not directly consider the PK type. It's necessarily
10573 : * binary coercible to the opcintype of the unique index column,
10574 : * and ri_triggers.c will only deal with PK datums in terms of
10575 : * that opcintype. Changing the opcintype also changes pfeqop.
10576 : */
10577 6 : old_check_ok = (new_pathtype == old_pathtype &&
10578 6 : new_castfunc == old_castfunc &&
10579 6 : (!IsPolymorphicType(pfeqop_right) ||
10580 12 : new_fktype == old_fktype) &&
10581 0 : (new_fkcoll == old_fkcoll ||
10582 0 : (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10583 : }
10584 :
10585 2826 : pfeqoperators[i] = pfeqop;
10586 2826 : ppeqoperators[i] = ppeqop;
10587 2826 : ffeqoperators[i] = ffeqop;
10588 : }
10589 :
10590 : /*
10591 : * For FKs with PERIOD we need additional operators to check whether the
10592 : * referencing row's range is contained by the aggregated ranges of the
10593 : * referenced row(s). For rangetypes and multirangetypes this is
10594 : * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10595 : * support for now. FKs will look these up at "runtime", but we should
10596 : * make sure the lookup works here, even if we don't use the values.
10597 : */
10598 2148 : if (with_period)
10599 : {
10600 : Oid periodoperoid;
10601 : Oid aggedperiodoperoid;
10602 : Oid intersectoperoid;
10603 :
10604 188 : FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10605 : &intersectoperoid);
10606 : }
10607 :
10608 : /* First, create the constraint catalog entry itself. */
10609 2148 : address = addFkConstraint(addFkBothSides,
10610 : fkconstraint->conname, fkconstraint, rel, pkrel,
10611 : indexOid,
10612 : InvalidOid, /* no parent constraint */
10613 : numfks,
10614 : pkattnum,
10615 : fkattnum,
10616 : pfeqoperators,
10617 : ppeqoperators,
10618 : ffeqoperators,
10619 : numfkdelsetcols,
10620 : fkdelsetcols,
10621 : false,
10622 : with_period);
10623 :
10624 : /* Next process the action triggers at the referenced side and recurse */
10625 2148 : addFkRecurseReferenced(fkconstraint, rel, pkrel,
10626 : indexOid,
10627 : address.objectId,
10628 : numfks,
10629 : pkattnum,
10630 : fkattnum,
10631 : pfeqoperators,
10632 : ppeqoperators,
10633 : ffeqoperators,
10634 : numfkdelsetcols,
10635 : fkdelsetcols,
10636 : old_check_ok,
10637 : InvalidOid, InvalidOid,
10638 : with_period);
10639 :
10640 : /* Lastly create the check triggers at the referencing side and recurse */
10641 2148 : addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10642 : indexOid,
10643 : address.objectId,
10644 : numfks,
10645 : pkattnum,
10646 : fkattnum,
10647 : pfeqoperators,
10648 : ppeqoperators,
10649 : ffeqoperators,
10650 : numfkdelsetcols,
10651 : fkdelsetcols,
10652 : old_check_ok,
10653 : lockmode,
10654 : InvalidOid, InvalidOid,
10655 : with_period);
10656 :
10657 : /*
10658 : * Done. Close pk table, but keep lock until we've committed.
10659 : */
10660 2148 : table_close(pkrel, NoLock);
10661 :
10662 2148 : return address;
10663 : }
10664 :
10665 : /*
10666 : * validateFkOnDeleteSetColumns
10667 : * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10668 : * column lists are valid.
10669 : *
10670 : * If there are duplicates in the fksetcolsattnums[] array, this silently
10671 : * removes the dups. The new count of numfksetcols is returned.
10672 : */
10673 : static int
10674 2622 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10675 : int numfksetcols, int16 *fksetcolsattnums,
10676 : List *fksetcols)
10677 : {
10678 2622 : int numcolsout = 0;
10679 :
10680 2652 : for (int i = 0; i < numfksetcols; i++)
10681 : {
10682 36 : int16 setcol_attnum = fksetcolsattnums[i];
10683 36 : bool seen = false;
10684 :
10685 : /* Make sure it's in fkattnums[] */
10686 66 : for (int j = 0; j < numfks; j++)
10687 : {
10688 60 : if (fkattnums[j] == setcol_attnum)
10689 : {
10690 30 : seen = true;
10691 30 : break;
10692 : }
10693 : }
10694 :
10695 36 : if (!seen)
10696 : {
10697 6 : char *col = strVal(list_nth(fksetcols, i));
10698 :
10699 6 : ereport(ERROR,
10700 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10701 : errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10702 : }
10703 :
10704 : /* Now check for dups */
10705 30 : seen = false;
10706 30 : for (int j = 0; j < numcolsout; j++)
10707 : {
10708 6 : if (fksetcolsattnums[j] == setcol_attnum)
10709 : {
10710 6 : seen = true;
10711 6 : break;
10712 : }
10713 : }
10714 30 : if (!seen)
10715 24 : fksetcolsattnums[numcolsout++] = setcol_attnum;
10716 : }
10717 2616 : return numcolsout;
10718 : }
10719 :
10720 : /*
10721 : * addFkConstraint
10722 : * Install pg_constraint entries to implement a foreign key constraint.
10723 : * Caller must separately invoke addFkRecurseReferenced and
10724 : * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10725 : * and (for partitioned tables) recurse to partitions.
10726 : *
10727 : * fkside: the side of the FK (or both) to create. Caller should
10728 : * call addFkRecurseReferenced if this is addFkReferencedSide,
10729 : * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10730 : * addFkBothSides.
10731 : * constraintname: the base name for the constraint being added,
10732 : * copied to fkconstraint->conname if the latter is not set
10733 : * fkconstraint: the constraint being added
10734 : * rel: the root referencing relation
10735 : * pkrel: the referenced relation; might be a partition, if recursing
10736 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10737 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10738 : * top-level constraint
10739 : * numfks: the number of columns in the foreign key
10740 : * pkattnum: the attnum array of referenced attributes
10741 : * fkattnum: the attnum array of referencing attributes
10742 : * pf/pp/ffeqoperators: OID array of operators between columns
10743 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10744 : * (...) clause
10745 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10746 : * NULL/DEFAULT clause
10747 : * with_period: true if this is a temporal FK
10748 : */
10749 : static ObjectAddress
10750 4226 : addFkConstraint(addFkConstraintSides fkside,
10751 : char *constraintname, Constraint *fkconstraint,
10752 : Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10753 : int numfks, int16 *pkattnum,
10754 : int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10755 : Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10756 : bool is_internal, bool with_period)
10757 : {
10758 : ObjectAddress address;
10759 : Oid constrOid;
10760 : char *conname;
10761 : bool conislocal;
10762 : int16 coninhcount;
10763 : bool connoinherit;
10764 :
10765 : /*
10766 : * Verify relkind for each referenced partition. At the top level, this
10767 : * is redundant with a previous check, but we need it when recursing.
10768 : */
10769 4226 : if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10770 886 : pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10771 0 : ereport(ERROR,
10772 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10773 : errmsg("referenced relation \"%s\" is not a table",
10774 : RelationGetRelationName(pkrel))));
10775 :
10776 : /*
10777 : * Caller supplies us with a constraint name; however, it may be used in
10778 : * this partition, so come up with a different one in that case. Unless
10779 : * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10780 : * supplied name with an underscore and digit(s) appended.
10781 : */
10782 4226 : if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10783 : RelationGetRelid(rel),
10784 : constraintname))
10785 1242 : conname = ChooseConstraintName(constraintname,
10786 : NULL,
10787 : "",
10788 1242 : RelationGetNamespace(rel), NIL);
10789 : else
10790 2984 : conname = constraintname;
10791 :
10792 4226 : if (fkconstraint->conname == NULL)
10793 472 : fkconstraint->conname = pstrdup(conname);
10794 :
10795 4226 : if (OidIsValid(parentConstr))
10796 : {
10797 2078 : conislocal = false;
10798 2078 : coninhcount = 1;
10799 2078 : connoinherit = false;
10800 : }
10801 : else
10802 : {
10803 2148 : conislocal = true;
10804 2148 : coninhcount = 0;
10805 :
10806 : /*
10807 : * always inherit for partitioned tables, never for legacy inheritance
10808 : */
10809 2148 : connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10810 : }
10811 :
10812 : /*
10813 : * Record the FK constraint in pg_constraint.
10814 : */
10815 4226 : constrOid = CreateConstraintEntry(conname,
10816 4226 : RelationGetNamespace(rel),
10817 : CONSTRAINT_FOREIGN,
10818 4226 : fkconstraint->deferrable,
10819 4226 : fkconstraint->initdeferred,
10820 4226 : fkconstraint->is_enforced,
10821 4226 : fkconstraint->initially_valid,
10822 : parentConstr,
10823 : RelationGetRelid(rel),
10824 : fkattnum,
10825 : numfks,
10826 : numfks,
10827 : InvalidOid, /* not a domain constraint */
10828 : indexOid,
10829 : RelationGetRelid(pkrel),
10830 : pkattnum,
10831 : pfeqoperators,
10832 : ppeqoperators,
10833 : ffeqoperators,
10834 : numfks,
10835 4226 : fkconstraint->fk_upd_action,
10836 4226 : fkconstraint->fk_del_action,
10837 : fkdelsetcols,
10838 : numfkdelsetcols,
10839 4226 : fkconstraint->fk_matchtype,
10840 : NULL, /* no exclusion constraint */
10841 : NULL, /* no check constraint */
10842 : NULL,
10843 : conislocal, /* islocal */
10844 : coninhcount, /* inhcount */
10845 : connoinherit, /* conNoInherit */
10846 : with_period, /* conPeriod */
10847 : is_internal); /* is_internal */
10848 :
10849 4226 : ObjectAddressSet(address, ConstraintRelationId, constrOid);
10850 :
10851 : /*
10852 : * In partitioning cases, create the dependency entries for this
10853 : * constraint. (For non-partitioned cases, relevant entries were created
10854 : * by CreateConstraintEntry.)
10855 : *
10856 : * On the referenced side, we need the constraint to have an internal
10857 : * dependency on its parent constraint; this means that this constraint
10858 : * cannot be dropped on its own -- only through the parent constraint. It
10859 : * also means the containing partition cannot be dropped on its own, but
10860 : * it can be detached, at which point this dependency is removed (after
10861 : * verifying that no rows are referenced via this FK.)
10862 : *
10863 : * When processing the referencing side, we link the constraint via the
10864 : * special partitioning dependencies: the parent constraint is the primary
10865 : * dependent, and the partition on which the foreign key exists is the
10866 : * secondary dependency. That way, this constraint is dropped if either
10867 : * of these objects is.
10868 : *
10869 : * Note that this is only necessary for the subsidiary pg_constraint rows
10870 : * in partitions; the topmost row doesn't need any of this.
10871 : */
10872 4226 : if (OidIsValid(parentConstr))
10873 : {
10874 : ObjectAddress referenced;
10875 :
10876 2078 : ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10877 :
10878 : Assert(fkside != addFkBothSides);
10879 2078 : if (fkside == addFkReferencedSide)
10880 1236 : recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10881 : else
10882 : {
10883 842 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10884 842 : ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10885 842 : recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10886 : }
10887 : }
10888 :
10889 : /* make new constraint visible, in case we add more */
10890 4226 : CommandCounterIncrement();
10891 :
10892 4226 : return address;
10893 : }
10894 :
10895 : /*
10896 : * addFkRecurseReferenced
10897 : * Recursive helper for the referenced side of foreign key creation,
10898 : * which creates the action triggers and recurses
10899 : *
10900 : * If the referenced relation is a plain relation, create the necessary action
10901 : * triggers that implement the constraint. If the referenced relation is a
10902 : * partitioned table, then we create a pg_constraint row referencing the parent
10903 : * of the referencing side for it and recurse on this routine for each
10904 : * partition.
10905 : *
10906 : * fkconstraint: the constraint being added
10907 : * rel: the root referencing relation
10908 : * pkrel: the referenced relation; might be a partition, if recursing
10909 : * indexOid: the OID of the index (on pkrel) implementing this constraint
10910 : * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10911 : * top-level constraint
10912 : * numfks: the number of columns in the foreign key
10913 : * pkattnum: the attnum array of referenced attributes
10914 : * fkattnum: the attnum array of referencing attributes
10915 : * numfkdelsetcols: the number of columns in the ON DELETE SET
10916 : * NULL/DEFAULT (...) clause
10917 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10918 : * NULL/DEFAULT clause
10919 : * pf/pp/ffeqoperators: OID array of operators between columns
10920 : * old_check_ok: true if this constraint replaces an existing one that
10921 : * was already validated (thus this one doesn't need validation)
10922 : * parentDelTrigger and parentUpdTrigger: when recursively called on a
10923 : * partition, the OIDs of the parent action triggers for DELETE and
10924 : * UPDATE respectively.
10925 : * with_period: true if this is a temporal FK
10926 : */
10927 : static void
10928 3492 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10929 : Relation pkrel, Oid indexOid, Oid parentConstr,
10930 : int numfks,
10931 : int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10932 : Oid *ppeqoperators, Oid *ffeqoperators,
10933 : int numfkdelsetcols, int16 *fkdelsetcols,
10934 : bool old_check_ok,
10935 : Oid parentDelTrigger, Oid parentUpdTrigger,
10936 : bool with_period)
10937 : {
10938 3492 : Oid deleteTriggerOid = InvalidOid,
10939 3492 : updateTriggerOid = InvalidOid;
10940 :
10941 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10942 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10943 :
10944 : /*
10945 : * Create action triggers to enforce the constraint, or skip them if the
10946 : * constraint is NOT ENFORCED.
10947 : */
10948 3492 : if (fkconstraint->is_enforced)
10949 3420 : createForeignKeyActionTriggers(RelationGetRelid(rel),
10950 : RelationGetRelid(pkrel),
10951 : fkconstraint,
10952 : parentConstr, indexOid,
10953 : parentDelTrigger, parentUpdTrigger,
10954 : &deleteTriggerOid, &updateTriggerOid);
10955 :
10956 : /*
10957 : * If the referenced table is partitioned, recurse on ourselves to handle
10958 : * each partition. We need one pg_constraint row created for each
10959 : * partition in addition to the pg_constraint row for the parent table.
10960 : */
10961 3492 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10962 : {
10963 576 : PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10964 :
10965 1548 : for (int i = 0; i < pd->nparts; i++)
10966 : {
10967 : Relation partRel;
10968 : AttrMap *map;
10969 : AttrNumber *mapped_pkattnum;
10970 : Oid partIndexId;
10971 : ObjectAddress address;
10972 :
10973 : /* XXX would it be better to acquire these locks beforehand? */
10974 972 : partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10975 :
10976 : /*
10977 : * Map the attribute numbers in the referenced side of the FK
10978 : * definition to match the partition's column layout.
10979 : */
10980 972 : map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10981 : RelationGetDescr(pkrel),
10982 : false);
10983 972 : if (map)
10984 : {
10985 136 : mapped_pkattnum = palloc_array(AttrNumber, numfks);
10986 284 : for (int j = 0; j < numfks; j++)
10987 148 : mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10988 : }
10989 : else
10990 836 : mapped_pkattnum = pkattnum;
10991 :
10992 : /* Determine the index to use at this level */
10993 972 : partIndexId = index_get_partition(partRel, indexOid);
10994 972 : if (!OidIsValid(partIndexId))
10995 0 : elog(ERROR, "index for %u not found in partition %s",
10996 : indexOid, RelationGetRelationName(partRel));
10997 :
10998 : /* Create entry at this level ... */
10999 972 : address = addFkConstraint(addFkReferencedSide,
11000 : fkconstraint->conname, fkconstraint, rel,
11001 : partRel, partIndexId, parentConstr,
11002 : numfks, mapped_pkattnum,
11003 : fkattnum, pfeqoperators, ppeqoperators,
11004 : ffeqoperators, numfkdelsetcols,
11005 : fkdelsetcols, true, with_period);
11006 : /* ... and recurse to our children */
11007 972 : addFkRecurseReferenced(fkconstraint, rel, partRel,
11008 : partIndexId, address.objectId, numfks,
11009 : mapped_pkattnum, fkattnum,
11010 : pfeqoperators, ppeqoperators, ffeqoperators,
11011 : numfkdelsetcols, fkdelsetcols,
11012 : old_check_ok,
11013 : deleteTriggerOid, updateTriggerOid,
11014 : with_period);
11015 :
11016 : /* Done -- clean up (but keep the lock) */
11017 972 : table_close(partRel, NoLock);
11018 972 : if (map)
11019 : {
11020 136 : pfree(mapped_pkattnum);
11021 136 : free_attrmap(map);
11022 : }
11023 : }
11024 : }
11025 3492 : }
11026 :
11027 : /*
11028 : * addFkRecurseReferencing
11029 : * Recursive helper for the referencing side of foreign key creation,
11030 : * which creates the check triggers and recurses
11031 : *
11032 : * If the referencing relation is a plain relation, create the necessary check
11033 : * triggers that implement the constraint, and set up for Phase 3 constraint
11034 : * verification. If the referencing relation is a partitioned table, then
11035 : * we create a pg_constraint row for it and recurse on this routine for each
11036 : * partition.
11037 : *
11038 : * We assume that the referenced relation is locked against concurrent
11039 : * deletions. If it's a partitioned relation, every partition must be so
11040 : * locked.
11041 : *
11042 : * wqueue: the ALTER TABLE work queue; NULL when not running as part
11043 : * of an ALTER TABLE sequence.
11044 : * fkconstraint: the constraint being added
11045 : * rel: the referencing relation; might be a partition, if recursing
11046 : * pkrel: the root referenced relation
11047 : * indexOid: the OID of the index (on pkrel) implementing this constraint
11048 : * parentConstr: the OID of the parent constraint (there is always one)
11049 : * numfks: the number of columns in the foreign key
11050 : * pkattnum: the attnum array of referenced attributes
11051 : * fkattnum: the attnum array of referencing attributes
11052 : * pf/pp/ffeqoperators: OID array of operators between columns
11053 : * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
11054 : * (...) clause
11055 : * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
11056 : * NULL/DEFAULT clause
11057 : * old_check_ok: true if this constraint replaces an existing one that
11058 : * was already validated (thus this one doesn't need validation)
11059 : * lockmode: the lockmode to acquire on partitions when recursing
11060 : * parentInsTrigger and parentUpdTrigger: when being recursively called on
11061 : * a partition, the OIDs of the parent check triggers for INSERT and
11062 : * UPDATE respectively.
11063 : * with_period: true if this is a temporal FK
11064 : */
11065 : static void
11066 2990 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
11067 : Relation pkrel, Oid indexOid, Oid parentConstr,
11068 : int numfks, int16 *pkattnum, int16 *fkattnum,
11069 : Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11070 : int numfkdelsetcols, int16 *fkdelsetcols,
11071 : bool old_check_ok, LOCKMODE lockmode,
11072 : Oid parentInsTrigger, Oid parentUpdTrigger,
11073 : bool with_period)
11074 : {
11075 2990 : Oid insertTriggerOid = InvalidOid,
11076 2990 : updateTriggerOid = InvalidOid;
11077 :
11078 : Assert(OidIsValid(parentConstr));
11079 : Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
11080 : Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
11081 :
11082 2990 : if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11083 0 : ereport(ERROR,
11084 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11085 : errmsg("foreign key constraints are not supported on foreign tables")));
11086 :
11087 : /*
11088 : * Add check triggers if the constraint is ENFORCED, and if needed,
11089 : * schedule them to be checked in Phase 3.
11090 : *
11091 : * If the relation is partitioned, drill down to do it to its partitions.
11092 : */
11093 2990 : if (fkconstraint->is_enforced)
11094 2942 : createForeignKeyCheckTriggers(RelationGetRelid(rel),
11095 : RelationGetRelid(pkrel),
11096 : fkconstraint,
11097 : parentConstr,
11098 : indexOid,
11099 : parentInsTrigger, parentUpdTrigger,
11100 : &insertTriggerOid, &updateTriggerOid);
11101 :
11102 2990 : if (rel->rd_rel->relkind == RELKIND_RELATION)
11103 : {
11104 : /*
11105 : * Tell Phase 3 to check that the constraint is satisfied by existing
11106 : * rows. We can skip this during table creation, when constraint is
11107 : * specified as NOT ENFORCED, or when requested explicitly by
11108 : * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11109 : * recreating a constraint following a SET DATA TYPE operation that
11110 : * did not impugn its validity.
11111 : */
11112 2500 : if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11113 772 : fkconstraint->is_enforced)
11114 : {
11115 : NewConstraint *newcon;
11116 : AlteredTableInfo *tab;
11117 :
11118 772 : tab = ATGetQueueEntry(wqueue, rel);
11119 :
11120 772 : newcon = palloc0_object(NewConstraint);
11121 772 : newcon->name = get_constraint_name(parentConstr);
11122 772 : newcon->contype = CONSTR_FOREIGN;
11123 772 : newcon->refrelid = RelationGetRelid(pkrel);
11124 772 : newcon->refindid = indexOid;
11125 772 : newcon->conid = parentConstr;
11126 772 : newcon->conwithperiod = fkconstraint->fk_with_period;
11127 772 : newcon->qual = (Node *) fkconstraint;
11128 :
11129 772 : tab->constraints = lappend(tab->constraints, newcon);
11130 : }
11131 : }
11132 490 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11133 : {
11134 490 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
11135 : Relation trigrel;
11136 :
11137 : /*
11138 : * Triggers of the foreign keys will be manipulated a bunch of times
11139 : * in the loop below. To avoid repeatedly opening/closing the trigger
11140 : * catalog relation, we open it here and pass it to the subroutines
11141 : * called below.
11142 : */
11143 490 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11144 :
11145 : /*
11146 : * Recurse to take appropriate action on each partition; either we
11147 : * find an existing constraint to reparent to ours, or we create a new
11148 : * one.
11149 : */
11150 872 : for (int i = 0; i < pd->nparts; i++)
11151 : {
11152 388 : Relation partition = table_open(pd->oids[i], lockmode);
11153 : List *partFKs;
11154 : AttrMap *attmap;
11155 : AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11156 : bool attached;
11157 : ObjectAddress address;
11158 :
11159 388 : CheckAlterTableIsSafe(partition);
11160 :
11161 382 : attmap = build_attrmap_by_name(RelationGetDescr(partition),
11162 : RelationGetDescr(rel),
11163 : false);
11164 986 : for (int j = 0; j < numfks; j++)
11165 604 : mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11166 :
11167 : /* Check whether an existing constraint can be repurposed */
11168 382 : partFKs = copyObject(RelationGetFKeyList(partition));
11169 382 : attached = false;
11170 782 : foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11171 : {
11172 30 : if (tryAttachPartitionForeignKey(wqueue,
11173 : fk,
11174 : partition,
11175 : parentConstr,
11176 : numfks,
11177 : mapped_fkattnum,
11178 : pkattnum,
11179 : pfeqoperators,
11180 : insertTriggerOid,
11181 : updateTriggerOid,
11182 : trigrel))
11183 : {
11184 12 : attached = true;
11185 12 : break;
11186 : }
11187 : }
11188 382 : if (attached)
11189 : {
11190 12 : table_close(partition, NoLock);
11191 12 : continue;
11192 : }
11193 :
11194 : /*
11195 : * No luck finding a good constraint to reuse; create our own.
11196 : */
11197 370 : address = addFkConstraint(addFkReferencingSide,
11198 : fkconstraint->conname, fkconstraint,
11199 : partition, pkrel, indexOid, parentConstr,
11200 : numfks, pkattnum,
11201 : mapped_fkattnum, pfeqoperators,
11202 : ppeqoperators, ffeqoperators,
11203 : numfkdelsetcols, fkdelsetcols, true,
11204 : with_period);
11205 :
11206 : /* call ourselves to finalize the creation and we're done */
11207 370 : addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11208 : indexOid,
11209 : address.objectId,
11210 : numfks,
11211 : pkattnum,
11212 : mapped_fkattnum,
11213 : pfeqoperators,
11214 : ppeqoperators,
11215 : ffeqoperators,
11216 : numfkdelsetcols,
11217 : fkdelsetcols,
11218 : old_check_ok,
11219 : lockmode,
11220 : insertTriggerOid,
11221 : updateTriggerOid,
11222 : with_period);
11223 :
11224 370 : table_close(partition, NoLock);
11225 : }
11226 :
11227 484 : table_close(trigrel, RowExclusiveLock);
11228 : }
11229 2984 : }
11230 :
11231 : /*
11232 : * CloneForeignKeyConstraints
11233 : * Clone foreign keys from a partitioned table to a newly acquired
11234 : * partition.
11235 : *
11236 : * partitionRel is a partition of parentRel, so we can be certain that it has
11237 : * the same columns with the same datatypes. The columns may be in different
11238 : * order, though.
11239 : *
11240 : * wqueue must be passed to set up phase 3 constraint checking, unless the
11241 : * referencing-side partition is known to be empty (such as in CREATE TABLE /
11242 : * PARTITION OF).
11243 : */
11244 : static void
11245 11548 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
11246 : Relation partitionRel)
11247 : {
11248 : /* This only works for declarative partitioning */
11249 : Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11250 :
11251 : /*
11252 : * First, clone constraints where the parent is on the referencing side.
11253 : */
11254 11548 : CloneFkReferencing(wqueue, parentRel, partitionRel);
11255 :
11256 : /*
11257 : * Clone constraints for which the parent is on the referenced side.
11258 : */
11259 11530 : CloneFkReferenced(parentRel, partitionRel);
11260 11530 : }
11261 :
11262 : /*
11263 : * CloneFkReferenced
11264 : * Subroutine for CloneForeignKeyConstraints
11265 : *
11266 : * Find all the FKs that have the parent relation on the referenced side;
11267 : * clone those constraints to the given partition. This is to be called
11268 : * when the partition is being created or attached.
11269 : *
11270 : * This recurses to partitions, if the relation being attached is partitioned.
11271 : * Recursion is done by calling addFkRecurseReferenced.
11272 : */
11273 : static void
11274 11530 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
11275 : {
11276 : Relation pg_constraint;
11277 : AttrMap *attmap;
11278 : ListCell *cell;
11279 : SysScanDesc scan;
11280 : ScanKeyData key[2];
11281 : HeapTuple tuple;
11282 11530 : List *clone = NIL;
11283 : Relation trigrel;
11284 :
11285 : /*
11286 : * Search for any constraints where this partition's parent is in the
11287 : * referenced side. However, we must not clone any constraint whose
11288 : * parent constraint is also going to be cloned, to avoid duplicates. So
11289 : * do it in two steps: first construct the list of constraints to clone,
11290 : * then go over that list cloning those whose parents are not in the list.
11291 : * (We must not rely on the parent being seen first, since the catalog
11292 : * scan could return children first.)
11293 : */
11294 11530 : pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11295 11530 : ScanKeyInit(&key[0],
11296 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11297 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11298 11530 : ScanKeyInit(&key[1],
11299 : Anum_pg_constraint_contype, BTEqualStrategyNumber,
11300 : F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11301 : /* This is a seqscan, as we don't have a usable index ... */
11302 11530 : scan = systable_beginscan(pg_constraint, InvalidOid, true,
11303 : NULL, 2, key);
11304 12016 : while ((tuple = systable_getnext(scan)) != NULL)
11305 : {
11306 486 : Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11307 :
11308 486 : clone = lappend_oid(clone, constrForm->oid);
11309 : }
11310 11530 : systable_endscan(scan);
11311 11530 : table_close(pg_constraint, RowShareLock);
11312 :
11313 : /*
11314 : * Triggers of the foreign keys will be manipulated a bunch of times in
11315 : * the loop below. To avoid repeatedly opening/closing the trigger
11316 : * catalog relation, we open it here and pass it to the subroutines called
11317 : * below.
11318 : */
11319 11530 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11320 :
11321 11530 : attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11322 : RelationGetDescr(parentRel),
11323 : false);
11324 12016 : foreach(cell, clone)
11325 : {
11326 486 : Oid constrOid = lfirst_oid(cell);
11327 : Form_pg_constraint constrForm;
11328 : Relation fkRel;
11329 : Oid indexOid;
11330 : Oid partIndexId;
11331 : int numfks;
11332 : AttrNumber conkey[INDEX_MAX_KEYS];
11333 : AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11334 : AttrNumber confkey[INDEX_MAX_KEYS];
11335 : Oid conpfeqop[INDEX_MAX_KEYS];
11336 : Oid conppeqop[INDEX_MAX_KEYS];
11337 : Oid conffeqop[INDEX_MAX_KEYS];
11338 : int numfkdelsetcols;
11339 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11340 : Constraint *fkconstraint;
11341 : ObjectAddress address;
11342 486 : Oid deleteTriggerOid = InvalidOid,
11343 486 : updateTriggerOid = InvalidOid;
11344 :
11345 486 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11346 486 : if (!HeapTupleIsValid(tuple))
11347 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11348 486 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11349 :
11350 : /*
11351 : * As explained above: don't try to clone a constraint for which we're
11352 : * going to clone the parent.
11353 : */
11354 486 : if (list_member_oid(clone, constrForm->conparentid))
11355 : {
11356 222 : ReleaseSysCache(tuple);
11357 222 : continue;
11358 : }
11359 :
11360 : /* We need the same lock level that CreateTrigger will acquire */
11361 264 : fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11362 :
11363 264 : indexOid = constrForm->conindid;
11364 264 : DeconstructFkConstraintRow(tuple,
11365 : &numfks,
11366 : conkey,
11367 : confkey,
11368 : conpfeqop,
11369 : conppeqop,
11370 : conffeqop,
11371 : &numfkdelsetcols,
11372 : confdelsetcols);
11373 :
11374 570 : for (int i = 0; i < numfks; i++)
11375 306 : mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11376 :
11377 264 : fkconstraint = makeNode(Constraint);
11378 264 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11379 264 : fkconstraint->conname = NameStr(constrForm->conname);
11380 264 : fkconstraint->deferrable = constrForm->condeferrable;
11381 264 : fkconstraint->initdeferred = constrForm->condeferred;
11382 264 : fkconstraint->location = -1;
11383 264 : fkconstraint->pktable = NULL;
11384 : /* ->fk_attrs determined below */
11385 264 : fkconstraint->pk_attrs = NIL;
11386 264 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11387 264 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11388 264 : fkconstraint->fk_del_action = constrForm->confdeltype;
11389 264 : fkconstraint->fk_del_set_cols = NIL;
11390 264 : fkconstraint->old_conpfeqop = NIL;
11391 264 : fkconstraint->old_pktable_oid = InvalidOid;
11392 264 : fkconstraint->is_enforced = constrForm->conenforced;
11393 264 : fkconstraint->skip_validation = false;
11394 264 : fkconstraint->initially_valid = constrForm->convalidated;
11395 :
11396 : /* set up colnames that are used to generate the constraint name */
11397 570 : for (int i = 0; i < numfks; i++)
11398 : {
11399 : Form_pg_attribute att;
11400 :
11401 306 : att = TupleDescAttr(RelationGetDescr(fkRel),
11402 306 : conkey[i] - 1);
11403 306 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11404 306 : makeString(NameStr(att->attname)));
11405 : }
11406 :
11407 : /*
11408 : * Add the new foreign key constraint pointing to the new partition.
11409 : * Because this new partition appears in the referenced side of the
11410 : * constraint, we don't need to set up for Phase 3 check.
11411 : */
11412 264 : partIndexId = index_get_partition(partitionRel, indexOid);
11413 264 : if (!OidIsValid(partIndexId))
11414 0 : elog(ERROR, "index for %u not found in partition %s",
11415 : indexOid, RelationGetRelationName(partitionRel));
11416 :
11417 : /*
11418 : * Get the "action" triggers belonging to the constraint to pass as
11419 : * parent OIDs for similar triggers that will be created on the
11420 : * partition in addFkRecurseReferenced().
11421 : */
11422 264 : if (constrForm->conenforced)
11423 258 : GetForeignKeyActionTriggers(trigrel, constrOid,
11424 : constrForm->confrelid, constrForm->conrelid,
11425 : &deleteTriggerOid, &updateTriggerOid);
11426 :
11427 : /* Add this constraint ... */
11428 264 : address = addFkConstraint(addFkReferencedSide,
11429 : fkconstraint->conname, fkconstraint, fkRel,
11430 : partitionRel, partIndexId, constrOid,
11431 : numfks, mapped_confkey,
11432 : conkey, conpfeqop, conppeqop, conffeqop,
11433 : numfkdelsetcols, confdelsetcols, false,
11434 264 : constrForm->conperiod);
11435 : /* ... and recurse */
11436 264 : addFkRecurseReferenced(fkconstraint,
11437 : fkRel,
11438 : partitionRel,
11439 : partIndexId,
11440 : address.objectId,
11441 : numfks,
11442 : mapped_confkey,
11443 : conkey,
11444 : conpfeqop,
11445 : conppeqop,
11446 : conffeqop,
11447 : numfkdelsetcols,
11448 : confdelsetcols,
11449 : true,
11450 : deleteTriggerOid,
11451 : updateTriggerOid,
11452 264 : constrForm->conperiod);
11453 :
11454 264 : table_close(fkRel, NoLock);
11455 264 : ReleaseSysCache(tuple);
11456 : }
11457 :
11458 11530 : table_close(trigrel, RowExclusiveLock);
11459 11530 : }
11460 :
11461 : /*
11462 : * CloneFkReferencing
11463 : * Subroutine for CloneForeignKeyConstraints
11464 : *
11465 : * For each FK constraint of the parent relation in the given list, find an
11466 : * equivalent constraint in its partition relation that can be reparented;
11467 : * if one cannot be found, create a new constraint in the partition as its
11468 : * child.
11469 : *
11470 : * If wqueue is given, it is used to set up phase-3 verification for each
11471 : * cloned constraint; omit it if such verification is not needed
11472 : * (example: the partition is being created anew).
11473 : */
11474 : static void
11475 11548 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11476 : {
11477 : AttrMap *attmap;
11478 : List *partFKs;
11479 11548 : List *clone = NIL;
11480 : ListCell *cell;
11481 : Relation trigrel;
11482 :
11483 : /* obtain a list of constraints that we need to clone */
11484 12900 : foreach(cell, RelationGetFKeyList(parentRel))
11485 : {
11486 1358 : ForeignKeyCacheInfo *fk = lfirst(cell);
11487 :
11488 : /*
11489 : * Refuse to attach a table as partition that this partitioned table
11490 : * already has a foreign key to. This isn't useful schema, which is
11491 : * proven by the fact that there have been no user complaints that
11492 : * it's already impossible to achieve this in the opposite direction,
11493 : * i.e., creating a foreign key that references a partition. This
11494 : * restriction allows us to dodge some complexities around
11495 : * pg_constraint and pg_trigger row creations that would be needed
11496 : * during ATTACH/DETACH for this kind of relationship.
11497 : */
11498 1358 : if (fk->confrelid == RelationGetRelid(partRel))
11499 6 : ereport(ERROR,
11500 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11501 : errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11502 : RelationGetRelationName(partRel),
11503 : get_constraint_name(fk->conoid))));
11504 :
11505 1352 : clone = lappend_oid(clone, fk->conoid);
11506 : }
11507 :
11508 : /*
11509 : * Silently do nothing if there's nothing to do. In particular, this
11510 : * avoids throwing a spurious error for foreign tables.
11511 : */
11512 11542 : if (clone == NIL)
11513 10950 : return;
11514 :
11515 592 : if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11516 0 : ereport(ERROR,
11517 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11518 : errmsg("foreign key constraints are not supported on foreign tables")));
11519 :
11520 : /*
11521 : * Triggers of the foreign keys will be manipulated a bunch of times in
11522 : * the loop below. To avoid repeatedly opening/closing the trigger
11523 : * catalog relation, we open it here and pass it to the subroutines called
11524 : * below.
11525 : */
11526 592 : trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11527 :
11528 : /*
11529 : * The constraint key may differ, if the columns in the partition are
11530 : * different. This map is used to convert them.
11531 : */
11532 592 : attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11533 : RelationGetDescr(parentRel),
11534 : false);
11535 :
11536 592 : partFKs = copyObject(RelationGetFKeyList(partRel));
11537 :
11538 1932 : foreach(cell, clone)
11539 : {
11540 1352 : Oid parentConstrOid = lfirst_oid(cell);
11541 : Form_pg_constraint constrForm;
11542 : Relation pkrel;
11543 : HeapTuple tuple;
11544 : int numfks;
11545 : AttrNumber conkey[INDEX_MAX_KEYS];
11546 : AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11547 : AttrNumber confkey[INDEX_MAX_KEYS];
11548 : Oid conpfeqop[INDEX_MAX_KEYS];
11549 : Oid conppeqop[INDEX_MAX_KEYS];
11550 : Oid conffeqop[INDEX_MAX_KEYS];
11551 : int numfkdelsetcols;
11552 : AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11553 : Constraint *fkconstraint;
11554 : bool attached;
11555 : Oid indexOid;
11556 : ObjectAddress address;
11557 : ListCell *lc;
11558 1352 : Oid insertTriggerOid = InvalidOid,
11559 1352 : updateTriggerOid = InvalidOid;
11560 : bool with_period;
11561 :
11562 1352 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11563 1352 : if (!HeapTupleIsValid(tuple))
11564 0 : elog(ERROR, "cache lookup failed for constraint %u",
11565 : parentConstrOid);
11566 1352 : constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11567 :
11568 : /* Don't clone constraints whose parents are being cloned */
11569 1352 : if (list_member_oid(clone, constrForm->conparentid))
11570 : {
11571 724 : ReleaseSysCache(tuple);
11572 874 : continue;
11573 : }
11574 :
11575 : /*
11576 : * Need to prevent concurrent deletions. If pkrel is a partitioned
11577 : * relation, that means to lock all partitions.
11578 : */
11579 628 : pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11580 628 : if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11581 250 : (void) find_all_inheritors(RelationGetRelid(pkrel),
11582 : ShareRowExclusiveLock, NULL);
11583 :
11584 628 : DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11585 : conpfeqop, conppeqop, conffeqop,
11586 : &numfkdelsetcols, confdelsetcols);
11587 1490 : for (int i = 0; i < numfks; i++)
11588 862 : mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11589 :
11590 : /*
11591 : * Get the "check" triggers belonging to the constraint, if it is
11592 : * ENFORCED, to pass as parent OIDs for similar triggers that will be
11593 : * created on the partition in addFkRecurseReferencing(). They are
11594 : * also passed to tryAttachPartitionForeignKey() below to simply
11595 : * assign as parents to the partition's existing "check" triggers,
11596 : * that is, if the corresponding constraints is deemed attachable to
11597 : * the parent constraint.
11598 : */
11599 628 : if (constrForm->conenforced)
11600 616 : GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11601 : constrForm->confrelid, constrForm->conrelid,
11602 : &insertTriggerOid, &updateTriggerOid);
11603 :
11604 : /*
11605 : * Before creating a new constraint, see whether any existing FKs are
11606 : * fit for the purpose. If one is, attach the parent constraint to
11607 : * it, and don't clone anything. This way we avoid the expensive
11608 : * verification step and don't end up with a duplicate FK, and we
11609 : * don't need to recurse to partitions for this constraint.
11610 : */
11611 628 : attached = false;
11612 718 : foreach(lc, partFKs)
11613 : {
11614 246 : ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11615 :
11616 246 : if (tryAttachPartitionForeignKey(wqueue,
11617 : fk,
11618 : partRel,
11619 : parentConstrOid,
11620 : numfks,
11621 : mapped_conkey,
11622 : confkey,
11623 : conpfeqop,
11624 : insertTriggerOid,
11625 : updateTriggerOid,
11626 : trigrel))
11627 : {
11628 150 : attached = true;
11629 150 : table_close(pkrel, NoLock);
11630 150 : break;
11631 : }
11632 : }
11633 622 : if (attached)
11634 : {
11635 150 : ReleaseSysCache(tuple);
11636 150 : continue;
11637 : }
11638 :
11639 : /* No dice. Set up to create our own constraint */
11640 472 : fkconstraint = makeNode(Constraint);
11641 472 : fkconstraint->contype = CONSTRAINT_FOREIGN;
11642 : /* ->conname determined below */
11643 472 : fkconstraint->deferrable = constrForm->condeferrable;
11644 472 : fkconstraint->initdeferred = constrForm->condeferred;
11645 472 : fkconstraint->location = -1;
11646 472 : fkconstraint->pktable = NULL;
11647 : /* ->fk_attrs determined below */
11648 472 : fkconstraint->pk_attrs = NIL;
11649 472 : fkconstraint->fk_matchtype = constrForm->confmatchtype;
11650 472 : fkconstraint->fk_upd_action = constrForm->confupdtype;
11651 472 : fkconstraint->fk_del_action = constrForm->confdeltype;
11652 472 : fkconstraint->fk_del_set_cols = NIL;
11653 472 : fkconstraint->old_conpfeqop = NIL;
11654 472 : fkconstraint->old_pktable_oid = InvalidOid;
11655 472 : fkconstraint->is_enforced = constrForm->conenforced;
11656 472 : fkconstraint->skip_validation = false;
11657 472 : fkconstraint->initially_valid = constrForm->convalidated;
11658 1064 : for (int i = 0; i < numfks; i++)
11659 : {
11660 : Form_pg_attribute att;
11661 :
11662 592 : att = TupleDescAttr(RelationGetDescr(partRel),
11663 592 : mapped_conkey[i] - 1);
11664 592 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11665 592 : makeString(NameStr(att->attname)));
11666 : }
11667 :
11668 472 : indexOid = constrForm->conindid;
11669 472 : with_period = constrForm->conperiod;
11670 :
11671 : /* Create the pg_constraint entry at this level */
11672 472 : address = addFkConstraint(addFkReferencingSide,
11673 472 : NameStr(constrForm->conname), fkconstraint,
11674 : partRel, pkrel, indexOid, parentConstrOid,
11675 : numfks, confkey,
11676 : mapped_conkey, conpfeqop,
11677 : conppeqop, conffeqop,
11678 : numfkdelsetcols, confdelsetcols,
11679 : false, with_period);
11680 :
11681 : /* Done with the cloned constraint's tuple */
11682 472 : ReleaseSysCache(tuple);
11683 :
11684 : /* Create the check triggers, and recurse to partitions, if any */
11685 472 : addFkRecurseReferencing(wqueue,
11686 : fkconstraint,
11687 : partRel,
11688 : pkrel,
11689 : indexOid,
11690 : address.objectId,
11691 : numfks,
11692 : confkey,
11693 : mapped_conkey,
11694 : conpfeqop,
11695 : conppeqop,
11696 : conffeqop,
11697 : numfkdelsetcols,
11698 : confdelsetcols,
11699 : false, /* no old check exists */
11700 : AccessExclusiveLock,
11701 : insertTriggerOid,
11702 : updateTriggerOid,
11703 : with_period);
11704 466 : table_close(pkrel, NoLock);
11705 : }
11706 :
11707 580 : table_close(trigrel, RowExclusiveLock);
11708 : }
11709 :
11710 : /*
11711 : * When the parent of a partition receives [the referencing side of] a foreign
11712 : * key, we must propagate that foreign key to the partition. However, the
11713 : * partition might already have an equivalent foreign key; this routine
11714 : * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11715 : * by the other parameters. If they are equivalent, create the link between
11716 : * the two constraints and return true.
11717 : *
11718 : * If the given FK does not match the one defined by rest of the params,
11719 : * return false.
11720 : */
11721 : static bool
11722 276 : tryAttachPartitionForeignKey(List **wqueue,
11723 : ForeignKeyCacheInfo *fk,
11724 : Relation partition,
11725 : Oid parentConstrOid,
11726 : int numfks,
11727 : AttrNumber *mapped_conkey,
11728 : AttrNumber *confkey,
11729 : Oid *conpfeqop,
11730 : Oid parentInsTrigger,
11731 : Oid parentUpdTrigger,
11732 : Relation trigrel)
11733 : {
11734 : HeapTuple parentConstrTup;
11735 : Form_pg_constraint parentConstr;
11736 : HeapTuple partcontup;
11737 : Form_pg_constraint partConstr;
11738 :
11739 276 : parentConstrTup = SearchSysCache1(CONSTROID,
11740 : ObjectIdGetDatum(parentConstrOid));
11741 276 : if (!HeapTupleIsValid(parentConstrTup))
11742 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11743 276 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11744 :
11745 : /*
11746 : * Do some quick & easy initial checks. If any of these fail, we cannot
11747 : * use this constraint.
11748 : */
11749 276 : if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11750 : {
11751 0 : ReleaseSysCache(parentConstrTup);
11752 0 : return false;
11753 : }
11754 768 : for (int i = 0; i < numfks; i++)
11755 : {
11756 492 : if (fk->conkey[i] != mapped_conkey[i] ||
11757 492 : fk->confkey[i] != confkey[i] ||
11758 492 : fk->conpfeqop[i] != conpfeqop[i])
11759 : {
11760 0 : ReleaseSysCache(parentConstrTup);
11761 0 : return false;
11762 : }
11763 : }
11764 :
11765 : /* Looks good so far; perform more extensive checks. */
11766 276 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11767 276 : if (!HeapTupleIsValid(partcontup))
11768 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11769 276 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11770 :
11771 : /*
11772 : * An error should be raised if the constraint enforceability is
11773 : * different. Returning false without raising an error, as we do for other
11774 : * attributes, could lead to a duplicate constraint with the same
11775 : * enforceability as the parent. While this may be acceptable, it may not
11776 : * be ideal. Therefore, it's better to raise an error and allow the user
11777 : * to correct the enforceability before proceeding.
11778 : */
11779 276 : if (partConstr->conenforced != parentConstr->conenforced)
11780 6 : ereport(ERROR,
11781 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11782 : errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11783 : NameStr(parentConstr->conname),
11784 : NameStr(partConstr->conname),
11785 : RelationGetRelationName(partition))));
11786 :
11787 270 : if (OidIsValid(partConstr->conparentid) ||
11788 240 : partConstr->condeferrable != parentConstr->condeferrable ||
11789 212 : partConstr->condeferred != parentConstr->condeferred ||
11790 212 : partConstr->confupdtype != parentConstr->confupdtype ||
11791 176 : partConstr->confdeltype != parentConstr->confdeltype ||
11792 176 : partConstr->confmatchtype != parentConstr->confmatchtype)
11793 : {
11794 108 : ReleaseSysCache(parentConstrTup);
11795 108 : ReleaseSysCache(partcontup);
11796 108 : return false;
11797 : }
11798 :
11799 162 : ReleaseSysCache(parentConstrTup);
11800 162 : ReleaseSysCache(partcontup);
11801 :
11802 : /* Looks good! Attach this constraint. */
11803 162 : AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11804 : parentConstrOid, parentInsTrigger,
11805 : parentUpdTrigger, trigrel);
11806 :
11807 162 : return true;
11808 : }
11809 :
11810 : /*
11811 : * AttachPartitionForeignKey
11812 : *
11813 : * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11814 : * attaching the constraint, removing redundant triggers and entries from
11815 : * pg_constraint, and setting the constraint's parent.
11816 : */
11817 : static void
11818 162 : AttachPartitionForeignKey(List **wqueue,
11819 : Relation partition,
11820 : Oid partConstrOid,
11821 : Oid parentConstrOid,
11822 : Oid parentInsTrigger,
11823 : Oid parentUpdTrigger,
11824 : Relation trigrel)
11825 : {
11826 : HeapTuple parentConstrTup;
11827 : Form_pg_constraint parentConstr;
11828 : HeapTuple partcontup;
11829 : Form_pg_constraint partConstr;
11830 : bool queueValidation;
11831 : Oid partConstrFrelid;
11832 : Oid partConstrRelid;
11833 : bool parentConstrIsEnforced;
11834 :
11835 : /* Fetch the parent constraint tuple */
11836 162 : parentConstrTup = SearchSysCache1(CONSTROID,
11837 : ObjectIdGetDatum(parentConstrOid));
11838 162 : if (!HeapTupleIsValid(parentConstrTup))
11839 0 : elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11840 162 : parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11841 162 : parentConstrIsEnforced = parentConstr->conenforced;
11842 :
11843 : /* Fetch the child constraint tuple */
11844 162 : partcontup = SearchSysCache1(CONSTROID,
11845 : ObjectIdGetDatum(partConstrOid));
11846 162 : if (!HeapTupleIsValid(partcontup))
11847 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11848 162 : partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11849 162 : partConstrFrelid = partConstr->confrelid;
11850 162 : partConstrRelid = partConstr->conrelid;
11851 :
11852 : /*
11853 : * If the referenced table is partitioned, then the partition we're
11854 : * attaching now has extra pg_constraint rows and action triggers that are
11855 : * no longer needed. Remove those.
11856 : */
11857 162 : if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11858 : {
11859 36 : Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11860 :
11861 36 : RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11862 : partConstrRelid);
11863 :
11864 36 : table_close(pg_constraint, RowShareLock);
11865 : }
11866 :
11867 : /*
11868 : * Will we need to validate this constraint? A valid parent constraint
11869 : * implies that all child constraints have been validated, so if this one
11870 : * isn't, we must trigger phase 3 validation.
11871 : */
11872 162 : queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11873 :
11874 162 : ReleaseSysCache(partcontup);
11875 162 : ReleaseSysCache(parentConstrTup);
11876 :
11877 : /*
11878 : * The action triggers in the new partition become redundant -- the parent
11879 : * table already has equivalent ones, and those will be able to reach the
11880 : * partition. Remove the ones in the partition. We identify them because
11881 : * they have our constraint OID, as well as being on the referenced rel.
11882 : */
11883 162 : DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11884 : partConstrRelid);
11885 :
11886 162 : ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11887 : RelationGetRelid(partition));
11888 :
11889 : /*
11890 : * Like the constraint, attach partition's "check" triggers to the
11891 : * corresponding parent triggers if the constraint is ENFORCED. NOT
11892 : * ENFORCED constraints do not have these triggers.
11893 : */
11894 162 : if (parentConstrIsEnforced)
11895 : {
11896 : Oid insertTriggerOid,
11897 : updateTriggerOid;
11898 :
11899 150 : GetForeignKeyCheckTriggers(trigrel,
11900 : partConstrOid, partConstrFrelid, partConstrRelid,
11901 : &insertTriggerOid, &updateTriggerOid);
11902 : Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11903 150 : TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11904 : RelationGetRelid(partition));
11905 : Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11906 150 : TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11907 : RelationGetRelid(partition));
11908 : }
11909 :
11910 : /*
11911 : * We updated this pg_constraint row above to set its parent; validating
11912 : * it will cause its convalidated flag to change, so we need CCI here. In
11913 : * addition, we need it unconditionally for the rare case where the parent
11914 : * table has *two* identical constraints; when reaching this function for
11915 : * the second one, we must have made our changes visible, otherwise we
11916 : * would try to attach both to this one.
11917 : */
11918 162 : CommandCounterIncrement();
11919 :
11920 : /* If validation is needed, put it in the queue now. */
11921 162 : if (queueValidation)
11922 : {
11923 : Relation conrel;
11924 : Oid confrelid;
11925 :
11926 18 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11927 :
11928 18 : partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11929 18 : if (!HeapTupleIsValid(partcontup))
11930 0 : elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11931 :
11932 18 : confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11933 :
11934 : /* Use the same lock as for AT_ValidateConstraint */
11935 18 : QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11936 : partcontup, ShareUpdateExclusiveLock);
11937 18 : ReleaseSysCache(partcontup);
11938 18 : table_close(conrel, RowExclusiveLock);
11939 : }
11940 162 : }
11941 :
11942 : /*
11943 : * RemoveInheritedConstraint
11944 : *
11945 : * Removes the constraint and its associated trigger from the specified
11946 : * relation, which inherited the given constraint.
11947 : */
11948 : static void
11949 36 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
11950 : Oid conrelid)
11951 : {
11952 : ObjectAddresses *objs;
11953 : HeapTuple consttup;
11954 : ScanKeyData key;
11955 : SysScanDesc scan;
11956 : HeapTuple trigtup;
11957 :
11958 36 : ScanKeyInit(&key,
11959 : Anum_pg_constraint_conrelid,
11960 : BTEqualStrategyNumber, F_OIDEQ,
11961 : ObjectIdGetDatum(conrelid));
11962 :
11963 36 : scan = systable_beginscan(conrel,
11964 : ConstraintRelidTypidNameIndexId,
11965 : true, NULL, 1, &key);
11966 36 : objs = new_object_addresses();
11967 324 : while ((consttup = systable_getnext(scan)) != NULL)
11968 : {
11969 288 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11970 :
11971 288 : if (conform->conparentid != conoid)
11972 210 : continue;
11973 : else
11974 : {
11975 : ObjectAddress addr;
11976 : SysScanDesc scan2;
11977 : ScanKeyData key2;
11978 : int n PG_USED_FOR_ASSERTS_ONLY;
11979 :
11980 78 : ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11981 78 : add_exact_object_address(&addr, objs);
11982 :
11983 : /*
11984 : * First we must delete the dependency record that binds the
11985 : * constraint records together.
11986 : */
11987 78 : n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11988 : conform->oid,
11989 : DEPENDENCY_INTERNAL,
11990 : ConstraintRelationId,
11991 : conoid);
11992 : Assert(n == 1); /* actually only one is expected */
11993 :
11994 : /*
11995 : * Now search for the triggers for this constraint and set them up
11996 : * for deletion too
11997 : */
11998 78 : ScanKeyInit(&key2,
11999 : Anum_pg_trigger_tgconstraint,
12000 : BTEqualStrategyNumber, F_OIDEQ,
12001 : ObjectIdGetDatum(conform->oid));
12002 78 : scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
12003 : true, NULL, 1, &key2);
12004 234 : while ((trigtup = systable_getnext(scan2)) != NULL)
12005 : {
12006 156 : ObjectAddressSet(addr, TriggerRelationId,
12007 : ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
12008 156 : add_exact_object_address(&addr, objs);
12009 : }
12010 78 : systable_endscan(scan2);
12011 : }
12012 : }
12013 : /* make the dependency deletions visible */
12014 36 : CommandCounterIncrement();
12015 36 : performMultipleDeletions(objs, DROP_RESTRICT,
12016 : PERFORM_DELETION_INTERNAL);
12017 36 : systable_endscan(scan);
12018 36 : }
12019 :
12020 : /*
12021 : * DropForeignKeyConstraintTriggers
12022 : *
12023 : * The subroutine for tryAttachPartitionForeignKey handles the deletion of
12024 : * action triggers for the foreign key constraint.
12025 : *
12026 : * If valid confrelid and conrelid values are not provided, the respective
12027 : * trigger check will be skipped, and the trigger will be considered for
12028 : * removal.
12029 : */
12030 : static void
12031 234 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
12032 : Oid conrelid)
12033 : {
12034 : ScanKeyData key;
12035 : SysScanDesc scan;
12036 : HeapTuple trigtup;
12037 :
12038 234 : ScanKeyInit(&key,
12039 : Anum_pg_trigger_tgconstraint,
12040 : BTEqualStrategyNumber, F_OIDEQ,
12041 : ObjectIdGetDatum(conoid));
12042 234 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12043 : NULL, 1, &key);
12044 1014 : while ((trigtup = systable_getnext(scan)) != NULL)
12045 : {
12046 780 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12047 : ObjectAddress trigger;
12048 :
12049 : /* Invalid if trigger is not for a referential integrity constraint */
12050 780 : if (!OidIsValid(trgform->tgconstrrelid))
12051 300 : continue;
12052 780 : if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12053 300 : continue;
12054 480 : if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12055 0 : continue;
12056 :
12057 : /* We should be dropping trigger related to foreign key constraint */
12058 : Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12059 : trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12060 : trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12061 : trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12062 : trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12063 : trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12064 : trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12065 : trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12066 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12067 : trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12068 : trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12069 : trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12070 :
12071 : /*
12072 : * The constraint is originally set up to contain this trigger as an
12073 : * implementation object, so there's a dependency record that links
12074 : * the two; however, since the trigger is no longer needed, we remove
12075 : * the dependency link in order to be able to drop the trigger while
12076 : * keeping the constraint intact.
12077 : */
12078 480 : deleteDependencyRecordsFor(TriggerRelationId,
12079 : trgform->oid,
12080 : false);
12081 : /* make dependency deletion visible to performDeletion */
12082 480 : CommandCounterIncrement();
12083 480 : ObjectAddressSet(trigger, TriggerRelationId,
12084 : trgform->oid);
12085 480 : performDeletion(&trigger, DROP_RESTRICT, 0);
12086 : /* make trigger drop visible, in case the loop iterates */
12087 480 : CommandCounterIncrement();
12088 : }
12089 :
12090 234 : systable_endscan(scan);
12091 234 : }
12092 :
12093 : /*
12094 : * GetForeignKeyActionTriggers
12095 : * Returns delete and update "action" triggers of the given relation
12096 : * belonging to the given constraint
12097 : */
12098 : static void
12099 258 : GetForeignKeyActionTriggers(Relation trigrel,
12100 : Oid conoid, Oid confrelid, Oid conrelid,
12101 : Oid *deleteTriggerOid,
12102 : Oid *updateTriggerOid)
12103 : {
12104 : ScanKeyData key;
12105 : SysScanDesc scan;
12106 : HeapTuple trigtup;
12107 :
12108 258 : *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12109 258 : ScanKeyInit(&key,
12110 : Anum_pg_trigger_tgconstraint,
12111 : BTEqualStrategyNumber, F_OIDEQ,
12112 : ObjectIdGetDatum(conoid));
12113 :
12114 258 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12115 : NULL, 1, &key);
12116 522 : while ((trigtup = systable_getnext(scan)) != NULL)
12117 : {
12118 522 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12119 :
12120 522 : if (trgform->tgconstrrelid != conrelid)
12121 4 : continue;
12122 518 : if (trgform->tgrelid != confrelid)
12123 0 : continue;
12124 : /* Only ever look at "action" triggers on the PK side. */
12125 518 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12126 2 : continue;
12127 516 : if (TRIGGER_FOR_DELETE(trgform->tgtype))
12128 : {
12129 : Assert(*deleteTriggerOid == InvalidOid);
12130 258 : *deleteTriggerOid = trgform->oid;
12131 : }
12132 258 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12133 : {
12134 : Assert(*updateTriggerOid == InvalidOid);
12135 258 : *updateTriggerOid = trgform->oid;
12136 : }
12137 : #ifndef USE_ASSERT_CHECKING
12138 : /* In an assert-enabled build, continue looking to find duplicates */
12139 516 : if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12140 258 : break;
12141 : #endif
12142 : }
12143 :
12144 258 : if (!OidIsValid(*deleteTriggerOid))
12145 0 : elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12146 : conoid);
12147 258 : if (!OidIsValid(*updateTriggerOid))
12148 0 : elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12149 : conoid);
12150 :
12151 258 : systable_endscan(scan);
12152 258 : }
12153 :
12154 : /*
12155 : * GetForeignKeyCheckTriggers
12156 : * Returns insert and update "check" triggers of the given relation
12157 : * belonging to the given constraint
12158 : */
12159 : static void
12160 874 : GetForeignKeyCheckTriggers(Relation trigrel,
12161 : Oid conoid, Oid confrelid, Oid conrelid,
12162 : Oid *insertTriggerOid,
12163 : Oid *updateTriggerOid)
12164 : {
12165 : ScanKeyData key;
12166 : SysScanDesc scan;
12167 : HeapTuple trigtup;
12168 :
12169 874 : *insertTriggerOid = *updateTriggerOid = InvalidOid;
12170 874 : ScanKeyInit(&key,
12171 : Anum_pg_trigger_tgconstraint,
12172 : BTEqualStrategyNumber, F_OIDEQ,
12173 : ObjectIdGetDatum(conoid));
12174 :
12175 874 : scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12176 : NULL, 1, &key);
12177 2820 : while ((trigtup = systable_getnext(scan)) != NULL)
12178 : {
12179 2820 : Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12180 :
12181 2820 : if (trgform->tgconstrrelid != confrelid)
12182 964 : continue;
12183 1856 : if (trgform->tgrelid != conrelid)
12184 0 : continue;
12185 : /* Only ever look at "check" triggers on the FK side. */
12186 1856 : if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12187 108 : continue;
12188 1748 : if (TRIGGER_FOR_INSERT(trgform->tgtype))
12189 : {
12190 : Assert(*insertTriggerOid == InvalidOid);
12191 874 : *insertTriggerOid = trgform->oid;
12192 : }
12193 874 : else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12194 : {
12195 : Assert(*updateTriggerOid == InvalidOid);
12196 874 : *updateTriggerOid = trgform->oid;
12197 : }
12198 : #ifndef USE_ASSERT_CHECKING
12199 : /* In an assert-enabled build, continue looking to find duplicates. */
12200 1748 : if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12201 874 : break;
12202 : #endif
12203 : }
12204 :
12205 874 : if (!OidIsValid(*insertTriggerOid))
12206 0 : elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12207 : conoid);
12208 874 : if (!OidIsValid(*updateTriggerOid))
12209 0 : elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12210 : conoid);
12211 :
12212 874 : systable_endscan(scan);
12213 874 : }
12214 :
12215 : /*
12216 : * ALTER TABLE ALTER CONSTRAINT
12217 : *
12218 : * Update the attributes of a constraint.
12219 : *
12220 : * Currently only works for Foreign Key and not null constraints.
12221 : *
12222 : * If the constraint is modified, returns its address; otherwise, return
12223 : * InvalidObjectAddress.
12224 : */
12225 : static ObjectAddress
12226 294 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
12227 : bool recurse, LOCKMODE lockmode)
12228 : {
12229 : Relation conrel;
12230 : Relation tgrel;
12231 : SysScanDesc scan;
12232 : ScanKeyData skey[3];
12233 : HeapTuple contuple;
12234 : Form_pg_constraint currcon;
12235 : ObjectAddress address;
12236 :
12237 : /*
12238 : * Disallow altering ONLY a partitioned table, as it would make no sense.
12239 : * This is okay for legacy inheritance.
12240 : */
12241 294 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12242 0 : ereport(ERROR,
12243 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12244 : errmsg("constraint must be altered in child tables too"),
12245 : errhint("Do not specify the ONLY keyword."));
12246 :
12247 :
12248 294 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12249 294 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12250 :
12251 : /*
12252 : * Find and check the target constraint
12253 : */
12254 294 : ScanKeyInit(&skey[0],
12255 : Anum_pg_constraint_conrelid,
12256 : BTEqualStrategyNumber, F_OIDEQ,
12257 : ObjectIdGetDatum(RelationGetRelid(rel)));
12258 294 : ScanKeyInit(&skey[1],
12259 : Anum_pg_constraint_contypid,
12260 : BTEqualStrategyNumber, F_OIDEQ,
12261 : ObjectIdGetDatum(InvalidOid));
12262 294 : ScanKeyInit(&skey[2],
12263 : Anum_pg_constraint_conname,
12264 : BTEqualStrategyNumber, F_NAMEEQ,
12265 294 : CStringGetDatum(cmdcon->conname));
12266 294 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12267 : true, NULL, 3, skey);
12268 :
12269 : /* There can be at most one matching row */
12270 294 : if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12271 6 : ereport(ERROR,
12272 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12273 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12274 : cmdcon->conname, RelationGetRelationName(rel))));
12275 :
12276 288 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12277 288 : if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12278 0 : ereport(ERROR,
12279 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12280 : errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12281 : cmdcon->conname, RelationGetRelationName(rel))));
12282 288 : if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12283 12 : ereport(ERROR,
12284 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12285 : errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12286 : cmdcon->conname, RelationGetRelationName(rel))));
12287 276 : if (cmdcon->alterInheritability &&
12288 90 : currcon->contype != CONSTRAINT_NOTNULL)
12289 24 : ereport(ERROR,
12290 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12291 : errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12292 : cmdcon->conname, RelationGetRelationName(rel)));
12293 :
12294 : /* Refuse to modify inheritability of inherited constraints */
12295 252 : if (cmdcon->alterInheritability &&
12296 66 : cmdcon->noinherit && currcon->coninhcount > 0)
12297 6 : ereport(ERROR,
12298 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12299 : errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12300 : NameStr(currcon->conname),
12301 : RelationGetRelationName(rel)));
12302 :
12303 : /*
12304 : * If it's not the topmost constraint, raise an error.
12305 : *
12306 : * Altering a non-topmost constraint leaves some triggers untouched, since
12307 : * they are not directly connected to this constraint; also, pg_dump would
12308 : * ignore the deferrability status of the individual constraint, since it
12309 : * only dumps topmost constraints. Avoid these problems by refusing this
12310 : * operation and telling the user to alter the parent constraint instead.
12311 : */
12312 246 : if (OidIsValid(currcon->conparentid))
12313 : {
12314 : HeapTuple tp;
12315 12 : Oid parent = currcon->conparentid;
12316 12 : char *ancestorname = NULL;
12317 12 : char *ancestortable = NULL;
12318 :
12319 : /* Loop to find the topmost constraint */
12320 24 : while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12321 : {
12322 24 : Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
12323 :
12324 : /* If no parent, this is the constraint we want */
12325 24 : if (!OidIsValid(contup->conparentid))
12326 : {
12327 12 : ancestorname = pstrdup(NameStr(contup->conname));
12328 12 : ancestortable = get_rel_name(contup->conrelid);
12329 12 : ReleaseSysCache(tp);
12330 12 : break;
12331 : }
12332 :
12333 12 : parent = contup->conparentid;
12334 12 : ReleaseSysCache(tp);
12335 : }
12336 :
12337 12 : ereport(ERROR,
12338 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12339 : errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12340 : cmdcon->conname, RelationGetRelationName(rel)),
12341 : ancestorname && ancestortable ?
12342 : errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12343 : cmdcon->conname, ancestorname, ancestortable) : 0,
12344 : errhint("You may alter the constraint it derives from instead.")));
12345 : }
12346 :
12347 234 : address = InvalidObjectAddress;
12348 :
12349 : /*
12350 : * Do the actual catalog work, and recurse if necessary.
12351 : */
12352 234 : if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12353 : contuple, recurse, lockmode))
12354 222 : ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12355 :
12356 228 : systable_endscan(scan);
12357 :
12358 228 : table_close(tgrel, RowExclusiveLock);
12359 228 : table_close(conrel, RowExclusiveLock);
12360 :
12361 228 : return address;
12362 : }
12363 :
12364 : /*
12365 : * A subroutine of ATExecAlterConstraint that calls the respective routines for
12366 : * altering constraint's enforceability, deferrability or inheritability.
12367 : */
12368 : static bool
12369 234 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
12370 : Relation conrel, Relation tgrel, Relation rel,
12371 : HeapTuple contuple, bool recurse,
12372 : LOCKMODE lockmode)
12373 : {
12374 : Form_pg_constraint currcon;
12375 234 : bool changed = false;
12376 234 : List *otherrelids = NIL;
12377 :
12378 234 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12379 :
12380 : /*
12381 : * Do the catalog work for the enforceability or deferrability change,
12382 : * recurse if necessary.
12383 : *
12384 : * Note that even if deferrability is requested to be altered along with
12385 : * enforceability, we don't need to explicitly update multiple entries in
12386 : * pg_trigger related to deferrability.
12387 : *
12388 : * Modifying enforceability involves either creating or dropping the
12389 : * trigger, during which the deferrability setting will be adjusted
12390 : * automatically.
12391 : */
12392 312 : if (cmdcon->alterEnforceability &&
12393 78 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12394 : currcon->conrelid, currcon->confrelid,
12395 : contuple, lockmode, InvalidOid,
12396 : InvalidOid, InvalidOid, InvalidOid))
12397 72 : changed = true;
12398 :
12399 258 : else if (cmdcon->alterDeferrability &&
12400 96 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12401 : contuple, recurse, &otherrelids,
12402 : lockmode))
12403 : {
12404 : /*
12405 : * AlterConstrUpdateConstraintEntry already invalidated relcache for
12406 : * the relations having the constraint itself; here we also invalidate
12407 : * for relations that have any triggers that are part of the
12408 : * constraint.
12409 : */
12410 306 : foreach_oid(relid, otherrelids)
12411 114 : CacheInvalidateRelcacheByRelid(relid);
12412 :
12413 96 : changed = true;
12414 : }
12415 :
12416 : /*
12417 : * Do the catalog work for the inheritability change.
12418 : */
12419 288 : if (cmdcon->alterInheritability &&
12420 60 : ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12421 : lockmode))
12422 54 : changed = true;
12423 :
12424 228 : return changed;
12425 : }
12426 :
12427 : /*
12428 : * Returns true if the constraint's enforceability is altered.
12429 : *
12430 : * Depending on whether the constraint is being set to ENFORCED or NOT
12431 : * ENFORCED, it creates or drops the trigger accordingly.
12432 : *
12433 : * Note that we must recurse even when trying to change a constraint to not
12434 : * enforced if it is already not enforced, in case descendant constraints
12435 : * might be enforced and need to be changed to not enforced. Conversely, we
12436 : * should do nothing if a constraint is being set to enforced and is already
12437 : * enforced, as descendant constraints cannot be different in that case.
12438 : */
12439 : static bool
12440 180 : ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
12441 : Relation conrel, Relation tgrel,
12442 : Oid fkrelid, Oid pkrelid,
12443 : HeapTuple contuple, LOCKMODE lockmode,
12444 : Oid ReferencedParentDelTrigger,
12445 : Oid ReferencedParentUpdTrigger,
12446 : Oid ReferencingParentInsTrigger,
12447 : Oid ReferencingParentUpdTrigger)
12448 : {
12449 : Form_pg_constraint currcon;
12450 : Oid conoid;
12451 : Relation rel;
12452 180 : bool changed = false;
12453 :
12454 : /* Since this function recurses, it could be driven to stack overflow */
12455 180 : check_stack_depth();
12456 :
12457 : Assert(cmdcon->alterEnforceability);
12458 :
12459 180 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12460 180 : conoid = currcon->oid;
12461 :
12462 : /* Should be foreign key constraint */
12463 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12464 :
12465 180 : rel = table_open(currcon->conrelid, lockmode);
12466 :
12467 180 : if (currcon->conenforced != cmdcon->is_enforced)
12468 : {
12469 174 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12470 174 : changed = true;
12471 : }
12472 :
12473 : /* Drop triggers */
12474 180 : if (!cmdcon->is_enforced)
12475 : {
12476 : /*
12477 : * When setting a constraint to NOT ENFORCED, the constraint triggers
12478 : * need to be dropped. Therefore, we must process the child relations
12479 : * first, followed by the parent, to account for dependencies.
12480 : */
12481 126 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12482 54 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12483 18 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12484 : fkrelid, pkrelid, contuple,
12485 : lockmode, InvalidOid, InvalidOid,
12486 : InvalidOid, InvalidOid);
12487 :
12488 : /* Drop all the triggers */
12489 72 : DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
12490 : }
12491 108 : else if (changed) /* Create triggers */
12492 : {
12493 108 : Oid ReferencedDelTriggerOid = InvalidOid,
12494 108 : ReferencedUpdTriggerOid = InvalidOid,
12495 108 : ReferencingInsTriggerOid = InvalidOid,
12496 108 : ReferencingUpdTriggerOid = InvalidOid;
12497 :
12498 : /* Prepare the minimal information required for trigger creation. */
12499 108 : Constraint *fkconstraint = makeNode(Constraint);
12500 :
12501 108 : fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12502 108 : fkconstraint->fk_matchtype = currcon->confmatchtype;
12503 108 : fkconstraint->fk_upd_action = currcon->confupdtype;
12504 108 : fkconstraint->fk_del_action = currcon->confdeltype;
12505 :
12506 : /* Create referenced triggers */
12507 108 : if (currcon->conrelid == fkrelid)
12508 66 : createForeignKeyActionTriggers(currcon->conrelid,
12509 : currcon->confrelid,
12510 : fkconstraint,
12511 : conoid,
12512 : currcon->conindid,
12513 : ReferencedParentDelTrigger,
12514 : ReferencedParentUpdTrigger,
12515 : &ReferencedDelTriggerOid,
12516 : &ReferencedUpdTriggerOid);
12517 :
12518 : /* Create referencing triggers */
12519 108 : if (currcon->confrelid == pkrelid)
12520 90 : createForeignKeyCheckTriggers(currcon->conrelid,
12521 : pkrelid,
12522 : fkconstraint,
12523 : conoid,
12524 : currcon->conindid,
12525 : ReferencingParentInsTrigger,
12526 : ReferencingParentUpdTrigger,
12527 : &ReferencingInsTriggerOid,
12528 : &ReferencingUpdTriggerOid);
12529 :
12530 : /*
12531 : * Tell Phase 3 to check that the constraint is satisfied by existing
12532 : * rows. Only applies to leaf partitions, and (for constraints that
12533 : * reference a partitioned table) only if this is not one of the
12534 : * pg_constraint rows that exist solely to support action triggers.
12535 : */
12536 108 : if (rel->rd_rel->relkind == RELKIND_RELATION &&
12537 90 : currcon->confrelid == pkrelid)
12538 : {
12539 : AlteredTableInfo *tab;
12540 : NewConstraint *newcon;
12541 :
12542 72 : newcon = palloc0_object(NewConstraint);
12543 72 : newcon->name = fkconstraint->conname;
12544 72 : newcon->contype = CONSTR_FOREIGN;
12545 72 : newcon->refrelid = currcon->confrelid;
12546 72 : newcon->refindid = currcon->conindid;
12547 72 : newcon->conid = currcon->oid;
12548 72 : newcon->qual = (Node *) fkconstraint;
12549 :
12550 : /* Find or create work queue entry for this table */
12551 72 : tab = ATGetQueueEntry(wqueue, rel);
12552 72 : tab->constraints = lappend(tab->constraints, newcon);
12553 : }
12554 :
12555 : /*
12556 : * If the table at either end of the constraint is partitioned, we
12557 : * need to recurse and create triggers for each constraint that is a
12558 : * child of this one.
12559 : */
12560 198 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12561 90 : get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12562 30 : AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12563 : fkrelid, pkrelid, contuple,
12564 : lockmode, ReferencedDelTriggerOid,
12565 : ReferencedUpdTriggerOid,
12566 : ReferencingInsTriggerOid,
12567 : ReferencingUpdTriggerOid);
12568 : }
12569 :
12570 180 : table_close(rel, NoLock);
12571 :
12572 180 : return changed;
12573 : }
12574 :
12575 : /*
12576 : * Returns true if the constraint's deferrability is altered.
12577 : *
12578 : * *otherrelids is appended OIDs of relations containing affected triggers.
12579 : *
12580 : * Note that we must recurse even when the values are correct, in case
12581 : * indirect descendants have had their constraints altered locally.
12582 : * (This could be avoided if we forbade altering constraints in partitions
12583 : * but existing releases don't do that.)
12584 : */
12585 : static bool
12586 162 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
12587 : Relation conrel, Relation tgrel, Relation rel,
12588 : HeapTuple contuple, bool recurse,
12589 : List **otherrelids, LOCKMODE lockmode)
12590 : {
12591 : Form_pg_constraint currcon;
12592 : Oid refrelid;
12593 162 : bool changed = false;
12594 :
12595 : /* since this function recurses, it could be driven to stack overflow */
12596 162 : check_stack_depth();
12597 :
12598 : Assert(cmdcon->alterDeferrability);
12599 :
12600 162 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12601 162 : refrelid = currcon->confrelid;
12602 :
12603 : /* Should be foreign key constraint */
12604 : Assert(currcon->contype == CONSTRAINT_FOREIGN);
12605 :
12606 : /*
12607 : * If called to modify a constraint that's already in the desired state,
12608 : * silently do nothing.
12609 : */
12610 162 : if (currcon->condeferrable != cmdcon->deferrable ||
12611 6 : currcon->condeferred != cmdcon->initdeferred)
12612 : {
12613 162 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12614 162 : changed = true;
12615 :
12616 : /*
12617 : * Now we need to update the multiple entries in pg_trigger that
12618 : * implement the constraint.
12619 : */
12620 162 : AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12621 162 : cmdcon->deferrable,
12622 162 : cmdcon->initdeferred, otherrelids);
12623 : }
12624 :
12625 : /*
12626 : * If the table at either end of the constraint is partitioned, we need to
12627 : * handle every constraint that is a child of this one.
12628 : */
12629 162 : if (recurse && changed &&
12630 300 : (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12631 138 : get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12632 42 : AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12633 : contuple, recurse, otherrelids,
12634 : lockmode);
12635 :
12636 162 : return changed;
12637 : }
12638 :
12639 : /*
12640 : * Returns true if the constraint's inheritability is altered.
12641 : */
12642 : static bool
12643 60 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
12644 : Relation conrel, Relation rel,
12645 : HeapTuple contuple, LOCKMODE lockmode)
12646 : {
12647 : Form_pg_constraint currcon;
12648 : AttrNumber colNum;
12649 : char *colName;
12650 : List *children;
12651 :
12652 : Assert(cmdcon->alterInheritability);
12653 :
12654 60 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12655 :
12656 : /* The current implementation only works for NOT NULL constraints */
12657 : Assert(currcon->contype == CONSTRAINT_NOTNULL);
12658 :
12659 : /*
12660 : * If called to modify a constraint that's already in the desired state,
12661 : * silently do nothing.
12662 : */
12663 60 : if (cmdcon->noinherit == currcon->connoinherit)
12664 0 : return false;
12665 :
12666 60 : AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12667 60 : CommandCounterIncrement();
12668 :
12669 : /* Fetch the column number and name */
12670 60 : colNum = extractNotNullColumn(contuple);
12671 60 : colName = get_attname(currcon->conrelid, colNum, false);
12672 :
12673 : /*
12674 : * Propagate the change to children. For this subcommand type we don't
12675 : * recursively affect children, just the immediate level.
12676 : */
12677 60 : children = find_inheritance_children(RelationGetRelid(rel),
12678 : lockmode);
12679 192 : foreach_oid(childoid, children)
12680 : {
12681 : ObjectAddress addr;
12682 :
12683 84 : if (cmdcon->noinherit)
12684 : {
12685 : HeapTuple childtup;
12686 : Form_pg_constraint childcon;
12687 :
12688 30 : childtup = findNotNullConstraint(childoid, colName);
12689 30 : if (!childtup)
12690 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12691 : colName, childoid);
12692 30 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12693 : Assert(childcon->coninhcount > 0);
12694 30 : childcon->coninhcount--;
12695 30 : childcon->conislocal = true;
12696 30 : CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12697 30 : heap_freetuple(childtup);
12698 : }
12699 : else
12700 : {
12701 54 : Relation childrel = table_open(childoid, NoLock);
12702 :
12703 54 : addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12704 : colName, true, true, lockmode);
12705 48 : if (OidIsValid(addr.objectId))
12706 48 : CommandCounterIncrement();
12707 48 : table_close(childrel, NoLock);
12708 : }
12709 : }
12710 :
12711 54 : return true;
12712 : }
12713 :
12714 : /*
12715 : * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12716 : * trigger's deferrability.
12717 : *
12718 : * The arguments to this function have the same meaning as the arguments to
12719 : * ATExecAlterConstrDeferrability.
12720 : */
12721 : static void
12722 162 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12723 : bool deferrable, bool initdeferred,
12724 : List **otherrelids)
12725 : {
12726 : HeapTuple tgtuple;
12727 : ScanKeyData tgkey;
12728 : SysScanDesc tgscan;
12729 :
12730 162 : ScanKeyInit(&tgkey,
12731 : Anum_pg_trigger_tgconstraint,
12732 : BTEqualStrategyNumber, F_OIDEQ,
12733 : ObjectIdGetDatum(conoid));
12734 162 : tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12735 : NULL, 1, &tgkey);
12736 630 : while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12737 : {
12738 468 : Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12739 : Form_pg_trigger copy_tg;
12740 : HeapTuple tgCopyTuple;
12741 :
12742 : /*
12743 : * Remember OIDs of other relation(s) involved in FK constraint.
12744 : * (Note: it's likely that we could skip forcing a relcache inval for
12745 : * other rels that don't have a trigger whose properties change, but
12746 : * let's be conservative.)
12747 : */
12748 468 : if (tgform->tgrelid != RelationGetRelid(rel))
12749 228 : *otherrelids = list_append_unique_oid(*otherrelids,
12750 : tgform->tgrelid);
12751 :
12752 : /*
12753 : * Update enable status and deferrability of RI_FKey_noaction_del,
12754 : * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12755 : * triggers, but not others; see createForeignKeyActionTriggers and
12756 : * CreateFKCheckTrigger.
12757 : */
12758 468 : if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12759 372 : tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12760 258 : tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12761 138 : tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12762 18 : continue;
12763 :
12764 450 : tgCopyTuple = heap_copytuple(tgtuple);
12765 450 : copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12766 :
12767 450 : copy_tg->tgdeferrable = deferrable;
12768 450 : copy_tg->tginitdeferred = initdeferred;
12769 450 : CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12770 :
12771 450 : InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12772 :
12773 450 : heap_freetuple(tgCopyTuple);
12774 : }
12775 :
12776 162 : systable_endscan(tgscan);
12777 162 : }
12778 :
12779 : /*
12780 : * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12781 : * the specified constraint.
12782 : *
12783 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12784 : * list of child relations and recursing; instead it uses the conparentid
12785 : * relationships. This may need to be reconsidered.
12786 : *
12787 : * The arguments to this function have the same meaning as the arguments to
12788 : * ATExecAlterConstrEnforceability.
12789 : */
12790 : static void
12791 48 : AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12792 : Relation conrel, Relation tgrel,
12793 : Oid fkrelid, Oid pkrelid,
12794 : HeapTuple contuple, LOCKMODE lockmode,
12795 : Oid ReferencedParentDelTrigger,
12796 : Oid ReferencedParentUpdTrigger,
12797 : Oid ReferencingParentInsTrigger,
12798 : Oid ReferencingParentUpdTrigger)
12799 : {
12800 : Form_pg_constraint currcon;
12801 : Oid conoid;
12802 : ScanKeyData pkey;
12803 : SysScanDesc pscan;
12804 : HeapTuple childtup;
12805 :
12806 48 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12807 48 : conoid = currcon->oid;
12808 :
12809 48 : ScanKeyInit(&pkey,
12810 : Anum_pg_constraint_conparentid,
12811 : BTEqualStrategyNumber, F_OIDEQ,
12812 : ObjectIdGetDatum(conoid));
12813 :
12814 48 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12815 : true, NULL, 1, &pkey);
12816 :
12817 150 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12818 102 : ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12819 : pkrelid, childtup, lockmode,
12820 : ReferencedParentDelTrigger,
12821 : ReferencedParentUpdTrigger,
12822 : ReferencingParentInsTrigger,
12823 : ReferencingParentUpdTrigger);
12824 :
12825 48 : systable_endscan(pscan);
12826 48 : }
12827 :
12828 : /*
12829 : * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12830 : * the specified constraint.
12831 : *
12832 : * Note that this doesn't handle recursion the normal way, viz. by scanning the
12833 : * list of child relations and recursing; instead it uses the conparentid
12834 : * relationships. This may need to be reconsidered.
12835 : *
12836 : * The arguments to this function have the same meaning as the arguments to
12837 : * ATExecAlterConstrDeferrability.
12838 : */
12839 : static void
12840 42 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
12841 : Relation conrel, Relation tgrel, Relation rel,
12842 : HeapTuple contuple, bool recurse,
12843 : List **otherrelids, LOCKMODE lockmode)
12844 : {
12845 : Form_pg_constraint currcon;
12846 : Oid conoid;
12847 : ScanKeyData pkey;
12848 : SysScanDesc pscan;
12849 : HeapTuple childtup;
12850 :
12851 42 : currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12852 42 : conoid = currcon->oid;
12853 :
12854 42 : ScanKeyInit(&pkey,
12855 : Anum_pg_constraint_conparentid,
12856 : BTEqualStrategyNumber, F_OIDEQ,
12857 : ObjectIdGetDatum(conoid));
12858 :
12859 42 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12860 : true, NULL, 1, &pkey);
12861 :
12862 108 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12863 : {
12864 66 : Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12865 : Relation childrel;
12866 :
12867 66 : childrel = table_open(childcon->conrelid, lockmode);
12868 :
12869 66 : ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12870 : childtup, recurse, otherrelids, lockmode);
12871 66 : table_close(childrel, NoLock);
12872 : }
12873 :
12874 42 : systable_endscan(pscan);
12875 42 : }
12876 :
12877 : /*
12878 : * Update the constraint entry for the given ATAlterConstraint command, and
12879 : * invoke the appropriate hooks.
12880 : */
12881 : static void
12882 396 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
12883 : HeapTuple contuple)
12884 : {
12885 : HeapTuple copyTuple;
12886 : Form_pg_constraint copy_con;
12887 :
12888 : Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12889 : cmdcon->alterInheritability);
12890 :
12891 396 : copyTuple = heap_copytuple(contuple);
12892 396 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12893 :
12894 396 : if (cmdcon->alterEnforceability)
12895 : {
12896 174 : copy_con->conenforced = cmdcon->is_enforced;
12897 :
12898 : /*
12899 : * NB: The convalidated status is irrelevant when the constraint is
12900 : * set to NOT ENFORCED, but for consistency, it should still be set
12901 : * appropriately. Similarly, if the constraint is later changed to
12902 : * ENFORCED, validation will be performed during phase 3, so it makes
12903 : * sense to mark it as valid in that case.
12904 : */
12905 174 : copy_con->convalidated = cmdcon->is_enforced;
12906 : }
12907 396 : if (cmdcon->alterDeferrability)
12908 : {
12909 168 : copy_con->condeferrable = cmdcon->deferrable;
12910 168 : copy_con->condeferred = cmdcon->initdeferred;
12911 : }
12912 396 : if (cmdcon->alterInheritability)
12913 60 : copy_con->connoinherit = cmdcon->noinherit;
12914 :
12915 396 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
12916 396 : InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12917 :
12918 : /* Make new constraint flags visible to others */
12919 396 : CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12920 :
12921 396 : heap_freetuple(copyTuple);
12922 396 : }
12923 :
12924 : /*
12925 : * ALTER TABLE VALIDATE CONSTRAINT
12926 : *
12927 : * XXX The reason we handle recursion here rather than at Phase 1 is because
12928 : * there's no good way to skip recursing when handling foreign keys: there is
12929 : * no need to lock children in that case, yet we wouldn't be able to avoid
12930 : * doing so at that level.
12931 : *
12932 : * Return value is the address of the validated constraint. If the constraint
12933 : * was already validated, InvalidObjectAddress is returned.
12934 : */
12935 : static ObjectAddress
12936 590 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12937 : bool recurse, bool recursing, LOCKMODE lockmode)
12938 : {
12939 : Relation conrel;
12940 : SysScanDesc scan;
12941 : ScanKeyData skey[3];
12942 : HeapTuple tuple;
12943 : Form_pg_constraint con;
12944 : ObjectAddress address;
12945 :
12946 590 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12947 :
12948 : /*
12949 : * Find and check the target constraint
12950 : */
12951 590 : ScanKeyInit(&skey[0],
12952 : Anum_pg_constraint_conrelid,
12953 : BTEqualStrategyNumber, F_OIDEQ,
12954 : ObjectIdGetDatum(RelationGetRelid(rel)));
12955 590 : ScanKeyInit(&skey[1],
12956 : Anum_pg_constraint_contypid,
12957 : BTEqualStrategyNumber, F_OIDEQ,
12958 : ObjectIdGetDatum(InvalidOid));
12959 590 : ScanKeyInit(&skey[2],
12960 : Anum_pg_constraint_conname,
12961 : BTEqualStrategyNumber, F_NAMEEQ,
12962 : CStringGetDatum(constrName));
12963 590 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12964 : true, NULL, 3, skey);
12965 :
12966 : /* There can be at most one matching row */
12967 590 : if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12968 0 : ereport(ERROR,
12969 : (errcode(ERRCODE_UNDEFINED_OBJECT),
12970 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12971 : constrName, RelationGetRelationName(rel))));
12972 :
12973 590 : con = (Form_pg_constraint) GETSTRUCT(tuple);
12974 590 : if (con->contype != CONSTRAINT_FOREIGN &&
12975 256 : con->contype != CONSTRAINT_CHECK &&
12976 112 : con->contype != CONSTRAINT_NOTNULL)
12977 0 : ereport(ERROR,
12978 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
12979 : errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12980 : constrName, RelationGetRelationName(rel)),
12981 : errdetail("This operation is not supported for this type of constraint."));
12982 :
12983 590 : if (!con->conenforced)
12984 6 : ereport(ERROR,
12985 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12986 : errmsg("cannot validate NOT ENFORCED constraint")));
12987 :
12988 584 : if (!con->convalidated)
12989 : {
12990 566 : if (con->contype == CONSTRAINT_FOREIGN)
12991 : {
12992 328 : QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12993 : tuple, lockmode);
12994 : }
12995 238 : else if (con->contype == CONSTRAINT_CHECK)
12996 : {
12997 126 : QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12998 : tuple, recurse, recursing, lockmode);
12999 : }
13000 112 : else if (con->contype == CONSTRAINT_NOTNULL)
13001 : {
13002 112 : QueueNNConstraintValidation(wqueue, conrel, rel,
13003 : tuple, recurse, recursing, lockmode);
13004 : }
13005 :
13006 566 : ObjectAddressSet(address, ConstraintRelationId, con->oid);
13007 : }
13008 : else
13009 18 : address = InvalidObjectAddress; /* already validated */
13010 :
13011 584 : systable_endscan(scan);
13012 :
13013 584 : table_close(conrel, RowExclusiveLock);
13014 :
13015 584 : return address;
13016 : }
13017 :
13018 : /*
13019 : * QueueFKConstraintValidation
13020 : *
13021 : * Add an entry to the wqueue to validate the given foreign key constraint in
13022 : * Phase 3 and update the convalidated field in the pg_constraint catalog
13023 : * for the specified relation and all its children.
13024 : */
13025 : static void
13026 406 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
13027 : Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
13028 : {
13029 : Form_pg_constraint con;
13030 : AlteredTableInfo *tab;
13031 : HeapTuple copyTuple;
13032 : Form_pg_constraint copy_con;
13033 :
13034 406 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13035 : Assert(con->contype == CONSTRAINT_FOREIGN);
13036 : Assert(!con->convalidated);
13037 :
13038 : /*
13039 : * Add the validation to phase 3's queue; not needed for partitioned
13040 : * tables themselves, only for their partitions.
13041 : *
13042 : * When the referenced table (pkrelid) is partitioned, the referencing
13043 : * table (fkrel) has one pg_constraint row pointing to each partition
13044 : * thereof. These rows are there only to support action triggers and no
13045 : * table scan is needed, therefore skip this for them as well.
13046 : */
13047 406 : if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13048 358 : con->confrelid == pkrelid)
13049 : {
13050 : NewConstraint *newcon;
13051 : Constraint *fkconstraint;
13052 :
13053 : /* Queue validation for phase 3 */
13054 340 : fkconstraint = makeNode(Constraint);
13055 : /* for now this is all we need */
13056 340 : fkconstraint->conname = pstrdup(NameStr(con->conname));
13057 :
13058 340 : newcon = palloc0_object(NewConstraint);
13059 340 : newcon->name = fkconstraint->conname;
13060 340 : newcon->contype = CONSTR_FOREIGN;
13061 340 : newcon->refrelid = con->confrelid;
13062 340 : newcon->refindid = con->conindid;
13063 340 : newcon->conid = con->oid;
13064 340 : newcon->qual = (Node *) fkconstraint;
13065 :
13066 : /* Find or create work queue entry for this table */
13067 340 : tab = ATGetQueueEntry(wqueue, fkrel);
13068 340 : tab->constraints = lappend(tab->constraints, newcon);
13069 : }
13070 :
13071 : /*
13072 : * If the table at either end of the constraint is partitioned, we need to
13073 : * recurse and handle every unvalidated constraint that is a child of this
13074 : * constraint.
13075 : */
13076 764 : if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13077 358 : get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13078 : {
13079 : ScanKeyData pkey;
13080 : SysScanDesc pscan;
13081 : HeapTuple childtup;
13082 :
13083 78 : ScanKeyInit(&pkey,
13084 : Anum_pg_constraint_conparentid,
13085 : BTEqualStrategyNumber, F_OIDEQ,
13086 : ObjectIdGetDatum(con->oid));
13087 :
13088 78 : pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13089 : true, NULL, 1, &pkey);
13090 :
13091 156 : while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13092 : {
13093 : Form_pg_constraint childcon;
13094 : Relation childrel;
13095 :
13096 78 : childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13097 :
13098 : /*
13099 : * If the child constraint has already been validated, no further
13100 : * action is required for it or its descendants, as they are all
13101 : * valid.
13102 : */
13103 78 : if (childcon->convalidated)
13104 18 : continue;
13105 :
13106 60 : childrel = table_open(childcon->conrelid, lockmode);
13107 :
13108 : /*
13109 : * NB: Note that pkrelid should be passed as-is during recursion,
13110 : * as it is required to identify the root referenced table.
13111 : */
13112 60 : QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13113 : childtup, lockmode);
13114 60 : table_close(childrel, NoLock);
13115 : }
13116 :
13117 78 : systable_endscan(pscan);
13118 : }
13119 :
13120 : /*
13121 : * Now mark the pg_constraint row as validated (even if we didn't check,
13122 : * notably the ones for partitions on the referenced side).
13123 : *
13124 : * We rely on transaction abort to roll back this change if phase 3
13125 : * ultimately finds violating rows. This is a bit ugly.
13126 : */
13127 406 : copyTuple = heap_copytuple(contuple);
13128 406 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13129 406 : copy_con->convalidated = true;
13130 406 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13131 :
13132 406 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13133 :
13134 406 : heap_freetuple(copyTuple);
13135 406 : }
13136 :
13137 : /*
13138 : * QueueCheckConstraintValidation
13139 : *
13140 : * Add an entry to the wqueue to validate the given check constraint in Phase 3
13141 : * and update the convalidated field in the pg_constraint catalog for the
13142 : * specified relation and all its inheriting children.
13143 : */
13144 : static void
13145 126 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13146 : char *constrName, HeapTuple contuple,
13147 : bool recurse, bool recursing, LOCKMODE lockmode)
13148 : {
13149 : Form_pg_constraint con;
13150 : AlteredTableInfo *tab;
13151 : HeapTuple copyTuple;
13152 : Form_pg_constraint copy_con;
13153 :
13154 126 : List *children = NIL;
13155 : ListCell *child;
13156 : NewConstraint *newcon;
13157 : Datum val;
13158 : char *conbin;
13159 :
13160 126 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13161 : Assert(con->contype == CONSTRAINT_CHECK);
13162 :
13163 : /*
13164 : * If we're recursing, the parent has already done this, so skip it. Also,
13165 : * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13166 : * for it in the children.
13167 : */
13168 126 : if (!recursing && !con->connoinherit)
13169 72 : children = find_all_inheritors(RelationGetRelid(rel),
13170 : lockmode, NULL);
13171 :
13172 : /*
13173 : * For CHECK constraints, we must ensure that we only mark the constraint
13174 : * as validated on the parent if it's already validated on the children.
13175 : *
13176 : * We recurse before validating on the parent, to reduce risk of
13177 : * deadlocks.
13178 : */
13179 246 : foreach(child, children)
13180 : {
13181 120 : Oid childoid = lfirst_oid(child);
13182 : Relation childrel;
13183 :
13184 120 : if (childoid == RelationGetRelid(rel))
13185 72 : continue;
13186 :
13187 : /*
13188 : * If we are told not to recurse, there had better not be any child
13189 : * tables, because we can't mark the constraint on the parent valid
13190 : * unless it is valid for all child tables.
13191 : */
13192 48 : if (!recurse)
13193 0 : ereport(ERROR,
13194 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13195 : errmsg("constraint must be validated on child tables too")));
13196 :
13197 : /* find_all_inheritors already got lock */
13198 48 : childrel = table_open(childoid, NoLock);
13199 :
13200 48 : ATExecValidateConstraint(wqueue, childrel, constrName, false,
13201 : true, lockmode);
13202 48 : table_close(childrel, NoLock);
13203 : }
13204 :
13205 : /* Queue validation for phase 3 */
13206 126 : newcon = palloc0_object(NewConstraint);
13207 126 : newcon->name = constrName;
13208 126 : newcon->contype = CONSTR_CHECK;
13209 126 : newcon->refrelid = InvalidOid;
13210 126 : newcon->refindid = InvalidOid;
13211 126 : newcon->conid = con->oid;
13212 :
13213 126 : val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13214 : Anum_pg_constraint_conbin);
13215 126 : conbin = TextDatumGetCString(val);
13216 126 : newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13217 :
13218 : /* Find or create work queue entry for this table */
13219 126 : tab = ATGetQueueEntry(wqueue, rel);
13220 126 : tab->constraints = lappend(tab->constraints, newcon);
13221 :
13222 : /*
13223 : * Invalidate relcache so that others see the new validated constraint.
13224 : */
13225 126 : CacheInvalidateRelcache(rel);
13226 :
13227 : /*
13228 : * Now update the catalog, while we have the door open.
13229 : */
13230 126 : copyTuple = heap_copytuple(contuple);
13231 126 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13232 126 : copy_con->convalidated = true;
13233 126 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13234 :
13235 126 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13236 :
13237 126 : heap_freetuple(copyTuple);
13238 126 : }
13239 :
13240 : /*
13241 : * QueueNNConstraintValidation
13242 : *
13243 : * Add an entry to the wqueue to validate the given not-null constraint in
13244 : * Phase 3 and update the convalidated field in the pg_constraint catalog for
13245 : * the specified relation and all its inheriting children.
13246 : */
13247 : static void
13248 112 : QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13249 : HeapTuple contuple, bool recurse, bool recursing,
13250 : LOCKMODE lockmode)
13251 : {
13252 : Form_pg_constraint con;
13253 : AlteredTableInfo *tab;
13254 : HeapTuple copyTuple;
13255 : Form_pg_constraint copy_con;
13256 112 : List *children = NIL;
13257 : AttrNumber attnum;
13258 : char *colname;
13259 :
13260 112 : con = (Form_pg_constraint) GETSTRUCT(contuple);
13261 : Assert(con->contype == CONSTRAINT_NOTNULL);
13262 :
13263 112 : attnum = extractNotNullColumn(contuple);
13264 :
13265 : /*
13266 : * If we're recursing, we've already done this for parent, so skip it.
13267 : * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13268 : * look for it in the children.
13269 : *
13270 : * We recurse before validating on the parent, to reduce risk of
13271 : * deadlocks.
13272 : */
13273 112 : if (!recursing && !con->connoinherit)
13274 76 : children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13275 :
13276 112 : colname = get_attname(RelationGetRelid(rel), attnum, false);
13277 378 : foreach_oid(childoid, children)
13278 : {
13279 : Relation childrel;
13280 : HeapTuple contup;
13281 : Form_pg_constraint childcon;
13282 : char *conname;
13283 :
13284 154 : if (childoid == RelationGetRelid(rel))
13285 76 : continue;
13286 :
13287 : /*
13288 : * If we are told not to recurse, there had better not be any child
13289 : * tables, because we can't mark the constraint on the parent valid
13290 : * unless it is valid for all child tables.
13291 : */
13292 78 : if (!recurse)
13293 0 : ereport(ERROR,
13294 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13295 : errmsg("constraint must be validated on child tables too"));
13296 :
13297 : /*
13298 : * The column on child might have a different attnum, so search by
13299 : * column name.
13300 : */
13301 78 : contup = findNotNullConstraint(childoid, colname);
13302 78 : if (!contup)
13303 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13304 : colname, get_rel_name(childoid));
13305 78 : childcon = (Form_pg_constraint) GETSTRUCT(contup);
13306 78 : if (childcon->convalidated)
13307 42 : continue;
13308 :
13309 : /* find_all_inheritors already got lock */
13310 36 : childrel = table_open(childoid, NoLock);
13311 36 : conname = pstrdup(NameStr(childcon->conname));
13312 :
13313 : /* XXX improve ATExecValidateConstraint API to avoid double search */
13314 36 : ATExecValidateConstraint(wqueue, childrel, conname,
13315 : false, true, lockmode);
13316 36 : table_close(childrel, NoLock);
13317 : }
13318 :
13319 : /* Set attnotnull appropriately without queueing another validation */
13320 112 : set_attnotnull(NULL, rel, attnum, true, false);
13321 :
13322 112 : tab = ATGetQueueEntry(wqueue, rel);
13323 112 : tab->verify_new_notnull = true;
13324 :
13325 : /*
13326 : * Invalidate relcache so that others see the new validated constraint.
13327 : */
13328 112 : CacheInvalidateRelcache(rel);
13329 :
13330 : /*
13331 : * Now update the catalogs, while we have the door open.
13332 : */
13333 112 : copyTuple = heap_copytuple(contuple);
13334 112 : copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13335 112 : copy_con->convalidated = true;
13336 112 : CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
13337 :
13338 112 : InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13339 :
13340 112 : heap_freetuple(copyTuple);
13341 112 : }
13342 :
13343 : /*
13344 : * transformColumnNameList - transform list of column names
13345 : *
13346 : * Lookup each name and return its attnum and, optionally, type and collation
13347 : * OIDs
13348 : *
13349 : * Note: the name of this function suggests that it's general-purpose,
13350 : * but actually it's only used to look up names appearing in foreign-key
13351 : * clauses. The error messages would need work to use it in other cases,
13352 : * and perhaps the validity checks as well.
13353 : */
13354 : static int
13355 6658 : transformColumnNameList(Oid relId, List *colList,
13356 : int16 *attnums, Oid *atttypids, Oid *attcollids)
13357 : {
13358 : ListCell *l;
13359 : int attnum;
13360 :
13361 6658 : attnum = 0;
13362 12142 : foreach(l, colList)
13363 : {
13364 5550 : char *attname = strVal(lfirst(l));
13365 : HeapTuple atttuple;
13366 : Form_pg_attribute attform;
13367 :
13368 5550 : atttuple = SearchSysCacheAttName(relId, attname);
13369 5550 : if (!HeapTupleIsValid(atttuple))
13370 54 : ereport(ERROR,
13371 : (errcode(ERRCODE_UNDEFINED_COLUMN),
13372 : errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13373 : attname)));
13374 5496 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13375 5496 : if (attform->attnum < 0)
13376 12 : ereport(ERROR,
13377 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13378 : errmsg("system columns cannot be used in foreign keys")));
13379 5484 : if (attnum >= INDEX_MAX_KEYS)
13380 0 : ereport(ERROR,
13381 : (errcode(ERRCODE_TOO_MANY_COLUMNS),
13382 : errmsg("cannot have more than %d keys in a foreign key",
13383 : INDEX_MAX_KEYS)));
13384 5484 : attnums[attnum] = attform->attnum;
13385 5484 : if (atttypids != NULL)
13386 5448 : atttypids[attnum] = attform->atttypid;
13387 5484 : if (attcollids != NULL)
13388 5448 : attcollids[attnum] = attform->attcollation;
13389 5484 : ReleaseSysCache(atttuple);
13390 5484 : attnum++;
13391 : }
13392 :
13393 6592 : return attnum;
13394 : }
13395 :
13396 : /*
13397 : * transformFkeyGetPrimaryKey -
13398 : *
13399 : * Look up the names, attnums, types, and collations of the primary key attributes
13400 : * for the pkrel. Also return the index OID and index opclasses of the
13401 : * index supporting the primary key. Also return whether the index has
13402 : * WITHOUT OVERLAPS.
13403 : *
13404 : * All parameters except pkrel are output parameters. Also, the function
13405 : * return value is the number of attributes in the primary key.
13406 : *
13407 : * Used when the column list in the REFERENCES specification is omitted.
13408 : */
13409 : static int
13410 1268 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
13411 : List **attnamelist,
13412 : int16 *attnums, Oid *atttypids, Oid *attcollids,
13413 : Oid *opclasses, bool *pk_has_without_overlaps)
13414 : {
13415 : List *indexoidlist;
13416 : ListCell *indexoidscan;
13417 1268 : HeapTuple indexTuple = NULL;
13418 1268 : Form_pg_index indexStruct = NULL;
13419 : Datum indclassDatum;
13420 : oidvector *indclass;
13421 : int i;
13422 :
13423 : /*
13424 : * Get the list of index OIDs for the table from the relcache, and look up
13425 : * each one in the pg_index syscache until we find one marked primary key
13426 : * (hopefully there isn't more than one such). Insist it's valid, too.
13427 : */
13428 1268 : *indexOid = InvalidOid;
13429 :
13430 1268 : indexoidlist = RelationGetIndexList(pkrel);
13431 :
13432 1274 : foreach(indexoidscan, indexoidlist)
13433 : {
13434 1274 : Oid indexoid = lfirst_oid(indexoidscan);
13435 :
13436 1274 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13437 1274 : if (!HeapTupleIsValid(indexTuple))
13438 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13439 1274 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13440 1274 : if (indexStruct->indisprimary && indexStruct->indisvalid)
13441 : {
13442 : /*
13443 : * Refuse to use a deferrable primary key. This is per SQL spec,
13444 : * and there would be a lot of interesting semantic problems if we
13445 : * tried to allow it.
13446 : */
13447 1268 : if (!indexStruct->indimmediate)
13448 0 : ereport(ERROR,
13449 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13450 : errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13451 : RelationGetRelationName(pkrel))));
13452 :
13453 1268 : *indexOid = indexoid;
13454 1268 : break;
13455 : }
13456 6 : ReleaseSysCache(indexTuple);
13457 : }
13458 :
13459 1268 : list_free(indexoidlist);
13460 :
13461 : /*
13462 : * Check that we found it
13463 : */
13464 1268 : if (!OidIsValid(*indexOid))
13465 0 : ereport(ERROR,
13466 : (errcode(ERRCODE_UNDEFINED_OBJECT),
13467 : errmsg("there is no primary key for referenced table \"%s\"",
13468 : RelationGetRelationName(pkrel))));
13469 :
13470 : /* Must get indclass the hard way */
13471 1268 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13472 : Anum_pg_index_indclass);
13473 1268 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13474 :
13475 : /*
13476 : * Now build the list of PK attributes from the indkey definition (we
13477 : * assume a primary key cannot have expressional elements)
13478 : */
13479 1268 : *attnamelist = NIL;
13480 3014 : for (i = 0; i < indexStruct->indnkeyatts; i++)
13481 : {
13482 1746 : int pkattno = indexStruct->indkey.values[i];
13483 :
13484 1746 : attnums[i] = pkattno;
13485 1746 : atttypids[i] = attnumTypeId(pkrel, pkattno);
13486 1746 : attcollids[i] = attnumCollationId(pkrel, pkattno);
13487 1746 : opclasses[i] = indclass->values[i];
13488 1746 : *attnamelist = lappend(*attnamelist,
13489 1746 : makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13490 : }
13491 :
13492 1268 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13493 :
13494 1268 : ReleaseSysCache(indexTuple);
13495 :
13496 1268 : return i;
13497 : }
13498 :
13499 : /*
13500 : * transformFkeyCheckAttrs -
13501 : *
13502 : * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13503 : * reference as part of a foreign key constraint.
13504 : *
13505 : * Returns the OID of the unique index supporting the constraint and
13506 : * populates the caller-provided 'opclasses' array with the opclasses
13507 : * associated with the index columns. Also sets whether the index
13508 : * uses WITHOUT OVERLAPS.
13509 : *
13510 : * Raises an ERROR on validation failure.
13511 : */
13512 : static Oid
13513 1294 : transformFkeyCheckAttrs(Relation pkrel,
13514 : int numattrs, int16 *attnums,
13515 : bool with_period, Oid *opclasses,
13516 : bool *pk_has_without_overlaps)
13517 : {
13518 1294 : Oid indexoid = InvalidOid;
13519 1294 : bool found = false;
13520 1294 : bool found_deferrable = false;
13521 : List *indexoidlist;
13522 : ListCell *indexoidscan;
13523 : int i,
13524 : j;
13525 :
13526 : /*
13527 : * Reject duplicate appearances of columns in the referenced-columns list.
13528 : * Such a case is forbidden by the SQL standard, and even if we thought it
13529 : * useful to allow it, there would be ambiguity about how to match the
13530 : * list to unique indexes (in particular, it'd be unclear which index
13531 : * opclass goes with which FK column).
13532 : */
13533 3016 : for (i = 0; i < numattrs; i++)
13534 : {
13535 2268 : for (j = i + 1; j < numattrs; j++)
13536 : {
13537 546 : if (attnums[i] == attnums[j])
13538 24 : ereport(ERROR,
13539 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13540 : errmsg("foreign key referenced-columns list must not contain duplicates")));
13541 : }
13542 : }
13543 :
13544 : /*
13545 : * Get the list of index OIDs for the table from the relcache, and look up
13546 : * each one in the pg_index syscache, and match unique indexes to the list
13547 : * of attnums we are given.
13548 : */
13549 1270 : indexoidlist = RelationGetIndexList(pkrel);
13550 :
13551 1450 : foreach(indexoidscan, indexoidlist)
13552 : {
13553 : HeapTuple indexTuple;
13554 : Form_pg_index indexStruct;
13555 :
13556 1438 : indexoid = lfirst_oid(indexoidscan);
13557 1438 : indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13558 1438 : if (!HeapTupleIsValid(indexTuple))
13559 0 : elog(ERROR, "cache lookup failed for index %u", indexoid);
13560 1438 : indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13561 :
13562 : /*
13563 : * Must have the right number of columns; must be unique (or if
13564 : * temporal then exclusion instead) and not a partial index; forget it
13565 : * if there are any expressions, too. Invalid indexes are out as well.
13566 : */
13567 2768 : if (indexStruct->indnkeyatts == numattrs &&
13568 1330 : (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13569 2632 : indexStruct->indisvalid &&
13570 2632 : heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13571 1316 : heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13572 : {
13573 : Datum indclassDatum;
13574 : oidvector *indclass;
13575 :
13576 : /* Must get indclass the hard way */
13577 1316 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13578 : Anum_pg_index_indclass);
13579 1316 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
13580 :
13581 : /*
13582 : * The given attnum list may match the index columns in any order.
13583 : * Check for a match, and extract the appropriate opclasses while
13584 : * we're at it.
13585 : *
13586 : * We know that attnums[] is duplicate-free per the test at the
13587 : * start of this function, and we checked above that the number of
13588 : * index columns agrees, so if we find a match for each attnums[]
13589 : * entry then we must have a one-to-one match in some order.
13590 : */
13591 3026 : for (i = 0; i < numattrs; i++)
13592 : {
13593 1768 : found = false;
13594 2348 : for (j = 0; j < numattrs; j++)
13595 : {
13596 2290 : if (attnums[i] == indexStruct->indkey.values[j])
13597 : {
13598 1710 : opclasses[i] = indclass->values[j];
13599 1710 : found = true;
13600 1710 : break;
13601 : }
13602 : }
13603 1768 : if (!found)
13604 58 : break;
13605 : }
13606 : /* The last attribute in the index must be the PERIOD FK part */
13607 1316 : if (found && with_period)
13608 : {
13609 122 : int16 periodattnum = attnums[numattrs - 1];
13610 :
13611 122 : found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13612 : }
13613 :
13614 : /*
13615 : * Refuse to use a deferrable unique/primary key. This is per SQL
13616 : * spec, and there would be a lot of interesting semantic problems
13617 : * if we tried to allow it.
13618 : */
13619 1316 : if (found && !indexStruct->indimmediate)
13620 : {
13621 : /*
13622 : * Remember that we found an otherwise matching index, so that
13623 : * we can generate a more appropriate error message.
13624 : */
13625 0 : found_deferrable = true;
13626 0 : found = false;
13627 : }
13628 :
13629 : /* We need to know whether the index has WITHOUT OVERLAPS */
13630 1316 : if (found)
13631 1258 : *pk_has_without_overlaps = indexStruct->indisexclusion;
13632 : }
13633 1438 : ReleaseSysCache(indexTuple);
13634 1438 : if (found)
13635 1258 : break;
13636 : }
13637 :
13638 1270 : if (!found)
13639 : {
13640 12 : if (found_deferrable)
13641 0 : ereport(ERROR,
13642 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13643 : errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13644 : RelationGetRelationName(pkrel))));
13645 : else
13646 12 : ereport(ERROR,
13647 : (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13648 : errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13649 : RelationGetRelationName(pkrel))));
13650 : }
13651 :
13652 1258 : list_free(indexoidlist);
13653 :
13654 1258 : return indexoid;
13655 : }
13656 :
13657 : /*
13658 : * findFkeyCast -
13659 : *
13660 : * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13661 : * Caller has equal regard for binary coercibility and for an exact match.
13662 : */
13663 : static CoercionPathType
13664 12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13665 : {
13666 : CoercionPathType ret;
13667 :
13668 12 : if (targetTypeId == sourceTypeId)
13669 : {
13670 12 : ret = COERCION_PATH_RELABELTYPE;
13671 12 : *funcid = InvalidOid;
13672 : }
13673 : else
13674 : {
13675 0 : ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13676 : COERCION_IMPLICIT, funcid);
13677 0 : if (ret == COERCION_PATH_NONE)
13678 : /* A previously-relied-upon cast is now gone. */
13679 0 : elog(ERROR, "could not find cast from %u to %u",
13680 : sourceTypeId, targetTypeId);
13681 : }
13682 :
13683 12 : return ret;
13684 : }
13685 :
13686 : /*
13687 : * Permissions checks on the referenced table for ADD FOREIGN KEY
13688 : *
13689 : * Note: we have already checked that the user owns the referencing table,
13690 : * else we'd have failed much earlier; no additional checks are needed for it.
13691 : */
13692 : static void
13693 2490 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13694 : {
13695 2490 : Oid roleid = GetUserId();
13696 : AclResult aclresult;
13697 : int i;
13698 :
13699 : /* Okay if we have relation-level REFERENCES permission */
13700 2490 : aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13701 : ACL_REFERENCES);
13702 2490 : if (aclresult == ACLCHECK_OK)
13703 2490 : return;
13704 : /* Else we must have REFERENCES on each column */
13705 0 : for (i = 0; i < natts; i++)
13706 : {
13707 0 : aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13708 : roleid, ACL_REFERENCES);
13709 0 : if (aclresult != ACLCHECK_OK)
13710 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13711 0 : RelationGetRelationName(rel));
13712 : }
13713 : }
13714 :
13715 : /*
13716 : * Scan the existing rows in a table to verify they meet a proposed FK
13717 : * constraint.
13718 : *
13719 : * Caller must have opened and locked both relations appropriately.
13720 : */
13721 : static void
13722 1178 : validateForeignKeyConstraint(char *conname,
13723 : Relation rel,
13724 : Relation pkrel,
13725 : Oid pkindOid,
13726 : Oid constraintOid,
13727 : bool hasperiod)
13728 : {
13729 : TupleTableSlot *slot;
13730 : TableScanDesc scan;
13731 1178 : Trigger trig = {0};
13732 : Snapshot snapshot;
13733 : MemoryContext oldcxt;
13734 : MemoryContext perTupCxt;
13735 :
13736 1178 : ereport(DEBUG1,
13737 : (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13738 :
13739 : /*
13740 : * Build a trigger call structure; we'll need it either way.
13741 : */
13742 1178 : trig.tgoid = InvalidOid;
13743 1178 : trig.tgname = conname;
13744 1178 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13745 1178 : trig.tgisinternal = true;
13746 1178 : trig.tgconstrrelid = RelationGetRelid(pkrel);
13747 1178 : trig.tgconstrindid = pkindOid;
13748 1178 : trig.tgconstraint = constraintOid;
13749 1178 : trig.tgdeferrable = false;
13750 1178 : trig.tginitdeferred = false;
13751 : /* we needn't fill in remaining fields */
13752 :
13753 : /*
13754 : * See if we can do it with a single LEFT JOIN query. A false result
13755 : * indicates we must proceed with the fire-the-trigger method. We can't do
13756 : * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13757 : * left joins.
13758 : */
13759 1178 : if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13760 984 : return;
13761 :
13762 : /*
13763 : * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13764 : * if that tuple had just been inserted. If any of those fail, it should
13765 : * ereport(ERROR) and that's that.
13766 : */
13767 108 : snapshot = RegisterSnapshot(GetLatestSnapshot());
13768 108 : slot = table_slot_create(rel, NULL);
13769 108 : scan = table_beginscan(rel, snapshot, 0, NULL);
13770 :
13771 108 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
13772 : "validateForeignKeyConstraint",
13773 : ALLOCSET_SMALL_SIZES);
13774 108 : oldcxt = MemoryContextSwitchTo(perTupCxt);
13775 :
13776 192 : while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13777 : {
13778 102 : LOCAL_FCINFO(fcinfo, 0);
13779 102 : TriggerData trigdata = {0};
13780 :
13781 102 : CHECK_FOR_INTERRUPTS();
13782 :
13783 : /*
13784 : * Make a call to the trigger function
13785 : *
13786 : * No parameters are passed, but we do set a context
13787 : */
13788 510 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13789 :
13790 : /*
13791 : * We assume RI_FKey_check_ins won't look at flinfo...
13792 : */
13793 102 : trigdata.type = T_TriggerData;
13794 102 : trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
13795 102 : trigdata.tg_relation = rel;
13796 102 : trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13797 102 : trigdata.tg_trigslot = slot;
13798 102 : trigdata.tg_trigger = &trig;
13799 :
13800 102 : fcinfo->context = (Node *) &trigdata;
13801 :
13802 102 : RI_FKey_check_ins(fcinfo);
13803 :
13804 84 : MemoryContextReset(perTupCxt);
13805 : }
13806 :
13807 90 : MemoryContextSwitchTo(oldcxt);
13808 90 : MemoryContextDelete(perTupCxt);
13809 90 : table_endscan(scan);
13810 90 : UnregisterSnapshot(snapshot);
13811 90 : ExecDropSingleTupleTableSlot(slot);
13812 : }
13813 :
13814 : /*
13815 : * CreateFKCheckTrigger
13816 : * Creates the insert (on_insert=true) or update "check" trigger that
13817 : * implements a given foreign key
13818 : *
13819 : * Returns the OID of the so created trigger.
13820 : */
13821 : static Oid
13822 6064 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13823 : Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13824 : bool on_insert)
13825 : {
13826 : ObjectAddress trigAddress;
13827 : CreateTrigStmt *fk_trigger;
13828 :
13829 : /*
13830 : * Note: for a self-referential FK (referencing and referenced tables are
13831 : * the same), it is important that the ON UPDATE action fires before the
13832 : * CHECK action, since both triggers will fire on the same row during an
13833 : * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13834 : * state of the row. Triggers fire in name order, so we ensure this by
13835 : * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13836 : * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13837 : */
13838 6064 : fk_trigger = makeNode(CreateTrigStmt);
13839 6064 : fk_trigger->replace = false;
13840 6064 : fk_trigger->isconstraint = true;
13841 6064 : fk_trigger->trigname = "RI_ConstraintTrigger_c";
13842 6064 : fk_trigger->relation = NULL;
13843 :
13844 : /* Either ON INSERT or ON UPDATE */
13845 6064 : if (on_insert)
13846 : {
13847 3032 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13848 3032 : fk_trigger->events = TRIGGER_TYPE_INSERT;
13849 : }
13850 : else
13851 : {
13852 3032 : fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13853 3032 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13854 : }
13855 :
13856 6064 : fk_trigger->args = NIL;
13857 6064 : fk_trigger->row = true;
13858 6064 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13859 6064 : fk_trigger->columns = NIL;
13860 6064 : fk_trigger->whenClause = NULL;
13861 6064 : fk_trigger->transitionRels = NIL;
13862 6064 : fk_trigger->deferrable = fkconstraint->deferrable;
13863 6064 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13864 6064 : fk_trigger->constrrel = NULL;
13865 :
13866 6064 : trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13867 : constraintOid, indexOid, InvalidOid,
13868 : parentTrigOid, NULL, true, false);
13869 :
13870 : /* Make changes-so-far visible */
13871 6064 : CommandCounterIncrement();
13872 :
13873 6064 : return trigAddress.objectId;
13874 : }
13875 :
13876 : /*
13877 : * createForeignKeyActionTriggers
13878 : * Create the referenced-side "action" triggers that implement a foreign
13879 : * key.
13880 : *
13881 : * Returns the OIDs of the so created triggers in *deleteTrigOid and
13882 : * *updateTrigOid.
13883 : */
13884 : static void
13885 3486 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13886 : Oid constraintOid, Oid indexOid,
13887 : Oid parentDelTrigger, Oid parentUpdTrigger,
13888 : Oid *deleteTrigOid, Oid *updateTrigOid)
13889 : {
13890 : CreateTrigStmt *fk_trigger;
13891 : ObjectAddress trigAddress;
13892 :
13893 : /*
13894 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13895 : * DELETE action on the referenced table.
13896 : */
13897 3486 : fk_trigger = makeNode(CreateTrigStmt);
13898 3486 : fk_trigger->replace = false;
13899 3486 : fk_trigger->isconstraint = true;
13900 3486 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13901 3486 : fk_trigger->relation = NULL;
13902 3486 : fk_trigger->args = NIL;
13903 3486 : fk_trigger->row = true;
13904 3486 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13905 3486 : fk_trigger->events = TRIGGER_TYPE_DELETE;
13906 3486 : fk_trigger->columns = NIL;
13907 3486 : fk_trigger->whenClause = NULL;
13908 3486 : fk_trigger->transitionRels = NIL;
13909 3486 : fk_trigger->constrrel = NULL;
13910 :
13911 3486 : switch (fkconstraint->fk_del_action)
13912 : {
13913 2834 : case FKCONSTR_ACTION_NOACTION:
13914 2834 : fk_trigger->deferrable = fkconstraint->deferrable;
13915 2834 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13916 2834 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13917 2834 : break;
13918 30 : case FKCONSTR_ACTION_RESTRICT:
13919 30 : fk_trigger->deferrable = false;
13920 30 : fk_trigger->initdeferred = false;
13921 30 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13922 30 : break;
13923 464 : case FKCONSTR_ACTION_CASCADE:
13924 464 : fk_trigger->deferrable = false;
13925 464 : fk_trigger->initdeferred = false;
13926 464 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13927 464 : break;
13928 98 : case FKCONSTR_ACTION_SETNULL:
13929 98 : fk_trigger->deferrable = false;
13930 98 : fk_trigger->initdeferred = false;
13931 98 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13932 98 : break;
13933 60 : case FKCONSTR_ACTION_SETDEFAULT:
13934 60 : fk_trigger->deferrable = false;
13935 60 : fk_trigger->initdeferred = false;
13936 60 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13937 60 : break;
13938 0 : default:
13939 0 : elog(ERROR, "unrecognized FK action type: %d",
13940 : (int) fkconstraint->fk_del_action);
13941 : break;
13942 : }
13943 :
13944 3486 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13945 : constraintOid, indexOid, InvalidOid,
13946 : parentDelTrigger, NULL, true, false);
13947 3486 : if (deleteTrigOid)
13948 3486 : *deleteTrigOid = trigAddress.objectId;
13949 :
13950 : /* Make changes-so-far visible */
13951 3486 : CommandCounterIncrement();
13952 :
13953 : /*
13954 : * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13955 : * UPDATE action on the referenced table.
13956 : */
13957 3486 : fk_trigger = makeNode(CreateTrigStmt);
13958 3486 : fk_trigger->replace = false;
13959 3486 : fk_trigger->isconstraint = true;
13960 3486 : fk_trigger->trigname = "RI_ConstraintTrigger_a";
13961 3486 : fk_trigger->relation = NULL;
13962 3486 : fk_trigger->args = NIL;
13963 3486 : fk_trigger->row = true;
13964 3486 : fk_trigger->timing = TRIGGER_TYPE_AFTER;
13965 3486 : fk_trigger->events = TRIGGER_TYPE_UPDATE;
13966 3486 : fk_trigger->columns = NIL;
13967 3486 : fk_trigger->whenClause = NULL;
13968 3486 : fk_trigger->transitionRels = NIL;
13969 3486 : fk_trigger->constrrel = NULL;
13970 :
13971 3486 : switch (fkconstraint->fk_upd_action)
13972 : {
13973 3028 : case FKCONSTR_ACTION_NOACTION:
13974 3028 : fk_trigger->deferrable = fkconstraint->deferrable;
13975 3028 : fk_trigger->initdeferred = fkconstraint->initdeferred;
13976 3028 : fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13977 3028 : break;
13978 36 : case FKCONSTR_ACTION_RESTRICT:
13979 36 : fk_trigger->deferrable = false;
13980 36 : fk_trigger->initdeferred = false;
13981 36 : fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13982 36 : break;
13983 318 : case FKCONSTR_ACTION_CASCADE:
13984 318 : fk_trigger->deferrable = false;
13985 318 : fk_trigger->initdeferred = false;
13986 318 : fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13987 318 : break;
13988 62 : case FKCONSTR_ACTION_SETNULL:
13989 62 : fk_trigger->deferrable = false;
13990 62 : fk_trigger->initdeferred = false;
13991 62 : fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13992 62 : break;
13993 42 : case FKCONSTR_ACTION_SETDEFAULT:
13994 42 : fk_trigger->deferrable = false;
13995 42 : fk_trigger->initdeferred = false;
13996 42 : fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13997 42 : break;
13998 0 : default:
13999 0 : elog(ERROR, "unrecognized FK action type: %d",
14000 : (int) fkconstraint->fk_upd_action);
14001 : break;
14002 : }
14003 :
14004 3486 : trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
14005 : constraintOid, indexOid, InvalidOid,
14006 : parentUpdTrigger, NULL, true, false);
14007 3486 : if (updateTrigOid)
14008 3486 : *updateTrigOid = trigAddress.objectId;
14009 3486 : }
14010 :
14011 : /*
14012 : * createForeignKeyCheckTriggers
14013 : * Create the referencing-side "check" triggers that implement a foreign
14014 : * key.
14015 : *
14016 : * Returns the OIDs of the so created triggers in *insertTrigOid and
14017 : * *updateTrigOid.
14018 : */
14019 : static void
14020 3032 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
14021 : Constraint *fkconstraint, Oid constraintOid,
14022 : Oid indexOid,
14023 : Oid parentInsTrigger, Oid parentUpdTrigger,
14024 : Oid *insertTrigOid, Oid *updateTrigOid)
14025 : {
14026 3032 : *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14027 : constraintOid, indexOid,
14028 : parentInsTrigger, true);
14029 3032 : *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14030 : constraintOid, indexOid,
14031 : parentUpdTrigger, false);
14032 3032 : }
14033 :
14034 : /*
14035 : * ALTER TABLE DROP CONSTRAINT
14036 : *
14037 : * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
14038 : */
14039 : static void
14040 812 : ATExecDropConstraint(Relation rel, const char *constrName,
14041 : DropBehavior behavior, bool recurse,
14042 : bool missing_ok, LOCKMODE lockmode)
14043 : {
14044 : Relation conrel;
14045 : SysScanDesc scan;
14046 : ScanKeyData skey[3];
14047 : HeapTuple tuple;
14048 812 : bool found = false;
14049 :
14050 812 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14051 :
14052 : /*
14053 : * Find and drop the target constraint
14054 : */
14055 812 : ScanKeyInit(&skey[0],
14056 : Anum_pg_constraint_conrelid,
14057 : BTEqualStrategyNumber, F_OIDEQ,
14058 : ObjectIdGetDatum(RelationGetRelid(rel)));
14059 812 : ScanKeyInit(&skey[1],
14060 : Anum_pg_constraint_contypid,
14061 : BTEqualStrategyNumber, F_OIDEQ,
14062 : ObjectIdGetDatum(InvalidOid));
14063 812 : ScanKeyInit(&skey[2],
14064 : Anum_pg_constraint_conname,
14065 : BTEqualStrategyNumber, F_NAMEEQ,
14066 : CStringGetDatum(constrName));
14067 812 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14068 : true, NULL, 3, skey);
14069 :
14070 : /* There can be at most one matching row */
14071 812 : if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14072 : {
14073 776 : dropconstraint_internal(rel, tuple, behavior, recurse, false,
14074 : missing_ok, lockmode);
14075 590 : found = true;
14076 : }
14077 :
14078 626 : systable_endscan(scan);
14079 :
14080 626 : if (!found)
14081 : {
14082 36 : if (!missing_ok)
14083 24 : ereport(ERROR,
14084 : errcode(ERRCODE_UNDEFINED_OBJECT),
14085 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14086 : constrName, RelationGetRelationName(rel)));
14087 : else
14088 12 : ereport(NOTICE,
14089 : errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14090 : constrName, RelationGetRelationName(rel)));
14091 : }
14092 :
14093 602 : table_close(conrel, RowExclusiveLock);
14094 602 : }
14095 :
14096 : /*
14097 : * Remove a constraint, using its pg_constraint tuple
14098 : *
14099 : * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14100 : * DROP NOT NULL.
14101 : *
14102 : * Returns the address of the constraint being removed.
14103 : */
14104 : static ObjectAddress
14105 1206 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
14106 : bool recurse, bool recursing, bool missing_ok,
14107 : LOCKMODE lockmode)
14108 : {
14109 : Relation conrel;
14110 : Form_pg_constraint con;
14111 : ObjectAddress conobj;
14112 : List *children;
14113 1206 : bool is_no_inherit_constraint = false;
14114 : char *constrName;
14115 1206 : char *colname = NULL;
14116 :
14117 : /* Guard against stack overflow due to overly deep inheritance tree. */
14118 1206 : check_stack_depth();
14119 :
14120 : /* At top level, permission check was done in ATPrepCmd, else do it */
14121 1206 : if (recursing)
14122 210 : ATSimplePermissions(AT_DropConstraint, rel,
14123 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
14124 :
14125 1200 : conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14126 :
14127 1200 : con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14128 1200 : constrName = NameStr(con->conname);
14129 :
14130 : /* Don't allow drop of inherited constraints */
14131 1200 : if (con->coninhcount > 0 && !recursing)
14132 156 : ereport(ERROR,
14133 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14134 : errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14135 : constrName, RelationGetRelationName(rel))));
14136 :
14137 : /*
14138 : * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14139 : *
14140 : * While doing that, we're in a good position to disallow dropping a not-
14141 : * null constraint underneath a primary key, a replica identity index, or
14142 : * a generated identity column.
14143 : */
14144 1044 : if (con->contype == CONSTRAINT_NOTNULL)
14145 : {
14146 314 : Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14147 314 : AttrNumber attnum = extractNotNullColumn(constraintTup);
14148 : Bitmapset *pkattrs;
14149 : Bitmapset *irattrs;
14150 : HeapTuple atttup;
14151 : Form_pg_attribute attForm;
14152 :
14153 : /* save column name for recursion step */
14154 314 : colname = get_attname(RelationGetRelid(rel), attnum, false);
14155 :
14156 : /*
14157 : * Disallow if it's in the primary key. For partitioned tables we
14158 : * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14159 : * return NULL if the primary key is invalid; but we still need to
14160 : * protect not-null constraints under such a constraint, so check the
14161 : * slow way.
14162 : */
14163 314 : pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
14164 :
14165 314 : if (pkattrs == NULL &&
14166 278 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14167 : {
14168 18 : Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14169 :
14170 18 : if (OidIsValid(pkindex))
14171 : {
14172 0 : Relation pk = relation_open(pkindex, AccessShareLock);
14173 :
14174 0 : pkattrs = NULL;
14175 0 : for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14176 0 : pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14177 :
14178 0 : relation_close(pk, AccessShareLock);
14179 : }
14180 : }
14181 :
14182 350 : if (pkattrs &&
14183 36 : bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
14184 24 : ereport(ERROR,
14185 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14186 : errmsg("column \"%s\" is in a primary key",
14187 : get_attname(RelationGetRelid(rel), attnum, false)));
14188 :
14189 : /* Disallow if it's in the replica identity */
14190 290 : irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
14191 290 : if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
14192 12 : ereport(ERROR,
14193 : errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14194 : errmsg("column \"%s\" is in index used as replica identity",
14195 : get_attname(RelationGetRelid(rel), attnum, false)));
14196 :
14197 : /* Disallow if it's a GENERATED AS IDENTITY column */
14198 278 : atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
14199 278 : if (!HeapTupleIsValid(atttup))
14200 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14201 : attnum, RelationGetRelid(rel));
14202 278 : attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14203 278 : if (attForm->attidentity != '\0')
14204 0 : ereport(ERROR,
14205 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14206 : errmsg("column \"%s\" of relation \"%s\" is an identity column",
14207 : get_attname(RelationGetRelid(rel), attnum,
14208 : false),
14209 : RelationGetRelationName(rel)));
14210 :
14211 : /* All good -- reset attnotnull if needed */
14212 278 : if (attForm->attnotnull)
14213 : {
14214 278 : attForm->attnotnull = false;
14215 278 : CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14216 : }
14217 :
14218 278 : table_close(attrel, RowExclusiveLock);
14219 : }
14220 :
14221 1008 : is_no_inherit_constraint = con->connoinherit;
14222 :
14223 : /*
14224 : * If it's a foreign-key constraint, we'd better lock the referenced table
14225 : * and check that that's not in use, just as we've already done for the
14226 : * constrained table (else we might, eg, be dropping a trigger that has
14227 : * unfired events). But we can/must skip that in the self-referential
14228 : * case.
14229 : */
14230 1008 : if (con->contype == CONSTRAINT_FOREIGN &&
14231 168 : con->confrelid != RelationGetRelid(rel))
14232 : {
14233 : Relation frel;
14234 :
14235 : /* Must match lock taken by RemoveTriggerById: */
14236 168 : frel = table_open(con->confrelid, AccessExclusiveLock);
14237 168 : CheckAlterTableIsSafe(frel);
14238 162 : table_close(frel, NoLock);
14239 : }
14240 :
14241 : /*
14242 : * Perform the actual constraint deletion
14243 : */
14244 1002 : ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14245 1002 : performDeletion(&conobj, behavior, 0);
14246 :
14247 : /*
14248 : * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14249 : * are dropped via the dependency mechanism, so we're done here.
14250 : */
14251 966 : if (con->contype != CONSTRAINT_CHECK &&
14252 630 : con->contype != CONSTRAINT_NOTNULL &&
14253 352 : rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14254 : {
14255 78 : table_close(conrel, RowExclusiveLock);
14256 78 : return conobj;
14257 : }
14258 :
14259 : /*
14260 : * Propagate to children as appropriate. Unlike most other ALTER
14261 : * routines, we have to do this one level of recursion at a time; we can't
14262 : * use find_all_inheritors to do it in one pass.
14263 : */
14264 888 : if (!is_no_inherit_constraint)
14265 602 : children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14266 : else
14267 286 : children = NIL;
14268 :
14269 2148 : foreach_oid(childrelid, children)
14270 : {
14271 : Relation childrel;
14272 : HeapTuple tuple;
14273 : Form_pg_constraint childcon;
14274 :
14275 : /* find_inheritance_children already got lock */
14276 384 : childrel = table_open(childrelid, NoLock);
14277 384 : CheckAlterTableIsSafe(childrel);
14278 :
14279 : /*
14280 : * We search for not-null constraints by column name, and others by
14281 : * constraint name.
14282 : */
14283 384 : if (con->contype == CONSTRAINT_NOTNULL)
14284 : {
14285 148 : tuple = findNotNullConstraint(childrelid, colname);
14286 148 : if (!HeapTupleIsValid(tuple))
14287 0 : elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14288 : colname, RelationGetRelid(childrel));
14289 : }
14290 : else
14291 : {
14292 : SysScanDesc scan;
14293 : ScanKeyData skey[3];
14294 :
14295 236 : ScanKeyInit(&skey[0],
14296 : Anum_pg_constraint_conrelid,
14297 : BTEqualStrategyNumber, F_OIDEQ,
14298 : ObjectIdGetDatum(childrelid));
14299 236 : ScanKeyInit(&skey[1],
14300 : Anum_pg_constraint_contypid,
14301 : BTEqualStrategyNumber, F_OIDEQ,
14302 : ObjectIdGetDatum(InvalidOid));
14303 236 : ScanKeyInit(&skey[2],
14304 : Anum_pg_constraint_conname,
14305 : BTEqualStrategyNumber, F_NAMEEQ,
14306 : CStringGetDatum(constrName));
14307 236 : scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14308 : true, NULL, 3, skey);
14309 : /* There can only be one, so no need to loop */
14310 236 : tuple = systable_getnext(scan);
14311 236 : if (!HeapTupleIsValid(tuple))
14312 0 : ereport(ERROR,
14313 : (errcode(ERRCODE_UNDEFINED_OBJECT),
14314 : errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14315 : constrName,
14316 : RelationGetRelationName(childrel))));
14317 236 : tuple = heap_copytuple(tuple);
14318 236 : systable_endscan(scan);
14319 : }
14320 :
14321 384 : childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14322 :
14323 : /* Right now only CHECK and not-null constraints can be inherited */
14324 384 : if (childcon->contype != CONSTRAINT_CHECK &&
14325 148 : childcon->contype != CONSTRAINT_NOTNULL)
14326 0 : elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14327 :
14328 384 : if (childcon->coninhcount <= 0) /* shouldn't happen */
14329 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14330 : childrelid, NameStr(childcon->conname));
14331 :
14332 384 : if (recurse)
14333 : {
14334 : /*
14335 : * If the child constraint has other definition sources, just
14336 : * decrement its inheritance count; if not, recurse to delete it.
14337 : */
14338 282 : if (childcon->coninhcount == 1 && !childcon->conislocal)
14339 : {
14340 : /* Time to delete this child constraint, too */
14341 210 : dropconstraint_internal(childrel, tuple, behavior,
14342 : recurse, true, missing_ok,
14343 : lockmode);
14344 : }
14345 : else
14346 : {
14347 : /* Child constraint must survive my deletion */
14348 72 : childcon->coninhcount--;
14349 72 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14350 :
14351 : /* Make update visible */
14352 72 : CommandCounterIncrement();
14353 : }
14354 : }
14355 : else
14356 : {
14357 : /*
14358 : * If we were told to drop ONLY in this table (no recursion) and
14359 : * there are no further parents for this constraint, we need to
14360 : * mark the inheritors' constraints as locally defined rather than
14361 : * inherited.
14362 : */
14363 102 : childcon->coninhcount--;
14364 102 : if (childcon->coninhcount == 0)
14365 102 : childcon->conislocal = true;
14366 :
14367 102 : CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14368 :
14369 : /* Make update visible */
14370 102 : CommandCounterIncrement();
14371 : }
14372 :
14373 378 : heap_freetuple(tuple);
14374 :
14375 378 : table_close(childrel, NoLock);
14376 : }
14377 :
14378 882 : table_close(conrel, RowExclusiveLock);
14379 :
14380 882 : return conobj;
14381 : }
14382 :
14383 : /*
14384 : * ALTER COLUMN TYPE
14385 : *
14386 : * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14387 : * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14388 : * transformed (and must be, because we rely on some transformed fields).
14389 : *
14390 : * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14391 : * table will be done "in parallel" during phase 3, so all the USING
14392 : * expressions should be parsed assuming the original column types. Also,
14393 : * this allows a USING expression to refer to a field that will be dropped.
14394 : *
14395 : * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14396 : * the first two execution steps in phase 2; they must not see the effects
14397 : * of any other subcommand types, since the USING expressions are parsed
14398 : * against the unmodified table's state.
14399 : */
14400 : static void
14401 1426 : ATPrepAlterColumnType(List **wqueue,
14402 : AlteredTableInfo *tab, Relation rel,
14403 : bool recurse, bool recursing,
14404 : AlterTableCmd *cmd, LOCKMODE lockmode,
14405 : AlterTableUtilityContext *context)
14406 : {
14407 1426 : char *colName = cmd->name;
14408 1426 : ColumnDef *def = (ColumnDef *) cmd->def;
14409 1426 : TypeName *typeName = def->typeName;
14410 1426 : Node *transform = def->cooked_default;
14411 : HeapTuple tuple;
14412 : Form_pg_attribute attTup;
14413 : AttrNumber attnum;
14414 : Oid targettype;
14415 : int32 targettypmod;
14416 : Oid targetcollid;
14417 : NewColumnValue *newval;
14418 1426 : ParseState *pstate = make_parsestate(NULL);
14419 : AclResult aclresult;
14420 : bool is_expr;
14421 :
14422 1426 : pstate->p_sourcetext = context->queryString;
14423 :
14424 1426 : if (rel->rd_rel->reloftype && !recursing)
14425 6 : ereport(ERROR,
14426 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14427 : errmsg("cannot alter column type of typed table"),
14428 : parser_errposition(pstate, def->location)));
14429 :
14430 : /* lookup the attribute so we can check inheritance status */
14431 1420 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14432 1420 : if (!HeapTupleIsValid(tuple))
14433 0 : ereport(ERROR,
14434 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14435 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14436 : colName, RelationGetRelationName(rel)),
14437 : parser_errposition(pstate, def->location)));
14438 1420 : attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14439 1420 : attnum = attTup->attnum;
14440 :
14441 : /* Can't alter a system attribute */
14442 1420 : if (attnum <= 0)
14443 6 : ereport(ERROR,
14444 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14445 : errmsg("cannot alter system column \"%s\"", colName),
14446 : parser_errposition(pstate, def->location)));
14447 :
14448 : /*
14449 : * Cannot specify USING when altering type of a generated column, because
14450 : * that would violate the generation expression.
14451 : */
14452 1414 : if (attTup->attgenerated && def->cooked_default)
14453 12 : ereport(ERROR,
14454 : (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14455 : errmsg("cannot specify USING when altering type of generated column"),
14456 : errdetail("Column \"%s\" is a generated column.", colName),
14457 : parser_errposition(pstate, def->location)));
14458 :
14459 : /*
14460 : * Don't alter inherited columns. At outer level, there had better not be
14461 : * any inherited definition; when recursing, we assume this was checked at
14462 : * the parent level (see below).
14463 : */
14464 1402 : if (attTup->attinhcount > 0 && !recursing)
14465 6 : ereport(ERROR,
14466 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14467 : errmsg("cannot alter inherited column \"%s\"", colName),
14468 : parser_errposition(pstate, def->location)));
14469 :
14470 : /* Don't alter columns used in the partition key */
14471 1396 : if (has_partition_attrs(rel,
14472 : bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
14473 : &is_expr))
14474 18 : ereport(ERROR,
14475 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14476 : errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14477 : colName, RelationGetRelationName(rel)),
14478 : parser_errposition(pstate, def->location)));
14479 :
14480 : /* Look up the target type */
14481 1378 : typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14482 :
14483 1372 : aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14484 1372 : if (aclresult != ACLCHECK_OK)
14485 12 : aclcheck_error_type(aclresult, targettype);
14486 :
14487 : /* And the collation */
14488 1360 : targetcollid = GetColumnDefCollation(pstate, def, targettype);
14489 :
14490 : /* make sure datatype is legal for a column */
14491 2708 : CheckAttributeType(colName, targettype, targetcollid,
14492 1354 : list_make1_oid(rel->rd_rel->reltype),
14493 1354 : (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14494 :
14495 1342 : if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14496 : {
14497 : /* do nothing */
14498 : }
14499 1306 : else if (tab->relkind == RELKIND_RELATION ||
14500 202 : tab->relkind == RELKIND_PARTITIONED_TABLE)
14501 : {
14502 : /*
14503 : * Set up an expression to transform the old data value to the new
14504 : * type. If a USING option was given, use the expression as
14505 : * transformed by transformAlterTableStmt, else just take the old
14506 : * value and try to coerce it. We do this first so that type
14507 : * incompatibility can be detected before we waste effort, and because
14508 : * we need the expression to be parsed against the original table row
14509 : * type.
14510 : */
14511 1170 : if (!transform)
14512 : {
14513 942 : transform = (Node *) makeVar(1, attnum,
14514 : attTup->atttypid, attTup->atttypmod,
14515 : attTup->attcollation,
14516 : 0);
14517 : }
14518 :
14519 1170 : transform = coerce_to_target_type(pstate,
14520 : transform, exprType(transform),
14521 : targettype, targettypmod,
14522 : COERCION_ASSIGNMENT,
14523 : COERCE_IMPLICIT_CAST,
14524 : -1);
14525 1170 : if (transform == NULL)
14526 : {
14527 : /* error text depends on whether USING was specified or not */
14528 24 : if (def->cooked_default != NULL)
14529 6 : ereport(ERROR,
14530 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14531 : errmsg("result of USING clause for column \"%s\""
14532 : " cannot be cast automatically to type %s",
14533 : colName, format_type_be(targettype)),
14534 : errhint("You might need to add an explicit cast.")));
14535 : else
14536 18 : ereport(ERROR,
14537 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14538 : errmsg("column \"%s\" cannot be cast automatically to type %s",
14539 : colName, format_type_be(targettype)),
14540 : !attTup->attgenerated ?
14541 : /* translator: USING is SQL, don't translate it */
14542 : errhint("You might need to specify \"USING %s::%s\".",
14543 : quote_identifier(colName),
14544 : format_type_with_typemod(targettype,
14545 : targettypmod)) : 0));
14546 : }
14547 :
14548 : /* Fix collations after all else */
14549 1146 : assign_expr_collations(pstate, transform);
14550 :
14551 : /* Expand virtual generated columns in the expr. */
14552 1146 : transform = expand_generated_columns_in_expr(transform, rel, 1);
14553 :
14554 : /* Plan the expr now so we can accurately assess the need to rewrite. */
14555 1146 : transform = (Node *) expression_planner((Expr *) transform);
14556 :
14557 : /*
14558 : * Add a work queue item to make ATRewriteTable update the column
14559 : * contents.
14560 : */
14561 1146 : newval = palloc0_object(NewColumnValue);
14562 1146 : newval->attnum = attnum;
14563 1146 : newval->expr = (Expr *) transform;
14564 1146 : newval->is_generated = false;
14565 :
14566 1146 : tab->newvals = lappend(tab->newvals, newval);
14567 1146 : if (ATColumnChangeRequiresRewrite(transform, attnum))
14568 944 : tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
14569 : }
14570 136 : else if (transform)
14571 12 : ereport(ERROR,
14572 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14573 : errmsg("\"%s\" is not a table",
14574 : RelationGetRelationName(rel))));
14575 :
14576 1306 : if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14577 : {
14578 : /*
14579 : * For relations or columns without storage, do this check now.
14580 : * Regular tables will check it later when the table is being
14581 : * rewritten.
14582 : */
14583 226 : find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14584 : }
14585 :
14586 1258 : ReleaseSysCache(tuple);
14587 :
14588 : /*
14589 : * Recurse manually by queueing a new command for each child, if
14590 : * necessary. We cannot apply ATSimpleRecursion here because we need to
14591 : * remap attribute numbers in the USING expression, if any.
14592 : *
14593 : * If we are told not to recurse, there had better not be any child
14594 : * tables; else the alter would put them out of step.
14595 : */
14596 1258 : if (recurse)
14597 : {
14598 1000 : Oid relid = RelationGetRelid(rel);
14599 : List *child_oids,
14600 : *child_numparents;
14601 : ListCell *lo,
14602 : *li;
14603 :
14604 1000 : child_oids = find_all_inheritors(relid, lockmode,
14605 : &child_numparents);
14606 :
14607 : /*
14608 : * find_all_inheritors does the recursive search of the inheritance
14609 : * hierarchy, so all we have to do is process all of the relids in the
14610 : * list that it returns.
14611 : */
14612 2208 : forboth(lo, child_oids, li, child_numparents)
14613 : {
14614 1232 : Oid childrelid = lfirst_oid(lo);
14615 1232 : int numparents = lfirst_int(li);
14616 : Relation childrel;
14617 : HeapTuple childtuple;
14618 : Form_pg_attribute childattTup;
14619 :
14620 1232 : if (childrelid == relid)
14621 1000 : continue;
14622 :
14623 : /* find_all_inheritors already got lock */
14624 232 : childrel = relation_open(childrelid, NoLock);
14625 232 : CheckAlterTableIsSafe(childrel);
14626 :
14627 : /*
14628 : * Verify that the child doesn't have any inherited definitions of
14629 : * this column that came from outside this inheritance hierarchy.
14630 : * (renameatt makes a similar test, though in a different way
14631 : * because of its different recursion mechanism.)
14632 : */
14633 232 : childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14634 : colName);
14635 232 : if (!HeapTupleIsValid(childtuple))
14636 0 : ereport(ERROR,
14637 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14638 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14639 : colName, RelationGetRelationName(childrel))));
14640 232 : childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14641 :
14642 232 : if (childattTup->attinhcount > numparents)
14643 6 : ereport(ERROR,
14644 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14645 : errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14646 : colName, RelationGetRelationName(childrel))));
14647 :
14648 226 : ReleaseSysCache(childtuple);
14649 :
14650 : /*
14651 : * Remap the attribute numbers. If no USING expression was
14652 : * specified, there is no need for this step.
14653 : */
14654 226 : if (def->cooked_default)
14655 : {
14656 : AttrMap *attmap;
14657 : bool found_whole_row;
14658 :
14659 : /* create a copy to scribble on */
14660 78 : cmd = copyObject(cmd);
14661 :
14662 78 : attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14663 : RelationGetDescr(rel),
14664 : false);
14665 156 : ((ColumnDef *) cmd->def)->cooked_default =
14666 78 : map_variable_attnos(def->cooked_default,
14667 : 1, 0,
14668 : attmap,
14669 : InvalidOid, &found_whole_row);
14670 78 : if (found_whole_row)
14671 6 : ereport(ERROR,
14672 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14673 : errmsg("cannot convert whole-row table reference"),
14674 : errdetail("USING expression contains a whole-row table reference.")));
14675 72 : pfree(attmap);
14676 : }
14677 220 : ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14678 208 : relation_close(childrel, NoLock);
14679 : }
14680 : }
14681 308 : else if (!recursing &&
14682 50 : find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
14683 0 : ereport(ERROR,
14684 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14685 : errmsg("type of inherited column \"%s\" must be changed in child tables too",
14686 : colName)));
14687 :
14688 1234 : if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14689 50 : ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14690 1228 : }
14691 :
14692 : /*
14693 : * When the data type of a column is changed, a rewrite might not be required
14694 : * if the new type is sufficiently identical to the old one, and the USING
14695 : * clause isn't trying to insert some other value. It's safe to skip the
14696 : * rewrite in these cases:
14697 : *
14698 : * - the old type is binary coercible to the new type
14699 : * - the new type is an unconstrained domain over the old type
14700 : * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14701 : *
14702 : * In the case of a constrained domain, we could get by with scanning the
14703 : * table and checking the constraint rather than actually rewriting it, but we
14704 : * don't currently try to do that.
14705 : */
14706 : static bool
14707 1146 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
14708 : {
14709 : Assert(expr != NULL);
14710 :
14711 : for (;;)
14712 : {
14713 : /* only one varno, so no need to check that */
14714 1264 : if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14715 202 : return false;
14716 1062 : else if (IsA(expr, RelabelType))
14717 106 : expr = (Node *) ((RelabelType *) expr)->arg;
14718 956 : else if (IsA(expr, CoerceToDomain))
14719 : {
14720 0 : CoerceToDomain *d = (CoerceToDomain *) expr;
14721 :
14722 0 : if (DomainHasConstraints(d->resulttype))
14723 0 : return true;
14724 0 : expr = (Node *) d->arg;
14725 : }
14726 956 : else if (IsA(expr, FuncExpr))
14727 : {
14728 750 : FuncExpr *f = (FuncExpr *) expr;
14729 :
14730 750 : switch (f->funcid)
14731 : {
14732 18 : case F_TIMESTAMPTZ_TIMESTAMP:
14733 : case F_TIMESTAMP_TIMESTAMPTZ:
14734 18 : if (TimestampTimestampTzRequiresRewrite())
14735 6 : return true;
14736 : else
14737 12 : expr = linitial(f->args);
14738 12 : break;
14739 732 : default:
14740 732 : return true;
14741 : }
14742 : }
14743 : else
14744 206 : return true;
14745 : }
14746 : }
14747 :
14748 : /*
14749 : * ALTER COLUMN .. SET DATA TYPE
14750 : *
14751 : * Return the address of the modified column.
14752 : */
14753 : static ObjectAddress
14754 1192 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
14755 : AlterTableCmd *cmd, LOCKMODE lockmode)
14756 : {
14757 1192 : char *colName = cmd->name;
14758 1192 : ColumnDef *def = (ColumnDef *) cmd->def;
14759 1192 : TypeName *typeName = def->typeName;
14760 : HeapTuple heapTup;
14761 : Form_pg_attribute attTup,
14762 : attOldTup;
14763 : AttrNumber attnum;
14764 : HeapTuple typeTuple;
14765 : Form_pg_type tform;
14766 : Oid targettype;
14767 : int32 targettypmod;
14768 : Oid targetcollid;
14769 : Node *defaultexpr;
14770 : Relation attrelation;
14771 : Relation depRel;
14772 : ScanKeyData key[3];
14773 : SysScanDesc scan;
14774 : HeapTuple depTup;
14775 : ObjectAddress address;
14776 :
14777 : /*
14778 : * Clear all the missing values if we're rewriting the table, since this
14779 : * renders them pointless.
14780 : */
14781 1192 : if (tab->rewrite)
14782 : {
14783 : Relation newrel;
14784 :
14785 884 : newrel = table_open(RelationGetRelid(rel), NoLock);
14786 884 : RelationClearMissing(newrel);
14787 884 : relation_close(newrel, NoLock);
14788 : /* make sure we don't conflict with later attribute modifications */
14789 884 : CommandCounterIncrement();
14790 : }
14791 :
14792 1192 : attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14793 :
14794 : /* Look up the target column */
14795 1192 : heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14796 1192 : if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14797 0 : ereport(ERROR,
14798 : (errcode(ERRCODE_UNDEFINED_COLUMN),
14799 : errmsg("column \"%s\" of relation \"%s\" does not exist",
14800 : colName, RelationGetRelationName(rel))));
14801 1192 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14802 1192 : attnum = attTup->attnum;
14803 1192 : attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14804 :
14805 : /* Check for multiple ALTER TYPE on same column --- can't cope */
14806 1192 : if (attTup->atttypid != attOldTup->atttypid ||
14807 1192 : attTup->atttypmod != attOldTup->atttypmod)
14808 0 : ereport(ERROR,
14809 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14810 : errmsg("cannot alter type of column \"%s\" twice",
14811 : colName)));
14812 :
14813 : /* Look up the target type (should not fail, since prep found it) */
14814 1192 : typeTuple = typenameType(NULL, typeName, &targettypmod);
14815 1192 : tform = (Form_pg_type) GETSTRUCT(typeTuple);
14816 1192 : targettype = tform->oid;
14817 : /* And the collation */
14818 1192 : targetcollid = GetColumnDefCollation(NULL, def, targettype);
14819 :
14820 : /*
14821 : * If there is a default expression for the column, get it and ensure we
14822 : * can coerce it to the new datatype. (We must do this before changing
14823 : * the column type, because build_column_default itself will try to
14824 : * coerce, and will not issue the error message we want if it fails.)
14825 : *
14826 : * We remove any implicit coercion steps at the top level of the old
14827 : * default expression; this has been agreed to satisfy the principle of
14828 : * least surprise. (The conversion to the new column type should act like
14829 : * it started from what the user sees as the stored expression, and the
14830 : * implicit coercions aren't going to be shown.)
14831 : */
14832 1192 : if (attTup->atthasdef)
14833 : {
14834 92 : defaultexpr = build_column_default(rel, attnum);
14835 : Assert(defaultexpr);
14836 92 : defaultexpr = strip_implicit_coercions(defaultexpr);
14837 92 : defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14838 : defaultexpr, exprType(defaultexpr),
14839 : targettype, targettypmod,
14840 : COERCION_ASSIGNMENT,
14841 : COERCE_IMPLICIT_CAST,
14842 : -1);
14843 92 : if (defaultexpr == NULL)
14844 : {
14845 6 : if (attTup->attgenerated)
14846 0 : ereport(ERROR,
14847 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14848 : errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14849 : colName, format_type_be(targettype))));
14850 : else
14851 6 : ereport(ERROR,
14852 : (errcode(ERRCODE_DATATYPE_MISMATCH),
14853 : errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14854 : colName, format_type_be(targettype))));
14855 : }
14856 : }
14857 : else
14858 1100 : defaultexpr = NULL;
14859 :
14860 : /*
14861 : * Find everything that depends on the column (constraints, indexes, etc),
14862 : * and record enough information to let us recreate the objects.
14863 : *
14864 : * The actual recreation does not happen here, but only after we have
14865 : * performed all the individual ALTER TYPE operations. We have to save
14866 : * the info before executing ALTER TYPE, though, else the deparser will
14867 : * get confused.
14868 : */
14869 1186 : RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
14870 :
14871 : /*
14872 : * Now scan for dependencies of this column on other things. The only
14873 : * things we should find are the dependency on the column datatype and
14874 : * possibly a collation dependency. Those can be removed.
14875 : */
14876 1150 : depRel = table_open(DependRelationId, RowExclusiveLock);
14877 :
14878 1150 : ScanKeyInit(&key[0],
14879 : Anum_pg_depend_classid,
14880 : BTEqualStrategyNumber, F_OIDEQ,
14881 : ObjectIdGetDatum(RelationRelationId));
14882 1150 : ScanKeyInit(&key[1],
14883 : Anum_pg_depend_objid,
14884 : BTEqualStrategyNumber, F_OIDEQ,
14885 : ObjectIdGetDatum(RelationGetRelid(rel)));
14886 1150 : ScanKeyInit(&key[2],
14887 : Anum_pg_depend_objsubid,
14888 : BTEqualStrategyNumber, F_INT4EQ,
14889 : Int32GetDatum((int32) attnum));
14890 :
14891 1150 : scan = systable_beginscan(depRel, DependDependerIndexId, true,
14892 : NULL, 3, key);
14893 :
14894 1154 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14895 : {
14896 4 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14897 : ObjectAddress foundObject;
14898 :
14899 4 : foundObject.classId = foundDep->refclassid;
14900 4 : foundObject.objectId = foundDep->refobjid;
14901 4 : foundObject.objectSubId = foundDep->refobjsubid;
14902 :
14903 4 : if (foundDep->deptype != DEPENDENCY_NORMAL)
14904 0 : elog(ERROR, "found unexpected dependency type '%c'",
14905 : foundDep->deptype);
14906 4 : if (!(foundDep->refclassid == TypeRelationId &&
14907 4 : foundDep->refobjid == attTup->atttypid) &&
14908 0 : !(foundDep->refclassid == CollationRelationId &&
14909 0 : foundDep->refobjid == attTup->attcollation))
14910 0 : elog(ERROR, "found unexpected dependency for column: %s",
14911 : getObjectDescription(&foundObject, false));
14912 :
14913 4 : CatalogTupleDelete(depRel, &depTup->t_self);
14914 : }
14915 :
14916 1150 : systable_endscan(scan);
14917 :
14918 1150 : table_close(depRel, RowExclusiveLock);
14919 :
14920 : /*
14921 : * Here we go --- change the recorded column type and collation. (Note
14922 : * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14923 : * fix up the missing value if any.
14924 : */
14925 1150 : if (attTup->atthasmissing)
14926 : {
14927 : Datum missingval;
14928 : bool missingNull;
14929 :
14930 : /* if rewrite is true the missing value should already be cleared */
14931 : Assert(tab->rewrite == 0);
14932 :
14933 : /* Get the missing value datum */
14934 6 : missingval = heap_getattr(heapTup,
14935 : Anum_pg_attribute_attmissingval,
14936 : attrelation->rd_att,
14937 : &missingNull);
14938 :
14939 : /* if it's a null array there is nothing to do */
14940 :
14941 6 : if (!missingNull)
14942 : {
14943 : /*
14944 : * Get the datum out of the array and repack it in a new array
14945 : * built with the new type data. We assume that since the table
14946 : * doesn't need rewriting, the actual Datum doesn't need to be
14947 : * changed, only the array metadata.
14948 : */
14949 :
14950 6 : int one = 1;
14951 : bool isNull;
14952 6 : Datum valuesAtt[Natts_pg_attribute] = {0};
14953 6 : bool nullsAtt[Natts_pg_attribute] = {0};
14954 6 : bool replacesAtt[Natts_pg_attribute] = {0};
14955 : HeapTuple newTup;
14956 :
14957 12 : missingval = array_get_element(missingval,
14958 : 1,
14959 : &one,
14960 : 0,
14961 6 : attTup->attlen,
14962 6 : attTup->attbyval,
14963 6 : attTup->attalign,
14964 : &isNull);
14965 6 : missingval = PointerGetDatum(construct_array(&missingval,
14966 : 1,
14967 : targettype,
14968 6 : tform->typlen,
14969 6 : tform->typbyval,
14970 6 : tform->typalign));
14971 :
14972 6 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14973 6 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14974 6 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14975 :
14976 6 : newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14977 : valuesAtt, nullsAtt, replacesAtt);
14978 6 : heap_freetuple(heapTup);
14979 6 : heapTup = newTup;
14980 6 : attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14981 : }
14982 : }
14983 :
14984 1150 : attTup->atttypid = targettype;
14985 1150 : attTup->atttypmod = targettypmod;
14986 1150 : attTup->attcollation = targetcollid;
14987 1150 : if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14988 0 : ereport(ERROR,
14989 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14990 : errmsg("too many array dimensions"));
14991 1150 : attTup->attndims = list_length(typeName->arrayBounds);
14992 1150 : attTup->attlen = tform->typlen;
14993 1150 : attTup->attbyval = tform->typbyval;
14994 1150 : attTup->attalign = tform->typalign;
14995 1150 : attTup->attstorage = tform->typstorage;
14996 1150 : attTup->attcompression = InvalidCompressionMethod;
14997 :
14998 1150 : ReleaseSysCache(typeTuple);
14999 :
15000 1150 : CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
15001 :
15002 1150 : table_close(attrelation, RowExclusiveLock);
15003 :
15004 : /* Install dependencies on new datatype and collation */
15005 1150 : add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
15006 1150 : add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
15007 :
15008 : /*
15009 : * Drop any pg_statistic entry for the column, since it's now wrong type
15010 : */
15011 1150 : RemoveStatistics(RelationGetRelid(rel), attnum);
15012 :
15013 1150 : InvokeObjectPostAlterHook(RelationRelationId,
15014 : RelationGetRelid(rel), attnum);
15015 :
15016 : /*
15017 : * Update the default, if present, by brute force --- remove and re-add
15018 : * the default. Probably unsafe to take shortcuts, since the new version
15019 : * may well have additional dependencies. (It's okay to do this now,
15020 : * rather than after other ALTER TYPE commands, since the default won't
15021 : * depend on other column types.)
15022 : */
15023 1150 : if (defaultexpr)
15024 : {
15025 : /*
15026 : * If it's a GENERATED default, drop its dependency records, in
15027 : * particular its INTERNAL dependency on the column, which would
15028 : * otherwise cause dependency.c to refuse to perform the deletion.
15029 : */
15030 86 : if (attTup->attgenerated)
15031 : {
15032 36 : Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
15033 :
15034 36 : if (!OidIsValid(attrdefoid))
15035 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15036 : RelationGetRelid(rel), attnum);
15037 36 : (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
15038 : }
15039 :
15040 : /*
15041 : * Make updates-so-far visible, particularly the new pg_attribute row
15042 : * which will be updated again.
15043 : */
15044 86 : CommandCounterIncrement();
15045 :
15046 : /*
15047 : * We use RESTRICT here for safety, but at present we do not expect
15048 : * anything to depend on the default.
15049 : */
15050 86 : RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
15051 : true);
15052 :
15053 86 : (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15054 : }
15055 :
15056 1150 : ObjectAddressSubSet(address, RelationRelationId,
15057 : RelationGetRelid(rel), attnum);
15058 :
15059 : /* Cleanup */
15060 1150 : heap_freetuple(heapTup);
15061 :
15062 1150 : return address;
15063 : }
15064 :
15065 : /*
15066 : * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
15067 : * that depends on the column (constraints, indexes, etc), and record enough
15068 : * information to let us recreate the objects.
15069 : */
15070 : static void
15071 1290 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
15072 : Relation rel, AttrNumber attnum, const char *colName)
15073 : {
15074 : Relation depRel;
15075 : ScanKeyData key[3];
15076 : SysScanDesc scan;
15077 : HeapTuple depTup;
15078 :
15079 : Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15080 :
15081 1290 : depRel = table_open(DependRelationId, RowExclusiveLock);
15082 :
15083 1290 : ScanKeyInit(&key[0],
15084 : Anum_pg_depend_refclassid,
15085 : BTEqualStrategyNumber, F_OIDEQ,
15086 : ObjectIdGetDatum(RelationRelationId));
15087 1290 : ScanKeyInit(&key[1],
15088 : Anum_pg_depend_refobjid,
15089 : BTEqualStrategyNumber, F_OIDEQ,
15090 : ObjectIdGetDatum(RelationGetRelid(rel)));
15091 1290 : ScanKeyInit(&key[2],
15092 : Anum_pg_depend_refobjsubid,
15093 : BTEqualStrategyNumber, F_INT4EQ,
15094 : Int32GetDatum((int32) attnum));
15095 :
15096 1290 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15097 : NULL, 3, key);
15098 :
15099 2526 : while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15100 : {
15101 1272 : Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15102 : ObjectAddress foundObject;
15103 :
15104 1272 : foundObject.classId = foundDep->classid;
15105 1272 : foundObject.objectId = foundDep->objid;
15106 1272 : foundObject.objectSubId = foundDep->objsubid;
15107 :
15108 1272 : switch (foundObject.classId)
15109 : {
15110 286 : case RelationRelationId:
15111 : {
15112 286 : char relKind = get_rel_relkind(foundObject.objectId);
15113 :
15114 286 : if (relKind == RELKIND_INDEX ||
15115 : relKind == RELKIND_PARTITIONED_INDEX)
15116 : {
15117 : Assert(foundObject.objectSubId == 0);
15118 248 : RememberIndexForRebuilding(foundObject.objectId, tab);
15119 : }
15120 38 : else if (relKind == RELKIND_SEQUENCE)
15121 : {
15122 : /*
15123 : * This must be a SERIAL column's sequence. We need
15124 : * not do anything to it.
15125 : */
15126 : Assert(foundObject.objectSubId == 0);
15127 : }
15128 : else
15129 : {
15130 : /* Not expecting any other direct dependencies... */
15131 0 : elog(ERROR, "unexpected object depending on column: %s",
15132 : getObjectDescription(&foundObject, false));
15133 : }
15134 286 : break;
15135 : }
15136 :
15137 686 : case ConstraintRelationId:
15138 : Assert(foundObject.objectSubId == 0);
15139 686 : RememberConstraintForRebuilding(foundObject.objectId, tab);
15140 686 : break;
15141 :
15142 0 : case ProcedureRelationId:
15143 :
15144 : /*
15145 : * A new-style SQL function can depend on a column, if that
15146 : * column is referenced in the parsed function body. Ideally
15147 : * we'd automatically update the function by deparsing and
15148 : * reparsing it, but that's risky and might well fail anyhow.
15149 : * FIXME someday.
15150 : *
15151 : * This is only a problem for AT_AlterColumnType, not
15152 : * AT_SetExpression.
15153 : */
15154 0 : if (subtype == AT_AlterColumnType)
15155 0 : ereport(ERROR,
15156 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15157 : errmsg("cannot alter type of a column used by a function or procedure"),
15158 : errdetail("%s depends on column \"%s\"",
15159 : getObjectDescription(&foundObject, false),
15160 : colName)));
15161 0 : break;
15162 :
15163 12 : case RewriteRelationId:
15164 :
15165 : /*
15166 : * View/rule bodies have pretty much the same issues as
15167 : * function bodies. FIXME someday.
15168 : */
15169 12 : if (subtype == AT_AlterColumnType)
15170 12 : ereport(ERROR,
15171 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15172 : errmsg("cannot alter type of a column used by a view or rule"),
15173 : errdetail("%s depends on column \"%s\"",
15174 : getObjectDescription(&foundObject, false),
15175 : colName)));
15176 0 : break;
15177 :
15178 0 : case TriggerRelationId:
15179 :
15180 : /*
15181 : * A trigger can depend on a column because the column is
15182 : * specified as an update target, or because the column is
15183 : * used in the trigger's WHEN condition. The first case would
15184 : * not require any extra work, but the second case would
15185 : * require updating the WHEN expression, which has the same
15186 : * issues as above. Since we can't easily tell which case
15187 : * applies, we punt for both. FIXME someday.
15188 : */
15189 0 : if (subtype == AT_AlterColumnType)
15190 0 : ereport(ERROR,
15191 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15192 : errmsg("cannot alter type of a column used in a trigger definition"),
15193 : errdetail("%s depends on column \"%s\"",
15194 : getObjectDescription(&foundObject, false),
15195 : colName)));
15196 0 : break;
15197 :
15198 0 : case PolicyRelationId:
15199 :
15200 : /*
15201 : * A policy can depend on a column because the column is
15202 : * specified in the policy's USING or WITH CHECK qual
15203 : * expressions. It might be possible to rewrite and recheck
15204 : * the policy expression, but punt for now. It's certainly
15205 : * easy enough to remove and recreate the policy; still, FIXME
15206 : * someday.
15207 : */
15208 0 : if (subtype == AT_AlterColumnType)
15209 0 : ereport(ERROR,
15210 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15211 : errmsg("cannot alter type of a column used in a policy definition"),
15212 : errdetail("%s depends on column \"%s\"",
15213 : getObjectDescription(&foundObject, false),
15214 : colName)));
15215 0 : break;
15216 :
15217 214 : case AttrDefaultRelationId:
15218 : {
15219 214 : ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
15220 :
15221 214 : if (col.objectId == RelationGetRelid(rel) &&
15222 214 : col.objectSubId == attnum)
15223 : {
15224 : /*
15225 : * Ignore the column's own default expression. The
15226 : * caller deals with it.
15227 : */
15228 : }
15229 : else
15230 : {
15231 : /*
15232 : * This must be a reference from the expression of a
15233 : * generated column elsewhere in the same table.
15234 : * Changing the type/generated expression of a column
15235 : * that is used by a generated column is not allowed
15236 : * by SQL standard, so just punt for now. It might be
15237 : * doable with some thinking and effort.
15238 : */
15239 24 : if (subtype == AT_AlterColumnType)
15240 24 : ereport(ERROR,
15241 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15242 : errmsg("cannot alter type of a column used by a generated column"),
15243 : errdetail("Column \"%s\" is used by generated column \"%s\".",
15244 : colName,
15245 : get_attname(col.objectId,
15246 : col.objectSubId,
15247 : false))));
15248 : }
15249 190 : break;
15250 : }
15251 :
15252 74 : case StatisticExtRelationId:
15253 :
15254 : /*
15255 : * Give the extended-stats machinery a chance to fix anything
15256 : * that this column type change would break.
15257 : */
15258 74 : RememberStatisticsForRebuilding(foundObject.objectId, tab);
15259 74 : break;
15260 :
15261 0 : case PublicationRelRelationId:
15262 :
15263 : /*
15264 : * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15265 : * clause. Same issues as above. FIXME someday.
15266 : */
15267 0 : if (subtype == AT_AlterColumnType)
15268 0 : ereport(ERROR,
15269 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15270 : errmsg("cannot alter type of a column used by a publication WHERE clause"),
15271 : errdetail("%s depends on column \"%s\"",
15272 : getObjectDescription(&foundObject, false),
15273 : colName)));
15274 0 : break;
15275 :
15276 0 : default:
15277 :
15278 : /*
15279 : * We don't expect any other sorts of objects to depend on a
15280 : * column.
15281 : */
15282 0 : elog(ERROR, "unexpected object depending on column: %s",
15283 : getObjectDescription(&foundObject, false));
15284 : break;
15285 : }
15286 : }
15287 :
15288 1254 : systable_endscan(scan);
15289 1254 : table_close(depRel, NoLock);
15290 1254 : }
15291 :
15292 : /*
15293 : * Subroutine for ATExecAlterColumnType: remember that a replica identity
15294 : * needs to be reset.
15295 : */
15296 : static void
15297 456 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
15298 : {
15299 456 : if (!get_index_isreplident(indoid))
15300 438 : return;
15301 :
15302 18 : if (tab->replicaIdentityIndex)
15303 0 : elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15304 :
15305 18 : tab->replicaIdentityIndex = get_rel_name(indoid);
15306 : }
15307 :
15308 : /*
15309 : * Subroutine for ATExecAlterColumnType: remember any clustered index.
15310 : */
15311 : static void
15312 456 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
15313 : {
15314 456 : if (!get_index_isclustered(indoid))
15315 438 : return;
15316 :
15317 18 : if (tab->clusterOnIndex)
15318 0 : elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15319 :
15320 18 : tab->clusterOnIndex = get_rel_name(indoid);
15321 : }
15322 :
15323 : /*
15324 : * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15325 : * to be rebuilt (which we might already know).
15326 : */
15327 : static void
15328 698 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
15329 : {
15330 : /*
15331 : * This de-duplication check is critical for two independent reasons: we
15332 : * mustn't try to recreate the same constraint twice, and if a constraint
15333 : * depends on more than one column whose type is to be altered, we must
15334 : * capture its definition string before applying any of the column type
15335 : * changes. ruleutils.c will get confused if we ask again later.
15336 : */
15337 698 : if (!list_member_oid(tab->changedConstraintOids, conoid))
15338 : {
15339 : /* OK, capture the constraint's existing definition string */
15340 608 : char *defstring = pg_get_constraintdef_command(conoid);
15341 : Oid indoid;
15342 :
15343 : /*
15344 : * It is critical to create not-null constraints ahead of primary key
15345 : * indexes; otherwise, the not-null constraint would be created by the
15346 : * primary key, and the constraint name would be wrong.
15347 : */
15348 608 : if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15349 : {
15350 198 : tab->changedConstraintOids = lcons_oid(conoid,
15351 : tab->changedConstraintOids);
15352 198 : tab->changedConstraintDefs = lcons(defstring,
15353 : tab->changedConstraintDefs);
15354 : }
15355 : else
15356 : {
15357 :
15358 410 : tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
15359 : conoid);
15360 410 : tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
15361 : defstring);
15362 : }
15363 :
15364 : /*
15365 : * For the index of a constraint, if any, remember if it is used for
15366 : * the table's replica identity or if it is a clustered index, so that
15367 : * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15368 : * those properties.
15369 : */
15370 608 : indoid = get_constraint_index(conoid);
15371 608 : if (OidIsValid(indoid))
15372 : {
15373 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
15374 228 : RememberClusterOnForRebuilding(indoid, tab);
15375 : }
15376 : }
15377 698 : }
15378 :
15379 : /*
15380 : * Subroutine for ATExecAlterColumnType: remember that an index needs
15381 : * to be rebuilt (which we might already know).
15382 : */
15383 : static void
15384 248 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
15385 : {
15386 : /*
15387 : * This de-duplication check is critical for two independent reasons: we
15388 : * mustn't try to recreate the same index twice, and if an index depends
15389 : * on more than one column whose type is to be altered, we must capture
15390 : * its definition string before applying any of the column type changes.
15391 : * ruleutils.c will get confused if we ask again later.
15392 : */
15393 248 : if (!list_member_oid(tab->changedIndexOids, indoid))
15394 : {
15395 : /*
15396 : * Before adding it as an index-to-rebuild, we'd better see if it
15397 : * belongs to a constraint, and if so rebuild the constraint instead.
15398 : * Typically this check fails, because constraint indexes normally
15399 : * have only dependencies on their constraint. But it's possible for
15400 : * such an index to also have direct dependencies on table columns,
15401 : * for example with a partial exclusion constraint.
15402 : */
15403 240 : Oid conoid = get_index_constraint(indoid);
15404 :
15405 240 : if (OidIsValid(conoid))
15406 : {
15407 12 : RememberConstraintForRebuilding(conoid, tab);
15408 : }
15409 : else
15410 : {
15411 : /* OK, capture the index's existing definition string */
15412 228 : char *defstring = pg_get_indexdef_string(indoid);
15413 :
15414 228 : tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
15415 : indoid);
15416 228 : tab->changedIndexDefs = lappend(tab->changedIndexDefs,
15417 : defstring);
15418 :
15419 : /*
15420 : * Remember if this index is used for the table's replica identity
15421 : * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15422 : * can queue up commands necessary to restore those properties.
15423 : */
15424 228 : RememberReplicaIdentityForRebuilding(indoid, tab);
15425 228 : RememberClusterOnForRebuilding(indoid, tab);
15426 : }
15427 : }
15428 248 : }
15429 :
15430 : /*
15431 : * Subroutine for ATExecAlterColumnType: remember that a statistics object
15432 : * needs to be rebuilt (which we might already know).
15433 : */
15434 : static void
15435 74 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
15436 : {
15437 : /*
15438 : * This de-duplication check is critical for two independent reasons: we
15439 : * mustn't try to recreate the same statistics object twice, and if the
15440 : * statistics object depends on more than one column whose type is to be
15441 : * altered, we must capture its definition string before applying any of
15442 : * the type changes. ruleutils.c will get confused if we ask again later.
15443 : */
15444 74 : if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15445 : {
15446 : /* OK, capture the statistics object's existing definition string */
15447 74 : char *defstring = pg_get_statisticsobjdef_string(stxoid);
15448 :
15449 74 : tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
15450 : stxoid);
15451 74 : tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
15452 : defstring);
15453 : }
15454 74 : }
15455 :
15456 : /*
15457 : * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15458 : * operations for a particular relation. We have to drop and recreate all the
15459 : * indexes and constraints that depend on the altered columns. We do the
15460 : * actual dropping here, but re-creation is managed by adding work queue
15461 : * entries to do those steps later.
15462 : */
15463 : static void
15464 1302 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
15465 : {
15466 : ObjectAddress obj;
15467 : ObjectAddresses *objects;
15468 : ListCell *def_item;
15469 : ListCell *oid_item;
15470 :
15471 : /*
15472 : * Collect all the constraints and indexes to drop so we can process them
15473 : * in a single call. That way we don't have to worry about dependencies
15474 : * among them.
15475 : */
15476 1302 : objects = new_object_addresses();
15477 :
15478 : /*
15479 : * Re-parse the index and constraint definitions, and attach them to the
15480 : * appropriate work queue entries. We do this before dropping because in
15481 : * the case of a constraint on another table, we might not yet have
15482 : * exclusive lock on the table the constraint is attached to, and we need
15483 : * to get that before reparsing/dropping. (That's possible at least for
15484 : * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15485 : * requires a dependency on the target table's composite type in the other
15486 : * table's constraint expressions.)
15487 : *
15488 : * We can't rely on the output of deparsing to tell us which relation to
15489 : * operate on, because concurrent activity might have made the name
15490 : * resolve differently. Instead, we've got to use the OID of the
15491 : * constraint or index we're processing to figure out which relation to
15492 : * operate on.
15493 : */
15494 1910 : forboth(oid_item, tab->changedConstraintOids,
15495 : def_item, tab->changedConstraintDefs)
15496 : {
15497 608 : Oid oldId = lfirst_oid(oid_item);
15498 : HeapTuple tup;
15499 : Form_pg_constraint con;
15500 : Oid relid;
15501 : Oid confrelid;
15502 : bool conislocal;
15503 :
15504 608 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15505 608 : if (!HeapTupleIsValid(tup)) /* should not happen */
15506 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15507 608 : con = (Form_pg_constraint) GETSTRUCT(tup);
15508 608 : if (OidIsValid(con->conrelid))
15509 594 : relid = con->conrelid;
15510 : else
15511 : {
15512 : /* must be a domain constraint */
15513 14 : relid = get_typ_typrelid(getBaseType(con->contypid));
15514 14 : if (!OidIsValid(relid))
15515 0 : elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15516 : }
15517 608 : confrelid = con->confrelid;
15518 608 : conislocal = con->conislocal;
15519 608 : ReleaseSysCache(tup);
15520 :
15521 608 : ObjectAddressSet(obj, ConstraintRelationId, oldId);
15522 608 : add_exact_object_address(&obj, objects);
15523 :
15524 : /*
15525 : * If the constraint is inherited (only), we don't want to inject a
15526 : * new definition here; it'll get recreated when
15527 : * ATAddCheckNNConstraint recurses from adding the parent table's
15528 : * constraint. But we had to carry the info this far so that we can
15529 : * drop the constraint below.
15530 : */
15531 608 : if (!conislocal)
15532 28 : continue;
15533 :
15534 : /*
15535 : * When rebuilding another table's constraint that references the
15536 : * table we're modifying, we might not yet have any lock on the other
15537 : * table, so get one now. We'll need AccessExclusiveLock for the DROP
15538 : * CONSTRAINT step, so there's no value in asking for anything weaker.
15539 : */
15540 580 : if (relid != tab->relid)
15541 48 : LockRelationOid(relid, AccessExclusiveLock);
15542 :
15543 580 : ATPostAlterTypeParse(oldId, relid, confrelid,
15544 580 : (char *) lfirst(def_item),
15545 580 : wqueue, lockmode, tab->rewrite);
15546 : }
15547 1530 : forboth(oid_item, tab->changedIndexOids,
15548 : def_item, tab->changedIndexDefs)
15549 : {
15550 228 : Oid oldId = lfirst_oid(oid_item);
15551 : Oid relid;
15552 :
15553 228 : relid = IndexGetRelation(oldId, false);
15554 :
15555 : /*
15556 : * As above, make sure we have lock on the index's table if it's not
15557 : * the same table.
15558 : */
15559 228 : if (relid != tab->relid)
15560 12 : LockRelationOid(relid, AccessExclusiveLock);
15561 :
15562 228 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15563 228 : (char *) lfirst(def_item),
15564 228 : wqueue, lockmode, tab->rewrite);
15565 :
15566 228 : ObjectAddressSet(obj, RelationRelationId, oldId);
15567 228 : add_exact_object_address(&obj, objects);
15568 : }
15569 :
15570 : /* add dependencies for new statistics */
15571 1376 : forboth(oid_item, tab->changedStatisticsOids,
15572 : def_item, tab->changedStatisticsDefs)
15573 : {
15574 74 : Oid oldId = lfirst_oid(oid_item);
15575 : Oid relid;
15576 :
15577 74 : relid = StatisticsGetRelation(oldId, false);
15578 :
15579 : /*
15580 : * As above, make sure we have lock on the statistics object's table
15581 : * if it's not the same table. However, we take
15582 : * ShareUpdateExclusiveLock here, aligning with the lock level used in
15583 : * CreateStatistics and RemoveStatisticsById.
15584 : *
15585 : * CAUTION: this should be done after all cases that grab
15586 : * AccessExclusiveLock, else we risk causing deadlock due to needing
15587 : * to promote our table lock.
15588 : */
15589 74 : if (relid != tab->relid)
15590 12 : LockRelationOid(relid, ShareUpdateExclusiveLock);
15591 :
15592 74 : ATPostAlterTypeParse(oldId, relid, InvalidOid,
15593 74 : (char *) lfirst(def_item),
15594 74 : wqueue, lockmode, tab->rewrite);
15595 :
15596 74 : ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15597 74 : add_exact_object_address(&obj, objects);
15598 : }
15599 :
15600 : /*
15601 : * Queue up command to restore replica identity index marking
15602 : */
15603 1302 : if (tab->replicaIdentityIndex)
15604 : {
15605 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15606 18 : ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
15607 :
15608 18 : subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15609 18 : subcmd->name = tab->replicaIdentityIndex;
15610 18 : cmd->subtype = AT_ReplicaIdentity;
15611 18 : cmd->def = (Node *) subcmd;
15612 :
15613 : /* do it after indexes and constraints */
15614 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15615 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15616 : }
15617 :
15618 : /*
15619 : * Queue up command to restore marking of index used for cluster.
15620 : */
15621 1302 : if (tab->clusterOnIndex)
15622 : {
15623 18 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15624 :
15625 18 : cmd->subtype = AT_ClusterOn;
15626 18 : cmd->name = tab->clusterOnIndex;
15627 :
15628 : /* do it after indexes and constraints */
15629 18 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15630 18 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15631 : }
15632 :
15633 : /*
15634 : * It should be okay to use DROP_RESTRICT here, since nothing else should
15635 : * be depending on these objects.
15636 : */
15637 1302 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
15638 :
15639 1302 : free_object_addresses(objects);
15640 :
15641 : /*
15642 : * The objects will get recreated during subsequent passes over the work
15643 : * queue.
15644 : */
15645 1302 : }
15646 :
15647 : /*
15648 : * Parse the previously-saved definition string for a constraint, index or
15649 : * statistics object against the newly-established column data type(s), and
15650 : * queue up the resulting command parsetrees for execution.
15651 : *
15652 : * This might fail if, for example, you have a WHERE clause that uses an
15653 : * operator that's not available for the new column type.
15654 : */
15655 : static void
15656 882 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15657 : List **wqueue, LOCKMODE lockmode, bool rewrite)
15658 : {
15659 : List *raw_parsetree_list;
15660 : List *querytree_list;
15661 : ListCell *list_item;
15662 : Relation rel;
15663 :
15664 : /*
15665 : * We expect that we will get only ALTER TABLE and CREATE INDEX
15666 : * statements. Hence, there is no need to pass them through
15667 : * parse_analyze_*() or the rewriter, but instead we need to pass them
15668 : * through parse_utilcmd.c to make them ready for execution.
15669 : */
15670 882 : raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15671 882 : querytree_list = NIL;
15672 1764 : foreach(list_item, raw_parsetree_list)
15673 : {
15674 882 : RawStmt *rs = lfirst_node(RawStmt, list_item);
15675 882 : Node *stmt = rs->stmt;
15676 :
15677 882 : if (IsA(stmt, IndexStmt))
15678 228 : querytree_list = lappend(querytree_list,
15679 228 : transformIndexStmt(oldRelId,
15680 : (IndexStmt *) stmt,
15681 : cmd));
15682 654 : else if (IsA(stmt, AlterTableStmt))
15683 : {
15684 : List *beforeStmts;
15685 : List *afterStmts;
15686 :
15687 566 : stmt = (Node *) transformAlterTableStmt(oldRelId,
15688 : (AlterTableStmt *) stmt,
15689 : cmd,
15690 : &beforeStmts,
15691 : &afterStmts);
15692 566 : querytree_list = list_concat(querytree_list, beforeStmts);
15693 566 : querytree_list = lappend(querytree_list, stmt);
15694 566 : querytree_list = list_concat(querytree_list, afterStmts);
15695 : }
15696 88 : else if (IsA(stmt, CreateStatsStmt))
15697 74 : querytree_list = lappend(querytree_list,
15698 74 : transformStatsStmt(oldRelId,
15699 : (CreateStatsStmt *) stmt,
15700 : cmd));
15701 : else
15702 14 : querytree_list = lappend(querytree_list, stmt);
15703 : }
15704 :
15705 : /* Caller should already have acquired whatever lock we need. */
15706 882 : rel = relation_open(oldRelId, NoLock);
15707 :
15708 : /*
15709 : * Attach each generated command to the proper place in the work queue.
15710 : * Note this could result in creation of entirely new work-queue entries.
15711 : *
15712 : * Also note that we have to tweak the command subtypes, because it turns
15713 : * out that re-creation of indexes and constraints has to act a bit
15714 : * differently from initial creation.
15715 : */
15716 1764 : foreach(list_item, querytree_list)
15717 : {
15718 882 : Node *stm = (Node *) lfirst(list_item);
15719 : AlteredTableInfo *tab;
15720 :
15721 882 : tab = ATGetQueueEntry(wqueue, rel);
15722 :
15723 882 : if (IsA(stm, IndexStmt))
15724 : {
15725 228 : IndexStmt *stmt = (IndexStmt *) stm;
15726 : AlterTableCmd *newcmd;
15727 :
15728 228 : if (!rewrite)
15729 56 : TryReuseIndex(oldId, stmt);
15730 228 : stmt->reset_default_tblspc = true;
15731 : /* keep the index's comment */
15732 228 : stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15733 :
15734 228 : newcmd = makeNode(AlterTableCmd);
15735 228 : newcmd->subtype = AT_ReAddIndex;
15736 228 : newcmd->def = (Node *) stmt;
15737 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
15738 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15739 : }
15740 654 : else if (IsA(stm, AlterTableStmt))
15741 : {
15742 566 : AlterTableStmt *stmt = (AlterTableStmt *) stm;
15743 : ListCell *lcmd;
15744 :
15745 1132 : foreach(lcmd, stmt->cmds)
15746 : {
15747 566 : AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
15748 :
15749 566 : if (cmd->subtype == AT_AddIndex)
15750 : {
15751 : IndexStmt *indstmt;
15752 : Oid indoid;
15753 :
15754 228 : indstmt = castNode(IndexStmt, cmd->def);
15755 228 : indoid = get_constraint_index(oldId);
15756 :
15757 228 : if (!rewrite)
15758 48 : TryReuseIndex(indoid, indstmt);
15759 : /* keep any comment on the index */
15760 228 : indstmt->idxcomment = GetComment(indoid,
15761 : RelationRelationId, 0);
15762 228 : indstmt->reset_default_tblspc = true;
15763 :
15764 228 : cmd->subtype = AT_ReAddIndex;
15765 228 : tab->subcmds[AT_PASS_OLD_INDEX] =
15766 228 : lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15767 :
15768 : /* recreate any comment on the constraint */
15769 228 : RebuildConstraintComment(tab,
15770 : AT_PASS_OLD_INDEX,
15771 : oldId,
15772 : rel,
15773 : NIL,
15774 228 : indstmt->idxname);
15775 : }
15776 338 : else if (cmd->subtype == AT_AddConstraint)
15777 : {
15778 338 : Constraint *con = castNode(Constraint, cmd->def);
15779 :
15780 338 : con->old_pktable_oid = refRelId;
15781 : /* rewriting neither side of a FK */
15782 338 : if (con->contype == CONSTR_FOREIGN &&
15783 72 : !rewrite && tab->rewrite == 0)
15784 6 : TryReuseForeignKey(oldId, con);
15785 338 : con->reset_default_tblspc = true;
15786 338 : cmd->subtype = AT_ReAddConstraint;
15787 338 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15788 338 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15789 :
15790 : /*
15791 : * Recreate any comment on the constraint. If we have
15792 : * recreated a primary key, then transformTableConstraint
15793 : * has added an unnamed not-null constraint here; skip
15794 : * this in that case.
15795 : */
15796 338 : if (con->conname)
15797 338 : RebuildConstraintComment(tab,
15798 : AT_PASS_OLD_CONSTR,
15799 : oldId,
15800 : rel,
15801 : NIL,
15802 338 : con->conname);
15803 : else
15804 : Assert(con->contype == CONSTR_NOTNULL);
15805 : }
15806 : else
15807 0 : elog(ERROR, "unexpected statement subtype: %d",
15808 : (int) cmd->subtype);
15809 : }
15810 : }
15811 88 : else if (IsA(stm, AlterDomainStmt))
15812 : {
15813 14 : AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
15814 :
15815 14 : if (stmt->subtype == AD_AddConstraint)
15816 : {
15817 14 : Constraint *con = castNode(Constraint, stmt->def);
15818 14 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
15819 :
15820 14 : cmd->subtype = AT_ReAddDomainConstraint;
15821 14 : cmd->def = (Node *) stmt;
15822 14 : tab->subcmds[AT_PASS_OLD_CONSTR] =
15823 14 : lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15824 :
15825 : /* recreate any comment on the constraint */
15826 14 : RebuildConstraintComment(tab,
15827 : AT_PASS_OLD_CONSTR,
15828 : oldId,
15829 : NULL,
15830 : stmt->typeName,
15831 14 : con->conname);
15832 : }
15833 : else
15834 0 : elog(ERROR, "unexpected statement subtype: %d",
15835 : (int) stmt->subtype);
15836 : }
15837 74 : else if (IsA(stm, CreateStatsStmt))
15838 : {
15839 74 : CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
15840 : AlterTableCmd *newcmd;
15841 :
15842 : /* keep the statistics object's comment */
15843 74 : stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15844 :
15845 74 : newcmd = makeNode(AlterTableCmd);
15846 74 : newcmd->subtype = AT_ReAddStatistics;
15847 74 : newcmd->def = (Node *) stmt;
15848 74 : tab->subcmds[AT_PASS_MISC] =
15849 74 : lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15850 : }
15851 : else
15852 0 : elog(ERROR, "unexpected statement type: %d",
15853 : (int) nodeTag(stm));
15854 : }
15855 :
15856 882 : relation_close(rel, NoLock);
15857 882 : }
15858 :
15859 : /*
15860 : * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15861 : * for a table or domain constraint that is being rebuilt.
15862 : *
15863 : * objid is the OID of the constraint.
15864 : * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15865 : * as a string list) for a domain constraint.
15866 : * (We could dig that info, as well as the conname, out of the pg_constraint
15867 : * entry; but callers already have them so might as well pass them.)
15868 : */
15869 : static void
15870 580 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
15871 : Relation rel, List *domname,
15872 : const char *conname)
15873 : {
15874 : CommentStmt *cmd;
15875 : char *comment_str;
15876 : AlterTableCmd *newcmd;
15877 :
15878 : /* Look for comment for object wanted, and leave if none */
15879 580 : comment_str = GetComment(objid, ConstraintRelationId, 0);
15880 580 : if (comment_str == NULL)
15881 490 : return;
15882 :
15883 : /* Build CommentStmt node, copying all input data for safety */
15884 90 : cmd = makeNode(CommentStmt);
15885 90 : if (rel)
15886 : {
15887 78 : cmd->objtype = OBJECT_TABCONSTRAINT;
15888 78 : cmd->object = (Node *)
15889 78 : list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
15890 : makeString(pstrdup(RelationGetRelationName(rel))),
15891 : makeString(pstrdup(conname)));
15892 : }
15893 : else
15894 : {
15895 12 : cmd->objtype = OBJECT_DOMCONSTRAINT;
15896 12 : cmd->object = (Node *)
15897 12 : list_make2(makeTypeNameFromNameList(copyObject(domname)),
15898 : makeString(pstrdup(conname)));
15899 : }
15900 90 : cmd->comment = comment_str;
15901 :
15902 : /* Append it to list of commands */
15903 90 : newcmd = makeNode(AlterTableCmd);
15904 90 : newcmd->subtype = AT_ReAddComment;
15905 90 : newcmd->def = (Node *) cmd;
15906 90 : tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15907 : }
15908 :
15909 : /*
15910 : * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15911 : * for the real analysis, then mutates the IndexStmt based on that verdict.
15912 : */
15913 : static void
15914 104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
15915 : {
15916 104 : if (CheckIndexCompatible(oldId,
15917 104 : stmt->accessMethod,
15918 104 : stmt->indexParams,
15919 104 : stmt->excludeOpNames,
15920 104 : stmt->iswithoutoverlaps))
15921 : {
15922 104 : Relation irel = index_open(oldId, NoLock);
15923 :
15924 : /* If it's a partitioned index, there is no storage to share. */
15925 104 : if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15926 : {
15927 74 : stmt->oldNumber = irel->rd_locator.relNumber;
15928 74 : stmt->oldCreateSubid = irel->rd_createSubid;
15929 74 : stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15930 : }
15931 104 : index_close(irel, NoLock);
15932 : }
15933 104 : }
15934 :
15935 : /*
15936 : * Subroutine for ATPostAlterTypeParse().
15937 : *
15938 : * Stash the old P-F equality operator into the Constraint node, for possible
15939 : * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15940 : * this constraint can be skipped.
15941 : */
15942 : static void
15943 6 : TryReuseForeignKey(Oid oldId, Constraint *con)
15944 : {
15945 : HeapTuple tup;
15946 : Datum adatum;
15947 : ArrayType *arr;
15948 : Oid *rawarr;
15949 : int numkeys;
15950 : int i;
15951 :
15952 : Assert(con->contype == CONSTR_FOREIGN);
15953 : Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15954 :
15955 6 : tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15956 6 : if (!HeapTupleIsValid(tup)) /* should not happen */
15957 0 : elog(ERROR, "cache lookup failed for constraint %u", oldId);
15958 :
15959 6 : adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15960 : Anum_pg_constraint_conpfeqop);
15961 6 : arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15962 6 : numkeys = ARR_DIMS(arr)[0];
15963 : /* test follows the one in ri_FetchConstraintInfo() */
15964 6 : if (ARR_NDIM(arr) != 1 ||
15965 6 : ARR_HASNULL(arr) ||
15966 6 : ARR_ELEMTYPE(arr) != OIDOID)
15967 0 : elog(ERROR, "conpfeqop is not a 1-D Oid array");
15968 6 : rawarr = (Oid *) ARR_DATA_PTR(arr);
15969 :
15970 : /* stash a List of the operator Oids in our Constraint node */
15971 12 : for (i = 0; i < numkeys; i++)
15972 6 : con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15973 :
15974 6 : ReleaseSysCache(tup);
15975 6 : }
15976 :
15977 : /*
15978 : * ALTER COLUMN .. OPTIONS ( ... )
15979 : *
15980 : * Returns the address of the modified column
15981 : */
15982 : static ObjectAddress
15983 172 : ATExecAlterColumnGenericOptions(Relation rel,
15984 : const char *colName,
15985 : List *options,
15986 : LOCKMODE lockmode)
15987 : {
15988 : Relation ftrel;
15989 : Relation attrel;
15990 : ForeignServer *server;
15991 : ForeignDataWrapper *fdw;
15992 : HeapTuple tuple;
15993 : HeapTuple newtuple;
15994 : bool isnull;
15995 : Datum repl_val[Natts_pg_attribute];
15996 : bool repl_null[Natts_pg_attribute];
15997 : bool repl_repl[Natts_pg_attribute];
15998 : Datum datum;
15999 : Form_pg_foreign_table fttableform;
16000 : Form_pg_attribute atttableform;
16001 : AttrNumber attnum;
16002 : ObjectAddress address;
16003 :
16004 172 : if (options == NIL)
16005 0 : return InvalidObjectAddress;
16006 :
16007 : /* First, determine FDW validator associated to the foreign table. */
16008 172 : ftrel = table_open(ForeignTableRelationId, AccessShareLock);
16009 172 : tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
16010 172 : if (!HeapTupleIsValid(tuple))
16011 0 : ereport(ERROR,
16012 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16013 : errmsg("foreign table \"%s\" does not exist",
16014 : RelationGetRelationName(rel))));
16015 172 : fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16016 172 : server = GetForeignServer(fttableform->ftserver);
16017 172 : fdw = GetForeignDataWrapper(server->fdwid);
16018 :
16019 172 : table_close(ftrel, AccessShareLock);
16020 172 : ReleaseSysCache(tuple);
16021 :
16022 172 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
16023 172 : tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
16024 172 : if (!HeapTupleIsValid(tuple))
16025 0 : ereport(ERROR,
16026 : (errcode(ERRCODE_UNDEFINED_COLUMN),
16027 : errmsg("column \"%s\" of relation \"%s\" does not exist",
16028 : colName, RelationGetRelationName(rel))));
16029 :
16030 : /* Prevent them from altering a system attribute */
16031 172 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16032 172 : attnum = atttableform->attnum;
16033 172 : if (attnum <= 0)
16034 6 : ereport(ERROR,
16035 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16036 : errmsg("cannot alter system column \"%s\"", colName)));
16037 :
16038 :
16039 : /* Initialize buffers for new tuple values */
16040 166 : memset(repl_val, 0, sizeof(repl_val));
16041 166 : memset(repl_null, false, sizeof(repl_null));
16042 166 : memset(repl_repl, false, sizeof(repl_repl));
16043 :
16044 : /* Extract the current options */
16045 166 : datum = SysCacheGetAttr(ATTNAME,
16046 : tuple,
16047 : Anum_pg_attribute_attfdwoptions,
16048 : &isnull);
16049 166 : if (isnull)
16050 156 : datum = PointerGetDatum(NULL);
16051 :
16052 : /* Transform the options */
16053 166 : datum = transformGenericOptions(AttributeRelationId,
16054 : datum,
16055 : options,
16056 : fdw->fdwvalidator);
16057 :
16058 166 : if (DatumGetPointer(datum) != NULL)
16059 166 : repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16060 : else
16061 0 : repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16062 :
16063 166 : repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16064 :
16065 : /* Everything looks good - update the tuple */
16066 :
16067 166 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16068 : repl_val, repl_null, repl_repl);
16069 :
16070 166 : CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16071 :
16072 166 : InvokeObjectPostAlterHook(RelationRelationId,
16073 : RelationGetRelid(rel),
16074 : atttableform->attnum);
16075 166 : ObjectAddressSubSet(address, RelationRelationId,
16076 : RelationGetRelid(rel), attnum);
16077 :
16078 166 : ReleaseSysCache(tuple);
16079 :
16080 166 : table_close(attrel, RowExclusiveLock);
16081 :
16082 166 : heap_freetuple(newtuple);
16083 :
16084 166 : return address;
16085 : }
16086 :
16087 : /*
16088 : * ALTER TABLE OWNER
16089 : *
16090 : * recursing is true if we are recursing from a table to its indexes,
16091 : * sequences, or toast table. We don't allow the ownership of those things to
16092 : * be changed separately from the parent table. Also, we can skip permission
16093 : * checks (this is necessary not just an optimization, else we'd fail to
16094 : * handle toast tables properly).
16095 : *
16096 : * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
16097 : * free-standing composite type.
16098 : */
16099 : void
16100 2284 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
16101 : {
16102 : Relation target_rel;
16103 : Relation class_rel;
16104 : HeapTuple tuple;
16105 : Form_pg_class tuple_class;
16106 :
16107 : /*
16108 : * Get exclusive lock till end of transaction on the target table. Use
16109 : * relation_open so that we can work on indexes and sequences.
16110 : */
16111 2284 : target_rel = relation_open(relationOid, lockmode);
16112 :
16113 : /* Get its pg_class tuple, too */
16114 2284 : class_rel = table_open(RelationRelationId, RowExclusiveLock);
16115 :
16116 2284 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16117 2284 : if (!HeapTupleIsValid(tuple))
16118 0 : elog(ERROR, "cache lookup failed for relation %u", relationOid);
16119 2284 : tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16120 :
16121 : /* Can we change the ownership of this tuple? */
16122 2284 : switch (tuple_class->relkind)
16123 : {
16124 2000 : case RELKIND_RELATION:
16125 : case RELKIND_VIEW:
16126 : case RELKIND_MATVIEW:
16127 : case RELKIND_FOREIGN_TABLE:
16128 : case RELKIND_PARTITIONED_TABLE:
16129 : /* ok to change owner */
16130 2000 : break;
16131 96 : case RELKIND_INDEX:
16132 96 : if (!recursing)
16133 : {
16134 : /*
16135 : * Because ALTER INDEX OWNER used to be allowed, and in fact
16136 : * is generated by old versions of pg_dump, we give a warning
16137 : * and do nothing rather than erroring out. Also, to avoid
16138 : * unnecessary chatter while restoring those old dumps, say
16139 : * nothing at all if the command would be a no-op anyway.
16140 : */
16141 0 : if (tuple_class->relowner != newOwnerId)
16142 0 : ereport(WARNING,
16143 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16144 : errmsg("cannot change owner of index \"%s\"",
16145 : NameStr(tuple_class->relname)),
16146 : errhint("Change the ownership of the index's table instead.")));
16147 : /* quick hack to exit via the no-op path */
16148 0 : newOwnerId = tuple_class->relowner;
16149 : }
16150 96 : break;
16151 20 : case RELKIND_PARTITIONED_INDEX:
16152 20 : if (recursing)
16153 20 : break;
16154 0 : ereport(ERROR,
16155 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16156 : errmsg("cannot change owner of index \"%s\"",
16157 : NameStr(tuple_class->relname)),
16158 : errhint("Change the ownership of the index's table instead.")));
16159 : break;
16160 118 : case RELKIND_SEQUENCE:
16161 118 : if (!recursing &&
16162 70 : tuple_class->relowner != newOwnerId)
16163 : {
16164 : /* if it's an owned sequence, disallow changing it by itself */
16165 : Oid tableId;
16166 : int32 colId;
16167 :
16168 0 : if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16169 0 : sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16170 0 : ereport(ERROR,
16171 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16172 : errmsg("cannot change owner of sequence \"%s\"",
16173 : NameStr(tuple_class->relname)),
16174 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
16175 : NameStr(tuple_class->relname),
16176 : get_rel_name(tableId))));
16177 : }
16178 118 : break;
16179 8 : case RELKIND_COMPOSITE_TYPE:
16180 8 : if (recursing)
16181 8 : break;
16182 0 : ereport(ERROR,
16183 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16184 : errmsg("\"%s\" is a composite type",
16185 : NameStr(tuple_class->relname)),
16186 : /* translator: %s is an SQL ALTER command */
16187 : errhint("Use %s instead.",
16188 : "ALTER TYPE")));
16189 : break;
16190 42 : case RELKIND_TOASTVALUE:
16191 42 : if (recursing)
16192 42 : break;
16193 : /* FALL THRU */
16194 : default:
16195 0 : ereport(ERROR,
16196 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16197 : errmsg("cannot change owner of relation \"%s\"",
16198 : NameStr(tuple_class->relname)),
16199 : errdetail_relkind_not_supported(tuple_class->relkind)));
16200 : }
16201 :
16202 : /*
16203 : * If the new owner is the same as the existing owner, consider the
16204 : * command to have succeeded. This is for dump restoration purposes.
16205 : */
16206 2284 : if (tuple_class->relowner != newOwnerId)
16207 : {
16208 : Datum repl_val[Natts_pg_class];
16209 : bool repl_null[Natts_pg_class];
16210 : bool repl_repl[Natts_pg_class];
16211 : Acl *newAcl;
16212 : Datum aclDatum;
16213 : bool isNull;
16214 : HeapTuple newtuple;
16215 :
16216 : /* skip permission checks when recursing to index or toast table */
16217 546 : if (!recursing)
16218 : {
16219 : /* Superusers can always do it */
16220 328 : if (!superuser())
16221 : {
16222 42 : Oid namespaceOid = tuple_class->relnamespace;
16223 : AclResult aclresult;
16224 :
16225 : /* Otherwise, must be owner of the existing object */
16226 42 : if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16227 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
16228 0 : RelationGetRelationName(target_rel));
16229 :
16230 : /* Must be able to become new owner */
16231 42 : check_can_set_role(GetUserId(), newOwnerId);
16232 :
16233 : /* New owner must have CREATE privilege on namespace */
16234 30 : aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16235 : ACL_CREATE);
16236 30 : if (aclresult != ACLCHECK_OK)
16237 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
16238 0 : get_namespace_name(namespaceOid));
16239 : }
16240 : }
16241 :
16242 534 : memset(repl_null, false, sizeof(repl_null));
16243 534 : memset(repl_repl, false, sizeof(repl_repl));
16244 :
16245 534 : repl_repl[Anum_pg_class_relowner - 1] = true;
16246 534 : repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16247 :
16248 : /*
16249 : * Determine the modified ACL for the new owner. This is only
16250 : * necessary when the ACL is non-null.
16251 : */
16252 534 : aclDatum = SysCacheGetAttr(RELOID, tuple,
16253 : Anum_pg_class_relacl,
16254 : &isNull);
16255 534 : if (!isNull)
16256 : {
16257 46 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16258 : tuple_class->relowner, newOwnerId);
16259 46 : repl_repl[Anum_pg_class_relacl - 1] = true;
16260 46 : repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16261 : }
16262 :
16263 534 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16264 :
16265 534 : CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16266 :
16267 534 : heap_freetuple(newtuple);
16268 :
16269 : /*
16270 : * We must similarly update any per-column ACLs to reflect the new
16271 : * owner; for neatness reasons that's split out as a subroutine.
16272 : */
16273 534 : change_owner_fix_column_acls(relationOid,
16274 : tuple_class->relowner,
16275 : newOwnerId);
16276 :
16277 : /*
16278 : * Update owner dependency reference, if any. A composite type has
16279 : * none, because it's tracked for the pg_type entry instead of here;
16280 : * indexes and TOAST tables don't have their own entries either.
16281 : */
16282 534 : if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16283 526 : tuple_class->relkind != RELKIND_INDEX &&
16284 430 : tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16285 410 : tuple_class->relkind != RELKIND_TOASTVALUE)
16286 368 : changeDependencyOnOwner(RelationRelationId, relationOid,
16287 : newOwnerId);
16288 :
16289 : /*
16290 : * Also change the ownership of the table's row type, if it has one
16291 : */
16292 534 : if (OidIsValid(tuple_class->reltype))
16293 342 : AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16294 :
16295 : /*
16296 : * If we are operating on a table or materialized view, also change
16297 : * the ownership of any indexes and sequences that belong to the
16298 : * relation, as well as its toast table (if it has one).
16299 : */
16300 534 : if (tuple_class->relkind == RELKIND_RELATION ||
16301 274 : tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16302 224 : tuple_class->relkind == RELKIND_MATVIEW ||
16303 224 : tuple_class->relkind == RELKIND_TOASTVALUE)
16304 : {
16305 : List *index_oid_list;
16306 : ListCell *i;
16307 :
16308 : /* Find all the indexes belonging to this relation */
16309 352 : index_oid_list = RelationGetIndexList(target_rel);
16310 :
16311 : /* For each index, recursively change its ownership */
16312 468 : foreach(i, index_oid_list)
16313 116 : ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16314 :
16315 352 : list_free(index_oid_list);
16316 : }
16317 :
16318 : /* If it has a toast table, recurse to change its ownership */
16319 534 : if (tuple_class->reltoastrelid != InvalidOid)
16320 42 : ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16321 : true, lockmode);
16322 :
16323 : /* If it has dependent sequences, recurse to change them too */
16324 534 : change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16325 : }
16326 :
16327 2272 : InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16328 :
16329 2272 : ReleaseSysCache(tuple);
16330 2272 : table_close(class_rel, RowExclusiveLock);
16331 2272 : relation_close(target_rel, NoLock);
16332 2272 : }
16333 :
16334 : /*
16335 : * change_owner_fix_column_acls
16336 : *
16337 : * Helper function for ATExecChangeOwner. Scan the columns of the table
16338 : * and fix any non-null column ACLs to reflect the new owner.
16339 : */
16340 : static void
16341 534 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16342 : {
16343 : Relation attRelation;
16344 : SysScanDesc scan;
16345 : ScanKeyData key[1];
16346 : HeapTuple attributeTuple;
16347 :
16348 534 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16349 534 : ScanKeyInit(&key[0],
16350 : Anum_pg_attribute_attrelid,
16351 : BTEqualStrategyNumber, F_OIDEQ,
16352 : ObjectIdGetDatum(relationOid));
16353 534 : scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16354 : true, NULL, 1, key);
16355 3768 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16356 : {
16357 3234 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16358 : Datum repl_val[Natts_pg_attribute];
16359 : bool repl_null[Natts_pg_attribute];
16360 : bool repl_repl[Natts_pg_attribute];
16361 : Acl *newAcl;
16362 : Datum aclDatum;
16363 : bool isNull;
16364 : HeapTuple newtuple;
16365 :
16366 : /* Ignore dropped columns */
16367 3234 : if (att->attisdropped)
16368 3232 : continue;
16369 :
16370 3234 : aclDatum = heap_getattr(attributeTuple,
16371 : Anum_pg_attribute_attacl,
16372 : RelationGetDescr(attRelation),
16373 : &isNull);
16374 : /* Null ACLs do not require changes */
16375 3234 : if (isNull)
16376 3232 : continue;
16377 :
16378 2 : memset(repl_null, false, sizeof(repl_null));
16379 2 : memset(repl_repl, false, sizeof(repl_repl));
16380 :
16381 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
16382 : oldOwnerId, newOwnerId);
16383 2 : repl_repl[Anum_pg_attribute_attacl - 1] = true;
16384 2 : repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16385 :
16386 2 : newtuple = heap_modify_tuple(attributeTuple,
16387 : RelationGetDescr(attRelation),
16388 : repl_val, repl_null, repl_repl);
16389 :
16390 2 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16391 :
16392 2 : heap_freetuple(newtuple);
16393 : }
16394 534 : systable_endscan(scan);
16395 534 : table_close(attRelation, RowExclusiveLock);
16396 534 : }
16397 :
16398 : /*
16399 : * change_owner_recurse_to_sequences
16400 : *
16401 : * Helper function for ATExecChangeOwner. Examines pg_depend searching
16402 : * for sequences that are dependent on serial columns, and changes their
16403 : * ownership.
16404 : */
16405 : static void
16406 534 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16407 : {
16408 : Relation depRel;
16409 : SysScanDesc scan;
16410 : ScanKeyData key[2];
16411 : HeapTuple tup;
16412 :
16413 : /*
16414 : * SERIAL sequences are those having an auto dependency on one of the
16415 : * table's columns (we don't care *which* column, exactly).
16416 : */
16417 534 : depRel = table_open(DependRelationId, AccessShareLock);
16418 :
16419 534 : ScanKeyInit(&key[0],
16420 : Anum_pg_depend_refclassid,
16421 : BTEqualStrategyNumber, F_OIDEQ,
16422 : ObjectIdGetDatum(RelationRelationId));
16423 534 : ScanKeyInit(&key[1],
16424 : Anum_pg_depend_refobjid,
16425 : BTEqualStrategyNumber, F_OIDEQ,
16426 : ObjectIdGetDatum(relationOid));
16427 : /* we leave refobjsubid unspecified */
16428 :
16429 534 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16430 : NULL, 2, key);
16431 :
16432 1512 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
16433 : {
16434 978 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16435 : Relation seqRel;
16436 :
16437 : /* skip dependencies other than auto dependencies on columns */
16438 978 : if (depForm->refobjsubid == 0 ||
16439 364 : depForm->classid != RelationRelationId ||
16440 142 : depForm->objsubid != 0 ||
16441 142 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16442 836 : continue;
16443 :
16444 : /* Use relation_open just in case it's an index */
16445 142 : seqRel = relation_open(depForm->objid, lockmode);
16446 :
16447 : /* skip non-sequence relations */
16448 142 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16449 : {
16450 : /* No need to keep the lock */
16451 116 : relation_close(seqRel, lockmode);
16452 116 : continue;
16453 : }
16454 :
16455 : /* We don't need to close the sequence while we alter it. */
16456 26 : ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16457 :
16458 : /* Now we can close it. Keep the lock till end of transaction. */
16459 26 : relation_close(seqRel, NoLock);
16460 : }
16461 :
16462 534 : systable_endscan(scan);
16463 :
16464 534 : relation_close(depRel, AccessShareLock);
16465 534 : }
16466 :
16467 : /*
16468 : * ALTER TABLE CLUSTER ON
16469 : *
16470 : * The only thing we have to do is to change the indisclustered bits.
16471 : *
16472 : * Return the address of the new clustering index.
16473 : */
16474 : static ObjectAddress
16475 64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16476 : {
16477 : Oid indexOid;
16478 : ObjectAddress address;
16479 :
16480 64 : indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16481 :
16482 64 : if (!OidIsValid(indexOid))
16483 0 : ereport(ERROR,
16484 : (errcode(ERRCODE_UNDEFINED_OBJECT),
16485 : errmsg("index \"%s\" for table \"%s\" does not exist",
16486 : indexName, RelationGetRelationName(rel))));
16487 :
16488 : /* Check index is valid to cluster on */
16489 64 : check_index_is_clusterable(rel, indexOid, lockmode);
16490 :
16491 : /* And do the work */
16492 64 : mark_index_clustered(rel, indexOid, false);
16493 :
16494 58 : ObjectAddressSet(address,
16495 : RelationRelationId, indexOid);
16496 :
16497 58 : return address;
16498 : }
16499 :
16500 : /*
16501 : * ALTER TABLE SET WITHOUT CLUSTER
16502 : *
16503 : * We have to find any indexes on the table that have indisclustered bit
16504 : * set and turn it off.
16505 : */
16506 : static void
16507 18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
16508 : {
16509 18 : mark_index_clustered(rel, InvalidOid, false);
16510 12 : }
16511 :
16512 : /*
16513 : * Preparation phase for SET ACCESS METHOD
16514 : *
16515 : * Check that the access method exists and determine whether a change is
16516 : * actually needed.
16517 : */
16518 : static void
16519 110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
16520 : {
16521 : Oid amoid;
16522 :
16523 : /*
16524 : * Look up the access method name and check that it differs from the
16525 : * table's current AM. If DEFAULT was specified for a partitioned table
16526 : * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16527 : */
16528 110 : if (amname != NULL)
16529 74 : amoid = get_table_am_oid(amname, false);
16530 36 : else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16531 18 : amoid = InvalidOid;
16532 : else
16533 18 : amoid = get_table_am_oid(default_table_access_method, false);
16534 :
16535 : /* if it's a match, phase 3 doesn't need to do anything */
16536 110 : if (rel->rd_rel->relam == amoid)
16537 12 : return;
16538 :
16539 : /* Save info for Phase 3 to do the real work */
16540 98 : tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
16541 98 : tab->newAccessMethod = amoid;
16542 98 : tab->chgAccessMethod = true;
16543 : }
16544 :
16545 : /*
16546 : * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16547 : * storage that have an interest in preserving AM.
16548 : *
16549 : * Since these have no storage, setting the access method is a catalog only
16550 : * operation.
16551 : */
16552 : static void
16553 44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
16554 : {
16555 : Relation pg_class;
16556 : Oid oldAccessMethodId;
16557 : HeapTuple tuple;
16558 : Form_pg_class rd_rel;
16559 44 : Oid reloid = RelationGetRelid(rel);
16560 :
16561 : /*
16562 : * Shouldn't be called on relations having storage; these are processed in
16563 : * phase 3.
16564 : */
16565 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16566 :
16567 : /* Get a modifiable copy of the relation's pg_class row. */
16568 44 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
16569 :
16570 44 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16571 44 : if (!HeapTupleIsValid(tuple))
16572 0 : elog(ERROR, "cache lookup failed for relation %u", reloid);
16573 44 : rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16574 :
16575 : /* Update the pg_class row. */
16576 44 : oldAccessMethodId = rd_rel->relam;
16577 44 : rd_rel->relam = newAccessMethodId;
16578 :
16579 : /* Leave if no update required */
16580 44 : if (rd_rel->relam == oldAccessMethodId)
16581 : {
16582 0 : heap_freetuple(tuple);
16583 0 : table_close(pg_class, RowExclusiveLock);
16584 0 : return;
16585 : }
16586 :
16587 44 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16588 :
16589 : /*
16590 : * Update the dependency on the new access method. No dependency is added
16591 : * if the new access method is InvalidOid (default case). Be very careful
16592 : * that this has to compare the previous value stored in pg_class with the
16593 : * new one.
16594 : */
16595 44 : if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16596 20 : {
16597 : ObjectAddress relobj,
16598 : referenced;
16599 :
16600 : /*
16601 : * New access method is defined and there was no dependency
16602 : * previously, so record a new one.
16603 : */
16604 20 : ObjectAddressSet(relobj, RelationRelationId, reloid);
16605 20 : ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16606 20 : recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16607 : }
16608 24 : else if (OidIsValid(oldAccessMethodId) &&
16609 24 : !OidIsValid(rd_rel->relam))
16610 : {
16611 : /*
16612 : * There was an access method defined, and no new one, so just remove
16613 : * the existing dependency.
16614 : */
16615 12 : deleteDependencyRecordsForClass(RelationRelationId, reloid,
16616 : AccessMethodRelationId,
16617 : DEPENDENCY_NORMAL);
16618 : }
16619 : else
16620 : {
16621 : Assert(OidIsValid(oldAccessMethodId) &&
16622 : OidIsValid(rd_rel->relam));
16623 :
16624 : /* Both are valid, so update the dependency */
16625 12 : changeDependencyFor(RelationRelationId, reloid,
16626 : AccessMethodRelationId,
16627 : oldAccessMethodId, rd_rel->relam);
16628 : }
16629 :
16630 : /* make the relam and dependency changes visible */
16631 44 : CommandCounterIncrement();
16632 :
16633 44 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16634 :
16635 44 : heap_freetuple(tuple);
16636 44 : table_close(pg_class, RowExclusiveLock);
16637 : }
16638 :
16639 : /*
16640 : * ALTER TABLE SET TABLESPACE
16641 : */
16642 : static void
16643 164 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16644 : {
16645 : Oid tablespaceId;
16646 :
16647 : /* Check that the tablespace exists */
16648 164 : tablespaceId = get_tablespace_oid(tablespacename, false);
16649 :
16650 : /* Check permissions except when moving to database's default */
16651 164 : if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16652 : {
16653 : AclResult aclresult;
16654 :
16655 66 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16656 66 : if (aclresult != ACLCHECK_OK)
16657 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16658 : }
16659 :
16660 : /* Save info for Phase 3 to do the real work */
16661 164 : if (OidIsValid(tab->newTableSpace))
16662 0 : ereport(ERROR,
16663 : (errcode(ERRCODE_SYNTAX_ERROR),
16664 : errmsg("cannot have multiple SET TABLESPACE subcommands")));
16665 :
16666 164 : tab->newTableSpace = tablespaceId;
16667 164 : }
16668 :
16669 : /*
16670 : * Set, reset, or replace reloptions.
16671 : */
16672 : static void
16673 960 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
16674 : LOCKMODE lockmode)
16675 : {
16676 : Oid relid;
16677 : Relation pgclass;
16678 : HeapTuple tuple;
16679 : HeapTuple newtuple;
16680 : Datum datum;
16681 : Datum newOptions;
16682 : Datum repl_val[Natts_pg_class];
16683 : bool repl_null[Natts_pg_class];
16684 : bool repl_repl[Natts_pg_class];
16685 960 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16686 :
16687 960 : if (defList == NIL && operation != AT_ReplaceRelOptions)
16688 0 : return; /* nothing to do */
16689 :
16690 960 : pgclass = table_open(RelationRelationId, RowExclusiveLock);
16691 :
16692 : /* Fetch heap tuple */
16693 960 : relid = RelationGetRelid(rel);
16694 960 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16695 960 : if (!HeapTupleIsValid(tuple))
16696 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
16697 :
16698 960 : if (operation == AT_ReplaceRelOptions)
16699 : {
16700 : /*
16701 : * If we're supposed to replace the reloptions list, we just pretend
16702 : * there were none before.
16703 : */
16704 194 : datum = (Datum) 0;
16705 : }
16706 : else
16707 : {
16708 : bool isnull;
16709 :
16710 : /* Get the old reloptions */
16711 766 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16712 : &isnull);
16713 766 : if (isnull)
16714 478 : datum = (Datum) 0;
16715 : }
16716 :
16717 : /* Generate new proposed reloptions (text array) */
16718 960 : newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16719 : operation == AT_ResetRelOptions);
16720 :
16721 : /* Validate */
16722 954 : switch (rel->rd_rel->relkind)
16723 : {
16724 536 : case RELKIND_RELATION:
16725 : case RELKIND_MATVIEW:
16726 536 : (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16727 536 : break;
16728 6 : case RELKIND_PARTITIONED_TABLE:
16729 6 : (void) partitioned_table_reloptions(newOptions, true);
16730 0 : break;
16731 296 : case RELKIND_VIEW:
16732 296 : (void) view_reloptions(newOptions, true);
16733 278 : break;
16734 116 : case RELKIND_INDEX:
16735 : case RELKIND_PARTITIONED_INDEX:
16736 116 : (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16737 94 : break;
16738 0 : case RELKIND_TOASTVALUE:
16739 : /* fall through to error -- shouldn't ever get here */
16740 : default:
16741 0 : ereport(ERROR,
16742 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16743 : errmsg("cannot set options for relation \"%s\"",
16744 : RelationGetRelationName(rel)),
16745 : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16746 : break;
16747 : }
16748 :
16749 : /* Special-case validation of view options */
16750 908 : if (rel->rd_rel->relkind == RELKIND_VIEW)
16751 : {
16752 278 : Query *view_query = get_view_query(rel);
16753 278 : List *view_options = untransformRelOptions(newOptions);
16754 : ListCell *cell;
16755 278 : bool check_option = false;
16756 :
16757 380 : foreach(cell, view_options)
16758 : {
16759 102 : DefElem *defel = (DefElem *) lfirst(cell);
16760 :
16761 102 : if (strcmp(defel->defname, "check_option") == 0)
16762 24 : check_option = true;
16763 : }
16764 :
16765 : /*
16766 : * If the check option is specified, look to see if the view is
16767 : * actually auto-updatable or not.
16768 : */
16769 278 : if (check_option)
16770 : {
16771 : const char *view_updatable_error =
16772 24 : view_query_is_auto_updatable(view_query, true);
16773 :
16774 24 : if (view_updatable_error)
16775 0 : ereport(ERROR,
16776 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16777 : errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16778 : errhint("%s", _(view_updatable_error))));
16779 : }
16780 : }
16781 :
16782 : /*
16783 : * All we need do here is update the pg_class row; the new options will be
16784 : * propagated into relcaches during post-commit cache inval.
16785 : */
16786 908 : memset(repl_val, 0, sizeof(repl_val));
16787 908 : memset(repl_null, false, sizeof(repl_null));
16788 908 : memset(repl_repl, false, sizeof(repl_repl));
16789 :
16790 908 : if (newOptions != (Datum) 0)
16791 614 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16792 : else
16793 294 : repl_null[Anum_pg_class_reloptions - 1] = true;
16794 :
16795 908 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16796 :
16797 908 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16798 : repl_val, repl_null, repl_repl);
16799 :
16800 908 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16801 908 : UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16802 :
16803 908 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16804 :
16805 908 : heap_freetuple(newtuple);
16806 :
16807 908 : ReleaseSysCache(tuple);
16808 :
16809 : /* repeat the whole exercise for the toast table, if there's one */
16810 908 : if (OidIsValid(rel->rd_rel->reltoastrelid))
16811 : {
16812 : Relation toastrel;
16813 268 : Oid toastid = rel->rd_rel->reltoastrelid;
16814 :
16815 268 : toastrel = table_open(toastid, lockmode);
16816 :
16817 : /* Fetch heap tuple */
16818 268 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16819 268 : if (!HeapTupleIsValid(tuple))
16820 0 : elog(ERROR, "cache lookup failed for relation %u", toastid);
16821 :
16822 268 : if (operation == AT_ReplaceRelOptions)
16823 : {
16824 : /*
16825 : * If we're supposed to replace the reloptions list, we just
16826 : * pretend there were none before.
16827 : */
16828 0 : datum = (Datum) 0;
16829 : }
16830 : else
16831 : {
16832 : bool isnull;
16833 :
16834 : /* Get the old reloptions */
16835 268 : datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16836 : &isnull);
16837 268 : if (isnull)
16838 232 : datum = (Datum) 0;
16839 : }
16840 :
16841 268 : newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16842 : false, operation == AT_ResetRelOptions);
16843 :
16844 268 : (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16845 :
16846 268 : memset(repl_val, 0, sizeof(repl_val));
16847 268 : memset(repl_null, false, sizeof(repl_null));
16848 268 : memset(repl_repl, false, sizeof(repl_repl));
16849 :
16850 268 : if (newOptions != (Datum) 0)
16851 42 : repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16852 : else
16853 226 : repl_null[Anum_pg_class_reloptions - 1] = true;
16854 :
16855 268 : repl_repl[Anum_pg_class_reloptions - 1] = true;
16856 :
16857 268 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16858 : repl_val, repl_null, repl_repl);
16859 :
16860 268 : CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16861 :
16862 268 : InvokeObjectPostAlterHookArg(RelationRelationId,
16863 : RelationGetRelid(toastrel), 0,
16864 : InvalidOid, true);
16865 :
16866 268 : heap_freetuple(newtuple);
16867 :
16868 268 : ReleaseSysCache(tuple);
16869 :
16870 268 : table_close(toastrel, NoLock);
16871 : }
16872 :
16873 908 : table_close(pgclass, RowExclusiveLock);
16874 : }
16875 :
16876 : /*
16877 : * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16878 : * rewriting to be done, so we just want to copy the data as fast as possible.
16879 : */
16880 : static void
16881 168 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16882 : {
16883 : Relation rel;
16884 : Oid reltoastrelid;
16885 : RelFileNumber newrelfilenumber;
16886 : RelFileLocator newrlocator;
16887 168 : List *reltoastidxids = NIL;
16888 : ListCell *lc;
16889 :
16890 : /*
16891 : * Need lock here in case we are recursing to toast table or index
16892 : */
16893 168 : rel = relation_open(tableOid, lockmode);
16894 :
16895 : /* Check first if relation can be moved to new tablespace */
16896 168 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16897 : {
16898 8 : InvokeObjectPostAlterHook(RelationRelationId,
16899 : RelationGetRelid(rel), 0);
16900 8 : relation_close(rel, NoLock);
16901 8 : return;
16902 : }
16903 :
16904 160 : reltoastrelid = rel->rd_rel->reltoastrelid;
16905 : /* Fetch the list of indexes on toast relation if necessary */
16906 160 : if (OidIsValid(reltoastrelid))
16907 : {
16908 20 : Relation toastRel = relation_open(reltoastrelid, lockmode);
16909 :
16910 20 : reltoastidxids = RelationGetIndexList(toastRel);
16911 20 : relation_close(toastRel, lockmode);
16912 : }
16913 :
16914 : /*
16915 : * Relfilenumbers are not unique in databases across tablespaces, so we
16916 : * need to allocate a new one in the new tablespace.
16917 : */
16918 160 : newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16919 160 : rel->rd_rel->relpersistence);
16920 :
16921 : /* Open old and new relation */
16922 160 : newrlocator = rel->rd_locator;
16923 160 : newrlocator.relNumber = newrelfilenumber;
16924 160 : newrlocator.spcOid = newTableSpace;
16925 :
16926 : /* hand off to AM to actually create new rel storage and copy the data */
16927 160 : if (rel->rd_rel->relkind == RELKIND_INDEX)
16928 : {
16929 62 : index_copy_data(rel, newrlocator);
16930 : }
16931 : else
16932 : {
16933 : Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16934 98 : table_relation_copy_data(rel, &newrlocator);
16935 : }
16936 :
16937 : /*
16938 : * Update the pg_class row.
16939 : *
16940 : * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16941 : * executed on pg_class or its indexes (the above copy wouldn't contain
16942 : * the updated pg_class entry), but that's forbidden with
16943 : * CheckRelationTableSpaceMove().
16944 : */
16945 160 : SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16946 :
16947 160 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16948 :
16949 160 : RelationAssumeNewRelfilelocator(rel);
16950 :
16951 160 : relation_close(rel, NoLock);
16952 :
16953 : /* Make sure the reltablespace change is visible */
16954 160 : CommandCounterIncrement();
16955 :
16956 : /* Move associated toast relation and/or indexes, too */
16957 160 : if (OidIsValid(reltoastrelid))
16958 20 : ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16959 180 : foreach(lc, reltoastidxids)
16960 20 : ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16961 :
16962 : /* Clean up */
16963 160 : list_free(reltoastidxids);
16964 : }
16965 :
16966 : /*
16967 : * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16968 : * storage that have an interest in preserving tablespace.
16969 : *
16970 : * Since these have no storage the tablespace can be updated with a simple
16971 : * metadata only operation to update the tablespace.
16972 : */
16973 : static void
16974 36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
16975 : {
16976 : /*
16977 : * Shouldn't be called on relations having storage; these are processed in
16978 : * phase 3.
16979 : */
16980 : Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16981 :
16982 : /* check if relation can be moved to its new tablespace */
16983 36 : if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16984 : {
16985 0 : InvokeObjectPostAlterHook(RelationRelationId,
16986 : RelationGetRelid(rel),
16987 : 0);
16988 0 : return;
16989 : }
16990 :
16991 : /* Update can be done, so change reltablespace */
16992 30 : SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16993 :
16994 30 : InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16995 :
16996 : /* Make sure the reltablespace change is visible */
16997 30 : CommandCounterIncrement();
16998 : }
16999 :
17000 : /*
17001 : * Alter Table ALL ... SET TABLESPACE
17002 : *
17003 : * Allows a user to move all objects of some type in a given tablespace in the
17004 : * current database to another tablespace. Objects can be chosen based on the
17005 : * owner of the object also, to allow users to move only their objects.
17006 : * The user must have CREATE rights on the new tablespace, as usual. The main
17007 : * permissions handling is done by the lower-level table move function.
17008 : *
17009 : * All to-be-moved objects are locked first. If NOWAIT is specified and the
17010 : * lock can't be acquired then we ereport(ERROR).
17011 : */
17012 : Oid
17013 30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
17014 : {
17015 30 : List *relations = NIL;
17016 : ListCell *l;
17017 : ScanKeyData key[1];
17018 : Relation rel;
17019 : TableScanDesc scan;
17020 : HeapTuple tuple;
17021 : Oid orig_tablespaceoid;
17022 : Oid new_tablespaceoid;
17023 30 : List *role_oids = roleSpecsToIds(stmt->roles);
17024 :
17025 : /* Ensure we were not asked to move something we can't */
17026 30 : if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17027 12 : stmt->objtype != OBJECT_MATVIEW)
17028 0 : ereport(ERROR,
17029 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17030 : errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17031 :
17032 : /* Get the orig and new tablespace OIDs */
17033 30 : orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17034 30 : new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17035 :
17036 : /* Can't move shared relations in to or out of pg_global */
17037 : /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17038 30 : if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
17039 : new_tablespaceoid == GLOBALTABLESPACE_OID)
17040 0 : ereport(ERROR,
17041 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17042 : errmsg("cannot move relations in to or out of pg_global tablespace")));
17043 :
17044 : /*
17045 : * Must have CREATE rights on the new tablespace, unless it is the
17046 : * database default tablespace (which all users implicitly have CREATE
17047 : * rights on).
17048 : */
17049 30 : if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
17050 : {
17051 : AclResult aclresult;
17052 :
17053 0 : aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
17054 : ACL_CREATE);
17055 0 : if (aclresult != ACLCHECK_OK)
17056 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
17057 0 : get_tablespace_name(new_tablespaceoid));
17058 : }
17059 :
17060 : /*
17061 : * Now that the checks are done, check if we should set either to
17062 : * InvalidOid because it is our database's default tablespace.
17063 : */
17064 30 : if (orig_tablespaceoid == MyDatabaseTableSpace)
17065 0 : orig_tablespaceoid = InvalidOid;
17066 :
17067 30 : if (new_tablespaceoid == MyDatabaseTableSpace)
17068 30 : new_tablespaceoid = InvalidOid;
17069 :
17070 : /* no-op */
17071 30 : if (orig_tablespaceoid == new_tablespaceoid)
17072 0 : return new_tablespaceoid;
17073 :
17074 : /*
17075 : * Walk the list of objects in the tablespace and move them. This will
17076 : * only find objects in our database, of course.
17077 : */
17078 30 : ScanKeyInit(&key[0],
17079 : Anum_pg_class_reltablespace,
17080 : BTEqualStrategyNumber, F_OIDEQ,
17081 : ObjectIdGetDatum(orig_tablespaceoid));
17082 :
17083 30 : rel = table_open(RelationRelationId, AccessShareLock);
17084 30 : scan = table_beginscan_catalog(rel, 1, key);
17085 132 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17086 : {
17087 102 : Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17088 102 : Oid relOid = relForm->oid;
17089 :
17090 : /*
17091 : * Do not move objects in pg_catalog as part of this, if an admin
17092 : * really wishes to do so, they can issue the individual ALTER
17093 : * commands directly.
17094 : *
17095 : * Also, explicitly avoid any shared tables, temp tables, or TOAST
17096 : * (TOAST will be moved with the main table).
17097 : */
17098 102 : if (IsCatalogNamespace(relForm->relnamespace) ||
17099 204 : relForm->relisshared ||
17100 204 : isAnyTempNamespace(relForm->relnamespace) ||
17101 102 : IsToastNamespace(relForm->relnamespace))
17102 0 : continue;
17103 :
17104 : /* Only move the object type requested */
17105 102 : if ((stmt->objtype == OBJECT_TABLE &&
17106 60 : relForm->relkind != RELKIND_RELATION &&
17107 36 : relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17108 66 : (stmt->objtype == OBJECT_INDEX &&
17109 36 : relForm->relkind != RELKIND_INDEX &&
17110 6 : relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17111 60 : (stmt->objtype == OBJECT_MATVIEW &&
17112 6 : relForm->relkind != RELKIND_MATVIEW))
17113 42 : continue;
17114 :
17115 : /* Check if we are only moving objects owned by certain roles */
17116 60 : if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17117 0 : continue;
17118 :
17119 : /*
17120 : * Handle permissions-checking here since we are locking the tables
17121 : * and also to avoid doing a bunch of work only to fail part-way. Note
17122 : * that permissions will also be checked by AlterTableInternal().
17123 : *
17124 : * Caller must be considered an owner on the table to move it.
17125 : */
17126 60 : if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17127 0 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
17128 0 : NameStr(relForm->relname));
17129 :
17130 60 : if (stmt->nowait &&
17131 0 : !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
17132 0 : ereport(ERROR,
17133 : (errcode(ERRCODE_OBJECT_IN_USE),
17134 : errmsg("aborting because lock on relation \"%s.%s\" is not available",
17135 : get_namespace_name(relForm->relnamespace),
17136 : NameStr(relForm->relname))));
17137 : else
17138 60 : LockRelationOid(relOid, AccessExclusiveLock);
17139 :
17140 : /* Add to our list of objects to move */
17141 60 : relations = lappend_oid(relations, relOid);
17142 : }
17143 :
17144 30 : table_endscan(scan);
17145 30 : table_close(rel, AccessShareLock);
17146 :
17147 30 : if (relations == NIL)
17148 12 : ereport(NOTICE,
17149 : (errcode(ERRCODE_NO_DATA_FOUND),
17150 : errmsg("no matching relations in tablespace \"%s\" found",
17151 : orig_tablespaceoid == InvalidOid ? "(database default)" :
17152 : get_tablespace_name(orig_tablespaceoid))));
17153 :
17154 : /* Everything is locked, loop through and move all of the relations. */
17155 90 : foreach(l, relations)
17156 : {
17157 60 : List *cmds = NIL;
17158 60 : AlterTableCmd *cmd = makeNode(AlterTableCmd);
17159 :
17160 60 : cmd->subtype = AT_SetTableSpace;
17161 60 : cmd->name = stmt->new_tablespacename;
17162 :
17163 60 : cmds = lappend(cmds, cmd);
17164 :
17165 60 : EventTriggerAlterTableStart((Node *) stmt);
17166 : /* OID is set by AlterTableInternal */
17167 60 : AlterTableInternal(lfirst_oid(l), cmds, false);
17168 60 : EventTriggerAlterTableEnd();
17169 : }
17170 :
17171 30 : return new_tablespaceoid;
17172 : }
17173 :
17174 : static void
17175 62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
17176 : {
17177 : SMgrRelation dstrel;
17178 :
17179 : /*
17180 : * Since we copy the file directly without looking at the shared buffers,
17181 : * we'd better first flush out any pages of the source relation that are
17182 : * in shared buffers. We assume no new changes will be made while we are
17183 : * holding exclusive lock on the rel.
17184 : */
17185 62 : FlushRelationBuffers(rel);
17186 :
17187 : /*
17188 : * Create and copy all forks of the relation, and schedule unlinking of
17189 : * old physical files.
17190 : *
17191 : * NOTE: any conflict in relfilenumber value will be caught in
17192 : * RelationCreateStorage().
17193 : */
17194 62 : dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17195 :
17196 : /* copy main fork */
17197 62 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
17198 62 : rel->rd_rel->relpersistence);
17199 :
17200 : /* copy those extra forks that exist */
17201 62 : for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17202 248 : forkNum <= MAX_FORKNUM; forkNum++)
17203 : {
17204 186 : if (smgrexists(RelationGetSmgr(rel), forkNum))
17205 : {
17206 0 : smgrcreate(dstrel, forkNum, false);
17207 :
17208 : /*
17209 : * WAL log creation if the relation is persistent, or this is the
17210 : * init fork of an unlogged relation.
17211 : */
17212 0 : if (RelationIsPermanent(rel) ||
17213 0 : (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17214 : forkNum == INIT_FORKNUM))
17215 0 : log_smgrcreate(&newrlocator, forkNum);
17216 0 : RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17217 0 : rel->rd_rel->relpersistence);
17218 : }
17219 : }
17220 :
17221 : /* drop old relation, and close new one */
17222 62 : RelationDropStorage(rel);
17223 62 : smgrclose(dstrel);
17224 62 : }
17225 :
17226 : /*
17227 : * ALTER TABLE ENABLE/DISABLE TRIGGER
17228 : *
17229 : * We just pass this off to trigger.c.
17230 : */
17231 : static void
17232 342 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17233 : char fires_when, bool skip_system, bool recurse,
17234 : LOCKMODE lockmode)
17235 : {
17236 342 : EnableDisableTrigger(rel, trigname, InvalidOid,
17237 : fires_when, skip_system, recurse,
17238 : lockmode);
17239 :
17240 342 : InvokeObjectPostAlterHook(RelationRelationId,
17241 : RelationGetRelid(rel), 0);
17242 342 : }
17243 :
17244 : /*
17245 : * ALTER TABLE ENABLE/DISABLE RULE
17246 : *
17247 : * We just pass this off to rewriteDefine.c.
17248 : */
17249 : static void
17250 46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
17251 : char fires_when, LOCKMODE lockmode)
17252 : {
17253 46 : EnableDisableRule(rel, rulename, fires_when);
17254 :
17255 46 : InvokeObjectPostAlterHook(RelationRelationId,
17256 : RelationGetRelid(rel), 0);
17257 46 : }
17258 :
17259 : /*
17260 : * ALTER TABLE INHERIT
17261 : *
17262 : * Add a parent to the child's parents. This verifies that all the columns and
17263 : * check constraints of the parent appear in the child and that they have the
17264 : * same data types and expressions.
17265 : */
17266 : static void
17267 464 : ATPrepAddInherit(Relation child_rel)
17268 : {
17269 464 : if (child_rel->rd_rel->reloftype)
17270 6 : ereport(ERROR,
17271 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17272 : errmsg("cannot change inheritance of typed table")));
17273 :
17274 458 : if (child_rel->rd_rel->relispartition)
17275 6 : ereport(ERROR,
17276 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17277 : errmsg("cannot change inheritance of a partition")));
17278 :
17279 452 : if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17280 6 : ereport(ERROR,
17281 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17282 : errmsg("cannot change inheritance of partitioned table")));
17283 446 : }
17284 :
17285 : /*
17286 : * Return the address of the new parent relation.
17287 : */
17288 : static ObjectAddress
17289 446 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17290 : {
17291 : Relation parent_rel;
17292 : List *children;
17293 : ObjectAddress address;
17294 : const char *trigger_name;
17295 :
17296 : /*
17297 : * A self-exclusive lock is needed here. See the similar case in
17298 : * MergeAttributes() for a full explanation.
17299 : */
17300 446 : parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17301 :
17302 : /*
17303 : * Must be owner of both parent and child -- child was checked by
17304 : * ATSimplePermissions call in ATPrepCmd
17305 : */
17306 446 : ATSimplePermissions(AT_AddInherit, parent_rel,
17307 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
17308 :
17309 : /* Permanent rels cannot inherit from temporary ones */
17310 446 : if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17311 6 : child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17312 0 : ereport(ERROR,
17313 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17314 : errmsg("cannot inherit from temporary relation \"%s\"",
17315 : RelationGetRelationName(parent_rel))));
17316 :
17317 : /* If parent rel is temp, it must belong to this session */
17318 446 : if (RELATION_IS_OTHER_TEMP(parent_rel))
17319 0 : ereport(ERROR,
17320 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17321 : errmsg("cannot inherit from temporary relation of another session")));
17322 :
17323 : /* Ditto for the child */
17324 446 : if (RELATION_IS_OTHER_TEMP(child_rel))
17325 0 : ereport(ERROR,
17326 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17327 : errmsg("cannot inherit to temporary relation of another session")));
17328 :
17329 : /* Prevent partitioned tables from becoming inheritance parents */
17330 446 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17331 6 : ereport(ERROR,
17332 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17333 : errmsg("cannot inherit from partitioned table \"%s\"",
17334 : parent->relname)));
17335 :
17336 : /* Likewise for partitions */
17337 440 : if (parent_rel->rd_rel->relispartition)
17338 6 : ereport(ERROR,
17339 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17340 : errmsg("cannot inherit from a partition")));
17341 :
17342 : /*
17343 : * Prevent circularity by seeing if proposed parent inherits from child.
17344 : * (In particular, this disallows making a rel inherit from itself.)
17345 : *
17346 : * This is not completely bulletproof because of race conditions: in
17347 : * multi-level inheritance trees, someone else could concurrently be
17348 : * making another inheritance link that closes the loop but does not join
17349 : * either of the rels we have locked. Preventing that seems to require
17350 : * exclusive locks on the entire inheritance tree, which is a cure worse
17351 : * than the disease. find_all_inheritors() will cope with circularity
17352 : * anyway, so don't sweat it too much.
17353 : *
17354 : * We use weakest lock we can on child's children, namely AccessShareLock.
17355 : */
17356 434 : children = find_all_inheritors(RelationGetRelid(child_rel),
17357 : AccessShareLock, NULL);
17358 :
17359 434 : if (list_member_oid(children, RelationGetRelid(parent_rel)))
17360 12 : ereport(ERROR,
17361 : (errcode(ERRCODE_DUPLICATE_TABLE),
17362 : errmsg("circular inheritance not allowed"),
17363 : errdetail("\"%s\" is already a child of \"%s\".",
17364 : parent->relname,
17365 : RelationGetRelationName(child_rel))));
17366 :
17367 : /*
17368 : * If child_rel has row-level triggers with transition tables, we
17369 : * currently don't allow it to become an inheritance child. See also
17370 : * prohibitions in ATExecAttachPartition() and CreateTrigger().
17371 : */
17372 422 : trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17373 422 : if (trigger_name != NULL)
17374 6 : ereport(ERROR,
17375 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17376 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17377 : trigger_name, RelationGetRelationName(child_rel)),
17378 : errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17379 :
17380 : /* OK to create inheritance */
17381 416 : CreateInheritance(child_rel, parent_rel, false);
17382 :
17383 326 : ObjectAddressSet(address, RelationRelationId,
17384 : RelationGetRelid(parent_rel));
17385 :
17386 : /* keep our lock on the parent relation until commit */
17387 326 : table_close(parent_rel, NoLock);
17388 :
17389 326 : return address;
17390 : }
17391 :
17392 : /*
17393 : * CreateInheritance
17394 : * Catalog manipulation portion of creating inheritance between a child
17395 : * table and a parent table.
17396 : *
17397 : * Common to ATExecAddInherit() and ATExecAttachPartition().
17398 : */
17399 : static void
17400 3432 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17401 : {
17402 : Relation catalogRelation;
17403 : SysScanDesc scan;
17404 : ScanKeyData key;
17405 : HeapTuple inheritsTuple;
17406 : int32 inhseqno;
17407 :
17408 : /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17409 3432 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17410 :
17411 : /*
17412 : * Check for duplicates in the list of parents, and determine the highest
17413 : * inhseqno already present; we'll use the next one for the new parent.
17414 : * Also, if proposed child is a partition, it cannot already be
17415 : * inheriting.
17416 : *
17417 : * Note: we do not reject the case where the child already inherits from
17418 : * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17419 : */
17420 3432 : ScanKeyInit(&key,
17421 : Anum_pg_inherits_inhrelid,
17422 : BTEqualStrategyNumber, F_OIDEQ,
17423 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17424 3432 : scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17425 : true, NULL, 1, &key);
17426 :
17427 : /* inhseqno sequences start at 1 */
17428 3432 : inhseqno = 0;
17429 3502 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17430 : {
17431 76 : Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17432 :
17433 76 : if (inh->inhparent == RelationGetRelid(parent_rel))
17434 6 : ereport(ERROR,
17435 : (errcode(ERRCODE_DUPLICATE_TABLE),
17436 : errmsg("relation \"%s\" would be inherited from more than once",
17437 : RelationGetRelationName(parent_rel))));
17438 :
17439 70 : if (inh->inhseqno > inhseqno)
17440 70 : inhseqno = inh->inhseqno;
17441 : }
17442 3426 : systable_endscan(scan);
17443 :
17444 : /* Match up the columns and bump attinhcount as needed */
17445 3426 : MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17446 :
17447 : /* Match up the constraints and bump coninhcount as needed */
17448 3294 : MergeConstraintsIntoExisting(child_rel, parent_rel);
17449 :
17450 : /*
17451 : * OK, it looks valid. Make the catalog entries that show inheritance.
17452 : */
17453 3234 : StoreCatalogInheritance1(RelationGetRelid(child_rel),
17454 : RelationGetRelid(parent_rel),
17455 : inhseqno + 1,
17456 : catalogRelation,
17457 3234 : parent_rel->rd_rel->relkind ==
17458 : RELKIND_PARTITIONED_TABLE);
17459 :
17460 : /* Now we're done with pg_inherits */
17461 3234 : table_close(catalogRelation, RowExclusiveLock);
17462 3234 : }
17463 :
17464 : /*
17465 : * Obtain the source-text form of the constraint expression for a check
17466 : * constraint, given its pg_constraint tuple
17467 : */
17468 : static char *
17469 388 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
17470 : {
17471 : Form_pg_constraint con;
17472 : bool isnull;
17473 : Datum attr;
17474 : Datum expr;
17475 :
17476 388 : con = (Form_pg_constraint) GETSTRUCT(contup);
17477 388 : attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17478 388 : if (isnull)
17479 0 : elog(ERROR, "null conbin for constraint %u", con->oid);
17480 :
17481 388 : expr = DirectFunctionCall2(pg_get_expr, attr,
17482 : ObjectIdGetDatum(con->conrelid));
17483 388 : return TextDatumGetCString(expr);
17484 : }
17485 :
17486 : /*
17487 : * Determine whether two check constraints are functionally equivalent
17488 : *
17489 : * The test we apply is to see whether they reverse-compile to the same
17490 : * source string. This insulates us from issues like whether attributes
17491 : * have the same physical column numbers in parent and child relations.
17492 : *
17493 : * Note that we ignore enforceability as there are cases where constraints
17494 : * with differing enforceability are allowed.
17495 : */
17496 : static bool
17497 194 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
17498 : {
17499 194 : Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
17500 194 : Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
17501 :
17502 194 : if (acon->condeferrable != bcon->condeferrable ||
17503 194 : acon->condeferred != bcon->condeferred ||
17504 194 : strcmp(decompile_conbin(a, tupleDesc),
17505 194 : decompile_conbin(b, tupleDesc)) != 0)
17506 6 : return false;
17507 : else
17508 188 : return true;
17509 : }
17510 :
17511 : /*
17512 : * Check columns in child table match up with columns in parent, and increment
17513 : * their attinhcount.
17514 : *
17515 : * Called by CreateInheritance
17516 : *
17517 : * Currently all parent columns must be found in child. Missing columns are an
17518 : * error. One day we might consider creating new columns like CREATE TABLE
17519 : * does. However, that is widely unpopular --- in the common use case of
17520 : * partitioned tables it's a foot-gun.
17521 : *
17522 : * The data type must match exactly. If the parent column is NOT NULL then
17523 : * the child must be as well. Defaults are not compared, however.
17524 : */
17525 : static void
17526 3426 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17527 : {
17528 : Relation attrrel;
17529 : TupleDesc parent_desc;
17530 :
17531 3426 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17532 3426 : parent_desc = RelationGetDescr(parent_rel);
17533 :
17534 11254 : for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17535 : {
17536 7960 : Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17537 7960 : char *parent_attname = NameStr(parent_att->attname);
17538 : HeapTuple tuple;
17539 :
17540 : /* Ignore dropped columns in the parent. */
17541 7960 : if (parent_att->attisdropped)
17542 296 : continue;
17543 :
17544 : /* Find same column in child (matching on column name). */
17545 7664 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17546 7664 : if (HeapTupleIsValid(tuple))
17547 : {
17548 7652 : Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17549 :
17550 7652 : if (parent_att->atttypid != child_att->atttypid ||
17551 7646 : parent_att->atttypmod != child_att->atttypmod)
17552 12 : ereport(ERROR,
17553 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17554 : errmsg("child table \"%s\" has different type for column \"%s\"",
17555 : RelationGetRelationName(child_rel), parent_attname)));
17556 :
17557 7640 : if (parent_att->attcollation != child_att->attcollation)
17558 6 : ereport(ERROR,
17559 : (errcode(ERRCODE_COLLATION_MISMATCH),
17560 : errmsg("child table \"%s\" has different collation for column \"%s\"",
17561 : RelationGetRelationName(child_rel), parent_attname)));
17562 :
17563 : /*
17564 : * If the parent has a not-null constraint that's not NO INHERIT,
17565 : * make sure the child has one too.
17566 : *
17567 : * Other constraints are checked elsewhere.
17568 : */
17569 7634 : if (parent_att->attnotnull && !child_att->attnotnull)
17570 : {
17571 : HeapTuple contup;
17572 :
17573 48 : contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17574 48 : parent_att->attnum);
17575 48 : if (HeapTupleIsValid(contup) &&
17576 48 : !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17577 30 : ereport(ERROR,
17578 : errcode(ERRCODE_DATATYPE_MISMATCH),
17579 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17580 : parent_attname, RelationGetRelationName(child_rel)));
17581 : }
17582 :
17583 : /*
17584 : * Child column must be generated if and only if parent column is.
17585 : */
17586 7604 : if (parent_att->attgenerated && !child_att->attgenerated)
17587 36 : ereport(ERROR,
17588 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17589 : errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17590 7568 : if (child_att->attgenerated && !parent_att->attgenerated)
17591 24 : ereport(ERROR,
17592 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17593 : errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17594 :
17595 7544 : if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17596 12 : ereport(ERROR,
17597 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17598 : errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17599 : errdetail("Parent column is %s, child column is %s.",
17600 : parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17601 : child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17602 :
17603 : /*
17604 : * Regular inheritance children are independent enough not to
17605 : * inherit identity columns. But partitions are integral part of
17606 : * a partitioned table and inherit identity column.
17607 : */
17608 7532 : if (ispartition)
17609 6804 : child_att->attidentity = parent_att->attidentity;
17610 :
17611 : /*
17612 : * OK, bump the child column's inheritance count. (If we fail
17613 : * later on, this change will just roll back.)
17614 : */
17615 7532 : if (pg_add_s16_overflow(child_att->attinhcount, 1,
17616 : &child_att->attinhcount))
17617 0 : ereport(ERROR,
17618 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17619 : errmsg("too many inheritance parents"));
17620 :
17621 : /*
17622 : * In case of partitions, we must enforce that value of attislocal
17623 : * is same in all partitions. (Note: there are only inherited
17624 : * attributes in partitions)
17625 : */
17626 7532 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17627 : {
17628 : Assert(child_att->attinhcount == 1);
17629 6804 : child_att->attislocal = false;
17630 : }
17631 :
17632 7532 : CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17633 7532 : heap_freetuple(tuple);
17634 : }
17635 : else
17636 : {
17637 12 : ereport(ERROR,
17638 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17639 : errmsg("child table is missing column \"%s\"", parent_attname)));
17640 : }
17641 : }
17642 :
17643 3294 : table_close(attrrel, RowExclusiveLock);
17644 3294 : }
17645 :
17646 : /*
17647 : * Check constraints in child table match up with constraints in parent,
17648 : * and increment their coninhcount.
17649 : *
17650 : * Constraints that are marked ONLY in the parent are ignored.
17651 : *
17652 : * Called by CreateInheritance
17653 : *
17654 : * Currently all constraints in parent must be present in the child. One day we
17655 : * may consider adding new constraints like CREATE TABLE does.
17656 : *
17657 : * XXX This is O(N^2) which may be an issue with tables with hundreds of
17658 : * constraints. As long as tables have more like 10 constraints it shouldn't be
17659 : * a problem though. Even 100 constraints ought not be the end of the world.
17660 : *
17661 : * XXX See MergeWithExistingConstraint too if you change this code.
17662 : */
17663 : static void
17664 3294 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
17665 : {
17666 : Relation constraintrel;
17667 : SysScanDesc parent_scan;
17668 : ScanKeyData parent_key;
17669 : HeapTuple parent_tuple;
17670 3294 : Oid parent_relid = RelationGetRelid(parent_rel);
17671 : AttrMap *attmap;
17672 :
17673 3294 : constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17674 :
17675 : /* Outer loop scans through the parent's constraint definitions */
17676 3294 : ScanKeyInit(&parent_key,
17677 : Anum_pg_constraint_conrelid,
17678 : BTEqualStrategyNumber, F_OIDEQ,
17679 : ObjectIdGetDatum(parent_relid));
17680 3294 : parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17681 : true, NULL, 1, &parent_key);
17682 :
17683 3294 : attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17684 : RelationGetDescr(child_rel),
17685 : true);
17686 :
17687 5746 : while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17688 : {
17689 2512 : Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17690 : SysScanDesc child_scan;
17691 : ScanKeyData child_key;
17692 : HeapTuple child_tuple;
17693 : AttrNumber parent_attno;
17694 2512 : bool found = false;
17695 :
17696 2512 : if (parent_con->contype != CONSTRAINT_CHECK &&
17697 2274 : parent_con->contype != CONSTRAINT_NOTNULL)
17698 1162 : continue;
17699 :
17700 : /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17701 1394 : if (parent_con->connoinherit)
17702 44 : continue;
17703 :
17704 1350 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17705 1132 : parent_attno = extractNotNullColumn(parent_tuple);
17706 : else
17707 218 : parent_attno = InvalidAttrNumber;
17708 :
17709 : /* Search for a child constraint matching this one */
17710 1350 : ScanKeyInit(&child_key,
17711 : Anum_pg_constraint_conrelid,
17712 : BTEqualStrategyNumber, F_OIDEQ,
17713 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
17714 1350 : child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17715 : true, NULL, 1, &child_key);
17716 :
17717 2138 : while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17718 : {
17719 2114 : Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17720 : HeapTuple child_copy;
17721 :
17722 2114 : if (child_con->contype != parent_con->contype)
17723 412 : continue;
17724 :
17725 : /*
17726 : * CHECK constraint are matched by constraint name, NOT NULL ones
17727 : * by attribute number.
17728 : */
17729 1702 : if (child_con->contype == CONSTRAINT_CHECK)
17730 : {
17731 374 : if (strcmp(NameStr(parent_con->conname),
17732 284 : NameStr(child_con->conname)) != 0)
17733 90 : continue;
17734 : }
17735 1418 : else if (child_con->contype == CONSTRAINT_NOTNULL)
17736 : {
17737 : Form_pg_attribute parent_attr;
17738 : Form_pg_attribute child_attr;
17739 : AttrNumber child_attno;
17740 :
17741 1418 : parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17742 1418 : child_attno = extractNotNullColumn(child_tuple);
17743 1418 : if (parent_attno != attmap->attnums[child_attno - 1])
17744 286 : continue;
17745 :
17746 1132 : child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17747 : /* there shouldn't be constraints on dropped columns */
17748 1132 : if (parent_attr->attisdropped || child_attr->attisdropped)
17749 0 : elog(ERROR, "found not-null constraint on dropped columns");
17750 : }
17751 :
17752 1326 : if (child_con->contype == CONSTRAINT_CHECK &&
17753 194 : !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17754 6 : ereport(ERROR,
17755 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17756 : errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17757 : RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17758 :
17759 : /*
17760 : * If the child constraint is "no inherit" then cannot merge
17761 : */
17762 1320 : if (child_con->connoinherit)
17763 12 : ereport(ERROR,
17764 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17765 : errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17766 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17767 :
17768 : /*
17769 : * If the child constraint is "not valid" then cannot merge with a
17770 : * valid parent constraint
17771 : */
17772 1308 : if (parent_con->convalidated && child_con->conenforced &&
17773 1200 : !child_con->convalidated)
17774 12 : ereport(ERROR,
17775 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17776 : errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17777 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17778 :
17779 : /*
17780 : * A NOT ENFORCED child constraint cannot be merged with an
17781 : * ENFORCED parent constraint. However, the reverse is allowed,
17782 : * where the child constraint is ENFORCED.
17783 : */
17784 1296 : if (parent_con->conenforced && !child_con->conenforced)
17785 6 : ereport(ERROR,
17786 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17787 : errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17788 : NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17789 :
17790 : /*
17791 : * OK, bump the child constraint's inheritance count. (If we fail
17792 : * later on, this change will just roll back.)
17793 : */
17794 1290 : child_copy = heap_copytuple(child_tuple);
17795 1290 : child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17796 :
17797 1290 : if (pg_add_s16_overflow(child_con->coninhcount, 1,
17798 : &child_con->coninhcount))
17799 0 : ereport(ERROR,
17800 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17801 : errmsg("too many inheritance parents"));
17802 :
17803 : /*
17804 : * In case of partitions, an inherited constraint must be
17805 : * inherited only once since it cannot have multiple parents and
17806 : * it is never considered local.
17807 : */
17808 1290 : if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17809 : {
17810 : Assert(child_con->coninhcount == 1);
17811 1142 : child_con->conislocal = false;
17812 : }
17813 :
17814 1290 : CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17815 1290 : heap_freetuple(child_copy);
17816 :
17817 1290 : found = true;
17818 1290 : break;
17819 : }
17820 :
17821 1314 : systable_endscan(child_scan);
17822 :
17823 1314 : if (!found)
17824 : {
17825 24 : if (parent_con->contype == CONSTRAINT_NOTNULL)
17826 0 : ereport(ERROR,
17827 : errcode(ERRCODE_DATATYPE_MISMATCH),
17828 : errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17829 : get_attname(parent_relid,
17830 : extractNotNullColumn(parent_tuple),
17831 : false),
17832 : RelationGetRelationName(child_rel)));
17833 :
17834 24 : ereport(ERROR,
17835 : (errcode(ERRCODE_DATATYPE_MISMATCH),
17836 : errmsg("child table is missing constraint \"%s\"",
17837 : NameStr(parent_con->conname))));
17838 : }
17839 : }
17840 :
17841 3234 : systable_endscan(parent_scan);
17842 3234 : table_close(constraintrel, RowExclusiveLock);
17843 3234 : }
17844 :
17845 : /*
17846 : * ALTER TABLE NO INHERIT
17847 : *
17848 : * Return value is the address of the relation that is no longer parent.
17849 : */
17850 : static ObjectAddress
17851 94 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
17852 : {
17853 : ObjectAddress address;
17854 : Relation parent_rel;
17855 :
17856 94 : if (rel->rd_rel->relispartition)
17857 0 : ereport(ERROR,
17858 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17859 : errmsg("cannot change inheritance of a partition")));
17860 :
17861 : /*
17862 : * AccessShareLock on the parent is probably enough, seeing that DROP
17863 : * TABLE doesn't lock parent tables at all. We need some lock since we'll
17864 : * be inspecting the parent's schema.
17865 : */
17866 94 : parent_rel = table_openrv(parent, AccessShareLock);
17867 :
17868 : /*
17869 : * We don't bother to check ownership of the parent table --- ownership of
17870 : * the child is presumed enough rights.
17871 : */
17872 :
17873 : /* Off to RemoveInheritance() where most of the work happens */
17874 94 : RemoveInheritance(rel, parent_rel, false);
17875 :
17876 88 : ObjectAddressSet(address, RelationRelationId,
17877 : RelationGetRelid(parent_rel));
17878 :
17879 : /* keep our lock on the parent relation until commit */
17880 88 : table_close(parent_rel, NoLock);
17881 :
17882 88 : return address;
17883 : }
17884 :
17885 : /*
17886 : * MarkInheritDetached
17887 : *
17888 : * Set inhdetachpending for a partition, for ATExecDetachPartition
17889 : * in concurrent mode. While at it, verify that no other partition is
17890 : * already pending detach.
17891 : */
17892 : static void
17893 146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
17894 : {
17895 : Relation catalogRelation;
17896 : SysScanDesc scan;
17897 : ScanKeyData key;
17898 : HeapTuple inheritsTuple;
17899 146 : bool found = false;
17900 :
17901 : Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17902 :
17903 : /*
17904 : * Find pg_inherits entries by inhparent. (We need to scan them all in
17905 : * order to verify that no other partition is pending detach.)
17906 : */
17907 146 : catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17908 146 : ScanKeyInit(&key,
17909 : Anum_pg_inherits_inhparent,
17910 : BTEqualStrategyNumber, F_OIDEQ,
17911 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17912 146 : scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17913 : true, NULL, 1, &key);
17914 :
17915 576 : while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17916 : {
17917 : Form_pg_inherits inhForm;
17918 :
17919 286 : inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17920 286 : if (inhForm->inhdetachpending)
17921 2 : ereport(ERROR,
17922 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17923 : errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17924 : get_rel_name(inhForm->inhrelid),
17925 : get_namespace_name(parent_rel->rd_rel->relnamespace),
17926 : RelationGetRelationName(parent_rel)),
17927 : errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17928 :
17929 284 : if (inhForm->inhrelid == RelationGetRelid(child_rel))
17930 : {
17931 : HeapTuple newtup;
17932 :
17933 144 : newtup = heap_copytuple(inheritsTuple);
17934 144 : ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17935 :
17936 144 : CatalogTupleUpdate(catalogRelation,
17937 144 : &inheritsTuple->t_self,
17938 : newtup);
17939 144 : found = true;
17940 144 : heap_freetuple(newtup);
17941 : /* keep looking, to ensure we catch others pending detach */
17942 : }
17943 : }
17944 :
17945 : /* Done */
17946 144 : systable_endscan(scan);
17947 144 : table_close(catalogRelation, RowExclusiveLock);
17948 :
17949 144 : if (!found)
17950 0 : ereport(ERROR,
17951 : (errcode(ERRCODE_UNDEFINED_TABLE),
17952 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17953 : RelationGetRelationName(child_rel),
17954 : RelationGetRelationName(parent_rel))));
17955 144 : }
17956 :
17957 : /*
17958 : * RemoveInheritance
17959 : *
17960 : * Drop a parent from the child's parents. This just adjusts the attinhcount
17961 : * and attislocal of the columns and removes the pg_inherit and pg_depend
17962 : * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17963 : *
17964 : * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17965 : * up attislocal stays true, which means if a child is ever removed from a
17966 : * parent then its columns will never be automatically dropped which may
17967 : * surprise. But at least we'll never surprise by dropping columns someone
17968 : * isn't expecting to be dropped which would actually mean data loss.
17969 : *
17970 : * coninhcount and conislocal for inherited constraints are adjusted in
17971 : * exactly the same way.
17972 : *
17973 : * Common to ATExecDropInherit() and ATExecDetachPartition().
17974 : */
17975 : static void
17976 1182 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17977 : {
17978 : Relation catalogRelation;
17979 : SysScanDesc scan;
17980 : ScanKeyData key[3];
17981 : HeapTuple attributeTuple,
17982 : constraintTuple;
17983 : AttrMap *attmap;
17984 : List *connames;
17985 : List *nncolumns;
17986 : bool found;
17987 : bool is_partitioning;
17988 :
17989 1182 : is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17990 :
17991 1182 : found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17992 : RelationGetRelid(parent_rel),
17993 : expect_detached,
17994 1182 : RelationGetRelationName(child_rel));
17995 1182 : if (!found)
17996 : {
17997 24 : if (is_partitioning)
17998 18 : ereport(ERROR,
17999 : (errcode(ERRCODE_UNDEFINED_TABLE),
18000 : errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18001 : RelationGetRelationName(child_rel),
18002 : RelationGetRelationName(parent_rel))));
18003 : else
18004 6 : ereport(ERROR,
18005 : (errcode(ERRCODE_UNDEFINED_TABLE),
18006 : errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18007 : RelationGetRelationName(parent_rel),
18008 : RelationGetRelationName(child_rel))));
18009 : }
18010 :
18011 : /*
18012 : * Search through child columns looking for ones matching parent rel
18013 : */
18014 1158 : catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
18015 1158 : ScanKeyInit(&key[0],
18016 : Anum_pg_attribute_attrelid,
18017 : BTEqualStrategyNumber, F_OIDEQ,
18018 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
18019 1158 : scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
18020 : true, NULL, 1, key);
18021 10664 : while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
18022 : {
18023 9506 : Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
18024 :
18025 : /* Ignore if dropped or not inherited */
18026 9506 : if (att->attisdropped)
18027 42 : continue;
18028 9464 : if (att->attinhcount <= 0)
18029 6978 : continue;
18030 :
18031 2486 : if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
18032 2486 : NameStr(att->attname)))
18033 : {
18034 : /* Decrement inhcount and possibly set islocal to true */
18035 2432 : HeapTuple copyTuple = heap_copytuple(attributeTuple);
18036 2432 : Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
18037 :
18038 2432 : copy_att->attinhcount--;
18039 2432 : if (copy_att->attinhcount == 0)
18040 2402 : copy_att->attislocal = true;
18041 :
18042 2432 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18043 2432 : heap_freetuple(copyTuple);
18044 : }
18045 : }
18046 1158 : systable_endscan(scan);
18047 1158 : table_close(catalogRelation, RowExclusiveLock);
18048 :
18049 : /*
18050 : * Likewise, find inherited check and not-null constraints and disinherit
18051 : * them. To do this, we first need a list of the names of the parent's
18052 : * check constraints. (We cheat a bit by only checking for name matches,
18053 : * assuming that the expressions will match.)
18054 : *
18055 : * For NOT NULL columns, we store column numbers to match, mapping them in
18056 : * to the child rel's attribute numbers.
18057 : */
18058 1158 : attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
18059 : RelationGetDescr(parent_rel),
18060 : false);
18061 :
18062 1158 : catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
18063 1158 : ScanKeyInit(&key[0],
18064 : Anum_pg_constraint_conrelid,
18065 : BTEqualStrategyNumber, F_OIDEQ,
18066 : ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18067 1158 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18068 : true, NULL, 1, key);
18069 :
18070 1158 : connames = NIL;
18071 1158 : nncolumns = NIL;
18072 :
18073 2208 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18074 : {
18075 1050 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18076 :
18077 1050 : if (con->connoinherit)
18078 224 : continue;
18079 :
18080 826 : if (con->contype == CONSTRAINT_CHECK)
18081 114 : connames = lappend(connames, pstrdup(NameStr(con->conname)));
18082 826 : if (con->contype == CONSTRAINT_NOTNULL)
18083 : {
18084 382 : AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
18085 :
18086 382 : nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18087 : }
18088 : }
18089 :
18090 1158 : systable_endscan(scan);
18091 :
18092 : /* Now scan the child's constraints to find matches */
18093 1158 : ScanKeyInit(&key[0],
18094 : Anum_pg_constraint_conrelid,
18095 : BTEqualStrategyNumber, F_OIDEQ,
18096 : ObjectIdGetDatum(RelationGetRelid(child_rel)));
18097 1158 : scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18098 : true, NULL, 1, key);
18099 :
18100 2204 : while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18101 : {
18102 1046 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18103 1046 : bool match = false;
18104 :
18105 : /*
18106 : * Match CHECK constraints by name, not-null constraints by column
18107 : * number, and ignore all others.
18108 : */
18109 1046 : if (con->contype == CONSTRAINT_CHECK)
18110 : {
18111 346 : foreach_ptr(char, chkname, connames)
18112 : {
18113 120 : if (con->contype == CONSTRAINT_CHECK &&
18114 120 : strcmp(NameStr(con->conname), chkname) == 0)
18115 : {
18116 114 : match = true;
18117 114 : connames = foreach_delete_current(connames, chkname);
18118 114 : break;
18119 : }
18120 : }
18121 : }
18122 876 : else if (con->contype == CONSTRAINT_NOTNULL)
18123 : {
18124 442 : AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18125 :
18126 890 : foreach_int(prevattno, nncolumns)
18127 : {
18128 388 : if (prevattno == child_attno)
18129 : {
18130 382 : match = true;
18131 382 : nncolumns = foreach_delete_current(nncolumns, prevattno);
18132 382 : break;
18133 : }
18134 : }
18135 : }
18136 : else
18137 434 : continue;
18138 :
18139 612 : if (match)
18140 : {
18141 : /* Decrement inhcount and possibly set islocal to true */
18142 496 : HeapTuple copyTuple = heap_copytuple(constraintTuple);
18143 496 : Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18144 :
18145 496 : if (copy_con->coninhcount <= 0) /* shouldn't happen */
18146 0 : elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18147 : RelationGetRelid(child_rel), NameStr(copy_con->conname));
18148 :
18149 496 : copy_con->coninhcount--;
18150 496 : if (copy_con->coninhcount == 0)
18151 478 : copy_con->conislocal = true;
18152 :
18153 496 : CatalogTupleUpdate(catalogRelation, ©Tuple->t_self, copyTuple);
18154 496 : heap_freetuple(copyTuple);
18155 : }
18156 : }
18157 :
18158 : /* We should have matched all constraints */
18159 1158 : if (connames != NIL || nncolumns != NIL)
18160 0 : elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18161 : list_length(connames) + list_length(nncolumns),
18162 : RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18163 :
18164 1158 : systable_endscan(scan);
18165 1158 : table_close(catalogRelation, RowExclusiveLock);
18166 :
18167 1158 : drop_parent_dependency(RelationGetRelid(child_rel),
18168 : RelationRelationId,
18169 : RelationGetRelid(parent_rel),
18170 : child_dependency_type(is_partitioning));
18171 :
18172 : /*
18173 : * Post alter hook of this inherits. Since object_access_hook doesn't take
18174 : * multiple object identifiers, we relay oid of parent relation using
18175 : * auxiliary_id argument.
18176 : */
18177 1158 : InvokeObjectPostAlterHookArg(InheritsRelationId,
18178 : RelationGetRelid(child_rel), 0,
18179 : RelationGetRelid(parent_rel), false);
18180 1158 : }
18181 :
18182 : /*
18183 : * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18184 : * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18185 : * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18186 : * be TypeRelationId). There's no convenient way to do this, so go trawling
18187 : * through pg_depend.
18188 : */
18189 : static void
18190 1170 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18191 : DependencyType deptype)
18192 : {
18193 : Relation catalogRelation;
18194 : SysScanDesc scan;
18195 : ScanKeyData key[3];
18196 : HeapTuple depTuple;
18197 :
18198 1170 : catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18199 :
18200 1170 : ScanKeyInit(&key[0],
18201 : Anum_pg_depend_classid,
18202 : BTEqualStrategyNumber, F_OIDEQ,
18203 : ObjectIdGetDatum(RelationRelationId));
18204 1170 : ScanKeyInit(&key[1],
18205 : Anum_pg_depend_objid,
18206 : BTEqualStrategyNumber, F_OIDEQ,
18207 : ObjectIdGetDatum(relid));
18208 1170 : ScanKeyInit(&key[2],
18209 : Anum_pg_depend_objsubid,
18210 : BTEqualStrategyNumber, F_INT4EQ,
18211 : Int32GetDatum(0));
18212 :
18213 1170 : scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18214 : NULL, 3, key);
18215 :
18216 3592 : while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18217 : {
18218 2422 : Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18219 :
18220 2422 : if (dep->refclassid == refclassid &&
18221 1212 : dep->refobjid == refobjid &&
18222 1170 : dep->refobjsubid == 0 &&
18223 1170 : dep->deptype == deptype)
18224 1170 : CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18225 : }
18226 :
18227 1170 : systable_endscan(scan);
18228 1170 : table_close(catalogRelation, RowExclusiveLock);
18229 1170 : }
18230 :
18231 : /*
18232 : * ALTER TABLE OF
18233 : *
18234 : * Attach a table to a composite type, as though it had been created with CREATE
18235 : * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18236 : * subject table must not have inheritance parents. These restrictions ensure
18237 : * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18238 : *
18239 : * The address of the type is returned.
18240 : */
18241 : static ObjectAddress
18242 66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18243 : {
18244 66 : Oid relid = RelationGetRelid(rel);
18245 : Type typetuple;
18246 : Form_pg_type typeform;
18247 : Oid typeid;
18248 : Relation inheritsRelation,
18249 : relationRelation;
18250 : SysScanDesc scan;
18251 : ScanKeyData key;
18252 : AttrNumber table_attno,
18253 : type_attno;
18254 : TupleDesc typeTupleDesc,
18255 : tableTupleDesc;
18256 : ObjectAddress tableobj,
18257 : typeobj;
18258 : HeapTuple classtuple;
18259 :
18260 : /* Validate the type. */
18261 66 : typetuple = typenameType(NULL, ofTypename, NULL);
18262 66 : check_of_type(typetuple);
18263 66 : typeform = (Form_pg_type) GETSTRUCT(typetuple);
18264 66 : typeid = typeform->oid;
18265 :
18266 : /* Fail if the table has any inheritance parents. */
18267 66 : inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18268 66 : ScanKeyInit(&key,
18269 : Anum_pg_inherits_inhrelid,
18270 : BTEqualStrategyNumber, F_OIDEQ,
18271 : ObjectIdGetDatum(relid));
18272 66 : scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18273 : true, NULL, 1, &key);
18274 66 : if (HeapTupleIsValid(systable_getnext(scan)))
18275 6 : ereport(ERROR,
18276 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18277 : errmsg("typed tables cannot inherit")));
18278 60 : systable_endscan(scan);
18279 60 : table_close(inheritsRelation, AccessShareLock);
18280 :
18281 : /*
18282 : * Check the tuple descriptors for compatibility. Unlike inheritance, we
18283 : * require that the order also match. However, attnotnull need not match.
18284 : */
18285 60 : typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18286 60 : tableTupleDesc = RelationGetDescr(rel);
18287 60 : table_attno = 1;
18288 190 : for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18289 : {
18290 : Form_pg_attribute type_attr,
18291 : table_attr;
18292 : const char *type_attname,
18293 : *table_attname;
18294 :
18295 : /* Get the next non-dropped type attribute. */
18296 154 : type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18297 154 : if (type_attr->attisdropped)
18298 44 : continue;
18299 110 : type_attname = NameStr(type_attr->attname);
18300 :
18301 : /* Get the next non-dropped table attribute. */
18302 : do
18303 : {
18304 122 : if (table_attno > tableTupleDesc->natts)
18305 6 : ereport(ERROR,
18306 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18307 : errmsg("table is missing column \"%s\"",
18308 : type_attname)));
18309 116 : table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18310 116 : table_attno++;
18311 116 : } while (table_attr->attisdropped);
18312 104 : table_attname = NameStr(table_attr->attname);
18313 :
18314 : /* Compare name. */
18315 104 : if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18316 6 : ereport(ERROR,
18317 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18318 : errmsg("table has column \"%s\" where type requires \"%s\"",
18319 : table_attname, type_attname)));
18320 :
18321 : /* Compare type. */
18322 98 : if (table_attr->atttypid != type_attr->atttypid ||
18323 92 : table_attr->atttypmod != type_attr->atttypmod ||
18324 86 : table_attr->attcollation != type_attr->attcollation)
18325 12 : ereport(ERROR,
18326 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18327 : errmsg("table \"%s\" has different type for column \"%s\"",
18328 : RelationGetRelationName(rel), type_attname)));
18329 : }
18330 36 : ReleaseTupleDesc(typeTupleDesc);
18331 :
18332 : /* Any remaining columns at the end of the table had better be dropped. */
18333 36 : for (; table_attno <= tableTupleDesc->natts; table_attno++)
18334 : {
18335 6 : Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18336 : table_attno - 1);
18337 :
18338 6 : if (!table_attr->attisdropped)
18339 6 : ereport(ERROR,
18340 : (errcode(ERRCODE_DATATYPE_MISMATCH),
18341 : errmsg("table has extra column \"%s\"",
18342 : NameStr(table_attr->attname))));
18343 : }
18344 :
18345 : /* If the table was already typed, drop the existing dependency. */
18346 30 : if (rel->rd_rel->reloftype)
18347 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18348 : DEPENDENCY_NORMAL);
18349 :
18350 : /* Record a dependency on the new type. */
18351 30 : tableobj.classId = RelationRelationId;
18352 30 : tableobj.objectId = relid;
18353 30 : tableobj.objectSubId = 0;
18354 30 : typeobj.classId = TypeRelationId;
18355 30 : typeobj.objectId = typeid;
18356 30 : typeobj.objectSubId = 0;
18357 30 : recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18358 :
18359 : /* Update pg_class.reloftype */
18360 30 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18361 30 : classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18362 30 : if (!HeapTupleIsValid(classtuple))
18363 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18364 30 : ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18365 30 : CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18366 :
18367 30 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18368 :
18369 30 : heap_freetuple(classtuple);
18370 30 : table_close(relationRelation, RowExclusiveLock);
18371 :
18372 30 : ReleaseSysCache(typetuple);
18373 :
18374 30 : return typeobj;
18375 : }
18376 :
18377 : /*
18378 : * ALTER TABLE NOT OF
18379 : *
18380 : * Detach a typed table from its originating type. Just clear reloftype and
18381 : * remove the dependency.
18382 : */
18383 : static void
18384 6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
18385 : {
18386 6 : Oid relid = RelationGetRelid(rel);
18387 : Relation relationRelation;
18388 : HeapTuple tuple;
18389 :
18390 6 : if (!OidIsValid(rel->rd_rel->reloftype))
18391 0 : ereport(ERROR,
18392 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18393 : errmsg("\"%s\" is not a typed table",
18394 : RelationGetRelationName(rel))));
18395 :
18396 : /*
18397 : * We don't bother to check ownership of the type --- ownership of the
18398 : * table is presumed enough rights. No lock required on the type, either.
18399 : */
18400 :
18401 6 : drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18402 : DEPENDENCY_NORMAL);
18403 :
18404 : /* Clear pg_class.reloftype */
18405 6 : relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18406 6 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18407 6 : if (!HeapTupleIsValid(tuple))
18408 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18409 6 : ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18410 6 : CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18411 :
18412 6 : InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18413 :
18414 6 : heap_freetuple(tuple);
18415 6 : table_close(relationRelation, RowExclusiveLock);
18416 6 : }
18417 :
18418 : /*
18419 : * relation_mark_replica_identity: Update a table's replica identity
18420 : *
18421 : * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18422 : * index. Otherwise, it must be InvalidOid.
18423 : *
18424 : * Caller had better hold an exclusive lock on the relation, as the results
18425 : * of running two of these concurrently wouldn't be pretty.
18426 : */
18427 : static void
18428 464 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18429 : bool is_internal)
18430 : {
18431 : Relation pg_index;
18432 : Relation pg_class;
18433 : HeapTuple pg_class_tuple;
18434 : HeapTuple pg_index_tuple;
18435 : Form_pg_class pg_class_form;
18436 : Form_pg_index pg_index_form;
18437 : ListCell *index;
18438 :
18439 : /*
18440 : * Check whether relreplident has changed, and update it if so.
18441 : */
18442 464 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18443 464 : pg_class_tuple = SearchSysCacheCopy1(RELOID,
18444 : ObjectIdGetDatum(RelationGetRelid(rel)));
18445 464 : if (!HeapTupleIsValid(pg_class_tuple))
18446 0 : elog(ERROR, "cache lookup failed for relation \"%s\"",
18447 : RelationGetRelationName(rel));
18448 464 : pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18449 464 : if (pg_class_form->relreplident != ri_type)
18450 : {
18451 414 : pg_class_form->relreplident = ri_type;
18452 414 : CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18453 : }
18454 464 : table_close(pg_class, RowExclusiveLock);
18455 464 : heap_freetuple(pg_class_tuple);
18456 :
18457 : /*
18458 : * Update the per-index indisreplident flags correctly.
18459 : */
18460 464 : pg_index = table_open(IndexRelationId, RowExclusiveLock);
18461 1188 : foreach(index, RelationGetIndexList(rel))
18462 : {
18463 724 : Oid thisIndexOid = lfirst_oid(index);
18464 724 : bool dirty = false;
18465 :
18466 724 : pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18467 : ObjectIdGetDatum(thisIndexOid));
18468 724 : if (!HeapTupleIsValid(pg_index_tuple))
18469 0 : elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18470 724 : pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18471 :
18472 724 : if (thisIndexOid == indexOid)
18473 : {
18474 : /* Set the bit if not already set. */
18475 240 : if (!pg_index_form->indisreplident)
18476 : {
18477 222 : dirty = true;
18478 222 : pg_index_form->indisreplident = true;
18479 : }
18480 : }
18481 : else
18482 : {
18483 : /* Unset the bit if set. */
18484 484 : if (pg_index_form->indisreplident)
18485 : {
18486 52 : dirty = true;
18487 52 : pg_index_form->indisreplident = false;
18488 : }
18489 : }
18490 :
18491 724 : if (dirty)
18492 : {
18493 274 : CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18494 274 : InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18495 : InvalidOid, is_internal);
18496 :
18497 : /*
18498 : * Invalidate the relcache for the table, so that after we commit
18499 : * all sessions will refresh the table's replica identity index
18500 : * before attempting any UPDATE or DELETE on the table. (If we
18501 : * changed the table's pg_class row above, then a relcache inval
18502 : * is already queued due to that; but we might not have.)
18503 : */
18504 274 : CacheInvalidateRelcache(rel);
18505 : }
18506 724 : heap_freetuple(pg_index_tuple);
18507 : }
18508 :
18509 464 : table_close(pg_index, RowExclusiveLock);
18510 464 : }
18511 :
18512 : /*
18513 : * ALTER TABLE <name> REPLICA IDENTITY ...
18514 : */
18515 : static void
18516 512 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
18517 : {
18518 : Oid indexOid;
18519 : Relation indexRel;
18520 : int key;
18521 :
18522 512 : if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18523 : {
18524 6 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18525 6 : return;
18526 : }
18527 506 : else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18528 : {
18529 170 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18530 170 : return;
18531 : }
18532 336 : else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18533 : {
18534 48 : relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18535 48 : return;
18536 : }
18537 288 : else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18538 : {
18539 : /* fallthrough */ ;
18540 : }
18541 : else
18542 0 : elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18543 :
18544 : /* Check that the index exists */
18545 288 : indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18546 288 : if (!OidIsValid(indexOid))
18547 0 : ereport(ERROR,
18548 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18549 : errmsg("index \"%s\" for table \"%s\" does not exist",
18550 : stmt->name, RelationGetRelationName(rel))));
18551 :
18552 288 : indexRel = index_open(indexOid, ShareLock);
18553 :
18554 : /* Check that the index is on the relation we're altering. */
18555 288 : if (indexRel->rd_index == NULL ||
18556 288 : indexRel->rd_index->indrelid != RelationGetRelid(rel))
18557 6 : ereport(ERROR,
18558 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18559 : errmsg("\"%s\" is not an index for table \"%s\"",
18560 : RelationGetRelationName(indexRel),
18561 : RelationGetRelationName(rel))));
18562 :
18563 : /*
18564 : * The AM must support uniqueness, and the index must in fact be unique.
18565 : * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18566 : * exclusion), we can use that too.
18567 : */
18568 282 : if ((!indexRel->rd_indam->amcanunique ||
18569 262 : !indexRel->rd_index->indisunique) &&
18570 26 : !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18571 12 : ereport(ERROR,
18572 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18573 : errmsg("cannot use non-unique index \"%s\" as replica identity",
18574 : RelationGetRelationName(indexRel))));
18575 : /* Deferred indexes are not guaranteed to be always unique. */
18576 270 : if (!indexRel->rd_index->indimmediate)
18577 12 : ereport(ERROR,
18578 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18579 : errmsg("cannot use non-immediate index \"%s\" as replica identity",
18580 : RelationGetRelationName(indexRel))));
18581 : /* Expression indexes aren't supported. */
18582 258 : if (RelationGetIndexExpressions(indexRel) != NIL)
18583 6 : ereport(ERROR,
18584 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18585 : errmsg("cannot use expression index \"%s\" as replica identity",
18586 : RelationGetRelationName(indexRel))));
18587 : /* Predicate indexes aren't supported. */
18588 252 : if (RelationGetIndexPredicate(indexRel) != NIL)
18589 6 : ereport(ERROR,
18590 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18591 : errmsg("cannot use partial index \"%s\" as replica identity",
18592 : RelationGetRelationName(indexRel))));
18593 :
18594 : /* Check index for nullable columns. */
18595 552 : for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18596 : {
18597 312 : int16 attno = indexRel->rd_index->indkey.values[key];
18598 : Form_pg_attribute attr;
18599 :
18600 : /*
18601 : * Reject any other system columns. (Going forward, we'll disallow
18602 : * indexes containing such columns in the first place, but they might
18603 : * exist in older branches.)
18604 : */
18605 312 : if (attno <= 0)
18606 0 : ereport(ERROR,
18607 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18608 : errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18609 : RelationGetRelationName(indexRel), attno)));
18610 :
18611 312 : attr = TupleDescAttr(rel->rd_att, attno - 1);
18612 312 : if (!attr->attnotnull)
18613 6 : ereport(ERROR,
18614 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18615 : errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18616 : RelationGetRelationName(indexRel),
18617 : NameStr(attr->attname))));
18618 : }
18619 :
18620 : /* This index is suitable for use as a replica identity. Mark it. */
18621 240 : relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18622 :
18623 240 : index_close(indexRel, NoLock);
18624 : }
18625 :
18626 : /*
18627 : * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18628 : */
18629 : static void
18630 348 : ATExecSetRowSecurity(Relation rel, bool rls)
18631 : {
18632 : Relation pg_class;
18633 : Oid relid;
18634 : HeapTuple tuple;
18635 :
18636 348 : relid = RelationGetRelid(rel);
18637 :
18638 : /* Pull the record for this relation and update it */
18639 348 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18640 :
18641 348 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18642 :
18643 348 : if (!HeapTupleIsValid(tuple))
18644 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18645 :
18646 348 : ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18647 348 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18648 :
18649 348 : InvokeObjectPostAlterHook(RelationRelationId,
18650 : RelationGetRelid(rel), 0);
18651 :
18652 348 : table_close(pg_class, RowExclusiveLock);
18653 348 : heap_freetuple(tuple);
18654 348 : }
18655 :
18656 : /*
18657 : * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18658 : */
18659 : static void
18660 132 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
18661 : {
18662 : Relation pg_class;
18663 : Oid relid;
18664 : HeapTuple tuple;
18665 :
18666 132 : relid = RelationGetRelid(rel);
18667 :
18668 132 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
18669 :
18670 132 : tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18671 :
18672 132 : if (!HeapTupleIsValid(tuple))
18673 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
18674 :
18675 132 : ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18676 132 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18677 :
18678 132 : InvokeObjectPostAlterHook(RelationRelationId,
18679 : RelationGetRelid(rel), 0);
18680 :
18681 132 : table_close(pg_class, RowExclusiveLock);
18682 132 : heap_freetuple(tuple);
18683 132 : }
18684 :
18685 : /*
18686 : * ALTER FOREIGN TABLE <name> OPTIONS (...)
18687 : */
18688 : static void
18689 58 : ATExecGenericOptions(Relation rel, List *options)
18690 : {
18691 : Relation ftrel;
18692 : ForeignServer *server;
18693 : ForeignDataWrapper *fdw;
18694 : HeapTuple tuple;
18695 : bool isnull;
18696 : Datum repl_val[Natts_pg_foreign_table];
18697 : bool repl_null[Natts_pg_foreign_table];
18698 : bool repl_repl[Natts_pg_foreign_table];
18699 : Datum datum;
18700 : Form_pg_foreign_table tableform;
18701 :
18702 58 : if (options == NIL)
18703 0 : return;
18704 :
18705 58 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18706 :
18707 58 : tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18708 : ObjectIdGetDatum(rel->rd_id));
18709 58 : if (!HeapTupleIsValid(tuple))
18710 0 : ereport(ERROR,
18711 : (errcode(ERRCODE_UNDEFINED_OBJECT),
18712 : errmsg("foreign table \"%s\" does not exist",
18713 : RelationGetRelationName(rel))));
18714 58 : tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18715 58 : server = GetForeignServer(tableform->ftserver);
18716 58 : fdw = GetForeignDataWrapper(server->fdwid);
18717 :
18718 58 : memset(repl_val, 0, sizeof(repl_val));
18719 58 : memset(repl_null, false, sizeof(repl_null));
18720 58 : memset(repl_repl, false, sizeof(repl_repl));
18721 :
18722 : /* Extract the current options */
18723 58 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
18724 : tuple,
18725 : Anum_pg_foreign_table_ftoptions,
18726 : &isnull);
18727 58 : if (isnull)
18728 4 : datum = PointerGetDatum(NULL);
18729 :
18730 : /* Transform the options */
18731 58 : datum = transformGenericOptions(ForeignTableRelationId,
18732 : datum,
18733 : options,
18734 : fdw->fdwvalidator);
18735 :
18736 56 : if (DatumGetPointer(datum) != NULL)
18737 56 : repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18738 : else
18739 0 : repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18740 :
18741 56 : repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18742 :
18743 : /* Everything looks good - update the tuple */
18744 :
18745 56 : tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18746 : repl_val, repl_null, repl_repl);
18747 :
18748 56 : CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18749 :
18750 : /*
18751 : * Invalidate relcache so that all sessions will refresh any cached plans
18752 : * that might depend on the old options.
18753 : */
18754 56 : CacheInvalidateRelcache(rel);
18755 :
18756 56 : InvokeObjectPostAlterHook(ForeignTableRelationId,
18757 : RelationGetRelid(rel), 0);
18758 :
18759 56 : table_close(ftrel, RowExclusiveLock);
18760 :
18761 56 : heap_freetuple(tuple);
18762 : }
18763 :
18764 : /*
18765 : * ALTER TABLE ALTER COLUMN SET COMPRESSION
18766 : *
18767 : * Return value is the address of the modified column
18768 : */
18769 : static ObjectAddress
18770 78 : ATExecSetCompression(Relation rel,
18771 : const char *column,
18772 : Node *newValue,
18773 : LOCKMODE lockmode)
18774 : {
18775 : Relation attrel;
18776 : HeapTuple tuple;
18777 : Form_pg_attribute atttableform;
18778 : AttrNumber attnum;
18779 : char *compression;
18780 : char cmethod;
18781 : ObjectAddress address;
18782 :
18783 78 : compression = strVal(newValue);
18784 :
18785 78 : attrel = table_open(AttributeRelationId, RowExclusiveLock);
18786 :
18787 : /* copy the cache entry so we can scribble on it below */
18788 78 : tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18789 78 : if (!HeapTupleIsValid(tuple))
18790 0 : ereport(ERROR,
18791 : (errcode(ERRCODE_UNDEFINED_COLUMN),
18792 : errmsg("column \"%s\" of relation \"%s\" does not exist",
18793 : column, RelationGetRelationName(rel))));
18794 :
18795 : /* prevent them from altering a system attribute */
18796 78 : atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18797 78 : attnum = atttableform->attnum;
18798 78 : if (attnum <= 0)
18799 0 : ereport(ERROR,
18800 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18801 : errmsg("cannot alter system column \"%s\"", column)));
18802 :
18803 : /*
18804 : * Check that column type is compressible, then get the attribute
18805 : * compression method code
18806 : */
18807 78 : cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18808 :
18809 : /* update pg_attribute entry */
18810 72 : atttableform->attcompression = cmethod;
18811 72 : CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18812 :
18813 72 : InvokeObjectPostAlterHook(RelationRelationId,
18814 : RelationGetRelid(rel),
18815 : attnum);
18816 :
18817 : /*
18818 : * Apply the change to indexes as well (only for simple index columns,
18819 : * matching behavior of index.c ConstructTupleDescriptor()).
18820 : */
18821 72 : SetIndexStorageProperties(rel, attrel, attnum,
18822 : false, 0,
18823 : true, cmethod,
18824 : lockmode);
18825 :
18826 72 : heap_freetuple(tuple);
18827 :
18828 72 : table_close(attrel, RowExclusiveLock);
18829 :
18830 : /* make changes visible */
18831 72 : CommandCounterIncrement();
18832 :
18833 72 : ObjectAddressSubSet(address, RelationRelationId,
18834 : RelationGetRelid(rel), attnum);
18835 72 : return address;
18836 : }
18837 :
18838 :
18839 : /*
18840 : * Preparation phase for SET LOGGED/UNLOGGED
18841 : *
18842 : * This verifies that we're not trying to change a temp table. Also,
18843 : * existing foreign key constraints are checked to avoid ending up with
18844 : * permanent tables referencing unlogged tables.
18845 : */
18846 : static void
18847 100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
18848 : {
18849 : Relation pg_constraint;
18850 : HeapTuple tuple;
18851 : SysScanDesc scan;
18852 : ScanKeyData skey[1];
18853 :
18854 : /*
18855 : * Disallow changing status for a temp table. Also verify whether we can
18856 : * get away with doing nothing; in such cases we don't need to run the
18857 : * checks below, either.
18858 : */
18859 100 : switch (rel->rd_rel->relpersistence)
18860 : {
18861 0 : case RELPERSISTENCE_TEMP:
18862 0 : ereport(ERROR,
18863 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18864 : errmsg("cannot change logged status of table \"%s\" because it is temporary",
18865 : RelationGetRelationName(rel)),
18866 : errtable(rel)));
18867 : break;
18868 56 : case RELPERSISTENCE_PERMANENT:
18869 56 : if (toLogged)
18870 : /* nothing to do */
18871 12 : return;
18872 50 : break;
18873 44 : case RELPERSISTENCE_UNLOGGED:
18874 44 : if (!toLogged)
18875 : /* nothing to do */
18876 6 : return;
18877 38 : break;
18878 : }
18879 :
18880 : /*
18881 : * Check that the table is not part of any publication when changing to
18882 : * UNLOGGED, as UNLOGGED tables can't be published.
18883 : */
18884 138 : if (!toLogged &&
18885 50 : GetRelationPublications(RelationGetRelid(rel)) != NIL)
18886 0 : ereport(ERROR,
18887 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18888 : errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18889 : RelationGetRelationName(rel)),
18890 : errdetail("Unlogged relations cannot be replicated.")));
18891 :
18892 : /*
18893 : * Check existing foreign key constraints to preserve the invariant that
18894 : * permanent tables cannot reference unlogged ones. Self-referencing
18895 : * foreign keys can safely be ignored.
18896 : */
18897 88 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18898 :
18899 : /*
18900 : * Scan conrelid if changing to permanent, else confrelid. This also
18901 : * determines whether a useful index exists.
18902 : */
18903 88 : ScanKeyInit(&skey[0],
18904 : toLogged ? Anum_pg_constraint_conrelid :
18905 : Anum_pg_constraint_confrelid,
18906 : BTEqualStrategyNumber, F_OIDEQ,
18907 : ObjectIdGetDatum(RelationGetRelid(rel)));
18908 88 : scan = systable_beginscan(pg_constraint,
18909 : toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18910 : true, NULL, 1, skey);
18911 :
18912 142 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18913 : {
18914 66 : Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
18915 :
18916 66 : if (con->contype == CONSTRAINT_FOREIGN)
18917 : {
18918 : Oid foreignrelid;
18919 : Relation foreignrel;
18920 :
18921 : /* the opposite end of what we used as scankey */
18922 30 : foreignrelid = toLogged ? con->confrelid : con->conrelid;
18923 :
18924 : /* ignore if self-referencing */
18925 30 : if (RelationGetRelid(rel) == foreignrelid)
18926 12 : continue;
18927 :
18928 18 : foreignrel = relation_open(foreignrelid, AccessShareLock);
18929 :
18930 18 : if (toLogged)
18931 : {
18932 6 : if (!RelationIsPermanent(foreignrel))
18933 6 : ereport(ERROR,
18934 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18935 : errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18936 : RelationGetRelationName(rel),
18937 : RelationGetRelationName(foreignrel)),
18938 : errtableconstraint(rel, NameStr(con->conname))));
18939 : }
18940 : else
18941 : {
18942 12 : if (RelationIsPermanent(foreignrel))
18943 6 : ereport(ERROR,
18944 : (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18945 : errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18946 : RelationGetRelationName(rel),
18947 : RelationGetRelationName(foreignrel)),
18948 : errtableconstraint(rel, NameStr(con->conname))));
18949 : }
18950 :
18951 6 : relation_close(foreignrel, AccessShareLock);
18952 : }
18953 : }
18954 :
18955 76 : systable_endscan(scan);
18956 :
18957 76 : table_close(pg_constraint, AccessShareLock);
18958 :
18959 : /* force rewrite if necessary; see comment in ATRewriteTables */
18960 76 : tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
18961 76 : if (toLogged)
18962 32 : tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18963 : else
18964 44 : tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18965 76 : tab->chgPersistence = true;
18966 : }
18967 :
18968 : /*
18969 : * Execute ALTER TABLE SET SCHEMA
18970 : */
18971 : ObjectAddress
18972 104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
18973 : {
18974 : Relation rel;
18975 : Oid relid;
18976 : Oid oldNspOid;
18977 : Oid nspOid;
18978 : RangeVar *newrv;
18979 : ObjectAddresses *objsMoved;
18980 : ObjectAddress myself;
18981 :
18982 104 : relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
18983 104 : stmt->missing_ok ? RVR_MISSING_OK : 0,
18984 : RangeVarCallbackForAlterRelation,
18985 : stmt);
18986 :
18987 102 : if (!OidIsValid(relid))
18988 : {
18989 12 : ereport(NOTICE,
18990 : (errmsg("relation \"%s\" does not exist, skipping",
18991 : stmt->relation->relname)));
18992 12 : return InvalidObjectAddress;
18993 : }
18994 :
18995 90 : rel = relation_open(relid, NoLock);
18996 :
18997 90 : oldNspOid = RelationGetNamespace(rel);
18998 :
18999 : /* If it's an owned sequence, disallow moving it by itself. */
19000 90 : if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19001 : {
19002 : Oid tableId;
19003 : int32 colId;
19004 :
19005 10 : if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19006 2 : sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
19007 6 : ereport(ERROR,
19008 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19009 : errmsg("cannot move an owned sequence into another schema"),
19010 : errdetail("Sequence \"%s\" is linked to table \"%s\".",
19011 : RelationGetRelationName(rel),
19012 : get_rel_name(tableId))));
19013 : }
19014 :
19015 : /* Get and lock schema OID and check its permissions. */
19016 84 : newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19017 84 : nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
19018 :
19019 : /* common checks on switching namespaces */
19020 84 : CheckSetNamespace(oldNspOid, nspOid);
19021 :
19022 84 : objsMoved = new_object_addresses();
19023 84 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
19024 84 : free_object_addresses(objsMoved);
19025 :
19026 84 : ObjectAddressSet(myself, RelationRelationId, relid);
19027 :
19028 84 : if (oldschema)
19029 84 : *oldschema = oldNspOid;
19030 :
19031 : /* close rel, but keep lock until commit */
19032 84 : relation_close(rel, NoLock);
19033 :
19034 84 : return myself;
19035 : }
19036 :
19037 : /*
19038 : * The guts of relocating a table or materialized view to another namespace:
19039 : * besides moving the relation itself, its dependent objects are relocated to
19040 : * the new schema.
19041 : */
19042 : void
19043 86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
19044 : ObjectAddresses *objsMoved)
19045 : {
19046 : Relation classRel;
19047 :
19048 : Assert(objsMoved != NULL);
19049 :
19050 : /* OK, modify the pg_class row and pg_depend entry */
19051 86 : classRel = table_open(RelationRelationId, RowExclusiveLock);
19052 :
19053 86 : AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
19054 : nspOid, true, objsMoved);
19055 :
19056 : /* Fix the table's row type too, if it has one */
19057 86 : if (OidIsValid(rel->rd_rel->reltype))
19058 84 : AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
19059 : false, /* isImplicitArray */
19060 : false, /* ignoreDependent */
19061 : false, /* errorOnTableType */
19062 : objsMoved);
19063 :
19064 : /* Fix other dependent stuff */
19065 86 : AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19066 86 : AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19067 : objsMoved, AccessExclusiveLock);
19068 86 : AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19069 : false, objsMoved);
19070 :
19071 86 : table_close(classRel, RowExclusiveLock);
19072 86 : }
19073 :
19074 : /*
19075 : * The guts of relocating a relation to another namespace: fix the pg_class
19076 : * entry, and the pg_depend entry if any. Caller must already have
19077 : * opened and write-locked pg_class.
19078 : */
19079 : void
19080 188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
19081 : Oid oldNspOid, Oid newNspOid,
19082 : bool hasDependEntry,
19083 : ObjectAddresses *objsMoved)
19084 : {
19085 : HeapTuple classTup;
19086 : Form_pg_class classForm;
19087 : ObjectAddress thisobj;
19088 188 : bool already_done = false;
19089 :
19090 : /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19091 188 : classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19092 188 : if (!HeapTupleIsValid(classTup))
19093 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
19094 188 : classForm = (Form_pg_class) GETSTRUCT(classTup);
19095 :
19096 : Assert(classForm->relnamespace == oldNspOid);
19097 :
19098 188 : thisobj.classId = RelationRelationId;
19099 188 : thisobj.objectId = relOid;
19100 188 : thisobj.objectSubId = 0;
19101 :
19102 : /*
19103 : * If the object has already been moved, don't move it again. If it's
19104 : * already in the right place, don't move it, but still fire the object
19105 : * access hook.
19106 : */
19107 188 : already_done = object_address_present(&thisobj, objsMoved);
19108 188 : if (!already_done && oldNspOid != newNspOid)
19109 146 : {
19110 146 : ItemPointerData otid = classTup->t_self;
19111 :
19112 : /* check for duplicate name (more friendly than unique-index failure) */
19113 146 : if (get_relname_relid(NameStr(classForm->relname),
19114 : newNspOid) != InvalidOid)
19115 0 : ereport(ERROR,
19116 : (errcode(ERRCODE_DUPLICATE_TABLE),
19117 : errmsg("relation \"%s\" already exists in schema \"%s\"",
19118 : NameStr(classForm->relname),
19119 : get_namespace_name(newNspOid))));
19120 :
19121 : /* classTup is a copy, so OK to scribble on */
19122 146 : classForm->relnamespace = newNspOid;
19123 :
19124 146 : CatalogTupleUpdate(classRel, &otid, classTup);
19125 146 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19126 :
19127 :
19128 : /* Update dependency on schema if caller said so */
19129 250 : if (hasDependEntry &&
19130 104 : changeDependencyFor(RelationRelationId,
19131 : relOid,
19132 : NamespaceRelationId,
19133 : oldNspOid,
19134 : newNspOid) != 1)
19135 0 : elog(ERROR, "could not change schema dependency for relation \"%s\"",
19136 : NameStr(classForm->relname));
19137 : }
19138 : else
19139 42 : UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19140 188 : if (!already_done)
19141 : {
19142 188 : add_exact_object_address(&thisobj, objsMoved);
19143 :
19144 188 : InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19145 : }
19146 :
19147 188 : heap_freetuple(classTup);
19148 188 : }
19149 :
19150 : /*
19151 : * Move all indexes for the specified relation to another namespace.
19152 : *
19153 : * Note: we assume adequate permission checking was done by the caller,
19154 : * and that the caller has a suitable lock on the owning relation.
19155 : */
19156 : static void
19157 86 : AlterIndexNamespaces(Relation classRel, Relation rel,
19158 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19159 : {
19160 : List *indexList;
19161 : ListCell *l;
19162 :
19163 86 : indexList = RelationGetIndexList(rel);
19164 :
19165 132 : foreach(l, indexList)
19166 : {
19167 46 : Oid indexOid = lfirst_oid(l);
19168 : ObjectAddress thisobj;
19169 :
19170 46 : thisobj.classId = RelationRelationId;
19171 46 : thisobj.objectId = indexOid;
19172 46 : thisobj.objectSubId = 0;
19173 :
19174 : /*
19175 : * Note: currently, the index will not have its own dependency on the
19176 : * namespace, so we don't need to do changeDependencyFor(). There's no
19177 : * row type in pg_type, either.
19178 : *
19179 : * XXX this objsMoved test may be pointless -- surely we have a single
19180 : * dependency link from a relation to each index?
19181 : */
19182 46 : if (!object_address_present(&thisobj, objsMoved))
19183 : {
19184 46 : AlterRelationNamespaceInternal(classRel, indexOid,
19185 : oldNspOid, newNspOid,
19186 : false, objsMoved);
19187 46 : add_exact_object_address(&thisobj, objsMoved);
19188 : }
19189 : }
19190 :
19191 86 : list_free(indexList);
19192 86 : }
19193 :
19194 : /*
19195 : * Move all identity and SERIAL-column sequences of the specified relation to another
19196 : * namespace.
19197 : *
19198 : * Note: we assume adequate permission checking was done by the caller,
19199 : * and that the caller has a suitable lock on the owning relation.
19200 : */
19201 : static void
19202 86 : AlterSeqNamespaces(Relation classRel, Relation rel,
19203 : Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19204 : LOCKMODE lockmode)
19205 : {
19206 : Relation depRel;
19207 : SysScanDesc scan;
19208 : ScanKeyData key[2];
19209 : HeapTuple tup;
19210 :
19211 : /*
19212 : * SERIAL sequences are those having an auto dependency on one of the
19213 : * table's columns (we don't care *which* column, exactly).
19214 : */
19215 86 : depRel = table_open(DependRelationId, AccessShareLock);
19216 :
19217 86 : ScanKeyInit(&key[0],
19218 : Anum_pg_depend_refclassid,
19219 : BTEqualStrategyNumber, F_OIDEQ,
19220 : ObjectIdGetDatum(RelationRelationId));
19221 86 : ScanKeyInit(&key[1],
19222 : Anum_pg_depend_refobjid,
19223 : BTEqualStrategyNumber, F_OIDEQ,
19224 : ObjectIdGetDatum(RelationGetRelid(rel)));
19225 : /* we leave refobjsubid unspecified */
19226 :
19227 86 : scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19228 : NULL, 2, key);
19229 :
19230 616 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
19231 : {
19232 530 : Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19233 : Relation seqRel;
19234 :
19235 : /* skip dependencies other than auto dependencies on columns */
19236 530 : if (depForm->refobjsubid == 0 ||
19237 382 : depForm->classid != RelationRelationId ||
19238 42 : depForm->objsubid != 0 ||
19239 42 : !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19240 488 : continue;
19241 :
19242 : /* Use relation_open just in case it's an index */
19243 42 : seqRel = relation_open(depForm->objid, lockmode);
19244 :
19245 : /* skip non-sequence relations */
19246 42 : if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19247 : {
19248 : /* No need to keep the lock */
19249 0 : relation_close(seqRel, lockmode);
19250 0 : continue;
19251 : }
19252 :
19253 : /* Fix the pg_class and pg_depend entries */
19254 42 : AlterRelationNamespaceInternal(classRel, depForm->objid,
19255 : oldNspOid, newNspOid,
19256 : true, objsMoved);
19257 :
19258 : /*
19259 : * Sequences used to have entries in pg_type, but no longer do. If we
19260 : * ever re-instate that, we'll need to move the pg_type entry to the
19261 : * new namespace, too (using AlterTypeNamespaceInternal).
19262 : */
19263 : Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19264 :
19265 : /* Now we can close it. Keep the lock till end of transaction. */
19266 42 : relation_close(seqRel, NoLock);
19267 : }
19268 :
19269 86 : systable_endscan(scan);
19270 :
19271 86 : relation_close(depRel, AccessShareLock);
19272 86 : }
19273 :
19274 :
19275 : /*
19276 : * This code supports
19277 : * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19278 : *
19279 : * Because we only support this for TEMP tables, it's sufficient to remember
19280 : * the state in a backend-local data structure.
19281 : */
19282 :
19283 : /*
19284 : * Register a newly-created relation's ON COMMIT action.
19285 : */
19286 : void
19287 182 : register_on_commit_action(Oid relid, OnCommitAction action)
19288 : {
19289 : OnCommitItem *oc;
19290 : MemoryContext oldcxt;
19291 :
19292 : /*
19293 : * We needn't bother registering the relation unless there is an ON COMMIT
19294 : * action we need to take.
19295 : */
19296 182 : if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19297 24 : return;
19298 :
19299 158 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
19300 :
19301 158 : oc = palloc_object(OnCommitItem);
19302 158 : oc->relid = relid;
19303 158 : oc->oncommit = action;
19304 158 : oc->creating_subid = GetCurrentSubTransactionId();
19305 158 : oc->deleting_subid = InvalidSubTransactionId;
19306 :
19307 : /*
19308 : * We use lcons() here so that ON COMMIT actions are processed in reverse
19309 : * order of registration. That might not be essential but it seems
19310 : * reasonable.
19311 : */
19312 158 : on_commits = lcons(oc, on_commits);
19313 :
19314 158 : MemoryContextSwitchTo(oldcxt);
19315 : }
19316 :
19317 : /*
19318 : * Unregister any ON COMMIT action when a relation is deleted.
19319 : *
19320 : * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19321 : */
19322 : void
19323 51698 : remove_on_commit_action(Oid relid)
19324 : {
19325 : ListCell *l;
19326 :
19327 51868 : foreach(l, on_commits)
19328 : {
19329 310 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19330 :
19331 310 : if (oc->relid == relid)
19332 : {
19333 140 : oc->deleting_subid = GetCurrentSubTransactionId();
19334 140 : break;
19335 : }
19336 : }
19337 51698 : }
19338 :
19339 : /*
19340 : * Perform ON COMMIT actions.
19341 : *
19342 : * This is invoked just before actually committing, since it's possible
19343 : * to encounter errors.
19344 : */
19345 : void
19346 900090 : PreCommit_on_commit_actions(void)
19347 : {
19348 : ListCell *l;
19349 900090 : List *oids_to_truncate = NIL;
19350 900090 : List *oids_to_drop = NIL;
19351 :
19352 900912 : foreach(l, on_commits)
19353 : {
19354 822 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19355 :
19356 : /* Ignore entry if already dropped in this xact */
19357 822 : if (oc->deleting_subid != InvalidSubTransactionId)
19358 74 : continue;
19359 :
19360 748 : switch (oc->oncommit)
19361 : {
19362 0 : case ONCOMMIT_NOOP:
19363 : case ONCOMMIT_PRESERVE_ROWS:
19364 : /* Do nothing (there shouldn't be such entries, actually) */
19365 0 : break;
19366 694 : case ONCOMMIT_DELETE_ROWS:
19367 :
19368 : /*
19369 : * If this transaction hasn't accessed any temporary
19370 : * relations, we can skip truncating ON COMMIT DELETE ROWS
19371 : * tables, as they must still be empty.
19372 : */
19373 694 : if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
19374 448 : oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19375 694 : break;
19376 54 : case ONCOMMIT_DROP:
19377 54 : oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19378 54 : break;
19379 : }
19380 : }
19381 :
19382 : /*
19383 : * Truncate relations before dropping so that all dependencies between
19384 : * relations are removed after they are worked on. Doing it like this
19385 : * might be a waste as it is possible that a relation being truncated will
19386 : * be dropped anyway due to its parent being dropped, but this makes the
19387 : * code more robust because of not having to re-check that the relation
19388 : * exists at truncation time.
19389 : */
19390 900090 : if (oids_to_truncate != NIL)
19391 382 : heap_truncate(oids_to_truncate);
19392 :
19393 900084 : if (oids_to_drop != NIL)
19394 : {
19395 48 : ObjectAddresses *targetObjects = new_object_addresses();
19396 :
19397 102 : foreach(l, oids_to_drop)
19398 : {
19399 : ObjectAddress object;
19400 :
19401 54 : object.classId = RelationRelationId;
19402 54 : object.objectId = lfirst_oid(l);
19403 54 : object.objectSubId = 0;
19404 :
19405 : Assert(!object_address_present(&object, targetObjects));
19406 :
19407 54 : add_exact_object_address(&object, targetObjects);
19408 : }
19409 :
19410 : /*
19411 : * Object deletion might involve toast table access (to clean up
19412 : * toasted catalog entries), so ensure we have a valid snapshot.
19413 : */
19414 48 : PushActiveSnapshot(GetTransactionSnapshot());
19415 :
19416 : /*
19417 : * Since this is an automatic drop, rather than one directly initiated
19418 : * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19419 : */
19420 48 : performMultipleDeletions(targetObjects, DROP_CASCADE,
19421 : PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
19422 :
19423 48 : PopActiveSnapshot();
19424 :
19425 : #ifdef USE_ASSERT_CHECKING
19426 :
19427 : /*
19428 : * Note that table deletion will call remove_on_commit_action, so the
19429 : * entry should get marked as deleted.
19430 : */
19431 : foreach(l, on_commits)
19432 : {
19433 : OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19434 :
19435 : if (oc->oncommit != ONCOMMIT_DROP)
19436 : continue;
19437 :
19438 : Assert(oc->deleting_subid != InvalidSubTransactionId);
19439 : }
19440 : #endif
19441 : }
19442 900084 : }
19443 :
19444 : /*
19445 : * Post-commit or post-abort cleanup for ON COMMIT management.
19446 : *
19447 : * All we do here is remove no-longer-needed OnCommitItem entries.
19448 : *
19449 : * During commit, remove entries that were deleted during this transaction;
19450 : * during abort, remove those created during this transaction.
19451 : */
19452 : void
19453 951716 : AtEOXact_on_commit_actions(bool isCommit)
19454 : {
19455 : ListCell *cur_item;
19456 :
19457 952574 : foreach(cur_item, on_commits)
19458 : {
19459 858 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19460 :
19461 966 : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19462 108 : oc->creating_subid != InvalidSubTransactionId)
19463 : {
19464 : /* cur_item must be removed */
19465 158 : on_commits = foreach_delete_current(on_commits, cur_item);
19466 158 : pfree(oc);
19467 : }
19468 : else
19469 : {
19470 : /* cur_item must be preserved */
19471 700 : oc->creating_subid = InvalidSubTransactionId;
19472 700 : oc->deleting_subid = InvalidSubTransactionId;
19473 : }
19474 : }
19475 951716 : }
19476 :
19477 : /*
19478 : * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19479 : *
19480 : * During subabort, we can immediately remove entries created during this
19481 : * subtransaction. During subcommit, just relabel entries marked during
19482 : * this subtransaction as being the parent's responsibility.
19483 : */
19484 : void
19485 20160 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
19486 : SubTransactionId parentSubid)
19487 : {
19488 : ListCell *cur_item;
19489 :
19490 20160 : foreach(cur_item, on_commits)
19491 : {
19492 0 : OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19493 :
19494 0 : if (!isCommit && oc->creating_subid == mySubid)
19495 : {
19496 : /* cur_item must be removed */
19497 0 : on_commits = foreach_delete_current(on_commits, cur_item);
19498 0 : pfree(oc);
19499 : }
19500 : else
19501 : {
19502 : /* cur_item must be preserved */
19503 0 : if (oc->creating_subid == mySubid)
19504 0 : oc->creating_subid = parentSubid;
19505 0 : if (oc->deleting_subid == mySubid)
19506 0 : oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19507 : }
19508 : }
19509 20160 : }
19510 :
19511 : /*
19512 : * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19513 : * the relation to be locked only if (1) it's a plain or partitioned table,
19514 : * materialized view, or TOAST table and (2) the current user is the owner (or
19515 : * the superuser) or has been granted MAINTAIN. This meets the
19516 : * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19517 : * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19518 : */
19519 : void
19520 1022 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
19521 : Oid relId, Oid oldRelId, void *arg)
19522 : {
19523 : char relkind;
19524 : AclResult aclresult;
19525 :
19526 : /* Nothing to do if the relation was not found. */
19527 1022 : if (!OidIsValid(relId))
19528 6 : return;
19529 :
19530 : /*
19531 : * If the relation does exist, check whether it's an index. But note that
19532 : * the relation might have been dropped between the time we did the name
19533 : * lookup and now. In that case, there's nothing to do.
19534 : */
19535 1016 : relkind = get_rel_relkind(relId);
19536 1016 : if (!relkind)
19537 0 : return;
19538 1016 : if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19539 142 : relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19540 28 : ereport(ERROR,
19541 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19542 : errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19543 :
19544 : /* Check permissions */
19545 988 : aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19546 988 : if (aclresult != ACLCHECK_OK)
19547 30 : aclcheck_error(aclresult,
19548 30 : get_relkind_objtype(get_rel_relkind(relId)),
19549 30 : relation->relname);
19550 : }
19551 :
19552 : /*
19553 : * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19554 : */
19555 : static void
19556 2178 : RangeVarCallbackForTruncate(const RangeVar *relation,
19557 : Oid relId, Oid oldRelId, void *arg)
19558 : {
19559 : HeapTuple tuple;
19560 :
19561 : /* Nothing to do if the relation was not found. */
19562 2178 : if (!OidIsValid(relId))
19563 0 : return;
19564 :
19565 2178 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19566 2178 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19567 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19568 :
19569 2178 : truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
19570 2172 : truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
19571 :
19572 2140 : ReleaseSysCache(tuple);
19573 : }
19574 :
19575 : /*
19576 : * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19577 : * the owner of the relation, or superuser.
19578 : */
19579 : void
19580 17192 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
19581 : Oid relId, Oid oldRelId, void *arg)
19582 : {
19583 : HeapTuple tuple;
19584 :
19585 : /* Nothing to do if the relation was not found. */
19586 17192 : if (!OidIsValid(relId))
19587 12 : return;
19588 :
19589 17180 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19590 17180 : if (!HeapTupleIsValid(tuple)) /* should not happen */
19591 0 : elog(ERROR, "cache lookup failed for relation %u", relId);
19592 :
19593 17180 : if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19594 24 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
19595 24 : relation->relname);
19596 :
19597 34192 : if (!allowSystemTableMods &&
19598 17036 : IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19599 2 : ereport(ERROR,
19600 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19601 : errmsg("permission denied: \"%s\" is a system catalog",
19602 : relation->relname)));
19603 :
19604 17154 : ReleaseSysCache(tuple);
19605 : }
19606 :
19607 : /*
19608 : * Common RangeVarGetRelid callback for rename, set schema, and alter table
19609 : * processing.
19610 : */
19611 : static void
19612 35154 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
19613 : void *arg)
19614 : {
19615 35154 : Node *stmt = (Node *) arg;
19616 : ObjectType reltype;
19617 : HeapTuple tuple;
19618 : Form_pg_class classform;
19619 : AclResult aclresult;
19620 : char relkind;
19621 :
19622 35154 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19623 35154 : if (!HeapTupleIsValid(tuple))
19624 220 : return; /* concurrently dropped */
19625 34934 : classform = (Form_pg_class) GETSTRUCT(tuple);
19626 34934 : relkind = classform->relkind;
19627 :
19628 : /* Must own relation. */
19629 34934 : if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19630 72 : aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
19631 :
19632 : /* No system table modifications unless explicitly allowed. */
19633 34862 : if (!allowSystemTableMods && IsSystemClass(relid, classform))
19634 30 : ereport(ERROR,
19635 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19636 : errmsg("permission denied: \"%s\" is a system catalog",
19637 : rv->relname)));
19638 :
19639 : /*
19640 : * Extract the specified relation type from the statement parse tree.
19641 : *
19642 : * Also, for ALTER .. RENAME, check permissions: the user must (still)
19643 : * have CREATE rights on the containing namespace.
19644 : */
19645 34832 : if (IsA(stmt, RenameStmt))
19646 : {
19647 478 : aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19648 : GetUserId(), ACL_CREATE);
19649 478 : if (aclresult != ACLCHECK_OK)
19650 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
19651 0 : get_namespace_name(classform->relnamespace));
19652 478 : reltype = ((RenameStmt *) stmt)->renameType;
19653 : }
19654 34354 : else if (IsA(stmt, AlterObjectSchemaStmt))
19655 92 : reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19656 :
19657 34262 : else if (IsA(stmt, AlterTableStmt))
19658 34262 : reltype = ((AlterTableStmt *) stmt)->objtype;
19659 : else
19660 : {
19661 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19662 : reltype = OBJECT_TABLE; /* placate compiler */
19663 : }
19664 :
19665 : /*
19666 : * For compatibility with prior releases, we allow ALTER TABLE to be used
19667 : * with most other types of relations (but not composite types). We allow
19668 : * similar flexibility for ALTER INDEX in the case of RENAME, but not
19669 : * otherwise. Otherwise, the user must select the correct form of the
19670 : * command for the relation at issue.
19671 : */
19672 34832 : if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19673 0 : ereport(ERROR,
19674 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19675 : errmsg("\"%s\" is not a sequence", rv->relname)));
19676 :
19677 34832 : if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19678 0 : ereport(ERROR,
19679 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19680 : errmsg("\"%s\" is not a view", rv->relname)));
19681 :
19682 34832 : if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19683 0 : ereport(ERROR,
19684 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19685 : errmsg("\"%s\" is not a materialized view", rv->relname)));
19686 :
19687 34832 : if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19688 0 : ereport(ERROR,
19689 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19690 : errmsg("\"%s\" is not a foreign table", rv->relname)));
19691 :
19692 34832 : if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19693 0 : ereport(ERROR,
19694 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19695 : errmsg("\"%s\" is not a composite type", rv->relname)));
19696 :
19697 34832 : if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19698 : relkind != RELKIND_PARTITIONED_INDEX
19699 34 : && !IsA(stmt, RenameStmt))
19700 6 : ereport(ERROR,
19701 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19702 : errmsg("\"%s\" is not an index", rv->relname)));
19703 :
19704 : /*
19705 : * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19706 : * TYPE for that.
19707 : */
19708 34826 : if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19709 0 : ereport(ERROR,
19710 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19711 : errmsg("\"%s\" is a composite type", rv->relname),
19712 : /* translator: %s is an SQL ALTER command */
19713 : errhint("Use %s instead.",
19714 : "ALTER TYPE")));
19715 :
19716 : /*
19717 : * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19718 : * to a different schema, such as indexes and TOAST tables.
19719 : */
19720 34826 : if (IsA(stmt, AlterObjectSchemaStmt))
19721 : {
19722 92 : if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19723 0 : ereport(ERROR,
19724 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19725 : errmsg("cannot change schema of index \"%s\"",
19726 : rv->relname),
19727 : errhint("Change the schema of the table instead.")));
19728 92 : else if (relkind == RELKIND_COMPOSITE_TYPE)
19729 0 : ereport(ERROR,
19730 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19731 : errmsg("cannot change schema of composite type \"%s\"",
19732 : rv->relname),
19733 : /* translator: %s is an SQL ALTER command */
19734 : errhint("Use %s instead.",
19735 : "ALTER TYPE")));
19736 92 : else if (relkind == RELKIND_TOASTVALUE)
19737 0 : ereport(ERROR,
19738 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19739 : errmsg("cannot change schema of TOAST table \"%s\"",
19740 : rv->relname),
19741 : errhint("Change the schema of the table instead.")));
19742 : }
19743 :
19744 34826 : ReleaseSysCache(tuple);
19745 : }
19746 :
19747 : /*
19748 : * Transform any expressions present in the partition key
19749 : *
19750 : * Returns a transformed PartitionSpec.
19751 : */
19752 : static PartitionSpec *
19753 5544 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
19754 : {
19755 : PartitionSpec *newspec;
19756 : ParseState *pstate;
19757 : ParseNamespaceItem *nsitem;
19758 : ListCell *l;
19759 :
19760 5544 : newspec = makeNode(PartitionSpec);
19761 :
19762 5544 : newspec->strategy = partspec->strategy;
19763 5544 : newspec->partParams = NIL;
19764 5544 : newspec->location = partspec->location;
19765 :
19766 : /* Check valid number of columns for strategy */
19767 8126 : if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19768 2582 : list_length(partspec->partParams) != 1)
19769 6 : ereport(ERROR,
19770 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19771 : errmsg("cannot use \"list\" partition strategy with more than one column")));
19772 :
19773 : /*
19774 : * Create a dummy ParseState and insert the target relation as its sole
19775 : * rangetable entry. We need a ParseState for transformExpr.
19776 : */
19777 5538 : pstate = make_parsestate(NULL);
19778 5538 : nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19779 : NULL, false, true);
19780 5538 : addNSItemToQuery(pstate, nsitem, true, true, true);
19781 :
19782 : /* take care of any partition expressions */
19783 11532 : foreach(l, partspec->partParams)
19784 : {
19785 6018 : PartitionElem *pelem = lfirst_node(PartitionElem, l);
19786 :
19787 6018 : if (pelem->expr)
19788 : {
19789 : /* Copy, to avoid scribbling on the input */
19790 352 : pelem = copyObject(pelem);
19791 :
19792 : /* Now do parse transformation of the expression */
19793 352 : pelem->expr = transformExpr(pstate, pelem->expr,
19794 : EXPR_KIND_PARTITION_EXPRESSION);
19795 :
19796 : /* we have to fix its collations too */
19797 328 : assign_expr_collations(pstate, pelem->expr);
19798 : }
19799 :
19800 5994 : newspec->partParams = lappend(newspec->partParams, pelem);
19801 : }
19802 :
19803 5514 : return newspec;
19804 : }
19805 :
19806 : /*
19807 : * Compute per-partition-column information from a list of PartitionElems.
19808 : * Expressions in the PartitionElems must be parse-analyzed already.
19809 : */
19810 : static void
19811 5514 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19812 : List **partexprs, Oid *partopclass, Oid *partcollation,
19813 : PartitionStrategy strategy)
19814 : {
19815 : int attn;
19816 : ListCell *lc;
19817 : Oid am_oid;
19818 :
19819 5514 : attn = 0;
19820 11376 : foreach(lc, partParams)
19821 : {
19822 5994 : PartitionElem *pelem = lfirst_node(PartitionElem, lc);
19823 : Oid atttype;
19824 : Oid attcollation;
19825 :
19826 5994 : if (pelem->name != NULL)
19827 : {
19828 : /* Simple attribute reference */
19829 : HeapTuple atttuple;
19830 : Form_pg_attribute attform;
19831 :
19832 5666 : atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
19833 5666 : pelem->name);
19834 5666 : if (!HeapTupleIsValid(atttuple))
19835 12 : ereport(ERROR,
19836 : (errcode(ERRCODE_UNDEFINED_COLUMN),
19837 : errmsg("column \"%s\" named in partition key does not exist",
19838 : pelem->name),
19839 : parser_errposition(pstate, pelem->location)));
19840 5654 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19841 :
19842 5654 : if (attform->attnum <= 0)
19843 6 : ereport(ERROR,
19844 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19845 : errmsg("cannot use system column \"%s\" in partition key",
19846 : pelem->name),
19847 : parser_errposition(pstate, pelem->location)));
19848 :
19849 : /*
19850 : * Stored generated columns cannot work: They are computed after
19851 : * BEFORE triggers, but partition routing is done before all
19852 : * triggers. Maybe virtual generated columns could be made to
19853 : * work, but then they would need to be handled as an expression
19854 : * below.
19855 : */
19856 5648 : if (attform->attgenerated)
19857 12 : ereport(ERROR,
19858 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19859 : errmsg("cannot use generated column in partition key"),
19860 : errdetail("Column \"%s\" is a generated column.",
19861 : pelem->name),
19862 : parser_errposition(pstate, pelem->location)));
19863 :
19864 5636 : partattrs[attn] = attform->attnum;
19865 5636 : atttype = attform->atttypid;
19866 5636 : attcollation = attform->attcollation;
19867 5636 : ReleaseSysCache(atttuple);
19868 : }
19869 : else
19870 : {
19871 : /* Expression */
19872 328 : Node *expr = pelem->expr;
19873 : char partattname[16];
19874 328 : Bitmapset *expr_attrs = NULL;
19875 : int i;
19876 :
19877 : Assert(expr != NULL);
19878 328 : atttype = exprType(expr);
19879 328 : attcollation = exprCollation(expr);
19880 :
19881 : /*
19882 : * The expression must be of a storable type (e.g., not RECORD).
19883 : * The test is the same as for whether a table column is of a safe
19884 : * type (which is why we needn't check for the non-expression
19885 : * case).
19886 : */
19887 328 : snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19888 328 : CheckAttributeType(partattname,
19889 : atttype, attcollation,
19890 : NIL, CHKATYPE_IS_PARTKEY);
19891 :
19892 : /*
19893 : * Strip any top-level COLLATE clause. This ensures that we treat
19894 : * "x COLLATE y" and "(x COLLATE y)" alike.
19895 : */
19896 316 : while (IsA(expr, CollateExpr))
19897 0 : expr = (Node *) ((CollateExpr *) expr)->arg;
19898 :
19899 : /*
19900 : * Examine all the columns in the partition key expression. When
19901 : * the whole-row reference is present, examine all the columns of
19902 : * the partitioned table.
19903 : */
19904 316 : pull_varattnos(expr, 1, &expr_attrs);
19905 316 : if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
19906 : {
19907 60 : expr_attrs = bms_add_range(expr_attrs,
19908 : 1 - FirstLowInvalidHeapAttributeNumber,
19909 30 : RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
19910 30 : expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
19911 : }
19912 :
19913 316 : i = -1;
19914 696 : while ((i = bms_next_member(expr_attrs, i)) >= 0)
19915 : {
19916 428 : AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
19917 :
19918 : Assert(attno != 0);
19919 :
19920 : /*
19921 : * Cannot allow system column references, since that would
19922 : * make partition routing impossible: their values won't be
19923 : * known yet when we need to do that.
19924 : */
19925 428 : if (attno < 0)
19926 0 : ereport(ERROR,
19927 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19928 : errmsg("partition key expressions cannot contain system column references")));
19929 :
19930 : /*
19931 : * Stored generated columns cannot work: They are computed
19932 : * after BEFORE triggers, but partition routing is done before
19933 : * all triggers. Virtual generated columns could probably
19934 : * work, but it would require more work elsewhere (for example
19935 : * SET EXPRESSION would need to check whether the column is
19936 : * used in partition keys). Seems safer to prohibit for now.
19937 : */
19938 428 : if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19939 48 : ereport(ERROR,
19940 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19941 : errmsg("cannot use generated column in partition key"),
19942 : errdetail("Column \"%s\" is a generated column.",
19943 : get_attname(RelationGetRelid(rel), attno, false)),
19944 : parser_errposition(pstate, pelem->location)));
19945 : }
19946 :
19947 268 : if (IsA(expr, Var) &&
19948 12 : ((Var *) expr)->varattno > 0)
19949 : {
19950 :
19951 : /*
19952 : * User wrote "(column)" or "(column COLLATE something)".
19953 : * Treat it like simple attribute anyway.
19954 : */
19955 6 : partattrs[attn] = ((Var *) expr)->varattno;
19956 : }
19957 : else
19958 : {
19959 262 : partattrs[attn] = 0; /* marks the column as expression */
19960 262 : *partexprs = lappend(*partexprs, expr);
19961 :
19962 : /*
19963 : * transformPartitionSpec() should have already rejected
19964 : * subqueries, aggregates, window functions, and SRFs, based
19965 : * on the EXPR_KIND_ for partition expressions.
19966 : */
19967 :
19968 : /*
19969 : * Preprocess the expression before checking for mutability.
19970 : * This is essential for the reasons described in
19971 : * contain_mutable_functions_after_planning. However, we call
19972 : * expression_planner for ourselves rather than using that
19973 : * function, because if constant-folding reduces the
19974 : * expression to a constant, we'd like to know that so we can
19975 : * complain below.
19976 : *
19977 : * Like contain_mutable_functions_after_planning, assume that
19978 : * expression_planner won't scribble on its input, so this
19979 : * won't affect the partexprs entry we saved above.
19980 : */
19981 262 : expr = (Node *) expression_planner((Expr *) expr);
19982 :
19983 : /*
19984 : * Partition expressions cannot contain mutable functions,
19985 : * because a given row must always map to the same partition
19986 : * as long as there is no change in the partition boundary
19987 : * structure.
19988 : */
19989 262 : if (contain_mutable_functions(expr))
19990 6 : ereport(ERROR,
19991 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19992 : errmsg("functions in partition key expression must be marked IMMUTABLE")));
19993 :
19994 : /*
19995 : * While it is not exactly *wrong* for a partition expression
19996 : * to be a constant, it seems better to reject such keys.
19997 : */
19998 256 : if (IsA(expr, Const))
19999 12 : ereport(ERROR,
20000 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20001 : errmsg("cannot use constant expression as partition key")));
20002 : }
20003 : }
20004 :
20005 : /*
20006 : * Apply collation override if any
20007 : */
20008 5886 : if (pelem->collation)
20009 54 : attcollation = get_collation_oid(pelem->collation, false);
20010 :
20011 : /*
20012 : * Check we have a collation iff it's a collatable type. The only
20013 : * expected failures here are (1) COLLATE applied to a noncollatable
20014 : * type, or (2) partition expression had an unresolved collation. But
20015 : * we might as well code this to be a complete consistency check.
20016 : */
20017 5886 : if (type_is_collatable(atttype))
20018 : {
20019 660 : if (!OidIsValid(attcollation))
20020 0 : ereport(ERROR,
20021 : (errcode(ERRCODE_INDETERMINATE_COLLATION),
20022 : errmsg("could not determine which collation to use for partition expression"),
20023 : errhint("Use the COLLATE clause to set the collation explicitly.")));
20024 : }
20025 : else
20026 : {
20027 5226 : if (OidIsValid(attcollation))
20028 0 : ereport(ERROR,
20029 : (errcode(ERRCODE_DATATYPE_MISMATCH),
20030 : errmsg("collations are not supported by type %s",
20031 : format_type_be(atttype))));
20032 : }
20033 :
20034 5886 : partcollation[attn] = attcollation;
20035 :
20036 : /*
20037 : * Identify the appropriate operator class. For list and range
20038 : * partitioning, we use a btree operator class; hash partitioning uses
20039 : * a hash operator class.
20040 : */
20041 5886 : if (strategy == PARTITION_STRATEGY_HASH)
20042 332 : am_oid = HASH_AM_OID;
20043 : else
20044 5554 : am_oid = BTREE_AM_OID;
20045 :
20046 5886 : if (!pelem->opclass)
20047 : {
20048 5748 : partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20049 :
20050 5748 : if (!OidIsValid(partopclass[attn]))
20051 : {
20052 12 : if (strategy == PARTITION_STRATEGY_HASH)
20053 0 : ereport(ERROR,
20054 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20055 : errmsg("data type %s has no default operator class for access method \"%s\"",
20056 : format_type_be(atttype), "hash"),
20057 : errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20058 : else
20059 12 : ereport(ERROR,
20060 : (errcode(ERRCODE_UNDEFINED_OBJECT),
20061 : errmsg("data type %s has no default operator class for access method \"%s\"",
20062 : format_type_be(atttype), "btree"),
20063 : errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20064 : }
20065 : }
20066 : else
20067 138 : partopclass[attn] = ResolveOpClass(pelem->opclass,
20068 : atttype,
20069 : am_oid == HASH_AM_OID ? "hash" : "btree",
20070 : am_oid);
20071 :
20072 5862 : attn++;
20073 : }
20074 5382 : }
20075 :
20076 : /*
20077 : * PartConstraintImpliedByRelConstraint
20078 : * Do scanrel's existing constraints imply the partition constraint?
20079 : *
20080 : * "Existing constraints" include its check constraints and column-level
20081 : * not-null constraints. partConstraint describes the partition constraint,
20082 : * in implicit-AND form.
20083 : */
20084 : bool
20085 3220 : PartConstraintImpliedByRelConstraint(Relation scanrel,
20086 : List *partConstraint)
20087 : {
20088 3220 : List *existConstraint = NIL;
20089 3220 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20090 : int i;
20091 :
20092 3220 : if (constr && constr->has_not_null)
20093 : {
20094 842 : int natts = scanrel->rd_att->natts;
20095 :
20096 2838 : for (i = 1; i <= natts; i++)
20097 : {
20098 1996 : CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20099 :
20100 : /* invalid not-null constraint must be ignored here */
20101 1996 : if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20102 : {
20103 1138 : Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20104 1138 : NullTest *ntest = makeNode(NullTest);
20105 :
20106 1138 : ntest->arg = (Expr *) makeVar(1,
20107 : i,
20108 : wholeatt->atttypid,
20109 : wholeatt->atttypmod,
20110 : wholeatt->attcollation,
20111 : 0);
20112 1138 : ntest->nulltesttype = IS_NOT_NULL;
20113 :
20114 : /*
20115 : * argisrow=false is correct even for a composite column,
20116 : * because attnotnull does not represent a SQL-spec IS NOT
20117 : * NULL test in such a case, just IS DISTINCT FROM NULL.
20118 : */
20119 1138 : ntest->argisrow = false;
20120 1138 : ntest->location = -1;
20121 1138 : existConstraint = lappend(existConstraint, ntest);
20122 : }
20123 : }
20124 : }
20125 :
20126 3220 : return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20127 : }
20128 :
20129 : /*
20130 : * ConstraintImpliedByRelConstraint
20131 : * Do scanrel's existing constraints imply the given constraint?
20132 : *
20133 : * testConstraint is the constraint to validate. provenConstraint is a
20134 : * caller-provided list of conditions which this function may assume
20135 : * to be true. Both provenConstraint and testConstraint must be in
20136 : * implicit-AND form, must only contain immutable clauses, and must
20137 : * contain only Vars with varno = 1.
20138 : */
20139 : bool
20140 4466 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20141 : {
20142 4466 : List *existConstraint = list_copy(provenConstraint);
20143 4466 : TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20144 : int num_check,
20145 : i;
20146 :
20147 4466 : num_check = (constr != NULL) ? constr->num_check : 0;
20148 4974 : for (i = 0; i < num_check; i++)
20149 : {
20150 : Node *cexpr;
20151 :
20152 : /*
20153 : * If this constraint hasn't been fully validated yet, we must ignore
20154 : * it here.
20155 : */
20156 508 : if (!constr->check[i].ccvalid)
20157 6 : continue;
20158 :
20159 : /*
20160 : * NOT ENFORCED constraints are always marked as invalid, which should
20161 : * have been ignored.
20162 : */
20163 : Assert(constr->check[i].ccenforced);
20164 :
20165 502 : cexpr = stringToNode(constr->check[i].ccbin);
20166 :
20167 : /*
20168 : * Run each expression through const-simplification and
20169 : * canonicalization. It is necessary, because we will be comparing it
20170 : * to similarly-processed partition constraint expressions, and may
20171 : * fail to detect valid matches without this.
20172 : */
20173 502 : cexpr = eval_const_expressions(NULL, cexpr);
20174 502 : cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20175 :
20176 502 : existConstraint = list_concat(existConstraint,
20177 502 : make_ands_implicit((Expr *) cexpr));
20178 : }
20179 :
20180 : /*
20181 : * Try to make the proof. Since we are comparing CHECK constraints, we
20182 : * need to use weak implication, i.e., we assume existConstraint is
20183 : * not-false and try to prove the same for testConstraint.
20184 : *
20185 : * Note that predicate_implied_by assumes its first argument is known
20186 : * immutable. That should always be true for both NOT NULL and partition
20187 : * constraints, so we don't test it here.
20188 : */
20189 4466 : return predicate_implied_by(testConstraint, existConstraint, true);
20190 : }
20191 :
20192 : /*
20193 : * QueuePartitionConstraintValidation
20194 : *
20195 : * Add an entry to wqueue to have the given partition constraint validated by
20196 : * Phase 3, for the given relation, and all its children.
20197 : *
20198 : * We first verify whether the given constraint is implied by pre-existing
20199 : * relation constraints; if it is, there's no need to scan the table to
20200 : * validate, so don't queue in that case.
20201 : */
20202 : static void
20203 2730 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
20204 : List *partConstraint,
20205 : bool validate_default)
20206 : {
20207 : /*
20208 : * Based on the table's existing constraints, determine whether or not we
20209 : * may skip scanning the table.
20210 : */
20211 2730 : if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20212 : {
20213 88 : if (!validate_default)
20214 66 : ereport(DEBUG1,
20215 : (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20216 : RelationGetRelationName(scanrel))));
20217 : else
20218 22 : ereport(DEBUG1,
20219 : (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20220 : RelationGetRelationName(scanrel))));
20221 88 : return;
20222 : }
20223 :
20224 : /*
20225 : * Constraints proved insufficient. For plain relations, queue a
20226 : * validation item now; for partitioned tables, recurse to process each
20227 : * partition.
20228 : */
20229 2642 : if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20230 : {
20231 : AlteredTableInfo *tab;
20232 :
20233 : /* Grab a work queue entry. */
20234 2204 : tab = ATGetQueueEntry(wqueue, scanrel);
20235 : Assert(tab->partition_constraint == NULL);
20236 2204 : tab->partition_constraint = (Expr *) linitial(partConstraint);
20237 2204 : tab->validate_default = validate_default;
20238 : }
20239 438 : else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20240 : {
20241 386 : PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20242 : int i;
20243 :
20244 812 : for (i = 0; i < partdesc->nparts; i++)
20245 : {
20246 : Relation part_rel;
20247 : List *thisPartConstraint;
20248 :
20249 : /*
20250 : * This is the minimum lock we need to prevent deadlocks.
20251 : */
20252 426 : part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20253 :
20254 : /*
20255 : * Adjust the constraint for scanrel so that it matches this
20256 : * partition's attribute numbers.
20257 : */
20258 : thisPartConstraint =
20259 426 : map_partition_varattnos(partConstraint, 1,
20260 : part_rel, scanrel);
20261 :
20262 426 : QueuePartitionConstraintValidation(wqueue, part_rel,
20263 : thisPartConstraint,
20264 : validate_default);
20265 426 : table_close(part_rel, NoLock); /* keep lock till commit */
20266 : }
20267 : }
20268 : }
20269 :
20270 : /*
20271 : * attachPartitionTable: attach a new partition to the partitioned table
20272 : *
20273 : * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
20274 : * of an ALTER TABLE sequence.
20275 : * rel: partitioned relation;
20276 : * attachrel: relation of attached partition;
20277 : * bound: bounds of attached relation.
20278 : */
20279 : static void
20280 3016 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
20281 : {
20282 : /*
20283 : * Create an inheritance; the relevant checks are performed inside the
20284 : * function.
20285 : */
20286 3016 : CreateInheritance(attachrel, rel, true);
20287 :
20288 : /* Update the pg_class entry. */
20289 2908 : StorePartitionBound(attachrel, rel, bound);
20290 :
20291 : /* Ensure there exists a correct set of indexes in the partition. */
20292 2908 : AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20293 :
20294 : /* and triggers */
20295 2878 : CloneRowTriggersToPartition(rel, attachrel);
20296 :
20297 : /*
20298 : * Clone foreign key constraints. Callee is responsible for setting up
20299 : * for phase 3 constraint verification.
20300 : */
20301 2872 : CloneForeignKeyConstraints(wqueue, rel, attachrel);
20302 2854 : }
20303 :
20304 : /*
20305 : * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20306 : *
20307 : * Return the address of the newly attached partition.
20308 : */
20309 : static ObjectAddress
20310 2524 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
20311 : AlterTableUtilityContext *context)
20312 : {
20313 : Relation attachrel,
20314 : catalog;
20315 : List *attachrel_children;
20316 : List *partConstraint;
20317 : SysScanDesc scan;
20318 : ScanKeyData skey;
20319 : AttrNumber attno;
20320 : int natts;
20321 : TupleDesc tupleDesc;
20322 : ObjectAddress address;
20323 : const char *trigger_name;
20324 : Oid defaultPartOid;
20325 : List *partBoundConstraint;
20326 2524 : ParseState *pstate = make_parsestate(NULL);
20327 :
20328 2524 : pstate->p_sourcetext = context->queryString;
20329 :
20330 : /*
20331 : * We must lock the default partition if one exists, because attaching a
20332 : * new partition will change its partition constraint.
20333 : */
20334 : defaultPartOid =
20335 2524 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20336 2524 : if (OidIsValid(defaultPartOid))
20337 182 : LockRelationOid(defaultPartOid, AccessExclusiveLock);
20338 :
20339 2524 : attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20340 :
20341 : /*
20342 : * XXX I think it'd be a good idea to grab locks on all tables referenced
20343 : * by FKs at this point also.
20344 : */
20345 :
20346 : /*
20347 : * Must be owner of both parent and source table -- parent was checked by
20348 : * ATSimplePermissions call in ATPrepCmd
20349 : */
20350 2518 : ATSimplePermissions(AT_AttachPartition, attachrel,
20351 : ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
20352 :
20353 : /* A partition can only have one parent */
20354 2512 : if (attachrel->rd_rel->relispartition)
20355 6 : ereport(ERROR,
20356 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20357 : errmsg("\"%s\" is already a partition",
20358 : RelationGetRelationName(attachrel))));
20359 :
20360 2506 : if (OidIsValid(attachrel->rd_rel->reloftype))
20361 6 : ereport(ERROR,
20362 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20363 : errmsg("cannot attach a typed table as partition")));
20364 :
20365 : /*
20366 : * Table being attached should not already be part of inheritance; either
20367 : * as a child table...
20368 : */
20369 2500 : catalog = table_open(InheritsRelationId, AccessShareLock);
20370 2500 : ScanKeyInit(&skey,
20371 : Anum_pg_inherits_inhrelid,
20372 : BTEqualStrategyNumber, F_OIDEQ,
20373 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20374 2500 : scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20375 : NULL, 1, &skey);
20376 2500 : if (HeapTupleIsValid(systable_getnext(scan)))
20377 6 : ereport(ERROR,
20378 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20379 : errmsg("cannot attach inheritance child as partition")));
20380 2494 : systable_endscan(scan);
20381 :
20382 : /* ...or as a parent table (except the case when it is partitioned) */
20383 2494 : ScanKeyInit(&skey,
20384 : Anum_pg_inherits_inhparent,
20385 : BTEqualStrategyNumber, F_OIDEQ,
20386 : ObjectIdGetDatum(RelationGetRelid(attachrel)));
20387 2494 : scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20388 : 1, &skey);
20389 2494 : if (HeapTupleIsValid(systable_getnext(scan)) &&
20390 280 : attachrel->rd_rel->relkind == RELKIND_RELATION)
20391 6 : ereport(ERROR,
20392 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20393 : errmsg("cannot attach inheritance parent as partition")));
20394 2488 : systable_endscan(scan);
20395 2488 : table_close(catalog, AccessShareLock);
20396 :
20397 : /*
20398 : * Prevent circularity by seeing if rel is a partition of attachrel. (In
20399 : * particular, this disallows making a rel a partition of itself.)
20400 : *
20401 : * We do that by checking if rel is a member of the list of attachrel's
20402 : * partitions provided the latter is partitioned at all. We want to avoid
20403 : * having to construct this list again, so we request the strongest lock
20404 : * on all partitions. We need the strongest lock, because we may decide
20405 : * to scan them if we find out that the table being attached (or its leaf
20406 : * partitions) may contain rows that violate the partition constraint. If
20407 : * the table has a constraint that would prevent such rows, which by
20408 : * definition is present in all the partitions, we need not scan the
20409 : * table, nor its partitions. But we cannot risk a deadlock by taking a
20410 : * weaker lock now and the stronger one only when needed.
20411 : */
20412 2488 : attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20413 : AccessExclusiveLock, NULL);
20414 2488 : if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20415 12 : ereport(ERROR,
20416 : (errcode(ERRCODE_DUPLICATE_TABLE),
20417 : errmsg("circular inheritance not allowed"),
20418 : errdetail("\"%s\" is already a child of \"%s\".",
20419 : RelationGetRelationName(rel),
20420 : RelationGetRelationName(attachrel))));
20421 :
20422 : /* If the parent is permanent, so must be all of its partitions. */
20423 2476 : if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20424 2434 : attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20425 6 : ereport(ERROR,
20426 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20427 : errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20428 : RelationGetRelationName(rel))));
20429 :
20430 : /* Temp parent cannot have a partition that is itself not a temp */
20431 2470 : if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20432 42 : attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20433 18 : ereport(ERROR,
20434 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20435 : errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20436 : RelationGetRelationName(rel))));
20437 :
20438 : /* If the parent is temp, it must belong to this session */
20439 2452 : if (RELATION_IS_OTHER_TEMP(rel))
20440 0 : ereport(ERROR,
20441 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20442 : errmsg("cannot attach as partition of temporary relation of another session")));
20443 :
20444 : /* Ditto for the partition */
20445 2452 : if (RELATION_IS_OTHER_TEMP(attachrel))
20446 0 : ereport(ERROR,
20447 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20448 : errmsg("cannot attach temporary relation of another session as partition")));
20449 :
20450 : /*
20451 : * Check if attachrel has any identity columns or any columns that aren't
20452 : * in the parent.
20453 : */
20454 2452 : tupleDesc = RelationGetDescr(attachrel);
20455 2452 : natts = tupleDesc->natts;
20456 8402 : for (attno = 1; attno <= natts; attno++)
20457 : {
20458 5992 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20459 5992 : char *attributeName = NameStr(attribute->attname);
20460 :
20461 : /* Ignore dropped */
20462 5992 : if (attribute->attisdropped)
20463 616 : continue;
20464 :
20465 5376 : if (attribute->attidentity)
20466 24 : ereport(ERROR,
20467 : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20468 : errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20469 : RelationGetRelationName(attachrel), attributeName),
20470 : errdetail("The new partition may not contain an identity column."));
20471 :
20472 : /* Try to find the column in parent (matching on column name) */
20473 5352 : if (!SearchSysCacheExists2(ATTNAME,
20474 : ObjectIdGetDatum(RelationGetRelid(rel)),
20475 : CStringGetDatum(attributeName)))
20476 18 : ereport(ERROR,
20477 : (errcode(ERRCODE_DATATYPE_MISMATCH),
20478 : errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20479 : RelationGetRelationName(attachrel), attributeName,
20480 : RelationGetRelationName(rel)),
20481 : errdetail("The new partition may contain only the columns present in parent.")));
20482 : }
20483 :
20484 : /*
20485 : * If child_rel has row-level triggers with transition tables, we
20486 : * currently don't allow it to become a partition. See also prohibitions
20487 : * in ATExecAddInherit() and CreateTrigger().
20488 : */
20489 2410 : trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20490 2410 : if (trigger_name != NULL)
20491 6 : ereport(ERROR,
20492 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20493 : errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20494 : trigger_name, RelationGetRelationName(attachrel)),
20495 : errdetail("ROW triggers with transition tables are not supported on partitions.")));
20496 :
20497 : /*
20498 : * Check that the new partition's bound is valid and does not overlap any
20499 : * of existing partitions of the parent - note that it does not return on
20500 : * error.
20501 : */
20502 2404 : check_new_partition_bound(RelationGetRelationName(attachrel), rel,
20503 : cmd->bound, pstate);
20504 :
20505 2368 : attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
20506 :
20507 : /*
20508 : * Generate a partition constraint from the partition bound specification.
20509 : * If the parent itself is a partition, make sure to include its
20510 : * constraint as well.
20511 : */
20512 2206 : partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20513 :
20514 : /*
20515 : * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20516 : * since it's needed later to construct the constraint expression for
20517 : * validating against the default partition, if any.
20518 : */
20519 2206 : partConstraint = list_concat_copy(partBoundConstraint,
20520 2206 : RelationGetPartitionQual(rel));
20521 :
20522 : /* Skip validation if there are no constraints to validate. */
20523 2206 : if (partConstraint)
20524 : {
20525 : /*
20526 : * Run the partition quals through const-simplification similar to
20527 : * check constraints. We skip canonicalize_qual, though, because
20528 : * partition quals should be in canonical form already.
20529 : */
20530 : partConstraint =
20531 2158 : (List *) eval_const_expressions(NULL,
20532 : (Node *) partConstraint);
20533 :
20534 : /* XXX this sure looks wrong */
20535 2158 : partConstraint = list_make1(make_ands_explicit(partConstraint));
20536 :
20537 : /*
20538 : * Adjust the generated constraint to match this partition's attribute
20539 : * numbers.
20540 : */
20541 2158 : partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20542 : rel);
20543 :
20544 : /* Validate partition constraints against the table being attached. */
20545 2158 : QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20546 : false);
20547 : }
20548 :
20549 : /*
20550 : * If we're attaching a partition other than the default partition and a
20551 : * default one exists, then that partition's partition constraint changes,
20552 : * so add an entry to the work queue to validate it, too. (We must not do
20553 : * this when the partition being attached is the default one; we already
20554 : * did it above!)
20555 : */
20556 2206 : if (OidIsValid(defaultPartOid))
20557 : {
20558 : Relation defaultrel;
20559 : List *defPartConstraint;
20560 :
20561 : Assert(!cmd->bound->is_default);
20562 :
20563 : /* we already hold a lock on the default partition */
20564 146 : defaultrel = table_open(defaultPartOid, NoLock);
20565 : defPartConstraint =
20566 146 : get_proposed_default_constraint(partBoundConstraint);
20567 :
20568 : /*
20569 : * Map the Vars in the constraint expression from rel's attnos to
20570 : * defaultrel's.
20571 : */
20572 : defPartConstraint =
20573 146 : map_partition_varattnos(defPartConstraint,
20574 : 1, defaultrel, rel);
20575 146 : QueuePartitionConstraintValidation(wqueue, defaultrel,
20576 : defPartConstraint, true);
20577 :
20578 : /* keep our lock until commit. */
20579 146 : table_close(defaultrel, NoLock);
20580 : }
20581 :
20582 2206 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20583 :
20584 : /*
20585 : * If the partition we just attached is partitioned itself, invalidate
20586 : * relcache for all descendent partitions too to ensure that their
20587 : * rd_partcheck expression trees are rebuilt; partitions already locked at
20588 : * the beginning of this function.
20589 : */
20590 2206 : if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20591 : {
20592 : ListCell *l;
20593 :
20594 1108 : foreach(l, attachrel_children)
20595 : {
20596 746 : CacheInvalidateRelcacheByRelid(lfirst_oid(l));
20597 : }
20598 : }
20599 :
20600 : /* keep our lock until commit */
20601 2206 : table_close(attachrel, NoLock);
20602 :
20603 2206 : return address;
20604 : }
20605 :
20606 : /*
20607 : * AttachPartitionEnsureIndexes
20608 : * subroutine for ATExecAttachPartition to create/match indexes
20609 : *
20610 : * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20611 : * PARTITION: every partition must have an index attached to each index on the
20612 : * partitioned table.
20613 : */
20614 : static void
20615 2908 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
20616 : {
20617 : List *idxes;
20618 : List *attachRelIdxs;
20619 : Relation *attachrelIdxRels;
20620 : IndexInfo **attachInfos;
20621 : ListCell *cell;
20622 : MemoryContext cxt;
20623 : MemoryContext oldcxt;
20624 :
20625 2908 : cxt = AllocSetContextCreate(CurrentMemoryContext,
20626 : "AttachPartitionEnsureIndexes",
20627 : ALLOCSET_DEFAULT_SIZES);
20628 2908 : oldcxt = MemoryContextSwitchTo(cxt);
20629 :
20630 2908 : idxes = RelationGetIndexList(rel);
20631 2908 : attachRelIdxs = RelationGetIndexList(attachrel);
20632 2908 : attachrelIdxRels = palloc_array(Relation, list_length(attachRelIdxs));
20633 2908 : attachInfos = palloc_array(IndexInfo *, list_length(attachRelIdxs));
20634 :
20635 : /* Build arrays of all existing indexes and their IndexInfos */
20636 6222 : foreach_oid(cldIdxId, attachRelIdxs)
20637 : {
20638 406 : int i = foreach_current_index(cldIdxId);
20639 :
20640 406 : attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20641 406 : attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20642 : }
20643 :
20644 : /*
20645 : * If we're attaching a foreign table, we must fail if any of the indexes
20646 : * is a constraint index; otherwise, there's nothing to do here. Do this
20647 : * before starting work, to avoid wasting the effort of building a few
20648 : * non-unique indexes before coming across a unique one.
20649 : */
20650 2908 : if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20651 : {
20652 88 : foreach(cell, idxes)
20653 : {
20654 36 : Oid idx = lfirst_oid(cell);
20655 36 : Relation idxRel = index_open(idx, AccessShareLock);
20656 :
20657 36 : if (idxRel->rd_index->indisunique ||
20658 24 : idxRel->rd_index->indisprimary)
20659 12 : ereport(ERROR,
20660 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20661 : errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20662 : RelationGetRelationName(attachrel),
20663 : RelationGetRelationName(rel)),
20664 : errdetail("Partitioned table \"%s\" contains unique indexes.",
20665 : RelationGetRelationName(rel))));
20666 24 : index_close(idxRel, AccessShareLock);
20667 : }
20668 :
20669 52 : goto out;
20670 : }
20671 :
20672 : /*
20673 : * For each index on the partitioned table, find a matching one in the
20674 : * partition-to-be; if one is not found, create one.
20675 : */
20676 3552 : foreach(cell, idxes)
20677 : {
20678 726 : Oid idx = lfirst_oid(cell);
20679 726 : Relation idxRel = index_open(idx, AccessShareLock);
20680 : IndexInfo *info;
20681 : AttrMap *attmap;
20682 726 : bool found = false;
20683 : Oid constraintOid;
20684 :
20685 : /*
20686 : * Ignore indexes in the partitioned table other than partitioned
20687 : * indexes.
20688 : */
20689 726 : if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20690 : {
20691 0 : index_close(idxRel, AccessShareLock);
20692 0 : continue;
20693 : }
20694 :
20695 : /* construct an indexinfo to compare existing indexes against */
20696 726 : info = BuildIndexInfo(idxRel);
20697 726 : attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20698 : RelationGetDescr(rel),
20699 : false);
20700 726 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
20701 :
20702 : /*
20703 : * Scan the list of existing indexes in the partition-to-be, and mark
20704 : * the first matching, valid, unattached one we find, if any, as
20705 : * partition of the parent index. If we find one, we're done.
20706 : */
20707 786 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20708 : {
20709 298 : Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20710 298 : Oid cldConstrOid = InvalidOid;
20711 :
20712 : /* does this index have a parent? if so, can't use it */
20713 298 : if (attachrelIdxRels[i]->rd_rel->relispartition)
20714 12 : continue;
20715 :
20716 : /* If this index is invalid, can't use it */
20717 286 : if (!attachrelIdxRels[i]->rd_index->indisvalid)
20718 6 : continue;
20719 :
20720 280 : if (CompareIndexInfo(attachInfos[i], info,
20721 280 : attachrelIdxRels[i]->rd_indcollation,
20722 280 : idxRel->rd_indcollation,
20723 280 : attachrelIdxRels[i]->rd_opfamily,
20724 280 : idxRel->rd_opfamily,
20725 : attmap))
20726 : {
20727 : /*
20728 : * If this index is being created in the parent because of a
20729 : * constraint, then the child needs to have a constraint also,
20730 : * so look for one. If there is no such constraint, this
20731 : * index is no good, so keep looking.
20732 : */
20733 244 : if (OidIsValid(constraintOid))
20734 : {
20735 : cldConstrOid =
20736 146 : get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
20737 : cldIdxId);
20738 : /* no dice */
20739 146 : if (!OidIsValid(cldConstrOid))
20740 6 : continue;
20741 :
20742 : /* Ensure they're both the same type of constraint */
20743 280 : if (get_constraint_type(constraintOid) !=
20744 140 : get_constraint_type(cldConstrOid))
20745 0 : continue;
20746 : }
20747 :
20748 : /* bingo. */
20749 238 : IndexSetParentIndex(attachrelIdxRels[i], idx);
20750 238 : if (OidIsValid(constraintOid))
20751 140 : ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20752 : RelationGetRelid(attachrel));
20753 238 : found = true;
20754 :
20755 238 : CommandCounterIncrement();
20756 238 : break;
20757 : }
20758 : }
20759 :
20760 : /*
20761 : * If no suitable index was found in the partition-to-be, create one
20762 : * now. Note that if this is a PK, not-null constraints must already
20763 : * exist.
20764 : */
20765 726 : if (!found)
20766 : {
20767 : IndexStmt *stmt;
20768 : Oid conOid;
20769 :
20770 488 : stmt = generateClonedIndexStmt(NULL,
20771 : idxRel, attmap,
20772 : &conOid);
20773 488 : DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
20774 : RelationGetRelid(idxRel),
20775 : conOid,
20776 : -1,
20777 : true, false, false, false, false);
20778 : }
20779 :
20780 708 : index_close(idxRel, AccessShareLock);
20781 : }
20782 :
20783 2878 : out:
20784 : /* Clean up. */
20785 3272 : for (int i = 0; i < list_length(attachRelIdxs); i++)
20786 394 : index_close(attachrelIdxRels[i], AccessShareLock);
20787 2878 : MemoryContextSwitchTo(oldcxt);
20788 2878 : MemoryContextDelete(cxt);
20789 2878 : }
20790 :
20791 : /*
20792 : * CloneRowTriggersToPartition
20793 : * subroutine for ATExecAttachPartition/DefineRelation to create row
20794 : * triggers on partitions
20795 : */
20796 : static void
20797 3352 : CloneRowTriggersToPartition(Relation parent, Relation partition)
20798 : {
20799 : Relation pg_trigger;
20800 : ScanKeyData key;
20801 : SysScanDesc scan;
20802 : HeapTuple tuple;
20803 : MemoryContext perTupCxt;
20804 :
20805 3352 : ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20806 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20807 3352 : pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20808 3352 : scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20809 : true, NULL, 1, &key);
20810 :
20811 3352 : perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
20812 : "clone trig", ALLOCSET_SMALL_SIZES);
20813 :
20814 5424 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20815 : {
20816 2078 : Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20817 : CreateTrigStmt *trigStmt;
20818 2078 : Node *qual = NULL;
20819 : Datum value;
20820 : bool isnull;
20821 2078 : List *cols = NIL;
20822 2078 : List *trigargs = NIL;
20823 : MemoryContext oldcxt;
20824 :
20825 : /*
20826 : * Ignore statement-level triggers; those are not cloned.
20827 : */
20828 2078 : if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20829 1880 : continue;
20830 :
20831 : /*
20832 : * Don't clone internal triggers, because the constraint cloning code
20833 : * will.
20834 : */
20835 2036 : if (trigForm->tgisinternal)
20836 1838 : continue;
20837 :
20838 : /*
20839 : * Complain if we find an unexpected trigger type.
20840 : */
20841 198 : if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20842 162 : !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 198 : 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 198 : value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20854 : RelationGetDescr(pg_trigger), &isnull);
20855 198 : if (!isnull)
20856 : {
20857 6 : qual = stringToNode(TextDatumGetCString(value));
20858 6 : qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20859 : partition, parent);
20860 6 : 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 198 : if (trigForm->tgattr.dim1 > 0)
20869 : {
20870 : int i;
20871 :
20872 12 : for (i = 0; i < trigForm->tgattr.dim1; i++)
20873 : {
20874 : Form_pg_attribute col;
20875 :
20876 6 : col = TupleDescAttr(parent->rd_att,
20877 6 : trigForm->tgattr.values[i] - 1);
20878 6 : cols = lappend(cols,
20879 6 : makeString(pstrdup(NameStr(col->attname))));
20880 : }
20881 : }
20882 :
20883 : /* Reconstruct trigger arguments list. */
20884 198 : if (trigForm->tgnargs > 0)
20885 : {
20886 : char *p;
20887 :
20888 54 : value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20889 : RelationGetDescr(pg_trigger), &isnull);
20890 54 : if (isnull)
20891 0 : elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20892 : NameStr(trigForm->tgname), RelationGetRelationName(partition));
20893 :
20894 54 : p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20895 :
20896 120 : for (int i = 0; i < trigForm->tgnargs; i++)
20897 : {
20898 66 : trigargs = lappend(trigargs, makeString(pstrdup(p)));
20899 66 : p += strlen(p) + 1;
20900 : }
20901 : }
20902 :
20903 198 : trigStmt = makeNode(CreateTrigStmt);
20904 198 : trigStmt->replace = false;
20905 198 : trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20906 198 : trigStmt->trigname = NameStr(trigForm->tgname);
20907 198 : trigStmt->relation = NULL;
20908 198 : trigStmt->funcname = NULL; /* passed separately */
20909 198 : trigStmt->args = trigargs;
20910 198 : trigStmt->row = true;
20911 198 : trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20912 198 : trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20913 198 : trigStmt->columns = cols;
20914 198 : trigStmt->whenClause = NULL; /* passed separately */
20915 198 : trigStmt->transitionRels = NIL; /* not supported at present */
20916 198 : trigStmt->deferrable = trigForm->tgdeferrable;
20917 198 : trigStmt->initdeferred = trigForm->tginitdeferred;
20918 198 : trigStmt->constrrel = NULL; /* passed separately */
20919 :
20920 198 : CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20921 : trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20922 : trigForm->tgfoid, trigForm->oid, qual,
20923 198 : false, true, trigForm->tgenabled);
20924 :
20925 192 : MemoryContextSwitchTo(oldcxt);
20926 192 : MemoryContextReset(perTupCxt);
20927 : }
20928 :
20929 3346 : MemoryContextDelete(perTupCxt);
20930 :
20931 3346 : systable_endscan(scan);
20932 3346 : table_close(pg_trigger, RowExclusiveLock);
20933 3346 : }
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 584 : 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 584 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20967 584 : 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 112 : if (concurrent)
20985 12 : ereport(ERROR,
20986 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20987 : errmsg("cannot detach partitions concurrently when a default partition exists")));
20988 100 : 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 572 : 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 560 : if (!concurrent)
21004 414 : RemoveInheritance(partRel, rel, false);
21005 : else
21006 146 : 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 540 : 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 506 : 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 140 : partrelid = RelationGetRelid(partRel);
21044 140 : parentrelid = RelationGetRelid(rel);
21045 140 : parentrelname = MemoryContextStrdup(PortalContext,
21046 140 : RelationGetRelationName(rel));
21047 140 : partrelname = MemoryContextStrdup(PortalContext,
21048 140 : RelationGetRelationName(partRel));
21049 :
21050 : /* Invalidate relcache entries for the parent -- must be before close */
21051 140 : CacheInvalidateRelcache(rel);
21052 :
21053 140 : table_close(partRel, NoLock);
21054 140 : table_close(rel, NoLock);
21055 140 : tab->rel = NULL;
21056 :
21057 : /* Make updated catalog entry visible */
21058 140 : PopActiveSnapshot();
21059 140 : CommitTransactionCommand();
21060 :
21061 140 : 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 140 : SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
21073 140 : 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 90 : rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
21080 90 : partRel = try_relation_open(partrelid, AccessExclusiveLock);
21081 :
21082 : /* If the relations aren't there, something bad happened; bail out */
21083 90 : 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 90 : 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 90 : 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 456 : PushActiveSnapshot(GetTransactionSnapshot());
21106 :
21107 : /* Do the final part of detaching */
21108 456 : DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21109 :
21110 454 : PopActiveSnapshot();
21111 :
21112 454 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21113 :
21114 : /* keep our lock until commit */
21115 454 : table_close(partRel, NoLock);
21116 :
21117 454 : 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 1040 : 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 1040 : Relation trigrel = NULL;
21140 1040 : List *fkoids = NIL;
21141 :
21142 1040 : 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 104 : RemoveInheritance(partRel, rel, true);
21149 : }
21150 :
21151 : /* Drop any triggers that were cloned on creation/attach. */
21152 1040 : DropClonedTriggersFromPartition(RelationGetRelid(partRel));
21153 :
21154 : /*
21155 : * Detach any foreign keys that are inherited. This includes creating
21156 : * additional action triggers.
21157 : */
21158 1040 : fks = copyObject(RelationGetFKeyList(partRel));
21159 1040 : if (fks != NIL)
21160 90 : 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 2254 : foreach_node(ForeignKeyCacheInfo, fk, fks)
21176 174 : fkoids = lappend_oid(fkoids, fk->conoid);
21177 :
21178 1214 : foreach(cell, fks)
21179 : {
21180 174 : ForeignKeyCacheInfo *fk = lfirst(cell);
21181 : HeapTuple contup;
21182 : Form_pg_constraint conform;
21183 :
21184 174 : contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21185 174 : if (!HeapTupleIsValid(contup))
21186 0 : elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21187 174 : 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 174 : if (conform->contype != CONSTRAINT_FOREIGN ||
21194 324 : !OidIsValid(conform->conparentid) ||
21195 150 : list_member_oid(fkoids, conform->conparentid))
21196 : {
21197 66 : ReleaseSysCache(contup);
21198 66 : 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 108 : 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 108 : if (fk->conenforced)
21214 : {
21215 : Oid insertTriggerOid,
21216 : updateTriggerOid;
21217 :
21218 108 : GetForeignKeyCheckTriggers(trigrel,
21219 : fk->conoid, fk->confrelid, fk->conrelid,
21220 : &insertTriggerOid, &updateTriggerOid);
21221 : Assert(OidIsValid(insertTriggerOid));
21222 108 : TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21223 : RelationGetRelid(partRel));
21224 : Assert(OidIsValid(updateTriggerOid));
21225 108 : 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 108 : 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 108 : fkconstraint = makeNode(Constraint);
21263 108 : fkconstraint->contype = CONSTRAINT_FOREIGN;
21264 108 : fkconstraint->conname = pstrdup(NameStr(conform->conname));
21265 108 : fkconstraint->deferrable = conform->condeferrable;
21266 108 : fkconstraint->initdeferred = conform->condeferred;
21267 108 : fkconstraint->is_enforced = conform->conenforced;
21268 108 : fkconstraint->skip_validation = true;
21269 108 : fkconstraint->initially_valid = conform->convalidated;
21270 : /* a few irrelevant fields omitted here */
21271 108 : fkconstraint->pktable = NULL;
21272 108 : fkconstraint->fk_attrs = NIL;
21273 108 : fkconstraint->pk_attrs = NIL;
21274 108 : fkconstraint->fk_matchtype = conform->confmatchtype;
21275 108 : fkconstraint->fk_upd_action = conform->confupdtype;
21276 108 : fkconstraint->fk_del_action = conform->confdeltype;
21277 108 : fkconstraint->fk_del_set_cols = NIL;
21278 108 : fkconstraint->old_conpfeqop = NIL;
21279 108 : fkconstraint->old_pktable_oid = InvalidOid;
21280 108 : fkconstraint->location = -1;
21281 :
21282 : /* set up colnames, used to generate the constraint name */
21283 264 : for (int i = 0; i < numfks; i++)
21284 : {
21285 : Form_pg_attribute att;
21286 :
21287 156 : att = TupleDescAttr(RelationGetDescr(partRel),
21288 156 : conkey[i] - 1);
21289 :
21290 156 : fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21291 156 : makeString(NameStr(att->attname)));
21292 : }
21293 :
21294 108 : refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
21295 :
21296 108 : 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 108 : conform->conperiod);
21311 108 : table_close(refdRel, NoLock); /* keep lock till end of xact */
21312 : }
21313 :
21314 108 : ReleaseSysCache(contup);
21315 : }
21316 1040 : list_free_deep(fks);
21317 1040 : if (trigrel)
21318 90 : 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 1130 : foreach(cell, GetParentedForeignKeyRefs(partRel))
21326 : {
21327 92 : Oid constrOid = lfirst_oid(cell);
21328 : ObjectAddress constraint;
21329 :
21330 92 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21331 92 : deleteDependencyRecordsForClass(ConstraintRelationId,
21332 : constrOid,
21333 : ConstraintRelationId,
21334 : DEPENDENCY_INTERNAL);
21335 92 : CommandCounterIncrement();
21336 :
21337 92 : ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21338 92 : performDeletion(&constraint, DROP_RESTRICT, 0);
21339 : }
21340 :
21341 : /* Now we can detach indexes */
21342 1038 : indexes = RelationGetIndexList(partRel);
21343 1478 : foreach(cell, indexes)
21344 : {
21345 440 : Oid idxid = lfirst_oid(cell);
21346 : Oid parentidx;
21347 : Relation idx;
21348 : Oid constrOid;
21349 : Oid parentConstrOid;
21350 :
21351 440 : if (!has_superclass(idxid))
21352 12 : continue;
21353 :
21354 428 : parentidx = get_partition_parent(idxid, false);
21355 : Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21356 :
21357 428 : idx = index_open(idxid, AccessExclusiveLock);
21358 428 : 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 428 : constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
21367 : idxid);
21368 428 : parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
21369 : parentidx);
21370 428 : if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21371 198 : ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
21372 :
21373 428 : index_close(idx, NoLock);
21374 : }
21375 :
21376 : /* Update pg_class tuple */
21377 1038 : classRel = table_open(RelationRelationId, RowExclusiveLock);
21378 1038 : tuple = SearchSysCacheCopy1(RELOID,
21379 : ObjectIdGetDatum(RelationGetRelid(partRel)));
21380 1038 : 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 1038 : memset(new_val, 0, sizeof(new_val));
21387 1038 : memset(new_null, false, sizeof(new_null));
21388 1038 : memset(new_repl, false, sizeof(new_repl));
21389 1038 : new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21390 1038 : new_null[Anum_pg_class_relpartbound - 1] = true;
21391 1038 : new_repl[Anum_pg_class_relpartbound - 1] = true;
21392 1038 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21393 : new_val, new_null, new_repl);
21394 :
21395 1038 : ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21396 1038 : CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21397 1038 : heap_freetuple(newtuple);
21398 1038 : table_close(classRel, RowExclusiveLock);
21399 :
21400 : /*
21401 : * Drop identity property from all identity columns of partition.
21402 : */
21403 3328 : for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21404 : {
21405 2290 : Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21406 :
21407 2290 : if (!attr->attisdropped && attr->attidentity)
21408 30 : ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21409 : AccessExclusiveLock, true, true);
21410 : }
21411 :
21412 1038 : 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 256 : if (RelationGetRelid(partRel) == defaultPartOid)
21423 44 : update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
21424 : else
21425 212 : 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 1038 : 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 1038 : if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21442 : {
21443 : List *children;
21444 :
21445 62 : children = find_all_inheritors(RelationGetRelid(partRel),
21446 : AccessExclusiveLock, NULL);
21447 204 : foreach(cell, children)
21448 : {
21449 142 : CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
21450 : }
21451 : }
21452 1038 : }
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 14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
21462 : {
21463 : Relation partRel;
21464 : ObjectAddress address;
21465 14 : Snapshot snap = GetActiveSnapshot();
21466 :
21467 14 : 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 14 : WaitForOlderSnapshots(snap->xmin, false);
21478 :
21479 14 : DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21480 :
21481 14 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21482 :
21483 14 : table_close(partRel, NoLock);
21484 :
21485 14 : 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 1040 : DropClonedTriggersFromPartition(Oid partitionId)
21496 : {
21497 : ScanKeyData skey;
21498 : SysScanDesc scan;
21499 : HeapTuple trigtup;
21500 : Relation tgrel;
21501 : ObjectAddresses *objects;
21502 :
21503 1040 : objects = new_object_addresses();
21504 :
21505 : /*
21506 : * Scan pg_trigger to search for all triggers on this rel.
21507 : */
21508 1040 : ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21509 : F_OIDEQ, ObjectIdGetDatum(partitionId));
21510 1040 : tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21511 1040 : scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21512 : true, NULL, 1, &skey);
21513 1560 : while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21514 : {
21515 520 : Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21516 : ObjectAddress trig;
21517 :
21518 : /* Ignore triggers that weren't cloned */
21519 520 : if (!OidIsValid(pg_trigger->tgparentid))
21520 460 : 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 436 : if (OidIsValid(pg_trigger->tgconstrrelid))
21528 376 : 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 60 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21535 : TriggerRelationId,
21536 : DEPENDENCY_PARTITION_PRI);
21537 60 : deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21538 : RelationRelationId,
21539 : DEPENDENCY_PARTITION_SEC);
21540 :
21541 : /* remember this trigger to remove it below */
21542 60 : ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21543 60 : add_exact_object_address(&trig, objects);
21544 : }
21545 :
21546 : /* make the dependency removal visible to the deletion below */
21547 1040 : CommandCounterIncrement();
21548 1040 : performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
21549 :
21550 : /* done */
21551 1040 : free_object_addresses(objects);
21552 1040 : systable_endscan(scan);
21553 1040 : table_close(tgrel, RowExclusiveLock);
21554 1040 : }
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 412 : 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 412 : state = (struct AttachIndexCallbackState *) arg;
21576 :
21577 412 : if (!state->lockedParentTbl)
21578 : {
21579 390 : LockRelationOid(state->parentTblOid, AccessShareLock);
21580 390 : 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 412 : 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 412 : if (!OidIsValid(relOid))
21597 6 : return;
21598 :
21599 406 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21600 406 : if (!HeapTupleIsValid(tuple))
21601 0 : return; /* concurrently dropped, so nothing to do */
21602 406 : classform = (Form_pg_class) GETSTRUCT(tuple);
21603 406 : if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21604 312 : classform->relkind != RELKIND_INDEX)
21605 6 : ereport(ERROR,
21606 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21607 : errmsg("\"%s\" is not an index", rv->relname)));
21608 400 : 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 400 : state->partitionOid = IndexGetRelation(relOid, false);
21615 400 : LockRelationOid(state->partitionOid, AccessShareLock);
21616 : }
21617 :
21618 : /*
21619 : * ALTER INDEX i1 ATTACH PARTITION i2
21620 : */
21621 : static ObjectAddress
21622 390 : 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 390 : state.partitionOid = InvalidOid;
21640 390 : state.parentTblOid = parentIdx->rd_index->indrelid;
21641 390 : state.lockedParentTbl = false;
21642 : partIdxId =
21643 390 : RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
21644 : RangeVarCallbackForAttachIndex,
21645 : &state);
21646 : /* Not there? */
21647 378 : 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 378 : partIdx = relation_open(partIdxId, AccessExclusiveLock);
21654 :
21655 : /* we already hold locks on both tables, so this is safe: */
21656 378 : parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21657 378 : partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21658 :
21659 378 : ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21660 :
21661 : /* Silently do nothing if already in the right state */
21662 756 : currParent = partIdx->rd_rel->relispartition ?
21663 378 : get_partition_parent(partIdxId, false) : InvalidOid;
21664 378 : 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 354 : cldConstrId = InvalidOid;
21674 :
21675 : /*
21676 : * If this partition already has an index attached, refuse the
21677 : * operation.
21678 : */
21679 354 : refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21680 :
21681 348 : 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 348 : partDesc = RelationGetPartitionDesc(parentTbl, true);
21692 348 : found = false;
21693 544 : for (i = 0; i < partDesc->nparts; i++)
21694 : {
21695 538 : if (partDesc->oids[i] == state.partitionOid)
21696 : {
21697 342 : found = true;
21698 342 : break;
21699 : }
21700 : }
21701 348 : if (!found)
21702 6 : 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 342 : childInfo = BuildIndexInfo(partIdx);
21713 342 : parentInfo = BuildIndexInfo(parentIdx);
21714 342 : attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21715 : RelationGetDescr(parentTbl),
21716 : false);
21717 342 : if (!CompareIndexInfo(childInfo, parentInfo,
21718 342 : partIdx->rd_indcollation,
21719 342 : parentIdx->rd_indcollation,
21720 342 : partIdx->rd_opfamily,
21721 342 : parentIdx->rd_opfamily,
21722 : attmap))
21723 42 : 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 300 : constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21735 : RelationGetRelid(parentIdx));
21736 :
21737 300 : if (OidIsValid(constraintOid))
21738 : {
21739 120 : cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
21740 : partIdxId);
21741 120 : if (!OidIsValid(cldConstrId))
21742 6 : 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 294 : if (parentIdx->rd_index->indisprimary)
21758 96 : verifyPartitionIndexNotNull(childInfo, partTbl);
21759 :
21760 : /* All good -- do it */
21761 294 : IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21762 294 : if (OidIsValid(constraintOid))
21763 114 : ConstraintSetParentConstraint(cldConstrId, constraintOid,
21764 : RelationGetRelid(partTbl));
21765 :
21766 294 : free_attrmap(attmap);
21767 :
21768 294 : validatePartitionedIndex(parentIdx, parentTbl);
21769 : }
21770 :
21771 318 : relation_close(parentTbl, AccessShareLock);
21772 : /* keep these locks till commit */
21773 318 : relation_close(partTbl, NoLock);
21774 318 : relation_close(partIdx, NoLock);
21775 :
21776 318 : 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 354 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21785 : {
21786 : Oid existingIdx;
21787 :
21788 354 : existingIdx = index_get_partition(partitionTbl,
21789 : RelationGetRelid(parentIdx));
21790 354 : if (OidIsValid(existingIdx))
21791 6 : 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 348 : }
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 336 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
21809 : {
21810 : Relation inheritsRel;
21811 : SysScanDesc scan;
21812 : ScanKeyData key;
21813 336 : int tuples = 0;
21814 : HeapTuple inhTup;
21815 336 : 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 336 : inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21825 336 : ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21826 : BTEqualStrategyNumber, F_OIDEQ,
21827 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21828 336 : scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21829 : NULL, 1, &key);
21830 870 : while ((inhTup = systable_getnext(scan)) != NULL)
21831 : {
21832 534 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21833 : HeapTuple indTup;
21834 : Form_pg_index indexForm;
21835 :
21836 534 : indTup = SearchSysCache1(INDEXRELID,
21837 : ObjectIdGetDatum(inhForm->inhrelid));
21838 534 : if (!HeapTupleIsValid(indTup))
21839 0 : elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21840 534 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21841 534 : if (indexForm->indisvalid)
21842 476 : tuples += 1;
21843 534 : ReleaseSysCache(indTup);
21844 : }
21845 :
21846 : /* Done with pg_inherits */
21847 336 : systable_endscan(scan);
21848 336 : 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 336 : if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21855 : {
21856 : Relation idxRel;
21857 : HeapTuple indTup;
21858 : Form_pg_index indexForm;
21859 :
21860 170 : idxRel = table_open(IndexRelationId, RowExclusiveLock);
21861 170 : indTup = SearchSysCacheCopy1(INDEXRELID,
21862 : ObjectIdGetDatum(RelationGetRelid(partedIdx)));
21863 170 : if (!HeapTupleIsValid(indTup))
21864 0 : elog(ERROR, "cache lookup failed for index %u",
21865 : RelationGetRelid(partedIdx));
21866 170 : indexForm = (Form_pg_index) GETSTRUCT(indTup);
21867 :
21868 170 : indexForm->indisvalid = true;
21869 170 : updated = true;
21870 :
21871 170 : CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21872 :
21873 170 : table_close(idxRel, RowExclusiveLock);
21874 170 : 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 336 : 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 42 : CommandCounterIncrement();
21890 :
21891 42 : parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21892 42 : parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21893 42 : parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21894 42 : parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21895 : Assert(!parentIdx->rd_index->indisvalid);
21896 :
21897 42 : validatePartitionedIndex(parentIdx, parentTbl);
21898 :
21899 42 : relation_close(parentIdx, AccessExclusiveLock);
21900 42 : relation_close(parentTbl, AccessExclusiveLock);
21901 : }
21902 336 : }
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 96 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
21911 : {
21912 194 : for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21913 : {
21914 98 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
21915 98 : iinfo->ii_IndexAttrNumbers[i] - 1);
21916 :
21917 98 : 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 96 : }
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 1580 : GetParentedForeignKeyRefs(Relation partition)
21933 : {
21934 : Relation pg_constraint;
21935 : HeapTuple tuple;
21936 : SysScanDesc scan;
21937 : ScanKeyData key[2];
21938 1580 : List *constraints = NIL;
21939 :
21940 : /*
21941 : * If no indexes, or no columns are referenceable by FKs, we can avoid the
21942 : * scan.
21943 : */
21944 2218 : if (RelationGetIndexList(partition) == NIL ||
21945 638 : bms_is_empty(RelationGetIndexAttrBitmap(partition,
21946 : INDEX_ATTR_BITMAP_KEY)))
21947 1194 : return NIL;
21948 :
21949 : /* Search for constraints referencing this table */
21950 386 : pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21951 386 : ScanKeyInit(&key[0],
21952 : Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21953 : F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21954 386 : 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 386 : scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21960 588 : while ((tuple = systable_getnext(scan)) != NULL)
21961 : {
21962 202 : 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 202 : if (!OidIsValid(constrForm->conparentid))
21968 0 : continue;
21969 :
21970 202 : constraints = lappend_oid(constraints, constrForm->oid);
21971 : }
21972 :
21973 386 : systable_endscan(scan);
21974 386 : table_close(pg_constraint, AccessShareLock);
21975 :
21976 386 : 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 540 : ATDetachCheckNoForeignKeyRefs(Relation partition)
21986 : {
21987 : List *constraints;
21988 : ListCell *cell;
21989 :
21990 540 : constraints = GetParentedForeignKeyRefs(partition);
21991 :
21992 616 : foreach(cell, constraints)
21993 : {
21994 110 : Oid constrOid = lfirst_oid(cell);
21995 : HeapTuple tuple;
21996 : Form_pg_constraint constrForm;
21997 : Relation rel;
21998 110 : Trigger trig = {0};
21999 :
22000 110 : tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
22001 110 : if (!HeapTupleIsValid(tuple))
22002 0 : elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22003 110 : 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 110 : rel = table_open(constrForm->conrelid, ShareLock);
22010 :
22011 110 : trig.tgoid = InvalidOid;
22012 110 : trig.tgname = NameStr(constrForm->conname);
22013 110 : trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
22014 110 : trig.tgisinternal = true;
22015 110 : trig.tgconstrrelid = RelationGetRelid(partition);
22016 110 : trig.tgconstrindid = constrForm->conindid;
22017 110 : trig.tgconstraint = constrForm->oid;
22018 110 : trig.tgdeferrable = false;
22019 110 : trig.tginitdeferred = false;
22020 : /* we needn't fill in remaining fields */
22021 :
22022 110 : RI_PartitionRemove_Check(&trig, rel, partition);
22023 :
22024 76 : ReleaseSysCache(tuple);
22025 :
22026 76 : table_close(rel, NoLock);
22027 : }
22028 506 : }
22029 :
22030 : /*
22031 : * resolve column compression specification to compression method.
22032 : */
22033 : static char
22034 269884 : GetAttributeCompression(Oid atttypid, const char *compression)
22035 : {
22036 : char cmethod;
22037 :
22038 269884 : if (compression == NULL || strcmp(compression, "default") == 0)
22039 269670 : 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 214 : if (!TypeIsToastable(atttypid))
22054 6 : 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 208 : cmethod = CompressionNameToMethod(compression);
22060 208 : if (!CompressionMethodIsValid(cmethod))
22061 12 : ereport(ERROR,
22062 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22063 : errmsg("invalid compression method \"%s\"", compression)));
22064 :
22065 196 : return cmethod;
22066 : }
22067 :
22068 : /*
22069 : * resolve column storage specification
22070 : */
22071 : static char
22072 304 : GetAttributeStorage(Oid atttypid, const char *storagemode)
22073 : {
22074 304 : char cstorage = 0;
22075 :
22076 304 : if (pg_strcasecmp(storagemode, "plain") == 0)
22077 56 : cstorage = TYPSTORAGE_PLAIN;
22078 248 : else if (pg_strcasecmp(storagemode, "external") == 0)
22079 176 : cstorage = TYPSTORAGE_EXTERNAL;
22080 72 : else if (pg_strcasecmp(storagemode, "extended") == 0)
22081 28 : cstorage = TYPSTORAGE_EXTENDED;
22082 44 : else if (pg_strcasecmp(storagemode, "main") == 0)
22083 38 : cstorage = TYPSTORAGE_MAIN;
22084 6 : else if (pg_strcasecmp(storagemode, "default") == 0)
22085 6 : 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 304 : if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22097 6 : 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 298 : 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 648 : 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 1320 : foreach_ptr(NewConstraint, con, tab->constraints)
22119 : {
22120 24 : switch (con->contype)
22121 : {
22122 24 : case CONSTR_CHECK:
22123 :
22124 : /*
22125 : * We already expanded virtual expression in
22126 : * createTableConstraints.
22127 : */
22128 24 : con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22129 24 : 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 1362 : foreach_ptr(NewColumnValue, ex, tab->newvals)
22141 66 : ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22142 648 : }
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 1018 : evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab,
22151 : Relation newPartRel,
22152 : TupleTableSlot *insertslot,
22153 : ExprContext *econtext)
22154 : {
22155 1018 : econtext->ecxt_scantuple = insertslot;
22156 :
22157 2132 : foreach_ptr(NewColumnValue, ex, tab->newvals)
22158 : {
22159 96 : if (!ex->is_generated)
22160 0 : continue;
22161 :
22162 96 : insertslot->tts_values[ex->attnum - 1]
22163 96 : = ExecEvalExpr(ex->exprstate,
22164 : econtext,
22165 96 : &insertslot->tts_isnull[ex->attnum - 1]);
22166 : }
22167 :
22168 2072 : foreach_ptr(NewConstraint, con, tab->constraints)
22169 : {
22170 36 : switch (con->contype)
22171 : {
22172 36 : case CONSTR_CHECK:
22173 36 : 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 36 : 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 1018 : }
22190 :
22191 : /*
22192 : * getAttributesList: build a list of columns (ColumnDef) based on parent_rel
22193 : */
22194 : static List *
22195 678 : getAttributesList(Relation parent_rel)
22196 : {
22197 : AttrNumber parent_attno;
22198 : TupleDesc modelDesc;
22199 678 : List *colList = NIL;
22200 :
22201 678 : modelDesc = RelationGetDescr(parent_rel);
22202 :
22203 2424 : for (parent_attno = 1; parent_attno <= modelDesc->natts;
22204 1746 : parent_attno++)
22205 : {
22206 1746 : Form_pg_attribute attribute = TupleDescAttr(modelDesc,
22207 : parent_attno - 1);
22208 : ColumnDef *def;
22209 :
22210 : /* Ignore dropped columns in the parent. */
22211 1746 : if (attribute->attisdropped)
22212 0 : continue;
22213 :
22214 1746 : def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22215 : attribute->atttypmod, attribute->attcollation);
22216 :
22217 1746 : def->is_not_null = attribute->attnotnull;
22218 :
22219 : /* Copy identity. */
22220 1746 : def->identity = attribute->attidentity;
22221 :
22222 : /* Copy attgenerated. */
22223 1746 : def->generated = attribute->attgenerated;
22224 :
22225 1746 : def->storage = attribute->attstorage;
22226 :
22227 : /* Likewise, copy compression. */
22228 1746 : if (CompressionMethodIsValid(attribute->attcompression))
22229 18 : def->compression =
22230 18 : pstrdup(GetCompressionMethodName(attribute->attcompression));
22231 : else
22232 1728 : def->compression = NULL;
22233 :
22234 : /* Add to column list. */
22235 1746 : colList = lappend(colList, def);
22236 : }
22237 :
22238 678 : 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 648 : 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 648 : List *constraints = NIL;
22257 648 : List *cookedConstraints = NIL;
22258 :
22259 648 : tupleDesc = RelationGetDescr(parent_rel);
22260 648 : constr = tupleDesc->constr;
22261 :
22262 648 : if (!constr)
22263 414 : 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 234 : attmap = build_attrmap_by_name(RelationGetDescr(newRel),
22271 : tupleDesc,
22272 : false);
22273 :
22274 : /* Cycle for default values. */
22275 888 : for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
22276 : {
22277 654 : Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
22278 : parent_attno - 1);
22279 :
22280 : /* Ignore dropped columns in the parent. */
22281 654 : if (attribute->attisdropped)
22282 0 : continue;
22283 :
22284 : /* Copy the default, if present, and it should be copied. */
22285 654 : if (attribute->atthasdef)
22286 : {
22287 150 : Node *this_default = NULL;
22288 : bool found_whole_row;
22289 : AttrNumber num;
22290 : Node *def;
22291 : NewColumnValue *newval;
22292 :
22293 150 : if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
22294 6 : this_default = build_generation_expression(parent_rel, attribute->attnum);
22295 : else
22296 : {
22297 144 : this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
22298 144 : 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 150 : num = attmap->attnums[parent_attno - 1];
22304 150 : def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
22305 :
22306 150 : 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 150 : 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 150 : if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
22319 : {
22320 66 : newval = palloc0_object(NewColumnValue);
22321 66 : newval->attnum = num;
22322 66 : newval->expr = expression_planner((Expr *) def);
22323 66 : newval->is_generated = (attribute->attgenerated != '\0');
22324 66 : tab->newvals = lappend(tab->newvals, newval);
22325 : }
22326 : }
22327 : }
22328 :
22329 : /* Cycle for CHECK constraints. */
22330 336 : for (ccnum = 0; ccnum < constr->num_check; ccnum++)
22331 : {
22332 102 : char *ccname = constr->check[ccnum].ccname;
22333 102 : char *ccbin = constr->check[ccnum].ccbin;
22334 102 : bool ccenforced = constr->check[ccnum].ccenforced;
22335 102 : bool ccnoinherit = constr->check[ccnum].ccnoinherit;
22336 102 : 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 102 : 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 102 : 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 102 : constr = makeNode(Constraint);
22362 102 : constr->contype = CONSTR_CHECK;
22363 102 : constr->conname = pstrdup(ccname);
22364 102 : constr->deferrable = false;
22365 102 : constr->initdeferred = false;
22366 102 : constr->is_enforced = ccenforced;
22367 102 : constr->skip_validation = !ccvalid;
22368 102 : constr->initially_valid = ccvalid;
22369 102 : constr->is_no_inherit = ccnoinherit;
22370 102 : constr->raw_expr = NULL;
22371 102 : constr->cooked_expr = nodeToString(ccbin_node);
22372 102 : constr->location = -1;
22373 102 : constraints = lappend(constraints, constr);
22374 : }
22375 :
22376 : /* Install all CHECK constraints. */
22377 234 : cookedConstraints = AddRelationNewConstraints(newRel, NIL, constraints,
22378 : false, true, true, NULL);
22379 :
22380 : /* Make the additional catalog changes visible. */
22381 234 : 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 570 : foreach_ptr(CookedConstraint, ccon, cookedConstraints)
22390 : {
22391 102 : if (!ccon->skip_validation)
22392 : {
22393 : Node *qual;
22394 66 : Bitmapset *attnums = NULL;
22395 :
22396 : Assert(ccon->contype == CONSTR_CHECK);
22397 66 : qual = expand_generated_columns_in_expr(ccon->expr, newRel, 1);
22398 66 : pull_varattnos(qual, 1, &attnums);
22399 :
22400 : /*
22401 : * Add a check only if it contains a tableoid
22402 : * (TableOidAttributeNumber).
22403 : */
22404 66 : if (bms_is_member(TableOidAttributeNumber - FirstLowInvalidHeapAttributeNumber,
22405 : attnums))
22406 : {
22407 : NewConstraint *newcon;
22408 :
22409 24 : newcon = palloc0_object(NewConstraint);
22410 24 : newcon->name = ccon->name;
22411 24 : newcon->contype = CONSTR_CHECK;
22412 24 : newcon->qual = qual;
22413 :
22414 24 : tab->constraints = lappend(tab->constraints, newcon);
22415 : }
22416 : }
22417 : }
22418 :
22419 : /* Don't need the cookedConstraints anymore. */
22420 234 : list_free_deep(cookedConstraints);
22421 :
22422 : /* Reproduce not-null constraints. */
22423 234 : 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 162 : 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 162 : 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 678 : 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 678 : List *colList = NIL;
22463 : Oid relamId;
22464 : Oid namespaceId;
22465 : AlteredTableInfo *new_partrel_tab;
22466 678 : Form_pg_class parent_relform = parent_rel->rd_rel;
22467 :
22468 : /* If the existing rel is temp, it must belong to this session. */
22469 678 : 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 678 : colList = getAttributesList(parent_rel);
22476 :
22477 : /* Create a tuple descriptor from the relation schema. */
22478 678 : descriptor = BuildDescForRelation(colList);
22479 :
22480 : /* Look up the access method for the new relation. */
22481 678 : 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 678 : RangeVarGetAndCheckCreationNamespace(newPartName, NoLock, &existingRelid);
22486 678 : 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 678 : if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22498 624 : newPartName->relpersistence == RELPERSISTENCE_TEMP)
22499 12 : 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 666 : if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22506 630 : parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22507 18 : 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 648 : 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 648 : 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 648 : CommandCounterIncrement();
22540 :
22541 : /*
22542 : * Open the new partition with no lock, because we already have an
22543 : * AccessExclusiveLock placed there after creation.
22544 : */
22545 648 : newRel = table_open(newRelId, NoLock);
22546 :
22547 : /* Find or create a work queue entry for the newly created table. */
22548 648 : new_partrel_tab = ATGetQueueEntry(wqueue, newRel);
22549 :
22550 : /* Create constraints, default values, and generated values. */
22551 648 : 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 648 : CommandCounterIncrement();
22558 :
22559 648 : 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 138 : 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 138 : 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 138 : tab = ATGetQueueEntry(wqueue, newPartRel);
22582 :
22583 : /* Generate the constraint and default execution states. */
22584 138 : estate = CreateExecutorState();
22585 :
22586 138 : buildExpressionExecutionStates(tab, newPartRel, estate);
22587 :
22588 138 : mycid = GetCurrentCommandId(true);
22589 :
22590 : /* Prepare a BulkInsertState for table_tuple_insert. */
22591 138 : bistate = GetBulkInsertState();
22592 :
22593 : /* Create the necessary tuple slot. */
22594 138 : dstslot = table_slot_create(newPartRel, NULL);
22595 :
22596 594 : 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 318 : econtext = GetPerTupleExprContext(estate);
22607 :
22608 : /*
22609 : * Partition is already locked in the transformPartitionCmdForMerge
22610 : * function.
22611 : */
22612 318 : mergingPartition = table_open(merging_oid, NoLock);
22613 :
22614 : /* Create a source tuple slot for the partition being merged. */
22615 318 : 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 318 : tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
22622 : RelationGetDescr(newPartRel));
22623 :
22624 : /* Scan through the rows. */
22625 318 : snapshot = RegisterSnapshot(GetLatestSnapshot());
22626 318 : 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 318 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
22633 :
22634 696 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
22635 : {
22636 : TupleTableSlot *insertslot;
22637 :
22638 378 : CHECK_FOR_INTERRUPTS();
22639 :
22640 378 : if (tuple_map)
22641 : {
22642 : /* Need to use a map to copy attributes. */
22643 42 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
22644 : }
22645 : else
22646 : {
22647 336 : slot_getallattrs(srcslot);
22648 :
22649 : /* Copy attributes directly. */
22650 336 : insertslot = dstslot;
22651 :
22652 336 : ExecClearTuple(insertslot);
22653 :
22654 336 : memcpy(insertslot->tts_values, srcslot->tts_values,
22655 336 : sizeof(Datum) * srcslot->tts_nvalid);
22656 336 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
22657 336 : sizeof(bool) * srcslot->tts_nvalid);
22658 :
22659 336 : 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 378 : 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 378 : evaluateGeneratedExpressionsAndCheckConstraints(tab, newPartRel,
22676 : insertslot, econtext);
22677 :
22678 : /* Write the tuple out to the new relation. */
22679 378 : table_tuple_insert(newPartRel, insertslot, mycid,
22680 : ti_options, bistate);
22681 :
22682 378 : ResetExprContext(econtext);
22683 : }
22684 :
22685 318 : MemoryContextSwitchTo(oldCxt);
22686 318 : table_endscan(scan);
22687 318 : UnregisterSnapshot(snapshot);
22688 :
22689 318 : if (tuple_map)
22690 30 : free_conversion_map(tuple_map);
22691 :
22692 318 : ExecDropSingleTupleTableSlot(srcslot);
22693 318 : table_close(mergingPartition, NoLock);
22694 : }
22695 :
22696 138 : FreeExecutorState(estate);
22697 138 : ExecDropSingleTupleTableSlot(dstslot);
22698 138 : FreeBulkInsertState(bistate);
22699 :
22700 138 : 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 276 : foreach(ltab, *wqueue)
22707 : {
22708 276 : tab = (AlteredTableInfo *) lfirst(ltab);
22709 276 : if (tab->relid == RelationGetRelid(newPartRel))
22710 : {
22711 138 : *wqueue = list_delete_cell(*wqueue, ltab);
22712 138 : break;
22713 : }
22714 : }
22715 138 : }
22716 :
22717 : /*
22718 : * detachPartitionTable: detach partition "child_rel" from partitioned table
22719 : * "parent_rel" with default partition identifier "defaultPartOid"
22720 : */
22721 : static void
22722 570 : detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
22723 : {
22724 : /* Remove the pg_inherits row first. */
22725 570 : 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 570 : PushActiveSnapshot(GetTransactionSnapshot());
22732 :
22733 : /* Do the final part of detaching. */
22734 570 : DetachPartitionFinalize(parent_rel, child_rel, false, defaultPartOid);
22735 :
22736 570 : PopActiveSnapshot();
22737 570 : }
22738 :
22739 : /*
22740 : * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
22741 : */
22742 : static void
22743 180 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
22744 : PartitionCmd *cmd, AlterTableUtilityContext *context)
22745 : {
22746 : Relation newPartRel;
22747 180 : List *mergingPartitions = NIL;
22748 : Oid defaultPartOid;
22749 : Oid existingRelid;
22750 180 : 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 756 : 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 408 : mergingPartition = table_openrv_extended(name, NoLock, false);
22770 : Assert(CheckRelationLockedByMe(mergingPartition, AccessExclusiveLock, false));
22771 :
22772 408 : if (OidIsValid(ownerId))
22773 : {
22774 : /* Do the partitions being merged have different owners? */
22775 228 : if (ownerId != mergingPartition->rd_rel->relowner)
22776 6 : ereport(ERROR,
22777 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22778 : errmsg("partitions being merged have different owners"));
22779 : }
22780 : else
22781 180 : ownerId = mergingPartition->rd_rel->relowner;
22782 :
22783 : /* Store the next merging partition into the list. */
22784 402 : mergingPartitions = lappend_oid(mergingPartitions,
22785 : RelationGetRelid(mergingPartition));
22786 :
22787 402 : table_close(mergingPartition, NoLock);
22788 : }
22789 :
22790 : /* Look up the existing relation by the new partition name. */
22791 174 : 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 174 : if (OidIsValid(existingRelid))
22800 : {
22801 26 : 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 20 : 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 20 : 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 20 : CommandCounterIncrement();
22825 : }
22826 : else
22827 : {
22828 6 : ereport(ERROR,
22829 : errcode(ERRCODE_DUPLICATE_TABLE),
22830 : errmsg("relation \"%s\" already exists", cmd->name->relname));
22831 : }
22832 : }
22833 :
22834 : defaultPartOid =
22835 168 : get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
22836 :
22837 : /* Detach all merging partitions. */
22838 714 : foreach_oid(mergingPartitionOid, mergingPartitions)
22839 : {
22840 : Relation child_rel;
22841 :
22842 378 : child_rel = table_open(mergingPartitionOid, NoLock);
22843 :
22844 378 : detachPartitionTable(rel, child_rel, defaultPartOid);
22845 :
22846 378 : 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 696 : foreach_oid(mergingPartitionOid, mergingPartitions)
22859 : {
22860 : ObjectAddress object;
22861 :
22862 : /* Get oid of the later to be dropped relation. */
22863 372 : object.objectId = mergingPartitionOid;
22864 372 : object.classId = RelationRelationId;
22865 372 : object.objectSubId = 0;
22866 :
22867 372 : 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 162 : 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 138 : GetUserIdAndSecContext(&save_userid, &save_sec_context);
22886 138 : SetUserIdAndSecContext(ownerId,
22887 : save_sec_context | SECURITY_RESTRICTED_OPERATION);
22888 138 : save_nestlevel = NewGUCNestLevel();
22889 138 : RestrictSearchPath();
22890 :
22891 : /* Copy data from merged partitions to the new partition. */
22892 138 : MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel);
22893 :
22894 : /* Drop the current partitions before attaching the new one. */
22895 594 : foreach_oid(mergingPartitionOid, mergingPartitions)
22896 : {
22897 : ObjectAddress object;
22898 :
22899 318 : object.objectId = mergingPartitionOid;
22900 318 : object.classId = RelationRelationId;
22901 318 : object.objectSubId = 0;
22902 :
22903 318 : performDeletion(&object, DROP_RESTRICT, 0);
22904 : }
22905 :
22906 138 : 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 138 : attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
22913 :
22914 : /* Keep the lock until commit. */
22915 138 : table_close(newPartRel, NoLock);
22916 :
22917 : /* Roll back any GUC changes executed by index functions. */
22918 138 : AtEOXact_GUC(false, save_nestlevel);
22919 :
22920 : /* Restore the userid and security context. */
22921 138 : SetUserIdAndSecContext(save_userid, save_sec_context);
22922 138 : }
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 510 : createSplitPartitionContext(Relation partRel)
22944 : {
22945 : SplitPartitionContext *pc;
22946 :
22947 510 : pc = palloc0_object(SplitPartitionContext);
22948 510 : 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 510 : pc->bistate = GetBulkInsertState();
22955 :
22956 : /* Create a destination tuple slot for the new partition. */
22957 510 : pc->dstslot = table_slot_create(pc->partRel, NULL);
22958 :
22959 510 : return pc;
22960 : }
22961 :
22962 : /*
22963 : * deleteSplitPartitionContext: delete context for partition
22964 : */
22965 : static void
22966 510 : deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, int ti_options)
22967 : {
22968 : ListCell *ltab;
22969 :
22970 510 : ExecDropSingleTupleTableSlot(pc->dstslot);
22971 510 : FreeBulkInsertState(pc->bistate);
22972 :
22973 510 : 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 1020 : foreach(ltab, *wqueue)
22980 : {
22981 1020 : AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
22982 :
22983 1020 : if (tab->relid == RelationGetRelid(pc->partRel))
22984 : {
22985 510 : *wqueue = list_delete_cell(*wqueue, ltab);
22986 510 : break;
22987 : }
22988 : }
22989 :
22990 510 : pfree(pc);
22991 510 : }
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 186 : 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 186 : 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 186 : List *partContexts = NIL;
23019 : TupleConversionMap *tuple_map;
23020 186 : SplitPartitionContext *defaultPartCtx = NULL,
23021 : *pc;
23022 :
23023 186 : mycid = GetCurrentCommandId(true);
23024 :
23025 186 : estate = CreateExecutorState();
23026 :
23027 696 : forboth(listptr, partlist, listptr2, newPartRels)
23028 : {
23029 510 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
23030 :
23031 510 : pc = createSplitPartitionContext((Relation) lfirst(listptr2));
23032 :
23033 : /* Find the work queue entry for the new partition table: newPartRel. */
23034 510 : pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
23035 :
23036 510 : buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
23037 :
23038 510 : 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 42 : defaultPartCtx = pc;
23045 : }
23046 : else
23047 : {
23048 : List *partConstraint;
23049 :
23050 : /* Build expression execution states for partition check quals. */
23051 468 : partConstraint = get_qual_from_partbound(rel, sps->bound);
23052 : partConstraint =
23053 468 : (List *) eval_const_expressions(NULL,
23054 : (Node *) partConstraint);
23055 : /* Make a boolean expression for ExecCheck(). */
23056 468 : 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 468 : partConstraint = map_partition_varattnos(partConstraint,
23063 : 1, splitRel, rel);
23064 :
23065 468 : pc->partqualstate =
23066 468 : ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
23067 : Assert(pc->partqualstate != NULL);
23068 : }
23069 :
23070 : /* Store partition context into a list. */
23071 510 : partContexts = lappend(partContexts, pc);
23072 : }
23073 :
23074 186 : econtext = GetPerTupleExprContext(estate);
23075 :
23076 : /* Create the necessary tuple slot. */
23077 186 : 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 186 : pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
23085 186 : tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
23086 186 : RelationGetDescr(pc->partRel));
23087 :
23088 : /* Scan through the rows. */
23089 186 : snapshot = RegisterSnapshot(GetLatestSnapshot());
23090 186 : 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 186 : oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
23097 :
23098 826 : while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
23099 : {
23100 640 : bool found = false;
23101 : TupleTableSlot *insertslot;
23102 :
23103 640 : CHECK_FOR_INTERRUPTS();
23104 :
23105 640 : econtext->ecxt_scantuple = srcslot;
23106 :
23107 : /* Search partition for the current slot, srcslot. */
23108 1718 : foreach(listptr, partContexts)
23109 : {
23110 1604 : pc = (SplitPartitionContext *) lfirst(listptr);
23111 :
23112 : /* skip DEFAULT partition */
23113 1604 : if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
23114 : {
23115 526 : found = true;
23116 526 : break;
23117 : }
23118 : }
23119 640 : if (!found)
23120 : {
23121 : /* Use the DEFAULT partition if it exists. */
23122 114 : if (defaultPartCtx)
23123 114 : 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 640 : if (tuple_map)
23132 : {
23133 : /* Need to use a map to copy attributes. */
23134 24 : insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
23135 : }
23136 : else
23137 : {
23138 : /* Extract data from the old tuple. */
23139 616 : slot_getallattrs(srcslot);
23140 :
23141 : /* Copy attributes directly. */
23142 616 : insertslot = pc->dstslot;
23143 :
23144 616 : ExecClearTuple(insertslot);
23145 :
23146 616 : memcpy(insertslot->tts_values, srcslot->tts_values,
23147 616 : sizeof(Datum) * srcslot->tts_nvalid);
23148 616 : memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23149 616 : sizeof(bool) * srcslot->tts_nvalid);
23150 :
23151 616 : 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 640 : 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 640 : evaluateGeneratedExpressionsAndCheckConstraints(pc->tab, pc->partRel,
23168 : insertslot, econtext);
23169 :
23170 : /* Write the tuple out to the new relation. */
23171 640 : table_tuple_insert(pc->partRel, insertslot, mycid,
23172 640 : ti_options, pc->bistate);
23173 :
23174 640 : ResetExprContext(econtext);
23175 : }
23176 :
23177 186 : MemoryContextSwitchTo(oldCxt);
23178 :
23179 186 : table_endscan(scan);
23180 186 : UnregisterSnapshot(snapshot);
23181 :
23182 186 : if (tuple_map)
23183 6 : free_conversion_map(tuple_map);
23184 :
23185 186 : ExecDropSingleTupleTableSlot(srcslot);
23186 :
23187 186 : FreeExecutorState(estate);
23188 :
23189 882 : foreach_ptr(SplitPartitionContext, spc, partContexts)
23190 510 : deleteSplitPartitionContext(spc, wqueue, ti_options);
23191 186 : }
23192 :
23193 : /*
23194 : * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
23195 : */
23196 : static void
23197 198 : 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 198 : bool isSameName = false;
23205 : char tmpRelName[NAMEDATALEN];
23206 198 : List *newPartRels = NIL;
23207 : ObjectAddress object;
23208 : Oid defaultPartOid;
23209 : Oid save_userid;
23210 : int save_sec_context;
23211 : int save_nestlevel;
23212 :
23213 198 : defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
23214 :
23215 : /*
23216 : * Partition is already locked in the transformPartitionCmdForSplit
23217 : * function.
23218 : */
23219 198 : splitRel = table_openrv(cmd->name, NoLock);
23220 :
23221 198 : splitRelOid = RelationGetRelid(splitRel);
23222 :
23223 : /* Check descriptions of new partitions. */
23224 912 : foreach_node(SinglePartitionSpec, sps, cmd->partlist)
23225 : {
23226 : Oid existingRelid;
23227 :
23228 : /* Look up the existing relation by the new partition name. */
23229 528 : 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 528 : if (existingRelid == splitRelOid && !isSameName)
23236 : /* One new partition can have the same name as a split partition. */
23237 44 : isSameName = true;
23238 484 : else if (OidIsValid(existingRelid))
23239 6 : ereport(ERROR,
23240 : errcode(ERRCODE_DUPLICATE_TABLE),
23241 : errmsg("relation \"%s\" already exists", sps->name->relname));
23242 : }
23243 :
23244 : /* Detach the split partition. */
23245 192 : 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 192 : object.objectId = splitRelOid;
23257 192 : object.classId = RelationRelationId;
23258 192 : object.objectSubId = 0;
23259 192 : 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 192 : if (isSameName)
23266 : {
23267 : /*
23268 : * We must bump the command counter to make the split partition tuple
23269 : * visible for renaming.
23270 : */
23271 44 : CommandCounterIncrement();
23272 : /* Rename partition. */
23273 44 : sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23274 44 : 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 44 : CommandCounterIncrement();
23281 : }
23282 :
23283 : /* Create new partitions (like a split partition), without indexes. */
23284 888 : foreach_node(SinglePartitionSpec, sps, cmd->partlist)
23285 : {
23286 : Relation newPartRel;
23287 :
23288 516 : newPartRel = createPartitionTable(wqueue, sps->name, rel,
23289 516 : splitRel->rd_rel->relowner);
23290 510 : 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 186 : GetUserIdAndSecContext(&save_userid, &save_sec_context);
23302 186 : SetUserIdAndSecContext(splitRel->rd_rel->relowner,
23303 : save_sec_context | SECURITY_RESTRICTED_OPERATION);
23304 186 : save_nestlevel = NewGUCNestLevel();
23305 186 : RestrictSearchPath();
23306 :
23307 : /* Copy data from the split partition to the new partitions. */
23308 186 : SplitPartitionMoveRows(wqueue, rel, splitRel, cmd->partlist, newPartRels);
23309 : /* Keep the lock until commit. */
23310 186 : table_close(splitRel, NoLock);
23311 :
23312 : /* Attach new partitions to the partitioned table. */
23313 696 : forboth(listptr, cmd->partlist, listptr2, newPartRels)
23314 : {
23315 510 : SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
23316 510 : Relation newPartRel = (Relation) lfirst(listptr2);
23317 :
23318 : /*
23319 : * wqueue = NULL: verification for each cloned constraint is not
23320 : * needed.
23321 : */
23322 510 : attachPartitionTable(NULL, rel, newPartRel, sps->bound);
23323 : /* Keep the lock until commit. */
23324 510 : table_close(newPartRel, NoLock);
23325 : }
23326 :
23327 : /* Drop the split partition. */
23328 186 : object.classId = RelationRelationId;
23329 186 : object.objectId = splitRelOid;
23330 186 : object.objectSubId = 0;
23331 : /* Probably DROP_CASCADE is not needed. */
23332 186 : performDeletion(&object, DROP_RESTRICT, 0);
23333 :
23334 : /* Roll back any GUC changes executed by index functions. */
23335 186 : AtEOXact_GUC(false, save_nestlevel);
23336 :
23337 : /* Restore the userid and security context. */
23338 186 : SetUserIdAndSecContext(save_userid, save_sec_context);
23339 186 : }
|